__main__.py 29 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2017-2020  IRISA
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# The original code contained here was initially developed by:
#
#     Pierre Vignet.
#     IRISA
#     Dyliss team
#     IRISA Campus de Beaulieu
#     35042 RENNES Cedex, FRANCE
24
"""Entry point and argument parser for cadbiom_cmd package"""
25 26
from __future__ import print_function

27 28 29
# Standard imports
import argparse
import os
30
import sys
31
from functools import wraps
32

33
# Custom imports
34
import cadbiom.commons as cm
VIGNET Pierre's avatar
VIGNET Pierre committed
35
from cadbiom_cmd import __version__
36
from cadbiom import __version__ as cadbiom_lib_version
37

38
LOGGER = cm.logger()
39
parser = None
40

41 42 43

def check_output_dir(function):
    """Decorator used process some arguments from argparse"""
VIGNET Pierre's avatar
VIGNET Pierre committed
44
    # Expose the docstring of the wrapped function instead of that of the decorator
45
    @wraps(function)
46 47
    def modified_func(*args, **kwargs):
        """Fix paths in arguments (add trailing '/')"""
48 49
        output_dir = args[0]["output"]
        output_dir = output_dir if output_dir[-1] == "/" else output_dir + "/"
50 51

        # Compatibility for output and output_dir
52 53
        args[0]["output"] = output_dir
        args[0]["output_dir"] = output_dir
54 55 56 57 58 59 60

        return function(*args, **kwargs)

    return modified_func


@check_output_dir
61 62 63
def solutions_search(args):
    """Launch the search for Minimum Activation Conditions (MAC) for entities
    of interest.
64 65
    """

66 67
    # Module import
    import solution_search
68

69 70
    # Temporary fix: all_macs is always activated in the new API
    args["all_macs"] = True
71
    solution_search.solutions_search(args)  # !
72

73

74
def solutions_sort(args):
VIGNET Pierre's avatar
VIGNET Pierre committed
75
    """Read a solution file or a directory containing MAC solutions files
76
    (*mac* files), and sort all frontier places/boundaries in alphabetical order.
77 78
    """

79 80
    # Module import
    import solution_sort
81 82

    solution_sort.solutions_sort(args["path"])
83

84

85
@check_output_dir
86
def solutions_2_graphs(args):
87
    """Create GraphML formated files containing a representation of the
VIGNET Pierre's avatar
VIGNET Pierre committed
88
    trajectories for each solution in complete MAC files (*mac_complete files).
89 90 91

    This is a function to visualize paths taken by the solver from the boundaries
    to the entities of interest.
92 93
    """

94
    # Module import
95
    import solution_sort
96 97

    solution_sort.solutions_2_graphs(args["output"], args["model_file"], args["path"])
98

99

100
@check_output_dir
101
def queries_2_json(args):
102
    """Create a JSON formated file containing all data from complete MAC files
103
    (*mac_complete files). The file will contain frontier places/boundaries
104
    and decompiled steps with their respective events for each solution.
105 106 107 108 109 110

    This is a function to quickly search all transition attributes involved
    in a solution.
    """

    # Module import
111
    import solution_sort
112

113
    solution_sort.queries_2_json(
114 115 116 117
        args["output"],
        args["model_file"],
        args["path"],
        conditions=not args["no_conditions"],  # Reverse the param...
118
    )
119

120

121
@check_output_dir
122 123 124 125
def json_2_interaction_graph(args):
    """Make an interaction weighted graph based on the searched molecule of interest.

    Read decompiled solutions files (*.json* files produced by the
126
    directive 'queries_2_json') and make a graph of the relationships
127 128 129 130 131 132
    between one or more molecules of interest, the genes and other
    frontier places/boundaries found among all the solutions.
    """

    # Module import
    import interaction_graph
133

134
    interaction_graph.json_2_interaction_graph(
135
        args["output"], args["molecules_of_interest"], args["path"]
136 137 138
    )


