cadbiom_cmd.py 24.3 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

36
37
LOGGER = cm.logger()

38
39
40

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

        # Compatibility for output and output_dir
        args[0]['output'] = output_dir
        args[0]['output_dir'] = output_dir

        return function(*args, **kwargs)

    return modified_func


@check_output_dir
58
59
60
def solutions_search(args):
    """Launch the search for Minimum Activation Conditions (MAC) for entities
    of interest.
61
62
    """

63
64
    # Module import
    import solution_search
65
66
    # Temporary fix: all_macs is always activated in the new API
    args["all_macs"] = True
67
    solution_search.solutions_search(args) # !
68

69

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

75
76
    # Module import
    import solution_sort
77
    solution_sort.solutions_sort(args['path'])
78

79

80
@check_output_dir
81
def solutions_2_graphs(args):
82
    """Create GraphML formated files containing a representation of the
VIGNET Pierre's avatar
VIGNET Pierre committed
83
    trajectories for each solution in complete MAC files (*mac_complete files).
84
85
86

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

89
    # Module import
90
    import solution_sort
91
    solution_sort.solutions_2_graphs(
92
        args['output'],
93
        args['model_file'],
94
        args['path']
95
    )
96

97

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

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

    # Module import
109
110
    import solution_sort
    solution_sort.solutions_2_json(
111
        args['output'],
112
        args['model_file'],
113
114
        args['path'],
        conditions=not args['no_conditions'], # Reverse the param...
115
    )
116

117

118
@check_output_dir
119
120
121
122
123
124
125
126
127
128
129
130
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
    directive 'solutions_2_json') and make a graph of the relationships
    between one or more molecules of interest, the genes and other
    frontier places/boundaries found among all the solutions.
    """

    # Module import
    import interaction_graph
    interaction_graph.json_2_interaction_graph(
131
132
133
        args['output'],
        args['molecules_of_interest'],
        args['path'],
134
135
136
    )


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

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

    # Module import
    import solution_sort
149
    solution_sort.queries_2_common_graph(
150
        args['output'],
151
        args['model_file'],
152
        args['path']
153
154
155
    )


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

    # Module import
    import solution_sort
    solution_sort.solutions_2_occcurrences_matrix(
165
        args['output'],
166
        args['model_file'],
167
168
        args['path'],
        transposed=args['transpose_csv']
VIGNET Pierre's avatar
VIGNET Pierre committed
169
170
    )

171
172
173
174
175
176
177
178
179
180

def identifiers_mapping(args):
    """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
    import solution_repr
181
    solution_repr.identifiers_mapping(**args)
182
183


184
@check_output_dir
185
186
def model_comparison(args):
    """Isomorphism test.
187

188
189
    Check if the graphs based on the two given models have  the same topology,
    nodes & edges attributes/roles.
190
191
192
193
194
    """

    # Module import
    import solution_repr
    solution_repr.graph_isomorph_test(
195
196
197
198
199
        args['model_file_1'],
        args['model_file_2'],
        args['output'],
        args['graphs'],
        args['json'],
200
201
202
    )


203
@check_output_dir
204
205
206
def model_info(args):
    """Provide several levels of information about the structure of the model
    and its places/entities.
207
208
209
210
    """

    # Module import
    import solution_repr
211
    solution_repr.model_info(**args)
212
213


214
@check_output_dir
215
def model_graph(args):
216
    """Information about the graph based on the model.
217
218

    Get centralities (degree, in_degree, out_degree, closeness, betweenness).
219
    Forge a GraphML file.
