roc-film 1.13.5__py3-none-any.whl → 1.14.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. roc/__init__.py +2 -1
  2. roc/film/__init__.py +2 -2
  3. roc/film/commands.py +372 -323
  4. roc/film/config/__init__.py +0 -1
  5. roc/film/constants.py +101 -65
  6. roc/film/descriptor.json +127 -96
  7. roc/film/exceptions.py +28 -27
  8. roc/film/tasks/__init__.py +16 -16
  9. roc/film/tasks/cat_solo_hk.py +86 -74
  10. roc/film/tasks/cdf_postpro.py +438 -309
  11. roc/film/tasks/check_dds.py +39 -45
  12. roc/film/tasks/db_to_anc_bia_sweep_table.py +381 -0
  13. roc/film/tasks/dds_to_l0.py +232 -180
  14. roc/film/tasks/export_solo_coord.py +147 -0
  15. roc/film/tasks/file_handler.py +91 -75
  16. roc/film/tasks/l0_to_hk.py +117 -103
  17. roc/film/tasks/l0_to_l1_bia_current.py +38 -30
  18. roc/film/tasks/l0_to_l1_bia_sweep.py +417 -329
  19. roc/film/tasks/l0_to_l1_sbm.py +250 -208
  20. roc/film/tasks/l0_to_l1_surv.py +185 -130
  21. roc/film/tasks/make_daily_tm.py +40 -37
  22. roc/film/tasks/merge_tcreport.py +77 -71
  23. roc/film/tasks/merge_tmraw.py +101 -88
  24. roc/film/tasks/parse_dds_xml.py +21 -20
  25. roc/film/tasks/set_l0_utc.py +51 -49
  26. roc/film/tests/cdf_compare.py +565 -0
  27. roc/film/tests/hdf5_compare.py +84 -62
  28. roc/film/tests/test_dds_to_l0.py +93 -51
  29. roc/film/tests/test_dds_to_tc.py +8 -11
  30. roc/film/tests/test_dds_to_tm.py +8 -10
  31. roc/film/tests/test_film.py +161 -116
  32. roc/film/tests/test_l0_to_hk.py +64 -36
  33. roc/film/tests/test_l0_to_l1_bia.py +10 -14
  34. roc/film/tests/test_l0_to_l1_sbm.py +14 -19
  35. roc/film/tests/test_l0_to_l1_surv.py +68 -41
  36. roc/film/tests/test_metadata.py +21 -20
  37. roc/film/tests/tests.py +743 -396
  38. roc/film/tools/__init__.py +5 -5
  39. roc/film/tools/dataset_tasks.py +34 -2
  40. roc/film/tools/file_helpers.py +390 -269
  41. roc/film/tools/l0.py +402 -324
  42. roc/film/tools/metadata.py +147 -127
  43. roc/film/tools/skeleton.py +12 -17
  44. roc/film/tools/tools.py +109 -92
  45. roc/film/tools/xlsx2skt.py +161 -139
  46. {roc_film-1.13.5.dist-info → roc_film-1.14.0.dist-info}/LICENSE +127 -125
  47. roc_film-1.14.0.dist-info/METADATA +60 -0
  48. roc_film-1.14.0.dist-info/RECORD +50 -0
  49. {roc_film-1.13.5.dist-info → roc_film-1.14.0.dist-info}/WHEEL +1 -1
  50. roc/film/tasks/l0_to_anc_bia_sweep_table.py +0 -348
  51. roc_film-1.13.5.dist-info/METADATA +0 -120
  52. roc_film-1.13.5.dist-info/RECORD +0 -48
