Blame view

src/core/pyros_django/seq_submit/functions.py 25.5 KB
eefbbbd2   Etienne Pallier   Model splitting g...
1
# Standard import
ad3b297c   Alexis Koralewski   add pagination to...
2
import ast
ad3b297c   Alexis Koralewski   add pagination to...
3
4
import datetime
import os, yaml
13b26571   Alexis Koralewski   Add night_id to s...
5
import pickle
eefbbbd2   Etienne Pallier   Model splitting g...
6
# Django imports
13b26571   Alexis Koralewski   Add night_id to s...
7
from django.forms.models import model_to_dict
b95a693f   Alexis Koralewski   restructuration d...
8
9
from user_mgmt.models import PyrosUser, ScientificProgram, Period
from seq_submit.models import Sequence, Album, Plan
eefbbbd2   Etienne Pallier   Model splitting g...
10
from .forms import SequenceForm, PlanForm   #, AlbumForm
8a9bdc27   Alexis Koralewski   add transaction a...
11
from django.db import IntegrityError, transaction
eefbbbd2   Etienne Pallier   Model splitting g...
12
# Project imports
b95a693f   Alexis Koralewski   restructuration d...
13
from src.core.pyros_django.obs_config.obsconfig_class import OBSConfig
13b26571   Alexis Koralewski   Add night_id to s...
14
from django.http import HttpRequest
23a61124   Alexis Koralewski   Change obsconfig ...
15
from dashboard.config_pyros import ConfigPyros
545ecb30   Alexis Koralewski   Add ephem to pick...
16
17
# guitastro import
import vendor.guitastro.src.guitastro as guitastro 
793d0190   Alexis Koralewski   rework routine_ma...
18
#from silk.profiling.profiler import silk_profile
eefbbbd2   Etienne Pallier   Model splitting g...
19

5c709a6e   Alexis Koralewski   Add ephemeris cal...
20
21
import numpy

793d0190   Alexis Koralewski   rework routine_ma...
22
#@silk_profile(name="check_sequence_file")
8a9bdc27   Alexis Koralewski   add transaction a...
23
@transaction.atomic
13b26571   Alexis Koralewski   Add night_id to s...
24
def check_sequence_file_validity_and_save(yaml_content: dict, request: HttpRequest):
1e252185   Etienne Pallier   petites optimisat...
25
26
    ''' Create a sequence in DB from the uploaded sequence (yaml_content) '''

1e252185   Etienne Pallier   petites optimisat...
27
    # Create a sequence seq object (from yaml_content) to be saved in DB
ad3b297c   Alexis Koralewski   add pagination to...
28
29
    seq = Sequence.objects.create()
    seq.pyros_user = PyrosUser.objects.get(id=request.user.id)
1e252185   Etienne Pallier   petites optimisat...
30
31
    
    # Get the unit config
ad3b297c   Alexis Koralewski   add pagination to...
32
    unit_name = os.environ["unit_name"]
793d0190   Alexis Koralewski   rework routine_ma...
33
    #with silk_profile(name="init obsconfig"):
ad3b297c   Alexis Koralewski   add pagination to...
34
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
ad3b297c   Alexis Koralewski   add pagination to...
35
36
37
38
    result = {
        "succeed": True,
        "errors": [],
    }
793d0190   Alexis Koralewski   rework routine_ma...
39
40
41
42
    # 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)
    is_simplified = yaml_content.get("simplified", False)
    # Get scientific programs for the user who is submitting the sequence file
    user_sp = request.user.get_scientific_programs()
8a9bdc27   Alexis Koralewski   add transaction a...
43

793d0190   Alexis Koralewski   rework routine_ma...
44
    process_sequence(yaml_content, seq, config, is_simplified, result, user_sp)
793d0190   Alexis Koralewski   rework routine_ma...
45
    process_albums(yaml_content, result, config, seq, is_simplified)
