Blame view

src/core/pyros_django/routine_manager/views.py 42.3 KB
0318e3c9   Alexis Koralewski   Add tests for F05...
1
from django.core.paginator import Paginator
ff76bcca   Alexis Koralewski   Adding Sequences ...
2
import pickle
0318e3c9   Alexis Koralewski   Add tests for F05...
3
4
5
6
7
8
from src.pyros_logger import log
from pprint import pprint
import mimetypes
from django.http import HttpResponse
from django.utils.encoding import smart_str
from wsgiref.util import FileWrapper
ad3b297c   Alexis Koralewski   add pagination to...
9
from collections import OrderedDict
1cffbf1c   Etienne Pallier   moved pyros_djang...
10
11
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
3b81a22b   Alexis Koralewski   Rework on Request...
12
from django.shortcuts import get_object_or_404, render, redirect
43e6176d   Etienne Pallier   Model splitting g...
13

1cffbf1c   Etienne Pallier   moved pyros_djang...
14
from common.models import *
43e6176d   Etienne Pallier   Model splitting g...
15
from routine_manager.models import *
1cffbf1c   Etienne Pallier   moved pyros_djang...
16
from django.db.models import Q
43e6176d   Etienne Pallier   Model splitting g...
17

1cffbf1c   Etienne Pallier   moved pyros_djang...
18
from django.contrib.auth.decorators import login_required
ad3b297c   Alexis Koralewski   add pagination to...
19
from src.core.pyros_django.dashboard.decorator import level_required
0318e3c9   Alexis Koralewski   Add tests for F05...
20
21
22
import ast
import os
import datetime
3b81a22b   Alexis Koralewski   Rework on Request...
23

ff76bcca   Alexis Koralewski   Adding Sequences ...
24
from django.forms.models import model_to_dict
ad3b297c   Alexis Koralewski   add pagination to...
25
from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig
43e6176d   Etienne Pallier   Model splitting g...
26
27
28
29

from .forms import SequenceForm, AlbumForm, PlanForm, uneditablePlanForm
#from .forms import RequestForm, SequenceForm, AlbumForm, PlanForm, uneditablePlanForm

1cffbf1c   Etienne Pallier   moved pyros_djang...
30
from .validators import check_plan_validity, check_album_validity, check_sequence_validity, check_request_validity
10252292   Alexis Koralewski   Change function t...
31
from .functions import check_sequence_file_validity_and_save
1cffbf1c   Etienne Pallier   moved pyros_djang...
32
33
from .RequestSerializer import RequestSerializer
import scheduler
0318e3c9   Alexis Koralewski   Add tests for F05...
34
from django.contrib import messages
ad3b297c   Alexis Koralewski   add pagination to...
35
from django.http import JsonResponse
1cffbf1c   Etienne Pallier   moved pyros_djang...
36
37
""" logger """
from django.conf import settings
4a596eec   Alexis Koralewski   Updating SP, Sequ...
38
# "import utils.Logger as l
0318e3c9   Alexis Koralewski   Add tests for F05...
39
# "log = l.setupLogger("routine_manager-views", "routine_manager-views")
1cffbf1c   Etienne Pallier   moved pyros_djang...
40

0318e3c9   Alexis Koralewski   Add tests for F05...
41
42
import yaml
import json
1cffbf1c   Etienne Pallier   moved pyros_djang...
43
""" XML Export / Import utils """
1cffbf1c   Etienne Pallier   moved pyros_djang...
44
45
46
47
SAVED_REQUESTS_FOLDER = "misc/saved_requests/"


@login_required
bce55cd9   Alexis Koralewski   Reworking Sequenc...
48
@level_required("Admin", "Unit-PI", "Observer")
0318e3c9   Alexis Koralewski   Add tests for F05...
49
def sequences_list(request, status=0, message="", import_response=None):
1cffbf1c   Etienne Pallier   moved pyros_djang...
50
51
52
53
54
    """
        Retrieves and display the routines list (routine manager main page)
    """

    if settings.DEBUG:
ad3b297c   Alexis Koralewski   add pagination to...
55
        #log.info("From sequences_list")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
56
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
57
58
59
60
61
62
63
64

    if status == "-1":
        error = True
    elif status == "1":
        success = True

    # TODO: uncomment for alert filter
#     requests_objs = Request.objects.filter(pyros_user=request.user).filter(is_alert=False).order_by("-updated")
bce55cd9   Alexis Koralewski   Reworking Sequenc...
65
66
67
68
69
70
71
72
    sp_of_user = request.user.get_scientific_program()
    sequences_objs = Sequence.objects.filter(scientific_program__in=sp_of_user).order_by("-updated")
    #sp_where_user_is_sp_pi = request.user.get_scientific_program_where_user_is_sp_pi()
    #sequences_objs = Sequence.objects.filter(
    #    pyros_user=request.user).order_by("-updated")
    # if sp_where_user_is_sp_pi.count() > 0:
    #     sequences_objs = sequences_objs | Sequence.objects.filter(
    #         scientific_program__in=sp_where_user_is_sp_pi).order_by("-updated")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
73
    sequences = []
bce55cd9   Alexis Koralewski   Reworking Sequenc...
74
    if request.session.get("role") in ("Admin", "Unit-PI"):
6b3644e7   Alexis Koralewski   Upgrading fontawe...
75
        sequences_objs = Sequence.objects.all().order_by("-updated")
0318e3c9   Alexis Koralewski   Add tests for F05...
76
    paginator = Paginator(sequences_objs, settings.NB_ELEMENT_PER_PAGE)
ad3b297c   Alexis Koralewski   add pagination to...
77
78
79
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)
    for seq in page_obj:
4a596eec   Alexis Koralewski   Updating SP, Sequ...
80
        nb_album = Album.objects.filter(sequence=seq).count
0318e3c9   Alexis Koralewski   Add tests for F05...
81
82
83
84
        nb_plan = Plan.objects.filter(
            album__in=Album.objects.filter(sequence=seq).values("id")).count
        sequences.append(
            {"seq": seq, "nb_album": nb_album, "nb_plan": nb_plan})
3b81a22b   Alexis Koralewski   Rework on Request...
85

4a596eec   Alexis Koralewski   Updating SP, Sequ...
86
    #print("REQ/views: Les requetes sont:")
3b81a22b   Alexis Koralewski   Rework on Request...
87
88
    # requests_objs = Request.objects.filter(pyros_user=request.user).order_by("-updated")
    # print("REQ/views: Les requetes sont:")
1cffbf1c   Etienne Pallier   moved pyros_djang...
89

3b81a22b   Alexis Koralewski   Rework on Request...
90
91
92
93
94
95
96
97
98
    # requests = []

    # for req in requests_objs:
    #     #print("- REQ/views: requete", req)
    #     sequences = req.sequences
    #     nb_executed = sequences.filter(status=Sequence.EXECUTED).count
    #     nb_cancelled = sequences.filter(Q(status=Sequence.INVALID) | Q(status=Sequence.CANCELLED)
    #                                     | Q(status=Sequence.UNPLANNABLE)).count()
    #     requests.append({'req': req, 'nb_seq': sequences.count(), 'nb_executed': nb_executed, 'nb_cancelled': nb_cancelled})
1cffbf1c   Etienne Pallier   moved pyros_djang...
99
    print("REQ/views: ICI:")
ad3b297c   Alexis Koralewski   add pagination to...
100
101
    # opens template src/routine_manager/templates/routine_manager/sequences_list.html with all local variables
    return render(request, "routine_manager/sequences_list.html", locals())
