MAJ terminée. Nous sommes passés en version 14.6.2 . Pour consulter les "releases notes" associées c'est ici :

https://about.gitlab.com/releases/2022/01/11/security-release-gitlab-14-6-2-released/
https://about.gitlab.com/releases/2022/01/04/gitlab-14-6-1-released/

__main__.py 28.9 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Copyright (C) 2017  IRISA
#
# 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
35
from cadbiom_cmd.__init__ 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
184
185
186
187
@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
    # output, path
    queries_2_clustermap.queries_2_clustermap(**args)


188
def model_identifier_mapping(args):
189
190
191
192
193
194
195
    """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
196
    import models
197

198
    models.model_identifier_mapping(**args)
199
200


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

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

    # Module import
210
    import models
211

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


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

    # Module import
228
    import models
229

230
    models.model_info(**args)
231
232


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

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

    # Module import
242
    import models
243

244
    models.model_graph(**args)
245
246


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

    # Module import
    import solution_merge
253
254

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


VIGNET Pierre's avatar
VIGNET Pierre committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
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"],}
274
275
276
277
278
    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
279
280
281
282
283
284
285
286
    data = {
        "url": "http://cadbiom.genouest.org/",
        "_rcn": "cadbiom",
        "_rck": params["subcommand"],
        "idsite": 2,
        "apiv": 1,
        "send_image": 0,
        "rec": 1,
287
        "_id": "0000" + "".join(re.findall("..", "%0012x" % getnode()))[:16],
VIGNET Pierre's avatar
VIGNET Pierre committed
288
289
290
291
        "_cvar": json.dumps(cvar).encode("utf8"),
        "ua": platform,
    }
    data = urlencode(data).encode("ascii")
VIGNET Pierre's avatar
VIGNET Pierre committed
292
    page = urlopen("http://cadbiom.genouest.org/lytic.html", data=data).read()
293
    m = re.search('.*URL=(.*)">', page)
VIGNET Pierre's avatar
VIGNET Pierre committed
294
295
    if m:
        urlopen(m.group(1), data=data)
VIGNET Pierre's avatar
VIGNET Pierre committed
296
297


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


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

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

310
    if not os.path.isfile(path) and not os.path.isdir(path):
311
312
313
314
315
316
317
318
        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()


319
320
321
322
323
324
325
326
327
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):
328
329
            LOGGER.error("readable_file:<{}> is not a valid file".format(prospective_file))
            exit()
330
331
332
333

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


class ReadableDir(argparse.Action):
339
340
341
342
343
344
345
346
    """
    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):
347
348
            LOGGER.error("readable_dir:<{}> is not a valid path".format(prospective_dir))
            exit()
349
350
351
352

        if os.access(prospective_dir, os.R_OK):
            setattr(namespace, self.dest, prospective_dir)
        else:
353
354
            LOGGER.error("readable_dir:<{}> is not a readable dir".format(prospective_dir))
            exit()
355
356


357
358
359
360
361
362
363
364
class CustomFormatter(
    argparse.ArgumentDefaultsHelpFormatter,
    argparse.RawDescriptionHelpFormatter
):
    """Formatter used to print default values AND do not format raw text"""
    pass


365
def main():
366
    """Argument parser"""
367

VIGNET Pierre's avatar
VIGNET Pierre committed
368
    # Global parser configuration
369
    global parser
370
371
372
373
374
    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
375
    $ cadbiom_cmd solutions_search -h""",
376
    )
377
    # Default log level: debug
378
    parser.add_argument("-vv", "--verbose", nargs="?", default="info")
379
380
    parser.add_argument(
        "-V", "--version", action="version",
381
        version="%(prog)s " + __version__ + "; cadbiom library " + cadbiom_lib_version
382
383
    )

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

VIGNET Pierre's avatar
VIGNET Pierre committed
387
388
389
390
391
392
393
394
    # 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)

395
396
397
398
    # PS:
    # nargs='?': optional
    # Default actions: https://docs.python.org/dev/library/argparse.html#action

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

VIGNET Pierre's avatar
VIGNET Pierre committed
414
415
    $ 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""",
416
    )
417
    ## I/O