8a9bdc27   Alexis Koralewski   add transaction a...
46

1e252185   Etienne Pallier   petites optimisat...
47
        
ea36dc1d   Etienne Pallier   2e petit changeme...
48
49
50
51
52
    # optim possible ?
    #[ process_plans(a["Album"].get("Plans")) for a in albums_from_file ]
    # Puis écrire la fonction process_plans()

    # For each Album a (in file)
1e252185   Etienne Pallier   petites optimisat...
53
54
    # Tu itères 2 fois sur albums_from_file, y aurait pas moyen d'iterer 1 seule fois dessus ?
    #for album in yaml_content["sequence"]["ALBUMS"]:
13b26571   Alexis Koralewski   Add night_id to s...
55
56
57
58
    """
    # Old


1e252185   Etienne Pallier   petites optimisat...
59
60
    for album in albums_from_file:

ad3b297c   Alexis Koralewski   add pagination to...
61
62
        album = album["Album"]
        plans = album.get("Plans")
13b26571   Alexis Koralewski   Add night_id to s...
63
        # If no plan has been defined
ad3b297c   Alexis Koralewski   add pagination to...
64
65
        if plans == None:
            result["errors"].append(f"Album {album['name']} has no plans. Please add at least one plan")
059447d3   Etienne Pallier   petit changement ...
66
67
            # pour eviter le else (plus lisible)
            continue
13b26571   Alexis Koralewski   Add night_id to s...
68
69
        plans = [a["Album"].get("Plans") for a in albums_from_file]
        process_plans(plans, result, is_simplified, config, album, seq)
ea36dc1d   Etienne Pallier   2e petit changeme...
70
        # For each plan p (in album a)
059447d3   Etienne Pallier   petit changement ...
71
        for plan in plans:
13b26571   Alexis Koralewski   Add night_id to s...
72
            new_plan_object = Plan.objects.create(album=Album.objects.get(name=album["name"], sequence=seq), complete=True)
059447d3   Etienne Pallier   petit changement ...
73
74
75
            new_plan_object.config_attributes = {}
            plan = plan["Plan"]
            config_attributes = {}
13b26571   Alexis Koralewski   Add night_id to s...
76
            plan_form = PlanForm(data_from_config=config.getEditableChannelAttributes(config.unit_name,list(config.get_channels(config.unit_name).keys())[0]),edited_plan=None)
ea36dc1d   Etienne Pallier   2e petit changeme...
77
            # Process each plan field
059447d3   Etienne Pallier   petit changement ...
78
79
80
81
82
83
84
85
86
87
            for field in plan_form.fields:
                plan_field = plan[field]
                '''
                min_value = None
                max_value = None
                value_type = None
                '''
                min_value = max_value = value_type = None
                if field not in plan.keys():
                    result["errors"].append(f"Missing field : '{field}' for plan {plans.index(plan)}")
ea36dc1d   Etienne Pallier   2e petit changeme...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
                    continue
                # TODO : ajouter max_value, min_value, suppression plan et album si invalides
                if not is_simplified:
                    if plan_field.get("value_type"):
                        value_type = plan_field["value_type"]
                        if type(plan_field["value"]) == str and ast.literal_eval(plan_field["value"]) != value_type:
                            result["errors"].append(f"Field {field} value doesn't correspond to the assigned type (type required : {value_type})")
                    if plan_field.get("min_value"):
                        min_value = plan_field["min_value"]
                        if type(min_value) == str:
                            min_value = ast.literal_eval(min_value)
                        '''
                        if type(plan_field["min_value"]) == str:
                            min_value = ast.literal_eval(plan_field["min_value"])
                        else:
059447d3   Etienne Pallier   petit changement ...
103
                            min_value = plan_field["min_value"]
059447d3   Etienne Pallier   petit changement ...
104
                        '''
ea36dc1d   Etienne Pallier   2e petit changeme...
105
106
107
108
109
110
111
                    if plan_field.get("max_value"):
                        max_value = plan_field["max_value"]
                        if type(max_value) == str:
                            max_value = ast.literal_eval(max_value)
                        '''
                        if type(plan_field.get("max_value")) == str:
                            max_value = ast.literal_eval(plan_field["max_value"])
ad3b297c   Alexis Koralewski   add pagination to...
112
                        else:
ea36dc1d   Etienne Pallier   2e petit changeme...
113
                            max_value = plan_field["max_value"]
059447d3   Etienne Pallier   petit changement ...
114
                        '''
ea36dc1d   Etienne Pallier   2e petit changeme...
115
116
117
118
119
                if field == "nb_images":
                    new_plan_object.__dict__[field] = plan_field if is_simplified else plan_field["value"]
                    '''
                    if is_simplified:
                        new_plan_object.__dict__[field] = plan_field    
059447d3   Etienne Pallier   petit changement ...
120
                    else:
ea36dc1d   Etienne Pallier   2e petit changeme...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
                        new_plan_object.__dict__[field] = plan_field["value"]
                    '''
                else:
                    # shortcut possible ?
                    #new_plan_object_field = new_plan_object.config_attributes[field]
                    if is_simplified:
                        new_plan_object.config_attributes[field] = plan_field
                    else:
                        if plan_field.get("values"):
                            index_value = plan_field["value"]
                            values =  plan_field["values"]
                            if index_value < 0 or index_value > len(plan_field["values"]):
                                result["errors"].append(f"Value of Plan field '{field}' isn't valid, index out of bounds ({index_value} > {len(values)})")
                                index_value = 0
                            value = plan_field["values"][index_value]
                            try:
                                # linked values
                                splitted_values = value.split(";")
                                config_attributes[field] = {}
                                for splitted_value in splitted_values:
                                    subkey,subvalue = splitted_value.split(":")
                                    config_attributes[field][subkey] = ast.literal_eval(subvalue)
                            # vaudrait mieux préciser l'exception ici
                            except:
                                # Do nothing, normal string
                                config_attributes[field] = ast.literal_eval(value)
                            new_plan_object.config_attributes[field] = config_attributes[field]
059447d3   Etienne Pallier   petit changement ...
148
                        else:
ea36dc1d   Etienne Pallier   2e petit changeme...
149
150
151
152
153
154
155
                            if max_value and min_value:
                                if plan_field["value"] > max_value:
                                    result["errors"].append(f"Plan field {field} doesn't respect max value")
                                if plan_field["value"] < min_value:
                                    result["errors"].append(f"Plan field {field} doesn't respect min value")
                            new_plan_object.config_attributes[field] = plan_field["value"]
            # end foreach plan field
059447d3   Etienne Pallier   petit changement ...
156
157
158
            new_plan_object.save()
        # end foreach plan
    # end foreach album
13b26571   Alexis Koralewski   Add night_id to s...
159
    """