1cffbf1c   Etienne Pallier   moved pyros_djang...
102

0318e3c9   Alexis Koralewski   Add tests for F05...
103

1cffbf1c   Etienne Pallier   moved pyros_djang...
104
105
106
107
108
109
110
111
112
113
@login_required
def action_request(request, req_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given request
    """

    req = Request.objects.get(id=req_id)
    depth_level = 1

    if settings.DEBUG:
4a596eec   Alexis Koralewski   Updating SP, Sequ...
114
115
        #log.info("From action_request")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

    if status == "-1":
        error = True
    elif status == "1":
        success = True

    if req.submitted == True and action == "edit":
        error = True
        message = "You can't edit a submitted request"
        action = "view"

    if check_request_validity(req) == True:
        return redirect(unsubmit_request, req_id)
    if action == "edit":
        form = RequestForm(instance=req)
        edit = True
        return render(request, "routine_manager/view_request.html", locals())
    elif action == "view":
        form = RequestForm(instance=req, readonly=True)
        edit = False
        return render(request, "routine_manager/view_request.html", locals())
    elif action == "delete":
        req.delete()
ad3b297c   Alexis Koralewski   add pagination to...
139
        return redirect(sequences_list, status='1', message="Successfully deleted the request")
1cffbf1c   Etienne Pallier   moved pyros_djang...
140

ad3b297c   Alexis Koralewski   add pagination to...
141
    return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
142

0318e3c9   Alexis Koralewski   Add tests for F05...
143

1cffbf1c   Etienne Pallier   moved pyros_djang...
144
145
146
147
148
149
150
151
152
153
154
155
@login_required
def request_validate(request, req_id):
    """
        Called when the request form was validated.
        Possible actions : Cancel, Save, Save and add sequence, Delete
    """

    action = request.POST.get("action")
    req = Request.objects.get(id=req_id)
    form = RequestForm(instance=req, data=request.POST)

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
156
157
        #log.info("From request_validate")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
158
159
160
161

    if action == "cancel":
        if req.name == "New request":
            req.delete()
ad3b297c   Alexis Koralewski   add pagination to...
162
        return redirect(sequences_list, status=1, message="Cancelled request modification")
1cffbf1c   Etienne Pallier   moved pyros_djang...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    elif action == "delete":
        return redirect(action_request, req_id, "delete")
    elif form.is_valid():
        req = form.save()
        if action == "save":
            return redirect(action_request, req_id=req_id, action="edit", status=1, message="Request saved")
        if action == "save_and_add":
            return redirect(create_sequence, req_id=req_id)
    else:
        error = True
        message = "Please check your field's validity"
        depth_level = 1
        edit = True
        return render(request, "routine_manager/view_request.html", locals())


@login_required
bce55cd9   Alexis Koralewski   Reworking Sequenc...
180
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
181
182
183
184
def action_sequence(request, seq_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given sequence
    """
3b81a22b   Alexis Koralewski   Rework on Request...
185
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
186
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
187
188
    seq_id = int(seq_id)
    seq = Sequence.objects.get(id=seq_id)
3b81a22b   Alexis Koralewski   Rework on Request...
189
    depth_level = 1
1cffbf1c   Etienne Pallier   moved pyros_djang...
190
191

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
192
193
        #log.info("From action_sequence")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
194
195
196
197
198

    if status == "-1":
        error = True
    elif status == "1":
        success = True
ad3b297c   Alexis Koralewski   add pagination to...
199
    if seq.status != Sequence.DRAFT and action == "edit":
c797f02d   Alexis Koralewski   Adding unsubmit b...
200
201
202
203
204
205
        pass
        # error = True
        # message = "You can't edit a submitted request"
        # messages.add_message(
        #     request, messages.WARNING, "You can't edit a submitted request")
        # action = "view"
1cffbf1c   Etienne Pallier   moved pyros_djang...
206

4a596eec   Alexis Koralewski   Updating SP, Sequ...
207
208
209
    horizon_line = config.getHorizonLine(config.unit_name)
    horizon_line_svg = f'<svg id="horizonline" width="30vw" height="5vh">\
    <line x1="{horizon_line[0][0]}" y1="{horizon_line[0][1]}" x2="{horizon_line[1][0]}" y2="{horizon_line[1][1]}" stroke="black" fontsize="2em"/>\
1b822613   Alexis Koralewski   small ui change o...
210
    </svg><p> (values : {horizon_line})</p>'
bce55cd9   Alexis Koralewski   Reworking Sequenc...
211
    if request.session.get("role") in ("Admin", "Unit-PI"):
ad3b297c   Alexis Koralewski   add pagination to...
212
        sp_list = ScientificProgram.objects.observable_programs()
4a596eec   Alexis Koralewski   Updating SP, Sequ...
213
214
215
    else:
        sp_of_user = request.user.get_scientific_program()
        sp_list = ScientificProgram.objects.observable_programs().filter(id__in=sp_of_user)
bce55cd9   Alexis Koralewski   Reworking Sequenc...
216
    if seq.pyros_user != None and seq.scientific_program != None and request.session.get("role") == "Observer" and seq.scientific_program not in sp_of_user:
0318e3c9   Alexis Koralewski   Add tests for F05...
217
218
219
        messages.add_message(
            request, messages.INFO, "You can't acces to this page because this sequence is linked to a scientific program that you aren't part of")
        return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
220
    if action == "edit":
0318e3c9   Alexis Koralewski   Add tests for F05...
221
222
223
        if len(sp_list) == 0:
            messages.add_message(
                request, messages.INFO, "Can't submit a Sequence : there is no scientific program to be assigned with")
ad3b297c   Alexis Koralewski   add pagination to...
224
            return redirect(sequences_list)
0318e3c9   Alexis Koralewski   Add tests for F05...
225
226
        form = SequenceForm(instance=seq, data_from_config=config.getEditableAttributesOfMount(
            config.unit_name), layouts=config.get_layouts(config.unit_name), sp_list=sp_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
227
228
229
        edit = True
        return render(request, "routine_manager/view_sequence.html", locals())
    elif action == "view":
0318e3c9   Alexis Koralewski   Add tests for F05...
230
        form = SequenceForm(instance=seq, data_from_config=config.getEditableAttributesOfMount(
860eb6af   Alexis Koralewski   Adding 3 full seq...
231
            config.unit_name), layouts=config.get_layouts(config.unit_name), sp_list=sp_list, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
232
233
234
        edit = False
        return render(request, "routine_manager/view_sequence.html", locals())
    elif action == "delete":
0318e3c9   Alexis Koralewski   Add tests for F05...
235
236
237
238
239
240
241
        if seq.status in (Sequence.DRAFT, Sequence.TOBEPLANNED):
            log.info(
                f"User {request.user} did action delete sequence {seq.name}")
            seq.delete()
            # if check_sequence_validity(seq) == True:
            # return redirect(unsubmit_request, req_id)
            return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
242

ad3b297c   Alexis Koralewski   add pagination to...
243
    return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
244

0318e3c9   Alexis Koralewski   Add tests for F05...
245

1cffbf1c   Etienne Pallier   moved pyros_djang...
246
@login_required
bce55cd9   Alexis Koralewski   Reworking Sequenc...
247
@level_required("Admin", "Unit-PI", "Observer")
c797f02d   Alexis Koralewski   Adding unsubmit b...
248
249
250
251
252
253
254
255
256
257
258
259
260
def unsubmit_sequence(request, seq_id):
    seq = Sequence.objects.get(id=int(seq_id))
    status = seq.status 
    message = ""
    if status == Sequence.TOBEPLANNED:
        seq.status = Sequence.DRAFT
        seq.save()
        message = "Sequence unsubmitted"
    else:
        message = "The sequence isn't submitted."
    messages.add_message(request,messages.INFO,message)
    return redirect(action_sequence, seq_id, "view")
    
ff76bcca   Alexis Koralewski   Adding Sequences ...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
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
    if not os.path.exists("sequences_pickle"):
        os.mkdir("./sequences_pickle")
    if not os.path.exists(f"sequences_pickle/P{period.id}"):
        os.mkdir(f"sequences_pickle/P{period.id}")
    seq_pickle_file_name = f"./sequences_pickle/P{period.id}/{sequence.name}.p"
    pickle.dump(fullseq_dict,open(seq_pickle_file_name,"wb"))
c797f02d   Alexis Koralewski   Adding unsubmit b...
278
279

@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
280
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
281
282
283
284
285
286
287
def sequence_validate(request, seq_id):
    """
        Called when the sequence form was validated.
        Possible actions : Cancel, Save, Save and add album, Delete
    """

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
288
289
        #log.info("From sequence_validate")
        pass
3b81a22b   Alexis Koralewski   Rework on Request...
290
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
291
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
292
293
294
    seq_id = int(seq_id)
    action = request.POST.get("action")
    seq = Sequence.objects.get(id=seq_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
295
296
    form = SequenceForm(instance=seq, data=request.POST, data_from_config=config.getEditableAttributesOfMount(
        config.unit_name), layouts=config.get_layouts(config.unit_name))
bce55cd9   Alexis Koralewski   Reworking Sequenc...
297
298
    seq.last_modified_by = request.user
    seq.save()
1cffbf1c   Etienne Pallier   moved pyros_djang...
299
    if action == "cancel":
c797f02d   Alexis Koralewski   Adding unsubmit b...
300
        seq.delete()
ad3b297c   Alexis Koralewski   add pagination to...
301
        return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
302
303
    elif action == "delete":
        return redirect(action_sequence, seq_id, "delete")
d42ec18b   Alexis Koralewski   disabling submit ...
304
305
    elif action == "check_validity":
        seq.save()
860eb6af   Alexis Koralewski   Adding 3 full seq...
306
        is_seq_valid = check_sequence_validity(seq)
d42ec18b   Alexis Koralewski   disabling submit ...
307
308
309
        for album in Album.objects.filter(sequence=seq):
            for plan in Plan.objects.filter(album=album):
                check_plan_validity(plan)
860eb6af   Alexis Koralewski   Adding 3 full seq...
310
311
312
313
        message = ""
        if is_seq_valid == True:
            message = f"The sequence is valid and can be submitted."
        else:
c797f02d   Alexis Koralewski   Adding unsubmit b...
314
315
            message = f"The sequence isn't valid. Check if your sequence have, at least, an album and that album has one plan at minimum.\
                Each album needs at least one plan."
860eb6af   Alexis Koralewski   Adding 3 full seq...
316
        messages.add_message(request, messages.INFO, message)
0318e3c9   Alexis Koralewski   Add tests for F05...
317
        return redirect(action_sequence, seq_id, "edit")
1cffbf1c   Etienne Pallier   moved pyros_djang...
318
319
320
    elif form.is_valid():
        seq = form.save()
        if action == "save":
0318e3c9   Alexis Koralewski   Add tests for F05...
321
322
            message = "Sequence saved"
            messages.add_message(request, messages.INFO, message)
1ba49504   Alexis Koralewski   fixing CSS and JS...
323
324
325
            return redirect(sequences_list, status=1, message="Sequence saved")
            # return redirect(action_sequence, seq_id=seq_id, action="edit", status=1, message="Sequence saved")
        elif action == "save_and_do_nothing":
1cffbf1c   Etienne Pallier   moved pyros_djang...
326
327
            return redirect(action_sequence, seq_id=seq_id, action="edit", status=1, message="Sequence saved")
        if action == "save_and_add":
0318e3c9   Alexis Koralewski   Add tests for F05...
328
329
            albums = config.getLayoutByName(
                unit_name=config.unit_name, name_of_layout=seq.config_attributes["layout"])["ALBUMS"]
cca89707   Alexis Koralewski   adapting observat...
330
            for album_name in albums:
0318e3c9   Alexis Koralewski   Add tests for F05...
331
332
333
334
335
336
337
                album_desc = config.getAlbumByName(
                    config.unit_name, album_name).get("description", "")
                create_album(request, seq_id,
                             album_name=album_name, desc=album_desc)
            # return redirect(create_album, seq_id=seq_id)
            messages.add_message(request, messages.INFO,
                                 "Created albums according to chosen layouts")
cca89707   Alexis Koralewski   adapting observat...
338
            return redirect(action_sequence, seq.id, "edit")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
339
340
341
342
        if action == "save_and_submit":
            if check_sequence_validity(seq):
                seq.status = Sequence.TOBEPLANNED
                seq.save()
0318e3c9   Alexis Koralewski   Add tests for F05...
343
                message = "Sequence submitted"
ff76bcca   Alexis Koralewski   Adding Sequences ...
344
                create_sequence_pickle(seq)
0318e3c9   Alexis Koralewski   Add tests for F05...
345
346
347
                messages.add_message(request, messages.INFO, message)
                log.info(
                    f"User {request.user} did action submit sequence {seq} for period {seq.period} ")
860eb6af   Alexis Koralewski   Adding 3 full seq...
348
                return redirect(sequences_list)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
349
350
            else:
                message = "Can't submit sequence because it's incomplete (Need at least 1 album with 1 plan)"
0318e3c9   Alexis Koralewski   Add tests for F05...
351
                messages.add_message(request, messages.ERROR, message)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
352
353
                status = 1
                edit = True
0318e3c9   Alexis Koralewski   Add tests for F05...
354
                return render(request, "routine_manager/view_sequence.html", locals())
1cffbf1c   Etienne Pallier   moved pyros_djang...
355
356
357
    else:
        error = True
        message = "Please check your field's validity"
3b81a22b   Alexis Koralewski   Rework on Request...
358
        depth_level = 1
1cffbf1c   Etienne Pallier   moved pyros_djang...
359
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
360
361
        return render(request, "routine_manager/view_sequence.html", locals())

0318e3c9   Alexis Koralewski   Add tests for F05...
362

1cffbf1c   Etienne Pallier   moved pyros_djang...
363
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
364
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
365
366
367
368
def action_album(request, alb_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given album
    """
3b81a22b   Alexis Koralewski   Rework on Request...
369
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
370
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
371
372
373

    alb_id = int(alb_id)
    alb = Album.objects.get(id=alb_id)
1cffbf1c   Etienne Pallier   moved pyros_djang...
374
    seq_id = alb.sequence.id
3b81a22b   Alexis Koralewski   Rework on Request...
375
376
    depth_level = 2
    seq = alb.sequence
1cffbf1c   Etienne Pallier   moved pyros_djang...
377
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
378
379
        #log.info("From action_album")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
380
381
382
383
384
385

    if status == "-1":
        error = True
    elif status == "1":
        success = True

ad3b297c   Alexis Koralewski   add pagination to...
386
    if alb.sequence.status != Sequence.DRAFT and action == "edit":
1cffbf1c   Etienne Pallier   moved pyros_djang...
387
388
389
390
        error = True
        message = "You can't edit a submitted request"
        action = "view"

d42ec18b   Alexis Koralewski   disabling submit ...
391
    # if check_album_validity(alb) == True:
0318e3c9   Alexis Koralewski   Add tests for F05...
392
        # return redirect(unsubmit_request, req_id)
d42ec18b   Alexis Koralewski   disabling submit ...
393
        # pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
394
    if action == "edit":
0318e3c9   Alexis Koralewski   Add tests for F05...
395
        form = AlbumForm(instance=alb, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
396
397
398
        edit = True
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "view":
cca89707   Alexis Koralewski   adapting observat...
399
        form = AlbumForm(instance=alb, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
400
401
402
        edit = False
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "delete":
0318e3c9   Alexis Koralewski   Add tests for F05...
403
404
        log.info(
            f"User {request.user} did action delete Album {alb.name} of Sequence {seq}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
405
406
407
        alb.delete()
        return redirect(action_sequence, seq_id=alb.sequence.id, action="edit", status='1', message="Successfully deleted the album")

ad3b297c   Alexis Koralewski   add pagination to...
408
    return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
409

0318e3c9   Alexis Koralewski   Add tests for F05...
410

1cffbf1c   Etienne Pallier   moved pyros_djang...
411
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
412
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
413
414
415
416
417
def album_validate(request, alb_id):
    """
        Called when the album form was validated.
        Possible actions : Cancel, Save, Save and add plan, Delete
    """
3b81a22b   Alexis Koralewski   Rework on Request...
418
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
419
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
420
421
422
    alb_id = int(alb_id)
    action = request.POST.get("action")
    alb = Album.objects.get(id=alb_id)
cca89707   Alexis Koralewski   adapting observat...
423
    form = AlbumForm(instance=alb, data=request.POST)
bce55cd9   Alexis Koralewski   Reworking Sequenc...
424
425
    alb.sequence.last_modified_by = request.user
    alb.sequence.save()
1cffbf1c   Etienne Pallier   moved pyros_djang...
426
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
427
428
        #log.info("From album_validate")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
429

1cffbf1c   Etienne Pallier   moved pyros_djang...
430
    if action == "cancel":
c797f02d   Alexis Koralewski   Adding unsubmit b...
431
        alb.delete()
1cffbf1c   Etienne Pallier   moved pyros_djang...
432
433
434
435
436
437
        return redirect(action_sequence, seq_id=alb.sequence.id, action="edit", status='1', message="Cancelled album modification")
    elif action == "delete":
        return redirect(action_album, alb_id, "delete")
    elif form.is_valid():
        alb = form.save()
        if action == "save":
3b81a22b   Alexis Koralewski   Rework on Request...
438
            return redirect(action_sequence, seq_id=alb.sequence.id, action="edit")
0318e3c9   Alexis Koralewski   Add tests for F05...
439
            # return redirect(action_album, alb_id=alb_id, action="edit", status=1, message="Album saved")
1cffbf1c   Etienne Pallier   moved pyros_djang...
440
441
442
443
444
        if action == "save_and_add":
            return redirect(create_plan, alb_id=alb_id)
    else:
        error = True
        message = "Please check your field's validity"
3b81a22b   Alexis Koralewski   Rework on Request...
445
        depth_level = 2
1cffbf1c   Etienne Pallier   moved pyros_djang...
446
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
447
448
449
450
        seq_id = alb.sequence.id
        action = "edit"
        return render(request, "routine_manager/view_album.html", locals())

0318e3c9   Alexis Koralewski   Add tests for F05...
451

1cffbf1c   Etienne Pallier   moved pyros_djang...
452
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
453
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
454
455
456
457
def action_plan(request, plan_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given plan
    """
3b81a22b   Alexis Koralewski   Rework on Request...
458
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
459
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
460
461
    plan_id = int(plan_id)
    plan = Plan.objects.get(id=plan_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
462
463
464
465
466
    album_name = plan.album.name
    channel = config.getAlbumByName(
        config.unit_name, album_name)["CHANNELS"][0]
    data_from_config = config.getEditableAttributesOfChannel(
        config.unit_name, channel)
1cffbf1c   Etienne Pallier   moved pyros_djang...
467
    seq_id = plan.album.sequence.id
3b81a22b   Alexis Koralewski   Rework on Request...
468
    seq = plan.album.sequence
1cffbf1c   Etienne Pallier   moved pyros_djang...
469
    alb_id = plan.album.id
3b81a22b   Alexis Koralewski   Rework on Request...
470
    depth_level = 3
0318e3c9   Alexis Koralewski   Add tests for F05...
471
472
473
    # get index of plan within the album. Adding one for human comprehension
    index_of_plan_in_album = list(plan.album.plans.all(
    ).values_list("id", flat=True)).index(plan.id) + 1
1cffbf1c   Etienne Pallier   moved pyros_djang...
474
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
475
476
        #log.info("From action_plan")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
477
478
479
480
481
482

    if status == "-1":
        error = True
    elif status == "1":
        success = True

ad3b297c   Alexis Koralewski   add pagination to...
483
    if plan.album.sequence.status != Sequence.DRAFT and action == "edit":
1cffbf1c   Etienne Pallier   moved pyros_djang...
484
        error = True
4a596eec   Alexis Koralewski   Updating SP, Sequ...
485
        message = "You can't edit a submitted sequence"
1cffbf1c   Etienne Pallier   moved pyros_djang...
486
487
        action = "view"

1cffbf1c   Etienne Pallier   moved pyros_djang...
488
    if action == "edit":
3b81a22b   Alexis Koralewski   Rework on Request...
489
        form = PlanForm(edited_plan=plan, data_from_config=data_from_config)
0318e3c9   Alexis Koralewski   Add tests for F05...
490
491
        uneditableForm = uneditablePlanForm(
            data_from_config=config.getUneditableAttributesOfChannel(config.unit_name, channel))
1cffbf1c   Etienne Pallier   moved pyros_djang...
492
493
494
        edit = True
        return render(request, "routine_manager/view_plan.html", locals())
    elif action == "view":
0318e3c9   Alexis Koralewski   Add tests for F05...
495
496
497
498
        form = PlanForm(edited_plan=plan,
                        data_from_config=data_from_config, readonly=True)
        uneditableForm = uneditablePlanForm(
            data_from_config=config.getUneditableAttributesOfChannel(config.unit_name, channel))
1cffbf1c   Etienne Pallier   moved pyros_djang...
499
500
501
        edit = False
        return render(request, "routine_manager/view_plan.html", locals())
    elif action == "delete":
0318e3c9   Alexis Koralewski   Add tests for F05...
502
503
        log.info(
            f"User {request.user} did action delete Plan {plan.id} of Sequence {seq}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
504
        plan.delete()
d42ec18b   Alexis Koralewski   disabling submit ...
505
506
        check_sequence_validity(seq)
        check_album_validity(plan.album)
1cffbf1c   Etienne Pallier   moved pyros_djang...
507
508
        return redirect(action_album, alb_id=plan.album.id, action="edit", status='1', message="Successfully deleted the plan")

ad3b297c   Alexis Koralewski   add pagination to...
509
    return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
510

0318e3c9   Alexis Koralewski   Add tests for F05...
511

1cffbf1c   Etienne Pallier   moved pyros_djang...
512
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
513
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
514
515
516
517
518
def plan_validate(request, plan_id):
    """
        Called when the plan form was validated.
        Possible actions : Cancel, Save, Delete
    """
3b81a22b   Alexis Koralewski   Rework on Request...
519
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
520
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
521
522
523
    plan_id = int(plan_id)
    action = request.POST.get("action")
    plan = Plan.objects.get(id=plan_id)
3b81a22b   Alexis Koralewski   Rework on Request...
524
    seq = plan.album.sequence
bce55cd9   Alexis Koralewski   Reworking Sequenc...
525
526
    seq.last_modified_by = request.user
    seq.save()
d42ec18b   Alexis Koralewski   disabling submit ...
527
528
529
530
531
532
533
    # name_of_channel_group = plan.album.name_of_channel_group
    # channel = None
    # if name_of_channel_group == "All":
    #     channel = config.getLayoutByName(config.unit_name,config.get_layouts(config.unit_name)["groups"][0]["name"])
    # else:
    #     # we retrieve config of the first channel of the group
    #     channel = config.getLayoutByName(config.unit_name,name_of_channel_group)["channels"][0]
0318e3c9   Alexis Koralewski   Add tests for F05...
534
535
536
537
538
    album_name = plan.album.name
    channel = config.getAlbumByName(
        config.unit_name, album_name)["CHANNELS"][0]
    data_from_config = config.getEditableAttributesOfChannel(
        config.unit_name, channel)
3b81a22b   Alexis Koralewski   Rework on Request...
539
540
    #form = PlanForm(instance=plan, data=request.POST)
    form = PlanForm(edited_plan=plan, data_from_config=data_from_config)
1cffbf1c   Etienne Pallier   moved pyros_djang...
541
542

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
543
544
        #log.info("From plan_validate")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
545
    if action == "cancel":
0318e3c9   Alexis Koralewski   Add tests for F05...
546

3b81a22b   Alexis Koralewski   Rework on Request...
547
        plan.delete()
1cffbf1c   Etienne Pallier   moved pyros_djang...
548
549
550
551
        return redirect(action_album, alb_id=plan.album.id, action="edit", status='1', message="Cancelled plan modification")
    elif action == "delete":
        return redirect(action_plan, plan_id, "delete")
    elif form.is_valid():
3b81a22b   Alexis Koralewski   Rework on Request...
552
        # unused
1cffbf1c   Etienne Pallier   moved pyros_djang...
553
554
        plan = form.save()
        if action == "save":
0318e3c9   Alexis Koralewski   Add tests for F05...
555
556
557
            action = "edit"
            status = 1
            message = "Plan saved"
3b81a22b   Alexis Koralewski   Rework on Request...
558
559
560
            return redirect(action_plan, locals())
    elif action == "save":
        if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
561

3b81a22b   Alexis Koralewski   Rework on Request...
562
            post_data = request.POST.copy()
0318e3c9   Alexis Koralewski   Add tests for F05...
563
564
            if post_data.get("csrfmiddlewaretoken"):
                post_data.pop("csrfmiddlewaretoken")
3b81a22b   Alexis Koralewski   Rework on Request...
565
566
567
            post_data.pop("action")
            nb_images = post_data.pop("nb_images")[0]
            config_attributes = {}
0318e3c9   Alexis Koralewski   Add tests for F05...
568
            for key, value in post_data.items():
3b81a22b   Alexis Koralewski   Rework on Request...
569
570
571
                if type(value) == str:
                    try:
                        # linked values
0318e3c9   Alexis Koralewski   Add tests for F05...
572
                        new_dict = {key: {}}
3b81a22b   Alexis Koralewski   Rework on Request...
573
574
575
                        splitted_values = value.split(";")
                        config_attributes[key] = {}
                        for splitted_value in splitted_values:
0318e3c9   Alexis Koralewski   Add tests for F05...
576
577
578
                            subkey, subvalue = splitted_value.split(":")
                            config_attributes[key][subkey] = ast.literal_eval(
                                subvalue)
3b81a22b   Alexis Koralewski   Rework on Request...
579
580
581
582
583
                    except:
                        # Do nothing, normal string
                        config_attributes[key] = ast.literal_eval(value)
            plan.nb_images = int(nb_images)
            plan.config_attributes = config_attributes
3b81a22b   Alexis Koralewski   Rework on Request...
584
            plan.save()
d42ec18b   Alexis Koralewski   disabling submit ...
585
            # if check_plan_validity(plan) == True:
0318e3c9   Alexis Koralewski   Add tests for F05...
586
587
            # return redirect(unsubmit_request, req_id)
            # pass
3b81a22b   Alexis Koralewski   Rework on Request...
588
            return redirect(action_album, plan.album.id, "edit")
0318e3c9   Alexis Koralewski   Add tests for F05...
589
            # return redirect(action_plan, plan_id, "edit", 1, "Plan saved")
1cffbf1c   Etienne Pallier   moved pyros_djang...
590
591
592
593
    else:
        error = True
        message = "Please check your field's validity"
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
594
595
        seq_id = plan.album.sequence.id
        alb_id = plan.album.id
3b81a22b   Alexis Koralewski   Rework on Request...
596
        depth_level = 3
1cffbf1c   Etienne Pallier   moved pyros_djang...
597
598
        return render(request, "routine_manager/view_plan.html", locals())

0318e3c9   Alexis Koralewski   Add tests for F05...
599

1cffbf1c   Etienne Pallier   moved pyros_djang...
600
601
602
603
604
605
@login_required
def create_request(request):
    """
        Create a new request and redirects to the editing action on it
    """

0318e3c9   Alexis Koralewski   Add tests for F05...
606
607
    req = Request.objects.create(pyros_user=request.user, name="New request",
                                 is_alert=False, autodeposit=False, complete=False, submitted=False)
1cffbf1c   Etienne Pallier   moved pyros_djang...
608
609
    return redirect(action_request, req.id, "edit")

0318e3c9   Alexis Koralewski   Add tests for F05...
610

1cffbf1c   Etienne Pallier   moved pyros_djang...
611
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
612
@level_required("Admin", "Unit-PI", "Observer")
3b81a22b   Alexis Koralewski   Rework on Request...
613
def create_sequence(request):
1cffbf1c   Etienne Pallier   moved pyros_djang...
614
615
616
    """
        Create a new sequence and redirects to the editing action on it
    """
3b81a22b   Alexis Koralewski   Rework on Request...
617
618
    date_of_sequence = datetime.datetime.utcnow()
    date_of_sequence = date_of_sequence.strftime("%Y%m%dT%H%M%S")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
619
620
    sp_of_user = request.user.get_scientific_program()
    sp_list = ScientificProgram.objects.observable_programs().filter(id__in=sp_of_user)
0318e3c9   Alexis Koralewski   Add tests for F05...
621
622
623
    if len(sp_list) == 0:
        messages.add_message(
            request, messages.INFO, "Can't submit a Sequence : there is no scientific program to be assigned with")
ad3b297c   Alexis Koralewski   add pagination to...
624
        return redirect(sequences_list)
0318e3c9   Alexis Koralewski   Add tests for F05...
625
626
    seq = Sequence.objects.create(
        pyros_user=request.user, name=f"seq_{str(date_of_sequence)}", status=Sequence.DRAFT)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
627
    log.info(f"User {request.user} did action create Sequence {seq.name}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
628
629
    return redirect(action_sequence, seq.id, "edit")

0318e3c9   Alexis Koralewski   Add tests for F05...
630

1cffbf1c   Etienne Pallier   moved pyros_djang...
631
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
632
633
@level_required("Admin", "Unit-PI", "Observer")
def create_album(request, seq_id, album_name, desc):
1cffbf1c   Etienne Pallier   moved pyros_djang...
634
635
636
637
638
    """
        Create a new album and redirects to the editing action on it
    """

    seq = Sequence.objects.get(id=seq_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
639
640
641
642
643
644
    alb = Album.objects.create(
        sequence=seq, name=album_name, desc=desc, complete=False)
    log.info(
        f"User {request.user} did action create Album {alb.id} of Sequence {seq.name}")
    # return redirect(action_album, alb.id, "edit")

1cffbf1c   Etienne Pallier   moved pyros_djang...
645
646

@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
647
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
648
649
650
651
652
653
def create_plan(request, alb_id):
    """
        Create a new plan and redirects to the editing action on it
    """

    alb = Album.objects.get(id=alb_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
654
655
656
    plan = Plan.objects.create(album=alb, nb_images=1)
    log.info(
        f"User {request.user} did action create Plan {plan.id} of Sequence {plan.album.sequence.name}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
657
658
    return redirect(action_plan, plan.id, "edit")

0318e3c9   Alexis Koralewski   Add tests for F05...
659

1cffbf1c   Etienne Pallier   moved pyros_djang...
660
661
662
663
664
665
666
@login_required
def submit_request(request, req_id, redir):
    """
        Submits a request and its sequences for scheduling
    """

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
667
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
        log.info("From submit_request")

    req = Request.objects.get(id=req_id)
    error = False
    if req.complete == False:
        error = True
        message = "A request must be complete to be submitted"
    elif req.submitted == True:
        error = True
        message = "The request is already submitted"

    if error == True:
        return redirect(action_request, req_id=req_id, action="view", status=-1, message=message)

    for seq in req.sequences.all():
        seq.status = Sequence.TOBEPLANNED
        seq.save()
    req.submitted = True
    req.save()

    # TODO : changer le first_schedule ...
    '''
    if settings.USE_CELERY:
        print("WITH CELERY")
        scheduler.tasks.scheduling.delay(first_schedule=True, alert=False)
    else:
    '''
    print("Change 1st schedule")
    scheduler.tasks.scheduling().run(first_schedule=True, alert=False)

    message = "The request was submitted"
    if redir == "action_request":
        return redirect(action_request, req_id=req_id, action="view", status=1, message=message)
    else:
ad3b297c   Alexis Koralewski   add pagination to...
702
        return redirect(sequences_list, status=1, message=message)
1cffbf1c   Etienne Pallier   moved pyros_djang...
703

0318e3c9   Alexis Koralewski   Add tests for F05...
704

1cffbf1c   Etienne Pallier   moved pyros_djang...
705
706
707
708
709
710
711
@login_required
def unsubmit_request(request, req_id):
    """
        Unsubmits a request and remove its sequences from scheduling
    """

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
712
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
713
714
715
716
717
718
719
720
721
722
723
724
        log.info("From unsubmit_request")

    req = Request.objects.get(id=req_id)

# TODO: uncomment pour la production
#     if req.sequences.filter(Q(status=Sequence.EXECUTED) | Q(status=Sequence.EXECUTING)).exists():
#         message = "You can't unsubmit a request with executed sequences"
#         return redirect(action_request, req_id=req_id, action="view", status=-1, message=message)

    req.submitted = False
    req.save()
    sequences = req.sequences.filter(Q(status=Sequence.TOBEPLANNED) | Q(status=Sequence.PLANNED) |
0318e3c9   Alexis Koralewski   Add tests for F05...
725
                                     Q(status=Sequence.INVALID) | Q(status=Sequence.UNPLANNABLE))
1cffbf1c   Etienne Pallier   moved pyros_djang...
726
727
728
729
730
731
732
733
734
735
736
737
    for seq in sequences:
        seq.status = Sequence.COMPLETE
        seq.save()
    # TODO: uncomment
    # scheduler.tasks.scheduling.delay(first_schedule=True, alert=False)  # TODO : changer le first_schedule ...

    if req.complete == True:
        message = "The request was unsubmitted"
    else:
        message = "The request was unsubmitted because it is incomplete"
    return redirect(action_request, req_id=req_id, action="edit", status=1, message=message)

0318e3c9   Alexis Koralewski   Add tests for F05...
738

1cffbf1c   Etienne Pallier   moved pyros_djang...
739
740
741
742
743
744
745
@login_required
def export_request(request, req_id):
    """
        Create an XML file with the given request, and send a download request to the user
    """

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
746
747
        #log.info("From export_request")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762

    req = Request.objects.get(id=req_id)
    if req.complete == False:
        message = "Request must be completely valid to be serialized"
        return redirect(action_request, req_id=req_id, action="view", status=-1, message=message)

    file_name = SAVED_REQUESTS_FOLDER + "request" + str(req.id) + ".xml"
    rs = RequestSerializer()
    rs.serialize(req, file_name)

    wrapper = FileWrapper(open(file_name, "r"))
    content_type = mimetypes.guess_type(file_name)[0]

    response = HttpResponse(wrapper, content_type=content_type)
    response['Content-Length'] = os.path.getsize(file_name)
0318e3c9   Alexis Koralewski   Add tests for F05...
763
764
    response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(
        os.path.basename(file_name))
1cffbf1c   Etienne Pallier   moved pyros_djang...
765
766
767

    return response

0318e3c9   Alexis Koralewski   Add tests for F05...
768

1cffbf1c   Etienne Pallier   moved pyros_djang...
769
770
771
772
773
774
775
776
777
@login_required
@csrf_exempt
def import_request(request):
    """
        Ask for a XML file, parse it and create a request in DB
        Don't do anything if there is a single error into the file
    """

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
778
779
        pass
        #log.info("From import_request")
1cffbf1c   Etienne Pallier   moved pyros_djang...
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804

    log.info(request)

    if request.method == "POST":
        file = request.FILES.get("request_file")
        if file is None:
            status = -1
            message = "File does not exist"
        elif file.size > 1000000:
            status = -1
            message = "File is too big (more than 1 000 000 bytes)"
        else:
            rs = RequestSerializer()
            message = rs.unserialize(file.read(), request.user)
            if message != "":
                status = -1
            else:
                status = 1
                message = "Request imported successfully. Please check it before submitting for scheduling."
        print("message is", message)
    else:
        status = -1
        message = "Internal error"

    # Now, display the list of requests (updated with this new request)
ad3b297c   Alexis Koralewski   add pagination to...
805
806
807
808
809
810
    return redirect(sequences_list, status=status, message=message)

# @login_required
# def test_create_plan(request):
#     config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"])
#     if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
811

ad3b297c   Alexis Koralewski   add pagination to...
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
#         post_data = request.POST.copy()
#         post_data.pop("csrfmiddlewaretoken")
#         nb_images = post_data.pop("nb_images")[0]
#         config_attributes = {}
#         for key,value in post_data.items():
#             if type(value) == str:
#                 try:
#                     # linked values
#                     new_dict = {key:{}}
#                     splitted_values = value.split(";")
#                     config_attributes[key] = {}
#                     for splitted_value in splitted_values:
#                         subkey,subvalue = splitted_value.split(":")
#                         config_attributes[key][subkey] = ast.literal_eval(subvalue)
#                 except:
#                     # Do nothing, normal string
#                     config_attributes[key] = ast.literal_eval(value)
#         plan = Plan()
#         plan.nb_images = int(nb_images)
#         plan.config_attributes = config_attributes
#         plan.save()
#     form = PlanForm(data_from_config=config.getEditableAttributesOfChannel(config.unit_name,list(config.get_channels(config.unit_name).keys())[0]))
#     uneditableForm = uneditablePlanForm(data_from_config=config.getUneditableAttributesOfChannel(config.unit_name,list(config.get_channels(config.unit_name).keys())[0]))
#     return render(request,"routine_manager/testcreateplan.html",{"form":form,"uneditableForm":uneditableForm})


3b81a22b   Alexis Koralewski   Rework on Request...
838
@login_required
ad3b297c   Alexis Koralewski   add pagination to...
839
@csrf_exempt
0318e3c9   Alexis Koralewski   Add tests for F05...
840
@level_required("Admin", "Unit-PI", "Observer")
ad3b297c   Alexis Koralewski   add pagination to...
841
842
843
844
845
def import_sequence(request):
    """
        Ask for a YAML file, parse it and create a request in DB
        Returns a JSON that contains at least, success boolean and errors if this boolean is false
    """
ad3b297c   Alexis Koralewski   add pagination to...
846
847
848
849
850
851
852
853
854
855
856
    if request.method == "POST":
        file = request.FILES.get("sequence_file")
        status = 0
        if file is None:
            status = -1
            message = "File does not exist"
        elif file.size > 1000000:
            status = -1
            message = "File is too big (more than 1 000 000 bytes)"
        else:
            yaml_content = yaml.safe_load(file.read())
10252292   Alexis Koralewski   Change function t...
857
            import_response = check_sequence_file_validity_and_save(
0318e3c9   Alexis Koralewski   Add tests for F05...
858
                yaml_content, request)
ad3b297c   Alexis Koralewski   add pagination to...
859
860
861
862
863
864
865
866
867
868
869
870
871
872
            if import_response["succeed"]:
                message = "Request imported successfully. Please check it before submitting for scheduling."
            else:
                message = "Failed to import, check errors"
        print("message is", message)
    else:
        status = -1
        message = "Internal error"
    return JsonResponse(import_response)
    # Now, display the list of requests (updated with this new request)
    return redirect(sequences_list, status=status, message=message, import_response=import_response)


@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
873
874
@level_required("Admin", "Unit-PI", "Observer")
def export_sequence(request, seq_id: int, type: str):
ad3b297c   Alexis Koralewski   add pagination to...
875
876
877
878
879
    sp_of_user = request.user.get_scientific_program()
    sp_list = ScientificProgram.objects.observable_programs().filter(id__in=sp_of_user)
    seq = Sequence.objects.get(id=seq_id)
    unit_name = os.environ["unit_name"]
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
0318e3c9   Alexis Koralewski   Add tests for F05...
880
881
    sequence_form = SequenceForm(instance=seq, data_from_config=config.getEditableAttributesOfMount(
        config.unit_name), layouts=config.get_layouts(config.unit_name), sp_list=sp_list)
ad3b297c   Alexis Koralewski   add pagination to...
882
    yaml_dict = {
0318e3c9   Alexis Koralewski   Add tests for F05...
883
        "sequence": {}
ad3b297c   Alexis Koralewski   add pagination to...
884
    }
0318e3c9   Alexis Koralewski   Add tests for F05...
885

ad3b297c   Alexis Koralewski   add pagination to...
886
887
888
    for field in sequence_form.fields.keys():
        if sequence_form.fields[field].required == False:
            continue
0318e3c9   Alexis Koralewski   Add tests for F05...
889
        field_dict = {"editable": True}
ad3b297c   Alexis Koralewski   add pagination to...
890
        if field == "scientific_program":
0318e3c9   Alexis Koralewski   Add tests for F05...
891
            sp_list = list(sp_list.values_list("name", flat=True))
ad3b297c   Alexis Koralewski   add pagination to...
892
893
894
            field_dict["value"] = sp_list.index(seq.scientific_program.name)
            field_dict["values"] = sp_list
            field_dict["value_type"] = "int"
0318e3c9   Alexis Koralewski   Add tests for F05...
895

ad3b297c   Alexis Koralewski   add pagination to...
896
        elif field == "start_date":
0ad59e43   Alexis Koralewski   fix & improve imp...
897
898
            #field_dict["value"] = seq.start_date.strftime("%d/%m/%Y %H:%M:%S")
            # ISO format
10252292   Alexis Koralewski   Change function t...
899
            field_dict["value"] = seq.start_date.strftime("%Y-%m-%dT%H:%M:%S.%f")
ad3b297c   Alexis Koralewski   add pagination to...
900
901
            field_dict["value_type"] = "str"
        else:
10252292   Alexis Koralewski   Change function t...
902
903
904
905
            if hasattr(sequence_form.fields[field].widget,"input_type"):
                field_dict["value_type"] = sequence_form.fields[field].widget.input_type
            else:
                field_dict["value_type"] = "str"
ad3b297c   Alexis Koralewski   add pagination to...
906
907
908
909
910
911
912
            if seq.__dict__.get(field) == None:
                value = seq.config_attributes.get(field)
            else:
                value = seq.__dict__.get(str(field))
            field_dict["value"] = value
            if sequence_form.fields[field].__dict__.get("_choices"):
                field_dict["value_type"] = "int"
0318e3c9   Alexis Koralewski   Add tests for F05...
913
914
                field_dict["values"] = [
                    value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
915
916
917
                field_dict["value"] = field_dict["values"].index(value)
            if field == "layout":
                field_dict["editable"] = False
3f8dc14d   Alexis Koralewski   Fixing value of s...
918
            if type == "template" and field != "layout":
ad3b297c   Alexis Koralewski   add pagination to...
919
920
                field_dict["value"] = None
            if sequence_form.fields[field].__dict__.get("min_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
921
922
                field_dict["min_value"] = sequence_form.fields[field].__dict__.get(
                    "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
923
            if sequence_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
924
925
                field_dict["max_value"] = sequence_form.fields[field].__dict__.get(
                    "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
926
927
928
929
930
931
932
933
934
935
        yaml_dict["sequence"][field] = field_dict
    album_list = []
    for album in seq.albums.all():
        album_dict = {
            "Album": {
                "name": album.name
            }
        }
        plans_list = []
        for plan in album.plans.all():
0318e3c9   Alexis Koralewski   Add tests for F05...
936
937
938
            plan_form = PlanForm(data_from_config=config.getEditableAttributesOfChannel(
                config.unit_name, list(config.get_channels(config.unit_name).keys())[0]), edited_plan=plan)
            plan_dict = {"Plan": {}}
ad3b297c   Alexis Koralewski   add pagination to...
939
940
941
942
943
944
945
946
947
948
            for field in plan_form.fields.keys():
                field_dict = {}
                if field == "nb_images":
                    field_dict["value"] = plan.nb_images
                    field_dict["value_type"] = "int"
                    field_dict["min_value"] = 1
                else:
                    if plan_form.fields[field].__dict__.get("_choices"):
                        value = plan.config_attributes.get(field)
                        field_dict["value_type"] = "int"
0318e3c9   Alexis Koralewski   Add tests for F05...
949
950
                        field_dict["values"] = [
                            value[0] for value in plan_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
951
952
                        container_value_to_str = ""
                        for subkey in value.keys():
0318e3c9   Alexis Koralewski   Add tests for F05...
953
                            container_value_to_str += f"{subkey}:{value[subkey]};"
ad3b297c   Alexis Koralewski   add pagination to...
954
                        container_value_to_str = container_value_to_str[:-1]
0318e3c9   Alexis Koralewski   Add tests for F05...
955
956
                        field_dict["value"] = field_dict["values"].index(
                            container_value_to_str)
ad3b297c   Alexis Koralewski   add pagination to...
957
958
959
960
                    else:
                        value = plan.config_attributes.get(field)
                        field_dict["value"] = value
                        if plan_form.fields[field].__dict__.get("min_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
961
962
                            field_dict["min_value"] = plan_form.fields[field].__dict__.get(
                                "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
963
                        if plan_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
964
965
                            field_dict["max_value"] = plan_form.fields[field].__dict__.get(
                                "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
966
967
968
969
970
971
972
                        field_dict["value_type"] = plan_form.fields[field].widget.input_type
                if type == "template":
                    field_dict["value"] = None
                plan_dict["Plan"][field] = field_dict
            plans_list.append(plan_dict)
        album_dict["Album"]["Plans"] = plans_list
        album_list.append(album_dict)
0318e3c9   Alexis Koralewski   Add tests for F05...
973

ad3b297c   Alexis Koralewski   add pagination to...
974
    yaml_dict["sequence"]["ALBUMS"] = album_list
0318e3c9   Alexis Koralewski   Add tests for F05...
975
976
977
978
    response = HttpResponse(
        yaml.dump(yaml_dict, sort_keys=False), content_type='application/yaml')
    response['Content-Disposition'] = 'attachment; filename="{}_{}.yml"'.format(
        seq.name, type)
ad3b297c   Alexis Koralewski   add pagination to...
979
980
981

    return response

3b81a22b   Alexis Koralewski   Rework on Request...
982

cca89707   Alexis Koralewski   adapting observat...
983
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
984
985
@level_required("Admin", "Unit-PI", "Observer")
def create_albums(request, seq_id, layout):
cca89707   Alexis Koralewski   adapting observat...
986
987
    seq = Sequence.objects.get(id=seq_id)
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
988
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
0318e3c9   Alexis Koralewski   Add tests for F05...
989
990
    albums = config.getLayoutByName(
        unit_name=config.unit_name, name_of_layout=layout)["ALBUMS"]
cca89707   Alexis Koralewski   adapting observat...
991
    for album_name in albums:
0318e3c9   Alexis Koralewski   Add tests for F05...
992
993
994
995
996
997
        album_desc = config.getAlbumByName(
            config.unit_name, album_name).get("description", "")
        create_album(request, seq_id, album_name=album_name, desc=album_desc)
        # return redirect(create_album, seq_id=seq_id)
    messages.add_message(request, messages.INFO,
                         "Created album(s) according to chosen layout")
cca89707   Alexis Koralewski   adapting observat...
998
999
1000
    return HttpResponse("ok")


cca89707   Alexis Koralewski   adapting observat...
1001
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
1002
1003
@level_required("Admin", "Unit-PI", "Observer")
def delete_albums(request, seq_id):
cca89707   Alexis Koralewski   adapting observat...
1004
1005
    seq = Sequence.objects.get(id=seq_id)
    albums = Album.objects.filter(sequence=seq).delete()
ad3b297c   Alexis Koralewski   add pagination to...
1006
1007
    seq.complete = False
    seq.save()
cca89707   Alexis Koralewski   adapting observat...
1008
1009
1010
    return HttpResponse(albums)


3b81a22b   Alexis Koralewski   Rework on Request...
1011
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
1012
1013
1014
1015
1016
@level_required("Admin", "Unit-PI", "Observer")
def edit_plan(request, id):
    plan = get_object_or_404(Plan, pk=id)
    config = OBSConfig(
        os.environ["PATH_TO_OBSCONF_FILE"], os.environ["unit_name"])
3b81a22b   Alexis Koralewski   Rework on Request...
1017
    if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
1018

3b81a22b   Alexis Koralewski   Rework on Request...
1019
1020
1021
1022
        post_data = request.POST.copy()
        post_data.pop("csrfmiddlewaretoken")
        nb_images = post_data.pop("nb_images")[0]
        config_attributes = {}
0318e3c9   Alexis Koralewski   Add tests for F05...
1023
        for key, value in post_data.items():
3b81a22b   Alexis Koralewski   Rework on Request...
1024
1025
1026
            if type(value) == str:
                try:
                    # linked values
0318e3c9   Alexis Koralewski   Add tests for F05...
1027
                    new_dict = {key: {}}
3b81a22b   Alexis Koralewski   Rework on Request...
1028
1029
1030
                    splitted_values = value.split(";")
                    config_attributes[key] = {}
                    for splitted_value in splitted_values:
0318e3c9   Alexis Koralewski   Add tests for F05...
1031
1032
1033
                        subkey, subvalue = splitted_value.split(":")
                        config_attributes[key][subkey] = ast.literal_eval(
                            subvalue)
3b81a22b   Alexis Koralewski   Rework on Request...
1034
1035
1036
1037
1038
1039
                except:
                    # Do nothing, normal string
                    config_attributes[key] = ast.literal_eval(value)
        plan.nb_images = int(nb_images)
        plan.config_attributes = config_attributes
        plan.save()
0318e3c9   Alexis Koralewski   Add tests for F05...
1040
1041
1042
1043
1044
    form = PlanForm(data_from_config=config.getEditableAttributesOfChannel(
        config.unit_name, list(config.get_channels(config.unit_name).keys())[0]), edited_plan=plan)
    uneditableForm = uneditablePlanForm(data_from_config=config.getUneditableAttributesOfChannel(
        config.unit_name, list(config.get_channels(config.unit_name).keys())[0]))
    return render(request, "routine_manager/testcreateplan.html", {"form": form, "uneditableForm": uneditableForm})
ad3b297c   Alexis Koralewski   add pagination to...
1045
1046
1047


@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
1048
1049
@level_required("Admin", "Unit-PI", "Observer")
def copy_sequence(request, seq_id):
ad3b297c   Alexis Koralewski   add pagination to...
1050
1051
1052
    sequence_to_be_copied = Sequence.objects.get(id=seq_id)
    original_sequence = Sequence.objects.get(id=seq_id)
    new_seq = sequence_to_be_copied
bbf0ac6e   Alexis Koralewski   Adding unique con...
1053
1054
1055
1056
    date_of_sequence = datetime.datetime.utcnow()
    date_of_sequence = date_of_sequence.strftime("%Y%m%dT%H%M%S")
    name = f"seq_{str(date_of_sequence)}"
    new_seq.name = name
ad3b297c   Alexis Koralewski   add pagination to...
1057
1058
    new_seq.pk = None
    new_seq._state.adding = True
a61943b5   Alexis Koralewski   Reworking Unit-bo...
1059
    new_seq.pyros_user = request.user
ad3b297c   Alexis Koralewski   add pagination to...
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
    new_seq.complete = False
    new_seq.status = Sequence.DRAFT
    new_seq.save()
    for album in original_sequence.albums.all():
        original_plans_of_album = Album.objects.get(id=album.id).plans.all()
        new_alb = album
        new_alb.complete = False
        new_alb.pk = None
        new_alb.sequence = new_seq
        new_alb._state.adding = True
        new_alb.save()
        for plan in original_plans_of_album:
            new_plan = plan
            new_plan.album = new_alb
            new_plan.complete = False
            new_plan.pk = None
            new_plan._state.adding = True
            new_plan.save()
0318e3c9   Alexis Koralewski   Add tests for F05...
1078
    return redirect(action_sequence, new_seq.id, "edit")