Blame view

src/core/pyros_django/routine_manager/views.py 40.7 KB
0318e3c9   Alexis Koralewski   Add tests for F05...
1
2
3
4
5
6
7
from django.core.paginator import Paginator
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...
8
from collections import OrderedDict
1cffbf1c   Etienne Pallier   moved pyros_djang...
9
10
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
3b81a22b   Alexis Koralewski   Rework on Request...
11
from django.shortcuts import get_object_or_404, render, redirect
1cffbf1c   Etienne Pallier   moved pyros_djang...
12
13
14
from common.models import *
from django.db.models import Q
from django.contrib.auth.decorators import login_required
ad3b297c   Alexis Koralewski   add pagination to...
15
from src.core.pyros_django.dashboard.decorator import level_required
0318e3c9   Alexis Koralewski   Add tests for F05...
16
17
18
import ast
import os
import datetime
3b81a22b   Alexis Koralewski   Rework on Request...
19

ad3b297c   Alexis Koralewski   add pagination to...
20
from src.core.pyros_django.obsconfig.obsconfig_class import OBSConfig
3b81a22b   Alexis Koralewski   Rework on Request...
21
from .forms import RequestForm, SequenceForm, AlbumForm, PlanForm, uneditablePlanForm
1cffbf1c   Etienne Pallier   moved pyros_djang...
22
from .validators import check_plan_validity, check_album_validity, check_sequence_validity, check_request_validity
ad3b297c   Alexis Koralewski   add pagination to...
23
from .functions import check_sequence_file_validity
1cffbf1c   Etienne Pallier   moved pyros_djang...
24
25
from .RequestSerializer import RequestSerializer
import scheduler
0318e3c9   Alexis Koralewski   Add tests for F05...
26
from django.contrib import messages
ad3b297c   Alexis Koralewski   add pagination to...
27
from django.http import JsonResponse
1cffbf1c   Etienne Pallier   moved pyros_djang...
28
29
""" logger """
from django.conf import settings
4a596eec   Alexis Koralewski   Updating SP, Sequ...
30
# "import utils.Logger as l
0318e3c9   Alexis Koralewski   Add tests for F05...
31
# "log = l.setupLogger("routine_manager-views", "routine_manager-views")
1cffbf1c   Etienne Pallier   moved pyros_djang...
32

0318e3c9   Alexis Koralewski   Add tests for F05...
33
34
import yaml
import json
1cffbf1c   Etienne Pallier   moved pyros_djang...
35
""" XML Export / Import utils """
1cffbf1c   Etienne Pallier   moved pyros_djang...
36
37
38
39
SAVED_REQUESTS_FOLDER = "misc/saved_requests/"


@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
40
41
@level_required("Admin", "Unit-PI", "Unit-board", "Observer")
def sequences_list(request, status=0, message="", import_response=None):
1cffbf1c   Etienne Pallier   moved pyros_djang...
42
43
44
45
46
    """
        Retrieves and display the routines list (routine manager main page)
    """

    if settings.DEBUG:
ad3b297c   Alexis Koralewski   add pagination to...
47
        #log.info("From sequences_list")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
48
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
49
50
51
52
53
54
55
56

    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")
ad3b297c   Alexis Koralewski   add pagination to...
57
    sp_where_user_is_sp_pi = request.user.get_scientific_program_where_user_is_sp_pi()
0318e3c9   Alexis Koralewski   Add tests for F05...
58
59
    sequences_objs = Sequence.objects.filter(
        pyros_user=request.user).order_by("-updated")
ad3b297c   Alexis Koralewski   add pagination to...
60
    if sp_where_user_is_sp_pi.count() > 0:
0318e3c9   Alexis Koralewski   Add tests for F05...
61
62
        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...
63
    sequences = []
0318e3c9   Alexis Koralewski   Add tests for F05...
64
    if request.session.get("role") in ("Admin", "Unit-PI", "Unit-board"):
6b3644e7   Alexis Koralewski   Upgrading fontawe...
65
        sequences_objs = Sequence.objects.all().order_by("-updated")
0318e3c9   Alexis Koralewski   Add tests for F05...
66
    paginator = Paginator(sequences_objs, settings.NB_ELEMENT_PER_PAGE)
ad3b297c   Alexis Koralewski   add pagination to...
67
68
69
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)
    for seq in page_obj:
4a596eec   Alexis Koralewski   Updating SP, Sequ...
70
        nb_album = Album.objects.filter(sequence=seq).count
0318e3c9   Alexis Koralewski   Add tests for F05...
71
72
73
74
        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...
75

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

3b81a22b   Alexis Koralewski   Rework on Request...
80
81
82
83
84
85
86
87
88
    # 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...
89
    print("REQ/views: ICI:")
ad3b297c   Alexis Koralewski   add pagination to...
90
91
    # 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...
92

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
94
95
96
97
98
99
100
101
102
103
@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...
104
105
        #log.info("From action_request")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

    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...
129
        return redirect(sequences_list, status='1', message="Successfully deleted the request")
1cffbf1c   Etienne Pallier   moved pyros_djang...
130

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
134
135
136
137
138
139
140
141
142
143
144
145
@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...
146
147
        #log.info("From request_validate")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
148
149
150
151

    if action == "cancel":
        if req.name == "New request":
            req.delete()
ad3b297c   Alexis Koralewski   add pagination to...
152
        return redirect(sequences_list, status=1, message="Cancelled request modification")
1cffbf1c   Etienne Pallier   moved pyros_djang...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
    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
0318e3c9   Alexis Koralewski   Add tests for F05...
170
@level_required("Admin", "Unit-PI", "Unit-board", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
171
172
173
174
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...
175
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
176
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
177
178
    seq_id = int(seq_id)
    seq = Sequence.objects.get(id=seq_id)
3b81a22b   Alexis Koralewski   Rework on Request...
179
    depth_level = 1
1cffbf1c   Etienne Pallier   moved pyros_djang...
180
181

    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
182
183
        #log.info("From action_sequence")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
184
185
186
187
188

    if status == "-1":
        error = True
    elif status == "1":
        success = True
ad3b297c   Alexis Koralewski   add pagination to...
189
    if seq.status != Sequence.DRAFT and action == "edit":
c797f02d   Alexis Koralewski   Adding unsubmit b...
190
191
192
193
194
195
        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...
196

4a596eec   Alexis Koralewski   Updating SP, Sequ...
197
198
199
    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...
200
    </svg><p> (values : {horizon_line})</p>'
0318e3c9   Alexis Koralewski   Add tests for F05...
201
    if request.session.get("role") in ("Admin", "Unit-PI", "Unit-board"):
ad3b297c   Alexis Koralewski   add pagination to...
202
        sp_list = ScientificProgram.objects.observable_programs()
4a596eec   Alexis Koralewski   Updating SP, Sequ...
203
204
205
    else:
        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...
206
207
208
209
    if seq.pyros_user != None and request.session.get("role") == "Observer" and seq.pyros_user != request.user:
        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...
210
    if action == "edit":
0318e3c9   Alexis Koralewski   Add tests for F05...
211
212
213
        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...
214
            return redirect(sequences_list)
0318e3c9   Alexis Koralewski   Add tests for F05...
215
216
        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...
217
218
219
        edit = True
        return render(request, "routine_manager/view_sequence.html", locals())
    elif action == "view":
0318e3c9   Alexis Koralewski   Add tests for F05...
220
        form = SequenceForm(instance=seq, data_from_config=config.getEditableAttributesOfMount(
860eb6af   Alexis Koralewski   Adding 3 full seq...
221
            config.unit_name), layouts=config.get_layouts(config.unit_name), sp_list=sp_list, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
222
223
224
        edit = False
        return render(request, "routine_manager/view_sequence.html", locals())
    elif action == "delete":
0318e3c9   Alexis Koralewski   Add tests for F05...
225
226
227
228
229
230
231
        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...
232

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
236
@login_required
c797f02d   Alexis Koralewski   Adding unsubmit b...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
@level_required("Admin", "Unit-PI", "Unit-board", "Observer")
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")
    

@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
253
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
254
255
256
257
258
259
260
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...
261
262
        #log.info("From sequence_validate")
        pass
3b81a22b   Alexis Koralewski   Rework on Request...
263
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
264
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
265
266
267
    seq_id = int(seq_id)
    action = request.POST.get("action")
    seq = Sequence.objects.get(id=seq_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
268
269
    form = SequenceForm(instance=seq, data=request.POST, data_from_config=config.getEditableAttributesOfMount(
        config.unit_name), layouts=config.get_layouts(config.unit_name))
1cffbf1c   Etienne Pallier   moved pyros_djang...
270
271

    if action == "cancel":
c797f02d   Alexis Koralewski   Adding unsubmit b...
272
        seq.delete()
ad3b297c   Alexis Koralewski   add pagination to...
273
        return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
274
275
    elif action == "delete":
        return redirect(action_sequence, seq_id, "delete")
d42ec18b   Alexis Koralewski   disabling submit ...
276
277
    elif action == "check_validity":
        seq.save()
860eb6af   Alexis Koralewski   Adding 3 full seq...
278
        is_seq_valid = check_sequence_validity(seq)
d42ec18b   Alexis Koralewski   disabling submit ...
279
280
281
        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...
282
283
284
285
        message = ""
        if is_seq_valid == True:
            message = f"The sequence is valid and can be submitted."
        else:
c797f02d   Alexis Koralewski   Adding unsubmit b...
286
287
            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...
288
        messages.add_message(request, messages.INFO, message)
0318e3c9   Alexis Koralewski   Add tests for F05...
289
        return redirect(action_sequence, seq_id, "edit")
1cffbf1c   Etienne Pallier   moved pyros_djang...
290
291
292
    elif form.is_valid():
        seq = form.save()
        if action == "save":
0318e3c9   Alexis Koralewski   Add tests for F05...
293
294
            message = "Sequence saved"
            messages.add_message(request, messages.INFO, message)
1ba49504   Alexis Koralewski   fixing CSS and JS...
295
296
297
            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...
298
299
            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...
300
301
            albums = config.getLayoutByName(
                unit_name=config.unit_name, name_of_layout=seq.config_attributes["layout"])["ALBUMS"]
cca89707   Alexis Koralewski   adapting observat...
302
            for album_name in albums:
0318e3c9   Alexis Koralewski   Add tests for F05...
303
304
305
306
307
308
309
                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...
310
            return redirect(action_sequence, seq.id, "edit")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
311
312
313
314
        if action == "save_and_submit":
            if check_sequence_validity(seq):
                seq.status = Sequence.TOBEPLANNED
                seq.save()
0318e3c9   Alexis Koralewski   Add tests for F05...
315
316
317
318
                message = "Sequence submitted"
                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...
319
                return redirect(sequences_list)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
320
321
            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...
322
                messages.add_message(request, messages.ERROR, message)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
323
324
                status = 1
                edit = True
0318e3c9   Alexis Koralewski   Add tests for F05...
325
                return render(request, "routine_manager/view_sequence.html", locals())
1cffbf1c   Etienne Pallier   moved pyros_djang...
326
327
328
    else:
        error = True
        message = "Please check your field's validity"
3b81a22b   Alexis Koralewski   Rework on Request...
329
        depth_level = 1
1cffbf1c   Etienne Pallier   moved pyros_djang...
330
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
331
332
        return render(request, "routine_manager/view_sequence.html", locals())

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
334
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
335
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
336
337
338
339
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...
340
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
341
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
342
343
344

    alb_id = int(alb_id)
    alb = Album.objects.get(id=alb_id)
1cffbf1c   Etienne Pallier   moved pyros_djang...
345
    seq_id = alb.sequence.id
3b81a22b   Alexis Koralewski   Rework on Request...
346
347
    depth_level = 2
    seq = alb.sequence
1cffbf1c   Etienne Pallier   moved pyros_djang...
348
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
349
350
        #log.info("From action_album")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
351
352
353
354
355
356

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

ad3b297c   Alexis Koralewski   add pagination to...
357
    if alb.sequence.status != Sequence.DRAFT and action == "edit":
1cffbf1c   Etienne Pallier   moved pyros_djang...
358
359
360
361
        error = True
        message = "You can't edit a submitted request"
        action = "view"

d42ec18b   Alexis Koralewski   disabling submit ...
362
    # if check_album_validity(alb) == True:
0318e3c9   Alexis Koralewski   Add tests for F05...
363
        # return redirect(unsubmit_request, req_id)
d42ec18b   Alexis Koralewski   disabling submit ...
364
        # pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
365
    if action == "edit":
0318e3c9   Alexis Koralewski   Add tests for F05...
366
        form = AlbumForm(instance=alb, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
367
368
369
        edit = True
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "view":
cca89707   Alexis Koralewski   adapting observat...
370
        form = AlbumForm(instance=alb, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
371
372
373
        edit = False
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "delete":
0318e3c9   Alexis Koralewski   Add tests for F05...
374
375
        log.info(
            f"User {request.user} did action delete Album {alb.name} of Sequence {seq}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
376
377
378
        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...
379
    return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
380

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
382
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
383
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
384
385
386
387
388
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...
389
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
390
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
391
392
393
    alb_id = int(alb_id)
    action = request.POST.get("action")
    alb = Album.objects.get(id=alb_id)
cca89707   Alexis Koralewski   adapting observat...
394
    form = AlbumForm(instance=alb, data=request.POST)
0318e3c9   Alexis Koralewski   Add tests for F05...
395

1cffbf1c   Etienne Pallier   moved pyros_djang...
396
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
397
398
        #log.info("From album_validate")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
399

1cffbf1c   Etienne Pallier   moved pyros_djang...
400
    if action == "cancel":
c797f02d   Alexis Koralewski   Adding unsubmit b...
401
        alb.delete()
1cffbf1c   Etienne Pallier   moved pyros_djang...
402
403
404
405
406
407
        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...
408
            return redirect(action_sequence, seq_id=alb.sequence.id, action="edit")
0318e3c9   Alexis Koralewski   Add tests for F05...
409
            # return redirect(action_album, alb_id=alb_id, action="edit", status=1, message="Album saved")
1cffbf1c   Etienne Pallier   moved pyros_djang...
410
411
412
413
414
        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...
415
        depth_level = 2
1cffbf1c   Etienne Pallier   moved pyros_djang...
416
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
417
418
419
420
        seq_id = alb.sequence.id
        action = "edit"
        return render(request, "routine_manager/view_album.html", locals())

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
422
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
423
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
424
425
426
427
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...
428
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
429
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
430
431
    plan_id = int(plan_id)
    plan = Plan.objects.get(id=plan_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
432
433
434
435
436
    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...
437
    seq_id = plan.album.sequence.id
3b81a22b   Alexis Koralewski   Rework on Request...
438
    seq = plan.album.sequence
1cffbf1c   Etienne Pallier   moved pyros_djang...
439
    alb_id = plan.album.id
3b81a22b   Alexis Koralewski   Rework on Request...
440
    depth_level = 3
0318e3c9   Alexis Koralewski   Add tests for F05...
441
442
443
    # 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...
444
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
445
446
        #log.info("From action_plan")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
447
448
449
450
451
452

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
458
    if action == "edit":
3b81a22b   Alexis Koralewski   Rework on Request...
459
        form = PlanForm(edited_plan=plan, data_from_config=data_from_config)
0318e3c9   Alexis Koralewski   Add tests for F05...
460
461
        uneditableForm = uneditablePlanForm(
            data_from_config=config.getUneditableAttributesOfChannel(config.unit_name, channel))
1cffbf1c   Etienne Pallier   moved pyros_djang...
462
463
464
        edit = True
        return render(request, "routine_manager/view_plan.html", locals())
    elif action == "view":
0318e3c9   Alexis Koralewski   Add tests for F05...
465
466
467
468
        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...
469
470
471
        edit = False
        return render(request, "routine_manager/view_plan.html", locals())
    elif action == "delete":
0318e3c9   Alexis Koralewski   Add tests for F05...
472
473
        log.info(
            f"User {request.user} did action delete Plan {plan.id} of Sequence {seq}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
474
        plan.delete()
d42ec18b   Alexis Koralewski   disabling submit ...
475
476
        check_sequence_validity(seq)
        check_album_validity(plan.album)
1cffbf1c   Etienne Pallier   moved pyros_djang...
477
478
        return redirect(action_album, alb_id=plan.album.id, action="edit", status='1', message="Successfully deleted the plan")

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
482
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
483
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
484
485
486
487
488
def plan_validate(request, plan_id):
    """
        Called when the plan form was validated.
        Possible actions : Cancel, Save, Delete
    """
3b81a22b   Alexis Koralewski   Rework on Request...
489
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
490
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
491
492
493
    plan_id = int(plan_id)
    action = request.POST.get("action")
    plan = Plan.objects.get(id=plan_id)
3b81a22b   Alexis Koralewski   Rework on Request...
494
    seq = plan.album.sequence
d42ec18b   Alexis Koralewski   disabling submit ...
495
496
497
498
499
500
501
    # 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...
502
503
504
505
506
    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...
507
508
    #form = PlanForm(instance=plan, data=request.POST)
    form = PlanForm(edited_plan=plan, data_from_config=data_from_config)
1cffbf1c   Etienne Pallier   moved pyros_djang...
509
510

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

3b81a22b   Alexis Koralewski   Rework on Request...
515
        plan.delete()
1cffbf1c   Etienne Pallier   moved pyros_djang...
516
517
518
519
        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...
520
        # unused
1cffbf1c   Etienne Pallier   moved pyros_djang...
521
522
        plan = form.save()
        if action == "save":
0318e3c9   Alexis Koralewski   Add tests for F05...
523
524
525
            action = "edit"
            status = 1
            message = "Plan saved"
3b81a22b   Alexis Koralewski   Rework on Request...
526
527
528
            return redirect(action_plan, locals())
    elif action == "save":
        if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
529

3b81a22b   Alexis Koralewski   Rework on Request...
530
            post_data = request.POST.copy()
0318e3c9   Alexis Koralewski   Add tests for F05...
531
532
            if post_data.get("csrfmiddlewaretoken"):
                post_data.pop("csrfmiddlewaretoken")
3b81a22b   Alexis Koralewski   Rework on Request...
533
534
535
            post_data.pop("action")
            nb_images = post_data.pop("nb_images")[0]
            config_attributes = {}
0318e3c9   Alexis Koralewski   Add tests for F05...
536
            for key, value in post_data.items():
3b81a22b   Alexis Koralewski   Rework on Request...
537
538
539
                if type(value) == str:
                    try:
                        # linked values
0318e3c9   Alexis Koralewski   Add tests for F05...
540
                        new_dict = {key: {}}
3b81a22b   Alexis Koralewski   Rework on Request...
541
542
543
                        splitted_values = value.split(";")
                        config_attributes[key] = {}
                        for splitted_value in splitted_values:
0318e3c9   Alexis Koralewski   Add tests for F05...
544
545
546
                            subkey, subvalue = splitted_value.split(":")
                            config_attributes[key][subkey] = ast.literal_eval(
                                subvalue)
3b81a22b   Alexis Koralewski   Rework on Request...
547
548
549
550
551
                    except:
                        # Do nothing, normal string
                        config_attributes[key] = ast.literal_eval(value)
            plan.nb_images = int(nb_images)
            plan.config_attributes = config_attributes
0318e3c9   Alexis Koralewski   Add tests for F05...
552

3b81a22b   Alexis Koralewski   Rework on Request...
553
            plan.save()
d42ec18b   Alexis Koralewski   disabling submit ...
554
            # if check_plan_validity(plan) == True:
0318e3c9   Alexis Koralewski   Add tests for F05...
555
556
            # return redirect(unsubmit_request, req_id)
            # pass
3b81a22b   Alexis Koralewski   Rework on Request...
557
            return redirect(action_album, plan.album.id, "edit")
0318e3c9   Alexis Koralewski   Add tests for F05...
558
            # return redirect(action_plan, plan_id, "edit", 1, "Plan saved")
1cffbf1c   Etienne Pallier   moved pyros_djang...
559
560
561
562
    else:
        error = True
        message = "Please check your field's validity"
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
563
564
        seq_id = plan.album.sequence.id
        alb_id = plan.album.id
3b81a22b   Alexis Koralewski   Rework on Request...
565
        depth_level = 3
1cffbf1c   Etienne Pallier   moved pyros_djang...
566
567
        return render(request, "routine_manager/view_plan.html", locals())

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
569
570
571
572
573
574
@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...
575
576
    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...
577
578
    return redirect(action_request, req.id, "edit")

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
580
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
581
@level_required("Admin", "Unit-PI", "Observer")
3b81a22b   Alexis Koralewski   Rework on Request...
582
def create_sequence(request):
1cffbf1c   Etienne Pallier   moved pyros_djang...
583
584
585
    """
        Create a new sequence and redirects to the editing action on it
    """
3b81a22b   Alexis Koralewski   Rework on Request...
586
587
    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...
588
589
    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...
590
591
592
    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...
593
        return redirect(sequences_list)
0318e3c9   Alexis Koralewski   Add tests for F05...
594
595
    seq = Sequence.objects.create(
        pyros_user=request.user, name=f"seq_{str(date_of_sequence)}", status=Sequence.DRAFT)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
596
    log.info(f"User {request.user} did action create Sequence {seq.name}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
597
598
    return redirect(action_sequence, seq.id, "edit")

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
600
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
601
602
@level_required("Admin", "Unit-PI", "Observer")
def create_album(request, seq_id, album_name, desc):
1cffbf1c   Etienne Pallier   moved pyros_djang...
603
604
605
606
607
    """
        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...
608
609
610
611
612
613
    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...
614
615

@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
616
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
617
618
619
620
621
622
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...
623
624
625
    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...
626
627
    return redirect(action_plan, plan.id, "edit")

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
629
630
631
632
633
634
635
@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...
636
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
        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...
671
        return redirect(sequences_list, status=1, message=message)
1cffbf1c   Etienne Pallier   moved pyros_djang...
672

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
674
675
676
677
678
679
680
@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...
681
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
682
683
684
685
686
687
688
689
690
691
692
693
        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...
694
                                     Q(status=Sequence.INVALID) | Q(status=Sequence.UNPLANNABLE))
1cffbf1c   Etienne Pallier   moved pyros_djang...
695
696
697
698
699
700
701
702
703
704
705
706
    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...
707

1cffbf1c   Etienne Pallier   moved pyros_djang...
708
709
710
711
712
713
714
@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...
715
716
        #log.info("From export_request")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731

    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...
732
733
    response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(
        os.path.basename(file_name))
1cffbf1c   Etienne Pallier   moved pyros_djang...
734
735
736

    return response

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
738
739
740
741
742
743
744
745
746
@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...
747
748
        pass
        #log.info("From import_request")
1cffbf1c   Etienne Pallier   moved pyros_djang...
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773

    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...
774
775
776
777
778
779
    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...
780

ad3b297c   Alexis Koralewski   add pagination to...
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
#         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...
807
@login_required
ad3b297c   Alexis Koralewski   add pagination to...
808
@csrf_exempt
0318e3c9   Alexis Koralewski   Add tests for F05...
809
@level_required("Admin", "Unit-PI", "Observer")
ad3b297c   Alexis Koralewski   add pagination to...
810
811
812
813
814
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...
815
816
817
818
819
820
821
822
823
824
825
    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())
0318e3c9   Alexis Koralewski   Add tests for F05...
826
827
            import_response = check_sequence_file_validity(
                yaml_content, request)
ad3b297c   Alexis Koralewski   add pagination to...
828
829
830
831
832
833
834
835
836
837
838
839
840
841
            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...
842
843
@level_required("Admin", "Unit-PI", "Observer")
def export_sequence(request, seq_id: int, type: str):
ad3b297c   Alexis Koralewski   add pagination to...
844
845
846
847
848
    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...
849
850
    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...
851
    yaml_dict = {
0318e3c9   Alexis Koralewski   Add tests for F05...
852
        "sequence": {}
ad3b297c   Alexis Koralewski   add pagination to...
853
    }
0318e3c9   Alexis Koralewski   Add tests for F05...
854

ad3b297c   Alexis Koralewski   add pagination to...
855
856
857
    for field in sequence_form.fields.keys():
        if sequence_form.fields[field].required == False:
            continue
0318e3c9   Alexis Koralewski   Add tests for F05...
858
        field_dict = {"editable": True}
ad3b297c   Alexis Koralewski   add pagination to...
859
        if field == "scientific_program":
0318e3c9   Alexis Koralewski   Add tests for F05...
860
            sp_list = list(sp_list.values_list("name", flat=True))
ad3b297c   Alexis Koralewski   add pagination to...
861
862
863
            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...
864

ad3b297c   Alexis Koralewski   add pagination to...
865
866
867
868
869
870
871
872
873
874
875
876
        elif field == "start_date":
            field_dict["value"] = seq.start_date.strftime("%d/%m/%Y %H:%M:%S")
            field_dict["value_type"] = "str"
        else:
            field_dict["value_type"] = sequence_form.fields[field].widget.input_type
            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...
877
878
                field_dict["values"] = [
                    value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
879
880
881
882
883
884
                field_dict["value"] = field_dict["values"].index(value)
            if field == "layout":
                field_dict["editable"] = False
            if type == "template":
                field_dict["value"] = None
            if sequence_form.fields[field].__dict__.get("min_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
885
886
                field_dict["min_value"] = sequence_form.fields[field].__dict__.get(
                    "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
887
            if sequence_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
888
889
                field_dict["max_value"] = sequence_form.fields[field].__dict__.get(
                    "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
890
891
892
893
894
895
896
897
898
899
        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...
900
901
902
            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...
903
904
905
906
907
908
909
910
911
912
            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...
913
914
                        field_dict["values"] = [
                            value[0] for value in plan_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
915
916
                        container_value_to_str = ""
                        for subkey in value.keys():
0318e3c9   Alexis Koralewski   Add tests for F05...
917
                            container_value_to_str += f"{subkey}:{value[subkey]};"
ad3b297c   Alexis Koralewski   add pagination to...
918
                        container_value_to_str = container_value_to_str[:-1]
0318e3c9   Alexis Koralewski   Add tests for F05...
919
920
                        field_dict["value"] = field_dict["values"].index(
                            container_value_to_str)
ad3b297c   Alexis Koralewski   add pagination to...
921
922
923
924
                    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...
925
926
                            field_dict["min_value"] = plan_form.fields[field].__dict__.get(
                                "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
927
                        if plan_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
928
929
                            field_dict["max_value"] = plan_form.fields[field].__dict__.get(
                                "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
930
931
932
933
934
935
936
                        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...
937

ad3b297c   Alexis Koralewski   add pagination to...
938
    yaml_dict["sequence"]["ALBUMS"] = album_list
0318e3c9   Alexis Koralewski   Add tests for F05...
939
940
941
942
    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...
943
944
945

    return response

3b81a22b   Alexis Koralewski   Rework on Request...
946

cca89707   Alexis Koralewski   adapting observat...
947
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
948
949
@level_required("Admin", "Unit-PI", "Observer")
def create_albums(request, seq_id, layout):
cca89707   Alexis Koralewski   adapting observat...
950
951
    seq = Sequence.objects.get(id=seq_id)
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
952
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
0318e3c9   Alexis Koralewski   Add tests for F05...
953
954
    albums = config.getLayoutByName(
        unit_name=config.unit_name, name_of_layout=layout)["ALBUMS"]
cca89707   Alexis Koralewski   adapting observat...
955
    for album_name in albums:
0318e3c9   Alexis Koralewski   Add tests for F05...
956
957
958
959
960
961
        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...
962
963
964
    return HttpResponse("ok")


cca89707   Alexis Koralewski   adapting observat...
965
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
966
967
@level_required("Admin", "Unit-PI", "Observer")
def delete_albums(request, seq_id):
cca89707   Alexis Koralewski   adapting observat...
968
969
    seq = Sequence.objects.get(id=seq_id)
    albums = Album.objects.filter(sequence=seq).delete()
ad3b297c   Alexis Koralewski   add pagination to...
970
971
    seq.complete = False
    seq.save()
cca89707   Alexis Koralewski   adapting observat...
972
973
974
    return HttpResponse(albums)


3b81a22b   Alexis Koralewski   Rework on Request...
975
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
976
977
978
979
980
@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...
981
    if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
982

3b81a22b   Alexis Koralewski   Rework on Request...
983
984
985
986
        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...
987
        for key, value in post_data.items():
3b81a22b   Alexis Koralewski   Rework on Request...
988
989
990
            if type(value) == str:
                try:
                    # linked values
0318e3c9   Alexis Koralewski   Add tests for F05...
991
                    new_dict = {key: {}}
3b81a22b   Alexis Koralewski   Rework on Request...
992
993
994
                    splitted_values = value.split(";")
                    config_attributes[key] = {}
                    for splitted_value in splitted_values:
0318e3c9   Alexis Koralewski   Add tests for F05...
995
996
997
                        subkey, subvalue = splitted_value.split(":")
                        config_attributes[key][subkey] = ast.literal_eval(
                            subvalue)
3b81a22b   Alexis Koralewski   Rework on Request...
998
999
1000
1001
1002
1003
                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...
1004
1005
1006
1007
1008
    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...
1009
1010
1011


@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
1012
1013
@level_required("Admin", "Unit-PI", "Observer")
def copy_sequence(request, seq_id):
ad3b297c   Alexis Koralewski   add pagination to...
1014
1015
1016
    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...
1017
1018
1019
1020
    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...
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
    new_seq.pk = None
    new_seq._state.adding = True
    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...
1041
    return redirect(action_sequence, new_seq.id, "edit")