1e252185   Etienne Pallier   petites optimisat...
160

ad3b297c   Alexis Koralewski   add pagination to...
161
162
    seq.status = Sequence.TOBEPLANNED
    seq.complete = True
98621b46   Alexis Koralewski   add DRF, pyros ap...
163
164
165
166
    period = Period.objects.exploitation_period()
    if Period.objects.next_period() != None and Period.objects.next_period().start_date < seq.start_date.date():
        period = Period.objects.next_period()
    seq.period = period
ef35b851   pyros_astroguita   add eph sun & moo...
167
168
169
170
171
172
173
174
175
176
177
    # Sum seq duration
    duration = 0
    max_duration = 0
    for album in seq.albums.all():
        for plan in album.plans.all():
            duration = plan.nb_images * (plan.config_attributes.get("exposuretime",0) + plan.config_attributes.get("readouttime",0))
            plan.duration = duration
            plan.save()
            if duration >= max_duration:
                max_duration = duration
    seq.duration = max_duration
545ecb30   Alexis Koralewski   Add ephem to pick...
178
179
180
181
182
    fn = guitastro.FileNames()
    home = config.getHome()
    guitastro_home = guitastro.Home(home)
    fn.longitude = guitastro_home.longitude
    seq.night_id = fn.get_night(seq.start_date.isoformat()[:19])
