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

32
# Custom imports
33
import cadbiom.commons as cm
34

35
36
LOGGER = cm.logger()

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

def check_output_dir(function):
    """Decorator used process some arguments from argparse"""

    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
56
57
58
def solutions_search(args):
    """Launch the search for Minimum Activation Conditions (MAC) for entities
    of interest.
59
60
    """

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

67

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

73
74
    # Module import
    import solution_sort
75
    solution_sort.solutions_sort(args['path'])
76

77

78
@check_output_dir
79
def solutions_2_graphs(args):
80
    """Create GraphML formated files containing a representation of the
81
    trajectories for every solution in complete MAC files (*mac_complete files).
82
83
84

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

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

95

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

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

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

115

116
@check_output_dir
117
118
119
120
121
122
123
124
125
126
127
128
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(
129
130
131
        args['output'],
        args['molecules_of_interest'],
        args['path'],
132
133
134
    )


135
@check_output_dir
136
137
def solutions_2_common_graph(args):
    """Create a GraphML formated file containing a representation of **all**
138
    trajectories for **all** solutions in complete MAC files (*mac_complete files).
139
140
141
142
143
144
145
146

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

    # Module import
    import solution_sort
    solution_sort.solutions_2_common_graph(
147
        args['output'],
148
        args['model_file'],
149
        args['path']
150
151
152
    )


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

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

168
169
170
171
172
173
174
175
176
177

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
178
    solution_repr.identifiers_mapping(**args)
179
180


181
@check_output_dir
182
183
def model_comparison(args):
    """Isomorphism test.
184

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

    # Module import
    import solution_repr
    solution_repr.graph_isomorph_test(
192
193
194
195
196
        args['model_file_1'],
        args['model_file_2'],
        args['output'],
        args['graphs'],
        args['json'],
197
198
199
    )


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

    # Module import
    import solution_repr
208
    solution_repr.model_info(**args)
209
210


211
@check_output_dir
212
def model_graph(args):
213
    """Information about the graph based on the model.
214
215

    Get centralities (degree, in_degree, out_degree, closeness, betweenness).
216
    Forge a GraphML file.
217
218
219
220
    """

    # Module import
    import solution_repr
221
    solution_repr.model_graph(**args)
222
223


224
@check_output_dir
225
def merge_macs(args):
226
    """Merge solutions to a csv file."""
227
228
229

    # Module import
    import solution_merge
230
231
    solution_merge.merge_macs_to_csv(args['solutions_directory'],
                                     args['output'])
232
233


VIGNET Pierre's avatar
VIGNET Pierre committed
234
235
236
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
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
    )


274
275
276
277
278
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'}


279
280
281
def ensure_dir_presence(path):
    """Test write mode and eventually create the given directory"""

282
283
284
285
    if not os.path.dirname(path):
        # Current directory
        path = "."

286
287
288
289
290
291
292
293
294
    if not os.path.isdir(path):
        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()


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

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


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

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


333
def main():
334
    """Argument parser"""
335
336
337
338

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

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

366
    # default: False
367
    parser_input_file.add_argument('--combinations', action='store_true',
368
369
        help="If input_file is set, we can compute all combinations of "
             "given elements on each line")
370
    parser_input_file.add_argument('--steps', type=int, nargs='?', default=7,
371
        help="Maximum of allowed steps to find macs")
372
373
    parser_input_file.add_argument('--limit', type=int, nargs='?', default=400,
        help="Limit the number of solutions.")
374
    # https://docs.python.org/dev/library/argparse.html#action
375
    # all_macs to False by default
376
377
378
    #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
379
    # continue to False by default
380
    parser_input_file.add_argument('--continue', action='store_true',
381
382
        help="Resume previous computations; if there is a mac file from a "
             "previous work, last frontier places/boundaries will be reloaded.")
383
384
385
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,
        help="Invariant property that will never occur during the simulation. "
             "The given logical formula will be enclose by a logical not() and "
             "will be checked at each step of the simulation.")
391

392
    parser_input_file.set_defaults(func=solutions_search)
393

394
    ## Solutions-related commands ##############################################
395
396
397

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


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


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


446
447
448
449
450
451
452
    # 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
453
    parser_json_2_interaction_graph.add_argument('molecules_of_interest', nargs='+',
454
455
456
457
458
        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"
459
             "(*mac_complete files) generated with the 'solutions_2_json' command.")
460
461
462
463
464
465
    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)


466
467
    # subparser: Common representation of the trajectories of MACs in a complete file.
    # Model file (xml : cadbiom language)
468
    # Solution file (mac_complete)
469
470
471
472
    parser_trajectories = subparsers.add_parser(
        'solutions_2_common_graph',
        help=solutions_2_common_graph.__doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
473
    parser_trajectories.add_argument('model_file',
474
475
476
        help="bcx model file.")
    parser_trajectories.add_argument('path',
        help="Complete solution file or directory with MAC solutions files "
477
             "(*mac_complete.txt files) generated with the 'compute_macs' command.")
478
479
480
481
482
483
    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
484
485
    # subparser: Create a matrix of occurrences counting entities in the solutions.
    # Model file (xml : cadbiom language)
486
    # Solution file (mac.txt)
VIGNET Pierre's avatar
VIGNET Pierre committed
487
488
489
490
    parser_occurrences_matrix = subparsers.add_parser(
        'solutions_2_occcurrences_matrix',
        help=solutions_2_occcurrences_matrix.__doc__,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
491
    parser_occurrences_matrix.add_argument('model_file',
VIGNET Pierre's avatar
VIGNET Pierre committed
492
493
494
        help="bcx model file.")
    parser_occurrences_matrix.add_argument('path',
        help="Directory with MAC solutions files "
495
             "(*mac.txt files) generated with the 'compute_macs' command.")
VIGNET Pierre's avatar
VIGNET Pierre committed
496
497
498
499
500
501
502
503
    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)


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

520
    ## Model-related commands ##################################################
521

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
    # 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)


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


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

599
600
601
602
603
604
605
606
607

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


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

633
634
    # get program args and launch associated command
    args = parser.parse_args()
635

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

639
640
641
642
643
644
645
646
    # 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
647
648
649
650
651
652
653
654
655
656
657
    # Analytics
    def anal():
        try:
            analytics(params)
        except:
            pass

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

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

    thread.join()