139
@check_output_dir
140
def queries_2_common_graph(args):
VIGNET Pierre's avatar
VIGNET Pierre committed
141
    """Create a GraphML formated file containing a unique representation of **all**
142
    trajectories corresponding to all solutions in each complete MAC file
VIGNET Pierre's avatar
VIGNET Pierre committed
143
    (*mac_complete files).
144 145 146 147 148 149 150

    This is a function to visualize paths taken by the solver from the boundaries
    to the entities of interest.
    """

    # Module import
    import solution_sort
151

152 153 154
    if not any((args["graphs"], args["csv"], args["json"])):
        parser.error("at least one flag is required")
    solution_sort.queries_2_common_graph(**args)
155 156


157
@check_output_dir
158
def queries_2_occcurrence_matrix(args):
VIGNET Pierre's avatar
VIGNET Pierre committed
159
    """Create a matrix of occurrences counting entities in the solutions found in
160
    *mac.txt files in the given path.
VIGNET Pierre's avatar
VIGNET Pierre committed
161 162 163 164
    """

    # Module import
    import solution_sort
165

166
    solution_sort.queries_2_occcurrence_matrix(
167 168 169 170
        args["output"],
        args["model_file"],
        args["path"],
        transposed=args["transpose_csv"],
VIGNET Pierre's avatar
VIGNET Pierre committed
171 172
    )

173

174 175 176 177 178 179 180 181 182 183
@check_output_dir
def queries_2_clustermap(args):
    """Create a ClusterMap (hierarchically-clustered heatmap) for boundaries
    found in **all** the solutions stored in each *mac.txt file in the given path.

    This is a function to visualize co-occurrences of the boundaries within
    the solutions obtained.
    """
    # Module import
    import queries_2_clustermap
VIGNET Pierre's avatar
VIGNET Pierre committed
184

185 186 187 188
    # output, path
    queries_2_clustermap.queries_2_clustermap(**args)


189
def model_identifier_mapping(args):
190 191 192 193 194 195 196
    """Mapping of identifiers from external databases.

    This function exports a CSV formated file presenting the list of known
    Cadbiom identifiers for each given external identifier.
    """

    # Module import
197
    import models
198

199
    models.model_identifier_mapping(**args)
200 201


202
@check_output_dir
203 204
def model_comparison(args):
    """Isomorphism test.
205

206 207
    Check if the graphs based on the two given models have  the same topology,
    nodes & edges attributes/roles.
208 209 210
    """

    # Module import
211
    import models
212

213
    models.graph_isomorph_test(
214 215 216 217 218
        args["model_file_1"],
        args["model_file_2"],
        args["output"],
        args["graphs"],
        args["json"],
219 220 221
    )


222
@check_output_dir
223 224 225
def model_info(args):
    """Provide several levels of information about the structure of the model
    and its places/entities.
226 227 228
    """

    # Module import
229
    import models
230

231
    models.model_info(**args)
232 233


234
@check_output_dir
235
def model_graph(args):
236
    """Information about the graph based on the model.
237

238
    Get centralities (degree).
239
    Forge a GraphML file.
240 241 242
    """

    # Module import
243
    import models
244

245
    models.model_graph(**args)
246 247


248
@check_output_dir
249
def merge_macs(args):
250
    """Merge solutions to a csv file."""
251 252 253

    # Module import
    import solution_merge
254 255

    solution_merge.merge_macs_to_csv(args["solutions_directory"], args["output"])
256 257


VIGNET Pierre's avatar
VIGNET Pierre committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
def analytics(params):
    from urllib import urlopen, urlencode
    from uuid import getnode
    import re
    from sys import platform
    import json

    white_list = (
        "subcommand", "model_file", "start_prop", "final_prop", "inv_prop",
        "limit",
        "molecules_of_interest",
        "model_file_1", "model_file_2", "graphs", "json",
        "default", "boundaries", "all_entities", "genes",
        "centralities", "graph", "json",
    )

    # {"1":["key","val"],}