4faed281   Alexis Koralewski   Catch integretyer...
183
184
185
186
    try:
        seq.save()
    except IntegrityError as e:
        result["errors"].append(str(e))
5c709a6e   Alexis Koralewski   Add ephemeris cal...
187
    if len(result["errors"]) > 0:
ad3b297c   Alexis Koralewski   add pagination to...
188
189
        result["succeed"] = False
        seq.delete()
98621b46   Alexis Koralewski   add DRF, pyros ap...
190
191
    else:
        result["sequence_id"] = seq.id
ad3b297c   Alexis Koralewski   add pagination to...
192
193
    return result

793d0190   Alexis Koralewski   rework routine_ma...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
def process_albums(yaml_content, result, config, seq, is_simplified):
    # Create ALBUMS
    albums_from_file = yaml_content["sequence"]["ALBUMS"] 
    chosen_layout = seq.config_attributes["layout"]
    if type(chosen_layout) == int:
        #with silk_profile(name="Get layout from config"):
        layouts = config.get_layouts(config.unit_name)["layouts"]
        chosen_layout = list(layouts)[chosen_layout]
    # Get album of the selected layout
    #with silk_profile(name="Get album of layout from config"):
    layout_albums = config.getLayoutByName(unit_name=config.unit_name, name_of_layout=chosen_layout)["ALBUMS"]
    
    # check if we have all the albums of that layout described in the sequence file
    #with silk_profile(name="Iterate on each album & plan (create)"):
    if len(layout_albums) == len(albums_from_file):
        for album in albums_from_file:
            album = album["Album"]
            if album["name"] not in layout_albums:
5c709a6e   Alexis Koralewski   Add ephemeris cal...
212
                result["errors"].append(f"Album {album['name']} is not in the chosen layout. The available albums on this layout are : {layout_albums}")
793d0190   Alexis Koralewski   rework routine_ma...
213
214
215
216
217
218
219
220
221
            else:
                # Create album 
                Album.objects.create(name=album["name"], sequence=seq, complete=True)
            # Create plan for that album
            plans = album.get("Plans")
            process_plans(plans, result, is_simplified, config, album, seq)
    else:
        result["errors"].append(f"The number of albums doesn't correspond to the chosen layout")

13b26571   Alexis Koralewski   Add night_id to s...
222

793d0190   Alexis Koralewski   rework routine_ma...
223
#@silk_profile(name="process_plans")
13b26571   Alexis Koralewski   Add night_id to s...
224
225
226
227
228
229
def process_plans(plans: dict, result: dict, is_simplified: bool, config: OBSConfig, album: dict, seq: dict):
    if plans == None:
        result["errors"].append(f"Album {album['name']} has no plans. Please add at least one plan")
        # exit function
        return None
    for plan in plans:
793d0190   Alexis Koralewski   rework routine_ma...
230
231
        #new_plan_object = Plan.objects.create(album=Album.objects.get(name=album["name"], sequence=seq), complete=True)
        #new_plan_object.config_attributes = {}
13b26571   Alexis Koralewski   Add night_id to s...
232
        plan = plan["Plan"]
793d0190   Alexis Koralewski   rework routine_ma...
233
        nb_images = 0
13b26571   Alexis Koralewski   Add night_id to s...
234
        config_attributes = {}
793d0190   Alexis Koralewski   rework routine_ma...
235
        #with silk_profile(name="Create plan form"):
13b26571   Alexis Koralewski   Add night_id to s...
236
237
        plan_form = PlanForm(data_from_config=config.getEditableChannelAttributes(config.unit_name, list(config.get_channels(config.unit_name).keys())[0]), edited_plan=None)
        # Process each plan field
