Blame view

src/core/pyros_django/routine_manager/views.py 41 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
bce55cd9   Alexis Koralewski   Reworking Sequenc...
40
@level_required("Admin", "Unit-PI", "Observer")
0318e3c9   Alexis Koralewski   Add tests for F05...
41
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")
bce55cd9   Alexis Koralewski   Reworking Sequenc...
57
58
59
60
61
62
63
64
    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...
65
    sequences = []
bce55cd9   Alexis Koralewski   Reworking Sequenc...
66
    if request.session.get("role") in ("Admin", "Unit-PI"):
6b3644e7   Alexis Koralewski   Upgrading fontawe...
67
        sequences_objs = Sequence.objects.all().order_by("-updated")
0318e3c9   Alexis Koralewski   Add tests for F05...
68
    paginator = Paginator(sequences_objs, settings.NB_ELEMENT_PER_PAGE)
ad3b297c   Alexis Koralewski   add pagination to...
69
70
71
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)
    for seq in page_obj:
4a596eec   Alexis Koralewski   Updating SP, Sequ...
72
        nb_album = Album.objects.filter(sequence=seq).count
0318e3c9   Alexis Koralewski   Add tests for F05...
73
74
75
76
        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...
77

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
238
@login_required
bce55cd9   Alexis Koralewski   Reworking Sequenc...
239
@level_required("Admin", "Unit-PI", "Observer")
c797f02d   Alexis Koralewski   Adding unsubmit b...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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...
255
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
256
257
258
259
260
261
262
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...
263
264
        #log.info("From sequence_validate")
        pass