275 276 277 278 279
    cvar = {
        str(i): [key, val]
        for i, (key, val) in enumerate(params.items(), 1)
        if val is not None and key in white_list
    }
VIGNET Pierre's avatar
VIGNET Pierre committed
280 281 282 283 284 285 286 287
    data = {
        "url": "http://cadbiom.genouest.org/",
        "_rcn": "cadbiom",
        "_rck": params["subcommand"],
        "idsite": 2,
        "apiv": 1,
        "send_image": 0,
        "rec": 1,
288
        "_id": "0000" + "".join(re.findall("..", "%0012x" % getnode()))[:16],
VIGNET Pierre's avatar
VIGNET Pierre committed
289 290 291 292
        "_cvar": json.dumps(cvar).encode("utf8"),
        "ua": platform,
    }
    data = urlencode(data).encode("ascii")
VIGNET Pierre's avatar
VIGNET Pierre committed
293
    page = urlopen("http://cadbiom.genouest.org/lytic.html", data=data).read()
294
    m = re.search('.*URL=(.*)">', page)
VIGNET Pierre's avatar
VIGNET Pierre committed
295 296
    if m:
        urlopen(m.group(1), data=data)
VIGNET Pierre's avatar
VIGNET Pierre committed
297 298


299 300
def args_to_param(args):
    """Return argparse namespace as a dict {variable name: value}"""
301
    return {k: v for k, v in vars(args).items() if k != "func"}
302 303


304 305 306
def ensure_dir_presence(path):
    """Test write mode and eventually create the given directory"""

307 308 309 310
    if not os.path.dirname(path):
        # Current directory
        path = "."

311
    if not os.path.isfile(path) and not os.path.isdir(path):
312 313 314 315 316 317 318 319
        LOGGER.info("Creation of the directory <{}>".format(path))
        os.mkdir(path)

    if not os.access(path, os.W_OK):
        LOGGER.error("<{}> is not writable".format(path))
        exit()


320 321 322 323 324 325 326 327 328
class ReadableFile(argparse.Action):
    """
    http://stackoverflow.com/questions/11415570/directory-path-types-with-argparse
    """

    def __call__(self, parser, namespace, values, option_string=None):
        prospective_file = values

        if not os.path.isfile(prospective_file):
329 330
            LOGGER.error("readable_file:<{}> is not a valid file".format(prospective_file))
            exit()
331 332 333 334

        if os.access(prospective_file, os.R_OK):
            setattr(namespace, self.dest, prospective_file)
        else:
335 336
            LOGGER.error("readable_file:<{}> is not a readable file".format(prospective_file))
            exit()
337 338 339


class ReadableDir(argparse.Action):
340 341 342 343 344 345 346 347
    """
    http://stackoverflow.com/questions/11415570/directory-path-types-with-argparse
    """

    def __call__(self, parser, namespace, values, option_string=None):
        prospective_dir = values

        if not os.path.isdir(prospective_dir):
VIGNET Pierre's avatar
VIGNET Pierre committed
348 349 350
            LOGGER.error(
                "readable_dir:<{}> is not a valid path".format(prospective_dir)
            )
351
            exit()
352 353 354 355

        if os.access(prospective_dir, os.R_OK):
            setattr(namespace, self.dest, prospective_dir)
        else:
VIGNET Pierre's avatar
VIGNET Pierre committed
356 357 358
            LOGGER.error(
                "readable_dir:<{}> is not a readable dir".format(prospective_dir)
            )
359
            exit()
360 361


362
class CustomFormatter(
VIGNET Pierre's avatar
VIGNET Pierre committed
363
    argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
364 365
):
    """Formatter used to print default values AND do not format raw text"""
VIGNET Pierre's avatar
VIGNET Pierre committed
366

367 368 369
    pass


370
def main():
371
    """Argument parser"""
372

VIGNET Pierre's avatar
VIGNET Pierre committed
373
    # Global parser configuration
374
    global parser