793d0190   Alexis Koralewski   rework routine_ma...
238
        #with silk_profile(name="iterate on plan fields"):
13b26571   Alexis Koralewski   Add night_id to s...
239
240
241
242
243
244
245
        for field in plan_form.fields:
            plan_field = plan[field]
            '''
            min_value = None
            max_value = None
            value_type = None
            '''
13b26571   Alexis Koralewski   Add night_id to s...
246
            if field == "nb_images":
793d0190   Alexis Koralewski   rework routine_ma...
247
                nb_images = plan_field if is_simplified else plan_field["value"]
13b26571   Alexis Koralewski   Add night_id to s...
248
            else:
793d0190   Alexis Koralewski   rework routine_ma...
249
                process_plan_field(result, config_attributes, plan_field, field, plans, plan, is_simplified)                    
13b26571   Alexis Koralewski   Add night_id to s...
250
        # end foreach plan field
5c709a6e   Alexis Koralewski   Add ephemeris cal...
251
252
253
254
        try:
            Plan.objects.create(album=Album.objects.get(name=album["name"], sequence=seq), complete=True, nb_images=nb_images, config_attributes=config_attributes)
        except Album.DoesNotExist:
            result["errors"].append(f"Album {album['name']} not appearing in obsconfig. Please refer to the observatory configuration to set a valid album name. ")
793d0190   Alexis Koralewski   rework routine_ma...
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
def process_plan_field(result, config_attributes, plan_field, field, plans, plan, is_simplified):

    
    if field not in plan.keys():
        result["errors"].append(f"Missing field : '{field}' for plan {plans.index(plan)}")
        # exit function
        return None
    if is_simplified:
        #new_plan_object.config_attributes[field] = plan_field
        config_attributes[field] = plan_field
    else:
        value_type, min_value, max_value = prepare_check_plan_field_value(plan_field, field, result)
        check_and_set_plan_field_value(config_attributes, plan_field, field, result, value_type, min_value, max_value)

        
def check_and_set_plan_field_value(config_attributes, plan_field, field, result, value_type, min_value, max_value):
    # if the value is a index of a list, get the value from this index
    if plan_field.get("values"):
        index_value = plan_field["value"]
        values =  plan_field["values"]
        if index_value < 0 or index_value > len(plan_field["values"]):
            result["errors"].append(f"Value of Plan field '{field}' isn't valid, index out of bounds ({index_value} > {len(values)})")
            index_value = 0
        value = plan_field["values"][index_value]
        try:
            # linked values
            splitted_values = value.split(";")
            config_attributes[field] = {}
            for splitted_value in splitted_values:
                subkey,subvalue = splitted_value.split(":")
                config_attributes[field][subkey] = ast.literal_eval(subvalue)
        # vaudrait mieux préciser l'exception ici
        except ValueError:
            # Do nothing, normal string
            config_attributes[field] = ast.literal_eval(value)
        #new_plan_object.config_attributes[field] = config_attributes[field]
    else:
        # check min and max values if they exist
        if max_value and min_value:
            if plan_field["value"] > max_value:
                result["errors"].append(f"Plan field {field} doesn't respect max value")
            if plan_field["value"] < min_value:
                result["errors"].append(f"Plan field {field} doesn't respect min value")
        #new_plan_object.config_attributes[field] = plan_field["value"]
        config_attributes[field] = plan_field["value"]


def prepare_check_plan_field_value(plan_field, field, result):
    min_value = max_value = value_type = None
    # get value type, min_value and max_value if they're in the plan form
    if plan_field.get("value_type"):
        value_type = plan_field["value_type"]
        # If value type doesn't match with the value from the form, add an error to result
        if type(plan_field["value"]) == str and ast.literal_eval(plan_field["value"]) != value_type:
            result["errors"].append(f"Field {field} value doesn't correspond to the assigned type (type required : {value_type})")
    if plan_field.get("min_value"):
        min_value = plan_field["min_value"]
        if type(min_value) == str:
            min_value = ast.literal_eval(min_value)
    if plan_field.get("max_value"):
        max_value = plan_field["max_value"]
        if type(max_value) == str:
            max_value = ast.literal_eval(max_value)
    return value_type, min_value, max_value


