Blame view

src/core/pyros_django/routine_manager/views.py 41.9 KB
0318e3c9   Alexis Koralewski   Add tests for F05...
1
from django.core.paginator import Paginator
ff76bcca   Alexis Koralewski   Adding Sequences ...
2
import pickle
0318e3c9   Alexis Koralewski   Add tests for F05...
3
4
5
6
7
8
from src.pyros_logger import log
from pprint import pprint
import mimetypes
from django.http import HttpResponse
from django.utils.encoding import smart_str
from wsgiref.util import FileWrapper
ad3b297c   Alexis Koralewski   add pagination to...
9
from collections import OrderedDict
1cffbf1c   Etienne Pallier   moved pyros_djang...
10
11
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
3b81a22b   Alexis Koralewski   Rework on Request...
12
from django.shortcuts import get_object_or_404, render, redirect
1cffbf1c   Etienne Pallier   moved pyros_djang...
13
14
15
from common.models import *
from django.db.models import Q
from django.contrib.auth.decorators import login_required
ad3b297c   Alexis Koralewski   add pagination to...
16
from src.core.pyros_django.dashboard.decorator import level_required
0318e3c9   Alexis Koralewski   Add tests for F05...
17
18
19
import ast
import os
import datetime
3b81a22b   Alexis Koralewski   Rework on Request...
20

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
240
@login_required
bce55cd9   Alexis Koralewski   Reworking Sequenc...
241
@level_required("Admin", "Unit-PI", "Observer")
c797f02d   Alexis Koralewski   Adding unsubmit b...
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")
    
ff76bcca   Alexis Koralewski   Adding Sequences ...
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
def create_sequence_pickle(sequence):
    seq_dict = model_to_dict(sequence)
    fullseq_dict = {
        "sequence":seq_dict,
        "albums": {}
    }
    for album in sequence.albums.all():
        fullseq_dict["albums"][album.name] = {"plans" : []}
        for plan in album.plans.all():
            fullseq_dict["albums"][f"{album.name}"]["plans"].append(model_to_dict(instance=plan))
    period = sequence.period
    if not os.path.exists("sequences_pickle"):
        os.mkdir("./sequences_pickle")
    if not os.path.exists(f"sequences_pickle/P{period.id}"):
        os.mkdir(f"sequences_pickle/P{period.id}")
    seq_pickle_file_name = f"./sequences_pickle/P{period.id}/{sequence.name}.p"
    pickle.dump(fullseq_dict,open(seq_pickle_file_name,"wb"))
c797f02d   Alexis Koralewski   Adding unsubmit b...
272
273

@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
274
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
275
276
277
278
279
280
281
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...
282
283
        #log.info("From sequence_validate")
        pass