375 376 377 378 379
    parser = argparse.ArgumentParser(
        description="Cadbiom - Command Line Interface",
        formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, width=90),
        epilog="""Examples:

VIGNET Pierre's avatar
VIGNET Pierre committed
380
    $ cadbiom_cmd solutions_search -h""",
381
    )
382
    # Default log level: debug
383
    parser.add_argument("-vv", "--verbose", nargs="?", default="info")
384 385
    parser.add_argument(
        "-V", "--version", action="version",
386
        version="%(prog)s " + __version__ + "; cadbiom library " + cadbiom_lib_version
387 388
    )

VIGNET Pierre's avatar
VIGNET Pierre committed
389
    # Subparsers and ability to get the current subcommand name
390
    subparsers = parser.add_subparsers(title="subcommands", dest="subcommand")
391

VIGNET Pierre's avatar
VIGNET Pierre committed
392 393 394 395 396 397 398 399
    # Common parser: I/O group with model file requirement
    parent_parser = argparse.ArgumentParser(add_help=False)
    input_output_group = parent_parser.add_argument_group(title="I/O")
    input_output_group.add_argument("model_file", help="Cadbiom model (.bcx file).")

    # Increase the width of help text
    custom_formatter = lambda prog: CustomFormatter(prog, width=90)

400 401 402 403
    # PS:
    # nargs='?': optional
    # Default actions: https://docs.python.org/dev/library/argparse.html#action

VIGNET Pierre's avatar
VIGNET Pierre committed
404
    ############################################################################
405
    ## Solutions-search commands ###############################################
VIGNET Pierre's avatar
VIGNET Pierre committed
406
    ############################################################################
407
    # subparser: Compute macs
408
    #    steps      = 7
409 410 411
    #    final_prop = "P"
    #    start_prop = None
    #    inv_prop   = None
412 413
    parser_search = subparsers.add_parser(
        "solutions_search",
414
        help=solutions_search.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
415 416
        parents=[parent_parser],
        formatter_class=custom_formatter,
417
        epilog="""Examples:
418

VIGNET Pierre's avatar
VIGNET Pierre committed
419 420
    $ cadbiom_cmd solutions_search model_pid.bcx SRP9 --steps 10 --output ./SRP9_solutions/
    $ cadbiom_cmd solutions_search model_pid.bcx --input_file my_queries.txt --output""",
421
    )
422
    ## I/O
VIGNET Pierre's avatar
VIGNET Pierre committed
423
    parser_search._action_groups[2].add_argument(
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
        "--output",
        action=ReadableDir,
        nargs="?",
        default="result/",
        help="Output directory.",
    )
    ## Settings
    tune_group = parser_search.add_argument_group(title="Search settings")
    tune_group.add_argument(
        "--steps",
        type=int,
        nargs="?",
        default=7,
        help="Maximum of allowed steps to find macs",
    )
    tune_group.add_argument(
        "--limit",
        type=int,
        nargs="?",
        default=400,
        help="Limit the number of solutions.",
    )
    # all_macs: False by default
    # parser_search.add_argument('--all_macs', action='store_true',
448 449
    #    help="Solver will try to search all macs from 0 to the maximum of "
    #         "allowed steps.")
450 451 452 453
    # continue: False by default
    tune_group.add_argument(
        "--continue",
        action="store_true",
454
        help="Resume previous computations; if there is a mac file from a "
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
        "previous work, last frontier places/boundaries will be reloaded.",
    )
    ## Properties
    properties_group = parser_search.add_argument_group(title="Model checking")
    # Get final_property alone OR an input_file containing multiple properties
    group = properties_group.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "final_prop",
        nargs="?",
        help="Final property that will occur at the end of the simulation.",
    )
    group.add_argument(
        "--input_file",
        action=ReadableFile,
        nargs="?",
        help="Replace 'final_prop' argument by a file with 1 logical formula "
        "per line. This allows the software to launch 1 process per formula.",
    )
    # combinations: False by default
    properties_group.add_argument(
        "--combinations",
        action="store_true",
        help="If input_file is set, we can compute the combinations of the "
        "formulas indicated on each line.",
    )
    properties_group.add_argument(
        "--start_prop",
        nargs="?",
        default=None,
484
        help="Property that will be part of the initial state of the model. "
485 486 487 488 489 490 491
        "In concrete terms, some entities can be activated by this "
        "mechanism without modifying the model.",
    )
    properties_group.add_argument(
        "--inv_prop",
        nargs="?",
        default=None,
492
        help="Invariant property that will always occur during the simulation. "
493 494
        "The given logical formula will be checked at each step of the simulation.",
    )