def process_sequence(yaml_content, seq, config, is_simplified, result, user_sp):
    
    # From user sp, get all SP that can observe / submit sequence for the current period
    sp_list = ScientificProgram.objects.observable_programs().filter(id__in=user_sp)
    # Create a Sequence form
    sequence_form = SequenceForm(instance=seq, data_from_config=config.getEditableMountAttributes(config.unit_name), layouts = config.get_layouts(config.unit_name), sp_list=sp_list)

    if is_simplified:
        seq.scientific_program = sp_list[yaml_content["sequence"]["scientific_program"]]    
ef35b851   pyros_astroguita   add eph sun & moo...
330
331
332
        if yaml_content["sequence"].get("id"):
            seq.id = yaml_content["sequence"].get("id")
            seq.save()
793d0190   Alexis Koralewski   rework routine_ma...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
    else:
        # get scientific program field's attributes 
        yaml_seq_sp = yaml_content["sequence"]["scientific_program"]
        sp_index_value = yaml_seq_sp["value"]
        values =  yaml_seq_sp["values"]
        # Check if index of sp is valid (in range of possible index from values)
        if sp_index_value < 0 or sp_index_value > len(values):
            result["errors"].append(f"SP value isn't valid, index out of bounds ({sp_index_value} > {len(values)})")
            sp_index_value = 0
        chosen_sp = ScientificProgram.objects.get(name=values[sp_index_value])
        # If the sp is associated to that user, associate the sp to the sequence
        if chosen_sp in sp_list:
            #seq.scientific_program = ScientificProgram.objects.get(name=yaml_content["sequence"]["scientific_program"]["values"][sp_index_value])
            seq.scientific_program = chosen_sp
        else:
            result["errors"].append(f"SP {chosen_sp.name} is not assigned to that user ")
    
    seq.config_attributes = {}

    # Fill all Sequence form fields
    #with silk_profile(name="iterate sequence fields form"):
    for field, field_attributes in sequence_form.fields.items():
        #if sequence_form.fields[field].required == False or field == "scientific_program":
        if not field_attributes.required or field=="scientific_program":
            continue
        # pour lisibilité, simplicité et éviter redondance
        yaml_field = yaml_content["sequence"][field]
        value = yaml_field if is_simplified else yaml_field["value"]
545ecb30   Alexis Koralewski   Add ephem to pick...
361
        