3b81a22b   Alexis Koralewski   Rework on Request...
265
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
266
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
267
268
269
    seq_id = int(seq_id)
    action = request.POST.get("action")
    seq = Sequence.objects.get(id=seq_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
270
271
    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...
272
273
    seq.last_modified_by = request.user
    seq.save()
1cffbf1c   Etienne Pallier   moved pyros_djang...
274
    if action == "cancel":
c797f02d   Alexis Koralewski   Adding unsubmit b...
275
        seq.delete()
ad3b297c   Alexis Koralewski   add pagination to...
276
        return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
277
278
    elif action == "delete":
        return redirect(action_sequence, seq_id, "delete")
d42ec18b   Alexis Koralewski   disabling submit ...
279
280
    elif action == "check_validity":
        seq.save()
860eb6af   Alexis Koralewski   Adding 3 full seq...
281
        is_seq_valid = check_sequence_validity(seq)
d42ec18b   Alexis Koralewski   disabling submit ...
282
283
284
        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...
285
286
287
288
        message = ""
        if is_seq_valid == True:
            message = f"The sequence is valid and can be submitted."
        else:
c797f02d   Alexis Koralewski   Adding unsubmit b...
289
290
            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...
291
        messages.add_message(request, messages.INFO, message)
0318e3c9   Alexis Koralewski   Add tests for F05...
292
        return redirect(action_sequence, seq_id, "edit")
1cffbf1c   Etienne Pallier   moved pyros_djang...
293
294
295
    elif form.is_valid():
        seq = form.save()
        if action == "save":
0318e3c9   Alexis Koralewski   Add tests for F05...
296
297
            message = "Sequence saved"
            messages.add_message(request, messages.INFO, message)
1ba49504   Alexis Koralewski   fixing CSS and JS...
298
299
300
            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...
301
302
            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...
303
304
            albums = config.getLayoutByName(
                unit_name=config.unit_name, name_of_layout=seq.config_attributes["layout"])["ALBUMS"]
cca89707   Alexis Koralewski   adapting observat...
305
            for album_name in albums:
0318e3c9   Alexis Koralewski   Add tests for F05...
306
307
308
309
310
311
312
                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...
313
            return redirect(action_sequence, seq.id, "edit")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
314
315
316
317
        if action == "save_and_submit":
            if check_sequence_validity(seq):
                seq.status = Sequence.TOBEPLANNED
                seq.save()
0318e3c9   Alexis Koralewski   Add tests for F05...
318
319
320
321
                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...
322
                return redirect(sequences_list)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
323
324
            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...
325
                messages.add_message(request, messages.ERROR, message)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
326
327
                status = 1
                edit = True
0318e3c9   Alexis Koralewski   Add tests for F05...
328
                return render(request, "routine_manager/view_sequence.html", locals())
1cffbf1c   Etienne Pallier   moved pyros_djang...
329
330
331
    else:
        error = True
        message = "Please check your field's validity"
3b81a22b   Alexis Koralewski   Rework on Request...
332
        depth_level = 1
1cffbf1c   Etienne Pallier   moved pyros_djang...
333
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
334
335
        return render(request, "routine_manager/view_sequence.html", locals())

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

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

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

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

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

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
385
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
386
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
387
388
389
390
391
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...
392
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
393
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
394
395
396
    alb_id = int(alb_id)
    action = request.POST.get("action")
    alb = Album.objects.get(id=alb_id)
cca89707   Alexis Koralewski   adapting observat...
397
    form = AlbumForm(instance=alb, data=request.POST)
bce55cd9   Alexis Koralewski   Reworking Sequenc...
398
399
    alb.sequence.last_modified_by = request.user
    alb.sequence.save()
1cffbf1c   Etienne Pallier   moved pyros_djang...
400
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
401
402
        #log.info("From album_validate")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
403

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

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

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

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

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

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

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

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

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

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

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

3b81a22b   Alexis Koralewski   Rework on Request...
536
            post_data = request.POST.copy()
0318e3c9   Alexis Koralewski   Add tests for F05...
537
538
            if post_data.get("csrfmiddlewaretoken"):
                post_data.pop("csrfmiddlewaretoken")
3b81a22b   Alexis Koralewski   Rework on Request...
539
540
541
            post_data.pop("action")
            nb_images = post_data.pop("nb_images")[0]
            config_attributes = {}
0318e3c9   Alexis Koralewski   Add tests for F05...
542
            for key, value in post_data.items():
3b81a22b   Alexis Koralewski   Rework on Request...
543
544
545
                if type(value) == str:
                    try:
                        # linked values
0318e3c9   Alexis Koralewski   Add tests for F05...
546
                        new_dict = {key: {}}
3b81a22b   Alexis Koralewski   Rework on Request...
547
548
549
                        splitted_values = value.split(";")
                        config_attributes[key] = {}
                        for splitted_value in splitted_values:
0318e3c9   Alexis Koralewski   Add tests for F05...
550
551
552
                            subkey, subvalue = splitted_value.split(":")
                            config_attributes[key][subkey] = ast.literal_eval(
                                subvalue)
3b81a22b   Alexis Koralewski   Rework on Request...
553
554
555
556
557
                    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...
558
            plan.save()
d42ec18b   Alexis Koralewski   disabling submit ...
559
            # if check_plan_validity(plan) == True:
0318e3c9   Alexis Koralewski   Add tests for F05...
560
561
            # return redirect(unsubmit_request, req_id)
            # pass
3b81a22b   Alexis Koralewski   Rework on Request...
562
            return redirect(action_album, plan.album.id, "edit")
0318e3c9   Alexis Koralewski   Add tests for F05...
563
            # return redirect(action_plan, plan_id, "edit", 1, "Plan saved")
1cffbf1c   Etienne Pallier   moved pyros_djang...
564
565
566
567
    else:
        error = True
        message = "Please check your field's validity"
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
568
569
        seq_id = plan.album.sequence.id
        alb_id = plan.album.id
3b81a22b   Alexis Koralewski   Rework on Request...
570
        depth_level = 3
1cffbf1c   Etienne Pallier   moved pyros_djang...
571
572
        return render(request, "routine_manager/view_plan.html", locals())

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
574
575
576
577
578
579
@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...
580
581
    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...
582
583
    return redirect(action_request, req.id, "edit")

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

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

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

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

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
634
635
636
637
638
639
640
@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...
641
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
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
671
672
673
674
675
        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...
676
        return redirect(sequences_list, status=1, message=message)
1cffbf1c   Etienne Pallier   moved pyros_djang...
677

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

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

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

    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...
737
738
    response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(
        os.path.basename(file_name))
1cffbf1c   Etienne Pallier   moved pyros_djang...
739
740
741

    return response

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

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

    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...
779
780
781
782
783
784
    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...
785

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

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

ad3b297c   Alexis Koralewski   add pagination to...
870
871
872
873
874
875
876
877
878
879
880
881
        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...
882
883
                field_dict["values"] = [
                    value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
884
885
886
887
888
889
                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...
890
891
                field_dict["min_value"] = sequence_form.fields[field].__dict__.get(
                    "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
892
            if sequence_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
893
894
                field_dict["max_value"] = sequence_form.fields[field].__dict__.get(
                    "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
895
896
897
898
899
900
901
902
903
904
        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...
905
906
907
            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...
908
909
910
911
912
913
914
915
916
917
            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...
918
919
                        field_dict["values"] = [
                            value[0] for value in plan_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
920
921
                        container_value_to_str = ""
                        for subkey in value.keys():
0318e3c9   Alexis Koralewski   Add tests for F05...
922
                            container_value_to_str += f"{subkey}:{value[subkey]};"
ad3b297c   Alexis Koralewski   add pagination to...
923
                        container_value_to_str = container_value_to_str[:-1]
0318e3c9   Alexis Koralewski   Add tests for F05...
924
925
                        field_dict["value"] = field_dict["values"].index(
                            container_value_to_str)
ad3b297c   Alexis Koralewski   add pagination to...
926
927
928
929
                    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...
930
931
                            field_dict["min_value"] = plan_form.fields[field].__dict__.get(
                                "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
932
                        if plan_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
933
934
                            field_dict["max_value"] = plan_form.fields[field].__dict__.get(
                                "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
935
936
937
938
939
940
941
                        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...
942

ad3b297c   Alexis Koralewski   add pagination to...
943
    yaml_dict["sequence"]["ALBUMS"] = album_list
0318e3c9   Alexis Koralewski   Add tests for F05...
944
945
946
947
    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...
948
949
950

    return response

3b81a22b   Alexis Koralewski   Rework on Request...
951

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


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


3b81a22b   Alexis Koralewski   Rework on Request...
980
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
981
982
983
984
985
@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...
986
    if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
987

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


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