495

496
    parser_search.set_defaults(func=solutions_search)
497

VIGNET Pierre's avatar
VIGNET Pierre committed
498
    ############################################################################
499
    ## Solutions-related commands ##############################################
VIGNET Pierre's avatar
VIGNET Pierre committed
500
    ############################################################################
501

VIGNET Pierre's avatar
VIGNET Pierre committed
502
    ## subparser: Sort solutions in alphabetical order in place
503
    # Solution file (complete or not)
VIGNET Pierre's avatar
VIGNET Pierre committed
504 505 506 507 508
    parser_solutions_sort = subparsers.add_parser(
        "solutions_sort", help=solutions_sort.__doc__
    )
    parser_solutions_sort.add_argument(
        "path",
509
        help="Solution file or directory with MAC solutions files "
VIGNET Pierre's avatar
VIGNET Pierre committed
510 511
        "(*mac* files) generated with the 'solutions_search' command.",
    )
512
    parser_solutions_sort.set_defaults(func=solutions_sort)
513

VIGNET Pierre's avatar
VIGNET Pierre committed
514
    ## subparser: Representation of the trajectories of MACs in a complete file.
515
    # Model file (xml : cadbiom language)
516
    # Solution file (mac_complete)
517
    parser_trajectories = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
518
        "solutions_2_graphs",
519
        help=solutions_2_graphs.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
520 521
        parents=[parent_parser],
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
522
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
523
    parser_trajectories._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
524
        "path",
525
        help="Complete solution file or directory with MAC solutions files "
526
        "(*mac_complete.txt files) generated with the 'solutions_search' command.",
VIGNET Pierre's avatar
VIGNET Pierre committed
527
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
528
    parser_trajectories._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
529 530 531 532 533 534
        "--output",
        action=ReadableDir,
        nargs="?",
        default="graphs/",
        help="Output directory for GraphML files.",
    )
535
    parser_trajectories.set_defaults(func=solutions_2_graphs)
536

VIGNET Pierre's avatar
VIGNET Pierre committed
537
    ## subparser: Decompilation of trajectories of MACs in a complete file/dir.
538
    # Model file (xml : cadbiom language)
539
    # Solution file (mac_complete)
540
    parser_queries_2_json = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
541
        "queries_2_json",
542
        help=queries_2_json.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
543 544
        parents=[parent_parser],
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
545
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
546
    parser_queries_2_json._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
547
        "path",
548
        help="Complete solution file or directory with MAC solutions files "
549
        "(*mac_complete.txt files) generated with the 'solutions_search' command.",
VIGNET Pierre's avatar
VIGNET Pierre committed
550
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
551
    parser_queries_2_json._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
552 553 554 555 556 557 558 559 560
        "--output",
        action=ReadableDir,
        nargs="?",
        default="decompiled_solutions/",
        help="Directory for newly created files.",
    )
    parser_queries_2_json.add_argument(
        "--no_conditions",
        action="store_true",
561
        help="Don't export conditions of transitions. This allows "
VIGNET Pierre's avatar
VIGNET Pierre committed
562 563 564
        "to have only places/entities that are used inside trajectories; "
        "thus, inhibitors nodes are not present in the json file",
    )
565
    parser_queries_2_json.set_defaults(func=queries_2_json)
566

VIGNET Pierre's avatar
VIGNET Pierre committed
567
    ## subparser: Make an interaction weighted graph based on the searched #####