VIGNET Pierre's avatar
VIGNET Pierre committed
418
    parser_search._action_groups[2].add_argument(
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
        "--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',
443
444
    #    help="Solver will try to search all macs from 0 to the maximum of "
    #         "allowed steps.")
445
446
447
448
    # continue: False by default
    tune_group.add_argument(
        "--continue",
        action="store_true",
449
        help="Resume previous computations; if there is a mac file from a "
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
        "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,
479
        help="Property that will be part of the initial state of the model. "
480
481
482
483
484
485
486
        "In concrete terms, some entities can be activated by this "
        "mechanism without modifying the model.",
    )
    properties_group.add_argument(
        "--inv_prop",
        nargs="?",
        default=None,
487
        help="Invariant property that will always occur during the simulation. "
488
489
        "The given logical formula will be checked at each step of the simulation.",
    )
490

491
    parser_search.set_defaults(func=solutions_search)
492

VIGNET Pierre's avatar
VIGNET Pierre committed
493
    ############################################################################
494
    ## Solutions-related commands ##############################################
VIGNET Pierre's avatar
VIGNET Pierre committed
495
    ############################################################################
496

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

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

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

VIGNET Pierre's avatar
VIGNET Pierre committed
562
    ## subparser: Make an interaction weighted graph based on the searched #####
563
564
565
    # molecule of interest
    # Require JSON decompilated solutions files
    parser_json_2_interaction_graph = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
566
        "json_2_interaction_graph",
567
        help=json_2_interaction_graph.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
568
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
569
570
571
572
    )
    parser_json_2_interaction_graph.add_argument(
        "molecules_of_interest",
        nargs="+",
573
        help="One or multiple molecule of interest to search in the trajectories"
VIGNET Pierre's avatar
VIGNET Pierre committed
574
575
576
577
578
579
        " of every solutions",
    )
    parser_json_2_interaction_graph.add_argument(
        "--path",
        nargs="?",
        default="decompiled_solutions/",
580
        help="JSON formated file containing all data from complete MAC files"
VIGNET Pierre's avatar
VIGNET Pierre committed
581
582
583
584
585
586
587
588
589
        "(*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.",
    )
590
591
    parser_json_2_interaction_graph.set_defaults(func=json_2_interaction_graph)

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

VIGNET Pierre's avatar
VIGNET Pierre committed
631
    ## subparser: Create a matrix of occurrences counting entities in the solutions.
VIGNET Pierre's avatar
VIGNET Pierre committed
632
    # Model file (xml : cadbiom language)
633
    # Solution file (mac.txt)
VIGNET Pierre's avatar
VIGNET Pierre committed
634
    parser_occurrences_matrix = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
635
        "queries_2_occcurrence_matrix",
636
        help=queries_2_occcurrence_matrix.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
637
638
        parents=[parent_parser],
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
639
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
640
    parser_occurrences_matrix._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
641
        "path",
VIGNET Pierre's avatar
VIGNET Pierre committed
642
        help="Directory with MAC solutions files "
643
        "(*mac.txt files) generated with the 'solutions_search' command.",
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
647
648
649
650
651
652
653
654
655
656
        "--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).",
    )
657
    parser_occurrences_matrix.set_defaults(func=queries_2_occcurrence_matrix)
VIGNET Pierre's avatar
VIGNET Pierre committed
658

659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
    ## 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
682
    ## subparser: Merge solutions to a csv file ################################
683
    # Solution file (mac.txt)
684
    # Output (csv)
685
    parser_merge_macs = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
686
        "merge_macs",
687
        help=merge_macs.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
688
        formatter_class=custom_formatter,
689
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
690
691
692
693
694
    parser_merge_macs.add_argument("solutions_directory", nargs="?", default="result/")
    parser_merge_macs.add_argument(
        "--output",
        nargs="?",
        default="result/",
695
        help="Directory for the CSV file merged_macs.csv; "
VIGNET Pierre's avatar
VIGNET Pierre committed
696
697
        "Structure: <Final property formula>;<mac>",
    )
698
    parser_merge_macs.set_defaults(func=merge_macs)
699

VIGNET Pierre's avatar
VIGNET Pierre committed
700
    ############################################################################
701
    ## Model-related commands ##################################################
VIGNET Pierre's avatar
VIGNET Pierre committed
702
    ############################################################################
703

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

VIGNET Pierre's avatar
VIGNET Pierre committed
724
    ## subparser: Model comparison #############################################
