Commit 3b81a22bb2e7f52c16b3b5f004378046963882db
1 parent
94e0e6e2
Exists in
dev
Rework on Request/ Sequences, remove tests of routine manager
Showing
18 changed files
with
931 additions
and
211 deletions
Show diff stats
pyros.py
... | ... | @@ -494,7 +494,8 @@ def install_or_update(UPDATE:bool=False, packages_only:bool=False, database_only |
494 | 494 | if UPDATE: |
495 | 495 | num+=1 |
496 | 496 | printFullTerm(Colors.BLUE, f"{num}) UPDATING SOURCE CODE: Running git pull") |
497 | - _gitpull() or die() | |
497 | + if not WITH_DOCKER: | |
498 | + _gitpull() or die() | |
498 | 499 | |
499 | 500 | # 2) Update python packages (pip upgrade AND pip install requirements) |
500 | 501 | if packages_only: |
... | ... | @@ -534,8 +535,8 @@ def test(app): |
534 | 535 | #start_dir = os.getcwd() |
535 | 536 | if app == None: |
536 | 537 | #apps = ['obsconfig','scientific_program','common', 'scheduler', 'routine_manager', 'user_manager', 'alert_manager.tests.TestStrategyChange'] |
537 | - # Removing alert_manager from tests | |
538 | - apps = ['obsconfig',"scientific_program",'common', 'scheduler', 'routine_manager', 'user_manager'] | |
538 | + # Removing alert_manager and routine_manager from tests | |
539 | + apps = ['obsconfig',"scientific_program",'common', 'scheduler', 'user_manager'] | |
539 | 540 | else: |
540 | 541 | os.environ["PATH_TO_OBSCONF_FILE"] = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"obsconfig/fixtures/observatory_configuration_ok_simple.yml") |
541 | 542 | change_dir(PYROS_DJANGO_BASE_DIR) | ... | ... |
src/core/pyros_django/common/admin.py
... | ... | @@ -101,8 +101,8 @@ class AlbumInline(admin.TabularInline): |
101 | 101 | |
102 | 102 | class PlanInline(admin.TabularInline): |
103 | 103 | model = Plan |
104 | - readonly_fields = ("name",) | |
105 | - fields = ("name",) | |
104 | + #readonly_fields = ("name",) | |
105 | + #fields = ("name",) | |
106 | 106 | show_change_link = True |
107 | 107 | |
108 | 108 | |
... | ... | @@ -144,9 +144,10 @@ class AlertInline(admin.TabularInline): |
144 | 144 | # Admin model classes |
145 | 145 | |
146 | 146 | class RequestAdmin(PyrosModelAdmin): |
147 | - inlines = [ | |
148 | - SequenceInline, | |
149 | - ] | |
147 | + pass | |
148 | + # inlines = [ | |
149 | + # SequenceInline, | |
150 | + # ] | |
150 | 151 | |
151 | 152 | |
152 | 153 | class SequenceAdmin(PyrosModelAdmin): |
... | ... | @@ -199,9 +200,10 @@ class UserLevelAdmin(PyrosModelAdmin): |
199 | 200 | |
200 | 201 | |
201 | 202 | class FilterAdmin(PyrosModelAdmin): |
202 | - inlines = [ | |
203 | - PlanInline, | |
204 | - ] | |
203 | + # inlines = [ | |
204 | + # PlanInline, | |
205 | + # ] | |
206 | + pass | |
205 | 207 | |
206 | 208 | |
207 | 209 | class FilterWheelAdmin(PyrosModelAdmin): |
... | ... | @@ -217,9 +219,10 @@ class NrtAnalysisAdmin(PyrosModelAdmin): |
217 | 219 | |
218 | 220 | |
219 | 221 | class DetectorAdmin(PyrosModelAdmin): |
220 | - inlines = [ | |
221 | - AlbumInline, | |
222 | - ] | |
222 | + pass | |
223 | + # inlines = [ | |
224 | + # AlbumInline, | |
225 | + # ] | |
223 | 226 | |
224 | 227 | |
225 | 228 | class TelescopeAdmin(PyrosModelAdmin): | ... | ... |
src/core/pyros_django/common/models.py
... | ... | @@ -356,8 +356,10 @@ class AgentSurvey(models.Model): |
356 | 356 | class Album(models.Model): |
357 | 357 | sequence = models.ForeignKey( |
358 | 358 | 'Sequence', on_delete=models.CASCADE, related_name="albums") |
359 | - detector = models.ForeignKey( | |
360 | - 'Detector', models.DO_NOTHING, related_name="albums", blank=True, null=True) | |
359 | + # detector = models.ForeignKey( | |
360 | + # 'Detector', models.DO_NOTHING, related_name="albums", blank=True, null=True) | |
361 | + name_of_channel_group = models.CharField(blank=True,null=True,max_length=150) | |
362 | + name_of_channel = models.CharField(blank=True,null=True,max_length=150) | |
361 | 363 | name = models.CharField(max_length=45, blank=True, null=True) |
362 | 364 | desc = models.TextField(blank=True, null=True) |
363 | 365 | created = models.DateTimeField(blank=True, null=True, auto_now_add=True) |
... | ... | @@ -1069,6 +1071,7 @@ class NrtAnalysis(models.Model): |
1069 | 1071 | return (str(self.name)) |
1070 | 1072 | |
1071 | 1073 | |
1074 | +""" | |
1072 | 1075 | class Plan(models.Model): |
1073 | 1076 | album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name="plans") |
1074 | 1077 | filter = models.ForeignKey(Filter, models.DO_NOTHING, related_name="plans", blank=True, null=True) |
... | ... | @@ -1089,9 +1092,16 @@ class Plan(models.Model): |
1089 | 1092 | |
1090 | 1093 | def __str__(self): |
1091 | 1094 | return (str(self.name)) |
1095 | +""" | |
1092 | 1096 | |
1093 | - | |
1094 | - | |
1097 | +class Plan(models.Model): | |
1098 | + album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name="plans") | |
1099 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
1100 | + updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
1101 | + duration = models.FloatField(default=0, blank=True, null=True) | |
1102 | + nb_images = models.PositiveIntegerField(blank=True, null=True,validators=[MinValueValidator(1)]) | |
1103 | + config_attributes = models.JSONField(blank=True,null = True) | |
1104 | + | |
1095 | 1105 | |
1096 | 1106 | class PlcDeviceStatus(models.Model): |
1097 | 1107 | device = models.ForeignKey('PlcDevice', on_delete=models.CASCADE, related_name='current_status') |
... | ... | @@ -1646,6 +1656,7 @@ class SP_PeriodWorkflow(models.Model): |
1646 | 1656 | created = models.DateTimeField(blank=True, null=True, auto_now_add=True) |
1647 | 1657 | |
1648 | 1658 | class Sequence(models.Model): |
1659 | + # TODO : mettre un status : draft ou submitted | |
1649 | 1660 | |
1650 | 1661 | """ Definition of Status enum values """ |
1651 | 1662 | |
... | ... | @@ -1674,8 +1685,12 @@ class Sequence(models.Model): |
1674 | 1685 | (INVALID, "Invalid"), |
1675 | 1686 | ) |
1676 | 1687 | |
1677 | - request = models.ForeignKey( | |
1678 | - Request, on_delete=models.CASCADE, related_name="sequences") | |
1688 | + #request = models.ForeignKey( | |
1689 | + # Request, on_delete=models.CASCADE, related_name="sequences") | |
1690 | + pyros_user = models.ForeignKey( | |
1691 | + 'PyrosUser', on_delete=models.DO_NOTHING, related_name="sequences") | |
1692 | + scientific_program = models.ForeignKey( | |
1693 | + 'ScientificProgram', on_delete=models.DO_NOTHING, related_name="sequences", blank=True, null=True) | |
1679 | 1694 | name = models.CharField(max_length=45, blank=True, null=True) |
1680 | 1695 | desc = models.TextField(blank=True, null=True) |
1681 | 1696 | created = models.DateTimeField(blank=True, null=True, auto_now_add=True) |
... | ... | @@ -1696,6 +1711,9 @@ class Sequence(models.Model): |
1696 | 1711 | obsolete = models.BooleanField(default=False) |
1697 | 1712 | processing = models.BooleanField(default=False) |
1698 | 1713 | flag = models.CharField(max_length=45, blank=True, null=True) |
1714 | + | |
1715 | + start_date = models.DateTimeField(blank=True, null=True, default=timezone.now,editable=True) | |
1716 | + end_date = models.DateTimeField(blank=True, null=True, default=timezone.now,editable=True) | |
1699 | 1717 | # jd1 et jd2 = julian day start / end |
1700 | 1718 | jd1 = models.DecimalField(default=0.0, max_digits=15, decimal_places=8) |
1701 | 1719 | jd2 = models.DecimalField(default=0.0, max_digits=15, decimal_places=8) |
... | ... | @@ -1705,6 +1723,8 @@ class Sequence(models.Model): |
1705 | 1723 | default=-1.0, max_digits=15, decimal_places=8) |
1706 | 1724 | # décomposer duration en duration pointing + duration album |
1707 | 1725 | overhead = models.DecimalField(default=0, max_digits=15, decimal_places=8) |
1726 | + submitted = models.BooleanField(default=False) | |
1727 | + config_attributes = models.JSONField(blank=True,null = True) | |
1708 | 1728 | |
1709 | 1729 | ra = models.FloatField(blank=True, null=True) |
1710 | 1730 | dec = models.FloatField(blank=True, null=True) | ... | ... |
src/core/pyros_django/common/tests.py
... | ... | @@ -7,6 +7,7 @@ from django.utils import timezone |
7 | 7 | from utils.Logger import * |
8 | 8 | log = setupLogger("common", "common") |
9 | 9 | |
10 | +""" | |
10 | 11 | class RequestBuilderTests(TestCase): |
11 | 12 | |
12 | 13 | fixtures = ['tests/common_test_TZ.json'] |
... | ... | @@ -62,7 +63,7 @@ class RequestBuilderTests(TestCase): |
62 | 63 | self.assertEqual(len(albums), 3, "There should be 3 albums") |
63 | 64 | for album in albums: |
64 | 65 | self.assertEqual(album.plans.count(), 1, "There should be 1 plan in each album") |
65 | - | |
66 | +""" | |
66 | 67 | |
67 | 68 | class CountryTests(TestCase): |
68 | 69 | |
... | ... | @@ -88,7 +89,7 @@ class DeviceTests(TestCase): |
88 | 89 | countries = Country.objects.all() |
89 | 90 | self.assertEqual(countries.count(), 0, "perdu !") |
90 | 91 | |
91 | - | |
92 | +""" | |
92 | 93 | class RequestTests(TestCase): |
93 | 94 | |
94 | 95 | def setUp(self): |
... | ... | @@ -181,3 +182,4 @@ class RequestTests(TestCase): |
181 | 182 | self.assertEqual(img1.name, "img1.1") # img1 et img11 sont différents |
182 | 183 | img1 = req1.sequences.get().albums.get().plans.get().images.get( |
183 | 184 | name="img1.2") |
185 | +""" | |
184 | 186 | \ No newline at end of file | ... | ... |
src/core/pyros_django/dashboard/templatetags/tags.py
... | ... | @@ -125,4 +125,12 @@ def get_item(dictionnary,key): |
125 | 125 | |
126 | 126 | @register.filter |
127 | 127 | def get_type(value): |
128 | - return type(value).__name__ | |
129 | 128 | \ No newline at end of file |
129 | + return type(value).__name__ | |
130 | + | |
131 | +@register.filter | |
132 | +def to_int(value): | |
133 | + return int(value) | |
134 | + | |
135 | +@register.filter | |
136 | +def index(indexable, i): | |
137 | + return indexable[i] | |
130 | 138 | \ No newline at end of file | ... | ... |
src/core/pyros_django/routine_manager/forms.py
1 | 1 | from django.conf import settings |
2 | 2 | from django import forms |
3 | +from django.db.models.fields import PositiveIntegerField | |
3 | 4 | from common.models import * |
4 | 5 | from routine_manager.validators import check_album_validity |
5 | 6 | |
6 | -import logging | |
7 | +import logging,ast | |
8 | + | |
9 | +from src.core.pyros_django.routine_manager.widgets import LinkedSelectWidget | |
7 | 10 | log = logging.getLogger("routine_manager-views") |
8 | 11 | |
9 | 12 | class RequestForm(forms.ModelForm): |
... | ... | @@ -37,14 +40,17 @@ class SequenceForm(forms.ModelForm): |
37 | 40 | ('MD', 'BETWEEN_JD1_JD2'), |
38 | 41 | ) |
39 | 42 | |
40 | - start_expo_pref = forms.ChoiceField(label="Start exposure prefered time", choices=START_EXPO_PREF) | |
43 | + start_expo_pref = forms.ChoiceField(label="Start sequence prefered time", choices=START_EXPO_PREF) | |
41 | 44 | duration = forms.IntegerField(label="duration") |
42 | 45 | |
43 | 46 | class Meta: |
44 | 47 | model = Sequence |
45 | - fields = ("name", "target_coords", "jd1", "jd2") | |
48 | + fields = ("scientific_program", "name", "start_date", "end_date") | |
49 | + #target_coords = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'RADEC 0H10M -15D'})) | |
50 | + start_date = forms.DateTimeField(input_formats=['%d/%m/%Y %H:%M'],label="Start date and time") | |
51 | + end_date = forms.DateTimeField(input_formats=['%d/%m/%Y %H:%M'],label="End date and time") | |
46 | 52 | |
47 | - def __init__(self, *args, readonly=False, **kwargs): | |
53 | + def __init__(self, *args, readonly=False, data_from_config, sp_list=None, **kwargs): | |
48 | 54 | super(SequenceForm, self).__init__(*args, **kwargs) |
49 | 55 | if (settings.DEBUG): |
50 | 56 | log.info("From __init__ of SequenceForm") |
... | ... | @@ -53,26 +59,98 @@ class SequenceForm(forms.ModelForm): |
53 | 59 | field.widget.attrs['readonly'] = True |
54 | 60 | field.widget.attrs['class'] = 'form-control' |
55 | 61 | field.required = True |
56 | - | |
62 | + | |
57 | 63 | self.fields['duration'].widget.attrs['readonly'] = True |
58 | 64 | self.fields['duration'].required = False |
59 | - | |
65 | + if sp_list == None: | |
66 | + sp_list = ScientificProgram.objects.all() | |
67 | + self.fields["scientific_program"].queryset = sp_list | |
60 | 68 | seq = kwargs["instance"] |
61 | 69 | if seq.duration: |
62 | 70 | self.fields["duration"].initial = seq.duration * 86400 |
71 | + # fields from config | |
72 | + for component in data_from_config: | |
73 | + for main_key in component.keys(): | |
74 | + edited_seq_value = None | |
75 | + if seq.config_attributes != None and seq.config_attributes.get(main_key,None) != None : | |
76 | + edited_seq_value = seq.config_attributes[main_key] | |
77 | + capability = component[main_key] | |
78 | + unit_of_field = capability.get('unit','None') | |
79 | + label_str = f"{capability['label']}" | |
80 | + if unit_of_field != "" and unit_of_field != "None": | |
81 | + label_str += f" ({unit_of_field})" | |
82 | + if capability.get("is_container",None) == True: | |
83 | + if type(capability["value"] == dict): | |
84 | + initial_index = 0 | |
85 | + first_key = list(capability["value"].keys())[0] | |
86 | + number_of_element = len(capability["value"][first_key]) | |
87 | + container_choices = [] | |
88 | + for i in range(number_of_element): | |
89 | + choice_value = "" | |
90 | + for sub_key in capability["value"].keys(): | |
91 | + if edited_seq_value: | |
92 | + edited_value = edited_seq_value[sub_key] | |
93 | + initial_index = capability["value"][sub_key].index(edited_value) | |
94 | + list_value_of_current_index = capability["value"][sub_key][i] | |
95 | + choice_value+=f"{sub_key}:{list_value_of_current_index};" | |
96 | + choice_value = choice_value[:-1] | |
97 | + container_choices.append((choice_value,"")) | |
98 | + | |
99 | + self.fields[main_key] = forms.ChoiceField(choices=container_choices,label=capability["label"],widget=LinkedSelectWidget(attrs={"label":capability["label"],"sublists":capability["value"],"initial_index":initial_index,"name":main_key})) | |
100 | + else: | |
101 | + # Never happen ? container that isn't a dict | |
102 | + container_choices = [] | |
103 | + for value in capability["value"]: | |
104 | + container_choices.append((value,value)) | |
105 | + self.fields[main_key] = forms.ChoiceField(choices=container_choices,label=label_str,widget=forms.CheckboxSelectMultiple,initial=container_choices[0]) | |
106 | + elif capability.get("is_enum",None) == True: | |
107 | + initial_index = 0 | |
108 | + if edited_seq_value: | |
109 | + initial_index = capability["value"].index(edited_seq_value) | |
110 | + enum_choices = [] | |
111 | + for value in capability["value"]: | |
112 | + enum_choices.append((value,value)) | |
113 | + self.fields[main_key] = forms.ChoiceField(choices=enum_choices,label=label_str,widget=forms.RadioSelect,initial=enum_choices[initial_index]) | |
114 | + else: | |
115 | + initial_value = capability["value"] | |
116 | + if edited_seq_value: | |
117 | + initial_value = edited_seq_value | |
118 | + if type(capability["value"]) == int: | |
119 | + max_value = capability.get("limsup",None) | |
120 | + min_value = capability.get("liminf",None) | |
121 | + self.fields[main_key] = forms.IntegerField(initial=initial_value,max_value=max_value,min_value=min_value,label=label_str) | |
122 | + elif type(capability["value"]) == float: | |
123 | + max_value = capability.get("limsup",None) | |
124 | + min_value = capability.get("liminf",None) | |
125 | + self.fields[main_key] = forms.FloatField(initial=initial_value,max_value=max_value,min_value=min_value,label=label_str) | |
126 | + else: | |
127 | + # type is str or anything else | |
128 | + self.fields[main_key] = forms.CharField(initial=initial_value,label=label_str) | |
129 | + for field in self.fields.values(): | |
130 | + if readonly == True: | |
131 | + field.widget.attrs['readonly'] = True | |
63 | 132 | |
64 | 133 | def save(self): |
65 | 134 | seq = super(SequenceForm, self).save() |
66 | 135 | if (settings.DEBUG): |
67 | 136 | log.info("From save function of SequenceForm") |
68 | - if self.cleaned_data["start_expo_pref"] == "IM": | |
137 | + start_expo_pref = self.cleaned_data.pop("start_expo_pref") | |
138 | + if start_expo_pref == "IM": | |
69 | 139 | seq.t_prefered = -1; |
70 | - elif self.cleaned_data["start_expo_pref"] == "BE": | |
140 | + elif start_expo_pref == "BE": | |
71 | 141 | seq.t_prefered = -1; # TODO : calculer avec le programme de simulation ? |
72 | - elif self.cleaned_data["start_expo_pref"] == "MD": | |
142 | + elif start_expo_pref == "MD": | |
73 | 143 | seq.t_prefered = (seq.jd1 + seq .jd2) / 2; |
74 | 144 | if self.cleaned_data["duration"]: |
75 | - seq.duration = self.cleaned_data["duration"] / 86400 | |
145 | + seq.duration = self.cleaned_data.pop("duration") / 86400 | |
146 | + if self.cleaned_data["target"]: | |
147 | + seq.target_coords = self.cleaned_data["target"] | |
148 | + # remove all key linked to form in order to get config_attributes | |
149 | + self.cleaned_data.pop("name") | |
150 | + self.cleaned_data.pop("start_date") | |
151 | + self.cleaned_data.pop("end_date") | |
152 | + self.cleaned_data.pop("scientific_program") | |
153 | + seq.config_attributes = self.cleaned_data | |
76 | 154 | seq.save() |
77 | 155 | return seq |
78 | 156 | |
... | ... | @@ -84,57 +162,175 @@ class AlbumForm(forms.ModelForm): |
84 | 162 | |
85 | 163 | class Meta: |
86 | 164 | model = Album |
87 | - fields = ("name", "detector") | |
165 | + fields = ("name", "name_of_channel_group", "name_of_channel") | |
88 | 166 | |
89 | - def __init__(self, *args, readonly=False, **kwargs): | |
167 | + def __init__(self, *args, readonly=False, channel_groups_config, **kwargs): | |
90 | 168 | super(AlbumForm, self).__init__(*args, **kwargs) |
91 | 169 | if (settings.DEBUG): |
92 | 170 | log.info("From __init__ of AlbumForm") |
171 | + | |
93 | 172 | for field in self.fields.values(): |
94 | 173 | if readonly == True: |
95 | 174 | field.widget.attrs['readonly'] = True |
96 | 175 | field.widget.attrs['class'] = 'form-control' |
97 | 176 | field.required = True |
177 | + channel_groups_choices = [] | |
178 | + channel_choices = [] | |
179 | + if channel_groups_config["global_groups_logic"] == "and": | |
180 | + channel_groups_choices.append(("All","All")) | |
181 | + else: | |
182 | + for group in channel_groups_config["groups"].values(): | |
183 | + channel_groups_choices.append((group["name"],group["name"])) | |
184 | + self.fields["name_of_channel_group"] = forms.ChoiceField(choices=channel_groups_choices,label="Channel group ",widget=forms.RadioSelect,initial=channel_groups_choices[0]) | |
185 | + if len(channel_groups_config["groups"]) == 1: | |
186 | + group_name = list(channel_groups_config["groups"].keys())[0] | |
187 | + if channel_groups_config["groups"][group_name]["logic"] == "and": | |
188 | + channel_choices.append(("All","All")) | |
189 | + else: | |
190 | + for channel in channel_groups_config["groups"][group_name]["channels"]: | |
191 | + channel_choices.append((channel,channel)) | |
192 | + self.fields["name_of_channel"] = forms.ChoiceField(choices=channel_choices,label="Channel ",widget=forms.RadioSelect,initial=channel_choices[0]) | |
98 | 193 | |
99 | 194 | |
100 | -class PlanForm(forms.ModelForm): | |
101 | - """ | |
102 | - Form for Plan edition | |
103 | - """ | |
195 | + for field in self.fields.values(): | |
196 | + if readonly == True: | |
197 | + field.widget.attrs['readonly'] = True | |
104 | 198 | |
105 | - duration = forms.IntegerField(label="Duration") | |
106 | 199 | |
107 | - class Meta: | |
108 | - model = Plan | |
109 | - fields = ("name", "filter", "nb_images") | |
200 | +# class PlanForm(forms.ModelForm): | |
201 | +# """ | |
202 | +# Form for Plan edition | |
203 | +# """ | |
110 | 204 | |
111 | - def __init__(self, *args, readonly=False, **kwargs): | |
112 | - super(PlanForm, self).__init__(*args, **kwargs) | |
113 | - if (settings.DEBUG): | |
114 | - log.info("From __init__ of PlanForm") | |
115 | - plan = kwargs["instance"] | |
205 | +# duration = forms.IntegerField(label="Duration") | |
116 | 206 | |
117 | - self.fields["filter"].queryset = plan.album.detector.filter_wheel.filters | |
207 | +# class Meta: | |
208 | +# model = Plan | |
209 | +# fields = ("name", "filter", "nb_images") | |
118 | 210 | |
119 | - for field in self.fields.values(): | |
120 | - if readonly == True: | |
121 | - field.widget.attrs['readonly'] = True | |
122 | - field.widget.attrs['class'] = 'form-control' | |
123 | - field.required = True | |
211 | +# def __init__(self, *args, readonly=False, **kwargs): | |
212 | +# super(PlanForm, self).__init__(*args, **kwargs) | |
213 | +# if (settings.DEBUG): | |
214 | +# log.info("From __init__ of PlanForm") | |
215 | +# plan = kwargs["instance"] | |
124 | 216 | |
125 | - if plan.duration: | |
126 | - self.fields["duration"].initial = plan.duration * 86400 | |
217 | +# self.fields["filter"].queryset = plan.album.detector.filter_wheel.filters | |
127 | 218 | |
128 | - def clean_duration(self): | |
129 | - ''' | |
130 | - Checks if duration is at least of 1s | |
131 | - ''' | |
219 | +# for field in self.fields.values(): | |
220 | +# if readonly == True: | |
221 | +# field.widget.attrs['readonly'] = True | |
222 | +# field.widget.attrs['class'] = 'form-control' | |
223 | +# field.required = True | |
224 | + | |
225 | +# if plan.duration: | |
226 | +# self.fields["duration"].initial = plan.duration * 86400 | |
227 | + | |
228 | +# def clean_duration(self): | |
229 | +# ''' | |
230 | +# Checks if duration is at least of 1s | |
231 | +# ''' | |
232 | + | |
233 | +# duration = int(self.cleaned_data.get("duration")) | |
234 | +# if duration < 1: | |
235 | +# raise forms.ValidationError("Duration (in seconds) must be at least of 1") | |
236 | +# return duration | |
237 | + | |
238 | +# def clean_nb_images(self): | |
239 | +# ''' | |
240 | +# Checks if the is at least 1 image | |
241 | +# ''' | |
242 | + | |
243 | +# nb_images = self.cleaned_data.get("nb_images") | |
244 | +# if nb_images < 1: | |
245 | +# raise forms.ValidationError("There must be at least 1 image") | |
246 | +# return nb_images | |
247 | + | |
248 | +# def save(self): | |
249 | +# plan = super(PlanForm, self).save() | |
250 | +# if (settings.DEBUG): | |
251 | +# log.info("From save of PlanForm") | |
252 | +# plan.complete = True | |
253 | +# plan.duration = self.cleaned_data["duration"] / 86400 | |
254 | +# plan.save() | |
255 | +# check_album_validity(plan.album) | |
256 | +# return plan | |
257 | + | |
258 | + | |
259 | + | |
260 | + | |
261 | +class PlanForm(forms.ModelForm): | |
262 | + #plan is linked to an album_id and a channel (name) | |
263 | + #nb_image = PositiveIntegerField() | |
264 | + duration = PositiveIntegerField(editable=False) | |
265 | + class Meta: | |
266 | + model = Plan | |
267 | + fields = ("nb_images",) | |
132 | 268 | |
133 | - duration = int(self.cleaned_data.get("duration")) | |
134 | - if duration < 1: | |
135 | - raise forms.ValidationError("Duration (in seconds) must be at least of 1") | |
136 | - return duration | |
137 | 269 | |
270 | + def __init__(self, *args, data_from_config, edited_plan=None, readonly=False, **kwargs): | |
271 | + super(PlanForm, self).__init__(*args, **kwargs) | |
272 | + self.fields["nb_images"].initial = edited_plan.nb_images | |
273 | + for component in data_from_config: | |
274 | + for main_key in component.keys(): | |
275 | + edited_plan_value = None | |
276 | + if edited_plan.config_attributes != None and edited_plan.config_attributes.get(main_key,None) != None : | |
277 | + edited_plan_value = edited_plan.config_attributes[main_key] | |
278 | + capability = component[main_key] | |
279 | + unit_of_field = capability.get('unit','None') | |
280 | + label_str = f"{capability['label']}" | |
281 | + if unit_of_field != "" and unit_of_field != "None": | |
282 | + label_str += f" ({unit_of_field})" | |
283 | + if capability.get("is_container",None) == True: | |
284 | + if type(capability["value"] == dict): | |
285 | + initial_index = 0 | |
286 | + first_key = list(capability["value"].keys())[0] | |
287 | + number_of_element = len(capability["value"][first_key]) | |
288 | + container_choices = [] | |
289 | + for i in range(number_of_element): | |
290 | + choice_value = "" | |
291 | + for sub_key in capability["value"].keys(): | |
292 | + if edited_plan_value: | |
293 | + edited_value = edited_plan_value[sub_key] | |
294 | + initial_index = capability["value"][sub_key].index(edited_value) | |
295 | + list_value_of_current_index = capability["value"][sub_key][i] | |
296 | + choice_value+=f"{sub_key}:{list_value_of_current_index};" | |
297 | + choice_value = choice_value[:-1] | |
298 | + container_choices.append((choice_value,"")) | |
299 | + | |
300 | + self.fields[main_key] = forms.ChoiceField(choices=container_choices,label=capability["label"],widget=LinkedSelectWidget(attrs={"label":capability["label"],"sublists":capability["value"],"initial_index":initial_index,"name":main_key})) | |
301 | + else: | |
302 | + # Never happen ? container that isn't a dict | |
303 | + container_choices = [] | |
304 | + for value in capability["value"]: | |
305 | + container_choices.append((value,value)) | |
306 | + self.fields[main_key] = forms.ChoiceField(choices=container_choices,label=label_str,widget=forms.CheckboxSelectMultiple,initial=container_choices[0]) | |
307 | + elif capability.get("is_enum",None) == True: | |
308 | + initial_index = 0 | |
309 | + if edited_plan_value: | |
310 | + initial_index = capability["value"].index(edited_plan_value) | |
311 | + enum_choices = [] | |
312 | + for value in capability["value"]: | |
313 | + enum_choices.append((value,value)) | |
314 | + self.fields[main_key] = forms.ChoiceField(choices=enum_choices,label=label_str,widget=forms.RadioSelect,initial=enum_choices[initial_index]) | |
315 | + else: | |
316 | + initial_value = capability["value"] | |
317 | + if edited_plan_value: | |
318 | + initial_value = edited_plan_value | |
319 | + if type(capability["value"]) == int: | |
320 | + max_value = capability.get("limsup",None) | |
321 | + min_value = capability.get("liminf",None) | |
322 | + self.fields[main_key] = forms.IntegerField(initial=initial_value,max_value=max_value,min_value=min_value,label=label_str) | |
323 | + elif type(capability["value"]) == float: | |
324 | + max_value = capability.get("limsup",None) | |
325 | + min_value = capability.get("liminf",None) | |
326 | + self.fields[main_key] = forms.FloatField(initial=initial_value,max_value=max_value,min_value=min_value,label=label_str) | |
327 | + else: | |
328 | + # type is str or anything else | |
329 | + self.fields[main_key] = forms.CharField(initial=initial_value,label=label_str) | |
330 | + for field in self.fields.values(): | |
331 | + if readonly == True: | |
332 | + field.widget.attrs['readonly'] = True | |
333 | + | |
138 | 334 | def clean_nb_images(self): |
139 | 335 | ''' |
140 | 336 | Checks if the is at least 1 image |
... | ... | @@ -145,12 +341,84 @@ class PlanForm(forms.ModelForm): |
145 | 341 | raise forms.ValidationError("There must be at least 1 image") |
146 | 342 | return nb_images |
147 | 343 | |
344 | + def clean(self): | |
345 | + nb_images = self.cleaned_data.get("nb_images") | |
346 | + if nb_images < 1: | |
347 | + raise forms.ValidationError("There must be at least 1 image") | |
348 | + return self.cleaned_data | |
349 | + | |
148 | 350 | def save(self): |
149 | 351 | plan = super(PlanForm, self).save() |
150 | 352 | if (settings.DEBUG): |
151 | 353 | log.info("From save of PlanForm") |
152 | - plan.complete = True | |
153 | - plan.duration = self.cleaned_data["duration"] / 86400 | |
354 | + self.cleaned_data.pop("csrfmiddlewaretoken") | |
355 | + nb_images = self.cleaned_data.pop("nb_images")[0] | |
356 | + config_attributes = {} | |
357 | + log.info(self.cleaned_data) | |
358 | + for key,value in self.cleaned_data.items(): | |
359 | + if type(value) == str: | |
360 | + try: | |
361 | + # linked values | |
362 | + new_dict = {key:{}} | |
363 | + splitted_values = value.split(";") | |
364 | + config_attributes[key] = {} | |
365 | + for splitted_value in splitted_values: | |
366 | + subkey,subvalue = splitted_value.split(":") | |
367 | + config_attributes[key][subkey] = ast.literal_eval(subvalue) | |
368 | + except: | |
369 | + # Do nothing, normal string | |
370 | + config_attributes[key] = ast.literal_eval(value) | |
371 | + plan.nb_images = int(nb_images) | |
372 | + plan.config_attributes = config_attributes | |
154 | 373 | plan.save() |
155 | - check_album_validity(plan.album) | |
156 | 374 | return plan |
375 | + | |
376 | + | |
377 | +class uneditablePlanForm(forms.Form): | |
378 | + def __init__(self, data_from_config, *args, **kwargs): | |
379 | + super(uneditablePlanForm, self).__init__(*args, **kwargs) | |
380 | + # Kwargs take as value the dict returned by ConfigPyros method : getEditableAttributesOfChannel | |
381 | + for component in data_from_config: | |
382 | + for key in component.keys(): | |
383 | + capability = component[key] | |
384 | + unit_of_field = capability.get('unit','None') | |
385 | + label_str = f"{capability['label']}" | |
386 | + if unit_of_field != "" and unit_of_field != "None": | |
387 | + label_str += f" ({unit_of_field})" | |
388 | + if capability.get("is_container",None) == True: | |
389 | + if type(capability["value"] == dict): | |
390 | + first_key = list(capability["value"].keys())[0] | |
391 | + number_of_element = len(capability["value"][first_key]) | |
392 | + container_choices = [] | |
393 | + for i in range(number_of_element): | |
394 | + choice_value = "" | |
395 | + for key in capability["value"].keys(): | |
396 | + list_value_of_current_index = capability["value"][key][i] | |
397 | + choice_value+=f"{key}:{list_value_of_current_index};" | |
398 | + choice_value = choice_value[:-1] | |
399 | + container_choices.append((choice_value,"")) | |
400 | + self.fields[key] = forms.ChoiceField(choices=container_choices,label=capability["label"],widget=LinkedSelectWidget(attrs={"label":capability["label"],"sublists":capability["value"]})) | |
401 | + else: | |
402 | + container_choices = [] | |
403 | + for value in capability["value"]: | |
404 | + container_choices.append((value,value)) | |
405 | + self.fields[key] = forms.ChoiceField(choices=container_choices,label=label_str ,widget=forms.CheckboxSelectMultiple,initial=container_choices[0]) | |
406 | + elif capability.get("is_enum",None) == True: | |
407 | + enum_choices = [] | |
408 | + for value in capability["value"]: | |
409 | + enum_choices.append((value,value)) | |
410 | + self.fields[key] = forms.ChoiceField(choices=enum_choices,label=label_str,widget=forms.RadioSelect,initial=enum_choices[0]) | |
411 | + else: | |
412 | + if type(capability["value"]) == int: | |
413 | + max_value = capability.get("limsup",None) | |
414 | + min_value = capability.get("liminf",None) | |
415 | + self.fields[key] = forms.IntegerField(initial=capability["value"],max_value=max_value,min_value=min_value,label=label_str) | |
416 | + elif type(capability["value"]) == float: | |
417 | + max_value = capability.get("limsup",None) | |
418 | + min_value = capability.get("liminf",None) | |
419 | + self.fields[key] = forms.FloatField(initial=capability["value"],max_value=max_value,min_value=min_value,label=label_str) | |
420 | + else: | |
421 | + # type is str or anything else | |
422 | + self.fields[key] = forms.CharField(initial=capability["value"],label=label_str) | |
423 | + for field in self.fields.values(): | |
424 | + field.widget.attrs['disabled'] = True | |
157 | 425 | \ No newline at end of file | ... | ... |
src/core/pyros_django/routine_manager/templates/routine_manager/edit_base.html
... | ... | @@ -11,21 +11,23 @@ |
11 | 11 | <div class="row text-center" style="margin-top: 20px;"> |
12 | 12 | |
13 | 13 | <ul class="nav nav-tabs col-lg-4" style="margin-bottom: 15px;"> |
14 | - | |
14 | + {% comment %} | |
15 | + | |
15 | 16 | {% if depth_level > 0 %} |
16 | - <li {% if depth_level == 1 %}class="active"{% endif %}><a href="{% url "action_request" req_id action%}" >Request Info</a></li> | |
17 | + <li {% if depth_level == 1 %}class="active"{% endif %}><a href="{% url "action_request" req_id action%}" >Request Info</a></li> | |
17 | 18 | {% endif %} |
19 | + {% endcomment %} | |
18 | 20 | |
19 | - {% if depth_level > 1 %} | |
20 | - <li {% if depth_level == 2 %}class="active"{% endif %}><a href="{% url "action_sequence" seq_id action%}" >Sequence Info</a></li> | |
21 | + {% if depth_level > 0 %} | |
22 | + <li {% if depth_level == 1 %}class="active"{% endif %}><a href="{% url "action_sequence" seq_id action%}" >Sequence Info</a></li> | |
21 | 23 | {% endif %} |
22 | 24 | |
23 | - {% if depth_level > 2 %} | |
24 | - <li {% if depth_level == 3 %}class="active"{% endif %}><a href="{% url "action_album" alb_id action%}" >Album Info</a></li> | |
25 | + {% if depth_level > 1 %} | |
26 | + <li {% if depth_level == 2 %}class="active"{% endif %}><a href="{% url "action_album" alb_id action%}" >Album Info</a></li> | |
25 | 27 | {% endif %} |
26 | 28 | |
27 | - {% if depth_level > 3 %} | |
28 | - <li {% if depth_level == 4 %}class="active"{% endif %}><a href="{% url "action_plan" plan_id action%}" >Plan Info</a></li> | |
29 | + {% if depth_level > 2 %} | |
30 | + <li {% if depth_level == 3 %}class="active"{% endif %}><a href="{% url "action_plan" plan_id action%}" >Plan Info</a></li> | |
29 | 31 | {% endif %} |
30 | 32 | </ul> |
31 | 33 | |
... | ... | @@ -41,17 +43,24 @@ |
41 | 43 | title="The request must be complete to be submitted">Submit request for scheduling</a> |
42 | 44 | {% endif %} |
43 | 45 | {% else %} |
44 | - <a href="{% url "unsubmit_request" req.id %}" class="btn btn-warning" onclick="return confirm('The request will be unvalidated, and the unexecuted sequences, unscheduled')" | |
45 | - title="The request will be unvalidated, and the unexecuted sequences, unscheduled">Unsubmit request</a> | |
46 | + {% comment %} | |
47 | + | |
48 | + <a href="{% url "unsubmit_request" req.id %}" class="btn btn-warning" onclick="return confirm('The request will be unvalidated, and the unexecuted sequences, unscheduled')" | |
49 | + title="The request will be unvalidated, and the unexecuted sequences, unscheduled">Unsubmit request</a> | |
50 | + {% endcomment %} | |
46 | 51 | {% endif %} |
47 | 52 | </div> |
48 | 53 | |
49 | 54 | <div class="row col-lg-4"> |
50 | - <a href="{% url "export_request" req.id %}" class="btn btn-info">Export request as XML file</a> | |
55 | + {# TODO : ajouter url export sequence as yaml file #} | |
56 | + <a href="#" class="btn btn-info">Export sequence as YAML file</a> | |
51 | 57 | </div> |
52 | 58 | |
53 | 59 | <div class="row col-lg-4"> |
60 | + {% comment %} | |
61 | + | |
54 | 62 | TODO : ajouter checkbox pour choisir en dynamique JD / GD |
63 | + {% endcomment %} | |
55 | 64 | </div> |
56 | 65 | </div> |
57 | 66 | |
... | ... | @@ -70,7 +79,7 @@ |
70 | 79 | {% endblock %} |
71 | 80 | |
72 | 81 | {% if action == "view" %} |
73 | - <a href="{{request.path|url_view_to_edit }}" class="btn btn-primary">Move to edition mode</a> | |
82 | + <a href="{{request.path|url_view_to_edit }}" class="btn btn-primary">Edit</a> | |
74 | 83 | |
75 | 84 | {% endif %} |
76 | 85 | </div> |
... | ... | @@ -79,34 +88,35 @@ |
79 | 88 | <div class="row col-lg-12"> |
80 | 89 | <div class="panel-heading"><h3>Summary</h3></div> |
81 | 90 | <div class="well" style="overflow:scroll; height:350px;"> |
91 | + {% comment %} | |
82 | 92 | <div> |
93 | + | |
83 | 94 | <a class="btn btn-sm {% if req.complete %}btn-success{% else %}btn-danger{% endif %}" href="{% url "action_request" req_id action%}" >Request: {{ req.name }}</a> |
84 | 95 | {% if depth_level == 1 %}<span class="label label-info">{{action}}ing ...</span>{% endif %} |
85 | 96 | </div> |
86 | - {% for seq in req.sequences.all %} | |
97 | + {% endcomment %} | |
87 | 98 | |
88 | 99 | <div> |
89 | - <a class="btn btn-sm {% if seq.status != "INCPL" %}btn-success{% else %}btn-danger{% endif %}" href="{% url "action_sequence" seq.id action%}" style="margin-left: 60px" >Sequence: {{ seq.name }}</a> | |
90 | - {% if depth_level == 2 and seq_id == seq.id %}<span class="label label-info">{{action}}ing ...</span>{% endif %} | |
100 | + <a class="btn btn-sm {% if seq.status != "INCPL" %}btn-success{% else %}btn-danger{% endif %}" href="{% url "action_sequence" seq_id action%}" style="margin-left: 60px" >Sequence: {{ seq.name }}</a> | |
101 | + {% if depth_level == 1 %}<span class="label label-info">{{action}}ing ...</span>{% endif %} | |
91 | 102 | </div> |
92 | 103 | {% for album in seq.albums.all %} |
93 | - | |
104 | + | |
94 | 105 | <div> |
95 | 106 | <a class="btn btn-sm {% if album.complete %}btn-success{% else %}btn-danger{% endif %}" href="{% url "action_album" album.id action%}" style="margin-left: 120px" >Album: {{ album.name }}</a> |
96 | - {% if depth_level == 3 and alb_id == album.id %}<span class="label label-info">{{action}}ing ...</span>{% endif %} | |
107 | + {% if depth_level == 2 and alb_id == album.id %}<span class="label label-info">{{action}}ing ...</span>{% endif %} | |
97 | 108 | </div> |
98 | 109 | {% for plan in album.plans.all %} |
99 | - | |
110 | + | |
100 | 111 | <div> |
101 | - <a class="btn btn-sm {% if plan.complete %}btn-success{% else %}btn-danger{% endif %}" href="{% url "action_plan" plan.id action%}" style="margin-left: 180px" >Plan: {{ plan.name }}</a> | |
102 | - {% if depth_level == 4 and plan_id == plan.id %}<span class="label label-info">{{action}}ing ...</span>{% endif %} | |
112 | + <a class="btn btn-sm {% if plan.complete %}btn-success{% else %}btn-danger{% endif %}" href="{% url "action_plan" plan.id action%}" style="margin-left: 180px" >Plan {{ forloop.counter }} ({{ plan.nb_images }} image(s)) : {{ plan.name }}</a> | |
113 | + {% if depth_level == 3 and plan_id == plan.id %}<span class="label label-info">{{action}}ing ...</span>{% endif %} | |
103 | 114 | </div> |
104 | 115 | |
105 | 116 | {% endfor %} |
106 | 117 | |
107 | 118 | {% endfor %} |
108 | 119 | |
109 | - {% endfor %} | |
110 | 120 | </div> |
111 | 121 | </div> |
112 | 122 | ... | ... |
src/core/pyros_django/routine_manager/templates/routine_manager/linkedSelectField_widget.html
0 → 100644
... | ... | @@ -0,0 +1,124 @@ |
1 | +{% comment %} | |
2 | +{% load tags %} | |
3 | + | |
4 | +{% with id=widget.attrs.id %} | |
5 | + | |
6 | + <table id="{{ group }}"> | |
7 | + <thead> | |
8 | + <tr> | |
9 | + <th> {{ widget.label }} </th> | |
10 | + {% for key in widget.attrs.sublists.keys %} | |
11 | + <th> {{ key }} </th> | |
12 | + | |
13 | + {% endfor %} | |
14 | + </tr> | |
15 | + </thead> | |
16 | + <tbody> | |
17 | + {% for key in widget.attrs.sublists.keys %} | |
18 | + {% if forloop.counter0 == 0 %} | |
19 | + {% for element in widget.attrs.sublists|get_item:key %} | |
20 | + {% with forloop.counter0 as line %} | |
21 | + <tr> | |
22 | + <td> | |
23 | + {% if widget.attrs.disabled %} | |
24 | + {% if line == 0 %} | |
25 | + <input name="{{ widget.attrs.label }}" type="radio" id="{{ id }}" value="{{ line }}" disabled checked> | |
26 | + {% else %} | |
27 | + <input name="{{ widget.attrs.label }}" type="radio" id="{{ id }}" value="{{ line }}" disabled> | |
28 | + {% endif %} | |
29 | + | |
30 | + {% else %} | |
31 | + {% if line == 0 %} | |
32 | + <input name="{{ widget.attrs.label }}" type="radio" id="{{ id }}" value="{{ line }}" checked> | |
33 | + {% else %} | |
34 | + <input name="{{ widget.attrs.label }}" type="radio" id="{{ id }}" value="{{ line }}"> | |
35 | + {% endif %} | |
36 | + {% endif %} | |
37 | + </td> | |
38 | + {% for key in widget.attrs.sublists.keys %} | |
39 | + {% with widget.attrs.sublists|get_item:key as list %} | |
40 | + | |
41 | + <td>{{ list | index:line }}</td> | |
42 | + {% endwith %} | |
43 | + {% endfor %} | |
44 | + </tr> | |
45 | + | |
46 | + {% endwith %} | |
47 | + | |
48 | + {% endfor %} | |
49 | + {% endif %} | |
50 | + {% endfor %} | |
51 | + </tbody> | |
52 | + </table> | |
53 | + {% endwith %} | |
54 | + | |
55 | + | |
56 | + | |
57 | +{% endcomment %} | |
58 | +{% load tags %} | |
59 | + | |
60 | + | |
61 | +<table id="{{ group }}"> | |
62 | + <thead> | |
63 | + <tr> | |
64 | + <th> {{ widget.label }} </th> | |
65 | + {% for key in widget.attrs.sublists.keys %} | |
66 | + <th> {{ key }} </th> | |
67 | + | |
68 | + {% endfor %} | |
69 | + </tr> | |
70 | + </thead> | |
71 | + <tbody> | |
72 | + {% with id=widget.attrs.id %} | |
73 | + <div | |
74 | + {% if id %} | |
75 | + id="{{ id }}" | |
76 | + {% endif %} | |
77 | + {% if widget.attrs.class %} | |
78 | + class="{{ widget.attrs.class }}" | |
79 | + {% endif %} | |
80 | + > | |
81 | + {% for group, options, index in widget.optgroups %} | |
82 | + {% if group %} | |
83 | + <div><label>{{ group }}</label> | |
84 | + {% endif %} | |
85 | + {% for option in options %} | |
86 | + | |
87 | + <tr> | |
88 | + | |
89 | + <td> | |
90 | + {% if widget.attrs.readonly %} | |
91 | + {% if option.index|to_int == 0 or option.index|to_int == widget.attrs.initial_index|to_int %} | |
92 | + <input name="{{ widget.attrs.name }}" type="radio" id="{{ id }}" value="{{ option.value }}" disabled checked> | |
93 | + {% else %} | |
94 | + <input name="{{ widget.attrs.name }}" type="radio" id="{{ id }}" value="{{ option.value }}" disabled> | |
95 | + {% endif %} | |
96 | + | |
97 | + {% else %} | |
98 | + {% if option.index|to_int == 0 or option.index|to_int == widget.attrs.initial_index|to_int %} | |
99 | + <input name="{{ widget.attrs.name }}" type="radio" id="{{ id }}" value="{{ option.value }}" checked> | |
100 | + {% else %} | |
101 | + <input name="{{ widget.attrs.name }}" type="radio" id="{{ id }}" value="{{ option.value }}"> | |
102 | + {% endif %} | |
103 | + {% endif %} | |
104 | + </td> | |
105 | + {% for key in widget.attrs.sublists.keys %} | |
106 | + {% with widget.attrs.sublists|get_item:key as list %} | |
107 | + {% with option.index|to_int as int_index %} | |
108 | + <td>{{ list | index:int_index }}</td> | |
109 | + {% endwith %} | |
110 | + {% endwith %} | |
111 | + {% endfor %} | |
112 | + | |
113 | + </tr> | |
114 | + | |
115 | + {% endfor %} | |
116 | + {% if group %} | |
117 | + </div> | |
118 | + {% endif %} | |
119 | + {% endfor %} | |
120 | + </div> | |
121 | + {% endwith %} | |
122 | + | |
123 | + </tbody> | |
124 | +</table> | |
0 | 125 | \ No newline at end of file | ... | ... |
src/core/pyros_django/routine_manager/templates/routine_manager/requests_list.html
... | ... | @@ -18,7 +18,7 @@ |
18 | 18 | <div class="col-lg-5"> |
19 | 19 | <form action="{% url "import_request" %}" method="post" enctype="multipart/form-data"> |
20 | 20 | <label class="btn btn-primary btn-file"> |
21 | - Import an YAML file as a request <input type="file" onchange="this.form.submit()" name="request_file" style="display:none;"/> | |
21 | + Import a YAML file as a request <input type="file" onchange="this.form.submit()" name="request_file" style="display:none;"/> | |
22 | 22 | </label> |
23 | 23 | </form> |
24 | 24 | </div> |
... | ... | @@ -26,8 +26,13 @@ |
26 | 26 | |
27 | 27 | <div class="row" style="margin-top:10px;"> |
28 | 28 | <div class="col-lg-12 text-left"> |
29 | + {% comment %} | |
30 | + | |
29 | 31 | <a href="{% url "create_request" %}"><button class="btn btn-primary">Create manually a new |
30 | - observation request</button></a> | |
32 | + observation request</button></a> | |
33 | + {% endcomment %} | |
34 | + <a href="{% url "create_sequence" %}"><button class="btn btn-primary">Create manually a new | |
35 | + observation request</button></a> | |
31 | 36 | </div> |
32 | 37 | </div> |
33 | 38 | |
... | ... | @@ -61,7 +66,7 @@ |
61 | 66 | <tbody> |
62 | 67 | {% for row in requests %} |
63 | 68 | <tr class={% if row.req.submitted %}"success"{% else %} {% if row.req.complete %}"info"{% else %}"warning"{% endif %} {% endif %}> |
64 | - <td><a href="{% url "action_request" row.req.id 'view' %}">{{ row.req.name }}</a></td> | |
69 | + <td><a href="{% url "action_sequence" row.id 'view' %}">{{ row.name }}</a></td> | |
65 | 70 | <td>{{ row.req.created }}</td> |
66 | 71 | <td>{{ row.req.updated }}</td> |
67 | 72 | <td>{{ row.req.type }}</td> |
... | ... | @@ -71,14 +76,17 @@ |
71 | 76 | <td> |
72 | 77 | <center> |
73 | 78 | {% if row.req.submitted == False %} |
74 | - <a href="{% url "action_request" row.req.id 'edit' %}" class="btn btn-primary">Edit</a> | |
79 | + <a href="{% url "action_sequence" row.id 'edit' %}" class="btn btn-primary">Edit</a> | |
75 | 80 | {% endif %} |
76 | - <a href="{% url "action_request" row.req.id 'view' %}" class="btn btn-primary">View</a> | |
81 | + {% comment %} | |
82 | + | |
83 | + <a href="{% url "action_sequence" row.id 'view' %}" class="btn btn-primary">View</a> | |
84 | + {% endcomment %} | |
77 | 85 | {% if row.req.submitted == False and row.req.complete == True %} |
78 | - <a href="{% url "submit_request" row.req.id "requests_list" %}" class="btn btn-success" onclick="return confirm('The request will be validated, and the sequences scheduled')" | |
86 | + <a href="{% url "submit_request" row.id "requests_list" %}" class="btn btn-success" onclick="return confirm('The request will be validated, and the sequences scheduled')" | |
79 | 87 | title="The request will be validated, and the sequences scheduled">Submit</a> |
80 | 88 | {% endif %} |
81 | - <a href="{% url "action_request" row.req.id 'delete' %}" class="btn btn-danger" | |
89 | + <a href="{% url "action_sequence" row.id 'delete' %}" class="btn btn-danger" | |
82 | 90 | onclick="return confirm('Are you sure you want to delete this request ?')">Delete</a> |
83 | 91 | </center> |
84 | 92 | </td> | ... | ... |
src/core/pyros_django/routine_manager/templates/routine_manager/testcreateplan.html
0 → 100644
... | ... | @@ -0,0 +1,43 @@ |
1 | +{% extends "routine_manager/edit_base.html" %} | |
2 | +{% load static %} | |
3 | + | |
4 | +{% block content %} | |
5 | +<form action="" method="post"> | |
6 | + {% csrf_token %} | |
7 | + <table class="table table-hover table-striped tablesorter"> | |
8 | + | |
9 | + {% for field in form %} | |
10 | + <tr class="fieldWrapper"> | |
11 | + <td>{{ field.label_tag }} {% if field.required %} *{% endif %} </td> | |
12 | + <td>{{ field }} | |
13 | + {{ field.errors }} | |
14 | + </td> | |
15 | + </tr> | |
16 | + {% endfor %} | |
17 | + | |
18 | + </table> | |
19 | + <input id="submit" class="btn btn-info" type="submit" value="Submit" /> | |
20 | +</form> | |
21 | +<button id="showUneditableForm" class="btn btn-info"> Show more informations </button> | |
22 | +<div id="uneditableForm"> | |
23 | + <table class="table table-hover table-striped tablesorter"> | |
24 | + {% for field in uneditableForm %} | |
25 | + <div class="form-group row"> | |
26 | + <tr> | |
27 | + <td>{{ field.label_tag }} {% if field.required %} *{% endif %} </td> | |
28 | + <td>{{ field }} | |
29 | + {{ field.errors }} | |
30 | + </td> | |
31 | + </tr> | |
32 | + </div> | |
33 | + {% endfor %} | |
34 | + | |
35 | + </table> | |
36 | +</div> | |
37 | +<script> | |
38 | + $("#uneditableForm").hide(); | |
39 | + $('#showUneditableForm').click( function(){ | |
40 | + $("#uneditableForm").toggle(); | |
41 | + }); | |
42 | +</script> | |
43 | +{% endblock %} | |
0 | 44 | \ No newline at end of file | ... | ... |
src/core/pyros_django/routine_manager/templates/routine_manager/view_album.html
... | ... | @@ -30,7 +30,7 @@ |
30 | 30 | <td> </td> |
31 | 31 | <td><button type="submit" class="btn btn-success" name="action" value="save">Save</button></td> |
32 | 32 | <td> </td> |
33 | - <td><button type="submit" class="btn btn-success" name="action" value="save_and_add">Save and add new plan</button></td> | |
33 | + <td><button type="submit" class="btn btn-success" name="action" value="save_and_add">Add new plan</button></td> | |
34 | 34 | <td> </td> |
35 | 35 | <td><button type="submit" class="btn btn-danger" name="action" value="delete">Delete</button></td> |
36 | 36 | </tr> |
... | ... | @@ -42,45 +42,48 @@ |
42 | 42 | {% endblock %} |
43 | 43 | |
44 | 44 | {% block children %} |
45 | - <h3>List of Plans - <span class="label label-primary">{{alb.plans.count}}</span></h3> | |
46 | - <div class="table-responsive"> | |
47 | - <table | |
45 | +{% comment %} | |
46 | + | |
47 | +<h3>List of Plans - <span class="label label-primary">{{alb.plans.count}}</span></h3> | |
48 | +<div class="table-responsive"> | |
49 | + <table | |
48 | 50 | class="table table-bordered table-hover table-striped tablesorter"> |
49 | 51 | <thead> |
50 | - <tr> | |
51 | - <th>Name <i class="fa fa-sort"></i></th> | |
52 | + <tr> | |
53 | + <th>Name <i class="fa fa-sort"></i></th> | |
52 | 54 | <th>Created<i class="fa fa-sort"></i></th> |
53 | 55 | <th>Updated<i class="fa fa-sort"></i></th> |
54 | 56 | <th>Filter<i class="fa fa-sort"></i></th> |
55 | -{# <th>Duration<i class="fa fa-sort"></i></th>#} | |
56 | -{# <th>Nb images<i class="fa fa-sort"></i></th>#} | |
57 | + {# <th>Duration<i class="fa fa-sort"></i></th>#} | |
58 | + {# <th>Nb images<i class="fa fa-sort"></i></th>#} | |
57 | 59 | <th>Actions</th> |
58 | - </tr> | |
59 | - </thead> | |
60 | - <tbody> | |
60 | + </tr> | |
61 | + </thead> | |
62 | + <tbody> | |
61 | 63 | {% for plan in alb.plans.all %} |
62 | - <tr class={% if not plan.complete %}"danger"{% else %}"success"{% endif %}> | |
63 | - <td>{{ plan.name }}</td> | |
64 | + <tr class={% if not plan.complete %}"danger"{% else %}"success"{% endif %}> | |
65 | + <td>{{ plan.name }}</td> | |
64 | 66 | <td>{{ plan.created }}</td> |
65 | 67 | <td>{{ plan.updated }}</td> |
66 | 68 | <td>{{ plan.filter }}</td> |
67 | 69 | {# <td>{{ plan.duration }}</td>#} |
68 | 70 | {# <td>{{ plan.nb_images }}</td>#} |
69 | 71 | <td> |
70 | - <center> | |
72 | + <center> | |
71 | 73 | {% if action == 'edit' %} |
72 | - <a href="{% url "action_plan" plan.id 'edit' %}" class="btn btn-primary">Edit</a> | |
74 | + <a href="{% url "action_plan" plan.id 'edit' %}" class="btn btn-primary">Edit</a> | |
73 | 75 | <a href="{% url "action_plan" plan.id 'delete' %}" class="btn btn-danger" |
74 | 76 | onclick="return confirm('Are you sure you want to delete this plan ?')">Delete</a> |
75 | - {% else %} | |
77 | + {% else %} | |
76 | 78 | <a href="{% url "action_plan" plan.id 'view' %}" class="btn btn-primary"><span class="glyphicon glyphicon-e"></span></a> |
77 | - {% endif %} | |
79 | + {% endif %} | |
78 | 80 | </center> |
79 | 81 | </td> |
80 | 82 | |
81 | 83 | </tr> |
82 | - {% endfor %} | |
83 | - </tbody> | |
84 | - </table> | |
85 | - </div> | |
86 | -{% endblock %} | |
87 | 84 | \ No newline at end of file |
85 | + {% endfor %} | |
86 | + </tbody> | |
87 | + </table> | |
88 | + </div> | |
89 | +{% endcomment %} | |
90 | + {% endblock %} | |
88 | 91 | \ No newline at end of file | ... | ... |
src/core/pyros_django/routine_manager/templates/routine_manager/view_plan.html
... | ... | @@ -5,20 +5,37 @@ |
5 | 5 | {% endblock %} |
6 | 6 | |
7 | 7 | {% block fields %} |
8 | - <form action="{% url "plan_validate" plan.id %}" method="post" enctype="multipart/form-data"> | |
8 | + <form action="{% url "plan_validate" plan.id %}" method="post" enctype="multipart/form-data"> | |
9 | 9 | {% csrf_token %} |
10 | 10 | <br> |
11 | - | |
11 | + {% comment %} | |
12 | + | |
12 | 13 | <table class="table table-hover table-striped tablesorter"> |
13 | - {% for field in form %} | |
14 | + {% for field in form %} | |
14 | 15 | <tr> |
15 | - <td>{{ field.label_tag }} {% if field.field.required %} *{% endif %} </td> | |
16 | + <td>{{ field.label_tag }} {% if field.field.required %} *{% endif %} </td> | |
16 | 17 | <td>{{ field }} |
17 | 18 | {{ field.errors }} |
18 | 19 | </td> |
19 | 20 | </tr> |
20 | - {% endfor %} | |
21 | + {% endfor %} | |
21 | 22 | </table> |
23 | + {% endcomment %} | |
24 | + <table class="table table-hover table-striped tablesorter"> | |
25 | + | |
26 | + {% for field in form %} | |
27 | + <tr class="fieldWrapper"> | |
28 | + <td>{{ field.label_tag }} {% if field.required %} *{% endif %} </td> | |
29 | + <td>{{ field }} | |
30 | + {{ field.errors }} | |
31 | + </td> | |
32 | + </tr> | |
33 | + {% endfor %} | |
34 | + | |
35 | + </table> | |
36 | + <button id="showUneditableForm" class="btn btn-info"> Show more informations </button> | |
37 | + | |
38 | + | |
22 | 39 | |
23 | 40 | {% if edit == True %} |
24 | 41 | <br> |
... | ... | @@ -36,9 +53,30 @@ |
36 | 53 | </div> |
37 | 54 | {% endif %} |
38 | 55 | </form> |
39 | - | |
56 | + <div id="uneditableForm"> | |
57 | + <table class="table table-hover table-striped tablesorter"> | |
58 | + {% for field in uneditableForm %} | |
59 | + <div class="form-group row"> | |
60 | + <tr> | |
61 | + <td>{{ field.label_tag }} {% if field.required %} *{% endif %} </td> | |
62 | + <td>{{ field }} | |
63 | + {{ field.errors }} | |
64 | + </td> | |
65 | + </tr> | |
66 | + </div> | |
67 | + {% endfor %} | |
68 | + | |
69 | + </table> | |
70 | + </div> | |
40 | 71 | {% endblock %} |
41 | 72 | |
42 | 73 | {% block children %} |
43 | 74 | |
75 | +<script> | |
76 | + $("#uneditableForm").hide(); | |
77 | + $('#showUneditableForm').click( function(){ | |
78 | + $("#uneditableForm").toggle(); | |
79 | + }); | |
80 | +</script> | |
81 | + | |
44 | 82 | {% endblock %} |
45 | 83 | \ No newline at end of file | ... | ... |
src/core/pyros_django/routine_manager/templates/routine_manager/view_sequence.html
... | ... | @@ -5,6 +5,7 @@ |
5 | 5 | {% endblock %} |
6 | 6 | |
7 | 7 | {% block fields %} |
8 | +<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> | |
8 | 9 | <form action="{% url "sequence_validate" seq.id %}" method="post" enctype="multipart/form-data"> |
9 | 10 | {% csrf_token %} |
10 | 11 | <br> |
... | ... | @@ -28,9 +29,11 @@ |
28 | 29 | <tr> |
29 | 30 | <td><button type="submit" class="btn btn-warning" name="action" value="cancel">Cancel</button></td> |
30 | 31 | <td> </td> |
31 | - <td><button type="submit" class="btn btn-success" name="action" value="save">Save</button></td> | |
32 | + <td><button type="submit" class="btn btn-success" name="action" value="save">Save and finish later</button></td> | |
32 | 33 | <td> </td> |
33 | - <td><button type="submit" class="btn btn-success" name="action" value="save_and_add">Save and add new album</button></td> | |
34 | + <td><button type="submit" class="btn btn-success" name="action" value="save_and_submit">Save and Submit</button></td> | |
35 | + <td> </td> | |
36 | + <td><button type="submit" class="btn btn-success" name="action" value="save_and_add">Add new Album</button></td> | |
34 | 37 | <td> </td> |
35 | 38 | <td><button type="submit" class="btn btn-danger" name="action" value="delete">Delete</button></td> |
36 | 39 | </tr> |
... | ... | @@ -39,44 +42,75 @@ |
39 | 42 | {% endif %} |
40 | 43 | </form> |
41 | 44 | |
45 | + | |
46 | +<script src="https://code.jquery.com/jquery-1.12.4.js"></script> | |
47 | +<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> | |
48 | +<script> | |
49 | +var $=jQuery.noConflict(); | |
50 | + | |
51 | + | |
52 | +$(function(){ | |
53 | + | |
54 | + var dateFormat = "yy-mm-dd"; | |
55 | + start_date = $( "#id_start_date" ).datepicker({ | |
56 | + defaultDate: Date.parse($("#id_start_date").val()), | |
57 | + changeMonth: true, | |
58 | + changeYear: true, | |
59 | + minDate: new Date(), | |
60 | + dateFormat: dateFormat, | |
61 | + constrainInput: false, | |
62 | + }); | |
63 | + end_date = $( "#id_end_date" ).datepicker({ | |
64 | + defaultDate: Date.parse($("#id_end_date").val()), | |
65 | + changeMonth: true, | |
66 | + changeYear: true, | |
67 | + minDate: new Date(), | |
68 | + dateFormat: dateFormat, | |
69 | + constrainInput: false, | |
70 | + }); | |
71 | +}) | |
72 | +</script> | |
42 | 73 | {% endblock %} |
43 | 74 | |
44 | 75 | {% block children %} |
45 | - <h3>List of Albums - <span class="label label-primary">{{seq.albums.count}}</span></h3> | |
46 | - <div class="table-responsive"> | |
76 | +{% comment %} | |
77 | + | |
78 | +<h3>List of Albums - <span class="label label-primary">{{seq.albums.count}}</span></h3> | |
79 | +<div class="table-responsive"> | |
47 | 80 | <table |
48 | - class="table table-bordered table-hover table-striped tablesorter"> | |
81 | + class="table table-bordered table-hover table-striped tablesorter"> | |
49 | 82 | <thead> |
50 | - <tr> | |
51 | - <th>Name <i class="fa fa-sort"></i></th> | |
83 | + <tr> | |
84 | + <th>Name <i class="fa fa-sort"></i></th> | |
52 | 85 | <th>Created<i class="fa fa-sort"></i></th> |
53 | 86 | <th>Updated<i class="fa fa-sort"></i></th> |
54 | 87 | <th>Detector<i class="fa fa-sort"></i></th> |
55 | 88 | <th>Actions</th> |
56 | - </tr> | |
57 | - </thead> | |
58 | - <tbody> | |
89 | + </tr> | |
90 | + </thead> | |
91 | + <tbody> | |
59 | 92 | {% for alb in seq.albums.all %} |
60 | - <tr class={% if not alb.complete %}"danger"{% else %}"success"{% endif %}> | |
93 | + <tr class={% if not alb.complete %}"danger"{% else %}"success"{% endif %}> | |
61 | 94 | <td>{{ alb.name }}</td> |
62 | 95 | <td>{{ alb.created }}</td> |
63 | 96 | <td>{{ alb.updated }}</td> |
64 | 97 | <td>{{ alb.detector }}</td> |
65 | 98 | <td> |
66 | 99 | <center> |
67 | - {% if action == 'edit' %} | |
68 | - <a href="{% url "action_album" alb.id 'edit' %}" class="btn btn-primary">Edit</a> | |
100 | + {% if action == 'edit' %} | |
101 | + <a href="{% url "action_album" alb.id 'edit' %}" class="btn btn-primary">Edit</a> | |
69 | 102 | <a href="{% url "action_album" alb.id 'delete' %}" class="btn btn-danger" |
70 | 103 | onclick="return confirm('Are you sure you want to delete this album ?')">Delete</a> |
71 | - {% else %} | |
104 | + {% else %} | |
72 | 105 | <a href="{% url "action_album" alb.id 'view' %}" class="btn btn-primary">View</a> |
73 | - {% endif %} | |
106 | + {% endif %} | |
74 | 107 | </center> |
75 | 108 | </td> |
76 | 109 | |
77 | 110 | </tr> |
78 | - {% endfor %} | |
111 | + {% endfor %} | |
79 | 112 | </tbody> |
80 | - </table> | |
113 | + </table> | |
81 | 114 | </div> |
82 | -{% endblock %} | |
83 | 115 | \ No newline at end of file |
116 | +{% endcomment %} | |
117 | + {% endblock %} | |
84 | 118 | \ No newline at end of file | ... | ... |
src/core/pyros_django/routine_manager/urls.py
1 | 1 | from django.conf.urls import url |
2 | +from django.urls import path | |
2 | 3 | from . import views |
3 | 4 | |
4 | 5 | urlpatterns = [ |
... | ... | @@ -23,7 +24,8 @@ urlpatterns = [ |
23 | 24 | url(r'^plan_validate/(?P<plan_id>\d+)$', views.plan_validate, name="plan_validate"), |
24 | 25 | |
25 | 26 | url(r'^create_request$', views.create_request, name="create_request"), |
26 | - url(r'^create_sequence/(?P<req_id>\d+)$', views.create_sequence, name="create_sequence"), | |
27 | + #url(r'^create_sequence/(?P<req_id>\d+)$', views.create_sequence, name="create_sequence"), | |
28 | + url(r'^create_sequence/', views.create_sequence, name="create_sequence"), | |
27 | 29 | url(r'^create_album/(?P<seq_id>\d+)$', views.create_album, name="create_album"), |
28 | 30 | url(r'^create_plan/(?P<alb_id>\d+)$', views.create_plan, name="create_plan"), |
29 | 31 | |
... | ... | @@ -32,6 +34,7 @@ urlpatterns = [ |
32 | 34 | |
33 | 35 | url(r'^export_request/(?P<req_id>\d+)$', views.export_request, name="export_request"), |
34 | 36 | url(r'^import_request$', views.import_request, name="import_request"), |
35 | - | |
37 | + path("test_create_plan",views.test_create_plan,name="test_create_plan"), | |
38 | + path("edit_plan/<int:id>",views.edit_plan,name="edit_plan") | |
36 | 39 | ] |
37 | 40 | ... | ... |
src/core/pyros_django/routine_manager/validators.py
... | ... | @@ -78,3 +78,20 @@ def check_request_validity(req): |
78 | 78 | req.complete = True |
79 | 79 | req.save() |
80 | 80 | return False |
81 | + | |
82 | +def check_sequence_validity(seq): | |
83 | + """ | |
84 | + Checks if a request is complete, and save its status in DB | |
85 | + A seq is complete if it has album and plan | |
86 | + :returns : a bool equal to True if the request was submitted but incomplete | |
87 | + """ | |
88 | + | |
89 | + if seq.status ==Sequence.INCOMPLETE != 0 or seq.album.count() == 0: | |
90 | + seq.complete = False | |
91 | + seq.save() | |
92 | + if seq.submitted == True: | |
93 | + return True | |
94 | + return False | |
95 | + seq.complete = True | |
96 | + seq.save() | |
97 | + return False | ... | ... |
src/core/pyros_django/routine_manager/views.py
1 | 1 | from django.utils.decorators import method_decorator |
2 | 2 | from django.views.decorators.csrf import csrf_exempt |
3 | -from django.shortcuts import render, redirect | |
3 | +from django.shortcuts import get_object_or_404, render, redirect | |
4 | 4 | from common.models import * |
5 | 5 | from django.db.models import Q |
6 | 6 | from django.contrib.auth.decorators import login_required |
7 | -from .forms import RequestForm, SequenceForm, AlbumForm, PlanForm | |
7 | + | |
8 | +from src.core.pyros_django.obsconfig.configpyros import ConfigPyros | |
9 | +from .forms import RequestForm, SequenceForm, AlbumForm, PlanForm, uneditablePlanForm | |
8 | 10 | from .validators import check_plan_validity, check_album_validity, check_sequence_validity, check_request_validity |
9 | 11 | from .RequestSerializer import RequestSerializer |
10 | 12 | import scheduler |
... | ... | @@ -19,7 +21,7 @@ from wsgiref.util import FileWrapper |
19 | 21 | from django.utils.encoding import smart_str |
20 | 22 | from django.http import HttpResponse |
21 | 23 | import mimetypes |
22 | -import os | |
24 | +import os, ast, datetime | |
23 | 25 | from pprint import pprint |
24 | 26 | |
25 | 27 | SAVED_REQUESTS_FOLDER = "misc/saved_requests/" |
... | ... | @@ -41,19 +43,23 @@ def requests_list(request, status=0, message=""): |
41 | 43 | |
42 | 44 | # TODO: uncomment for alert filter |
43 | 45 | # requests_objs = Request.objects.filter(pyros_user=request.user).filter(is_alert=False).order_by("-updated") |
44 | - | |
45 | - requests_objs = Request.objects.filter(pyros_user=request.user).order_by("-updated") | |
46 | + sequences_objs = Sequence.objects.filter(pyros_user=request.user).order_by("-updated") | |
46 | 47 | print("REQ/views: Les requetes sont:") |
47 | 48 | |
48 | - requests = [] | |
49 | + requests = sequences_objs | |
50 | + | |
51 | + # requests_objs = Request.objects.filter(pyros_user=request.user).order_by("-updated") | |
52 | + # print("REQ/views: Les requetes sont:") | |
49 | 53 | |
50 | - for req in requests_objs: | |
51 | - #print("- REQ/views: requete", req) | |
52 | - sequences = req.sequences | |
53 | - nb_executed = sequences.filter(status=Sequence.EXECUTED).count | |
54 | - nb_cancelled = sequences.filter(Q(status=Sequence.INVALID) | Q(status=Sequence.CANCELLED) | |
55 | - | Q(status=Sequence.UNPLANNABLE)).count() | |
56 | - requests.append({'req': req, 'nb_seq': sequences.count(), 'nb_executed': nb_executed, 'nb_cancelled': nb_cancelled}) | |
54 | + # requests = [] | |
55 | + | |
56 | + # for req in requests_objs: | |
57 | + # #print("- REQ/views: requete", req) | |
58 | + # sequences = req.sequences | |
59 | + # nb_executed = sequences.filter(status=Sequence.EXECUTED).count | |
60 | + # nb_cancelled = sequences.filter(Q(status=Sequence.INVALID) | Q(status=Sequence.CANCELLED) | |
61 | + # | Q(status=Sequence.UNPLANNABLE)).count() | |
62 | + # requests.append({'req': req, 'nb_seq': sequences.count(), 'nb_executed': nb_executed, 'nb_cancelled': nb_cancelled}) | |
57 | 63 | |
58 | 64 | print("REQ/views: ICI:") |
59 | 65 | # opens template src/routine_manager/templates/routine_manager/requests_list.html with all local variables |
... | ... | @@ -136,12 +142,11 @@ def action_sequence(request, seq_id, action, status=0, message=""): |
136 | 142 | """ |
137 | 143 | Apply actions (view, edit, delete) on a given sequence |
138 | 144 | """ |
139 | - | |
145 | + unit_name = os.environ["unit_name"] | |
146 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"], unit_name) | |
140 | 147 | seq_id = int(seq_id) |
141 | 148 | seq = Sequence.objects.get(id=seq_id) |
142 | - req = seq.request | |
143 | - req_id = req.id | |
144 | - depth_level = 2 | |
149 | + depth_level = 1 | |
145 | 150 | |
146 | 151 | if (settings.DEBUG): |
147 | 152 | log.info("From action_sequence") |
... | ... | @@ -157,20 +162,22 @@ def action_sequence(request, seq_id, action, status=0, message=""): |
157 | 162 | action = "view" |
158 | 163 | |
159 | 164 | if check_sequence_validity(seq) == True: |
160 | - return redirect(unsubmit_request, req_id) | |
165 | + #return redirect(unsubmit_request, req_id) | |
166 | + pass | |
161 | 167 | if action == "edit": |
162 | - form = SequenceForm(instance=seq) | |
168 | + form = SequenceForm(instance=seq, data_from_config=config.getEditableAttributesOfMount(config.unit_name)) | |
163 | 169 | edit = True |
164 | 170 | return render(request, "routine_manager/view_sequence.html", locals()) |
165 | 171 | elif action == "view": |
166 | - form = SequenceForm(instance=seq, readonly=True) | |
172 | + form = SequenceForm(instance=seq, readonly=True, data_from_config=config.getEditableAttributesOfMount(config.unit_name)) | |
167 | 173 | edit = False |
168 | 174 | return render(request, "routine_manager/view_sequence.html", locals()) |
169 | 175 | elif action == "delete": |
170 | 176 | seq.delete() |
171 | - if check_request_validity(req) == True: | |
172 | - return redirect(unsubmit_request, req_id) | |
173 | - return redirect(action_request, req_id=seq.request.id, action="edit", status='1', message="Successfully deleted the sequence") | |
177 | + if check_sequence_validity(seq) == True: | |
178 | + #return redirect(unsubmit_request, req_id) | |
179 | + pass | |
180 | + return redirect(requests_list) | |
174 | 181 | |
175 | 182 | return redirect(requests_list) |
176 | 183 | |
... | ... | @@ -183,16 +190,17 @@ def sequence_validate(request, seq_id): |
183 | 190 | |
184 | 191 | if (settings.DEBUG): |
185 | 192 | log.info("From sequence_validate") |
186 | - | |
193 | + unit_name = os.environ["unit_name"] | |
194 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"], unit_name) | |
187 | 195 | seq_id = int(seq_id) |
188 | 196 | action = request.POST.get("action") |
189 | 197 | seq = Sequence.objects.get(id=seq_id) |
190 | - form = SequenceForm(instance=seq, data=request.POST) | |
198 | + form = SequenceForm(instance=seq, data=request.POST, data_from_config=config.getEditableAttributesOfMount(config.unit_name)) | |
191 | 199 | |
192 | 200 | if action == "cancel": |
193 | 201 | if seq.name == "New sequence": |
194 | 202 | seq.delete() |
195 | - return redirect(action_request, req_id=seq.request.id, action="edit", status='1', message="Cancelled sequence modification") | |
203 | + return redirect(requests_list) | |
196 | 204 | elif action == "delete": |
197 | 205 | return redirect(action_sequence, seq_id, "delete") |
198 | 206 | elif form.is_valid(): |
... | ... | @@ -204,10 +212,8 @@ def sequence_validate(request, seq_id): |
204 | 212 | else: |
205 | 213 | error = True |
206 | 214 | message = "Please check your field's validity" |
207 | - depth_level = 2 | |
215 | + depth_level = 1 | |
208 | 216 | edit = True |
209 | - req = seq.request | |
210 | - req_id = req.id | |
211 | 217 | return render(request, "routine_manager/view_sequence.html", locals()) |
212 | 218 | |
213 | 219 | @login_required |
... | ... | @@ -215,14 +221,14 @@ def action_album(request, alb_id, action, status=0, message=""): |
215 | 221 | """ |
216 | 222 | Apply actions (view, edit, delete) on a given album |
217 | 223 | """ |
224 | + unit_name = os.environ["unit_name"] | |
225 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"], unit_name) | |
218 | 226 | |
219 | 227 | alb_id = int(alb_id) |
220 | 228 | alb = Album.objects.get(id=alb_id) |
221 | - req = alb.sequence.request | |
222 | - req_id = req.id | |
223 | 229 | seq_id = alb.sequence.id |
224 | - depth_level = 3 | |
225 | - | |
230 | + depth_level = 2 | |
231 | + seq = alb.sequence | |
226 | 232 | if (settings.DEBUG): |
227 | 233 | log.info("From action_album") |
228 | 234 | |
... | ... | @@ -236,14 +242,15 @@ def action_album(request, alb_id, action, status=0, message=""): |
236 | 242 | message = "You can't edit a submitted request" |
237 | 243 | action = "view" |
238 | 244 | |
239 | - if check_album_validity(alb) == True: | |
240 | - return redirect(unsubmit_request, req_id) | |
245 | + # if check_album_validity(alb) == True: | |
246 | + # #return redirect(unsubmit_request, req_id) | |
247 | + # pass | |
241 | 248 | if action == "edit": |
242 | - form = AlbumForm(instance=alb) | |
249 | + form = AlbumForm(instance=alb, channel_groups_config=config.get_channel_groups(config.unit_name)) | |
243 | 250 | edit = True |
244 | 251 | return render(request, "routine_manager/view_album.html", locals()) |
245 | 252 | elif action == "view": |
246 | - form = AlbumForm(instance=alb, readonly=True) | |
253 | + form = AlbumForm(instance=alb, readonly=True, channel_groups_config=config.get_channel_groups(config.unit_name)) | |
247 | 254 | edit = False |
248 | 255 | return render(request, "routine_manager/view_album.html", locals()) |
249 | 256 | elif action == "delete": |
... | ... | @@ -258,16 +265,16 @@ def album_validate(request, alb_id): |
258 | 265 | Called when the album form was validated. |
259 | 266 | Possible actions : Cancel, Save, Save and add plan, Delete |
260 | 267 | """ |
261 | - | |
268 | + unit_name = os.environ["unit_name"] | |
269 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"], unit_name) | |
262 | 270 | alb_id = int(alb_id) |
263 | 271 | action = request.POST.get("action") |
264 | 272 | alb = Album.objects.get(id=alb_id) |
265 | - form = AlbumForm(instance=alb, data=request.POST) | |
266 | - | |
273 | + form = AlbumForm(instance=alb, data=request.POST, channel_groups_config=config.get_channel_groups(config.unit_name)) | |
274 | + | |
267 | 275 | if (settings.DEBUG): |
268 | 276 | log.info("From album_validate") |
269 | 277 | |
270 | - | |
271 | 278 | if action == "cancel": |
272 | 279 | if alb.name == "New album": |
273 | 280 | alb.delete() |
... | ... | @@ -277,16 +284,15 @@ def album_validate(request, alb_id): |
277 | 284 | elif form.is_valid(): |
278 | 285 | alb = form.save() |
279 | 286 | if action == "save": |
280 | - return redirect(action_album, alb_id=alb_id, action="edit", status=1, message="Album saved") | |
287 | + return redirect(action_sequence, seq_id=alb.sequence.id, action="edit") | |
288 | + #return redirect(action_album, alb_id=alb_id, action="edit", status=1, message="Album saved") | |
281 | 289 | if action == "save_and_add": |
282 | 290 | return redirect(create_plan, alb_id=alb_id) |
283 | 291 | else: |
284 | 292 | error = True |
285 | 293 | message = "Please check your field's validity" |
286 | - depth_level = 3 | |
294 | + depth_level = 2 | |
287 | 295 | edit = True |
288 | - req = alb.sequence.request | |
289 | - req_id = req.id | |
290 | 296 | seq_id = alb.sequence.id |
291 | 297 | action = "edit" |
292 | 298 | return render(request, "routine_manager/view_album.html", locals()) |
... | ... | @@ -296,14 +302,22 @@ def action_plan(request, plan_id, action, status=0, message=""): |
296 | 302 | """ |
297 | 303 | Apply actions (view, edit, delete) on a given plan |
298 | 304 | """ |
299 | - | |
305 | + unit_name = os.environ["unit_name"] | |
306 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"], unit_name) | |
300 | 307 | plan_id = int(plan_id) |
301 | 308 | plan = Plan.objects.get(id=plan_id) |
302 | - req = plan.album.sequence.request | |
303 | - req_id = req.id | |
309 | + name_of_channel_group = plan.album.name_of_channel_group | |
310 | + channel = None | |
311 | + if name_of_channel_group == "All": | |
312 | + channel = config.getGroupOfChannelByName(config.unit_name,config.get_channel_groups(config.unit_name)["groups"][0]["name"]) | |
313 | + else: | |
314 | + # we retrieve config of the first channel of the group | |
315 | + channel = config.getGroupOfChannelByName(config.unit_name,name_of_channel_group)["channels"][0] | |
316 | + data_from_config = config.getEditableAttributesOfChannel(config.unit_name,channel) | |
304 | 317 | seq_id = plan.album.sequence.id |
318 | + seq = plan.album.sequence | |
305 | 319 | alb_id = plan.album.id |
306 | - depth_level = 4 | |
320 | + depth_level = 3 | |
307 | 321 | |
308 | 322 | if (settings.DEBUG): |
309 | 323 | log.info("From action_plan") |
... | ... | @@ -318,14 +332,17 @@ def action_plan(request, plan_id, action, status=0, message=""): |
318 | 332 | message = "You can't edit a submitted request" |
319 | 333 | action = "view" |
320 | 334 | |
321 | - if check_plan_validity(plan) == True: | |
322 | - return redirect(unsubmit_request, req_id) | |
335 | + # if check_plan_validity(plan) == True: | |
336 | + # #return redirect(unsubmit_request, req_id) | |
337 | + # pass | |
323 | 338 | if action == "edit": |
324 | - form = PlanForm(instance=plan) | |
339 | + form = PlanForm(edited_plan=plan, data_from_config=data_from_config) | |
340 | + uneditableForm = uneditablePlanForm(data_from_config=config.getUneditableAttributesOfChannel(config.unit_name,channel)) | |
325 | 341 | edit = True |
326 | 342 | return render(request, "routine_manager/view_plan.html", locals()) |
327 | 343 | elif action == "view": |
328 | - form = PlanForm(instance=plan, readonly=True) | |
344 | + form = PlanForm(edited_plan=plan, data_from_config=data_from_config,readonly=True) | |
345 | + uneditableForm = uneditablePlanForm(data_from_config=config.getUneditableAttributesOfChannel(config.unit_name,channel)) | |
329 | 346 | edit = False |
330 | 347 | return render(request, "routine_manager/view_plan.html", locals()) |
331 | 348 | elif action == "delete": |
... | ... | @@ -340,35 +357,72 @@ def plan_validate(request, plan_id): |
340 | 357 | Called when the plan form was validated. |
341 | 358 | Possible actions : Cancel, Save, Delete |
342 | 359 | """ |
343 | - | |
360 | + unit_name = os.environ["unit_name"] | |
361 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"], unit_name) | |
344 | 362 | plan_id = int(plan_id) |
345 | 363 | action = request.POST.get("action") |
346 | 364 | plan = Plan.objects.get(id=plan_id) |
347 | - form = PlanForm(instance=plan, data=request.POST) | |
365 | + seq = plan.album.sequence | |
366 | + name_of_channel_group = plan.album.name_of_channel_group | |
367 | + channel = None | |
368 | + if name_of_channel_group == "All": | |
369 | + channel = config.getGroupOfChannelByName(config.unit_name,config.get_channel_groups(config.unit_name)["groups"][0]["name"]) | |
370 | + else: | |
371 | + # we retrieve config of the first channel of the group | |
372 | + channel = config.getGroupOfChannelByName(config.unit_name,name_of_channel_group)["channels"][0] | |
373 | + data_from_config = config.getEditableAttributesOfChannel(config.unit_name,channel) | |
374 | + #form = PlanForm(instance=plan, data=request.POST) | |
375 | + form = PlanForm(edited_plan=plan, data_from_config=data_from_config) | |
348 | 376 | |
349 | 377 | if (settings.DEBUG): |
350 | 378 | log.info("From plan_validate") |
351 | - | |
352 | - | |
353 | 379 | if action == "cancel": |
354 | - if plan.name == "New plan": | |
355 | - plan.delete() | |
380 | + | |
381 | + plan.delete() | |
356 | 382 | return redirect(action_album, alb_id=plan.album.id, action="edit", status='1', message="Cancelled plan modification") |
357 | 383 | elif action == "delete": |
358 | 384 | return redirect(action_plan, plan_id, "delete") |
359 | 385 | elif form.is_valid(): |
386 | + # unused | |
360 | 387 | plan = form.save() |
361 | 388 | if action == "save": |
362 | - return redirect(action_plan, plan_id=plan_id, action="edit", status=1, message="Plan saved") | |
389 | + action="edit" | |
390 | + status=1 | |
391 | + message="Plan saved" | |
392 | + return redirect(action_plan, locals()) | |
393 | + elif action == "save": | |
394 | + if request.POST: | |
395 | + | |
396 | + post_data = request.POST.copy() | |
397 | + post_data.pop("csrfmiddlewaretoken") | |
398 | + post_data.pop("action") | |
399 | + nb_images = post_data.pop("nb_images")[0] | |
400 | + config_attributes = {} | |
401 | + for key,value in post_data.items(): | |
402 | + if type(value) == str: | |
403 | + try: | |
404 | + # linked values | |
405 | + new_dict = {key:{}} | |
406 | + splitted_values = value.split(";") | |
407 | + config_attributes[key] = {} | |
408 | + for splitted_value in splitted_values: | |
409 | + subkey,subvalue = splitted_value.split(":") | |
410 | + config_attributes[key][subkey] = ast.literal_eval(subvalue) | |
411 | + except: | |
412 | + # Do nothing, normal string | |
413 | + config_attributes[key] = ast.literal_eval(value) | |
414 | + plan.nb_images = int(nb_images) | |
415 | + plan.config_attributes = config_attributes | |
416 | + plan.save() | |
417 | + return redirect(action_album, plan.album.id, "edit") | |
418 | + #return redirect(action_plan, plan_id, "edit", 1, "Plan saved") | |
363 | 419 | else: |
364 | 420 | error = True |
365 | 421 | message = "Please check your field's validity" |
366 | 422 | edit = True |
367 | - req = plan.album.sequence.request | |
368 | - req_id = req.id | |
369 | 423 | seq_id = plan.album.sequence.id |
370 | 424 | alb_id = plan.album.id |
371 | - depth_level = 4 | |
425 | + depth_level = 3 | |
372 | 426 | return render(request, "routine_manager/view_plan.html", locals()) |
373 | 427 | |
374 | 428 | @login_required |
... | ... | @@ -381,13 +435,13 @@ def create_request(request): |
381 | 435 | return redirect(action_request, req.id, "edit") |
382 | 436 | |
383 | 437 | @login_required |
384 | -def create_sequence(request, req_id): | |
438 | +def create_sequence(request): | |
385 | 439 | """ |
386 | 440 | Create a new sequence and redirects to the editing action on it |
387 | 441 | """ |
388 | - | |
389 | - req = Request.objects.get(id=req_id) | |
390 | - seq = Sequence.objects.create(request=req, name="New sequence", status=Sequence.INCOMPLETE) | |
442 | + date_of_sequence = datetime.datetime.utcnow() | |
443 | + date_of_sequence = date_of_sequence.strftime("%Y%m%dT%H%M%S") | |
444 | + seq = Sequence.objects.create(pyros_user=request.user, name=f"seq_{str(date_of_sequence)}", status=Sequence.INCOMPLETE) | |
391 | 445 | return redirect(action_sequence, seq.id, "edit") |
392 | 446 | |
393 | 447 | @login_required |
... | ... | @@ -407,7 +461,7 @@ def create_plan(request, alb_id): |
407 | 461 | """ |
408 | 462 | |
409 | 463 | alb = Album.objects.get(id=alb_id) |
410 | - plan = Plan.objects.create(album=alb, name="New plan", complete=False) | |
464 | + plan = Plan.objects.create(album=alb,nb_images=1) | |
411 | 465 | return redirect(action_plan, plan.id, "edit") |
412 | 466 | |
413 | 467 | @login_required |
... | ... | @@ -548,3 +602,63 @@ def import_request(request): |
548 | 602 | |
549 | 603 | # Now, display the list of requests (updated with this new request) |
550 | 604 | return redirect(requests_list, status=status, message=message) |
605 | + | |
606 | +@login_required | |
607 | +def test_create_plan(request): | |
608 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
609 | + if request.POST: | |
610 | + | |
611 | + post_data = request.POST.copy() | |
612 | + post_data.pop("csrfmiddlewaretoken") | |
613 | + nb_images = post_data.pop("nb_images")[0] | |
614 | + config_attributes = {} | |
615 | + for key,value in post_data.items(): | |
616 | + if type(value) == str: | |
617 | + try: | |
618 | + # linked values | |
619 | + new_dict = {key:{}} | |
620 | + splitted_values = value.split(";") | |
621 | + config_attributes[key] = {} | |
622 | + for splitted_value in splitted_values: | |
623 | + subkey,subvalue = splitted_value.split(":") | |
624 | + config_attributes[key][subkey] = ast.literal_eval(subvalue) | |
625 | + except: | |
626 | + # Do nothing, normal string | |
627 | + config_attributes[key] = ast.literal_eval(value) | |
628 | + plan = Plan() | |
629 | + plan.nb_images = int(nb_images) | |
630 | + plan.config_attributes = config_attributes | |
631 | + plan.save() | |
632 | + form = PlanForm(data_from_config=config.getEditableAttributesOfChannel(config.unit_name,list(config.get_channels(config.unit_name).keys())[0])) | |
633 | + uneditableForm = uneditablePlanForm(data_from_config=config.getUneditableAttributesOfChannel(config.unit_name,list(config.get_channels(config.unit_name).keys())[0])) | |
634 | + return render(request,"routine_manager/testcreateplan.html",{"form":form,"uneditableForm":uneditableForm}) | |
635 | + | |
636 | +@login_required | |
637 | +def edit_plan(request,id): | |
638 | + plan = get_object_or_404(Plan,pk=id) | |
639 | + config = ConfigPyros(os.environ["PATH_TO_OBSCONF_FILE"],os.environ["unit_name"]) | |
640 | + if request.POST: | |
641 | + | |
642 | + post_data = request.POST.copy() | |
643 | + post_data.pop("csrfmiddlewaretoken") | |
644 | + nb_images = post_data.pop("nb_images")[0] | |
645 | + config_attributes = {} | |
646 | + for key,value in post_data.items(): | |
647 | + if type(value) == str: | |
648 | + try: | |
649 | + # linked values | |
650 | + new_dict = {key:{}} | |
651 | + splitted_values = value.split(";") | |
652 | + config_attributes[key] = {} | |
653 | + for splitted_value in splitted_values: | |
654 | + subkey,subvalue = splitted_value.split(":") | |
655 | + config_attributes[key][subkey] = ast.literal_eval(subvalue) | |
656 | + except: | |
657 | + # Do nothing, normal string | |
658 | + config_attributes[key] = ast.literal_eval(value) | |
659 | + plan.nb_images = int(nb_images) | |
660 | + plan.config_attributes = config_attributes | |
661 | + plan.save() | |
662 | + form = PlanForm(data_from_config=config.getEditableAttributesOfChannel(config.unit_name,list(config.get_channels(config.unit_name).keys())[0]),edited_plan=plan) | |
663 | + uneditableForm = uneditablePlanForm(data_from_config=config.getUneditableAttributesOfChannel(config.unit_name,list(config.get_channels(config.unit_name).keys())[0])) | |
664 | + return render(request,"routine_manager/testcreateplan.html",{"form":form,"uneditableForm":uneditableForm}) | |
551 | 665 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +from django.forms import widgets | |
2 | +from django.forms.renderers import get_default_renderer | |
3 | + | |
4 | + | |
5 | +class LinkedSelectWidget(widgets.RadioSelect): | |
6 | + def __init__(self,attrs=None,*args,**kwargs): | |
7 | + super(LinkedSelectWidget,self).__init__(attrs,*args,**kwargs) | |
8 | + | |
9 | + template_name = "routine_manager/linkedSelectField_widget.html" | |
10 | + | |
11 | + | |
0 | 12 | \ No newline at end of file | ... | ... |