Commit 793d01902f575a3fad242077aa9ae7e9e811ea9a

Authored by Alexis Koralewski
1 parent d6e916dc
Exists in dev

rework routine_manager functions.py

Showing 1 changed file with 216 additions and 202 deletions   Show diff stats
src/core/pyros_django/routine_manager/functions.py
... ... @@ -12,155 +12,33 @@ from django.db import IntegrityError
12 12 # Project imports
13 13 from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig
14 14 from django.http import HttpRequest
  15 +#from silk.profiling.profiler import silk_profile
15 16  
  17 +#@silk_profile(name="check_sequence_file")
16 18 def check_sequence_file_validity_and_save(yaml_content: dict, request: HttpRequest):
17 19 ''' Create a sequence in DB from the uploaded sequence (yaml_content) '''
18 20  
19   - # Get boolean to simplified to know if the file is written in simplified mode (i.e. : Each field of the form is directly associated to its value)
20   - is_simplified = yaml_content.get("simplified", False)
21   - # Get scientific programs for the user who is submitting the sequence file
22   - user_sp = request.user.get_scientific_programs()
23   - # From user sp, get all SP that can observe / submit sequence for the current period
24   - sp_list = ScientificProgram.objects.observable_programs().filter(id__in=user_sp)
25   -
26 21 # Create a sequence seq object (from yaml_content) to be saved in DB
27 22 seq = Sequence.objects.create()
28 23 seq.pyros_user = PyrosUser.objects.get(id=request.user.id)
29 24  
30 25 # Get the unit config
31 26 unit_name = os.environ["unit_name"]
  27 + #with silk_profile(name="init obsconfig"):
32 28 config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
33   -
34   - # Create a Sequence form
35   - sequence_form = SequenceForm(instance=seq, data_from_config=config.getEditableMountAttributes(config.unit_name), layouts = config.get_layouts(config.unit_name), sp_list=sp_list)
36 29 result = {
37 30 "succeed": True,
38 31 "errors": [],
39 32 }
40   -
41   - if is_simplified:
42   - seq.scientific_program = sp_list[yaml_content["sequence"]["scientific_program"]]
  33 + # Get boolean to simplified to know if the file is written in simplified mode (i.e. : Each field of the form is directly associated to its value)
  34 + is_simplified = yaml_content.get("simplified", False)
  35 + # Get scientific programs for the user who is submitting the sequence file
  36 + user_sp = request.user.get_scientific_programs()
  37 + process_sequence(yaml_content, seq, config, is_simplified, result, user_sp)
43 38  
44   - else:
45   - # pour la lisibilité du code (et éviter la redondance)
46   - # get scientific program field's attributes
47   - yaml_seq_sp = yaml_content["sequence"]["scientific_program"]
48   - sp_index_value = yaml_seq_sp["value"]
49   - values = yaml_seq_sp["values"]
50   - # Check if index of sp is valid (in range of possible index from values)
51   - if sp_index_value < 0 or sp_index_value > len(values):
52   - result["errors"].append(f"SP value isn't valid, index out of bounds ({sp_index_value} > {len(values)})")
53   - sp_index_value = 0
54   - chosen_sp = ScientificProgram.objects.get(name=values[sp_index_value])
55   - # If the sp is associated to that user, associate the sp to the sequence
56   - if chosen_sp in sp_list:
57   - #seq.scientific_program = ScientificProgram.objects.get(name=yaml_content["sequence"]["scientific_program"]["values"][sp_index_value])
58   - seq.scientific_program = chosen_sp
59   - else:
60   - result["errors"].append(f"SP {chosen_sp.name} is not assigned to that user ")
  39 + process_albums(yaml_content, result, config, seq, is_simplified)