3b81a22b   Alexis Koralewski   Rework on Request...
284
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
285
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
286
287
288
    seq_id = int(seq_id)
    action = request.POST.get("action")
    seq = Sequence.objects.get(id=seq_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
289
290
    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...
291
292
    seq.last_modified_by = request.user
    seq.save()
1cffbf1c   Etienne Pallier   moved pyros_djang...
293
    if action == "cancel":
c797f02d   Alexis Koralewski   Adding unsubmit b...
294
        seq.delete()
ad3b297c   Alexis Koralewski   add pagination to...
295
        return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
296
297
    elif action == "delete":
        return redirect(action_sequence, seq_id, "delete")
d42ec18b   Alexis Koralewski   disabling submit ...
298
299
    elif action == "check_validity":
        seq.save()
860eb6af   Alexis Koralewski   Adding 3 full seq...
300
        is_seq_valid = check_sequence_validity(seq)
d42ec18b   Alexis Koralewski   disabling submit ...
301
302
303
        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...
304
305
306
307
        message = ""
        if is_seq_valid == True:
            message = f"The sequence is valid and can be submitted."
        else:
c797f02d   Alexis Koralewski   Adding unsubmit b...
308
309
            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...
310
        messages.add_message(request, messages.INFO, message)
0318e3c9   Alexis Koralewski   Add tests for F05...
311
        return redirect(action_sequence, seq_id, "edit")
1cffbf1c   Etienne Pallier   moved pyros_djang...
312
313
314
    elif form.is_valid():
        seq = form.save()
        if action == "save":
0318e3c9   Alexis Koralewski   Add tests for F05...
315
316
            message = "Sequence saved"
            messages.add_message(request, messages.INFO, message)
1ba49504   Alexis Koralewski   fixing CSS and JS...
317
318
319
            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...
320
321
            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...
322
323
            albums = config.getLayoutByName(
                unit_name=config.unit_name, name_of_layout=seq.config_attributes["layout"])["ALBUMS"]
cca89707   Alexis Koralewski   adapting observat...
324
            for album_name in albums:
0318e3c9   Alexis Koralewski   Add tests for F05...
325
326
327
328
329
330
331
                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...
332
            return redirect(action_sequence, seq.id, "edit")
4a596eec   Alexis Koralewski   Updating SP, Sequ...
333
334
335
336
        if action == "save_and_submit":
            if check_sequence_validity(seq):
                seq.status = Sequence.TOBEPLANNED
                seq.save()
0318e3c9   Alexis Koralewski   Add tests for F05...
337
                message = "Sequence submitted"
ff76bcca   Alexis Koralewski   Adding Sequences ...
338
                create_sequence_pickle(seq)
0318e3c9   Alexis Koralewski   Add tests for F05...
339
340
341
                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...
342
                return redirect(sequences_list)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
343
344
            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...
345
                messages.add_message(request, messages.ERROR, message)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
346
347
                status = 1
                edit = True
0318e3c9   Alexis Koralewski   Add tests for F05...
348
                return render(request, "routine_manager/view_sequence.html", locals())
1cffbf1c   Etienne Pallier   moved pyros_djang...
349
350
351
    else:
        error = True
        message = "Please check your field's validity"
3b81a22b   Alexis Koralewski   Rework on Request...
352
        depth_level = 1
1cffbf1c   Etienne Pallier   moved pyros_djang...
353
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
354
355
        return render(request, "routine_manager/view_sequence.html", locals())

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
357
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
358
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
359
360
361
362
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...
363
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
364
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
365
366
367

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

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

ad3b297c   Alexis Koralewski   add pagination to...
380
    if alb.sequence.status != Sequence.DRAFT and action == "edit":
1cffbf1c   Etienne Pallier   moved pyros_djang...
381
382
383
384
        error = True
        message = "You can't edit a submitted request"
        action = "view"

d42ec18b   Alexis Koralewski   disabling submit ...
385
    # if check_album_validity(alb) == True:
0318e3c9   Alexis Koralewski   Add tests for F05...
386
        # return redirect(unsubmit_request, req_id)
d42ec18b   Alexis Koralewski   disabling submit ...
387
        # pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
388
    if action == "edit":
0318e3c9   Alexis Koralewski   Add tests for F05...
389
        form = AlbumForm(instance=alb, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
390
391
392
        edit = True
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "view":
cca89707   Alexis Koralewski   adapting observat...
393
        form = AlbumForm(instance=alb, readonly=True)
1cffbf1c   Etienne Pallier   moved pyros_djang...
394
395
396
        edit = False
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "delete":
0318e3c9   Alexis Koralewski   Add tests for F05...
397
398
        log.info(
            f"User {request.user} did action delete Album {alb.name} of Sequence {seq}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
399
400
401
        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...
402
    return redirect(sequences_list)
1cffbf1c   Etienne Pallier   moved pyros_djang...
403

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
405
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
406
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
407
408
409
410
411
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...
412
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
413
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
414
415
416
    alb_id = int(alb_id)
    action = request.POST.get("action")
    alb = Album.objects.get(id=alb_id)
cca89707   Alexis Koralewski   adapting observat...
417
    form = AlbumForm(instance=alb, data=request.POST)
bce55cd9   Alexis Koralewski   Reworking Sequenc...
418
419
    alb.sequence.last_modified_by = request.user
    alb.sequence.save()
1cffbf1c   Etienne Pallier   moved pyros_djang...
420
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
421
422
        #log.info("From album_validate")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
423

1cffbf1c   Etienne Pallier   moved pyros_djang...
424
    if action == "cancel":
c797f02d   Alexis Koralewski   Adding unsubmit b...
425
        alb.delete()
1cffbf1c   Etienne Pallier   moved pyros_djang...
426
427
428
429
430
431
        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...
432
            return redirect(action_sequence, seq_id=alb.sequence.id, action="edit")
0318e3c9   Alexis Koralewski   Add tests for F05...
433
            # return redirect(action_album, alb_id=alb_id, action="edit", status=1, message="Album saved")
1cffbf1c   Etienne Pallier   moved pyros_djang...
434
435
436
437
438
        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...
439
        depth_level = 2
1cffbf1c   Etienne Pallier   moved pyros_djang...
440
        edit = True
1cffbf1c   Etienne Pallier   moved pyros_djang...
441
442
443
444
        seq_id = alb.sequence.id
        action = "edit"
        return render(request, "routine_manager/view_album.html", locals())

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
446
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
447
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
448
449
450
451
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...
452
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
453
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
454
455
    plan_id = int(plan_id)
    plan = Plan.objects.get(id=plan_id)
0318e3c9   Alexis Koralewski   Add tests for F05...
456
457
458
459
460
    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...
461
    seq_id = plan.album.sequence.id
3b81a22b   Alexis Koralewski   Rework on Request...
462
    seq = plan.album.sequence
1cffbf1c   Etienne Pallier   moved pyros_djang...
463
    alb_id = plan.album.id
3b81a22b   Alexis Koralewski   Rework on Request...
464
    depth_level = 3
0318e3c9   Alexis Koralewski   Add tests for F05...
465
466
467
    # 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...
468
    if (settings.DEBUG):
4a596eec   Alexis Koralewski   Updating SP, Sequ...
469
470
        #log.info("From action_plan")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
471
472
473
474
475
476

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

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

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

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
506
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
507
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
508
509
510
511
512
def plan_validate(request, plan_id):
    """
        Called when the plan form was validated.
        Possible actions : Cancel, Save, Delete
    """
3b81a22b   Alexis Koralewski   Rework on Request...
513
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
514
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
1cffbf1c   Etienne Pallier   moved pyros_djang...
515
516
517
    plan_id = int(plan_id)
    action = request.POST.get("action")
    plan = Plan.objects.get(id=plan_id)
3b81a22b   Alexis Koralewski   Rework on Request...
518
    seq = plan.album.sequence
bce55cd9   Alexis Koralewski   Reworking Sequenc...
519
520
    seq.last_modified_by = request.user
    seq.save()
d42ec18b   Alexis Koralewski   disabling submit ...
521
522
523
524
525
526
527
    # 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...
528
529
530
531
532
    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...
533
534
    #form = PlanForm(instance=plan, data=request.POST)
    form = PlanForm(edited_plan=plan, data_from_config=data_from_config)
1cffbf1c   Etienne Pallier   moved pyros_djang...
535
536

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

3b81a22b   Alexis Koralewski   Rework on Request...
541
        plan.delete()
1cffbf1c   Etienne Pallier   moved pyros_djang...
542
543
544
545
        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...
546
        # unused
1cffbf1c   Etienne Pallier   moved pyros_djang...
547
548
        plan = form.save()
        if action == "save":
0318e3c9   Alexis Koralewski   Add tests for F05...
549
550
551
            action = "edit"
            status = 1
            message = "Plan saved"
3b81a22b   Alexis Koralewski   Rework on Request...
552
553
554
            return redirect(action_plan, locals())
    elif action == "save":
        if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
555

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

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
594
595
596
597
598
599
@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...
600
601
    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...
602
603
    return redirect(action_request, req.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
@level_required("Admin", "Unit-PI", "Observer")
3b81a22b   Alexis Koralewski   Rework on Request...
607
def create_sequence(request):
1cffbf1c   Etienne Pallier   moved pyros_djang...
608
609
610
    """
        Create a new sequence and redirects to the editing action on it
    """
3b81a22b   Alexis Koralewski   Rework on Request...
611
612
    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...
613
614
    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...
615
616
617
    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...
618
        return redirect(sequences_list)
0318e3c9   Alexis Koralewski   Add tests for F05...
619
620
    seq = Sequence.objects.create(
        pyros_user=request.user, name=f"seq_{str(date_of_sequence)}", status=Sequence.DRAFT)
4a596eec   Alexis Koralewski   Updating SP, Sequ...
621
    log.info(f"User {request.user} did action create Sequence {seq.name}")
1cffbf1c   Etienne Pallier   moved pyros_djang...
622
623
    return redirect(action_sequence, seq.id, "edit")

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
625
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
626
627
@level_required("Admin", "Unit-PI", "Observer")
def create_album(request, seq_id, album_name, desc):
1cffbf1c   Etienne Pallier   moved pyros_djang...
628
629
630
631
632
    """
        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...
633
634
635
636
637
638
    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...
639
640

@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
641
@level_required("Admin", "Unit-PI", "Observer")
1cffbf1c   Etienne Pallier   moved pyros_djang...
642
643
644
645
646
647
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...
648
649
650
    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...
651
652
    return redirect(action_plan, plan.id, "edit")

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
654
655
656
657
658
659
660
@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...
661
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
        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...
696
        return redirect(sequences_list, status=1, message=message)
1cffbf1c   Etienne Pallier   moved pyros_djang...
697

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
699
700
701
702
703
704
705
@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...
706
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
707
708
709
710
711
712
713
714
715
716
717
718
        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...
719
                                     Q(status=Sequence.INVALID) | Q(status=Sequence.UNPLANNABLE))
1cffbf1c   Etienne Pallier   moved pyros_djang...
720
721
722
723
724
725
726
727
728
729
730
731
    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...
732

1cffbf1c   Etienne Pallier   moved pyros_djang...
733
734
735
736
737
738
739
@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...
740
741
        #log.info("From export_request")
        pass
1cffbf1c   Etienne Pallier   moved pyros_djang...
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756

    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...
757
758
    response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(
        os.path.basename(file_name))
1cffbf1c   Etienne Pallier   moved pyros_djang...
759
760
761

    return response

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

1cffbf1c   Etienne Pallier   moved pyros_djang...
763
764
765
766
767
768
769
770
771
@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...
772
773
        pass
        #log.info("From import_request")
1cffbf1c   Etienne Pallier   moved pyros_djang...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798

    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...
799
800
801
802
803
804
    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...
805

ad3b297c   Alexis Koralewski   add pagination to...
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
#         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...
832
@login_required
ad3b297c   Alexis Koralewski   add pagination to...
833
@csrf_exempt
0318e3c9   Alexis Koralewski   Add tests for F05...
834
@level_required("Admin", "Unit-PI", "Observer")
ad3b297c   Alexis Koralewski   add pagination to...
835
836
837
838
839
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...
840
841
842
843
844
845
846
847
848
849
850
    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...
851
852
            import_response = check_sequence_file_validity(
                yaml_content, request)
ad3b297c   Alexis Koralewski   add pagination to...
853
854
855
856
857
858
859
860
861
862
863
864
865
866
            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...
867
868
@level_required("Admin", "Unit-PI", "Observer")
def export_sequence(request, seq_id: int, type: str):
ad3b297c   Alexis Koralewski   add pagination to...
869
870
871
872
873
    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...
874
875
    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...
876
    yaml_dict = {
0318e3c9   Alexis Koralewski   Add tests for F05...
877
        "sequence": {}
ad3b297c   Alexis Koralewski   add pagination to...
878
    }
0318e3c9   Alexis Koralewski   Add tests for F05...
879

ad3b297c   Alexis Koralewski   add pagination to...
880
881
882
    for field in sequence_form.fields.keys():
        if sequence_form.fields[field].required == False:
            continue
0318e3c9   Alexis Koralewski   Add tests for F05...
883
        field_dict = {"editable": True}
ad3b297c   Alexis Koralewski   add pagination to...
884
        if field == "scientific_program":
0318e3c9   Alexis Koralewski   Add tests for F05...
885
            sp_list = list(sp_list.values_list("name", flat=True))
ad3b297c   Alexis Koralewski   add pagination to...
886
887
888
            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...
889

ad3b297c   Alexis Koralewski   add pagination to...
890
891
892
893
894
895
896
897
898
899
900
901
        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...
902
903
                field_dict["values"] = [
                    value[0] for value in sequence_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
904
905
906
                field_dict["value"] = field_dict["values"].index(value)
            if field == "layout":
                field_dict["editable"] = False
3f8dc14d   Alexis Koralewski   Fixing value of s...
907
            if type == "template" and field != "layout":
ad3b297c   Alexis Koralewski   add pagination to...
908
909
                field_dict["value"] = None
            if sequence_form.fields[field].__dict__.get("min_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
910
911
                field_dict["min_value"] = sequence_form.fields[field].__dict__.get(
                    "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
912
            if sequence_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
913
914
                field_dict["max_value"] = sequence_form.fields[field].__dict__.get(
                    "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
915
916
917
918
919
920
921
922
923
924
        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...
925
926
927
            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...
928
929
930
931
932
933
934
935
936
937
            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...
938
939
                        field_dict["values"] = [
                            value[0] for value in plan_form.fields[field].__dict__.get("_choices")]
ad3b297c   Alexis Koralewski   add pagination to...
940
941
                        container_value_to_str = ""
                        for subkey in value.keys():
0318e3c9   Alexis Koralewski   Add tests for F05...
942
                            container_value_to_str += f"{subkey}:{value[subkey]};"
ad3b297c   Alexis Koralewski   add pagination to...
943
                        container_value_to_str = container_value_to_str[:-1]
0318e3c9   Alexis Koralewski   Add tests for F05...
944
945
                        field_dict["value"] = field_dict["values"].index(
                            container_value_to_str)
ad3b297c   Alexis Koralewski   add pagination to...
946
947
948
949
                    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...
950
951
                            field_dict["min_value"] = plan_form.fields[field].__dict__.get(
                                "min_value")
ad3b297c   Alexis Koralewski   add pagination to...
952
                        if plan_form.fields[field].__dict__.get("max_value"):
0318e3c9   Alexis Koralewski   Add tests for F05...
953
954
                            field_dict["max_value"] = plan_form.fields[field].__dict__.get(
                                "max_value")
ad3b297c   Alexis Koralewski   add pagination to...
955
956
957
958
959
960
961
                        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...
962

ad3b297c   Alexis Koralewski   add pagination to...
963
    yaml_dict["sequence"]["ALBUMS"] = album_list
0318e3c9   Alexis Koralewski   Add tests for F05...
964
965
966
967
    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...
968
969
970

    return response

3b81a22b   Alexis Koralewski   Rework on Request...
971

cca89707   Alexis Koralewski   adapting observat...
972
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
973
974
@level_required("Admin", "Unit-PI", "Observer")
def create_albums(request, seq_id, layout):
cca89707   Alexis Koralewski   adapting observat...
975
976
    seq = Sequence.objects.get(id=seq_id)
    unit_name = os.environ["unit_name"]
ad3b297c   Alexis Koralewski   add pagination to...
977
    config = OBSConfig(os.environ["PATH_TO_OBSCONF_FILE"], unit_name)
0318e3c9   Alexis Koralewski   Add tests for F05...
978
979
    albums = config.getLayoutByName(
        unit_name=config.unit_name, name_of_layout=layout)["ALBUMS"]
cca89707   Alexis Koralewski   adapting observat...
980
    for album_name in albums:
0318e3c9   Alexis Koralewski   Add tests for F05...
981
982
983
984
985
986
        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...
987
988
989
    return HttpResponse("ok")


cca89707   Alexis Koralewski   adapting observat...
990
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
991
992
@level_required("Admin", "Unit-PI", "Observer")
def delete_albums(request, seq_id):
cca89707   Alexis Koralewski   adapting observat...
993
994
    seq = Sequence.objects.get(id=seq_id)
    albums = Album.objects.filter(sequence=seq).delete()
ad3b297c   Alexis Koralewski   add pagination to...
995
996
    seq.complete = False
    seq.save()
cca89707   Alexis Koralewski   adapting observat...
997
998
999
    return HttpResponse(albums)


3b81a22b   Alexis Koralewski   Rework on Request...
1000
@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
1001
1002
1003
1004
1005
@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...
1006
    if request.POST:
0318e3c9   Alexis Koralewski   Add tests for F05...
1007

3b81a22b   Alexis Koralewski   Rework on Request...
1008
1009
1010
1011
        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...
1012
        for key, value in post_data.items():
3b81a22b   Alexis Koralewski   Rework on Request...
1013
1014
1015
            if type(value) == str:
                try:
                    # linked values
0318e3c9   Alexis Koralewski   Add tests for F05...
1016
                    new_dict = {key: {}}
3b81a22b   Alexis Koralewski   Rework on Request...
1017
1018
1019
                    splitted_values = value.split(";")
                    config_attributes[key] = {}
                    for splitted_value in splitted_values:
0318e3c9   Alexis Koralewski   Add tests for F05...
1020
1021
1022
                        subkey, subvalue = splitted_value.split(":")
                        config_attributes[key][subkey] = ast.literal_eval(
                            subvalue)
3b81a22b   Alexis Koralewski   Rework on Request...
1023
1024
1025
1026
1027
1028
                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...
1029
1030
1031
1032
1033
    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...
1034
1035
1036


@login_required
0318e3c9   Alexis Koralewski   Add tests for F05...
1037
1038
@level_required("Admin", "Unit-PI", "Observer")
def copy_sequence(request, seq_id):
ad3b297c   Alexis Koralewski   add pagination to...
1039
1040
1041
    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...
1042
1043
1044
1045
    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...
1046
1047
    new_seq.pk = None
    new_seq._state.adding = True
a61943b5   Alexis Koralewski   Reworking Unit-bo...
1048
    new_seq.pyros_user = request.user
ad3b297c   Alexis Koralewski   add pagination to...
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
    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...
1067
    return redirect(action_sequence, new_seq.id, "edit")