793d0190   Alexis Koralewski   rework routine_ma...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
        ''' (orig)
        if is_simplified:
            value = yaml_content["sequence"][field]
        else:
            value = yaml_content["sequence"][field]["value"]
        '''
        # If the current field of the sequence isn't found in the file, add an error message to inform the user the missing field
        if field not in yaml_content["sequence"]:
            result["errors"].append(f"{field} not in yaml file")
        else:
            if is_simplified:
                # If the field is a choicefield, get choices and associate the index to the real value
                if sequence_form.fields[field].__dict__.get("_choices"):
                    # 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
                    values = [value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
                    value = values[value]
2dd6bd6c   Alexis Koralewski   try to fix error ...
378
379
380
381
382
383
384
                # Transform the string value to a datetime value
                if field == "start_date":
                    if type(value) != datetime.datetime:
                        #value = datetime.datetime.strptime(yaml_content["sequence"][field]["value"],'%d/%m/%Y %H:%M:%S')
                        # ISO format
                        value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
                        seq.__dict__[field] = value
793d0190   Alexis Koralewski   rework routine_ma...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
            else:
                if yaml_field.get("values"):
                    # Transform the original value which is an index to a "real" value from the "values" attributes
                    index_value = yaml_field["value"]
                    values = yaml_field["values"]
                    if index_value < 0 or index_value > len(yaml_field["values"]):
                        result["errors"].append(f"Value of {field} isn't valid, index out of bounds ({index_value} > {len(values)})")
                        index_value = 0
                    value = yaml_field["values"][index_value]
                else:
                    # Transform the string value to a datetime value
                    if field == "start_date":
                        if type(value) != datetime.datetime:
                            #value = datetime.datetime.strptime(yaml_content["sequence"][field]["value"],'%d/%m/%Y %H:%M:%S')
                            # ISO format
                            value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
                            seq.__dict__[field] = value
        ''' (orig)
        else:
            if not is_simplified:
                if yaml_content["sequence"][field].get("values"):
                    index_value = yaml_content["sequence"][field]["value"]
                    values =  yaml_content["sequence"][field]["values"]
                    if index_value < 0 or index_value > len(yaml_content["sequence"][field]["values"]):
                        result["errors"].append(f"Value of {field} isn't valid, index out of bounds ({index_value} > {len(values)})")
                        index_value = 0
                    value = yaml_content["sequence"][field]["values"][index_value]
                else:
                    if field == "start_date":
                        if type(value) != datetime.datetime:
                            #value = datetime.datetime.strptime(yaml_content["sequence"][field]["value"],'%d/%m/%Y %H:%M:%S')
                            # ISO format
                            value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f')
                            seq.__dict__[field] = value
            else:
                if sequence_form.fields[field].__dict__.get("_choices"):
                    values = [value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
                    value = values[value]
        '''

        # suffisant ? => if field in seq.__dict__
        # If field is an attribute of the sequence, associate the field to the value
        if field in seq.__dict__:
            seq.__dict__[field] = value
        else:
            # else associate field & value in config_attributes sequence's field (JsonField) = variable fields of an sequence
            seq.config_attributes[field] = value

13b26571   Alexis Koralewski   Add night_id to s...
433
434
435
436
437
438
439
440
441
442
443
def create_sequence_pickle(sequence):
    seq_dict = model_to_dict(sequence)
    fullseq_dict = {
        "sequence":seq_dict,
        "albums": {}
    }
    for album in sequence.albums.all():
        fullseq_dict["albums"][album.name] = {"plans" : []}
        for plan in album.plans.all():
            fullseq_dict["albums"][f"{album.name}"]["plans"].append(model_to_dict(instance=plan))
    period = sequence.period
545ecb30   Alexis Koralewski   Add ephem to pick...
444
445
446
447
448
449
450
451
452
453
    # Old folder & file creation
    # root_project_path = os.environ.get("PROJECT_ROOT_PATH")
    # data_path = root_project_path + "/data/"
    # if not os.path.exists(data_path + "sequences_pickle"):
    #     os.mkdir(data_path +"sequences_pickle")
    # if not os.path.exists(data_path + f"sequences_pickle/P{period.id}"):
    #     os.mkdir(data_path + f"sequences_pickle/P{period.id}")
    # if not os.path.exists(data_path +f"sequences_pickle/P{period.id}/{sequence.night_id}"):
    #     os.mkdir(data_path +f"sequences_pickle/P{period.id}/{sequence.night_id}")
    # seq_pickle_file_name = data_path +f"./sequences_pickle/P{period.id}/{sequence.night_id}/{sequence.id}.p"
793d0190   Alexis Koralewski   rework routine_ma...
454
    # get guitastro ephemeris
545ecb30   Alexis Koralewski   Add ephem to pick...
455
456
457
    unit_name = os.environ["unit_name"]
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
    pyros_config = ConfigPyros(os.environ["pyros_config_file"])
d3e71677   Alexis Koralewski   Put seq & eph fn_...
458
    config.fn.fcontext = "pyros_seq"
5c709a6e   Alexis Koralewski   Add ephemeris cal...
459
    period_id = str(period.id)
545ecb30   Alexis Koralewski   Add ephem to pick...
460
    if len(str(period.id)) < 3:
545ecb30   Alexis Koralewski   Add ephem to pick...
461
462
463
464
465
466
467
468
469
        while len(period_id) < 3:
            period_id = "0" + period_id
    fn_param = {
        "period" : f"P{period_id}",
        "version": "1",
        "unit": config.unit_name,
        "date": sequence.night_id,
        "id_seq": sequence.id
    }
ef35b851   pyros_astroguita   add eph sun & moo...
470
471
472
473
474
475
    test_mode = False
    if sequence.id >= 9990000000:
        # in test mode
        config.fn.rootdir = os.path.abspath(config.fn.rootdir.replace("PRODUCTS/","PRODUCTS/TESTS/", 1))
        test_mode = True

d3e71677   Alexis Koralewski   Put seq & eph fn_...
476
477
    config.fn.fname = config.fn.naming_set(fn_param)
    fpath_name = config.fn.join(config.fn.fname)
545ecb30   Alexis Koralewski   Add ephem to pick...
478
479
    # create dirs if they don't exist
    os.makedirs(os.path.dirname(fpath_name), exist_ok=True)
b95a693f   Alexis Koralewski   restructuration d...
480
    print(fpath_name)
545ecb30   Alexis Koralewski   Add ephem to pick...
481
482
483
    eph = guitastro.Ephemeris()
    eph.set_home(config.getHome())
    # duskelev a parametrer dans obsconfig (yml)
2c993be1   pyros_astroguita   add real dusk_eve...
484
    _, duskelev = config.getDuskElev()
5c709a6e   Alexis Koralewski   Add ephemeris cal...
485
    errors = []
545ecb30   Alexis Koralewski   Add ephem to pick...
486
    try:
3ee14364   Alexis Koralewski   Sequence two pic...
487
        #fullseq_dict["ephem"] = eph.target2night(fullseq_dict["sequence"]["config_attributes"]["target"], sequence.night_id, None, None, preferance=sequence.start_expo_pref, duskelev=duskelev)
ef35b851   pyros_astroguita   add eph sun & moo...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
        # change fcontext to eph context
        config.fn.fcontext = "pyros_eph"
        if test_mode:
            config.fn.rootdir = os.path.abspath(config.fn.rootdir.replace("PRODUCTS/","PRODUCTS/TESTS/", 1))
        eph_root_dir = config.fn.rootdir
        fn_param["target"] = "sun"
        config.fn.fname = config.fn.naming_set(fn_param)
        sun_eph_fpath = config.fn.join(config.fn.fname)
        fn_param["target"] = "moon"
        config.fn.fname = config.fn.naming_set(fn_param)
        moon_eph_fpath = config.fn.join(config.fn.fname)
        # open eph files
        sun_eph = pickle.load(open(sun_eph_fpath,"rb"))
        moon_eph = pickle.load(open(moon_eph_fpath,"rb"))
        ephem = eph.target2night(fullseq_dict["sequence"]["config_attributes"]["target"], sequence.night_id, sun_eph, moon_eph, preference=sequence.start_expo_pref, duskelev=duskelev)
5c709a6e   Alexis Koralewski   Add ephemeris cal...
503
504
505
506
507
508
509
510
511
512
    except ValueError:
        errors.append("Target value is not valid")
    except guitastro.ephemeris.EphemerisException as ephemException:
        errors.append(str(ephemException))
    if len(errors) == 0 and numpy.sum(ephem["visibility"]) == 0 :
        errors.append("Target is not visible.")
    if len(errors) == 0:
        pickle.dump(ephem, open(f"{fpath_name[:-2]}.f","wb"))
        pickle.dump(fullseq_dict, open(fpath_name,"wb"))
    return errors