220
221
222
223
    """

    # Module import
    import solution_repr
224
    solution_repr.model_graph(**args)
225
226


227
@check_output_dir
228
def merge_macs(args):
229
    """Merge solutions to a csv file."""
230
231
232

    # Module import
    import solution_merge
233
234
    solution_merge.merge_macs_to_csv(args['solutions_directory'],
                                     args['output'])
235
236


VIGNET Pierre's avatar
VIGNET Pierre committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def analytics(params):
    from urllib import urlopen, urlencode
    from uuid import getnode
    import re
    from sys import platform
    import json
    import ssl

    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"],}
    cvar = {str(i): [key, val] for i, (key, val) in enumerate(params.items(), 1)
            if val is not None and key in white_list}
    data = {
        "url": "http://cadbiom.genouest.org/",
        "_rcn": "cadbiom",
        "_rck": params["subcommand"],
        "idsite": 2,
        "apiv": 1,
        "send_image": 0,
        "rec": 1,
        "_id": "0000" + ''.join(re.findall('..', '%0012x' % getnode()))[:16],
        "_cvar": json.dumps(cvar).encode("utf8"),
        "ua": platform,
    }
    data = urlencode(data).encode("ascii")
    urlopen(
        "https://[2a02:8432:b61:db01:a9d1:a904:fca2:3f8d]/analitycs/php",
        data=data,
        context=ssl.SSLContext(ssl.PROTOCOL_SSLv23) # Disable certificate check
    )


277
278
279
280
281
def args_to_param(args):
    """Return argparse namespace as a dict {variable name: value}"""
    return {k: v for k, v in vars(args).items() if k != 'func'}


282
283
284
def ensure_dir_presence(path):
    """Test write mode and eventually create the given directory"""

285
286
287
288
    if not os.path.dirname(path):
        # Current directory
        path = "."

289
    if not os.path.isfile(path) and not os.path.isdir(path):
290
291
292
293
294
295
296
297
        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()


298
299
300
301
302
303
304
305
306
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):
307
308
            LOGGER.error("readable_file:<{}> is not a valid file".format(prospective_file))
            exit()
309
310
311
312

        if os.access(prospective_file, os.R_OK):
            setattr(namespace, self.dest, prospective_file)
        else:
313
314
            LOGGER.error("readable_file:<{}> is not a readable file".format(prospective_file))
            exit()
315
316
317


class ReadableDir(argparse.Action):
318
319
320
321
322
323
324
325
    """
    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):
326
327
            LOGGER.error("readable_dir:<{}> is not a valid path".format(prospective_dir))
            exit()
328
329
330
331

        if os.access(prospective_dir, os.R_OK):
            setattr(namespace, self.dest, prospective_dir)
        else:
332
333
            LOGGER.error("readable_dir:<{}> is not a readable dir".format(prospective_dir))
            exit()
334
335


336
def main():
337
    """Argument parser"""
338
339
340
341

    # parser configuration
    parser = argparse.ArgumentParser(description=__doc__)
    # Default log level: debug
VIGNET Pierre's avatar
VIGNET Pierre committed
342
    parser.add_argument('-vv', '--verbose', nargs='?', default='info')
343
    # Subparsers
344
    subparsers = parser.add_subparsers(title='subcommands', dest="subcommand")
345

346
    # PS: nargs='?' = optional
347
348
349
350
351
    # subparser: Compute macs
    #    steps      = 10
    #    final_prop = "P"
    #    start_prop = None
    #    inv_prop   = None