568 569 570
    # molecule of interest
    # Require JSON decompilated solutions files
    parser_json_2_interaction_graph = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
571
        "json_2_interaction_graph",
572
        help=json_2_interaction_graph.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
573
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
574 575 576 577
    )
    parser_json_2_interaction_graph.add_argument(
        "molecules_of_interest",
        nargs="+",
578
        help="One or multiple molecule of interest to search in the trajectories"
VIGNET Pierre's avatar
VIGNET Pierre committed
579 580 581 582 583 584
        " of every solutions",
    )
    parser_json_2_interaction_graph.add_argument(
        "--path",
        nargs="?",
        default="decompiled_solutions/",
585
        help="JSON formated file containing all data from complete MAC files"
VIGNET Pierre's avatar
VIGNET Pierre committed
586 587 588 589 590 591 592 593 594
        "(*mac_complete files) generated with the 'queries_2_json' command.",
    )
    parser_json_2_interaction_graph.add_argument(
        "--output",
        action=ReadableDir,
        nargs="?",
        default="graphs/",
        help="Directory for the newly created file.",
    )
595 596
    parser_json_2_interaction_graph.set_defaults(func=json_2_interaction_graph)

VIGNET Pierre's avatar
VIGNET Pierre committed
597
    ## subparser: Common representation of the trajectories of MACs in a complete file.
598
    # Model file (xml : cadbiom language)
599
    # Solution file (mac_complete)
600
    parser_trajectories = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
601
        "queries_2_common_graph",
602
        help=queries_2_common_graph.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
603 604
        parents=[parent_parser],
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
605
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
606
    parser_trajectories._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
607
        "path",
608
        help="Complete solution file or directory with MAC solutions files "
609
        "(*mac_complete.txt files) generated with the 'solutions_search' command.",
VIGNET Pierre's avatar
VIGNET Pierre committed
610
    )
611
    # Outputs
VIGNET Pierre's avatar
VIGNET Pierre committed
612
    parser_trajectories._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
613 614
        "--graphs", action="store_true", help="Create a GraphML file for each MAC file."
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
615
    parser_trajectories._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
616 617
        "--csv",
        action="store_true",
618
        help="Create a CSV file containing a summary about places/entities "
VIGNET Pierre's avatar
VIGNET Pierre committed
619 620
        "of the solutions.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
621
    parser_trajectories._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
622 623
        "--json",
        action="store_true",
624
        help="Create a JSON formated file containing a summary about "
VIGNET Pierre's avatar
VIGNET Pierre committed
625 626
        "places/entities of the solutions.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
627
    parser_trajectories._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
628 629 630 631 632 633
        "--output",
        action=ReadableDir,
        nargs="?",
        default="graphs/",
        help="Output directory for GraphML files.",
    )
634
    parser_trajectories.set_defaults(func=queries_2_common_graph)
635

VIGNET Pierre's avatar
VIGNET Pierre committed
636
    ## subparser: Create a matrix of occurrences counting entities in the solutions.
VIGNET Pierre's avatar
VIGNET Pierre committed
637
    # Model file (xml : cadbiom language)
638
    # Solution file (mac.txt)
VIGNET Pierre's avatar
VIGNET Pierre committed
639
    parser_occurrences_matrix = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
640
        "queries_2_occcurrence_matrix",
641
        help=queries_2_occcurrence_matrix.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
642 643
        parents=[parent_parser],
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
644
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
645
    parser_occurrences_matrix._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
646
        "path",
VIGNET Pierre's avatar
VIGNET Pierre committed
647
        help="Directory with MAC solutions files "
648
        "(*mac.txt files) generated with the 'solutions_search' command.",
VIGNET Pierre's avatar
VIGNET Pierre committed
649
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
650
    parser_occurrences_matrix._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