61 40  
62   - seq.config_attributes = {}
63   -
64   - # Fill all Sequence form fields
65   - # keys() inutile ? => for field in sequence_form.fields :
66   - # Sinon, y a aussi => for key,val in sequence_form.fields.items():
67   - # => Ca éviterait de faire => sequence_form.fields[field] pour recuperer "val"
68   - for field, field_attribute in sequence_form.fields.items():
69 41  
70   - #if sequence_form.fields[field].required == False or field == "scientific_program":
71   - if not field_attribute.required or field=="scientific_program":
72   - continue
73   - # pour lisibilité, simplicité et éviter redondance
74   - yaml_field = yaml_content["sequence"][field]
75   - value = yaml_field if is_simplified else yaml_field["value"]
76   - ''' (orig)
77   - if is_simplified:
78   - value = yaml_content["sequence"][field]
79   - else:
80   - value = yaml_content["sequence"][field]["value"]
81   - '''
82   - # If the current field of the sequence isn't found in the file, add an error message to inform the user the missing field
83   - if field not in yaml_content["sequence"]:
84   - result["errors"].append(f"{field} not in yaml file")
85   - else:
86   - if is_simplified:
87   - # If the field is a choicefield, get choices and associate the index to the real value
88   - if sequence_form.fields[field].__dict__.get("_choices"):
89   - # y a pas conflit ici avec la variable "value" définie au-dessus ? -> Non car on transforme l'ancien value qui est un index en une vraie valeur
90   - values = [value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
91   - value = values[value]
92   - else:
93   - if yaml_field.get("values"):
94   - # Transform the original value which is an index to a "real" value from the "values" attributes
95   - index_value = yaml_field["value"]
96   - values = yaml_field["values"]
97   - if index_value < 0 or index_value > len(yaml_field["values"]):
98   - result["errors"].append(f"Value of {field} isn't valid, index out of bounds ({index_value} > {len(values)})")
99   - index_value = 0
100   - value = yaml_field["values"][index_value]
101   - else:
102   - # Transform the string value to a datetime value
103   - if field == "start_date":
104   - if type(value) != datetime.datetime:
105   - #value = datetime.datetime.strptime(yaml_content["sequence"][field]["value"],'%d/%m/%Y %H:%M:%S')
106   - # ISO format
107   - value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
108   - seq.__dict__[field] = value
109   - ''' (orig)
110   - else:
111   - if not is_simplified:
112   - if yaml_content["sequence"][field].get("values"):
113   - index_value = yaml_content["sequence"][field]["value"]
114   - values = yaml_content["sequence"][field]["values"]
115   - if index_value < 0 or index_value > len(yaml_content["sequence"][field]["values"]):
116   - result["errors"].append(f"Value of {field} isn't valid, index out of bounds ({index_value} > {len(values)})")
117   - index_value = 0
118   - value = yaml_content["sequence"][field]["values"][index_value]
119   - else:
120   - if field == "start_date":
121   - if type(value) != datetime.datetime:
122   - #value = datetime.datetime.strptime(yaml_content["sequence"][field]["value"],'%d/%m/%Y %H:%M:%S')
123   - # ISO format
124   - value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
125   - seq.__dict__[field] = value
126   - else:
127   - if sequence_form.fields[field].__dict__.get("_choices"):
128   - values = [value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
129   - value = values[value]
130   - '''
131   -
132   - # suffisant ? => if field in seq.__dict__
133   - # If field is an attribute of the sequence, associate the field to the value
134   - if field in seq.__dict__:
135   - seq.__dict__[field] = value
136   - else:
137   - # else associate field & value in config_attributes sequence's field (JsonField) = variable fields of an sequence
138   - seq.config_attributes[field] = value
139   -
140   - # Create ALBUMS
141   - albums_from_file = yaml_content["sequence"]["ALBUMS"]
142   - chosen_layout = seq.config_attributes["layout"]
143   - if type(chosen_layout) == int:
144   - layouts = config.get_layouts(config.unit_name)["layouts"]
145   - chosen_layout = list(layouts.keys())[chosen_layout]
146   - # Get album of the selected layout
147   - layout_albums = config.getLayoutByName(unit_name=config.unit_name, name_of_layout=chosen_layout)["ALBUMS"]
148   -
149   - # check if we have all the albums of that layout described in the sequence file
150   - if len(layout_albums) == len(albums_from_file):
151   - for album in albums_from_file:
152   - album = album["Album"]
153   - if album["name"] not in layout_albums:
154   - result["errors"].append(f"Album {album['name']} is not the chosen layout")
155   - else:
156   - # Create album
157   - Album.objects.create(name=album["name"], sequence=seq, complete=True)
158   - # Create plan for that album
159   - plans = [a["Album"].get("Plans") for a in albums_from_file]
160   - process_plans(plans, result, is_simplified, config, album, seq)
161   - else:
162   - result["errors"].append(f"The number of albums doesn't correspond to the chosen layout")
163   -
164 42 # optim possible ?
165 43 #[ process_plans(a["Album"].get("Plans")) for a in albums_from_file ]
166 44 # Puis écrire la fonction process_plans()
... ... @@ -291,19 +169,51 @@ def check_sequence_file_validity_and_save(yaml_content: dict, request: HttpReque
291 169 result["sequence_id"] = seq.id
292 170 return result
293 171  
  172 +def process_albums(yaml_content, result, config, seq, is_simplified):
  173 + # Create ALBUMS
  174 + albums_from_file = yaml_content["sequence"]["ALBUMS"]
  175 + chosen_layout = seq.config_attributes["layout"]
  176 + if type(chosen_layout) == int:
  177 + #with silk_profile(name="Get layout from config"):
  178 + layouts = config.get_layouts(config.unit_name)["layouts"]
  179 + chosen_layout = list(layouts)[chosen_layout]
  180 + # Get album of the selected layout
  181 + #with silk_profile(name="Get album of layout from config"):
  182 + layout_albums = config.getLayoutByName(unit_name=config.unit_name, name_of_layout=chosen_layout)["ALBUMS"]
  183 +
  184 + # check if we have all the albums of that layout described in the sequence file
  185 + #with silk_profile(name="Iterate on each album & plan (create)"):
  186 + if len(layout_albums) == len(albums_from_file):
  187 + for album in albums_from_file:
  188 + album = album["Album"]
  189 + if album["name"] not in layout_albums:
  190 + result["errors"].append(f"Album {album['name']} is not the chosen layout")
  191 + else:
  192 + # Create album
  193 + Album.objects.create(name=album["name"], sequence=seq, complete=True)
  194 + # Create plan for that album
  195 + plans = album.get("Plans")
  196 + process_plans(plans, result, is_simplified, config, album, seq)
  197 + else:
  198 + result["errors"].append(f"The number of albums doesn't correspond to the chosen layout")
  199 +
294 200  
  201 +#@silk_profile(name="process_plans")
295 202 def process_plans(plans: dict, result: dict, is_simplified: bool, config: OBSConfig, album: dict, seq: dict):
296 203 if plans == None:
297 204 result["errors"].append(f"Album {album['name']} has no plans. Please add at least one plan")
298 205 # exit function
299 206 return None
300 207 for plan in plans:
301   - new_plan_object = Plan.objects.create(album=Album.objects.get(name=album["name"], sequence=seq), complete=True)
302   - new_plan_object.config_attributes = {}
  208 + #new_plan_object = Plan.objects.create(album=Album.objects.get(name=album["name"], sequence=seq), complete=True)
  209 + #new_plan_object.config_attributes = {}
303 210 plan = plan["Plan"]
  211 + nb_images = 0
304 212 config_attributes = {}
  213 + #with silk_profile(name="Create plan form"):
305 214 plan_form = PlanForm(data_from_config=config.getEditableChannelAttributes(config.unit_name, list(config.get_channels(config.unit_name).keys())[0]), edited_plan=None)
306 215 # Process each plan field
  216 + #with silk_profile(name="iterate on plan fields"):
307 217 for field in plan_form.fields:
308 218 plan_field = plan[field]
309 219 '''
... ... @@ -311,78 +221,180 @@ def process_plans(plans: dict, result: dict, is_simplified: bool, config: OBSCon
311 221 max_value = None
312 222 value_type = None
313 223 '''
314   - min_value = max_value = value_type = None
315   - if field not in plan.keys():
316   - result["errors"].append(f"Missing field : '{field}' for plan {plans.index(plan)}")
317   - continue
318   - # TODO : ajouter max_value, min_value, suppression plan et album si invalides
319   - if not is_simplified:
320   - if plan_field.get("value_type"):
321   - value_type = plan_field["value_type"]
322   - if type(plan_field["value"]) == str and ast.literal_eval(plan_field["value"]) != value_type:
323   - result["errors"].append(f"Field {field} value doesn't correspond to the assigned type (type required : {value_type})")
324   - if plan_field.get("min_value"):
325   - min_value = plan_field["min_value"]
326   - if type(min_value) == str:
327   - min_value = ast.literal_eval(min_value)
328   - '''
329   - if type(plan_field["min_value"]) == str:
330   - min_value = ast.literal_eval(plan_field["min_value"])
331   - else:
332   - min_value = plan_field["min_value"]
333   - '''
334   - if plan_field.get("max_value"):
335   - max_value = plan_field["max_value"]
336   - if type(max_value) == str:
337   - max_value = ast.literal_eval(max_value)
338   - '''
339   - if type(plan_field.get("max_value")) == str:
340   - max_value = ast.literal_eval(plan_field["max_value"])
341   - else:
342   - max_value = plan_field["max_value"]
343   - '''
344 224 if field == "nb_images":
345   - new_plan_object.__dict__[field] = plan_field if is_simplified else plan_field["value"]
346   - '''
347   - if is_simplified:
348   - new_plan_object.__dict__[field] = plan_field
349   - else:
350   - new_plan_object.__dict__[field] = plan_field["value"]
351   - '''
  225 + nb_images = plan_field if is_simplified else plan_field["value"]
352 226 else:
353   - # shortcut possible ?
354   - #new_plan_object_field = new_plan_object.config_attributes[field]
355   - if is_simplified:
356   - new_plan_object.config_attributes[field] = plan_field
357   - else:
358   - if plan_field.get("values"):
359   - index_value = plan_field["value"]
360   - values = plan_field["values"]
361   - if index_value < 0 or index_value > len(plan_field["values"]):
362   - result["errors"].append(f"Value of Plan field '{field}' isn't valid, index out of bounds ({index_value} > {len(values)})")
363   - index_value = 0
364   - value = plan_field["values"][index_value]
365   - try:
366   - # linked values
367   - splitted_values = value.split(";")
368   - config_attributes[field] = {}
369   - for splitted_value in splitted_values:
370   - subkey,subvalue = splitted_value.split(":")
371   - config_attributes[field][subkey] = ast.literal_eval(subvalue)
372   - # vaudrait mieux préciser l'exception ici
373   - except:
374   - # Do nothing, normal string
375   - config_attributes[field] = ast.literal_eval(value)
376   - new_plan_object.config_attributes[field] = config_attributes[field]
377   - else:
378   - if max_value and min_value:
379   - if plan_field["value"] > max_value:
380   - result["errors"].append(f"Plan field {field} doesn't respect max value")
381   - if plan_field["value"] < min_value:
382   - result["errors"].append(f"Plan field {field} doesn't respect min value")
383   - new_plan_object.config_attributes[field] = plan_field["value"]
  227 + process_plan_field(result, config_attributes, plan_field, field, plans, plan, is_simplified)
384 228 # end foreach plan field
385   - new_plan_object.save()
  229 + Plan.objects.create(album=Album.objects.get(name=album["name"], sequence=seq), complete=True, nb_images=nb_images, config_attributes=config_attributes)
  230 +
  231 +def process_plan_field(result, config_attributes, plan_field, field, plans, plan, is_simplified):
  232 +
  233 +
  234 + if field not in plan.keys():
  235 + result["errors"].append(f"Missing field : '{field}' for plan {plans.index(plan)}")
  236 + # exit function
  237 + return None
  238 + if is_simplified:
  239 + #new_plan_object.config_attributes[field] = plan_field
  240 + config_attributes[field] = plan_field
  241 + else:
  242 + value_type, min_value, max_value = prepare_check_plan_field_value(plan_field, field, result)
  243 + check_and_set_plan_field_value(config_attributes, plan_field, field, result, value_type, min_value, max_value)
  244 +
  245 +
  246 +def check_and_set_plan_field_value(config_attributes, plan_field, field, result, value_type, min_value, max_value):
  247 + # if the value is a index of a list, get the value from this index
  248 + if plan_field.get("values"):
  249 + index_value = plan_field["value"]
  250 + values = plan_field["values"]
  251 + if index_value < 0 or index_value > len(plan_field["values"]):
  252 + result["errors"].append(f"Value of Plan field '{field}' isn't valid, index out of bounds ({index_value} > {len(values)})")
  253 + index_value = 0
  254 + value = plan_field["values"][index_value]
  255 + try:
  256 + # linked values
  257 + splitted_values = value.split(";")
  258 + config_attributes[field] = {}
  259 + for splitted_value in splitted_values:
  260 + subkey,subvalue = splitted_value.split(":")
  261 + config_attributes[field][subkey] = ast.literal_eval(subvalue)
  262 + # vaudrait mieux préciser l'exception ici
  263 + except ValueError:
  264 + # Do nothing, normal string
  265 + config_attributes[field] = ast.literal_eval(value)
  266 + #new_plan_object.config_attributes[field] = config_attributes[field]
  267 + else:
  268 + # check min and max values if they exist
  269 + if max_value and min_value:
  270 + if plan_field["value"] > max_value:
  271 + result["errors"].append(f"Plan field {field} doesn't respect max value")
  272 + if plan_field["value"] < min_value:
  273 + result["errors"].append(f"Plan field {field} doesn't respect min value")
  274 + #new_plan_object.config_attributes[field] = plan_field["value"]
  275 + config_attributes[field] = plan_field["value"]
  276 +
  277 +
  278 +def prepare_check_plan_field_value(plan_field, field, result):
  279 + min_value = max_value = value_type = None
  280 + # get value type, min_value and max_value if they're in the plan form
  281 + if plan_field.get("value_type"):
  282 + value_type = plan_field["value_type"]
  283 + # If value type doesn't match with the value from the form, add an error to result
  284 + if type(plan_field["value"]) == str and ast.literal_eval(plan_field["value"]) != value_type:
  285 + result["errors"].append(f"Field {field} value doesn't correspond to the assigned type (type required : {value_type})")
  286 + if plan_field.get("min_value"):
  287 + min_value = plan_field["min_value"]
  288 + if type(min_value) == str:
  289 + min_value = ast.literal_eval(min_value)
  290 + if plan_field.get("max_value"):
  291 + max_value = plan_field["max_value"]
  292 + if type(max_value) == str:
  293 + max_value = ast.literal_eval(max_value)
  294 + return value_type, min_value, max_value
  295 +
  296 +
  297 +def process_sequence(yaml_content, seq, config, is_simplified, result, user_sp):
  298 +
  299 + # From user sp, get all SP that can observe / submit sequence for the current period
  300 + sp_list = ScientificProgram.objects.observable_programs().filter(id__in=user_sp)
  301 + # Create a Sequence form
  302 + sequence_form = SequenceForm(instance=seq, data_from_config=config.getEditableMountAttributes(config.unit_name), layouts = config.get_layouts(config.unit_name), sp_list=sp_list)
  303 +
  304 + if is_simplified:
  305 + seq.scientific_program = sp_list[yaml_content["sequence"]["scientific_program"]]
  306 + else:
  307 + # get scientific program field's attributes
  308 + yaml_seq_sp = yaml_content["sequence"]["scientific_program"]
  309 + sp_index_value = yaml_seq_sp["value"]
  310 + values = yaml_seq_sp["values"]
  311 + # Check if index of sp is valid (in range of possible index from values)
  312 + if sp_index_value < 0 or sp_index_value > len(values):
  313 + result["errors"].append(f"SP value isn't valid, index out of bounds ({sp_index_value} > {len(values)})")
  314 + sp_index_value = 0
  315 + chosen_sp = ScientificProgram.objects.get(name=values[sp_index_value])
  316 + # If the sp is associated to that user, associate the sp to the sequence
  317 + if chosen_sp in sp_list:
  318 + #seq.scientific_program = ScientificProgram.objects.get(name=yaml_content["sequence"]["scientific_program"]["values"][sp_index_value])
  319 + seq.scientific_program = chosen_sp
  320 + else:
  321 + result["errors"].append(f"SP {chosen_sp.name} is not assigned to that user ")
  322 +
  323 + seq.config_attributes = {}
  324 +
  325 + # Fill all Sequence form fields
  326 + #with silk_profile(name="iterate sequence fields form"):
  327 + for field, field_attributes in sequence_form.fields.items():
  328 + #if sequence_form.fields[field].required == False or field == "scientific_program":
  329 + if not field_attributes.required or field=="scientific_program":
  330 + continue
  331 + # pour lisibilité, simplicité et éviter redondance
  332 + yaml_field = yaml_content["sequence"][field]
  333 + value = yaml_field if is_simplified else yaml_field["value"]
  334 + ''' (orig)
  335 + if is_simplified:
  336 + value = yaml_content["sequence"][field]
  337 + else:
  338 + value = yaml_content["sequence"][field]["value"]
  339 + '''
  340 + # If the current field of the sequence isn't found in the file, add an error message to inform the user the missing field
  341 + if field not in yaml_content["sequence"]:
  342 + result["errors"].append(f"{field} not in yaml file")
  343 + else:
  344 + if is_simplified:
  345 + # If the field is a choicefield, get choices and associate the index to the real value
  346 + if sequence_form.fields[field].__dict__.get("_choices"):
  347 + # y a pas conflit ici avec la variable "value" définie au-dessus ? -> Non car on transforme l'ancien value qui est un index en une vraie valeur
  348 + values = [value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
  349 + value = values[value]
  350 + else:
  351 + if yaml_field.get("values"):
  352 + # Transform the original value which is an index to a "real" value from the "values" attributes
  353 + index_value = yaml_field["value"]
  354 + values = yaml_field["values"]
  355 + if index_value < 0 or index_value > len(yaml_field["values"]):
  356 + result["errors"].append(f"Value of {field} isn't valid, index out of bounds ({index_value} > {len(values)})")
  357 + index_value = 0
  358 + value = yaml_field["values"][index_value]
  359 + else:
  360 + # Transform the string value to a datetime value
  361 + if field == "start_date":
  362 + if type(value) != datetime.datetime:
  363 + #value = datetime.datetime.strptime(yaml_content["sequence"][field]["value"],'%d/%m/%Y %H:%M:%S')
  364 + # ISO format
  365 + value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
  366 + seq.__dict__[field] = value
  367 + ''' (orig)
  368 + else:
  369 + if not is_simplified:
  370 + if yaml_content["sequence"][field].get("values"):
  371 + index_value = yaml_content["sequence"][field]["value"]
  372 + values = yaml_content["sequence"][field]["values"]
  373 + if index_value < 0 or index_value > len(yaml_content["sequence"][field]["values"]):
  374 + result["errors"].append(f"Value of {field} isn't valid, index out of bounds ({index_value} > {len(values)})")
  375 + index_value = 0
  376 + value = yaml_content["sequence"][field]["values"][index_value]
  377 + else:
  378 + if field == "start_date":
  379 + if type(value) != datetime.datetime:
  380 + #value = datetime.datetime.strptime(yaml_content["sequence"][field]["value"],'%d/%m/%Y %H:%M:%S')
  381 + # ISO format
  382 + value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
  383 + seq.__dict__[field] = value
  384 + else:
  385 + if sequence_form.fields[field].__dict__.get("_choices"):
  386 + values = [value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
  387 + value = values[value]
  388 + '''
  389 +
  390 + # suffisant ? => if field in seq.__dict__
  391 + # If field is an attribute of the sequence, associate the field to the value
  392 + if field in seq.__dict__:
  393 + seq.__dict__[field] = value
  394 + else:
  395 + # else associate field & value in config_attributes sequence's field (JsonField) = variable fields of an sequence
  396 + seq.config_attributes[field] = value
  397 +
386 398  
387 399 def create_sequence_pickle(sequence):
388 400 seq_dict = model_to_dict(sequence)
... ... @@ -404,4 +416,6 @@ def create_sequence_pickle(sequence):
404 416 if not os.path.exists(data_path +f"sequences_pickle/P{period.id}/{sequence.night_id}"):
405 417 os.mkdir(data_path +f"sequences_pickle/P{period.id}/{sequence.night_id}")
406 418 seq_pickle_file_name = data_path +f"./sequences_pickle/P{period.id}/{sequence.night_id}/{sequence.id}.p"
  419 + # get guitastro ephemeris
  420 + #fullseq_dict["ephem"] = fn.ephem(sequence.target)
407 421 pickle.dump(fullseq_dict,open(seq_pickle_file_name,"wb"))
408 422 \ No newline at end of file
... ...