Commit a6e636042a40776beb38f49f28a98a149db32235
1 parent
b6a13fc4
Exists in
dev
adding agentSP and tests for F02, updating other tests according new attributes of SCP
Showing
18 changed files
with
1293 additions
and
416 deletions
Show diff stats
pyros.py
... | ... | @@ -37,7 +37,8 @@ AGENTS = { |
37 | 37 | "agentX" : "AgentX", |
38 | 38 | "agentA" : "AgentA", |
39 | 39 | "agentB" : "AgentB", |
40 | - "agentM" : "AgentM", | |
40 | + "agentM" : "AgentM", | |
41 | + "agentSP" : "AgentSP", | |
41 | 42 | #"agentDevice" : "AgentDevice", |
42 | 43 | #"agentDeviceTelescopeGemini" : "AgentDeviceTelescopeGemini", |
43 | 44 | "agentDeviceGemini" : "AgentDeviceGemini", |
... | ... | @@ -524,8 +525,8 @@ def test(app): |
524 | 525 | #start_dir = os.getcwd() |
525 | 526 | if app == None: |
526 | 527 | #apps = ['obsconfig','scientific_program','common', 'scheduler', 'routine_manager', 'user_manager', 'alert_manager.tests.TestStrategyChange'] |
527 | - # Removing temporiraly scientific_program and alert_manager from tests | |
528 | - apps = ['obsconfig','common', 'scheduler', 'routine_manager', 'user_manager'] | |
528 | + # Removing alert_manager from tests | |
529 | + apps = ['obsconfig',"scientific_program",'common', 'scheduler', 'routine_manager', 'user_manager'] | |
529 | 530 | else: |
530 | 531 | os.environ["PATH_TO_OBSCONF_FILE"] = os.path.join(os.path.abspath(PYROS_DJANGO_BASE_DIR),"obsconfig/fixtures/observatory_configuration_ok_simple.yml") |
531 | 532 | change_dir(PYROS_DJANGO_BASE_DIR) |
... | ... | @@ -712,6 +713,8 @@ def start(agent:str, configfile:str,observatory:str): |
712 | 713 | ##agentX.run(FOR_REAL=True) |
713 | 714 | if agent_name == "agentM": |
714 | 715 | os.chdir(PYROS_DJANGO_BASE_DIR+"/monitoring/") |
716 | + elif agent_name == "agentSP": | |
717 | + os.chdir(PYROS_DJANGO_BASE_DIR+"/scientific_program/") | |
715 | 718 | else: |
716 | 719 | os.chdir(PYROS_DJANGO_BASE_DIR+"/agent/") |
717 | 720 | #cmd = "-m AgentX" | ... | ... |
src/core/pyros_django/common/models.py
... | ... | @@ -10,7 +10,7 @@ from typing import List |
10 | 10 | from django.core.validators import MaxValueValidator, MinValueValidator |
11 | 11 | |
12 | 12 | # DJANGO imports |
13 | -from django.contrib.auth.models import AbstractUser | |
13 | +from django.contrib.auth.models import AbstractUser, UserManager | |
14 | 14 | from django.db import models |
15 | 15 | from django.db.models import Q, Max |
16 | 16 | from django.core.validators import MaxValueValidator, MinValueValidator |
... | ... | @@ -1234,7 +1234,6 @@ class ScienceTheme(models.Model): |
1234 | 1234 | |
1235 | 1235 | def __str__(self) -> str: |
1236 | 1236 | return str(self.name) |
1237 | - | |
1238 | 1237 | class Institute(models.Model): |
1239 | 1238 | name = models.CharField(max_length=100,blank=False,null=False,unique=True) |
1240 | 1239 | quota = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100)]) |
... | ... | @@ -1243,6 +1242,13 @@ class Institute(models.Model): |
1243 | 1242 | def __str__(self) -> str: |
1244 | 1243 | return str(self.name) |
1245 | 1244 | |
1245 | +class PyrosUserManager(UserManager): | |
1246 | + def tac_users(self): | |
1247 | + return PyrosUser.objects.filter(user_level__name="TAC") | |
1248 | + | |
1249 | + def unit_users(self): | |
1250 | + return PyrosUser.objects.filter(Q(user_level__name="Unit-PI")|Q(user_level__name="Unit-board")) | |
1251 | + | |
1246 | 1252 | class PyrosUser(AbstractUser): |
1247 | 1253 | username = models.CharField(max_length=255, blank=False, null=False, unique=True) |
1248 | 1254 | is_active = models.BooleanField(default='False') |
... | ... | @@ -1274,6 +1280,8 @@ class PyrosUser(AbstractUser): |
1274 | 1280 | # can_del_void_req = models.BooleanField(default=False) |
1275 | 1281 | validator = models.ForeignKey("PyrosUser", on_delete=models.DO_NOTHING, null=True, related_name="pyros_users") |
1276 | 1282 | referee_themes = models.ManyToManyField("ScienceTheme",related_name="referee_themes",blank=True) |
1283 | + | |
1284 | + objects = PyrosUserManager() | |
1277 | 1285 | class Meta: |
1278 | 1286 | managed = True |
1279 | 1287 | db_table = 'pyros_user' |
... | ... | @@ -1397,7 +1405,6 @@ class PeriodManager(models.Manager): |
1397 | 1405 | submission_periods_id = [] |
1398 | 1406 | periods = Period.objects.filter(start_date__gte=today) |
1399 | 1407 | for period in periods: |
1400 | - print(period) | |
1401 | 1408 | if period.submission_start_date <= today and period.submission_end_date > today: |
1402 | 1409 | submission_periods_id.append(period.id) |
1403 | 1410 | periods = periods.filter(id__in=submission_periods_id) |
... | ... | @@ -1418,7 +1425,7 @@ class PeriodManager(models.Manager): |
1418 | 1425 | validation_periods_id = [] |
1419 | 1426 | periods = Period.objects.filter(start_date__gte=today) |
1420 | 1427 | for period in periods: |
1421 | - if period.unit_pi_validation_start_date >= today and period.notification_start_date < today: | |
1428 | + if period.unit_pi_validation_start_date <= today and period.notification_start_date > today: | |
1422 | 1429 | validation_periods_id.append(period.id) |
1423 | 1430 | periods = periods.filter(id__in=validation_periods_id) |
1424 | 1431 | return periods |
... | ... | @@ -1428,7 +1435,7 @@ class PeriodManager(models.Manager): |
1428 | 1435 | notification_periods_id = [] |
1429 | 1436 | periods = Period.objects.filter(start_date__gte=today) |
1430 | 1437 | for period in periods: |
1431 | - if period.notification_start_date >= today and period.start_date < today: | |
1438 | + if period.notification_start_date <= today and period.start_date > today: | |
1432 | 1439 | notification_periods_id.append(period.id) |
1433 | 1440 | periods = periods.filter(id__in=notification_periods_id) |
1434 | 1441 | return periods |
... | ... | @@ -1453,6 +1460,12 @@ class PeriodManager(models.Manager): |
1453 | 1460 | today = timezone.now().date() |
1454 | 1461 | previous_periods = Period.objects.filter(start_date__lt=today).order_by("-start_date").exclude(id=self.exploitation_period().id) |
1455 | 1462 | return previous_periods |
1463 | + | |
1464 | + def next_period(self)->any: | |
1465 | + current_period = self.exploitation_period() | |
1466 | + next_period = Period.objects.filter(start_date__gt=current_period.start_date).order_by("start_date").first() | |
1467 | + return next_period | |
1468 | + | |
1456 | 1469 | |
1457 | 1470 | class Period(models.Model): |
1458 | 1471 | # if change of default value, those values need to be changed also in create_period and edit_period.html (Javascript ) |
... | ... | @@ -1467,7 +1480,7 @@ class Period(models.Model): |
1467 | 1480 | start_date = models.DateField(blank=True, null=True, default=timezone.now,editable=True) |
1468 | 1481 | |
1469 | 1482 | exploitation_duration = models.PositiveIntegerField(blank=True, null=True, default=182,editable=True) |
1470 | - submission_duration = models.PositiveIntegerField(blank=True, null=True, default=182,editable=True) | |
1483 | + submission_duration = models.PositiveIntegerField(blank=True, null=True, default=136,editable=True) | |
1471 | 1484 | evaluation_duration = models.PositiveIntegerField(blank=True,null=True,default=31,editable=True) |
1472 | 1485 | validation_duration = models.PositiveIntegerField(blank=True, null=True, default=5,editable=True) |
1473 | 1486 | notification_duration = models.PositiveIntegerField(blank=True, null=True, default=10,editable=True) |
... | ... | @@ -1480,19 +1493,19 @@ class Period(models.Model): |
1480 | 1493 | |
1481 | 1494 | @property |
1482 | 1495 | def submission_start_date(self): |
1483 | - return self.start_date + relativedelta(days=-self.exploitation_duration) | |
1496 | + return self.start_date + relativedelta(days=-(self.submission_duration+self.evaluation_duration+self.validation_duration+self.notification_duration)) | |
1484 | 1497 | |
1485 | 1498 | @property |
1486 | 1499 | def submission_end_date(self): |
1487 | - return self.start_date + relativedelta(days=-self.evaluation_duration) | |
1500 | + return self.submission_start_date + relativedelta(days=self.submission_duration) | |
1488 | 1501 | |
1489 | 1502 | @property |
1490 | 1503 | def unit_pi_validation_start_date(self): |
1491 | - return self.start_date + relativedelta(days=-self.validation_duration) | |
1504 | + return self.submission_end_date + relativedelta(days=self.evaluation_duration) | |
1492 | 1505 | |
1493 | 1506 | @property |
1494 | 1507 | def notification_start_date(self): |
1495 | - return self.start_date + relativedelta(days=-self.notification_duration) | |
1508 | + return self.unit_pi_validation_start_date + relativedelta(days=self.validation_duration) | |
1496 | 1509 | |
1497 | 1510 | @property |
1498 | 1511 | def property_of_data_end_date(self): |
... | ... | @@ -1616,6 +1629,22 @@ class SP_Period_User(models.Model): |
1616 | 1629 | class SP_Period_Guest(models.Model): |
1617 | 1630 | SP_Period = models.ForeignKey(SP_Period, on_delete=models.DO_NOTHING,related_name="SP_Period_Guests") |
1618 | 1631 | email = models.EmailField(max_length=254) |
1632 | + | |
1633 | +class SP_PeriodWorkflow(models.Model): | |
1634 | + SUBMISSION = "SUB" | |
1635 | + EVALUATION = "EVAL" | |
1636 | + VALIDATION = "VALI" | |
1637 | + NOTIFICATION = "NOTI" | |
1638 | + ACTIONS_CHOICES = ( | |
1639 | + (SUBMISSION,"Submission"), | |
1640 | + (EVALUATION,"Evaluation"), | |
1641 | + (VALIDATION,"Validation"), | |
1642 | + (NOTIFICATION,"Notification") | |
1643 | + ) | |
1644 | + action = models.CharField(max_length=120,choices=ACTIONS_CHOICES) | |
1645 | + period = models.ForeignKey("Period",on_delete=models.DO_NOTHING,related_name="SP_Period_Workflows") | |
1646 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
1647 | + | |
1619 | 1648 | class Sequence(models.Model): |
1620 | 1649 | |
1621 | 1650 | """ Definition of Status enum values """ | ... | ... |
src/core/pyros_django/misc/fixtures/tests/scientific_program_TZ.json
... | ... | @@ -126,7 +126,7 @@ |
126 | 126 | "username": "observer", |
127 | 127 | "first_name": "", |
128 | 128 | "last_name": "", |
129 | - "email": "", | |
129 | + "email": "observer@test.com", | |
130 | 130 | "is_staff": true, |
131 | 131 | "is_active": true, |
132 | 132 | "date_joined": "2016-05-02T14:02:36.495Z", |
... | ... | @@ -153,7 +153,7 @@ |
153 | 153 | "username": "tac", |
154 | 154 | "first_name": "", |
155 | 155 | "last_name": "", |
156 | - "email": "", | |
156 | + "email": "tac@test.com", | |
157 | 157 | "is_staff": true, |
158 | 158 | "is_active": true, |
159 | 159 | "date_joined": "2016-05-02T14:02:36.495Z", |
... | ... | @@ -167,7 +167,8 @@ |
167 | 167 | "tel": "", |
168 | 168 | "address": "", |
169 | 169 | "laboratory": "", |
170 | - "institute": 1 | |
170 | + "institute": 1, | |
171 | + "referee_themes":[1] | |
171 | 172 | } |
172 | 173 | }, |
173 | 174 | { |
... | ... | @@ -180,7 +181,7 @@ |
180 | 181 | "username": "unit_pi", |
181 | 182 | "first_name": "", |
182 | 183 | "last_name": "", |
183 | - "email": "", | |
184 | + "email": "unit_pi@test.com", | |
184 | 185 | "is_staff": true, |
185 | 186 | "is_active": true, |
186 | 187 | "date_joined": "2016-05-02T14:02:36.495Z", |
... | ... | @@ -207,7 +208,7 @@ |
207 | 208 | "username": "operator", |
208 | 209 | "first_name": "", |
209 | 210 | "last_name": "", |
210 | - "email": "", | |
211 | + "email": "operator@test.com", | |
211 | 212 | "is_staff": true, |
212 | 213 | "is_active": true, |
213 | 214 | "date_joined": "2016-05-02T14:02:36.495Z", |
... | ... | @@ -234,7 +235,89 @@ |
234 | 235 | "username": "observer2", |
235 | 236 | "first_name": "", |
236 | 237 | "last_name": "", |
237 | - "email": "", | |
238 | + "email": "observer2@test.com", | |
239 | + "is_staff": true, | |
240 | + "is_active": true, | |
241 | + "date_joined": "2016-05-02T14:02:36.495Z", | |
242 | + "groups": [], | |
243 | + "user_permissions": [], | |
244 | + "country": 1, | |
245 | + "user_level": [2], | |
246 | + "desc": "", | |
247 | + "created": "2016-06-03T09:29:15.450Z", | |
248 | + "updated": "2016-06-03T09:29:15.450Z", | |
249 | + "tel": "", | |
250 | + "address": "", | |
251 | + "laboratory": "", | |
252 | + "institute": 1 | |
253 | + } | |
254 | +}, | |
255 | +{ | |
256 | + "model": "common.pyrosuser", | |
257 | + "pk": 7, | |
258 | + "fields": { | |
259 | + "password": "pbkdf2_sha256$24000$NC9UO4LPGvDN$+pkRcZUbmV3HEtbrhBdDCSPnAsDVValrbwLt7cXqrJE=", | |
260 | + "last_login": "2016-06-03T09:27:53.245Z", | |
261 | + "is_superuser": false, | |
262 | + "username": "tac2", | |
263 | + "first_name": "", | |
264 | + "last_name": "", | |
265 | + "email": "tac2@test.com", | |
266 | + "is_staff": true, | |
267 | + "is_active": true, | |
268 | + "date_joined": "2016-05-02T14:02:36.495Z", | |
269 | + "groups": [], | |
270 | + "user_permissions": [], | |
271 | + "country": 1, | |
272 | + "user_level": [3], | |
273 | + "desc": "", | |
274 | + "created": "2016-06-03T09:29:15.450Z", | |
275 | + "updated": "2016-06-03T09:29:15.450Z", | |
276 | + "tel": "", | |
277 | + "address": "", | |
278 | + "laboratory": "", | |
279 | + "institute": 1, | |
280 | + "referee_themes":[1] | |
281 | + } | |
282 | +}, | |
283 | +{ | |
284 | + "model": "common.pyrosuser", | |
285 | + "pk": 8, | |
286 | + "fields": { | |
287 | + "password": "pbkdf2_sha256$24000$NC9UO4LPGvDN$+pkRcZUbmV3HEtbrhBdDCSPnAsDVValrbwLt7cXqrJE=", | |
288 | + "last_login": "2016-06-03T09:27:53.245Z", | |
289 | + "is_superuser": false, | |
290 | + "username": "observer3", | |
291 | + "first_name": "", | |
292 | + "last_name": "", | |
293 | + "email": "observer3@test.com", | |
294 | + "is_staff": true, | |
295 | + "is_active": true, | |
296 | + "date_joined": "2016-05-02T14:02:36.495Z", | |
297 | + "groups": [], | |
298 | + "user_permissions": [], | |
299 | + "country": 1, | |
300 | + "user_level": [2], | |
301 | + "desc": "", | |
302 | + "created": "2016-06-03T09:29:15.450Z", | |
303 | + "updated": "2016-06-03T09:29:15.450Z", | |
304 | + "tel": "", | |
305 | + "address": "", | |
306 | + "laboratory": "", | |
307 | + "institute": 1 | |
308 | + } | |
309 | +}, | |
310 | +{ | |
311 | + "model": "common.pyrosuser", | |
312 | + "pk": 9, | |
313 | + "fields": { | |
314 | + "password": "pbkdf2_sha256$24000$NC9UO4LPGvDN$+pkRcZUbmV3HEtbrhBdDCSPnAsDVValrbwLt7cXqrJE=", | |
315 | + "last_login": "2016-06-03T09:27:53.245Z", | |
316 | + "is_superuser": false, | |
317 | + "username": "observer4", | |
318 | + "first_name": "", | |
319 | + "last_name": "", | |
320 | + "email": "observer4@test.com", | |
238 | 321 | "is_staff": true, |
239 | 322 | "is_active": true, |
240 | 323 | "date_joined": "2016-05-02T14:02:36.495Z", |
... | ... | @@ -250,5 +333,26 @@ |
250 | 333 | "laboratory": "", |
251 | 334 | "institute": 1 |
252 | 335 | } |
336 | +}, | |
337 | +{ | |
338 | + "model":"common.ScienceTheme", | |
339 | + "pk":1, | |
340 | + "fields":{ | |
341 | + "name":"Solar System" | |
342 | + } | |
343 | +}, | |
344 | +{ | |
345 | + "model":"common.ScienceTheme", | |
346 | + "pk":2, | |
347 | + "fields":{ | |
348 | + "name":"Galatic" | |
349 | + } | |
350 | +}, | |
351 | +{ | |
352 | + "model":"common.ScienceTheme", | |
353 | + "pk":3, | |
354 | + "fields":{ | |
355 | + "name":"Extra Galatic" | |
356 | + } | |
253 | 357 | } |
254 | 358 | ] |
255 | 359 | \ No newline at end of file | ... | ... |
src/core/pyros_django/obsconfig/tests.py
... | ... | @@ -39,7 +39,7 @@ class ObservatoryConfigurationTests(TestCase): |
39 | 39 | u1 = PyrosUser.objects.get(username="haribo") |
40 | 40 | u1.set_password("password123") |
41 | 41 | u1.save() |
42 | - self.client.login(username="haribo",password="password123") | |
42 | + self.client.post(reverse("login_validation"),{"email":"haribo","password":"password123"}) | |
43 | 43 | response = self.client.get(reverse('obs_astronomer_config')) |
44 | 44 | self.assertEqual(response.status_code, 200) |
45 | 45 | self.assertTemplateUsed(response, 'base.html') |
... | ... | @@ -57,7 +57,8 @@ class ObservatoryConfigurationTests(TestCase): |
57 | 57 | u1 = PyrosUser.objects.get(username="haribo") |
58 | 58 | u1.set_password("password123") |
59 | 59 | u1.save() |
60 | - self.client.login(username="haribo",password="password123") | |
60 | + #self.client.login(username="haribo",password="password123") | |
61 | + self.client.post(reverse("login_validation"),{"email":"haribo","password":"password123"}) | |
61 | 62 | response = self.client.get(reverse('obs_astronomer_config')) |
62 | 63 | self.assertEqual(response.status_code, 200) |
63 | 64 | self.assertTemplateUsed(response, 'base.html') | ... | ... |
src/core/pyros_django/pyros/settings.py
... | ... | @@ -399,7 +399,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' |
399 | 399 | |
400 | 400 | python_version = subprocess.run( "python --version | cut -d ' ' -f 2 | cut -d '.' -f 1,2",shell=True,stdout=subprocess.PIPE,universal_newlines=True) |
401 | 401 | python_version = python_version.stdout |
402 | -today = "2021-10-14" | |
402 | +today = "2021-11-02" | |
403 | 403 | django_version_major,django_version_minor = django.VERSION[:2][0],django.VERSION[:2][1] |
404 | -pyros_version = "0.2.10.0" | |
404 | +pyros_version = "0.2.11.0" | |
405 | 405 | VERSION_NUMBER = f"{pyros_version}_{django_version_major}.{django_version_minor}_{python_version}_{today}" |
406 | 406 | \ No newline at end of file | ... | ... |
src/core/pyros_django/scheduler/Scheduler.py
1 | 1 | from operator import attrgetter |
2 | 2 | |
3 | 3 | from scheduler.templatetags.jdconverter import jdtodate |
4 | -from .UserManager import UserManager | |
4 | +from .UserManager import UserQuotaManager | |
5 | 5 | from .Interval import * |
6 | 6 | from django.db.models import Q |
7 | 7 | |
... | ... | @@ -221,11 +221,11 @@ class Scheduler(IntervalManagement): |
221 | 221 | #print("sequences are", self.sequences) |
222 | 222 | for sequence, shs in list(self.sequences): |
223 | 223 | #print("placeSequences() sequence is", sequence) |
224 | - quota = UserManager.determineQuota(sequence) | |
224 | + quota = UserQuotaManager.determineQuota(sequence) | |
225 | 225 | #print("quota is", quota) |
226 | - if not UserManager.isSufficient(quota, sequence): | |
226 | + if not UserQuotaManager.isSufficient(quota, sequence): | |
227 | 227 | shs.status = Sequence.REJECTED |
228 | - shs.desc = UserManager.REJECTED | |
228 | + shs.desc = UserQuotaManager.REJECTED | |
229 | 229 | continue |
230 | 230 | matching_intervals = self.getMatchingIntervals(sequence) |
231 | 231 | if len(matching_intervals) > 0: |
... | ... | @@ -350,7 +350,7 @@ class Scheduler(IntervalManagement): |
350 | 350 | return 0 |
351 | 351 | |
352 | 352 | def decreaseQuota(self, sequence: Sequence, quota: float) -> int: |
353 | - user = UserManager(sequence.request.pyros_user) | |
353 | + user = UserQuotaManager(sequence.request.pyros_user) | |
354 | 354 | if SIMULATION: |
355 | 355 | return 0 |
356 | 356 | return user.decreaseQuota(Decimal(quota)) | ... | ... |
src/core/pyros_django/scheduler/UserManager.py
... | ... | @@ -0,0 +1,222 @@ |
1 | +import sys | |
2 | + | |
3 | +sys.path.append("..") | |
4 | +sys.path.append("../../../..") | |
5 | +from src.core.pyros_django.agent.Agent import Agent, build_agent | |
6 | +from common.models import Period, SP_Period, PyrosUser, SP_Period_Guest, SP_PeriodWorkflow, ScientificProgram,SP_Period_User, ScienceTheme | |
7 | +from django.shortcuts import reverse | |
8 | +from django.conf import settings | |
9 | +from django.core.mail import send_mail | |
10 | +from dateutil.relativedelta import relativedelta | |
11 | +from django.db.models import Q | |
12 | +from django.utils import timezone | |
13 | +from django.test.utils import setup_test_environment | |
14 | +import numpy as np | |
15 | + | |
16 | +class AgentSP(Agent): | |
17 | + | |
18 | + period = None | |
19 | + | |
20 | + def __init__(self, config_filename=None, RUN_IN_THREAD=True,use_db_test=False): | |
21 | + ##if name is None: name = self.__class__.__name__ | |
22 | + if use_db_test: | |
23 | + print("USE DB TEST") | |
24 | + setup_test_environment() | |
25 | + super().__init__(None, RUN_IN_THREAD) | |
26 | + print(Period.objects.all()) | |
27 | + next_period = Period.objects.next_period() | |
28 | + period = next_period | |
29 | + | |
30 | + # @override | |
31 | + def init(self): | |
32 | + super().init() | |
33 | + | |
34 | + def associate_tac_sp_auto(self,themes,tac_users,scientific_programs): | |
35 | + print("Associating tac to sp") | |
36 | + matrix_tac_themes = np.zeros([len(tac_users),len(themes)]) | |
37 | + maxtrix_themes_sp = np.zeros([len(themes),len(scientific_programs)]) | |
38 | + matrix_tac_sp = np.zeros([len(tac_users),len(scientific_programs)]) | |
39 | + for i,tac_user in enumerate(tac_users): | |
40 | + for j,theme in enumerate(themes): | |
41 | + if theme.name in tac_user.get_referee_themes_as_str(): | |
42 | + matrix_tac_themes[i,j] = 1 | |
43 | + for i,theme in enumerate(themes): | |
44 | + for j,sp in enumerate(scientific_programs): | |
45 | + if theme.id == sp.science_theme.id: | |
46 | + maxtrix_themes_sp[i,j] = 1 | |
47 | + matrix_tac_sp = np.dot(matrix_tac_themes,maxtrix_themes_sp) | |
48 | + nb_tac_per_sp = np.sum(matrix_tac_sp,axis=0) | |
49 | + next_period = Period.objects.next_period() | |
50 | + for i,sp in enumerate(scientific_programs): | |
51 | + if nb_tac_per_sp[i-1] == 2: | |
52 | + # We auto assign the tac users to scientific programs | |
53 | + print(sp) | |
54 | + sp_period = SP_Period.objects.get(scientific_program=sp,period=next_period) | |
55 | + available_tac_users = PyrosUser.objects.filter(referee_themes=sp.science_theme) | |
56 | + print("available tacs :") | |
57 | + print(available_tac_users) | |
58 | + sp_period.referee1 = available_tac_users[0] | |
59 | + sp_period.referee2 = available_tac_users[1] | |
60 | + sp_period.save() | |
61 | + #return matrix_tac_sp | |
62 | + | |
63 | + def change_sp_status(self,scientific_programs,new_status): | |
64 | + print(f"---- CHANGE STATUS FOR {scientific_programs} TO {new_status}------- ") | |
65 | + for sp in scientific_programs: | |
66 | + if sp.status != new_status: | |
67 | + sp.status = new_status | |
68 | + sp.save() | |
69 | + | |
70 | + def send_mail_to_tac_for_evaluation(self,tac_users,next_period): | |
71 | + domain = settings.DEFAULT_DOMAIN | |
72 | + url = f"{domain}{reverse('list_submitted_scientific_program')}" | |
73 | + mail_subject = '[PyROS CC] The evaluation period is now opened' | |
74 | + mail_message = (f"Hi,\n\nYou can now evaluate scientific programs for the next period ({next_period}).\n" | |
75 | + f"Click on the following link {url} to evaluate your assignated scientific programs." | |
76 | + "\n\nCordially,\n\nPyROS Control Center") | |
77 | + email_list = tac_users.values_list("email") | |
78 | + for email in email_list: | |
79 | + send_mail( | |
80 | + mail_subject, | |
81 | + mail_message, | |
82 | + from_email=None, | |
83 | + recipient_list=[email], | |
84 | + fail_silently=False, | |
85 | + ) | |
86 | + | |
87 | + def send_mail_to_observers_for_notification(self,sp_periods): | |
88 | + for sp_period in sp_periods: | |
89 | + sp_pi = sp_period.scientific_program.sp_pi | |
90 | + scientific_program = sp_period.scientific_program | |
91 | + domain = settings.DEFAULT_DOMAIN | |
92 | + url = f"{domain}{reverse('sp_register',args=(scientific_program.pk,sp_period.period.pk))}" | |
93 | + mail_subject = '[PyROS CC] New registration to a scientific program' | |
94 | + mail_message = (f"Hi,\n\nYou were invited to join a scientific program that as been submitted using PyROS.\n" | |
95 | + f"The name of the scientific program is {scientific_program.name} and his PI is {sp_pi.first_name} {sp_pi.last_name}.\n" | |
96 | + f"To accept this invitation, click on the following link : {url}\n" | |
97 | + f"Once you have joined the scientific program, you can start to submit sequences" | |
98 | + "You might be asked to login first and will be redirected to the scientific program page.\n" | |
99 | + "If the redirection doesn't work, click again on the link after you've logged in.\n" | |
100 | + "If you don't own an PyROS account, go on the website in order to create an account with the same mail adress that you are using to read this mail." | |
101 | + "\n\nCordially,\n\nPyROS Control Center") | |
102 | + invited_observers_of_sp = SP_Period_Guest.objects.filter(SP_Period=sp_period).values("user") | |
103 | + recipient_list = invited_observers_of_sp | |
104 | + for invited_observer in recipient_list: | |
105 | + send_mail( | |
106 | + mail_subject, | |
107 | + mail_message, | |
108 | + from_email=None, | |
109 | + recipient_list=[invited_observer], | |
110 | + fail_silently=False, | |
111 | + ) | |
112 | + | |
113 | + def send_mail_to_unit_users_for_tac_assignation(self): | |
114 | + domain = settings.DEFAULT_DOMAIN | |
115 | + url = f"{domain}{reverse('list_drafted_scientific_program')}" | |
116 | + mail_subject = '[PyROS CC] TAC assignation to scientific programs for the next period' | |
117 | + mail_message = (f"Hi,\n\nYou can assign TAC users to scientific programs by choosing them in the {url} page.\n" | |
118 | + "PyROS has suggested TAC to some of the scientific programs but you can change those assignations.\n" | |
119 | + f"The TAC assignation will be effective and couldn't be modified at the {self.period.submission_end_date}.\n" | |
120 | + "\n\nCordially,\n\nPyROS Control Center") | |
121 | + unit_users = PyrosUser.objects.unit_users().values_list("email",flat=True) | |
122 | + send_mail( | |
123 | + mail_subject, | |
124 | + mail_message, | |
125 | + from_email=None, | |
126 | + recipient_list=unit_users, | |
127 | + fail_silently=False, | |
128 | + ) | |
129 | + print("--------- SEND MAIL TO UNIT USERS ----------") | |
130 | + | |
131 | + | |
132 | + def automatic_period_workflow(self): | |
133 | + today = timezone.now().date() | |
134 | + next_period = Period.objects.next_period() | |
135 | + # check if next_period has changed | |
136 | + if self.period != next_period: | |
137 | + self.period = next_period | |
138 | + # get scientific program for next_period | |
139 | + next_sp = SP_Period.objects.filter(period=next_period) | |
140 | + auto_validated_sp = ScientificProgram.objects.filter(is_auto_validated=True) | |
141 | + auto_validated_sp_periods = SP_Period.objects.filter(scientific_program__in=auto_validated_sp,period=next_period) | |
142 | + # remove auto validated sp from next_sp | |
143 | + next_sp = next_sp.exclude(scientific_program__in=auto_validated_sp) | |
144 | + # get all tac users | |
145 | + tac_users = PyrosUser.objects.filter(user_level__name="TAC") | |
146 | + # submission workflow | |
147 | + if not SP_PeriodWorkflow.objects.filter(action=SP_PeriodWorkflow.SUBMISSION, period=self.period).exists(): | |
148 | + print("routine automatic period workflow SUBMISSION") | |
149 | + # if the next_period is actually in the "submission" subperiod | |
150 | + if next_period in Period.objects.submission_periods(): | |
151 | + # we have to assign TAC to SP | |
152 | + themes = ScienceTheme.objects.all() | |
153 | + # get id of scientific programs from SP_Period | |
154 | + sp_id = next_sp.exclude(scientific_program__is_auto_validated=True).filter(Q(referee1=None)|Q(referee2=None)).values("scientific_program") | |
155 | + # get scientific programs | |
156 | + sp = ScientificProgram.objects.filter(id__in=sp_id).order_by("name") | |
157 | + # if we are ten days before the end of the submission period, we have to assign TAC to scientific programs | |
158 | + # and send a mail to the Unit users to they assign the TAC users to SP | |
159 | + if next_period.submission_end_date + relativedelta(days=-10) == today : | |
160 | + self.associate_tac_sp_auto(themes,tac_users,sp) | |
161 | + # send mail to unit pi to tell him to associate TAC to SP | |
162 | + self.send_mail_to_unit_users_for_tac_assignation() | |
163 | + SP_PeriodWorkflow.objects.create(period=self.period,action=SP_PeriodWorkflow.SUBMISSION) | |
164 | + | |
165 | + if not SP_PeriodWorkflow.objects.filter(action=SP_PeriodWorkflow.EVALUATION, period=self.period).exists(): | |
166 | + print("routine automatic period workflow EVALUATION") | |
167 | + if next_period in Period.objects.evaluation_periods() and next_period.submission_end_date == today : | |
168 | + next_sp = SP_Period.objects.filter(period=next_period).exclude(scientific_program__in=auto_validated_sp).filter(status=SP_Period.STATUSES_DRAFT) | |
169 | + self.change_sp_status(next_sp,SP_Period.STATUSES_SUBMITTED) | |
170 | + self.send_mail_to_tac_for_evaluation(tac_users,next_period) | |
171 | + | |
172 | + # for auto validated sp, we have to change their status | |
173 | + self.change_sp_status(auto_validated_sp_periods,SP_Period.STATUSES_ACCEPTED) | |
174 | + for sp in auto_validated_sp_periods: | |
175 | + sp.is_valid = SP_Period.IS_VALID_ACCEPTED | |
176 | + sp.save() | |
177 | + | |
178 | + SP_PeriodWorkflow.objects.create(period=self.period,action=SP_PeriodWorkflow.EVALUATION) | |
179 | + if not SP_PeriodWorkflow.objects.filter(action=SP_PeriodWorkflow.VALIDATION, period=self.period).exists(): | |
180 | + print("routine automatic period workflow VALIDATION") | |
181 | + if next_period.unit_pi_validation_start_date == today : | |
182 | + next_sp = SP_Period.objects.filter(period=next_period).exclude(scientific_program__in=auto_validated_sp).filter(status=SP_Period.STATUSES_SUBMITTED) | |
183 | + self.change_sp_status(next_sp,SP_Period.STATUSES_EVALUATED) | |
184 | + next_sp = SP_Period.objects.filter(period=next_period).exclude(scientific_program__in=auto_validated_sp).filter(status=SP_Period.STATUSES_EVALUATED) | |
185 | + SP_PeriodWorkflow.objects.create(period=self.period,action=SP_PeriodWorkflow.VALIDATION) | |
186 | + if not SP_PeriodWorkflow.objects.filter(action=SP_PeriodWorkflow.NOTIFICATION, period=self.period).exists(): | |
187 | + print("routine automatic period workflow NOTIFICATION") | |
188 | + if next_period in Period.objects.notification_periods(): | |
189 | + next_sp_accepted = SP_Period.objects.filter(period=next_period).filter(is_valid=SP_Period.IS_VALID_ACCEPTED) | |
190 | + self.change_sp_status(next_sp_accepted,SP_Period.STATUSES_ACCEPTED) | |
191 | + next_sp_rejected = SP_Period.objects.filter(period=next_period).filter(is_valid=SP_Period.IS_VALID_REJECTED) | |
192 | + self.change_sp_status(next_sp_rejected,SP_Period.STATUSES_REJECTED) | |
193 | + next_sp_to_be_notified = next_sp.filter(status=SP_Period.STATUSES_ACCEPTED,is_valid = True) | |
194 | + self.send_mail_to_observers_for_notification(next_sp_to_be_notified) | |
195 | + SP_PeriodWorkflow.objects.create(period=self.period,action=SP_PeriodWorkflow.NOTIFICATION) | |
196 | + | |
197 | + def routine_process_body(self): | |
198 | + print("routine automatic period workflow") | |
199 | + print(SP_PeriodWorkflow.objects.all()) | |
200 | + for sp_period_workflow in SP_PeriodWorkflow.objects.all(): | |
201 | + print(sp_period_workflow.period) | |
202 | + print(sp_period_workflow.action) | |
203 | + self.automatic_period_workflow() | |
204 | + | |
205 | + | |
206 | +if __name__ == "__main__": | |
207 | + | |
208 | + # with thread | |
209 | + RUN_IN_THREAD=True | |
210 | + # with process | |
211 | + #RUN_IN_THREAD=False | |
212 | + if len(sys.argv) > 1 and sys.argv[1] == "test": | |
213 | + agent = AgentSP(None,RUN_IN_THREAD=RUN_IN_THREAD,use_db_test=True) | |
214 | + else: | |
215 | + agent = build_agent(AgentSP, RUN_IN_THREAD=RUN_IN_THREAD) | |
216 | + ''' | |
217 | + TEST_MODE, configfile = extract_parameters() | |
218 | + agent = AgentM("AgentM", configfile, RUN_IN_THREAD) | |
219 | + agent.setSimulatorMode(TEST_MODE) | |
220 | + ''' | |
221 | + print(agent) | |
222 | + agent.run() | ... | ... |
src/core/pyros_django/scientific_program/functions.py
src/core/pyros_django/scientific_program/templates/scientific_program/create_period.html
... | ... | @@ -145,19 +145,24 @@ function loadDate(){ |
145 | 145 | // set date for each duration |
146 | 146 | |
147 | 147 | duration_value = parseInt($("#id_submission_duration").val()) |
148 | - date = period_start_date.addDays(-duration_value); | |
148 | + proposal_duration = parseInt($("#id_submission_duration").val()) + parseInt($("#id_evaluation_duration").val()) | |
149 | + + parseInt($("#id_validation_duration").val()) + parseInt($("#id_notification_duration").val()); | |
150 | + date = period_start_date.addDays(-proposal_duration); | |
151 | + submission_start_date = date; | |
149 | 152 | $("#submission_duration_date").html(date.toLocaleDateString("en-GB")); |
150 | 153 | |
151 | 154 | duration_value = parseInt($("#id_evaluation_duration").val()) |
152 | - date = period_start_date.addDays(-duration_value); | |
155 | + date = submission_start_date.addDays(duration_value); | |
156 | + evaluation_start_date = date; | |
153 | 157 | $("#evaluation_duration_date").html(date.toLocaleDateString("en-GB")); |
154 | 158 | |
155 | 159 | duration_value = parseInt($("#id_validation_duration").val()) |
156 | - date = period_start_date.addDays(-duration_value); | |
160 | + date = evaluation_start_date.addDays(duration_value); | |
161 | + validation_start_date = date; | |
157 | 162 | $("#validation_duration_date").html(date.toLocaleDateString("en-GB")); |
158 | 163 | |
159 | 164 | duration_value = parseInt($("#id_notification_duration").val()) |
160 | - date = period_start_date.addDays(-duration_value); | |
165 | + date = validation_start_date.addDays(duration_value); | |
161 | 166 | $("#notification_duration_date").html(date.toLocaleDateString("en-GB")); |
162 | 167 | |
163 | 168 | duration_value = parseInt($("#id_exploitation_duration").val()) |
... | ... | @@ -180,52 +185,32 @@ $( document ).ready(function() { |
180 | 185 | |
181 | 186 | // IMPORTANT NOTE : In Javascript Month count start to zero (Zero is january, 1 is february etc) |
182 | 187 | $("#id_exploitation_duration").on("input",function(){ |
183 | - duration_value = parseInt($("#id_exploitation_duration").val()) | |
184 | - split = $("#start_date_picker").val().split("/"); | |
185 | - period_start_date = new Date(split[2],split[1]-1,split[0]); | |
186 | - date = period_start_date.addDays(duration_value); | |
187 | - $("#exploitation_duration_date").html(date.toLocaleDateString("en-GB")); | |
188 | + loadDate(); | |
188 | 189 | }); |
189 | 190 | |
190 | 191 | $("#id_submission_duration").on("input",function(){ |
191 | - duration_value = parseInt($("#id_submission_duration").val()) | |
192 | - split = $("#start_date_picker").val().split("/"); | |
193 | - period_start_date = new Date(split[2],split[1]-1,split[0]); | |
194 | - date = period_start_date.addDays(duration_value); | |
195 | - $("#submission_duration_date").html(date.toLocaleDateString("en-GB")); | |
192 | + loadDate(); | |
196 | 193 | }); |
197 | 194 | |
198 | 195 | $("#id_evaluation_duration").on("input",function(){ |
199 | - duration_value = parseInt($("#id_evaluation_duration").val()) | |
200 | - split = $("#start_date_picker").val().split("/"); | |
201 | - period_start_date = new Date(split[2],split[1]-1,split[0]); | |
202 | - date = period_start_date.addDays(duration_value); | |
203 | - $("#evaluation_duration_date").html(date.toLocaleDateString("en-GB")); | |
196 | + loadDate(); | |
204 | 197 | }); |
205 | 198 | |
206 | -$("#id_notation_duration").on("input",function(){ | |
207 | - duration_value = parseInt($("#id_notation_duration").val()) | |
208 | - split = $("#start_date_picker").val().split("/"); | |
209 | - period_start_date = new Date(split[2],split[1]-1,split[0]); | |
210 | - date = period_start_date.addDays(duration_value); | |
211 | - $("#notation_duration_date").html(date.toLocaleDateString("en-GB")); | |
199 | +$("#id_validation_duration").on("input",function(){ | |
200 | + loadDate(); | |
201 | +}); | |
202 | + | |
203 | +$("#id_notification_duration").on("input",function(){ | |
204 | + loadDate(); | |
212 | 205 | }); |
213 | 206 | |
214 | 207 | |
215 | 208 | $("#id_property_of_data_duration").on("input",function(){ |
216 | - duration_value = parseInt($("#id_property_of_data_duration").val()) | |
217 | - split = $("#start_date_picker").val().split("/"); | |
218 | - period_start_date = new Date(split[2],split[1]-1,split[0]); | |
219 | - date = period_start_date.addDays(duration_value); | |
220 | - $("#property_of_data_duration_date").html(date.toLocaleDateString("en-GB")); | |
209 | + loadDate(); | |
221 | 210 | }); |
222 | 211 | |
223 | 212 | $("#id_data_accessibility_duration").on("input",function(){ |
224 | - duration_value = parseInt($("#id_data_accessibility_duration").val()) | |
225 | - split = $("#start_date_picker").val().split("/"); | |
226 | - period_start_date = new Date(split[2],split[1]-1,split[0]); | |
227 | - date = period_start_date.addDays(duration_value); | |
228 | - $("#data_accessibility_duration_date").html(date.toLocaleDateString("en-GB")); | |
213 | + loadDate(); | |
229 | 214 | }); |
230 | 215 | |
231 | 216 | $(function() { | ... | ... |
src/core/pyros_django/scientific_program/templates/scientific_program/period_detail.html
... | ... | @@ -18,6 +18,7 @@ |
18 | 18 | <p><strong>Submission start date : </strong>{{ period.submission_start_date }}</p> |
19 | 19 | <p><strong>Submission end date : </strong>{{ period.submission_end_date }}</p> |
20 | 20 | <p><strong>Unit-PI validation start date : </strong>{{ period.unit_pi_validation_start_date}}</p> |
21 | + <p><strong>Notification start date : </strong>{{ period.notification_start_date}}</p> | |
21 | 22 | <p><strong>Property of date end date : </strong>{{ period.property_of_data_end_date }}</p> |
22 | 23 | <p><strong>Data accessibility end date : </strong>{{ period.data_accessibility_end_date }}</p> |
23 | 24 | ... | ... |
src/core/pyros_django/scientific_program/templates/scientific_program/scientific_program_detail.html
... | ... | @@ -46,7 +46,7 @@ |
46 | 46 | <form class="modal-content" action="{% url "delete_scientific_program" scientific_program.id %}" method="post"> |
47 | 47 | {% csrf_token %} |
48 | 48 | <div class="container"> |
49 | - <h1> Delete Account </h1> | |
49 | + <h1> Delete scientific program </h1> | |
50 | 50 | <p>Are you sure you want to delete this scientific program ?</p> |
51 | 51 | |
52 | 52 | <div class="clearfix"> | ... | ... |
src/core/pyros_django/scientific_program/templates/scientific_program/scientific_program_detail_edit.html renamed to src/core/pyros_django/scientific_program/templates/scientific_program/scientific_program_period_detail_edit.html
1 | -{% extends 'base.html' %} | |
2 | -{% load tags %} | |
3 | -{% block content %} | |
4 | -<form id="editSPForm" action="" method="post"> | |
5 | - {% csrf_token %} | |
6 | - | |
7 | - {% for field in SPForm %} | |
8 | - <div class="fieldWrapper"> | |
9 | - {{ field.label_tag }} {{ field.value }} | |
10 | - {{ field.as_hidden }} | |
11 | - </div> | |
12 | - {% endfor %} | |
13 | - {% if sp_period.status == "Draft" %} | |
14 | - <p style="color:red;"><strong>This proposal will be automatically submitted for evaluation on {{ sp_period.period.submission_end_date }}</strong> </p> | |
15 | - {% endif %} | |
16 | - {% if USER_LEVEL|ifinlist:"Unit-PI,Admin,Unit-board"%} | |
17 | - {% if sp_period.referee1 != None and sp_period.referee1 == request.user %} | |
18 | - <p><strong>vote referee 1: </strong>{{ sp_period.vote_referee1 }} {{ sp_period.reason_referee1 }} by {{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p> | |
19 | - {% endif %} | |
20 | - {% if sp_period.referee2 != None %} | |
21 | - <p><strong>vote referee 2: </strong>{{ sp_period.vote_referee1 }} {{ sp_period.reason_referee1 }} by {{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p> | |
22 | - {% endif %} | |
23 | - <p><strong>Minimal quota : </strong>{{ sp_period.quota_minimal }}</p> | |
24 | - <p><strong>Nominal quota : </strong>{{ sp_period.quota_nominal }}</p> | |
25 | - <p><strong>Over quota duration : </strong>{{ sp_period.over_quota_duration }}</p> | |
26 | - <p><strong>Token : </strong>{{ sp_period.token }}</p> | |
27 | - {% endif %} | |
28 | - {% for field in SP_PeriodForm %} | |
29 | - {% if "referee1" in field.label_tag %} | |
30 | - {% if SP_PeriodForm.reason_referee1.value == "" %} | |
31 | - | |
32 | - {% if sp_period.status == "Submitted" and USER_LEVEL|ifinlist:"TAC,Admin"%} | |
33 | - <div class="fieldWrapper"> | |
34 | - {{ field.label_tag }} {{ field }} | |
35 | - {{ field.errors }} | |
36 | - {% if field.help_text %} | |
37 | - <p class="help">{{ field.help_text|safe }}</p> | |
38 | - {% endif %} | |
39 | - </div> | |
40 | - {% endif %} | |
41 | - | |
42 | - {% else %} | |
43 | - {% if sp_period.referee1 == request.user %} | |
44 | - {% if sp_period.status == "Submitted" and USER_LEVEL|ifinlist:"TAC,Admin"%} | |
45 | - <div class="fieldWrapper"> | |
46 | - {{ field.label_tag }} {{ field }} | |
47 | - {{ field.errors }} | |
48 | - {% if field.help_text %} | |
49 | - <p class="help">{{ field.help_text|safe }}</p> | |
50 | - {% endif %} | |
51 | - </div> | |
52 | - {% endif %} | |
53 | - {% else %} | |
54 | - {{ field.as_hidden}} | |
55 | - {% endif %} | |
56 | - {% endif %} | |
57 | - {% elif "referee2" in field.label_tag %} | |
58 | - {% if SP_PeriodForm.reason_referee1.value != "" and SP_PeriodForm.reason_referee2.value == "" and request.user != sp_period.referee1 %} | |
59 | - {% if sp_period.status == "Submitted" and USER_LEVEL|ifinlist:"TAC,Admin"%} | |
60 | - <div class="fieldWrapper"> | |
61 | - {{ field.label_tag }} {{ field }} | |
62 | - {{ field.errors }} | |
63 | - {% if field.help_text %} | |
64 | - <p class="help">{{ field.help_text|safe }}</p> | |
65 | - {% endif %} | |
66 | - </div> | |
67 | - {% endif %} | |
68 | - {% else %} | |
69 | - {{ field.as_hidden}} | |
70 | - {% endif %} | |
71 | - {% elif "is_valid" in field.label_tag or "allocated" in field.label_tag or "priority" in field.label_tag %} | |
72 | - {% if sp_period.status != "Draft" and USER_LEVEL|ifinlist:"Unit-PI,Admin" %} | |
73 | - <div class="fieldWrapper"> | |
74 | - {{ field.label_tag }} {{ field }} | |
75 | - {{ field.errors }} | |
76 | - {% if field.help_text %} | |
77 | - <p class="help">{{ field.help_text|safe }}</p> | |
78 | - {% endif %} | |
79 | - </div> | |
80 | - {% endif %} | |
81 | - | |
82 | - {% elif sp_period.status == "Draft" and USER_LEVEL|ifinlist:"Observer" %} | |
83 | - <div class="fieldWrapper"> | |
84 | - {{ field.label_tag }} {{ field }} | |
85 | - {{ field.errors }} | |
86 | - {% if field.help_text %} | |
87 | - <p class="help">{{ field.help_text|safe }}</p> | |
88 | - {% endif %} | |
89 | - </div> | |
90 | - {% else %} | |
91 | - {{ field.as_hidden}} | |
92 | - {% endif %} | |
93 | - {% endfor %} | |
94 | - {% if sp_period.status == "Draft" and USER_LEVEL|ifinlist:"Observer" %} | |
95 | - <label> Invite user(s) with their email adress: (list them separated by a ","):</label> | |
96 | - <div class="fieldWrapper"> | |
97 | - <textarea id="users" name="users">{{guests|default_if_none:""}} </textarea> | |
98 | - {{ error_message }} | |
99 | - </div> | |
100 | - {% endif %} | |
101 | - {% if USER_LEVEL|ifinlist:"Admin,Observer" %} | |
102 | - <p> <b> Users : </b> | |
103 | - {% for user in users_of_sp_period %} | |
104 | - {% comment %} | |
105 | - input name is the same for all role so we can retrieve a list of selected values | |
106 | - {% endcomment %} | |
107 | - <div> | |
108 | - {% if sp_period.status == "Draft" and USER_LEVEL|ifinlist:"Observer" %} | |
109 | - <input type="checkbox" checked id="{{user.username}}" name="user" value="{{user.id}}"> | |
110 | - {% endif %} | |
111 | - <label for="{{user.username}}"> {{ user.first_name }} {{ user.last_name }} ({{user.username}})</label> | |
112 | - </div> | |
113 | - {% endfor %} | |
114 | - {% endif %} | |
115 | - <div> | |
116 | - <input id="submit" class="btn btn-info" type="submit" value="Submit" /> | |
117 | - <a href="{% url "detail_scientific_program_period" id_sp=id_sp id_period=id_period %}" class="btn btn-info" role="button">Cancel</a> | |
118 | - </div> | |
119 | -</form> | |
120 | - | |
1 | +{% extends 'base.html' %} | |
2 | +{% load tags %} | |
3 | +{% block content %} | |
4 | +<form id="editSPForm" action="" method="post"> | |
5 | + {% csrf_token %} | |
6 | + | |
7 | + {% for field in SPForm %} | |
8 | + <div class="fieldWrapper"> | |
9 | + {{ field.label_tag }} {{ field.value }} | |
10 | + {{ field.as_hidden }} | |
11 | + </div> | |
12 | + {% endfor %} | |
13 | + {% if sp_period.status == "Draft" %} | |
14 | + <p style="color:red;"><strong>This proposal will be automatically submitted for evaluation on {{ sp_period.period.submission_end_date }}</strong> </p> | |
15 | + {% endif %} | |
16 | + {% if CAN_VIEW_TAC_VOTES %} | |
17 | + {% if sp_period.referee1 != None and sp_period.referee1 == request.user %} | |
18 | + <p><strong>vote referee 1: </strong>{{ sp_period.vote_referee1 }} {{ sp_period.reason_referee1 }} by {{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p> | |
19 | + {% endif %} | |
20 | + {% if sp_period.referee2 != None %} | |
21 | + <p><strong>vote referee 2: </strong>{{ sp_period.vote_referee1 }} {{ sp_period.reason_referee1 }} by {{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p> | |
22 | + {% endif %} | |
23 | + <p><strong>Minimal quota : </strong>{{ sp_period.quota_minimal }}</p> | |
24 | + <p><strong>Nominal quota : </strong>{{ sp_period.quota_nominal }}</p> | |
25 | + <p><strong>Over quota duration : </strong>{{ sp_period.over_quota_duration }}</p> | |
26 | + <p><strong>Token : </strong>{{ sp_period.token }}</p> | |
27 | + {% endif %} | |
28 | + {% for field in SP_PeriodForm %} | |
29 | + {% if "referee1" in field.label_tag %} | |
30 | + {% if SP_PeriodForm.reason_referee1.value == "" %} | |
31 | + | |
32 | + {% if sp_period.status == "Submitted" and USER_LEVEL|ifinlist:"TAC,Admin"%} | |
33 | + <div class="fieldWrapper"> | |
34 | + {{ field.label_tag }} {{ field }} | |
35 | + {{ field.errors }} | |
36 | + {% if field.help_text %} | |
37 | + <p class="help">{{ field.help_text|safe }}</p> | |
38 | + {% endif %} | |
39 | + </div> | |
40 | + {% endif %} | |
41 | + | |
42 | + {% else %} | |
43 | + {% if sp_period.referee1 == request.user %} | |
44 | + {% if sp_period.status == "Submitted" and USER_LEVEL|ifinlist:"TAC,Admin"%} | |
45 | + <div class="fieldWrapper"> | |
46 | + {{ field.label_tag }} {{ field }} | |
47 | + {{ field.errors }} | |
48 | + {% if field.help_text %} | |
49 | + <p class="help">{{ field.help_text|safe }}</p> | |
50 | + {% endif %} | |
51 | + </div> | |
52 | + {% endif %} | |
53 | + {% else %} | |
54 | + {{ field.as_hidden}} | |
55 | + {% endif %} | |
56 | + {% endif %} | |
57 | + {% elif "referee2" in field.label_tag %} | |
58 | + {% if SP_PeriodForm.reason_referee1.value != "" and SP_PeriodForm.reason_referee2.value == "" and request.user != sp_period.referee1 %} | |
59 | + {% if sp_period.status == "Submitted" and USER_LEVEL|ifinlist:"TAC,Admin"%} | |
60 | + <div class="fieldWrapper"> | |
61 | + {{ field.label_tag }} {{ field }} | |
62 | + {{ field.errors }} | |
63 | + {% if field.help_text %} | |
64 | + <p class="help">{{ field.help_text|safe }}</p> | |
65 | + {% endif %} | |
66 | + </div> | |
67 | + {% endif %} | |
68 | + {% else %} | |
69 | + {{ field.as_hidden}} | |
70 | + {% endif %} | |
71 | + {% elif "is_valid" in field.label_tag or "allocated" in field.label_tag or "priority" in field.label_tag %} | |
72 | + {% if sp_period.status != "Draft" and USER_LEVEL|ifinlist:"Unit-PI,Admin" %} | |
73 | + <div class="fieldWrapper"> | |
74 | + {{ field.label_tag }} {{ field }} | |
75 | + {{ field.errors }} | |
76 | + {% if field.help_text %} | |
77 | + <p class="help">{{ field.help_text|safe }}</p> | |
78 | + {% endif %} | |
79 | + </div> | |
80 | + {% endif %} | |
81 | + | |
82 | + {% elif sp_period.status == "Draft" and USER_LEVEL|ifinlist:"Observer" %} | |
83 | + <div class="fieldWrapper"> | |
84 | + {{ field.label_tag }} {{ field }} | |
85 | + {{ field.errors }} | |
86 | + {% if field.help_text %} | |
87 | + <p class="help">{{ field.help_text|safe }}</p> | |
88 | + {% endif %} | |
89 | + </div> | |
90 | + {% else %} | |
91 | + {{ field.as_hidden}} | |
92 | + {% endif %} | |
93 | + {% endfor %} | |
94 | + {% if sp_period.status == "Draft" and USER_LEVEL|ifinlist:"Observer" %} | |
95 | + <label> Invite user(s) with their email adress: (list them separated by a ","):</label> | |
96 | + <div class="fieldWrapper"> | |
97 | + <textarea id="users" name="users">{{guests|default_if_none:""}} </textarea> | |
98 | + {{ error_message }} | |
99 | + </div> | |
100 | + {% endif %} | |
101 | + {% if USER_LEVEL|ifinlist:"Admin,Observer" %} | |
102 | + <p> <b> Users : </b> | |
103 | + {% for user in users_of_sp_period %} | |
104 | + {% comment %} | |
105 | + input name is the same for all role so we can retrieve a list of selected values | |
106 | + {% endcomment %} | |
107 | + <div> | |
108 | + {% if sp_period.status == "Draft" and USER_LEVEL|ifinlist:"Observer" %} | |
109 | + <input type="checkbox" checked id="{{user.username}}" name="user" value="{{user.id}}"> | |
110 | + {% endif %} | |
111 | + <label for="{{user.username}}"> {{ user.first_name }} {{ user.last_name }} ({{user.username}})</label> | |
112 | + </div> | |
113 | + {% endfor %} | |
114 | + {% endif %} | |
115 | + <div> | |
116 | + <input id="submit" class="btn btn-info" type="submit" value="Submit" /> | |
117 | + <a href="{% url "detail_scientific_program_period" id_sp=id_sp id_period=id_period %}" class="btn btn-info" role="button">Cancel</a> | |
118 | + </div> | |
119 | +</form> | |
120 | + | |
121 | 121 | {% endblock %} |
122 | 122 | \ No newline at end of file | ... | ... |
src/core/pyros_django/scientific_program/templates/scientific_program/test_tac_auto.html
0 โ 100644
src/core/pyros_django/scientific_program/tests.py
1 | +from logging import log | |
2 | +from sys import stdout | |
1 | 3 | from django.http import response |
2 | 4 | from django.test import TestCase |
5 | +from django.utils import timezone, tree | |
3 | 6 | from common.models import PyrosUser,ScientificProgram,Period,SP_Period,SP_Period_User,SP_Period_Guest,UserLevel,Institute,Country |
4 | 7 | from django.urls import reverse |
5 | 8 | from django.core import mail |
6 | 9 | from django.conf import settings |
7 | -import unittest,os | |
10 | +from datetime import date | |
11 | +from datetime import datetime | |
12 | +from unittest.mock import patch | |
13 | +import subprocess,time,os | |
14 | +import multiprocessing | |
15 | +from .AgentSP import * | |
8 | 16 | # Create your tests here. |
9 | 17 | class ScientificProgramTests(TestCase): |
10 | 18 | fixtures = ['tests/scientific_program_TZ.json'] |
11 | 19 | def setUp(self) -> None: |
12 | - """ | |
13 | - institute = Institute.objects.get(id=1) | |
14 | - country = Country.objects.create() | |
15 | - | |
16 | - self.usr1 = PyrosUser.objects.create(username="toto", country=country, institute=institute) | |
17 | - UserLevel.objects.get(name = "Observer").pyros_users.add(self.usr1) | |
18 | - self.usr1.save() | |
19 | - self.usr2 = PyrosUser.objects.create(username="titi", country=country, institute=institute) | |
20 | - UserLevel.objects.get(name = "Unit-PI").pyros_users.add(self.usr2) | |
21 | - self.usr2.save() | |
22 | - self.usr3 = PyrosUser.objects.create(username="tata", country=country, institute=institute) | |
23 | - UserLevel.objects.get(name = "Operator").pyros_users.add(self.usr3) | |
24 | - self.usr3.save() | |
25 | - self.usr4 = PyrosUser.objects.create(username="tutu", country=country, institute=institute) | |
26 | - UserLevel.objects.get(name = "TAC").pyros_users.add(self.usr4) | |
27 | - self.usr4.save() | |
28 | - """ | |
29 | 20 | password = "password123" |
30 | 21 | Period.objects.create() |
31 | - Period.objects.create(start_date=Period.objects.all().first().end_date) | |
22 | + self.period2 = Period.objects.create(start_date=Period.objects.all().first().end_date) | |
23 | + self.period3 = Period.objects.create(start_date=self.period2.end_date,submission_duration=550) | |
32 | 24 | self.usr1 = PyrosUser.objects.get(username="observer") |
33 | 25 | self.usr2 = PyrosUser.objects.get(username="unit_pi") |
34 | 26 | self.usr3 = PyrosUser.objects.get(username="tac") |
35 | 27 | self.usr4 = PyrosUser.objects.get(username="operator") |
36 | 28 | self.usr5 = PyrosUser.objects.get(username="observer2") |
29 | + self.usr6 = PyrosUser.objects.get(username="tac2") | |
30 | + self.usr7 = PyrosUser.objects.get(username="observer3") | |
31 | + self.usr8 = PyrosUser.objects.get(username="observer4") | |
37 | 32 | |
38 | 33 | |
39 | 34 | self.usr1.set_password(password) |
... | ... | @@ -46,216 +41,722 @@ class ScientificProgramTests(TestCase): |
46 | 41 | self.usr4.save() |
47 | 42 | self.usr5.set_password(password) |
48 | 43 | self.usr5.save() |
49 | - def test_SCP_observer_can_create_sp(self): | |
50 | - self.client.login(username=self.usr1,password="password123") | |
51 | - path = reverse('create_scientific_program') | |
52 | - response = self.client.post(path, {"name": "test", "description_short": "sp short desc", "description_long": "sp long desc", | |
53 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
54 | - "over_quota_duration": 5, "token": 3, | |
55 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
56 | - }) | |
57 | - self.assertEqual(response.status_code,302) | |
58 | - self.assertEqual(len(mail.outbox),3) | |
59 | - self.assertIn("test@example.com",mail.outbox[0].recipients()) | |
60 | - self.assertIn("test2@example.com",mail.outbox[1].recipients()) | |
61 | - self.assertIn("test3@example.com",mail.outbox[2].recipients()) | |
62 | - domain = settings.DEFAULT_DOMAIN | |
63 | - url = f"{domain}{reverse('sp_register',args=(ScientificProgram.objects.all().order_by('-id').first().pk,Period.objects.all().order_by('-id').first().pk))}" | |
64 | - self.assertIn(url,mail.outbox[2].body) | |
65 | - self.assertEqual(ScientificProgram.objects.all().count(),1) | |
66 | - self.assertEqual(list(SP_Period_Guest.objects.all().values_list("email",flat=True)),["test@example.com","test2@example.com","test3@example.com"]) | |
67 | - self.assertEqual(SP_Period.objects.get(scientific_program__name="test").status,SP_Period.STATUSES_DRAFT) | |
68 | - self.assertEqual(ScientificProgram.objects.get(name="test").sp_pi,self.usr1) | |
44 | + self.usr6.set_password(password) | |
45 | + self.usr6.save() | |
46 | + self.usr7.set_password(password) | |
47 | + self.usr7.save() | |
48 | + self.usr8.set_password(password) | |
49 | + self.usr8.save() | |
50 | + | |
51 | + def logout(self): | |
69 | 52 | self.client.get(reverse("user_logout")) |
70 | - # u2 shouldn't be able to create a new scientific program | |
71 | - self.client.login(username=self.usr2,password="password123") | |
53 | + print("Log out") | |
54 | + def login_as_user(self,number): | |
55 | + if number == 1: | |
56 | + self.client.post(reverse("login_validation"),{"email":self.usr1,"password":"password123"}) | |
57 | + elif number == 2: | |
58 | + self.client.post(reverse("login_validation"),{"email":self.usr2,"password":"password123"}) | |
59 | + elif number == 3: | |
60 | + self.client.post(reverse("login_validation"),{"email":self.usr3,"password":"password123"}) | |
61 | + elif number == 4: | |
62 | + self.client.post(reverse("login_validation"),{"email":self.usr4,"password":"password123"}) | |
63 | + elif number == 5: | |
64 | + self.client.post(reverse("login_validation"),{"email":self.usr5,"password":"password123"}) | |
65 | + elif number == 6: | |
66 | + self.client.post(reverse("login_validation"),{"email":self.usr6,"password":"password123"}) | |
67 | + elif number == 7: | |
68 | + self.client.post(reverse("login_validation"),{"email":self.usr7,"password":"password123"}) | |
69 | + elif number == 8: | |
70 | + self.client.post(reverse("login_validation"),{"email":self.usr8,"password":"password123"}) | |
71 | + print(f'Log in as {self.client.session["user"]}') | |
72 | + | |
73 | + def create_dummy_SCP(self,name,user_number): | |
74 | + """ | |
75 | + Generate a SCP | |
76 | + """ | |
77 | + self.login_as_user(user_number) | |
72 | 78 | path = reverse('create_scientific_program') |
73 | - response = self.client.post(path, {"name": "test2", "description_short": "sp short desc", "description_long": "sp long desc", | |
74 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
75 | - "over_quota_duration": 5, "token": 3, | |
76 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
77 | - }) | |
79 | + post_data = { | |
80 | + "name":name, | |
81 | + "description_short":"sp short desc", | |
82 | + "description_long":"sp long desc", | |
83 | + "science_theme":1, | |
84 | + } | |
85 | + response = self.client.post(path,post_data) | |
86 | + self.logout() | |
87 | + return ScientificProgram.objects.get(name=name) | |
88 | + | |
89 | + def create_dummy_SCP_Period(self,scientific_program,user_number): | |
90 | + self.login_as_user(user_number) | |
91 | + | |
92 | + path = reverse('create_scientific_program_period',kwargs={"id_SP":scientific_program.id}) | |
93 | + post_data = { | |
94 | + "period":Period.objects.next_period().id, | |
95 | + "public_visibility":SP_Period.VISIBILITY_YES, | |
96 | + "quota_minimal":2, | |
97 | + "quota_nominal":5, | |
98 | + "over_quota_duration":2, | |
99 | + "token":2 | |
100 | + } | |
101 | + | |
102 | + response = self.client.post(path,post_data) | |
103 | + sp_period = SP_Period.objects.get(scientific_program=scientific_program,period=Period.objects.next_period()) | |
104 | + | |
105 | + self.logout() | |
106 | + return sp_period | |
107 | + | |
108 | + def create_dummies_SCP_and_SCP_Period(self,name_of_SCP,user_number): | |
109 | + self.create_dummy_SCP(name_of_SCP,user_number) | |
110 | + scientific_program = ScientificProgram.objects.get(name=name_of_SCP) | |
111 | + sp_period = self.create_dummy_SCP_Period(scientific_program,user_number) | |
112 | + return sp_period | |
113 | + | |
114 | + def test_SCP_Observer_can_submit_new_SP_from_scratch(self): | |
115 | + self.login_as_user(1) | |
116 | + path = reverse('create_scientific_program') | |
117 | + post_data = { | |
118 | + "name":"test", | |
119 | + "description_short":"sp short desc", | |
120 | + "description_long":"sp long desc", | |
121 | + "science_theme":1, | |
122 | + } | |
123 | + response = self.client.post(path,post_data) | |
78 | 124 | self.assertEqual(response.status_code,302) |
79 | - self.assertEqual(len(mail.outbox),3) | |
80 | 125 | self.assertEqual(ScientificProgram.objects.all().count(),1) |
126 | + current_sp = ScientificProgram.objects.all().first() | |
127 | + self.assertEqual(current_sp.sp_pi,self.usr1) | |
81 | 128 | |
82 | - def test_SCP_view_sp_list(self): | |
83 | - self.client.login(username=self.usr1,password="password123") | |
129 | + self.logout() | |
130 | + | |
131 | + self.login_as_user(2) | |
84 | 132 | path = reverse('create_scientific_program') |
85 | - self.client.post(path, {"name": "test", "description_short": "sp short desc", "description_long": "sp long desc", | |
86 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
87 | - "over_quota_duration": 5, "token": 3, | |
88 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
89 | - }) | |
90 | - | |
91 | - self.client.post(path, {"name": "sp2", "description_short": "sp short desc", "description_long": "sp long desc", | |
92 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
93 | - "over_quota_duration": 5, "token": 3, | |
94 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
95 | - }) | |
96 | - self.client.get(reverse("user_logout")) | |
97 | - # login with another user to create a SP that user 1 shouldn't be able to see | |
133 | + post_data = { | |
134 | + "name":"test 2", | |
135 | + "description_short":"test2 short desc", | |
136 | + "description_long":"test2 long desc", | |
137 | + "science_theme":2, | |
138 | + } | |
139 | + response = self.client.post(path,post_data) | |
140 | + self.assertEqual(response.status_code,302) | |
141 | + # user can't access to this page | |
142 | + self.assertEqual(response.context,None) | |
143 | + self.logout() | |
144 | + | |
145 | + def test_SCP_Observer_can_submit_new_SP_from_template(self): | |
146 | + self.create_dummy_SCP("test",1) | |
147 | + current_sp = ScientificProgram.objects.all().first() | |
148 | + | |
149 | + self.login_as_user(1) | |
150 | + path = reverse('create_scientific_program_with_template',kwargs={"id_SP":current_sp.id}) | |
151 | + post_data = { | |
152 | + "name":"test 2", | |
153 | + "description_short":"test2 short desc", | |
154 | + "description_long":"test2 long desc", | |
155 | + "science_theme":2, | |
156 | + } | |
157 | + response = self.client.post(path,post_data) | |
158 | + self.assertEqual(response.status_code,302) | |
159 | + self.assertEqual(ScientificProgram.objects.all().count(),2) | |
160 | + current_sp = ScientificProgram.objects.all().first() | |
161 | + self.assertEqual(current_sp.sp_pi,self.usr1) | |
162 | + | |
163 | + self.logout() | |
164 | + | |
165 | + self.login_as_user(2) | |
98 | 166 | |
99 | - self.client.login(username=self.usr5,password="password123") | |
100 | - self.client.post(path, {"name": "third_project", "description_short": "sp short desc", "description_long": "sp long desc", | |
101 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
102 | - "over_quota_duration": 5, "token": 3, | |
103 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
104 | - }) | |
105 | - self.client.get(reverse("user_logout")) | |
106 | - # log in again as user 1 to see the scientific program list | |
107 | - self.client.login(username=self.usr1,password="password123") | |
108 | - path = reverse("scientific_program_list") | |
109 | - response = self.client.get(path) | |
110 | - self.assertContains(response,"test") | |
111 | - self.assertContains(response,"sp2") | |
112 | - self.assertNotContains(response,"third_project") | |
113 | - self.client.get(reverse("user_logout")) | |
167 | + path = reverse('create_scientific_program_with_template',kwargs={"id_SP":current_sp.id}) | |
168 | + post_data = { | |
169 | + "name":"test 3", | |
170 | + "description_short":"test3 short desc", | |
171 | + "description_long":"test3 long desc", | |
172 | + "science_theme":2, | |
173 | + } | |
174 | + response = self.client.post(path,post_data) | |
175 | + self.assertEqual(response.status_code,302) | |
176 | + # user can't access to this page | |
177 | + self.assertEqual(response.context,None) | |
178 | + self.assertEqual(ScientificProgram.objects.all().count(),2) | |
179 | + self.logout() | |
114 | 180 | |
115 | - # log in as unit pi (user 2) | |
116 | - self.client.login(username=self.usr2,password="password123") | |
117 | - path = reverse("scientific_program_list") | |
118 | - response = self.client.get(path) | |
119 | - self.assertContains(response,"test") | |
120 | - self.assertContains(response,"sp2") | |
121 | - self.assertContains(response,"third_project") | |
181 | + def test_SCP_Observer_can_submit_SP_Period_from_scratch(self): | |
182 | + self.create_dummy_SCP("test",1) | |
183 | + self.logout() | |
184 | + self.login_as_user(1) | |
185 | + current_sp = ScientificProgram.objects.all().first() | |
186 | + path = reverse('create_scientific_program_period',kwargs={"id_SP":current_sp.id}) | |
187 | + post_data = { | |
188 | + "period":Period.objects.next_period().id, | |
189 | + "public_visibility":SP_Period.VISIBILITY_YES, | |
190 | + "quota_minimal":2, | |
191 | + "quota_nominal":5, | |
192 | + "over_quota_duration":2, | |
193 | + "token":2 | |
194 | + } | |
195 | + | |
196 | + response = self.client.post(path,post_data) | |
197 | + | |
198 | + self.assertEqual(response.status_code,302) | |
199 | + self.assertEqual(SP_Period.objects.filter(scientific_program=current_sp).count(),1) | |
200 | + current_sp_period = SP_Period.objects.filter(scientific_program=current_sp).first() | |
201 | + self.assertEqual(current_sp_period.quota_minimal,2) | |
202 | + self.assertEqual(current_sp_period.status,SP_Period.STATUSES_DRAFT) | |
122 | 203 | |
123 | - self.client.get(reverse("user_logout")) | |
204 | + self.logout() | |
124 | 205 | |
125 | - # log in as operator (shoudln't be able to see anything) | |
126 | - self.client.login(username=self.usr4,password="password123") | |
127 | - path = reverse("scientific_program_list") | |
128 | - response = self.client.get(path) | |
129 | - # user is redirected to his previous page, however it was the first page he visited to he is redirected to a None page | |
206 | + self.login_as_user(2) | |
207 | + path = reverse('create_scientific_program_period',args=[1]) | |
208 | + post_data = { | |
209 | + "public_visibility":SP_Period.VISIBILITY_YES, | |
210 | + "quota_minimal":2, | |
211 | + "quota_nominal":5, | |
212 | + "quota_allocated":None, | |
213 | + "over_quota_duration":2, | |
214 | + "over_quota_duration_allocated":None, | |
215 | + "token":2, | |
216 | + "token_allocated":None, | |
217 | + "vote_referee1":None, | |
218 | + "reason_referee1":None, | |
219 | + "vote_referee2":None, | |
220 | + "reason_referee2":None, | |
221 | + "priority":None, | |
222 | + "is_valid":False | |
223 | + } | |
224 | + response = self.client.post(path,post_data) | |
130 | 225 | self.assertEqual(response.status_code,302) |
226 | + # user can't access to this page | |
131 | 227 | self.assertEqual(response.context,None) |
228 | + self.logout() | |
132 | 229 | |
133 | - def test_SCP_view_sp_details(self): | |
134 | - self.client.login(username=self.usr1,password="password123") | |
135 | - path = reverse('create_scientific_program') | |
136 | - self.client.post(path, {"name": "test", "description_short": "sp short desc", "description_long": "sp long desc", | |
137 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
138 | - "over_quota_duration": 5, "token": 3, | |
139 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
140 | - }) | |
230 | + def test_SCP_Observer_can_submit_existing_SP_for_new_Period(self): | |
231 | + self.create_dummy_SCP("test",1) | |
232 | + current_scientific_program = ScientificProgram.objects.all().first() | |
233 | + self.create_dummy_SCP_Period(current_scientific_program,1) | |
234 | + sp_period = SP_Period.objects.all().first() | |
141 | 235 | |
142 | - self.client.post(path, {"name": "sp2", "description_short": "sp short desc", "description_long": "sp long desc", | |
143 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
144 | - "over_quota_duration": 5, "token": 3, | |
145 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
146 | - }) | |
147 | - self.client.get(reverse("user_logout")) | |
148 | - # login with another user to create a SP that user 1 shouldn't be able to see | |
236 | + self.login_as_user(1) | |
237 | + path = reverse('repropose_scientific_program',kwargs={"id_SP":current_scientific_program.id,"id_SP_Period":sp_period.id}) | |
238 | + post_data = { | |
239 | + "period":self.period3.id, | |
240 | + "public_visibility":SP_Period.VISIBILITY_YES, | |
241 | + "quota_minimal":2, | |
242 | + "quota_nominal":5, | |
243 | + "over_quota_duration":2, | |
244 | + "token":2 | |
245 | + } | |
246 | + response = self.client.post(path,post_data) | |
247 | + self.assertEqual(SP_Period.objects.filter(scientific_program=current_scientific_program).count(),2) | |
248 | + current_sp_period = SP_Period.objects.filter(scientific_program=current_scientific_program)[1] | |
249 | + SP_Period_User(SP_Period=current_sp_period,user=self.usr5).save() | |
250 | + self.assertEqual(current_sp_period.quota_minimal,2) | |
251 | + self.assertEqual(current_sp_period.status,SP_Period.STATUSES_DRAFT) | |
252 | + # We should have one user which is from the previous period | |
253 | + self.assertEqual(SP_Period_User.objects.filter(SP_Period=current_sp_period).count(),1) | |
254 | + | |
255 | + def test_SCP_Unit_PI_can_add_Period(self): | |
256 | + self.login_as_user(2) | |
257 | + path = reverse("create_period") | |
258 | + today = timezone.now().date().strftime("%d/%m/%Y") | |
259 | + post_data = { | |
260 | + "start_date_picker":today, | |
261 | + "exploitation_duration":182, | |
262 | + "submission_duration":182, | |
263 | + "evaluation_duration":31, | |
264 | + "validation_duration":5, | |
265 | + "notification_duration":10, | |
266 | + "property_of_data_duration":365, | |
267 | + "data_accessibility_duration":365*10 | |
268 | + } | |
149 | 269 | |
150 | - self.client.login(username=self.usr5,password="password123") | |
151 | - self.client.post(path, {"name": "third_project", "description_short": "sp short desc", "description_long": "sp long desc", | |
152 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
153 | - "over_quota_duration": 5, "token": 3, | |
154 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
155 | - }) | |
156 | - self.client.get(reverse("user_logout")) | |
157 | - # log in as user 1 | |
158 | - self.client.login(username=self.usr1,password="password123") | |
159 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="test").id]) | |
160 | - response = self.client.get(path) | |
270 | + | |
271 | + response = self.client.post(path,post_data) | |
272 | + # This creation should fail, we should have 3 periods | |
273 | + self.assertEqual(Period.objects.all().count(),3) | |
161 | 274 | self.assertEqual(response.status_code,200) |
162 | - self.assertContains(response,"sp long desc") | |
163 | - self.assertContains(response,"test") | |
275 | + self.assertTrue(response.context["error"]) | |
276 | + | |
277 | + start_date = Period.objects.latest_period().end_date.strftime("%d/%m/%Y") | |
278 | + post_data = { | |
279 | + "start_date_picker":start_date, | |
280 | + "exploitation_duration":182, | |
281 | + "submission_duration":182, | |
282 | + "evaluation_duration":31, | |
283 | + "validation_duration":5, | |
284 | + "notification_duration":10, | |
285 | + "property_of_data_duration":365, | |
286 | + "data_accessibility_duration":365*10 | |
287 | + } | |
288 | + response = self.client.post(path,post_data) | |
289 | + # This creation should success, we should have 4 periods and should be redirected to period_list | |
290 | + self.assertEqual(Period.objects.all().count(),4) | |
291 | + self.assertEqual(response.status_code,302) | |
292 | + self.logout() | |
293 | + | |
294 | + def test_SCP_view_main_menu(self): | |
295 | + self.create_dummy_SCP("test",1) | |
296 | + sp_period = self.create_dummy_SCP_Period(ScientificProgram.objects.all().first(),1) | |
297 | + self.login_as_user(1) | |
298 | + path = reverse("index_scientific_program") | |
164 | 299 | |
165 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="sp2").id]) | |
166 | 300 | response = self.client.get(path) |
167 | - self.assertEqual(response.status_code,200) | |
168 | - self.assertContains(response,"sp long desc") | |
169 | - self.assertContains(response,"sp2") | |
301 | + # u1 should be able to add, view scientific programs and view Periods | |
302 | + self.assertTrue(response.context["CAN_SUBMIT_SP"]) | |
303 | + self.assertTrue(response.context["CAN_VIEW_SP"]) | |
304 | + self.assertTrue(response.context["CAN_VIEW_EXPLOITATION_PERIOD"]) | |
305 | + self.assertFalse(response.context["CAN_VIEW_ALL_SP"]) | |
306 | + self.assertFalse(response.context["CAN_EVALUATE_SP"]) | |
307 | + self.assertFalse(response.context["CAN_VALIDATE_SP"]) | |
308 | + self.assertFalse(response.context["CAN_CREATE_EXPLOITATION_PERIOD"]) | |
309 | + self.assertFalse(response.context["CAN_ASSOCIATE_TAC_TO_SP"]) | |
170 | 310 | |
171 | - self.client.get(reverse("user_logout")) | |
311 | + self.logout() | |
312 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().submission_start_date),"%Y-%m-%d")): | |
313 | + # u2 should be able to associate TAC to SP | |
314 | + # Submission period | |
315 | + self.login_as_user(2) | |
316 | + response = self.client.get(path) | |
317 | + self.assertTrue(response.context["CAN_ASSOCIATE_TAC_TO_SP"]) | |
318 | + self.logout() | |
319 | + | |
320 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().unit_pi_validation_start_date),"%Y-%m-%d")): | |
321 | + self.login_as_user(2) | |
322 | + # Validation period | |
323 | + sp_period.status = SP_Period.STATUSES_EVALUATED | |
324 | + sp_period.save() | |
325 | + response = self.client.get(path) | |
326 | + self.assertEqual(self.client.session["role"],"Unit-PI") | |
327 | + # u2 should be able to do everything outside submitting, evaluate SP and see the obsever's view for sp | |
328 | + self.assertTrue(response.context["CAN_VIEW_EXPLOITATION_PERIOD"]) | |
329 | + self.assertTrue(response.context["CAN_VIEW_ALL_SP"]) | |
330 | + self.assertTrue(response.context["CAN_CREATE_EXPLOITATION_PERIOD"]) | |
331 | + self.assertTrue(response.context["CAN_VALIDATE_SP"]) | |
332 | + self.assertFalse(response.context["CAN_ASSOCIATE_TAC_TO_SP"]) | |
333 | + self.assertFalse(response.context["CAN_VIEW_SP"]) | |
334 | + self.assertFalse(response.context["CAN_SUBMIT_SP"]) | |
335 | + self.assertFalse(response.context["CAN_EVALUATE_SP"]) | |
336 | + self.logout() | |
337 | + | |
338 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().submission_end_date),"%Y-%m-%d")): | |
339 | + self.login_as_user(3) | |
340 | + sp_period.status = SP_Period.STATUSES_SUBMITTED | |
341 | + sp_period.save() | |
342 | + | |
343 | + response = self.client.get(path) | |
344 | + # u3 should be able to only evaluate SP | |
345 | + self.assertTrue(response.context["CAN_EVALUATE_SP"]) | |
346 | + self.assertFalse(response.context["CAN_VIEW_EXPLOITATION_PERIOD"]) | |
347 | + self.assertFalse(response.context["CAN_VIEW_ALL_SP"]) | |
348 | + self.assertFalse(response.context["CAN_CREATE_EXPLOITATION_PERIOD"]) | |
349 | + self.assertFalse(response.context["CAN_VALIDATE_SP"]) | |
350 | + self.assertFalse(response.context["CAN_VIEW_SP"]) | |
351 | + self.assertFalse(response.context["CAN_SUBMIT_SP"]) | |
352 | + self.assertFalse(response.context["CAN_ASSOCIATE_TAC_TO_SP"]) | |
172 | 353 | |
173 | - # log in as user 2 | |
174 | - self.client.login(username=self.usr2,password="password123") | |
175 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="test").id]) | |
354 | + self.logout() | |
355 | + | |
356 | + def test_SCP_view_sp_list_and_detail(self): | |
357 | + sp1 = self.create_dummies_SCP_and_SCP_Period("test",1) | |
358 | + sp2 = self.create_dummies_SCP_and_SCP_Period("test2",1) | |
359 | + sp3 = self.create_dummies_SCP_and_SCP_Period("test3",1) | |
360 | + sp4 = self.create_dummies_SCP_and_SCP_Period("test4",1) | |
361 | + sp5 = self.create_dummies_SCP_and_SCP_Period("test5",1) | |
362 | + sp6 = self.create_dummies_SCP_and_SCP_Period("test6",8) | |
363 | + sp7 = self.create_dummies_SCP_and_SCP_Period("test7",7) | |
364 | + sp2.status = SP_Period.STATUSES_SUBMITTED | |
365 | + sp2.referee2 = self.usr3 | |
366 | + sp2.referee1 = self.usr6 | |
367 | + sp2.save() | |
368 | + sp3.status = SP_Period.STATUSES_EVALUATED | |
369 | + sp3.referee1 = self.usr6 | |
370 | + sp3.referee2 = self.usr3 | |
371 | + sp3.save() | |
372 | + sp4.status = SP_Period.STATUSES_ACCEPTED | |
373 | + sp4.save() | |
374 | + SP_Period_User.objects.create(SP_Period=sp4,user=self.usr5) | |
375 | + sp5.status = SP_Period.STATUSES_REJECTED | |
376 | + sp6.status = SP_Period.STATUSES_ACCEPTED | |
377 | + sp6.save() | |
378 | + SP_Period_User.objects.create(SP_Period=sp6,user=self.usr1) | |
379 | + sp7.status = SP_Period.STATUSES_ACCEPTED | |
380 | + sp7.save() | |
381 | + SP_Period_User.objects.create(SP_Period=sp7,user=self.usr5) | |
382 | + | |
383 | + self.login_as_user(1) | |
384 | + path = reverse("own_scientific_program_list") | |
176 | 385 | response = self.client.get(path) |
177 | - self.assertEqual(response.status_code,200) | |
178 | - self.assertContains(response,"sp long desc") | |
179 | - self.assertContains(response,"test") | |
386 | + # Can't compare 2 queryset, it returns an error despite they are equal.. so we transform those queryset into list and compare them. | |
387 | + self.assertEqual(list(ScientificProgram.objects.filter(sp_pi=self.usr1).order_by("-id")),list(response.context["sp_of_user"])) | |
180 | 388 | |
181 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="sp2").id]) | |
389 | + path = reverse("detail_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
182 | 390 | response = self.client.get(path) |
183 | - self.assertEqual(response.status_code,200) | |
184 | - self.assertContains(response,"sp long desc") | |
185 | - self.assertContains(response,"sp2") | |
391 | + self.assertFalse(response.context["CAN_VIEW_TAC_VOTES"]) | |
186 | 392 | |
187 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="third_project").id]) | |
393 | + self.logout() | |
394 | + | |
395 | + # login as TAC 1 and put today date to evaluation period | |
396 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().submission_end_date),"%Y-%m-%d")): | |
397 | + self.login_as_user(3) | |
398 | + path = reverse("list_submitted_scientific_program") | |
399 | + response = self.client.get(path) | |
400 | + self.assertEqual(list(SP_Period.objects.filter(referee2=self.usr3,period=Period.objects.next_period(),status=SP_Period.STATUSES_SUBMITTED)),response.context["list_of_evaluated_sp"]) | |
401 | + path = reverse("detail_scientific_program_period",kwargs={"id_sp":sp2.scientific_program.id,"id_period":sp2.period.id}) | |
402 | + | |
403 | + response = self.client.get(path) | |
404 | + self.assertEqual(response.status_code,302) | |
405 | + # user can't access to this page | |
406 | + self.assertEqual(response.context,None) | |
407 | + self.logout() | |
408 | + | |
409 | + # log in as user observer | |
410 | + self.login_as_user(5) | |
411 | + path = reverse("scientific_program_list") | |
188 | 412 | response = self.client.get(path) |
189 | - self.assertEqual(response.status_code,200) | |
190 | - self.assertContains(response,"sp long desc") | |
191 | - self.assertContains(response,"third_project") | |
413 | + sp_of_user = list(("test4","test7")) | |
414 | + self.assertListEqual(sp_of_user,list(response.context["sp_of_user"].values_list("name",flat=True))) | |
415 | + path = reverse("detail_scientific_program_period",kwargs={"id_sp":sp4.scientific_program.id,"id_period":sp4.period.id}) | |
416 | + response = self.client.get(path) | |
417 | + self.assertFalse(response.context["CAN_VIEW_TAC_VOTES"]) | |
418 | + self.assertFalse(response.context["CAN_SUBMIT_PROPOSAL"]) | |
419 | + self.logout() | |
420 | + | |
421 | + # log in as unit pi | |
422 | + self.login_as_user(2) | |
423 | + path = reverse("scientific_program_list") | |
424 | + response = self.client.get(path) | |
425 | + self.assertListEqual(list(ScientificProgram.objects.all().order_by("-id")),list(response.context["scientific_programs"])) | |
426 | + self.logout() | |
192 | 427 | |
193 | - self.client.get(reverse("user_logout")) | |
428 | + def test_SCP_list_Periods(self): | |
429 | + self.login_as_user(1) | |
430 | + path = reverse("period_list") | |
431 | + response = self.client.get(path) | |
432 | + self.assertContains(response,"(Current period)") | |
433 | + | |
434 | + self.assertEqual(Period.objects.all().count(),len(response.context["future_periods"])) | |
435 | + self.logout() | |
436 | + | |
437 | + def test_SCP_detail_Period(self): | |
438 | + sp1 = self.create_dummies_SCP_and_SCP_Period("test",1) | |
439 | + sp2 = self.create_dummies_SCP_and_SCP_Period("test2",1) | |
440 | + sp3 = self.create_dummies_SCP_and_SCP_Period("test3",1) | |
441 | + sp4 = self.create_dummies_SCP_and_SCP_Period("test4",1) | |
442 | + sp5 = self.create_dummies_SCP_and_SCP_Period("test5",1) | |
443 | + | |
444 | + self.login_as_user(1) | |
445 | + period = sp1.period | |
446 | + path = reverse("detail_period",kwargs={"id":period.id}) | |
447 | + response = self.client.get(path) | |
448 | + # we should see the same number of SP_Period (5) that are associated to user 1 | |
449 | + self.assertEqual(SP_Period.objects.all().count(),len(response.context["sp_periods"])) | |
450 | + self.logout() | |
451 | + | |
452 | + def test_SCP_edit(self): | |
453 | + sp1 = self.create_dummies_SCP_and_SCP_Period("test",1) | |
454 | + sp2 = self.create_dummies_SCP_and_SCP_Period("test2",1) | |
455 | + sp3 = self.create_dummies_SCP_and_SCP_Period("test3",1) | |
456 | + sp4 = self.create_dummies_SCP_and_SCP_Period("test4",1) | |
457 | + sp5 = self.create_dummies_SCP_and_SCP_Period("test5",5) | |
458 | + sp2.status = SP_Period.STATUSES_SUBMITTED | |
459 | + sp2.save() | |
460 | + sp3.status = SP_Period.STATUSES_ACCEPTED | |
461 | + sp3.save() | |
462 | + sp4.status = SP_Period.STATUSES_ACCEPTED | |
463 | + sp4.save() | |
464 | + sp5.status = SP_Period.STATUSES_ACCEPTED | |
465 | + sp5.save() | |
466 | + post_data = { | |
467 | + | |
468 | + "name": "test", | |
469 | + "description_short":"modified test", | |
470 | + "description_long":"modified test", | |
471 | + "science_theme" :"2", | |
472 | + "public_visibility":"Yes", | |
473 | + "quota_minimal":"0", | |
474 | + "quota_nominal":"0", | |
475 | + "over_quota_duration":"0", | |
476 | + "token":"0", | |
477 | + "vote_referee1":"", | |
478 | + "reason_referee1":"", | |
479 | + "users":"", | |
480 | + } | |
481 | + self.login_as_user(1) | |
482 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
483 | + response = self.client.post(path,post_data) | |
484 | + self.assertEqual(response.status_code,302) | |
485 | + updated_sp = SP_Period.objects.get(id=sp1.id) | |
486 | + self.assertEqual(updated_sp.scientific_program.description_short,"modified test") | |
487 | + | |
488 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp2.scientific_program.id,"id_period":sp2.period.id}) | |
489 | + post_data = { | |
490 | + | |
491 | + "name": "test", | |
492 | + "description_short":"modified test2", | |
493 | + "description_long":"modified test2", | |
494 | + "science_theme" :"2", | |
495 | + "public_visibility":"Yes", | |
496 | + "quota_minimal":"10", | |
497 | + "quota_nominal":"0", | |
498 | + "over_quota_duration":"0", | |
499 | + "token":"0", | |
500 | + "vote_referee1":"", | |
501 | + "reason_referee1":"", | |
502 | + "users":"", | |
503 | + } | |
504 | + response = self.client.post(path,post_data) | |
505 | + self.assertEqual(response.status_code,302) | |
506 | + updated_sp = SP_Period.objects.get(id=sp2.id) | |
507 | + # user can't modify this SP_Period because the status isn't DRAFT | |
508 | + self.assertNotEqual(updated_sp.scientific_program.description_short,"modified test2") | |
509 | + self.assertNotEqual(updated_sp.quota_minimal,10) | |
510 | + | |
511 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp5.scientific_program.id,"id_period":sp5.period.id}) | |
512 | + response = self.client.post(path,post_data) | |
513 | + # u1 can't edit SP5 because he is not the SP_PI | |
514 | + self.assertEqual(response.status_code,302) | |
515 | + self.assertNotEqual(updated_sp.scientific_program.description_short,"modified test2") | |
516 | + self.assertNotEqual(updated_sp.quota_minimal,10) | |
517 | + self.assertEqual(response.url,reverse("detail_scientific_program_period",kwargs={"id_sp":sp5.scientific_program.id,"id_period":sp5.period.id})) | |
518 | + self.logout() | |
519 | + | |
520 | + def test_SCP_add_users(self): | |
521 | + sp1 = self.create_dummies_SCP_and_SCP_Period("test",1) | |
522 | + post_data = { | |
194 | 523 | |
195 | - # log in as user 4 (operator) | |
524 | + "name": "test", | |
525 | + "description_short":"modified test", | |
526 | + "description_long":"modified test", | |
527 | + "science_theme" :"2", | |
528 | + "public_visibility":"Yes", | |
529 | + "quota_minimal":"0", | |
530 | + "quota_nominal":"0", | |
531 | + "over_quota_duration":"0", | |
532 | + "token":"0", | |
533 | + "vote_referee1":"", | |
534 | + "reason_referee1":"", | |
535 | + "users":"observer2@test.com", | |
536 | + } | |
537 | + self.login_as_user(1) | |
538 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
539 | + response = self.client.post(path,post_data) | |
540 | + self.assertEqual(SP_Period_Guest.objects.filter(SP_Period=sp1).first().email,"observer2@test.com") | |
541 | + self.assertEqual(len(mail.outbox),1) | |
542 | + self.assertIn("observer2@test.com",mail.outbox[0].recipients()) | |
543 | + self.logout() | |
196 | 544 | |
197 | - self.client.login(username=self.usr4,password="password123") | |
198 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="test").id]) | |
545 | + url = reverse("sp_register",kwargs={"id_SP":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
546 | + self.login_as_user(5) | |
547 | + response = self.client.get(url) | |
548 | + self.assertEqual(response.status_code,302) | |
549 | + self.assertEqual(response.url,reverse("detail_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id})) | |
550 | + self.assertEqual(SP_Period_Guest.objects.all().count(),0) | |
551 | + self.assertEqual(SP_Period_User.objects.filter(SP_Period=sp1).count(),1) | |
552 | + self.assertEqual(SP_Period_User.objects.filter(SP_Period=sp1).first().user,self.usr5) | |
553 | + self.logout() | |
554 | + | |
555 | + def test_SCP_delete(self): | |
556 | + sp1 = self.create_dummies_SCP_and_SCP_Period("test",1) | |
557 | + sp2 = self.create_dummy_SCP("test2",1) | |
558 | + self.login_as_user(1) | |
559 | + path = reverse("delete_scientific_program",kwargs={"id_sp":sp1.scientific_program.id}) | |
199 | 560 | response = self.client.get(path) |
200 | - # user is redirected to his previous page, however it was the first page he visited to he is redirected to a None page | |
561 | + # Can't delete this SP because it's linked to a period | |
201 | 562 | self.assertEqual(response.status_code,302) |
202 | - self.assertEqual(response.context,None) | |
563 | + self.assertEqual(response.url,reverse("detail_scientific_program", args=[sp1.scientific_program.id])) | |
564 | + self.assertEqual(ScientificProgram.objects.all().count(),2) | |
203 | 565 | |
204 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="sp2").id]) | |
566 | + path = reverse("delete_scientific_program",kwargs={"id_sp":sp2.id}) | |
205 | 567 | response = self.client.get(path) |
206 | - # user is redirected to his previous page, however it was the first page he visited to he is redirected to a None page | |
568 | + # Can delete this SP | |
207 | 569 | self.assertEqual(response.status_code,302) |
208 | - self.assertEqual(response.context,None) | |
570 | + self.assertEqual(response.url,reverse("index_scientific_program")) | |
571 | + self.assertEqual(ScientificProgram.objects.all().count(),1) | |
572 | + | |
573 | + self.logout() | |
209 | 574 | |
210 | - path = reverse("detail_scientific_program",args=[ScientificProgram.objects.get(name="third_project").id]) | |
575 | + def test_SCP_Period_delete(self): | |
576 | + sp1 = self.create_dummies_SCP_and_SCP_Period("test",1) | |
577 | + sp2 = self.create_dummies_SCP_and_SCP_Period("test2",1) | |
578 | + sp2.status = SP_Period.STATUSES_ACCEPTED | |
579 | + sp2.save() | |
580 | + self.login_as_user(1) | |
581 | + path = reverse("delete_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
211 | 582 | response = self.client.get(path) |
212 | - # user is redirected to his previous page, however it was the first page he visited to he is redirected to a None page | |
583 | + # Can delete this SP because it's in draft status | |
213 | 584 | self.assertEqual(response.status_code,302) |
214 | - self.assertEqual(response.context,None) | |
215 | - | |
216 | - def test_SCP_sp_update(self): | |
217 | - self.client.login(username=self.usr1,password="password123") | |
218 | - path = reverse('create_scientific_program') | |
219 | - self.client.post(path, {"name": "sp1", "description_short": "sp short desc", "description_long": "sp long desc", | |
220 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
221 | - "over_quota_duration": 5, "token": 3, | |
222 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
223 | - }) | |
585 | + self.assertEqual(response.url,reverse("detail_scientific_program", args=[sp1.scientific_program.id])) | |
586 | + self.assertEqual(SP_Period.objects.filter(scientific_program=sp1.scientific_program).count(),0) | |
587 | + | |
588 | + path = reverse("delete_scientific_program_period",kwargs={"id_sp":sp2.scientific_program.id,"id_period":sp2.period.id}) | |
589 | + response = self.client.get(path) | |
590 | + # Can't delete this SP | |
591 | + self.assertEqual(response.status_code,302) | |
592 | + self.assertEqual(response.url,reverse("detail_scientific_program", args=[sp2.scientific_program.id])) | |
593 | + self.assertEqual(SP_Period.objects.filter(scientific_program=sp2.scientific_program).count(),1) | |
594 | + self.logout() | |
595 | + | |
596 | + def run_agent_SP(self): | |
597 | + agent = build_agent(AgentSP, RUN_IN_THREAD=True) | |
598 | + print(agent) | |
599 | + agent.run() | |
600 | + def test_SCP_lifecycle(self): | |
224 | 601 | |
225 | - self.client.post(path, {"name": "sp2", "description_short": "sp short desc", "description_long": "sp long desc", | |
226 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
227 | - "over_quota_duration": 5, "token": 3, | |
228 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
229 | - }) | |
230 | - self.client.post(path, {"name": "sp3", "description_short": "sp short desc", "description_long": "sp long desc", | |
231 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
232 | - "over_quota_duration": 5, "token": 3, | |
233 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
234 | - }) | |
235 | - self.client.post(path, {"name": "sp4", "description_short": "sp short desc", "description_long": "sp long desc", | |
236 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
237 | - "over_quota_duration": 5, "token": 3, | |
238 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
239 | - }) | |
240 | - self.client.get(reverse("user_logout")) | |
241 | - # login with another user to create a SP that user 1 shouldn't be able to see | |
242 | 602 | |
243 | - self.client.login(username=self.usr5,password="password123") | |
244 | - self.client.post(path, {"name": "sp5", "description_short": "sp short desc", "description_long": "sp long desc", | |
245 | - "public_visibility": "All", "quota_minimal": 10, "quota_nominal": 15, | |
246 | - "over_quota_duration": 5, "token": 3, | |
247 | - "users":"test@example.com,test2@example.com,test3@example.com" | |
248 | - }) | |
249 | - self.client.get(reverse("user_logout")) | |
250 | - # log in as user 1 | |
251 | - self.client.login(username=self.usr1,password="password123") | |
252 | - period = Period.objects.all().order_by('-id').first() | |
253 | - sp1 = ScientificProgram.objects.get(name="sp1") | |
254 | - path = reverse("edit_scientific_program_period",args=[sp1.id,period.id]) | |
255 | - self.assertEqual(SP_Period.objects.get(scientific_program=sp1,period=period).status,SP_Period.STATUSES_DRAFT) | |
256 | - self.client.post(path, {"name": "sp4", "description_short": "sp modified short desc", "description_long": "sp long desc", | |
257 | - "public_visibility": "All", "quota_minimal": 15, "quota_nominal": 20, | |
258 | - "over_quota_duration": 5, "token": 3, | |
259 | - "users":"test@example.com,test2@example.com" | |
260 | - }) | |
261 | - | |
262 | 603 | \ No newline at end of file |
604 | + print("--------- LIFE CYCLE START----------") | |
605 | + sp1 = self.create_dummies_SCP_and_SCP_Period("test",1) | |
606 | + sp2 = self.create_dummies_SCP_and_SCP_Period("test2",1) | |
607 | + sp3 = self.create_dummies_SCP_and_SCP_Period("test3",1) | |
608 | + sp3.scientific_program.is_auto_validated = True | |
609 | + sp3.scientific_program.save() | |
610 | + os.chdir("./scientific_program/") | |
611 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().submission_end_date + relativedelta(days=-10)),"%Y-%m-%d")): | |
612 | + # TAC Assignation | |
613 | + p = multiprocessing.Process(target= self.run_agent_SP,args=()) | |
614 | + p.daemon = True | |
615 | + p.start() | |
616 | + | |
617 | + time.sleep(10) | |
618 | + p.terminate() | |
619 | + for sp_period in SP_Period.objects.all().exclude(id=sp3.id): | |
620 | + # sp period should have referees | |
621 | + self.assertNotEqual(sp_period.referee1,None) | |
622 | + self.assertNotEqual(sp_period.referee2,None) | |
623 | + | |
624 | + | |
625 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().submission_end_date),"%Y-%m-%d")): | |
626 | + # Evaluation start | |
627 | + p = multiprocessing.Process(target= self.run_agent_SP,args=()) | |
628 | + p.daemon = True | |
629 | + p.start() | |
630 | + | |
631 | + time.sleep(10) | |
632 | + p.terminate() | |
633 | + for sp_period in SP_Period.objects.all().exclude(id=sp3.id): | |
634 | + # sp period should have referees | |
635 | + self.assertEqual(sp_period.status,SP_Period.STATUSES_SUBMITTED) | |
636 | + | |
637 | + # sp3 should be valid | |
638 | + self.assertEqual(SP_Period.objects.get(id=sp3.id).status,SP_Period.STATUSES_ACCEPTED) | |
639 | + self.assertEqual(SP_Period.objects.get(id=sp3.id).is_valid,SP_Period.IS_VALID_ACCEPTED) | |
640 | + | |
641 | + # log in as tac to vote | |
642 | + self.login_as_user(3) | |
643 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
644 | + post_data = { | |
645 | + | |
646 | + "name": sp1.scientific_program.name, | |
647 | + "description_short":"modified test", | |
648 | + "description_long":"modified test", | |
649 | + "science_theme" :sp1.scientific_program.science_theme, | |
650 | + "public_visibility":"Yes", | |
651 | + "quota_minimal":sp1.quota_minimal, | |
652 | + "quota_nominal":sp1.quota_nominal, | |
653 | + "over_quota_duration":sp1.over_quota_duration, | |
654 | + "token":sp1.token, | |
655 | + "vote_referee1":SP_Period.VOTES_YES, | |
656 | + "reason_referee1":"i agree", | |
657 | + "users":"", | |
658 | + } | |
659 | + response = self.client.post(path,post_data) | |
660 | + self.assertEqual(response.status_code,302) | |
661 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).vote_referee1,SP_Period.VOTES_YES) | |
662 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).reason_referee1,"i agree") | |
663 | + self.logout() | |
664 | + | |
665 | + # log in as tac 2 to vote | |
666 | + self.login_as_user(6) | |
667 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
668 | + post_data = { | |
669 | + | |
670 | + "name": sp1.scientific_program.name, | |
671 | + "description_short":"modified test", | |
672 | + "description_long":"modified test", | |
673 | + "science_theme" :sp1.scientific_program.science_theme, | |
674 | + "public_visibility":"Yes", | |
675 | + "quota_minimal":sp1.quota_minimal, | |
676 | + "quota_nominal":sp1.quota_nominal, | |
677 | + "over_quota_duration":sp1.over_quota_duration, | |
678 | + "token":sp1.token, | |
679 | + "vote_referee2":SP_Period.VOTES_YES, | |
680 | + "reason_referee2":"i agree", | |
681 | + "users":"", | |
682 | + } | |
683 | + response = self.client.post(path,post_data) | |
684 | + self.assertEqual(response.status_code,302) | |
685 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).vote_referee2,SP_Period.VOTES_YES) | |
686 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).reason_referee2,"i agree") | |
687 | + self.logout() | |
688 | + | |
689 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().unit_pi_validation_start_date),"%Y-%m-%d")): | |
690 | + # validation start | |
691 | + p = multiprocessing.Process(target= self.run_agent_SP,args=()) | |
692 | + p.daemon = True | |
693 | + p.start() | |
694 | + | |
695 | + time.sleep(10) | |
696 | + p.terminate() | |
697 | + for sp_period in SP_Period.objects.all().exclude(id=sp3.id): | |
698 | + # sp period should be evaluated | |
699 | + self.assertEqual(sp_period.status,SP_Period.STATUSES_EVALUATED) | |
700 | + # log in as unit pi | |
701 | + | |
702 | + self.login_as_user(2) | |
703 | + # valid sp1 | |
704 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp1.scientific_program.id,"id_period":sp1.period.id}) | |
705 | + post_data = { | |
706 | + "name": sp1.scientific_program.name, | |
707 | + "description_short":"modified test", | |
708 | + "description_long":"modified test", | |
709 | + "science_theme" :sp1.scientific_program.science_theme, | |
710 | + "public_visibility":"Yes", | |
711 | + "quota_minimal":sp1.quota_minimal, | |
712 | + "quota_nominal":sp1.quota_nominal, | |
713 | + "over_quota_duration":sp1.over_quota_duration, | |
714 | + "token":sp1.token, | |
715 | + "is_valid":SP_Period.IS_VALID_ACCEPTED, | |
716 | + "quota_allocated" : 10, | |
717 | + "token_allocated": 5, | |
718 | + "over_quota_duration_allocated": 5 | |
719 | + } | |
720 | + response = self.client.post(path,post_data) | |
721 | + self.assertEqual(response.status_code,302) | |
722 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).quota_allocated,10) | |
723 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).is_valid,SP_Period.IS_VALID_ACCEPTED) | |
724 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).token_allocated,5) | |
725 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).over_quota_duration_allocated,5) | |
726 | + | |
727 | + # reject sp 2 | |
728 | + path = reverse("edit_scientific_program_period",kwargs={"id_sp":sp2.scientific_program.id,"id_period":sp2.period.id}) | |
729 | + post_data = { | |
730 | + "name": sp2.scientific_program.name, | |
731 | + "description_short":"modified test", | |
732 | + "description_long":"modified test", | |
733 | + "science_theme" :sp2.scientific_program.science_theme, | |
734 | + "public_visibility":"Yes", | |
735 | + "quota_minimal":sp2.quota_minimal, | |
736 | + "quota_nominal":sp2.quota_nominal, | |
737 | + "over_quota_duration":sp2.over_quota_duration, | |
738 | + "token":sp2.token, | |
739 | + "is_valid":SP_Period.IS_VALID_REJECTED, | |
740 | + "quota_allocated" : 0, | |
741 | + "token_allocated": 0, | |
742 | + "over_quota_duration_allocated": 0 | |
743 | + } | |
744 | + response = self.client.post(path,post_data) | |
745 | + self.assertEqual(response.status_code,302) | |
746 | + self.assertEqual(SP_Period.objects.get(id=sp2.id).is_valid,SP_Period.IS_VALID_REJECTED) | |
747 | + self.assertEqual(SP_Period.objects.get(id=sp2.id).quota_allocated,0) | |
748 | + self.assertEqual(SP_Period.objects.get(id=sp2.id).token_allocated,0) | |
749 | + self.assertEqual(SP_Period.objects.get(id=sp2.id).over_quota_duration_allocated,0) | |
750 | + self.logout() | |
751 | + | |
752 | + | |
753 | + with patch('django.utils.timezone.now', return_value=datetime.strptime(str(Period.objects.next_period().notification_start_date),"%Y-%m-%d")): | |
754 | + # validation start | |
755 | + p = multiprocessing.Process(target= self.run_agent_SP,args=()) | |
756 | + p.daemon = True | |
757 | + p.start() | |
758 | + | |
759 | + time.sleep(10) | |
760 | + p.terminate() | |
761 | + self.assertEqual(SP_Period.objects.get(id=sp1.id).status,SP_Period.STATUSES_ACCEPTED) | |
762 | + self.assertEqual(SP_Period.objects.get(id=sp3.id).status,SP_Period.STATUSES_ACCEPTED) | |
763 | + self.assertEqual(SP_Period.objects.get(id=sp2.id).status,SP_Period.STATUSES_REJECTED) | ... | ... |
src/core/pyros_django/scientific_program/urls.py
... | ... | @@ -10,6 +10,7 @@ urlpatterns = [ |
10 | 10 | path('add_SP_with_template/<int:id_SP>', views.create_scientific_program_with_template,name='create_scientific_program_with_template'), |
11 | 11 | path('add_SP_Period/<int:id_SP>/', views.create_scientific_program_period,name='create_scientific_program_period'), |
12 | 12 | path('repropose_scientific_program/<int:id_SP>/<int:id_SP_Period>', views.create_scientific_program_period,name='repropose_scientific_program'), |
13 | + path('detail_scientific_program/<int:id>/', views.detail_scientific_program,name='detail_scientific_program'), | |
13 | 14 | path('detail_scientific_program_period/<int:id_sp>/<int:id_period>', views.detail_scientific_program_period,name='detail_scientific_program_period'), |
14 | 15 | path('edit_scientific_program_period/<int:id_sp>/<int:id_period>', views.edit_scientific_program_period,name='edit_scientific_program_period'), |
15 | 16 | path('delete_scientific_program_period/<int:id_sp>/<int:id_period>', views.delete_scientific_program_period,name='delete_scientific_program_period'), |
... | ... | @@ -18,7 +19,6 @@ urlpatterns = [ |
18 | 19 | path('associate_tac_to_scientific_program/<int:id_sp>/<int:id_period>/',views.associate_tac_to_scientific_program,name="associate_tac_to_scientific_program"), |
19 | 20 | path('scientific_program_list', views.scientific_program_list,name='scientific_program_list'), |
20 | 21 | path('own_scientific_program_list', views.own_scientific_program_list,name='own_scientific_program_list'), |
21 | - path('detail_scientific_program/<int:id>/', views.detail_scientific_program,name='detail_scientific_program'), | |
22 | 22 | path("list_evaluated_scientific_program",views.list_evaluated_scientific_program,name="list_evaluated_scientific_program"), |
23 | 23 | path("list_submitted_scientific_program",views.list_submitted_scientific_program,name="list_submitted_scientific_program"), |
24 | 24 | path("list_scientific_program_as_template",views.list_scientific_program_as_template,name="list_scientific_program_as_template"), |
... | ... | @@ -43,4 +43,5 @@ urlpatterns = [ |
43 | 43 | path("detail_science_theme/<int:id>/",views.detail_science_theme,name="detail_science_theme"), |
44 | 44 | path("edit_science_theme/<int:id>/",views.edit_science_theme,name="edit_science_theme"), |
45 | 45 | path("delete_science_theme/<int:id>/",views.delete_science_theme,name="delete_science_theme"), |
46 | + path("test_tac_auto",views.test_tac_auto,name="test_tac_auto") | |
46 | 47 | ] | ... | ... |
src/core/pyros_django/scientific_program/views.py
... | ... | @@ -16,7 +16,8 @@ from django.utils import timezone |
16 | 16 | from django.db.models import Q,F |
17 | 17 | from .functions import get_global_svg_timeline, get_svg_timeline, get_proposal_svg_timeline |
18 | 18 | from django.views.decorators.csrf import csrf_exempt |
19 | -import re | |
19 | +import matplotlib.pyplot as plt | |
20 | +import re,io,urllib,base64 | |
20 | 21 | |
21 | 22 | # Scientific program CRUD |
22 | 23 | |
... | ... | @@ -41,8 +42,7 @@ def index_scientific_program(request): |
41 | 42 | validation_periods = Period.objects.filter( |
42 | 43 | start_date__gte=today, submission_end_date__gt=today,unit_pi_validation_start_date__gte=today) |
43 | 44 | """ |
44 | - does_next_period_exist = Period.objects.filter( | |
45 | - start_date__gt=today).order_by("start_date").first() != None | |
45 | + does_next_period_exist = Period.objects.next_period() != None | |
46 | 46 | current_period = Period.objects.exploitation_period() |
47 | 47 | future_period = Period.objects.filter(start_date__gte=current_period.end_date).first() |
48 | 48 | previous_period = Period.objects.previous_periods().first() |
... | ... | @@ -50,6 +50,7 @@ def index_scientific_program(request): |
50 | 50 | validation_periods = Period.objects.validation_periods() |
51 | 51 | sp_to_be_evaluated = SP_Period.objects.filter(period__in=evaluation_periods,status__in=(SP_Period.STATUSES_SUBMITTED,SP_Period.STATUSES_EVALUATED)).count() > 0 |
52 | 52 | sp_to_be_validated = SP_Period.objects.filter(period__in=validation_periods,status__in=(SP_Period.STATUSES_EVALUATED,SP_Period.STATUSES_ACCEPTED,SP_Period.STATUSES_REJECTED)).count() > 0 |
53 | + does_next_period_need_tac_association = Period.objects.next_period() in Period.objects.submission_periods() | |
53 | 54 | # date_diff_today_end_of_property will give a value to make |
54 | 55 | # the date of today shift through the time line |
55 | 56 | if previous_period: |
... | ... | @@ -65,7 +66,7 @@ def index_scientific_program(request): |
65 | 66 | CAN_VIEW_ALL_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") |
66 | 67 | CAN_VIEW_EXPLOITATION_PERIOD = request.session.get("role") in ("Admin","Observer","Unit-PI","Unit-board") |
67 | 68 | CAN_CREATE_EXPLOITATION_PERIOD = request.session.get("role") in ("Admin","Unit-PI","Unit-board") |
68 | - CAN_ASSOCIATE_TAC_TO_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") | |
69 | + CAN_ASSOCIATE_TAC_TO_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") and does_next_period_need_tac_association | |
69 | 70 | svg = get_svg_timeline(previous_period,current_period,future_period) |
70 | 71 | global_svg = get_global_svg_timeline(previous_period,current_period,future_period) |
71 | 72 | #global_svg = get_global_svg_timeline(current_period,current_period,future_period) |
... | ... | @@ -79,7 +80,7 @@ def index_scientific_program(request): |
79 | 80 | "svg":svg, |
80 | 81 | "global_svg":global_svg, |
81 | 82 | "CAN_VALIDATE_SP": CAN_VALIDATE_SP, |
82 | - "CAN_EVALUATE": CAN_EVALUATE_SP, | |
83 | + "CAN_EVALUATE_SP": CAN_EVALUATE_SP, | |
83 | 84 | "CAN_SUBMIT_SP":CAN_SUBMIT_SP, |
84 | 85 | "CAN_VIEW_SP": CAN_VIEW_SP, |
85 | 86 | "CAN_VIEW_EXPLOITATION_PERIOD": CAN_VIEW_EXPLOITATION_PERIOD, |
... | ... | @@ -238,7 +239,8 @@ def list_drafted_scientific_program(request): |
238 | 239 | select_message = "Select an SP to associate TAC users to it for the incomming evaluation time" |
239 | 240 | none_message = f"There is no existing SP to be associated with TAC users for a new exploitation period" |
240 | 241 | non_autovalidated_sp = ScientificProgram.objects.exclude(is_auto_validated=True) |
241 | - sp_periods_without_tac = SP_Period.objects.filter(scientific_program__in=non_autovalidated_sp,period=Period.objects.submission_periods().first()).filter(Q(referee1=None)|Q(referee2=None)).order_by("scientific_program__science_theme") | |
242 | + #sp_periods_without_tac = SP_Period.objects.filter(scientific_program__in=non_autovalidated_sp,period=Period.objects.submission_periods().first()).filter(Q(referee1=None)|Q(referee2=None)).order_by("scientific_program__science_theme") | |
243 | + sp_periods_without_tac = SP_Period.objects.filter(scientific_program__in=non_autovalidated_sp,period=Period.objects.submission_periods().first()).order_by("scientific_program__science_theme") | |
242 | 244 | return render(request, "scientific_program/list_of_scientific_program_select.html", { |
243 | 245 | "select_message": select_message, |
244 | 246 | "none_message": none_message, |
... | ... | @@ -331,8 +333,10 @@ def list_submitted_scientific_program(request): |
331 | 333 | next_period = Period.objects.evaluation_periods().first() |
332 | 334 | none_message = f"There is no SP to be evaluated for next exploitation period {next_period}" |
333 | 335 | # Note : ~Q mean not query |
334 | - list_of_sp = SP_Period.objects.filter(Q(referee1=None) & ~Q(referee2=request.user) | ~Q( | |
335 | - referee1=request.user) & Q(referee2=None), status=SP_Period.STATUSES_SUBMITTED, period=next_period) | |
336 | + #list_of_sp = SP_Period.objects.filter(Q(referee1=None) & ~Q(referee2=request.user) | ~Q( | |
337 | + # referee1=request.user) & Q(referee2=None), status=SP_Period.STATUSES_SUBMITTED, period=next_period) | |
338 | + | |
339 | + list_of_sp = SP_Period.objects.filter(status=SP_Period.STATUSES_SUBMITTED, period=next_period) | |
336 | 340 | list_of_evaluated_sp = [] |
337 | 341 | for sp in list_of_sp: |
338 | 342 | if sp.referee1 == request.user or sp.referee2 == request.user: |
... | ... | @@ -390,6 +394,7 @@ def accept_sp_invitation(request, id_SP, id_period): |
390 | 394 | SP_Period=sp_period, email=current_user.email).delete() |
391 | 395 | return HttpResponseRedirect(reverse("detail_scientific_program_period", kwargs={"id_sp": id_SP, "id_period": id_period})) |
392 | 396 | return HttpResponseRedirect(reverse("index")) |
397 | + | |
393 | 398 | @login_required |
394 | 399 | @level_required("Admin", "Unit-PI", "Unit-board") |
395 | 400 | def associate_tac_to_scientific_program(request,id_sp,id_period): |
... | ... | @@ -443,7 +448,11 @@ def edit_scientific_program_period(request, id_sp, id_period): |
443 | 448 | list_of_emails = None |
444 | 449 | recipient_list = [] |
445 | 450 | if request.POST: |
451 | + if SPForm.is_valid() and sp_period.status == SP_Period.STATUSES_DRAFT: | |
452 | + SPForm.save() | |
446 | 453 | if SP_Period_form.is_valid(): |
454 | + if sp_period.status == SP_Period.STATUSES_DRAFT: | |
455 | + SP_period = SP_Period_form.save() | |
447 | 456 | # can send invitation until the end of exploitation |
448 | 457 | if request.POST.get("users") and today < period.end_date: |
449 | 458 | # Invite user to join the SP |
... | ... | @@ -482,8 +491,9 @@ def edit_scientific_program_period(request, id_sp, id_period): |
482 | 491 | recipient_list=[recipient], |
483 | 492 | fail_silently=False, |
484 | 493 | ) |
494 | + | |
485 | 495 | else: |
486 | - return render(request, 'scientific_program/scientific_program_detail_edit.html', { | |
496 | + return render(request, 'scientific_program/scientific_program_period_detail_edit.html', { | |
487 | 497 | 'id_sp': id_sp, |
488 | 498 | "id_period": id_period, |
489 | 499 | "SPForm": SPForm, |
... | ... | @@ -494,21 +504,8 @@ def edit_scientific_program_period(request, id_sp, id_period): |
494 | 504 | "sp_period": sp_period, |
495 | 505 | "error_message":error_message |
496 | 506 | }) |
497 | - SP_period = SP_Period_form.save() | |
498 | 507 | # save changes on user list |
499 | - if request.POST.getlist("user"): | |
500 | - for sp_period_user in users_informations: | |
501 | - if sp_period_user.id not in map(int, request.POST.getlist("user")): | |
502 | - sp_period_user.delete() | |
503 | - | |
504 | - for user in request.POST.getlist("user"): | |
505 | - user_instance = PyrosUser.objects.get(id=int(user)) | |
506 | - if user_instance not in users_informations: | |
507 | - SP_Period_User.objects.create( | |
508 | - SP_Period=sp_period, user=user_instance) | |
509 | - else: | |
510 | - SP_Period_User.objects.filter( | |
511 | - SP_Period=sp_period).delete() | |
508 | + | |
512 | 509 | return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) |
513 | 510 | if SP_Period_form.is_valid() and request.session.get("role") == "TAC" and sp_period.status != SP_Period.STATUSES_DRAFT: |
514 | 511 | # vote TAC |
... | ... | @@ -535,15 +532,16 @@ def edit_scientific_program_period(request, id_sp, id_period): |
535 | 532 | #sp_period.status = SP_Period.STATUSES_EVALUATED |
536 | 533 | sp_period.save() |
537 | 534 | return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) |
538 | - elif SP_Period_form.is_valid() and request.session.get("role") in ["Unit-PI", "Unit-board"] and sp_period.status in [SP_Period.STATUSES_EVALUATED, SP_Period.STATUSES_ACCEPTED, SP_Period.STATUSES_REJECTED]: | |
535 | + elif SP_Period_form.is_valid() and request.session.get("role") in ["Admin","Unit-PI", "Unit-board"] and sp_period.status in [SP_Period.STATUSES_EVALUATED, SP_Period.STATUSES_ACCEPTED, SP_Period.STATUSES_REJECTED]: | |
539 | 536 | #if request.POST.get("is_valid") != None: |
540 | 537 | #sp_period.status = request.POST.get("is_valid") |
541 | 538 | SP_Period_form.save() |
542 | - sp_period.save() | |
543 | 539 | return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) |
544 | 540 | else: |
545 | 541 | print(SP_Period_form.errors) |
546 | - return render(request, 'scientific_program/scientific_program_detail_edit.html', { | |
542 | + return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) | |
543 | + CAN_VIEW_TAC_VOTES = request.session.get("role") in ("Admin","Unit-PI","Unit-board") | |
544 | + return render(request, 'scientific_program/scientific_program_period_detail_edit.html', { | |
547 | 545 | 'id_sp': id_sp, |
548 | 546 | "id_period": id_period, |
549 | 547 | "SPForm": SPForm, |
... | ... | @@ -552,7 +550,8 @@ def edit_scientific_program_period(request, id_sp, id_period): |
552 | 550 | "users_of_sp_period": users_informations, |
553 | 551 | "SP_PeriodForm": SP_Period_form, |
554 | 552 | "sp_period": sp_period, |
555 | - "error_message":error_message | |
553 | + "error_message":error_message, | |
554 | + "CAN_VIEW_TAC_VOTES":CAN_VIEW_TAC_VOTES | |
556 | 555 | }) |
557 | 556 | |
558 | 557 | |
... | ... | @@ -588,7 +587,7 @@ def detail_scientific_program(request, id): |
588 | 587 | }) |
589 | 588 | |
590 | 589 | |
591 | -@level_required("Admin", "Unit-PI", "Observer", "TAC") | |
590 | +@level_required("Admin", "Unit-PI", "Observer") | |
592 | 591 | @login_required |
593 | 592 | def detail_scientific_program_period(request, id_sp, id_period): |
594 | 593 | scientific_program = get_object_or_404(ScientificProgram, pk=id_sp) |
... | ... | @@ -644,12 +643,11 @@ def delete_scientific_program(request, id_sp): |
644 | 643 | scientific_program = get_object_or_404(ScientificProgram, pk=id_sp) |
645 | 644 | sp_periods = SP_Period.objects.filter(scientific_program=scientific_program) |
646 | 645 | if sp_periods: |
647 | - for sp_period in sp_periods: | |
648 | - SP_Period_User.objects.filter(SP_Period=sp_period).delete() | |
649 | - SP_Period_Guest.objects.filter(SP_Period=sp_period).delete() | |
650 | - sp_periods.delete() | |
651 | - scientific_program.delete() | |
652 | - return HttpResponseRedirect(reverse('index_scientific_program')) | |
646 | + # can't delete | |
647 | + return HttpResponseRedirect(reverse("detail_scientific_program", args=[scientific_program.id ])) | |
648 | + else: | |
649 | + scientific_program.delete() | |
650 | + return HttpResponseRedirect(reverse('index_scientific_program')) | |
653 | 651 | |
654 | 652 | @level_required("Admin", "Unit-PI", "Observer") |
655 | 653 | @login_required() |
... | ... | @@ -658,6 +656,7 @@ def delete_scientific_program_period(request, id_sp, id_period): |
658 | 656 | sp_period = SP_Period.objects.get( |
659 | 657 | scientific_program=scientific_program, period=Period.objects.get(id=id_period)) |
660 | 658 | if sp_period.status == SP_Period.STATUSES_DRAFT: |
659 | + # Delete only if in draft status | |
661 | 660 | sp_period_users = SP_Period_User.objects.filter(SP_Period=sp_period) |
662 | 661 | sp_period_guests = SP_Period_Guest.objects.filter(SP_Period=sp_period) |
663 | 662 | sp_period_guests.delete() |
... | ... | @@ -1153,3 +1152,25 @@ def delete_science_theme(request,id): |
1153 | 1152 | return HttpResponseRedirect(reverse('science_theme_list')) |
1154 | 1153 | return HttpResponseRedirect(reverse('detail_science_theme',args=[science_theme.id])) |
1155 | 1154 | |
1155 | +def test_tac_auto(request): | |
1156 | + next_period = Period.objects.next_period() | |
1157 | + themes = ScienceTheme.objects.all() | |
1158 | + tac_users = PyrosUser.objects.filter(user_level__name="TAC") | |
1159 | + sp_id = SP_Period.objects.filter(period=next_period).filter(Q(referee1=None)|Q(referee2=None)).values("scientific_program") | |
1160 | + sp = ScientificProgram.objects.filter(id__in=sp_id).order_by("name") | |
1161 | + | |
1162 | + matrix = associate_tac_sp_auto(themes,tac_users,sp) | |
1163 | + fig,ax = plt.subplots(1,1) | |
1164 | + plt.xlabel("scientific programs") | |
1165 | + plt.ylabel("Number of available TAC users") | |
1166 | + plt.pcolor(matrix,edgecolors='k', linewidths=2) | |
1167 | + plt.colorbar(ticks=[0,1]) | |
1168 | + ax.set_xticklabels(sp.values_list("name",flat=True)) | |
1169 | + plt.yticks([i for i in range(len(tac_users))],tac_users.values_list("last_name",flat=True),rotation="45") | |
1170 | + #ax.set_yticklabels() | |
1171 | + buf = io.BytesIO() | |
1172 | + fig.savefig(buf,format="png") | |
1173 | + buf.seek(0) | |
1174 | + string = base64.b64encode(buf.read()) | |
1175 | + uri = urllib.parse.quote(string) | |
1176 | + return render(request,"scientific_program/test_tac_auto.html",{"data":uri}) | |
1156 | 1177 | \ No newline at end of file | ... | ... |
src/core/pyros_django/user_manager/tests.py
... | ... | @@ -292,7 +292,8 @@ class UserManagerTests(TestCase): |
292 | 292 | |
293 | 293 | u2 = PyrosUser.objects.get(username="u2@test.fr") |
294 | 294 | |
295 | - response = self.client.post(reverse("login_validation"),{"email":u2.email,"password":"password123"}) | |
295 | + response = self.client.post(reverse("login_validation"),{"email":u2.email,"password":"password123"}) | |
296 | + | |
296 | 297 | self.assertEqual(response.status_code,302) |
297 | 298 | response = self.client.get(reverse("users")) |
298 | 299 | self.assertEqual(response.status_code,200) | ... | ... |