651 652 653 654 655 656 657 658 659 660 661
        "--output",
        action=ReadableDir,
        nargs="?",
        default="./",
        help="Output directory for CSV files.",
    )
    parser_occurrences_matrix.add_argument(
        "--transpose_csv",
        action="store_true",
        help="Transpose the final matrix (switch columns and rows).",
    )
662
    parser_occurrences_matrix.set_defaults(func=queries_2_occcurrence_matrix)
VIGNET Pierre's avatar
VIGNET Pierre committed
663

664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    ## subparser: Clustermap for solutions in MAC files.
    # Solution file (mac.txt)
    # Output (csv + svg)
    parser_clustermap = subparsers.add_parser(
        "queries_2_clustermap",
        help=queries_2_clustermap.__doc__,
        formatter_class=custom_formatter,
    )
    input_output_clustermap_group = parser_clustermap.add_argument_group(title="I/O")
    input_output_clustermap_group.add_argument(
        "path",
        help="Directory with MAC solutions files or MAC file"
        "(*mac.txt files) generated with the 'solutions_search' command.",
    )
    input_output_clustermap_group.add_argument(
        "--output",
        action=ReadableDir,
        nargs="?",
        default="./",
        help="Output directory for CSV and SVG files.",
    )
    parser_clustermap.set_defaults(func=queries_2_clustermap)

VIGNET Pierre's avatar
VIGNET Pierre committed
687
    ## subparser: Merge solutions to a csv file ################################
688
    # Solution file (mac.txt)
689
    # Output (csv)
690
    parser_merge_macs = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
691
        "merge_macs", help=merge_macs.__doc__, formatter_class=custom_formatter
692
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
693 694 695 696 697
    parser_merge_macs.add_argument("solutions_directory", nargs="?", default="result/")
    parser_merge_macs.add_argument(
        "--output",
        nargs="?",
        default="result/",
698
        help="Directory for the CSV file merged_macs.csv; "
VIGNET Pierre's avatar
VIGNET Pierre committed
699 700
        "Structure: <Final property formula>;<mac>",
    )
701
    parser_merge_macs.set_defaults(func=merge_macs)
702

VIGNET Pierre's avatar
VIGNET Pierre committed
703
    ############################################################################
704
    ## Model-related commands ##################################################
VIGNET Pierre's avatar
VIGNET Pierre committed
705
    ############################################################################
706

707 708
    # subparser: Mapping of identifiers
    # output: CSV file
709
    parser_model_identifier_mapping = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
710
        "model_identifier_mapping",
711
        help=model_identifier_mapping.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
712 713
        parents=[parent_parser],
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
714
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
715
    group = parser_model_identifier_mapping._action_groups[2].add_mutually_exclusive_group(required=True)
VIGNET Pierre's avatar
VIGNET Pierre committed
716 717 718
    group.add_argument(
        "--external_file",
        help="File with 1 external identifiers to be mapped per line.",
719
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
720 721 722 723
    group.add_argument(
        "--external_identifiers",
        nargs="+",
        help="Multiple external identifiers to be mapped.",
724
    )
725
    parser_model_identifier_mapping.set_defaults(func=model_identifier_mapping)
726

VIGNET Pierre's avatar
VIGNET Pierre committed
727
    ## subparser: Model comparison #############################################
728
    # 2 models
729
    parser_model_comparison = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
730
        "model_comparison",
731
        help=model_comparison.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
732
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
733 734 735 736 737 738
    )
    parser_model_comparison.add_argument(
        "model_file_1", help="Cadbiom model (.bcx file)."
    )
    parser_model_comparison.add_argument(
        "model_file_2", help="Cadbiom model (.bcx file)."
739
    )
740
    # Export graphs for the 2 models; default: false
VIGNET Pierre's avatar
VIGNET Pierre committed
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
    parser_model_comparison.add_argument(
        "--graphs",
        action="store_true",
        help="Create two GraphML files from the given models.",
    )
    parser_model_comparison.add_argument(
        "--json", action="store_true", help="Create a summary dumped into a json file."
    )
    parser_model_comparison.add_argument(
        "--output",
        action=ReadableDir,
        nargs="?",
        default="graphs/",
        help="Directory for created graphs files.",
    )
