Compare View

switch
from
...
to
 
Commits (3)
src/core/pyros_django/majordome/agent/Agent.py
@@ -154,7 +154,7 @@ import config.old_config as config_old @@ -154,7 +154,7 @@ import config.old_config as config_old
154 #from config import * 154 #from config import *
155 155
156 from majordome.models import AgentSurvey, AgentCmd, AgentLogs 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 from vendor.guitastro.src.guitastro import Ephemeris 159 from vendor.guitastro.src.guitastro import Ephemeris
160 import pickle 160 import pickle
@@ -3592,7 +3592,17 @@ class Agent: @@ -3592,7 +3592,17 @@ class Agent:
3592 val[0] = d_total 3592 val[0] = d_total
3593 val[-1] = d_total - val[-2] 3593 val[-1] = d_total - val[-2]
3594 # --- update db TODO 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 #log.info(f"Write {filename=}") 3606 #log.info(f"Write {filename=}")
3597 #pickle.dump(night_info, open(filename, "wb")) 3607 #pickle.dump(night_info, open(filename, "wb"))
3598 return night_info 3608 return night_info
src/core/pyros_django/misc/fixtures/initial_fixture_dev_TZ.json
@@ -287,8 +287,7 @@ @@ -287,8 +287,7 @@
287 "fields": { 287 "fields": {
288 "name": "Admin", 288 "name": "Admin",
289 "desc": "", 289 "desc": "",
290 - "priority": 8,  
291 - "quota": 9999.0 290 + "priority": 8
292 } 291 }
293 }, 292 },
294 { 293 {
@@ -297,8 +296,7 @@ @@ -297,8 +296,7 @@
297 "fields": { 296 "fields": {
298 "name": "Observer", 297 "name": "Observer",
299 "desc": "", 298 "desc": "",
300 - "priority": 2,  
301 - "quota": 9999.0 299 + "priority": 2
302 } 300 }
303 }, 301 },
304 { 302 {
@@ -307,8 +305,7 @@ @@ -307,8 +305,7 @@
307 "fields": { 305 "fields": {
308 "name": "TAC", 306 "name": "TAC",
309 "desc": "", 307 "desc": "",
310 - "priority": 1,  
311 - "quota": 9999.0 308 + "priority": 1
312 } 309 }
313 }, 310 },
314 { 311 {
@@ -317,8 +314,7 @@ @@ -317,8 +314,7 @@
317 "fields": { 314 "fields": {
318 "name": "Management board member", 315 "name": "Management board member",
319 "desc": "", 316 "desc": "",
320 - "priority": 3,  
321 - "quota": 9999.0 317 + "priority": 3
322 } 318 }
323 }, 319 },
324 { 320 {
@@ -327,8 +323,7 @@ @@ -327,8 +323,7 @@
327 "fields": { 323 "fields": {
328 "name": "Operator", 324 "name": "Operator",
329 "desc": "", 325 "desc": "",
330 - "priority": 4,  
331 - "quota": 9999.0 326 + "priority": 4
332 } 327 }
333 }, 328 },
334 { 329 {
@@ -337,8 +332,7 @@ @@ -337,8 +332,7 @@
337 "fields": { 332 "fields": {
338 "name": "Unit-PI", 333 "name": "Unit-PI",
339 "desc": "", 334 "desc": "",
340 - "priority": 7,  
341 - "quota": 9999.0 335 + "priority": 7
342 } 336 }
343 }, 337 },
344 { 338 {
@@ -347,8 +341,7 @@ @@ -347,8 +341,7 @@
347 "fields": { 341 "fields": {
348 "name": "Unit-board", 342 "name": "Unit-board",
349 "desc": "", 343 "desc": "",
350 - "priority": 6,  
351 - "quota": 9999.0 344 + "priority": 6
352 } 345 }
353 }, 346 },
354 { 347 {
@@ -357,8 +350,7 @@ @@ -357,8 +350,7 @@
357 "fields": { 350 "fields": {
358 "name": "Visitor", 351 "name": "Visitor",
359 "desc": "Account without any privilege", 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,7 +358,7 @@
366 "pk": 2, 358 "pk": 2,
367 "fields": { 359 "fields": {
368 "name": "CNRS", 360 "name": "CNRS",
369 - "quota": 20 361 + "f_quota": 0.2
370 } 362 }
371 }, 363 },
372 { 364 {
@@ -374,7 +366,7 @@ @@ -374,7 +366,7 @@
374 "pk": 1, 366 "pk": 1,
375 "fields": { 367 "fields": {
376 "name": "CNES", 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,6 +222,12 @@ class A_Scheduler(Agent):
222 # 'vote_referee2' 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 def _compute_schedule_1(self): 231 def _compute_schedule_1(self):
226 """Simple scheduler based on selection-insertion one state algorithm. 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,7 +17,7 @@ for short_path in short_paths:
17 17
18 # Project imports 18 # Project imports
19 from majordome.agent.Agent import Agent, build_agent 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 import vendor.guitastro.src.guitastro as guitastro 21 import vendor.guitastro.src.guitastro as guitastro
22 22
23 # Django imports 23 # Django imports
@@ -229,6 +229,9 @@ class A_SCP_Manager(Agent): @@ -229,6 +229,9 @@ class A_SCP_Manager(Agent):
229 next_sp_to_be_notified = next_sp.filter(status=SP_Period.STATUSES_ACCEPTED,is_valid = True) 229 next_sp_to_be_notified = next_sp.filter(status=SP_Period.STATUSES_ACCEPTED,is_valid = True)
230 self.send_mail_to_observers_for_notification(next_sp_to_be_notified) 230 self.send_mail_to_observers_for_notification(next_sp_to_be_notified)
231 SP_PeriodWorkflow.objects.create(period=self.period,action=SP_PeriodWorkflow.NOTIFICATION) 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 def routine_process_body(self): 236 def routine_process_body(self):
234 print("routine automatic period workflow") 237 print("routine automatic period workflow")
@@ -243,66 +246,27 @@ class A_SCP_Manager(Agent): @@ -243,66 +246,27 @@ class A_SCP_Manager(Agent):
243 for n in range(int((end_date - start_date).days)): 246 for n in range(int((end_date - start_date).days)):
244 yield start_date + timedelta(n) 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 if __name__ == "__main__": 271 if __name__ == "__main__":
308 272
src/core/pyros_django/scp_mgmt/forms.py
@@ -39,13 +39,13 @@ class SP_PeriodForm(forms.ModelForm): @@ -39,13 +39,13 @@ class SP_PeriodForm(forms.ModelForm):
39 fields = ( 39 fields = (
40 #"period", 40 #"period",
41 "public_visibility", 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 "vote_referee1", 49 "vote_referee1",
50 "reason_referee1", 50 "reason_referee1",
51 "vote_referee2", 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,6 +34,7 @@ from django.db.models import Q
34 34
35 # Project imports 35 # Project imports
36 from user_mgmt.models import PyrosUser, ScientificProgram, Period 36 from user_mgmt.models import PyrosUser, ScientificProgram, Period
  37 +from scp_mgmt.models import Quota
37 # DeviceCommand is used by class Command 38 # DeviceCommand is used by class Command
38 sys.path.append("../../..") 39 sys.path.append("../../..")
39 from vendor.guitastro.src.guitastro import FileNames, Ima 40 from vendor.guitastro.src.guitastro import FileNames, Ima
@@ -349,8 +350,9 @@ class Sequence(models.Model): @@ -349,8 +350,9 @@ class Sequence(models.Model):
349 obsolete = models.BooleanField(default=False) 350 obsolete = models.BooleanField(default=False)
350 processing = models.BooleanField(default=False) 351 processing = models.BooleanField(default=False)
351 flag = models.CharField(max_length=45, blank=True, null=True) 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 #period = models.ForeignKey("Period", on_delete=models.DO_NOTHING, related_name="sequences", blank=True, null=True) 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 start_date = models.DateTimeField( 357 start_date = models.DateTimeField(
356 blank=True, null=True, default=timezone.now, editable=True) 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,6 +37,11 @@ from django.db.models.query import QuerySet
37 from django.utils import timezone 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 # Project imports 47 # Project imports
@@ -52,7 +57,6 @@ class UserLevel(models.Model): @@ -52,7 +57,6 @@ class UserLevel(models.Model):
52 name = models.CharField(max_length=45, blank=True, null=True) 57 name = models.CharField(max_length=45, blank=True, null=True)
53 desc = models.TextField(blank=True, null=True) 58 desc = models.TextField(blank=True, null=True)
54 priority = models.IntegerField(blank=True, null=True) 59 priority = models.IntegerField(blank=True, null=True)
55 - quota = models.FloatField(blank=True, null=True)  
56 60
57 class Meta: 61 class Meta:
58 managed = True 62 managed = True
@@ -79,9 +83,11 @@ class Country(models.Model): @@ -79,9 +83,11 @@ class Country(models.Model):
79 class Institute(models.Model): 83 class Institute(models.Model):
80 name = models.CharField(max_length=100, blank=False, 84 name = models.CharField(max_length=100, blank=False,
81 null=False, unique=True) 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 #representative_user = models.ForeignKey("PyrosUser", on_delete=models.DO_NOTHING,related_name="institutes",default=1) 91 #representative_user = models.ForeignKey("PyrosUser", on_delete=models.DO_NOTHING,related_name="institutes",default=1)
86 92
87 def __str__(self) -> str: 93 def __str__(self) -> str:
@@ -90,6 +96,8 @@ class Institute(models.Model): @@ -90,6 +96,8 @@ class Institute(models.Model):
90 class Meta: 96 class Meta:
91 db_table = "institute" 97 db_table = "institute"
92 98
  99 +
  100 +
93 class ScienceTheme(models.Model): 101 class ScienceTheme(models.Model):
94 name = models.CharField(max_length=120, blank=False, null=False, default="", unique=True) 102 name = models.CharField(max_length=120, blank=False, null=False, default="", unique=True)
95 def __str__(self) -> str: 103 def __str__(self) -> str:
@@ -367,6 +375,8 @@ class Period(models.Model): @@ -367,6 +375,8 @@ class Period(models.Model):
367 data_accessibility_duration = models.PositiveIntegerField( 375 data_accessibility_duration = models.PositiveIntegerField(
368 blank=True, null=True, default=365*10, editable=True) 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 @property 380 @property
371 def end_date(self): 381 def end_date(self):
372 return self.start_date + relativedelta(days=self.exploitation_duration) 382 return self.start_date + relativedelta(days=self.exploitation_duration)
@@ -461,11 +471,11 @@ class ScientificProgram(models.Model): @@ -461,11 +471,11 @@ class ScientificProgram(models.Model):
461 description_short = models.TextField(default="", max_length=320) 471 description_short = models.TextField(default="", max_length=320)
462 description_long = models.TextField(default="") 472 description_long = models.TextField(default="")
463 institute = models.ForeignKey('Institute', on_delete=models.DO_NOTHING, related_name="scientific_programs") 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 science_theme = models.ForeignKey(ScienceTheme, on_delete=models.DO_NOTHING, related_name="scientific_program_theme", default=1) 475 science_theme = models.ForeignKey(ScienceTheme, on_delete=models.DO_NOTHING, related_name="scientific_program_theme", default=1)
466 is_auto_validated = models.BooleanField(default=False) 476 is_auto_validated = models.BooleanField(default=False)
467 objects = ScientificProgramManager() 477 objects = ScientificProgramManager()
468 - 478 + quota = models.ForeignKey(Quota, on_delete=models.DO_NOTHING,related_name="scientific_program_quotas", blank=True, null=True)
469 class Meta: 479 class Meta:
470 managed = True 480 managed = True
471 db_table = 'scientific_program' 481 db_table = 'scientific_program'
@@ -525,14 +535,6 @@ class SP_Period(models.Model): @@ -525,14 +535,6 @@ class SP_Period(models.Model):
525 is_valid = models.TextField( 535 is_valid = models.TextField(
526 choices=IS_VALID, default=IS_VALID_REJECTED, blank=True) 536 choices=IS_VALID, default=IS_VALID_REJECTED, blank=True)
527 status = models.TextField(choices=STATUSES, default=STATUSES_DRAFT) 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 token = models.PositiveIntegerField(default=0) 538 token = models.PositiveIntegerField(default=0)
537 token_allocated = models.PositiveIntegerField(default=0, blank=True) 539 token_allocated = models.PositiveIntegerField(default=0, blank=True)
538 token_remaining = models.PositiveIntegerField(default=0, blank=True) 540 token_remaining = models.PositiveIntegerField(default=0, blank=True)