Compare View
Commits (3)
Showing
8 changed files
Show diff stats
src/core/pyros_django/majordome/agent/Agent.py
... | ... | @@ -154,7 +154,7 @@ import config.old_config as config_old |
154 | 154 | #from config import * |
155 | 155 | |
156 | 156 | from majordome.models import AgentSurvey, AgentCmd, AgentLogs |
157 | -from user_mgmt.models import Period | |
157 | +from user_mgmt.models import Period, Quota | |
158 | 158 | |
159 | 159 | from vendor.guitastro.src.guitastro import Ephemeris |
160 | 160 | import pickle |
... | ... | @@ -3592,7 +3592,17 @@ class Agent: |
3592 | 3592 | val[0] = d_total |
3593 | 3593 | val[-1] = d_total - val[-2] |
3594 | 3594 | # --- update db TODO |
3595 | - | |
3595 | + quota = Quota() | |
3596 | + quota_attributes = {} | |
3597 | + quota_attributes["id_period"] = operiod.id | |
3598 | + quota_attributes["night_id"] = night | |
3599 | + quota_attributes["d_previousq"] = d_prev | |
3600 | + quota_attributes["d_currentq"] = d_cur | |
3601 | + quota_attributes["d_totalq"] = d_total | |
3602 | + quota_attributes["d_nextq"] = d_total - d_cur | |
3603 | + quota.set_attributes_and_save(quota_attributes) | |
3604 | + operiod.quota = quota | |
3605 | + operiod.save() | |
3596 | 3606 | #log.info(f"Write {filename=}") |
3597 | 3607 | #pickle.dump(night_info, open(filename, "wb")) |
3598 | 3608 | return night_info | ... | ... |
src/core/pyros_django/misc/fixtures/initial_fixture_dev_TZ.json
... | ... | @@ -287,8 +287,7 @@ |
287 | 287 | "fields": { |
288 | 288 | "name": "Admin", |
289 | 289 | "desc": "", |
290 | - "priority": 8, | |
291 | - "quota": 9999.0 | |
290 | + "priority": 8 | |
292 | 291 | } |
293 | 292 | }, |
294 | 293 | { |
... | ... | @@ -297,8 +296,7 @@ |
297 | 296 | "fields": { |
298 | 297 | "name": "Observer", |
299 | 298 | "desc": "", |
300 | - "priority": 2, | |
301 | - "quota": 9999.0 | |
299 | + "priority": 2 | |
302 | 300 | } |
303 | 301 | }, |
304 | 302 | { |
... | ... | @@ -307,8 +305,7 @@ |
307 | 305 | "fields": { |
308 | 306 | "name": "TAC", |
309 | 307 | "desc": "", |
310 | - "priority": 1, | |
311 | - "quota": 9999.0 | |
308 | + "priority": 1 | |
312 | 309 | } |
313 | 310 | }, |
314 | 311 | { |
... | ... | @@ -317,8 +314,7 @@ |
317 | 314 | "fields": { |
318 | 315 | "name": "Management board member", |
319 | 316 | "desc": "", |
320 | - "priority": 3, | |
321 | - "quota": 9999.0 | |
317 | + "priority": 3 | |
322 | 318 | } |
323 | 319 | }, |
324 | 320 | { |
... | ... | @@ -327,8 +323,7 @@ |
327 | 323 | "fields": { |
328 | 324 | "name": "Operator", |
329 | 325 | "desc": "", |
330 | - "priority": 4, | |
331 | - "quota": 9999.0 | |
326 | + "priority": 4 | |
332 | 327 | } |
333 | 328 | }, |
334 | 329 | { |
... | ... | @@ -337,8 +332,7 @@ |
337 | 332 | "fields": { |
338 | 333 | "name": "Unit-PI", |
339 | 334 | "desc": "", |
340 | - "priority": 7, | |
341 | - "quota": 9999.0 | |
335 | + "priority": 7 | |
342 | 336 | } |
343 | 337 | }, |
344 | 338 | { |
... | ... | @@ -347,8 +341,7 @@ |
347 | 341 | "fields": { |
348 | 342 | "name": "Unit-board", |
349 | 343 | "desc": "", |
350 | - "priority": 6, | |
351 | - "quota": 9999.0 | |
344 | + "priority": 6 | |
352 | 345 | } |
353 | 346 | }, |
354 | 347 | { |
... | ... | @@ -357,8 +350,7 @@ |
357 | 350 | "fields": { |
358 | 351 | "name": "Visitor", |
359 | 352 | "desc": "Account without any privilege", |
360 | - "priority": 0, | |
361 | - "quota": 0.0 | |
353 | + "priority": 0 | |
362 | 354 | } |
363 | 355 | }, |
364 | 356 | { |
... | ... | @@ -366,7 +358,7 @@ |
366 | 358 | "pk": 2, |
367 | 359 | "fields": { |
368 | 360 | "name": "CNRS", |
369 | - "quota": 20 | |
361 | + "f_quota": 0.2 | |
370 | 362 | } |
371 | 363 | }, |
372 | 364 | { |
... | ... | @@ -374,7 +366,7 @@ |
374 | 366 | "pk": 1, |
375 | 367 | "fields": { |
376 | 368 | "name": "CNES", |
377 | - "quota": 80 | |
369 | + "f_quota": 0.8 | |
378 | 370 | } |
379 | 371 | }, |
380 | 372 | { | ... | ... |
src/core/pyros_django/scheduling/A_Scheduler.py
... | ... | @@ -222,6 +222,12 @@ class A_Scheduler(Agent): |
222 | 222 | # 'vote_referee2' |
223 | 223 | """ |
224 | 224 | |
225 | + | |
226 | + def update_db_quota_sequence(sequence, quota_attributes, id_period, night_id, d_total=sequence_info['duration']): | |
227 | + sequence_quota = sequence.quota | |
228 | + sp_quota = sequence.scientific_program | |
229 | + institute_quota = | |
230 | + | |
225 | 231 | def _compute_schedule_1(self): |
226 | 232 | """Simple scheduler based on selection-insertion one state algorithm. |
227 | 233 | ... | ... |
src/core/pyros_django/scp_mgmt/A_SCP_Manager.py
... | ... | @@ -17,7 +17,7 @@ for short_path in short_paths: |
17 | 17 | |
18 | 18 | # Project imports |
19 | 19 | from majordome.agent.Agent import Agent, build_agent |
20 | -from user_mgmt.models import PyrosUser, SP_Period, Period, SP_Period, SP_Period_Guest, SP_PeriodWorkflow | |
20 | +from user_mgmt.models import PyrosUser, Institute, SP_Period, Period, SP_Period, SP_Period_Guest, SP_PeriodWorkflow | |
21 | 21 | import vendor.guitastro.src.guitastro as guitastro |
22 | 22 | |
23 | 23 | # Django imports |
... | ... | @@ -229,6 +229,9 @@ class A_SCP_Manager(Agent): |
229 | 229 | next_sp_to_be_notified = next_sp.filter(status=SP_Period.STATUSES_ACCEPTED,is_valid = True) |
230 | 230 | self.send_mail_to_observers_for_notification(next_sp_to_be_notified) |
231 | 231 | SP_PeriodWorkflow.objects.create(period=self.period,action=SP_PeriodWorkflow.NOTIFICATION) |
232 | + self.update_sun_moon_ephems() | |
233 | + self.set_quota_for_institutes(self.period.id) | |
234 | + self.set_quota_for_sp(self.period.id) | |
232 | 235 | |
233 | 236 | def routine_process_body(self): |
234 | 237 | print("routine automatic period workflow") |
... | ... | @@ -243,66 +246,27 @@ class A_SCP_Manager(Agent): |
243 | 246 | for n in range(int((end_date - start_date).days)): |
244 | 247 | yield start_date + timedelta(n) |
245 | 248 | |
246 | - def do_generate_ephem_moon_and_sun_for_period(self, period_id:int): | |
247 | - # Obsolete TODO | |
248 | - period = Period.objects.get(id=period_id) | |
249 | - period_start_date = period.start_date | |
250 | - period_end_date = period.end_date | |
251 | - | |
252 | - root_path = os.environ.get("PROJECT_ROOT_PATH") | |
253 | - os.chdir(root_path) | |
254 | - | |
255 | - home = guitastro.Home(self._oc["config"].getHome()) | |
256 | - self._fn.longitude(home.longitude) | |
257 | - | |
258 | - ephem_data_night_folder = self._fn.rootdir | |
259 | - if os.path.exists(ephem_data_night_folder): | |
260 | - period_id = str(period.id) | |
261 | - # form correct period string | |
262 | - if len(str(period.id)) < 3: | |
263 | - while len(period_id) < 3: | |
264 | - period_id = "0" + period_id | |
265 | - period_id = "P" + period_id | |
266 | - ephem_data_night_folder = os.path.join(ephem_data_night_folder, period_id) | |
267 | - if not os.path.exists(ephem_data_night_folder): | |
268 | - os.makedirs(ephem_data_night_folder, exist_ok=True) | |
269 | - for single_date in self.daterange(period_start_date, period_end_date): | |
270 | - current_date = single_date.strftime("%Y%m%d") | |
271 | - | |
272 | - eph = guitastro.Ephemeris() | |
273 | - target_sun = "sun" | |
274 | - ephem_sun = eph.target2night(target_sun, current_date, None, None) | |
275 | - | |
276 | - self._fn.fcontext_create("pyros_eph", "Ephemeris PyROS") | |
277 | - self._fn.fcontext = "pyros_eph" | |
278 | - self._fn.pathnaming("PyROS.eph.1") | |
279 | - self._fn.rootdir = "/tmp/eph" | |
280 | - self._fn.extension = ".f" | |
281 | - param = {} | |
282 | - param['period'] = period_id | |
283 | - param['date'] = current_date | |
284 | - param['unit'] = self.pconfig.unit_name | |
285 | - param['version'] = 1 | |
286 | - param['target'] = "sun" | |
287 | - fname = self._fn.naming_set(param) | |
288 | - file_name_sun = self._fn.join(fname) | |
289 | - | |
290 | - # moon parameters | |
291 | - param['target'] = "moon" | |
292 | - fname = self._fn.naming_set(param) | |
293 | - file_name_moon = self._fn.join(fname) | |
294 | - os.chdir(ephem_data_night_folder) | |
295 | - pickle.dump(ephem_sun, open(f"{file_name_sun}.f","wb")) | |
296 | - target_moon = "moon" | |
297 | - ephem_moon = eph.target2night(target_moon, current_date, None, None) | |
298 | - pickle.dump(ephem_moon, open(f"{file_name_moon}.f","wb")) | |
299 | - | |
300 | - | |
301 | - | |
302 | - | |
303 | - # lire tous les fichiers sun de la period et appliquer le sky_elev pour déterminer le quota total de période | |
304 | - # prendre le champ alt & | |
305 | - # -> prendre tous les éléments en dessous de duskelev (cf l412 d'A_Sheduler, exemple 14 des éphémérides pour trier ces éléménts) et faire un sum | |
249 | + def set_quota_for_institutes(self, id_period): | |
250 | + for institute in Institute.objects.all(): | |
251 | + f_quota = institute.f_quota | |
252 | + # the lowest id of quota table for this period should be the first night of the period | |
253 | + period_quota = Period.objects.get(id=id_period).quota | |
254 | + institute_quota = period_quota.convert_to_quota(f_quota) | |
255 | + new_quota = Quota() | |
256 | + new_quota.set_attributes_and_save(institute_quota) | |
257 | + institute.quota = new_quota | |
258 | + institute.save() | |
259 | + | |
260 | + def set_quota_for_SP(self, id_period): | |
261 | + period = Period.objects.get(id=id_period) | |
262 | + for sp_period in SP_Period.objects.filter(period=period) | |
263 | + sp = sp_period.scientific_program | |
264 | + institute = sp.institute | |
265 | + institute_quota = institute.quota | |
266 | + new_quota = Quota() | |
267 | + quota_attributes = institute_quota.convert_to_quota(sp.f_quota) | |
268 | + new_quota.set_attributes_and_save(quota_attributes) | |
269 | + | |
306 | 270 | |
307 | 271 | if __name__ == "__main__": |
308 | 272 | ... | ... |
src/core/pyros_django/scp_mgmt/forms.py
... | ... | @@ -39,13 +39,13 @@ class SP_PeriodForm(forms.ModelForm): |
39 | 39 | fields = ( |
40 | 40 | #"period", |
41 | 41 | "public_visibility", |
42 | - "quota_minimal", | |
43 | - "quota_nominal", | |
44 | - "quota_allocated", | |
45 | - "over_quota_duration", | |
46 | - "over_quota_duration_allocated", | |
47 | - "token", | |
48 | - "token_allocated", | |
42 | + # "quota_minimal", | |
43 | + # "quota_nominal", | |
44 | + # "quota_allocated", | |
45 | + # "over_quota_duration", | |
46 | + # "over_quota_duration_allocated", | |
47 | + # "token", | |
48 | + # "token_allocated", | |
49 | 49 | "vote_referee1", |
50 | 50 | "reason_referee1", |
51 | 51 | "vote_referee2", | ... | ... |
src/core/pyros_django/scp_mgmt/models.py
1 | -#from django.db import models | |
1 | +from django.db import models | |
2 | + | |
3 | + | |
4 | +class Quota(models.Model): | |
5 | + id_period = models.BigIntegerField(blank=True, null=True) | |
6 | + night_id = models.CharField(max_length=8, null=True, blank=True, db_index=True) | |
7 | + d_totalq = models.BigIntegerField(default=0, blank=True, null=True) | |
8 | + d_totalx = models.BigIntegerField(default=0, blank=True, null=True) | |
9 | + | |
10 | + d_previousq = models.BigIntegerField(default=0, blank=True, null=True) | |
11 | + d_previousx = models.BigIntegerField(default=0, blank=True, null=True) | |
12 | + | |
13 | + d_currentq = models.BigIntegerField(default=0, blank=True, null=True) | |
14 | + d_currentx = models.BigIntegerField(default=0, blank=True, null=True) | |
15 | + | |
16 | + d_passedq = models.BigIntegerField(default=0, blank=True, null=True) | |
17 | + d_passedx = models.BigIntegerField(default=0, blank=True, null=True) | |
18 | + | |
19 | + d_scheduleq = models.BigIntegerField(default=0, blank=True, null=True) | |
20 | + d_schedulex = models.BigIntegerField(default=0, blank=True, null=True) | |
21 | + | |
22 | + d_nextq = models.BigIntegerField(default=0, blank=True, null=True) | |
23 | + d_nextx = models.BigIntegerField(default=0, blank=True, null=True) | |
24 | + | |
25 | + @property | |
26 | + def d_total(self): | |
27 | + return self.d_totalq + self.d_totalx | |
28 | + | |
29 | + @property | |
30 | + def d_previous(self): | |
31 | + return self.d_previousq + self.d_previousx | |
32 | + | |
33 | + @property | |
34 | + def d_current(self): | |
35 | + return self.d_currentq + self.d_currentx | |
36 | + | |
37 | + @property | |
38 | + def d_passed(self): | |
39 | + return self.d_passedq + self.d_passedx | |
40 | + | |
41 | + @property | |
42 | + def d_schedule(self): | |
43 | + return self.d_scheduleq + self.d_schedulex | |
44 | + | |
45 | + @property | |
46 | + def d_next(self): | |
47 | + return self.d_nextq + self.d_nextx | |
48 | + | |
49 | + def set_attributes_and_save(self, quota_attributes:dict): | |
50 | + | |
51 | + if quota_attributes.get("id_period") != None: | |
52 | + self.id_period = quota_attributes["id_period"] | |
53 | + if quota_attributes.get("night_id") != None: | |
54 | + self.night_id = quota_attributes["night_id"] | |
55 | + if quota_attributes.get("d_totalq") != None: | |
56 | + self.d_totalq = quota_attributes["d_totalq"] | |
57 | + if quota_attributes.get("d_totalx") != None: | |
58 | + self.d_totalx = quota_attributes["d_totalx"] | |
59 | + if quota_attributes.get("d_previousq") != None: | |
60 | + self.d_previousq = quota_attributes["d_previousq"] | |
61 | + if quota_attributes.get("d_previousx") != None: | |
62 | + self.d_previousx = quota_attributes["d_previousx"] | |
63 | + if quota_attributes.get("d_currentq") != None: | |
64 | + self.d_currentq = quota_attributes["d_currentq"] | |
65 | + if quota_attributes.get("d_currentx") != None: | |
66 | + self.d_currentx = quota_attributes["d_currentx"] | |
67 | + if quota_attributes.get("d_scheduleq") != None: | |
68 | + self.d_scheduleq = quota_attributes["d_scheduleq"] | |
69 | + if quota_attributes.get("d_schedulex") != None: | |
70 | + self.d_schedule = quota_attributes["d_schedulex"] | |
71 | + if quota_attributes.get("d_nextq") != None: | |
72 | + self.d_nextq = quota_attributes["d_nextq"] | |
73 | + if quota_attributes.get("d_nextx") != None: | |
74 | + self.d_nextx = quota_attributes["d_nextx"] | |
75 | + self.save() | |
76 | + | |
77 | + def convert_to_quota(self, f_quota): | |
78 | + quota_institute = {} | |
79 | + | |
80 | + quota_institute["d_totalq"] = self.d_totalq * f_quota | |
81 | + quota_institute["d_totalx"] = self.d_totalx * f_quota | |
82 | + quota_institute["d_previousq"] = self.d_previousq * f_quota | |
83 | + quota_institute["d_previousx"] = self.d_previousx * f_quota | |
84 | + quota_institute["d_currentq"] = self.d_currentq * f_quota | |
85 | + quota_institute["d_currentx"] = self.d_currentx * f_quota | |
86 | + quota_institute["d_passedq"] = self.d_passedq * f_quota | |
87 | + quota_institute["d_passedx"] = self.d_passedx * f_quota | |
88 | + quota_institute["d_scheduleq"] = self.d_scheduleq * f_quota | |
89 | + quota_institute["d_schedulex"] = self.d_schedulex * f_quota | |
90 | + quota_institute["d_nextq"] = self.d_nextq * f_quota | |
91 | + quota_institute["d_nextx"] = self.d_nextx * f_quota | |
92 | + | |
93 | + return quota_institute | ... | ... |
src/core/pyros_django/seq_submit/models.py
... | ... | @@ -34,6 +34,7 @@ from django.db.models import Q |
34 | 34 | |
35 | 35 | # Project imports |
36 | 36 | from user_mgmt.models import PyrosUser, ScientificProgram, Period |
37 | +from scp_mgmt.models import Quota | |
37 | 38 | # DeviceCommand is used by class Command |
38 | 39 | sys.path.append("../../..") |
39 | 40 | from vendor.guitastro.src.guitastro import FileNames, Ima |
... | ... | @@ -349,8 +350,9 @@ class Sequence(models.Model): |
349 | 350 | obsolete = models.BooleanField(default=False) |
350 | 351 | processing = models.BooleanField(default=False) |
351 | 352 | flag = models.CharField(max_length=45, blank=True, null=True) |
352 | - period = models.ForeignKey(Period, on_delete=models.DO_NOTHING, related_name="sequences", blank=True, null=True) | |
353 | + period = models.ForeignKey(Period, on_delete=models.DO_NOTHING, related_name="sequence_period", blank=True, null=True) | |
353 | 354 | #period = models.ForeignKey("Period", on_delete=models.DO_NOTHING, related_name="sequences", blank=True, null=True) |
355 | + quota = models.ForeignKey(Quota, on_delete=models.DO_NOTHING,related_name="sequence_quotas", blank=True, null=True) | |
354 | 356 | |
355 | 357 | start_date = models.DateTimeField( |
356 | 358 | blank=True, null=True, default=timezone.now, editable=True) | ... | ... |
src/core/pyros_django/user_mgmt/models.py
... | ... | @@ -37,6 +37,11 @@ from django.db.models.query import QuerySet |
37 | 37 | from django.utils import timezone |
38 | 38 | |
39 | 39 | |
40 | +# Pyros import | |
41 | + | |
42 | +from scp_mgmt.models import Quota | |
43 | + | |
44 | + | |
40 | 45 | |
41 | 46 | |
42 | 47 | # Project imports |
... | ... | @@ -52,7 +57,6 @@ class UserLevel(models.Model): |
52 | 57 | name = models.CharField(max_length=45, blank=True, null=True) |
53 | 58 | desc = models.TextField(blank=True, null=True) |
54 | 59 | priority = models.IntegerField(blank=True, null=True) |
55 | - quota = models.FloatField(blank=True, null=True) | |
56 | 60 | |
57 | 61 | class Meta: |
58 | 62 | managed = True |
... | ... | @@ -79,9 +83,11 @@ class Country(models.Model): |
79 | 83 | class Institute(models.Model): |
80 | 84 | name = models.CharField(max_length=100, blank=False, |
81 | 85 | null=False, unique=True) |
82 | - # quota % | |
83 | - quota = models.IntegerField( | |
84 | - validators=[MinValueValidator(0), MaxValueValidator(100)]) | |
86 | + # fraction quota | |
87 | + f_quota = models.FloatField( | |
88 | + validators=[MinValueValidator(0), MaxValueValidator(1)], blank=True, null=True) | |
89 | + | |
90 | + quota = models.ForeignKey(Quota, on_delete=models.DO_NOTHING,related_name="institute_quotas", blank=True, null=True) | |
85 | 91 | #representative_user = models.ForeignKey("PyrosUser", on_delete=models.DO_NOTHING,related_name="institutes",default=1) |
86 | 92 | |
87 | 93 | def __str__(self) -> str: |
... | ... | @@ -90,6 +96,8 @@ class Institute(models.Model): |
90 | 96 | class Meta: |
91 | 97 | db_table = "institute" |
92 | 98 | |
99 | + | |
100 | + | |
93 | 101 | class ScienceTheme(models.Model): |
94 | 102 | name = models.CharField(max_length=120, blank=False, null=False, default="", unique=True) |
95 | 103 | def __str__(self) -> str: |
... | ... | @@ -367,6 +375,8 @@ class Period(models.Model): |
367 | 375 | data_accessibility_duration = models.PositiveIntegerField( |
368 | 376 | blank=True, null=True, default=365*10, editable=True) |
369 | 377 | |
378 | + quota = models.ForeignKey(Quota, on_delete=models.DO_NOTHING,related_name="period_quotas", blank=True, null=True) | |
379 | + | |
370 | 380 | @property |
371 | 381 | def end_date(self): |
372 | 382 | return self.start_date + relativedelta(days=self.exploitation_duration) |
... | ... | @@ -461,11 +471,11 @@ class ScientificProgram(models.Model): |
461 | 471 | description_short = models.TextField(default="", max_length=320) |
462 | 472 | description_long = models.TextField(default="") |
463 | 473 | institute = models.ForeignKey('Institute', on_delete=models.DO_NOTHING, related_name="scientific_programs") |
464 | - sp_pi = models.ForeignKey('PyrosUser', on_delete=models.DO_NOTHING, related_name="Scientific_Program_Users") | |
474 | + sp_pi = models.ForeignKey('PyrosUser', on_delete=models.DO_NOTHING, related_name="scientific_Program_Users") | |
465 | 475 | science_theme = models.ForeignKey(ScienceTheme, on_delete=models.DO_NOTHING, related_name="scientific_program_theme", default=1) |
466 | 476 | is_auto_validated = models.BooleanField(default=False) |
467 | 477 | objects = ScientificProgramManager() |
468 | - | |
478 | + quota = models.ForeignKey(Quota, on_delete=models.DO_NOTHING,related_name="scientific_program_quotas", blank=True, null=True) | |
469 | 479 | class Meta: |
470 | 480 | managed = True |
471 | 481 | db_table = 'scientific_program' |
... | ... | @@ -525,14 +535,6 @@ class SP_Period(models.Model): |
525 | 535 | is_valid = models.TextField( |
526 | 536 | choices=IS_VALID, default=IS_VALID_REJECTED, blank=True) |
527 | 537 | status = models.TextField(choices=STATUSES, default=STATUSES_DRAFT) |
528 | - quota_minimal = models.PositiveIntegerField(default=0) | |
529 | - quota_nominal = models.PositiveIntegerField(default=0) | |
530 | - quota_allocated = models.PositiveIntegerField(default=0, blank=True) | |
531 | - quota_remaining = models.PositiveIntegerField(default=0, blank=True) | |
532 | - over_quota_duration = models.PositiveIntegerField(default=0) | |
533 | - over_quota_duration_allocated = models.PositiveIntegerField( | |
534 | - default=0, blank=True) | |
535 | - over_quota_duration_remaining = models.PositiveIntegerField(default=0) | |
536 | 538 | token = models.PositiveIntegerField(default=0) |
537 | 539 | token_allocated = models.PositiveIntegerField(default=0, blank=True) |
538 | 540 | token_remaining = models.PositiveIntegerField(default=0, blank=True) | ... | ... |