725
    # 2 models
726
    parser_model_comparison = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
727
        "model_comparison",
728
        help=model_comparison.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
729
        formatter_class=custom_formatter,
VIGNET Pierre's avatar
VIGNET Pierre committed
730
731
732
733
734
735
    )
    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)."
736
    )
737
    # Export graphs for the 2 models; default: false
VIGNET Pierre's avatar
VIGNET Pierre committed
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
    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.",
    )
753
    parser_model_comparison.set_defaults(func=model_comparison)
754

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

VIGNET Pierre's avatar
VIGNET Pierre committed
817
    ## subparser: Model graph ##################################################
818
    parser_model_graph = subparsers.add_parser(
VIGNET Pierre's avatar
VIGNET Pierre committed
819
        "model_graph",
820
        help=model_graph.__doc__,
VIGNET Pierre's avatar
VIGNET Pierre committed
821
822
        parents=[parent_parser],
        formatter_class=custom_formatter,
823
824
    )
    # Additional data
VIGNET Pierre's avatar
VIGNET Pierre committed
825
826
827
    parser_model_graph.add_argument(
        "--centralities",
        action="store_true",
VIGNET Pierre's avatar
VIGNET Pierre committed
828
        help="Get centralities for each node of the graph "
VIGNET Pierre's avatar
VIGNET Pierre committed
829
830
831
        "(degree, in_degree, out_degree, closeness, betweenness). "
        "Works in conjunction with the ``--json`` option.",
    )
832
    # Outputs
VIGNET Pierre's avatar
VIGNET Pierre committed
833
    parser_model_graph._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
834
835
        "--graph",
        action="store_true",
836
        help="Translate the model into a GraphML formated file which can "
VIGNET Pierre's avatar
VIGNET Pierre committed
837
838
        "be opened in Cytoscape.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
839
    parser_model_graph._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
840
841
        "--json",
        action="store_true",
842
        help="Create a JSON formated file containing a summary of the graph "
VIGNET Pierre's avatar
VIGNET Pierre committed
843
844
        "based on the model.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
845
    parser_model_graph._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
846
847
        "--json_graph",
        action="store_true",
VIGNET Pierre's avatar
VIGNET Pierre committed
848
        help="Create a JSON formated file containing the graph based on the "
VIGNET Pierre's avatar
VIGNET Pierre committed
849
850
        "model, which can be opened by Web applications.",
    )
VIGNET Pierre's avatar
VIGNET Pierre committed
851
    parser_model_graph._action_groups[2].add_argument(
VIGNET Pierre's avatar
VIGNET Pierre committed
852
853
854
855
856
857
        "--output",
        action=ReadableDir,
        nargs="?",
        default="graphs/",
        help="Directory for newly created files.",
    )
858
859
    parser_model_graph.set_defaults(func=model_graph)

860
861
    # Workaround for sphinx-argparse module that require the object parser
    # before the call of parse_args()
VIGNET Pierre's avatar
VIGNET Pierre committed
862
    if "html" in sys.argv:
863
        return parser
864

865
866
    # get program args and launch associated command
    args = parser.parse_args()
867

868
    # Set log level
VIGNET Pierre's avatar
VIGNET Pierre committed
869
    cm.log_level(vars(args)["verbose"])
870

871
872
873
874
875
    # Take argparse arguments and put them in a standard dict
    params = args_to_param(args)

    # Ultimate check of the presence of the directories
    know_directory_variables = ("output", "path", "solutions_directory")
VIGNET Pierre's avatar
VIGNET Pierre committed
876
877
878
879
880
    [
        ensure_dir_presence(params[var])
        for var in know_directory_variables
        if params.get(var, None)
    ]
881

VIGNET Pierre's avatar
VIGNET Pierre committed
882
883
884
885
886
887
888
889
    # Analytics
    def anal():
        try:
            analytics(params)
        except:
            pass

    from threading import Thread
VIGNET Pierre's avatar
VIGNET Pierre committed
890

VIGNET Pierre's avatar
VIGNET Pierre committed
891
892
893
    thread = Thread(target=anal)
    thread.start()

894
895
    # Launch associated command
    args.func(params)
VIGNET Pierre's avatar
VIGNET Pierre committed
896
897

    thread.join()