756
    parser_model_comparison.set_defaults(func=model_comparison)
757

VIGNET Pierre's avatar
VIGNET Pierre committed
758
    ## subparser: Model info ###################################################
759
    # 1 model
760
    parser_model_info = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
761
        "model_info",
762
        help=model_info.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
763 764
        parents=[parent_parser],
        formatter_class=custom_formatter,
765
    )
766
    # Filters
VIGNET Pierre's avatar
VIGNET Pierre committed
767 768
    filters_group = parser_model_info.add_argument_group(title="Filters")
    group = filters_group.add_mutually_exclusive_group(required=True)
769
    # PS: Argparse doesn't allow to select a default value here
VIGNET Pierre's avatar
VIGNET Pierre committed
770 771 772
    group.add_argument(
        "--default",
        action="store_true",
773
        help="Display quick description of the model "
VIGNET Pierre's avatar
VIGNET Pierre committed
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
        "(Number of places/entities, transitions, entity types, locations)",
    )
    group.add_argument(
        "--all_entities",
        action="store_true",
        help="Retrieve data for all places/entities of the model.",
    )
    group.add_argument(
        "--boundaries",
        action="store_true",
        help="Retrieve data only for the frontier places/boundaries of the model.",
    )
    group.add_argument(
        "--genes",
        action="store_true",
        help="Retrieve data only for the genes in the model.",
    )
    group.add_argument(
        "--smallmolecules",
        action="store_true",
        help="Retrieve data only for the smallmolecules in the model.",
    )
796
    # Outputs
VIGNET Pierre's avatar
VIGNET Pierre committed
797
    parser_model_info._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
798 799
        "--csv",
        action="store_true",
800
        help="Create a CSV file containing data about previously filtered "
VIGNET Pierre's avatar
VIGNET Pierre committed
801 802
        "places/entities of the model.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
803
    parser_model_info._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
804 805
        "--json",
        action="store_true",
806
        help="Create a JSON formated file containing data about previously "
VIGNET Pierre's avatar
VIGNET Pierre committed
807 808 809 810
        "filtered places/entities of the model, and a full summary about the "
        "model itself (boundaries, transitions, events, entities locations,"
        " entities types).",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
811
    parser_model_info._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
812 813 814 815 816 817
        "--output",
        action=ReadableDir,
        nargs="?",
        default="./",
        help="Directory for newly created files.",
    )
818
    parser_model_info.set_defaults(func=model_info)
819

VIGNET Pierre's avatar
VIGNET Pierre committed
820
    ## subparser: Model graph ##################################################
821
    parser_model_graph = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
822
        "model_graph",
823
        help=model_graph.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
824 825
        parents=[parent_parser],
        formatter_class=custom_formatter,
826 827
    )
    # Additional data
VIGNET Pierre's avatar
VIGNET Pierre committed
828 829 830
    parser_model_graph.add_argument(
        "--centralities",
        action="store_true",
VIGNET Pierre's avatar
VIGNET Pierre committed
831
        help="Get centralities for each node of the graph "
VIGNET Pierre's avatar
VIGNET Pierre committed
832 833 834
        "(degree, in_degree, out_degree, closeness, betweenness). "
        "Works in conjunction with the ``--json`` option.",
    )
835
    # Outputs
VIGNET Pierre's avatar
VIGNET Pierre committed
836
    parser_model_graph._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
837 838
        "--graph",
        action="store_true",
839
        help="Translate the model into a GraphML formated file which can "
VIGNET Pierre's avatar
VIGNET Pierre committed
840 841
        "be opened in Cytoscape.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
842
    parser_model_graph._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
843 844
        "--json",
        action="store_true",
845
        help="Create a JSON formated file containing a summary of the graph "
VIGNET Pierre's avatar
VIGNET Pierre committed
846 847
        "based on the model.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
848
    parser_model_graph._action_groups[2].add_argument(