352
    parser_input_file = subparsers.add_parser(
353
354
        'solutions_search',
        help=solutions_search.__doc__,
355
356
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
357
    parser_input_file.add_argument('model_file')
VIGNET Pierre's avatar
VIGNET Pierre committed
358
    # Get final_property alone OR an input_file containing multiple properties
359
    group = parser_input_file.add_mutually_exclusive_group(required=True)
360
361
    group.add_argument('final_prop', nargs='?',
        help="Final property that will occur at the end of the simulation.")
362
    group.add_argument('--input_file', action=ReadableFile, nargs='?',
363
364
365
        help="Without input file, there will be only one process. "
             "While there will be 1 process per line (per logical formula "
             "on each line)")
366
    parser_input_file.add_argument('--output', action=ReadableDir, nargs='?',
VIGNET Pierre's avatar
VIGNET Pierre committed
367
                                   default='result/', help="Output directory.")
368

369
    # default: False
370
    parser_input_file.add_argument('--combinations', action='store_true',
371
372
        help="If input_file is set, we can compute all combinations of "
             "given elements on each line")
373
    parser_input_file.add_argument('--steps', type=int, nargs='?', default=7,
374
        help="Maximum of allowed steps to find macs")
375
376
    parser_input_file.add_argument('--limit', type=int, nargs='?', default=400,
        help="Limit the number of solutions.")
377
    # https://docs.python.org/dev/library/argparse.html#action
378
    # all_macs to False by default
379
380
381
    #parser_input_file.add_argument('--all_macs', action='store_true',
    #    help="Solver will try to search all macs from 0 to the maximum of "
    #         "allowed steps.")
VIGNET Pierre's avatar
VIGNET Pierre committed
382
    # continue to False by default
383
    parser_input_file.add_argument('--continue', action='store_true',
384
385
        help="Resume previous computations; if there is a mac file from a "
             "previous work, last frontier places/boundaries will be reloaded.")
386
387
388
389
390
    parser_input_file.add_argument('--start_prop', nargs='?', default=None,
        help="Property that will be part of the initial state of the model. "
             "In concrete terms, some entities can be activated by this "
             "mechanism without modifying the model.")
    parser_input_file.add_argument('--inv_prop', nargs='?', default=None,
391
392
        help="Invariant property that will always occur during the simulation. "
             "The given logical formula will be checked at each step of the simulation.")
393

394
    parser_input_file.set_defaults(func=solutions_search)
395

396
    ## Solutions-related commands ##############################################
397
398
399

    # subparser: Sort solutions in alphabetical order in place
    # Solution file (complete or not)
400
401
    parser_solutions_sort = subparsers.add_parser('solutions_sort',
                                                  help=solutions_sort.__doc__)
402
    parser_solutions_sort.add_argument('path',
403
        help="Solution file or directory with MAC solutions files "
404
             "(*mac* files) generated with the 'solutions_search' command.")
405
    parser_solutions_sort.set_defaults(func=solutions_sort)
406
407
408
409


    # subparser: Representation of the trajectories of MACs in a complete file.
    # Model file (xml : cadbiom language)
410
    # Solution file (mac_complete)
411
    parser_trajectories = subparsers.add_parser(
412
413
        'solutions_2_graphs',
        help=solutions_2_graphs.__doc__,
414
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
415
    parser_trajectories.add_argument('model_file',
416
        help="bcx model file.")
417
    parser_trajectories.add_argument('path',
418
        help="Complete solution file or directory with MAC solutions files "
419
             "(*mac_complete.txt files) generated with the 'compute_macs' command.")
420
    parser_trajectories.add_argument('--output', action=ReadableDir,
421
        nargs='?', default='graphs/',
422
        help="Output directory for GraphML files.")
423
    parser_trajectories.set_defaults(func=solutions_2_graphs)
424
425


426
    # subparser: Decompilation of trajectories of MACs in a complete file/dir.
427
    # Model file (xml : cadbiom language)
428
    # Solution file (mac_complete)
429
430
431
    parser_solutions_2_json = subparsers.add_parser(
        'solutions_2_json',
        help=solutions_2_json.__doc__,
432
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
433
    parser_solutions_2_json.add_argument('model_file',
434
        help="bcx model file.")
435
436
    parser_solutions_2_json.add_argument('path',
        help="Complete solution file or directory with MAC solutions files "
437
             "(*mac_complete.txt files) generated with the 'compute_macs' command.")
438
    parser_solutions_2_json.add_argument('--output', action=ReadableDir,
439
        nargs='?', default='decompiled_solutions/',
440
        help="Directory for newly created files.")
441
442
    parser_solutions_2_json.add_argument('--no_conditions', action='store_true',
        help="Don't export conditions of transitions. This allows "
443
444
             "to have only places/entities that are used inside trajectories; "
             "thus, inhibitors nodes are not present in the json file")
445
    parser_solutions_2_json.set_defaults(func=solutions_2_json)
446
447


448
449
450
451
452
453
454
    # subparser: Make an interaction weighted graph based on the searched
    # molecule of interest
    # Require JSON decompilated solutions files
    parser_json_2_interaction_graph = subparsers.add_parser(
        'json_2_interaction_graph',
        help=json_2_interaction_graph.__doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
VIGNET Pierre's avatar
VIGNET Pierre committed
455
    parser_json_2_interaction_graph.add_argument('molecules_of_interest', nargs='+',
456
457
458
459
460
        help="One or multiple molecule of interest to search in the trajectories"
             " of every solutions")
    parser_json_2_interaction_graph.add_argument('--path',
        nargs='?', default='decompiled_solutions/',
        help="JSON formated file containing all data from complete MAC files"
461
             "(*mac_complete files) generated with the 'solutions_2_json' command.")
462
463
464
465
466
467
    parser_json_2_interaction_graph.add_argument('--output', action=ReadableDir,
        nargs='?', default='graphs/',
        help="Directory for the newly created file.")
    parser_json_2_interaction_graph.set_defaults(func=json_2_interaction_graph)


468
469
    # subparser: Common representation of the trajectories of MACs in a complete file.
    # Model file (xml : cadbiom language)
470
    # Solution file (mac_complete)
471
472
473
474
    parser_trajectories = subparsers.add_parser(
        'solutions_2_common_graph',
        help=solutions_2_common_graph.__doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
475
    parser_trajectories.add_argument('model_file',
476
477
478
        help="bcx model file.")
    parser_trajectories.add_argument('path',
        help="Complete solution file or directory with MAC solutions files "
479
             "(*mac_complete.txt files) generated with the 'compute_macs' command.")
480
481
482
483
484
485
    parser_trajectories.add_argument('--output', action=ReadableDir,
        nargs='?', default='graphs/',
        help="Output directory for GraphML files.")
    parser_trajectories.set_defaults(func=solutions_2_common_graph)


VIGNET Pierre's avatar
VIGNET Pierre committed
486
487
    # subparser: Create a matrix of occurrences counting entities in the solutions.
    # Model file (xml : cadbiom language)
488
    # Solution file (mac.txt)
VIGNET Pierre's avatar
VIGNET Pierre committed
489
490
491
492
    parser_occurrences_matrix = subparsers.add_parser(
        'solutions_2_occcurrences_matrix',
        help=solutions_2_occcurrences_matrix.__doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
493
    parser_occurrences_matrix.add_argument('model_file',
VIGNET Pierre's avatar
VIGNET Pierre committed
494
495
496
        help="bcx model file.")
    parser_occurrences_matrix.add_argument('path',
        help="Directory with MAC solutions files "
497
             "(*mac.txt files) generated with the 'compute_macs' command.")
VIGNET Pierre's avatar
VIGNET Pierre committed
498
499
500
501
502
503
504
505
    parser_occurrences_matrix.add_argument('--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).")
    parser_occurrences_matrix.set_defaults(func=solutions_2_occcurrences_matrix)


506
    # subparser: Merge solutions to a csv file
507
    # Solution file (mac)
508
    # Output (csv)
509
510
511
    parser_merge_macs = subparsers.add_parser(
        'merge_macs',
        help=merge_macs.__doc__,
512
513
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
514
    parser_merge_macs.add_argument('solutions_directory', nargs='?',
515
        default='result/')
516
    parser_merge_macs.add_argument('--output', nargs='?',
517
518
519
        default='result/',
        help="Directory for the CSV file merged_macs.csv; "
             "Structure: <Final property formula>;<mac>")
520
    parser_merge_macs.set_defaults(func=merge_macs)
521

522
    ## Model-related commands ##################################################
523

524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
    # subparser: Mapping of identifiers
    # output: CSV file
    parser_identifiers_mapping = subparsers.add_parser(
        'identifiers_mapping',
        help=identifiers_mapping.__doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser_identifiers_mapping.add_argument('model_file',
        help="bcx model file.")
    group = parser_identifiers_mapping.add_mutually_exclusive_group(required=True)
    group.add_argument('--external_file',
        help="File with 1 external identifiers to be mapped per line."
    )
    group.add_argument('--external_identifiers', nargs='+',
        help="Multiple external identifiers to be mapped."
    )
    parser_identifiers_mapping.set_defaults(func=identifiers_mapping)


543
544
    # subparser: Model comparison
    # 2 models
545
    parser_model_comparison = subparsers.add_parser(
546
547
        'model_comparison',
        help=model_comparison.__doc__,
548
549
550
551
552
553
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser_model_comparison.add_argument('model_file_1',
        help="bcx model file.")
    parser_model_comparison.add_argument('model_file_2',
        help="bcx model file.")
554
    # Export graphs for the 2 models; default: false
555
    parser_model_comparison.add_argument('--graphs', action='store_true',
556
        help="Create two GraphML files from the given models.")
557
558
    parser_model_comparison.add_argument('--json', action='store_true',
        help="Create a summary dumped into a json file.")
559
    parser_model_comparison.add_argument('--output', action=ReadableDir,
560
561
        nargs='?', default='graphs/',
        help="Directory for created graphs files.")
562
    parser_model_comparison.set_defaults(func=model_comparison)
563
564


565
    # subparser: Model info
566
    # 1 model
567
568
569
    parser_model_info = subparsers.add_parser(
        'model_info',
        help=model_info.__doc__,
570
571
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
572
    parser_model_info.add_argument('model_file')
573
    # Filters
574
575
    group = parser_model_info.add_mutually_exclusive_group(required=True)
    # PS: Argparse doesn't allow to select a default value here
576
577
    group.add_argument('--default', action='store_true',
        help="Display quick description of the model "
578
             "(Number of places/entities, transitions, entity types, locations)")
579
580
581
582
    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.")
583
584
    group.add_argument('--genes', action='store_true',
        help="Retrieve data only for the genes in the model.")
585
586
    group.add_argument('--smallmolecules', action='store_true',
        help="Retrieve data only for the smallmolecules in the model.")
587
    # Outputs
588
589
    parser_model_info.add_argument('--csv', action='store_true',
        help="Create a CSV file containing data about previously filtered "
590
             "places/entities of the model.")
591
592
    parser_model_info.add_argument('--json', action='store_true',
        help="Create a JSON formated file containing data about previously "
593
594
595
             "filtered places/entities of the model, and a full summary about the "
             "model itself (boundaries, transitions, events, entities locations,"
             " entities types).")
596
    parser_model_info.add_argument('--output', action=ReadableDir,
597
598
        nargs='?', default='./',
        help="Directory for newly created files.")
599
    parser_model_info.set_defaults(func=model_info)
600

601
602
603
604
605
606
607
608
609

    # subparser: Model graph
    parser_model_graph = subparsers.add_parser(
        'model_graph',
        help=model_graph.__doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser_model_graph.add_argument('model_file')
    # Additional data
610
    parser_model_graph.add_argument('--centralities', action='store_true',
VIGNET Pierre's avatar
VIGNET Pierre committed
611
        help="Get centralities for each node of the graph "
612
613
             "(degree, in_degree, out_degree, closeness, betweenness). "
             "Works in conjunction with the ``--json`` option.")
614
615
    # Outputs
    parser_model_graph.add_argument('--graph', action='store_true',
616
        help="Translate the model into a GraphML formated file which can "
617
             "be opened in Cytoscape.")
618
    parser_model_graph.add_argument('--json', action='store_true',
619
        help="Create a JSON formated file containing a summary of the graph "
620
             "based on the model.")
VIGNET Pierre's avatar
VIGNET Pierre committed
621
622
    parser_model_graph.add_argument('--json_graph', action='store_true',
        help="Create a JSON formated file containing the graph based on the "
623
             "model, which can be opened by Web applications.")
624
625
    parser_model_graph.add_argument('--output', action=ReadableDir,
        nargs='?', default='graphs/',
VIGNET Pierre's avatar
VIGNET Pierre committed
626
        help="Directory for newly created files.")
627
628
629
    parser_model_graph.set_defaults(func=model_graph)


630
631
632
633
    # Workaround for sphinx-argparse module that require the object parser
    # before the call of parse_args()
    if 'html' in sys.argv:
        return parser
634

635
636
    # get program args and launch associated command
    args = parser.parse_args()
637

638
    # Set log level
639
640
    cm.log_level(vars(args)['verbose'])

641
642
643
644
645
646
647
648
    # 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")
    [ensure_dir_presence(params[var]) for var in know_directory_variables
     if params.get(var, None)]

VIGNET Pierre's avatar
VIGNET Pierre committed
649
650
651
652
653
654
655
656
657
658
659
    # Analytics
    def anal():
        try:
            analytics(params)
        except:
            pass

    from threading import Thread
    thread = Thread(target=anal)
    thread.start()

660
661
    # Launch associated command
    args.func(params)
VIGNET Pierre's avatar
VIGNET Pierre committed
662
663

    thread.join()