@@ -0,0 +1,565 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """Maser4py - Compare 2 CDF files module"""
5
+
6
+ import logging
7
+ import os
8
+ import os.path
9
+ import sys
10
+ from pprint import pformat
11
+
12
+ import numpy as np
13
+ from spacepy.pycdf import CDF
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ # Checking file
19
+ def checking_file_exist(cdf_file):
20
+ if not os.path.isfile(cdf_file):
21
+ raise FileNotFoundError("%s : does not exist !" % cdf_file)
22
+ if not os.access(cdf_file, os.R_OK):
23
+ raise IOError("%s : is not readable !" % cdf_file)
24
+
25
+
26
+ #
27
+ def get_keys_and_attributes(cdf_filepath):
28
+ """
29
+ Get the list data keys (zVars) and global attributes (gAttrs)
30
+
31
+ :param cdf_filepath:
32
+ :return:
33
+ """
34
+ with CDF(cdf_filepath) as cdf_file:
35
+ return list(cdf_file.keys()), cdf_file.attrs.copy()
36
+
37
+
38
+ def list_differences(a, b):
39
+ """
40
+ Find list differences
41
+
42
+ :param a: first list
43
+ :param b: second list
44
+ :return: a tuple containing the differences
45
+ """
46
+ return [x for x in a if x not in b], [x for x in b if x not in a]
47
+
48
+
49
+ # Listing all items
50
+ def list_elements(liste):
51
+ n = len(liste)
52
+ i = 0
53
+ while i < n:
54
+ # logger.debug(" %s", liste[i])
55
+ i += 1
56
+
57
+
58
+ # Deleting a dictionnary's key
59
+ def delete_key(dict, key_to_remove):
60
+ if key_to_remove in dict:
61
+ del dict[key_to_remove]
62
+ else:
63
+ logger.debug("Key to remove '%s' does not exist !", key_to_remove)
64
+ return dict
65
+
66
+
67
+ # Getting a given vAttr's not matched keys
68
+ def get_v_att_keys_diff(field1, field2):
69
+ """
70
+ get the differences between v_att keys of field 1 and field 2
71
+
72
+ :param field1:
73
+ :param field2:
74
+ :return:
75
+ """
76
+
77
+ # convert v_att (class 'spacepy.cdf.zAttrList') of cdf files to sorted
78
+ # lists
79
+
80
+ list_a1 = sorted([attr_key for attr_key in field1.attrs])
81
+
82
+ list_a2 = sorted([attr_key for attr_key in field2.attrs])
83
+ if list_a1 != list_a2:
84
+ result = list_differences(list_a1, list_a2)
85
+ else:
86
+ result = []
87
+
88
+ return result
89
+
90
+
91
+ # Getting a given vAttr's matched keys
92
+ def get_matched_vAttrKey(field1, field2):
93
+ set_a1 = set([attr_key for attr_key in field1.attrs])
94
+
95
+ set_a2 = set([attr_key for attr_key in field2.attrs])
96
+
97
+ return set_a1 & set_a2
98
+
99
+
100
+ def compare_global_attributes(global_att1, global_att2, list_ignore_gatt):
101
+ """
102
+
103
+ Compare global attributes
104
+
105
+ :return: the global attribute differences
106
+ """
107
+
108
+ # initialize the Global Attributes dictionary
109
+ gAttrs = {}
110
+
111
+ list_global_att1 = sorted(list(global_att1.keys()))
112
+ list_global_att2 = sorted(list(global_att2.keys()))
113
+
114
+ if list_ignore_gatt != []:
115
+ list_global_att1 = [n for n in list_global_att1 if n not in list_ignore_gatt]
116
+ list_global_att2 = [n for n in list_global_att2 if n not in list_ignore_gatt]
117
+ not_match_attribute = list_differences(list_global_att1, list_global_att2)
118
+
119
+ l1 = len(not_match_attribute[0])
120
+ l2 = len(not_match_attribute[1])
121
+
122
+ if list_ignore_gatt != []:
123
+ logger.debug(
124
+ "%s Global Attributes to be ignored for comparison : %s",
125
+ len(list_ignore_gatt),
126
+ list_ignore_gatt,
127
+ )
128
+ if l1 != 0 or l2 != 0:
129
+ logger.debug("Global attributes: different")
130
+ logger.debug("File 1 : %s%s", str(len(global_att1)), " global attributes")
131
+ logger.debug("File1's Global Attributes List : %s", list_global_att1)
132
+ logger.debug("File 2 : %s%s", str(len(global_att2)), " global attributes")
133
+ logger.debug("File2's Global Attributes List : %s", list_global_att2)
134
+ logger.debug("Not matched global attributes")
135
+ logger.debug(
136
+ " File1 : %s - %s", len(not_match_attribute[0]), not_match_attribute[0]
137
+ )
138
+ logger.debug(
139
+ " File2 : %s - %s", len(not_match_attribute[1]), not_match_attribute[1]
140
+ )
141
+
142
+ gAttrs["NotMatched"] = not_match_attribute
143
+ # Remove not matched keys from the 2 dictionaries
144
+ not_match1 = not_match_attribute[0]
145
+ not_match2 = not_match_attribute[1]
146
+
147
+ for key_to_remove in not_match1:
148
+ global_att1 = delete_key(global_att1, key_to_remove)
149
+
150
+ for key_to_remove in not_match2:
151
+ global_att2 = delete_key(global_att2, key_to_remove)
152
+
153
+ # Compare Global Attributes's value
154
+ nl1 = len(global_att1)
155
+ nl2 = len(global_att2)
156
+ if nl1 == nl2: # The 2 dictionaries have the same keys
157
+ # Compare 2 dictionaries
158
+ checking = global_att1 == global_att2
159
+ if not checking:
160
+ # Not equal !!
161
+ logger.debug("Global attributes value: different")
162
+ common_att = sorted(list(global_att1.keys()))
163
+
164
+ DiffValueAttr = {}
165
+ for com_att in common_att:
166
+ if com_att not in list_ignore_gatt:
167
+ dd1 = global_att1.get(com_att)
168
+ dd2 = global_att2.get(com_att)
169
+ if dd1 != dd2:
170
+ val1 = dd1[0]
171
+ val2 = dd2[0]
172
+ logger.debug("** %s", com_att)
173
+ logger.debug(" File1 : %s", val1)
174
+ logger.debug(" File2 : %s", val2)
175
+
176
+ DiffValueAttr[com_att] = [val1, val2]
177
+ if DiffValueAttr:
178
+ gAttrs["Value"] = DiffValueAttr
179
+ logger.debug("gAttrs: %s", pformat(DiffValueAttr, width=1000))
180
+
181
+ return gAttrs
182
+
183
+
184
+ def precision_dict_from_list(precision_list):
185
+ precision_dict = {}
186
+ for key_value in precision_list:
187
+ key, value = key_value.split(":")
188
+ precision_dict[key] = float(value)
189
+ return precision_dict
190
+
191
+
192
+ def compare_z_var(
193
+ field1,
194
+ field2,
195
+ key,
196
+ dict_numerical_precision={},
197
+ shape_diff_dict={},
198
+ value_diff_dict={},
199
+ ):
200
+ # check if fields have the same shape
201
+ if field1.shape:
202
+ data1 = field1[...]
203
+ else:
204
+ # Make sure to have numpy ndarray if data1 is a scalar
205
+ data1 = np.atleast_1d(field1[...])
206
+ if field2.shape:
207
+ data2 = field2[...]
208
+ else:
209
+ # Make sure to have numpy ndarray if data2 is a scalar
210
+ data2 = np.atleast_1d(field2[...])
211
+
212
+ res = False
213
+
214
+ if data1.shape != data2.shape:
215
+ logger.debug(
216
+ "%s - array shape is different: %s | %s", key, field1.shape, field2.shape
217
+ )
218
+ shape_diff_dict[key] = [data1.shape, data2.shape]
219
+
220
+ else:
221
+ differences_mask = data1 != data2
222
+ fields_are_different = np.any(differences_mask)
223
+
224
+ # zVariable's key : check for different values
225
+ if fields_are_different:
226
+ if key in dict_numerical_precision.keys():
227
+ tab_diff = np.isclose(data1, data2, atol=dict_numerical_precision[key])
228
+ res = np.all(tab_diff)
229
+ if not res:
230
+ logger.debug(
231
+ "Different values for zVariable '%s' : %s | %s",
232
+ key,
233
+ field1,
234
+ field2,
235
+ )
236
+ value_diff_dict[key] = [
237
+ data1[differences_mask],
238
+ data2[differences_mask],
239
+ ]
240
+
241
+ else:
242
+ logger.debug(
243
+ "Different values for zVariable '%s' : %s | %s", key, field1, field2
244
+ )
245
+ value_diff_dict[key] = [
246
+ data1[differences_mask],
247
+ data2[differences_mask],
248
+ ]
249
+
250
+ return shape_diff_dict, value_diff_dict
251
+
252
+
253
+ def compare_v_att(
254
+ field1, field2, key, key_diff_dict={}, value_diff_dict={}, list_ignore_vatt=[]
255
+ ):
256
+ v_att_keys_diff = list_differences(field1.attrs, field2.attrs)
257
+
258
+ if list_ignore_vatt != []:
259
+ logger.debug(
260
+ "%s Variable Attributes to be ignored for comparison : %s",
261
+ len(list_ignore_vatt),
262
+ list_ignore_vatt,
263
+ )
264
+ for item1 in list_ignore_vatt:
265
+ if item1 in v_att_keys_diff[0]:
266
+ (v_att_keys_diff[0]).remove(item1)
267
+ for item2 in list_ignore_vatt:
268
+ if item2 in v_att_keys_diff[1]:
269
+ (v_att_keys_diff[1]).remove(item2)
270
+
271
+ if len(v_att_keys_diff[0]) != 0 or len(v_att_keys_diff[1]) != 0:
272
+ logger.debug(
273
+ "Different Variable Attribute's keys of the zVariable '%s' : %s",
274
+ key,
275
+ v_att_keys_diff,
276
+ )
277
+ key_diff_dict[key] = v_att_keys_diff
278
+
279
+ common_v_att_keys = get_matched_vAttrKey(field1, field2)
280
+ logger.debug("Identical Variable Attribute's key : %s", common_v_att_keys)
281
+
282
+ vAttrsList1 = field1.attrs
283
+ vAttrsList2 = field2.attrs
284
+
285
+ DiffValue_vAttr = {}
286
+
287
+ for check_item in common_v_att_keys:
288
+ logger.debug(
289
+ "%s : %s | %s",
290
+ check_item,
291
+ vAttrsList1[check_item],
292
+ vAttrsList2[check_item],
293
+ )
294
+
295
+ if vAttrsList1[check_item] == vAttrsList2[check_item]:
296
+ logger.debug("%s : equal", check_item)
297
+ else:
298
+ logger.debug("%s : not equal", check_item)
299
+ DiffValue_vAttr[check_item] = [
300
+ vAttrsList1[check_item],
301
+ vAttrsList2[check_item],
302
+ ]
303
+ value_diff_dict[key] = DiffValue_vAttr
304
+ logger.debug("vAttrs['Value'] : %s", value_diff_dict)
305
+
306
+ return key_diff_dict, value_diff_dict
307
+
308
+
309
+ def compare_data(
310
+ cdf1,
311
+ cdf2,
312
+ cdf_keys1,
313
+ cdf_keys2,
314
+ list_ignore_zvar=[],
315
+ list_ignore_vatt=[],
316
+ list_numerical_precision=[],
317
+ ):
318
+ zVars = {} # store data differences
319
+ vAttrs = {} # store attribute differences
320
+
321
+ # Ignored zVariables for comparison
322
+ if list_ignore_zvar != []:
323
+ logger.debug(
324
+ "%s zVariables to be ignored for comparison : %s",
325
+ len(list_ignore_zvar),
326
+ list_ignore_zvar,
327
+ )
328
+
329
+ if len(cdf_keys1) != len(cdf_keys2):
330
+ logger.debug("Zvariables: different")
331
+
332
+ # ***** Not matched keys *****
333
+ list_diff1, list_diff2 = list_differences(cdf_keys1, cdf_keys2)
334
+
335
+ if cdf_keys1 == cdf_keys2:
336
+ logger.debug("Zvariables keys: identical")
337
+ same_keys = cdf_keys1
338
+ ordered_common_keys = sorted(list(same_keys))
339
+ else:
340
+ if (list_diff1 != []) or (list_diff2 != []):
341
+ zVars["Keys"] = [list_diff1, list_diff2]
342
+
343
+ logger.debug("NOT MATCHED zVARIABLES :")
344
+ for idx, diff_list in enumerate(zVars["Keys"]):
345
+ logger.debug(" File %d : %d - %s", idx + 1, len(diff_list), diff_list)
346
+ # ***** Matched keys *****
347
+ same_keys = set(cdf_keys1) & set(cdf_keys2)
348
+
349
+ # ***** Alphabetical order *****
350
+ ordered_common_keys = sorted(list(same_keys))
351
+
352
+ logger.debug("MATCHED zVARIABLES : %d", len(ordered_common_keys))
353
+ logger.debug(" %s", ordered_common_keys)
354
+
355
+ # prepare the dicts to store z_var and v_att diff
356
+ v_att_key_diff_dict = {}
357
+ v_att_value_diff_dict = {}
358
+ z_var_shape_diff_dict = {}
359
+ z_var_value_diff_dict = {}
360
+
361
+ for key in ordered_common_keys:
362
+ if key in list_ignore_zvar:
363
+ continue
364
+
365
+ # Raw values comparison : It's really necessary for time values like "Epoch"
366
+ # cdf1.raw_var(key) => 549441617029459008
367
+ # cdf1[key] => 2017-05-30 18:39:07.845459
368
+ field1 = cdf1.raw_var(key)
369
+ field2 = cdf2.raw_var(key)
370
+
371
+ dict_numerical_precision = {}
372
+ if len(list_numerical_precision) != 0:
373
+ dict_numerical_precision = precision_dict_from_list(
374
+ list_numerical_precision
375
+ )
376
+
377
+ compare_z_var(
378
+ field1,
379
+ field2,
380
+ key,
381
+ dict_numerical_precision=dict_numerical_precision,
382
+ shape_diff_dict=z_var_shape_diff_dict,
383
+ value_diff_dict=z_var_value_diff_dict,
384
+ )
385
+
386
+ compare_v_att(
387
+ field1,
388
+ field2,
389
+ key,
390
+ list_ignore_vatt=list_ignore_vatt,
391
+ key_diff_dict=v_att_key_diff_dict,
392
+ value_diff_dict=v_att_value_diff_dict,
393
+ )
394
+
395
+ if v_att_value_diff_dict:
396
+ vAttrs["Value"] = v_att_value_diff_dict
397
+ logger.debug("vAttrs['Value'] : %s", vAttrs["Value"])
398
+
399
+ if z_var_shape_diff_dict:
400
+ zVars["Shape"] = z_var_shape_diff_dict
401
+ if z_var_value_diff_dict:
402
+ zVars["Value"] = z_var_value_diff_dict
403
+
404
+ if v_att_key_diff_dict:
405
+ vAttrs["Keys"] = v_att_key_diff_dict
406
+ if v_att_value_diff_dict:
407
+ vAttrs["Value"] = v_att_value_diff_dict
408
+
409
+ return zVars, vAttrs
410
+
411
+
412
+ # Comparing 2 CDF files data
413
+ def cdf_compare(
414
+ cdf_file1,
415
+ cdf_file2,
416
+ list_ignore_gatt=[],
417
+ list_ignore_zvar=[],
418
+ list_ignore_vatt=[],
419
+ list_numerical_precision=[],
420
+ ):
421
+ logger.debug(" CDF file 1 : %s", cdf_file1)
422
+ logger.debug(" CDF file 2 : %s", cdf_file2)
423
+ checking_file_exist(cdf_file1)
424
+ checking_file_exist(cdf_file2)
425
+
426
+ cdf_keys1, global_att1 = get_keys_and_attributes(cdf_file1)
427
+ cdf_keys2, global_att2 = get_keys_and_attributes(cdf_file2)
428
+
429
+ cdf1 = CDF(cdf_file1)
430
+ cdf2 = CDF(cdf_file2)
431
+
432
+ dict_result = {}
433
+
434
+ gAttrs = compare_global_attributes(global_att1, global_att2, list_ignore_gatt)
435
+
436
+ if gAttrs:
437
+ dict_result["gAttrs"] = gAttrs
438
+
439
+ zVars, vAttrs = compare_data(
440
+ cdf1,
441
+ cdf2,
442
+ cdf_keys1,
443
+ cdf_keys2,
444
+ list_ignore_zvar=list_ignore_zvar,
445
+ list_ignore_vatt=list_ignore_vatt,
446
+ list_numerical_precision=list_numerical_precision,
447
+ )
448
+
449
+ if zVars:
450
+ dict_result["zVars"] = zVars
451
+
452
+ if vAttrs:
453
+ dict_result["vAttrs"] = vAttrs
454
+
455
+ cdf1.close()
456
+ cdf2.close()
457
+
458
+ for key, value in dict_result.items():
459
+ logger.debug("*°*°* %s *°*°*", key)
460
+ for key1, value1 in dict_result[key].items():
461
+ logger.debug(" *°* %s : %s", key1, value1)
462
+
463
+ # Case of we need to force to ignore some zVariables
464
+ # if forced_ignored_zvar != []:
465
+ # logger.debug("Forced ignored zVariables (Particular case) : %s", forced_ignored_zvar)
466
+
467
+ logger.debug("Return value: %s", pformat(dict_result, width=1000))
468
+ return dict_result
469
+
470
+
471
+ def main(cdf_file1, cdf_file2):
472
+ if len(sys.argv) >= 3:
473
+ list_argv = sys.argv
474
+ list_ignore_gatt = []
475
+ list_ignore_zvar = []
476
+ list_ignore_vatt = []
477
+ list_numerical_precision = []
478
+
479
+ if "--ignore_gatt" in (list_argv):
480
+ ind_ignore_gatt = (list_argv).index("--ignore_gatt")
481
+ list_ignore_gatt = []
482
+ for item in list_argv[ind_ignore_gatt + 1 :]:
483
+ if not item.startswith("--ignore_") and not item.startswith(
484
+ "--precision"
485
+ ):
486
+ list_ignore_gatt.append(item)
487
+ else:
488
+ break
489
+ logger.warning("Ignored global attributes list : %s", list_ignore_gatt)
490
+ else:
491
+ logger.debug("No global attributes to be ignored")
492
+
493
+ if "--ignore_zvar" in (list_argv):
494
+ ind_ignore_zvar = (list_argv).index("--ignore_zvar")
495
+ list_ignore_zvar = []
496
+ for item in list_argv[ind_ignore_zvar + 1 :]:
497
+ if not item.startswith("--ignore_") and not item.startswith(
498
+ "--precision"
499
+ ):
500
+ list_ignore_zvar.append(item)
501
+ else:
502
+ break
503
+ logger.warning("Ignored zVariables list : %s", list_ignore_zvar)
504
+ else:
505
+ logger.debug("No zVariables to be ignored")
506
+
507
+ if "--ignore_vatt" in (list_argv):
508
+ ind_ignore_vatt = (list_argv).index("--ignore_vatt")
509
+ list_ignore_vatt = []
510
+ for item in list_argv[ind_ignore_vatt + 1 :]:
511
+ if not item.startswith("--ignore_") and not item.startswith(
512
+ "--precision"
513
+ ):
514
+ list_ignore_vatt.append(item)
515
+ else:
516
+ break
517
+ logger.warning("Ignored variable attributes list : %s", list_ignore_vatt)
518
+ else:
519
+ logger.debug("No variable attributes to be ignored")
520
+
521
+ if "--precision" in (list_argv):
522
+ ind_precision_zvar = (list_argv).index("--precision")
523
+ list_numerical_precision = []
524
+ for item in list_argv[ind_precision_zvar + 1 :]:
525
+ if not item.startswith("--ignore_") and not item.startswith(
526
+ "--precision"
527
+ ):
528
+ list_numerical_precision.append(item)
529
+ else:
530
+ break
531
+ logger.warning("Numerical precision list : %s", list_numerical_precision)
532
+ else:
533
+ logger.debug("No zVariable precision set")
534
+
535
+ if "--precision" in (list_argv):
536
+ ind_precision_zvar = (list_argv).index("--precision")
537
+ list_numerical_precision = []
538
+ for item in list_argv[ind_precision_zvar + 1 :]:
539
+ if not item.startswith("--ignore_") and not item.startswith(
540
+ "--precision"
541
+ ):
542
+ list_numerical_precision.append(item)
543
+ else:
544
+ break
545
+ logger.warning("Numerical precision list : %s", list_numerical_precision)
546
+ else:
547
+ logger.debug("No zVariable precision set")
548
+
549
+ result = cdf_compare(
550
+ cdf_file1,
551
+ cdf_file2,
552
+ list_ignore_gatt=list_ignore_gatt,
553
+ list_ignore_zvar=list_ignore_zvar,
554
+ list_ignore_vatt=list_ignore_vatt,
555
+ list_numerical_precision=list_numerical_precision,
556
+ )
557
+ logger.info("Final result : %s", pformat(result, width=1000))
558
+
559
+
560
+ # *°*°*°*°*°*°*°*°
561
+ # Main program
562
+ # *°*°*°*°*°*°*°*°
563
+
564
+ if __name__ == "__main__":
565
+ main(sys.argv[1], sys.argv[2])