annotate galaxy-tools/biobank/updater/merge_individuals.py @ 0:e54d14bed3f5 draft default tip

Uploaded
author ric
date Thu, 29 Sep 2016 06:09:15 -0400
parents
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
1 #=======================================
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
2 # This tool moves all informations related to an individual (source) to
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
3 # another (target). Moved informations are:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
4 # * children (Individual objects)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
5 # * ActionOnInvidual
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
6 # * Enrollments
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
7 # * EHR records
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
8 #
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
9 # The tool expects as input a TSV file like this
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
10 # source target
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
11 # V0468D2D96999548BF9FC6AD24C055E038 V060BAA01C662240D181BB98A51885C498
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
12 # V029CC0A614E2D42D0837602B15193EB58 V01B8122A7C75A452E9F80381CEA988557
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
13 # V0B20C93E8A88D43EFB87A7E6911292A05 V0BED85E8E76A54AA7AB0AFB09F95798A8
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
14 # ...
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
15 #
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
16 # NOTE WELL:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
17 # * Parents of the "source" indivudal WILL NOT BE ASSIGNED
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
18 # to the "target" individual
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
19 # * For the Enrollmnent objects, if
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
20 # "target" individual has already a code in the same study of "source"
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
21 # individual, the script will try to move the Enrollment to the
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
22 # "duplicated" study (this will be fixed when a proper ALIASES
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
23 # manegement will be introduced)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
24 # =======================================
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
25
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
26 import sys, argparse, csv, time, json, os
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
27
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
28 from bl.vl.kb import KnowledgeBase as KB
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
29 from bl.vl.kb import KBError
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
30 import bl.vl.utils.ome_utils as vlu
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
31 from bl.vl.utils import get_logger, LOG_LEVELS
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
32
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
33
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
34 def make_parser():
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
35 parser = argparse.ArgumentParser(description='merge informations related to an individual ("source") to another one ("target")')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
36 parser.add_argument('--logfile', type=str, help='log file (default=stderr)')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
37 parser.add_argument('--loglevel', type=str, choices = LOG_LEVELS,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
38 help='logging level (default=INFO)', default='INFO')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
39 parser.add_argument('-H', '--host', type=str, help='omero hostname')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
40 parser.add_argument('-U', '--user', type=str, help='omero user')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
41 parser.add_argument('-P', '--passwd', type=str, help='omero password')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
42 parser.add_argument('-O', '--operator', type=str, help='operator',
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
43 required=True)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
44 parser.add_argument('--in_file', type=str, required = True,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
45 help='input TSV file')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
46 return parser
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
47
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
48
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
49 def update_object(obj, backup_values, operator, kb, logger):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
50 logger.debug('Building ActionOnAction for object %s::%s' %
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
51 (obj.get_ome_table(),
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
52 obj.id)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
53 )
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
54 act_setup = build_action_setup('merge-individuals-%f' % time.time(),
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
55 backup_values, kb)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
56 aoa_conf = {
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
57 'setup': act_setup,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
58 'actionCategory' : kb.ActionCategory.UPDATE,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
59 'operator': operator,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
60 'target': obj.lastUpdate if obj.lastUpdate else obj.action,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
61 'context': obj.action.context
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
62 }
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
63 logger.debug('Updating object with new ActionOnAction')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
64 obj.lastUpdate = kb.factory.create(kb.ActionOnAction, aoa_conf)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
65
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
66
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
67 def build_action_setup(label, backup, kb, logger):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
68 logger.debug('Creating a new ActionSetup with label %s and backup %r' % (label, backup))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
69 conf = {
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
70 'label': label,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
71 'conf': json.dumps({'backup' : backup})
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
72 }
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
73 asetup = kb.factory.create(kb.ActionSetup, conf)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
74 return asetup
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
75
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
76
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
77 def update_children(source_ind, target_ind, operator, kb, logger):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
78 if source_ind.gender.enum_label() == kb.Gender.MALE.enum_label():
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
79 parent_type = 'father'
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
80 elif source_ind.gender.enum_label() == kb.Gender.FEMALE.enum_label():
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
81 parent_type = 'mother'
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
82 else:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
83 raise ValueError('%s is not a valid gender value' % (source_ind.gender.enum_label()))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
84 query = '''
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
85 SELECT ind FROM Individual ind
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
86 JOIN ind.{0} AS {0}
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
87 WHERE {0}.vid = :parent_vid
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
88 '''.format(parent_type)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
89 children = kb.find_all_by_query(query, {'parent_vid' : source_ind.id})
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
90 logger.info('Retrieved %d children for source individual' % len(children))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
91 for child in children:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
92 backup = {}
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
93 logger.debug('Changing %s for individual %s' % (parent_type,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
94 child.id))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
95 backup[parent_type] = getattr(child, parent_type).id
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
96 setattr(child, parent_type, target_ind)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
97 update_object(child, backup, operator, kb)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
98 kb.save_array(children)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
99
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
100
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
101 def update_action_on_ind(source_ind, target_ind, operator, kb, logger):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
102 query = '''SELECT act FROM ActionOnIndividual act
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
103 JOIN act.target AS ind
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
104 WHERE ind.vid = :ind_vid
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
105 '''
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
106 src_acts = kb.find_all_by_query(query, {'ind_vid' : source_ind.id})
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
107 logger.info('Retrieved %d actions for source individual' % len(src_acts))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
108 connected = kb.dt.get_connected(source_ind, direction=kb.dt.DIRECTION_OUTGOING,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
109 query_depth=1)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
110 if source_ind in connected:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
111 connected.remove(source_ind)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
112 for sa in src_acts:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
113 logger.debug('Changing target for action %s' % sa.id)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
114 sa.target = target_ind
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
115 logger.debug('Action %s target updated' % sa.id)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
116 kb.save_array(src_acts)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
117 for conn in connected:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
118 kb.dt.destroy_edge(source_ind, conn)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
119 kb.dt.create_edge(conn.action, target_ind, conn)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
120
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
121
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
122 def update_enrollments(source_ind, target_ind, operator, kb, logger):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
123 query = '''SELECT en FROM Enrollment en
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
124 JOIN en.individual AS ind
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
125 WHERE ind.vid = :ind_vid
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
126 '''
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
127 enrolls = kb.find_all_by_query(query, {'ind_vid' : source_ind.id})
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
128 logger.info('Retrieved %d enrollments for source individual' % len(enrolls))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
129 for sren in enrolls:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
130 try:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
131 sren.individual = target_ind
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
132 logger.debug('Changing individual for enrollment %s in study %s' % (sren.studyCode,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
133 sren.study.label))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
134 kb.save(sren)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
135 logger.info('Changed individual for enrollment %s (study code %s -- study %s)' % (sren.id,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
136 sren.studyCode,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
137 sren.study.label))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
138 except KBError, kbe:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
139 logger.warning('Unable to update enrollment %s (study code %s -- study %s)' % (sren.id,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
140 sren.studyCode,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
141 sren.study.label))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
142 move_to_duplicated(sren, operator, kb, logger)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
143
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
144
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
145 def update_ehr_records(source_ind, target_ind, kb):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
146 kb.update_table_rows(kb.eadpt.EAV_EHR_TABLE, '(i_vid == "%s")' % source_ind.id,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
147 {'i_vid' : target_ind.id})
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
148
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
149
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
150 # This method should be considered as a temporary hack that will be
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
151 # used untill a proper ALIAS management will be introduced into the
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
152 # system
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
153 def move_to_duplicated(enrollment, operator, kb, logger):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
154 old_st = enrollment.study
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
155 dupl_st = kb.get_study('%s_DUPLICATI' % old_st.label)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
156 if not dupl_st:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
157 logger.warning('No "duplicated" study ({0}_DUPLICATI) found for study {0}'.format(old_st.label))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
158 return
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
159 enrollment.study = dupl_st
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
160 try:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
161 kb.save(enrollment)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
162 logger.info('Enrollmnet %s moved from study %s to study %s' % (enrollment.studyCode,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
163 old_st.label, dupl_st.label))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
164 except:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
165 logger.error('An error occurred while moving enrollment %s from study %s to %s' % (enrollment.studyCode,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
166 old_st.label,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
167 dupl_st.label))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
168
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
169
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
170 def main(argv):
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
171 parser = make_parser()
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
172 args = parser.parse_args(argv)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
173
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
174 logger = get_logger('merge_individuals', level=args.loglevel,
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
175 filename=args.logfile)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
176
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
177 try:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
178 host = args.host or vlu.ome_host()
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
179 user = args.user or vlu.ome_user()
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
180 passwd = args.passwd or vlu.ome_passwd()
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
181 except ValueError, ve:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
182 logger.critical(ve)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
183 sys.exit(ve)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
184
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
185 kb = KB(driver='omero')(host, user, passwd)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
186
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
187 logger.debug('Retrieving Individuals')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
188 individuals = kb.get_objects(kb.Individual)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
189 logger.debug('Retrieved %d Individuals' % len(individuals))
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
190 ind_lookup = {}
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
191 for i in individuals:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
192 ind_lookup[i.id] = i
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
193
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
194 with open(args.in_file) as in_file:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
195 reader = csv.DictReader(in_file, delimiter='\t')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
196 for row in reader:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
197 try:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
198 source = ind_lookup[row['source']]
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
199 logger.info('Selected as source individual with ID %s' % source.id)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
200 target = ind_lookup[row['target']]
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
201 logger.info('Selected as destination individual with ID %s' % target.id)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
202 except KeyError, ke:
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
203 logger.warning('Unable to retrieve individual with ID %s, skipping row' % ke)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
204 continue
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
205
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
206 logger.info('Updating children connected to source individual')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
207 update_children(source, target, args.operator, kb, logger)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
208 logger.info('Children update complete')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
209
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
210 logger.info('Updating ActionOnIndividual related to source individual')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
211 update_action_on_ind(source, target, args.operator, kb, logger)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
212 logger.info('ActionOnIndividual update completed')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
213
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
214 logger.info('Updating enrollments related to source individual')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
215 update_enrollments(source, target, args.operator, kb, logger)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
216 logger.info('Enrollments update completed')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
217
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
218 logger.info('Updating EHR records related to source individual')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
219 update_ehr_records(source, target, kb)
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
220 logger.info('EHR records update completed')
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
221
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
222 if __name__ == '__main__':
e54d14bed3f5 Uploaded
ric
parents:
diff changeset
223 main(sys.argv[1:])