Commit e00964fc414d616fa2b38368ada7c224bb2de483

Authored by Alexis Koralewski
1 parent 47667b81
Exists in dev

new version of timeline, fixing SP repropose, add copy of users when reproposing…

… a SP for a new period, removing guest field as required
src/core/pyros_django/scientific_program/functions.py
... ... @@ -35,7 +35,7 @@ def get_svg_timeline(previous_period,current_period,future_period):
35 35 <text class="date_text" x="25%" y="45%" fontsize="2em" fill="black">{current_period.start_date.strftime("%d/%m/%Y")}</text>\
36 36 <text x="32%" y="37%" fontsize="2em" fill="red">Exploitation</text>'
37 37 #<text x="40%" y="45%" fontsize="2em" fill="black">{current_period.end_date.strftime("%d/%m/%Y")}</text>\
38   - data_access_current_period = f'<rect id="exploit_current_period" x=45% y=30% width="20%" height="10%" style="fill:rgb(192,192,220);stroke-width:1"></rect>\
  38 + data_access_current_period = f'<rect id="data_access_current_period" x=45% y=30% width="20%" height="10%" style="fill:rgb(192,192,220);stroke-width:1"></rect>\
39 39 <text class="date_text" x="45%" y="45%" fontsize="2em" fill="black">{current_period.end_date.strftime("%d/%m/%Y")}</text>\
40 40 <text class="date_text" x="60%" y="45%" fontsize="2em" fill="black">{current_period.property_of_data_end_date.strftime("%d/%m/%Y")}</text>\
41 41 <text x="52%" y="37%" fontsize="2em" fill="white">Private data access</text>'
... ... @@ -58,49 +58,56 @@ def get_svg_timeline(previous_period,current_period,future_period):
58 58 return svg_content
59 59  
60 60 def get_global_svg_timeline(previous_period,current_period,future_period):
61   - svg_content = '<svg id="global_svg" width="80vw" height="30vh">'
  61 + svg_content = '<svg id="global_svg" width="80vw" height="25vh">'
62 62 svg_previous_period = ""
63 63 svg_current_period = ""
64 64 svg_future_period = ""
65 65  
66 66 if previous_period is not None:
67 67 previous_period_svg = f'<rect id="previous_period" x="5%" y=0% width="20%" height="10%" style="fill:rgb(230, 244, 177);stroke-width:1"></rect>\
68   - <text class="date_text" x="5%" y="15%" fontsize="2em" fill="black">{previous_period.start_date.strftime("%d/%m/%Y")}</text>\
69   - <text class="date_text" x="20%" y="15%" fontsize="2e" fill="black">{previous_period.end_date.strftime("%d/%m/%Y")}</text>'
70   - svg_content+=f'<text x="0%" y="7%" fontsize="2em" fill="black">Previous(P{current_period.id})</text>' + previous_period_svg
  68 + <rect id="previous_period" x="25%" y=0 width="40%" height="10%" style="fill:blue; stroke-width:1"></rect>\
  69 + <text x="15%" y="7%" fontsize="2em" fill="black">Exploitation</text>'
  70 + #<text class="date_text" x="5%" y="15%" fontsize="2em" fill="black">{previous_period.start_date.strftime("%d/%m/%Y")}</text>\
  71 + #<text class="date_text" x="20%" y="15%" fontsize="2e" fill="black">{previous_period.end_date.strftime("%d/%m/%Y")}</text>\
  72 + svg_content+=f'<text x="0%" y="7%" fontsize="2em" fill="black">Period(P{current_period.id})</text>' + previous_period_svg
71 73  
72 74 if current_period is not None:
73 75 current_period_svg = f'<rect id="current_period" x="25%" y=30% width="20%" height="10%" style="fill:rgb(230, 244, 177);stroke-width:1"></rect>\
74   - <text class="date_text" x="25%" y="45%" fontsize="2em" fill="black">{current_period.start_date.strftime("%d/%m/%Y")}</text>\
75   - <text class="date_text" x="40%" y="45%" fontsize="2e" fill="black">{current_period.end_date.strftime("%d/%m/%Y")}</text>'
76   - svg_content+=f'<text x="0%" y="37%" fontsize="2em" fill="black">Current(P{current_period.id})</text>' + current_period_svg
  76 + <rect id="current_period" x="5%" y=30% width="20%" height="10%" style="fill:silver;stroke-width:1"></rect>\
  77 + <rect id="current_period" x=45% y=30% width="20%" height="10%" style="fill:rgb(192,192,220);stroke-width:1"></rect>\
  78 + <text x="32%" y="37%" fontsize="2em" fill="red">Exploitation</text>'
  79 + #<text class="date_text" x="25%" y="45%" fontsize="2em" fill="black">{current_period.start_date.strftime("%d/%m/%Y")}</text>\
  80 + #<text class="date_text" x="40%" y="45%" fontsize="2e" fill="black">{current_period.end_date.strftime("%d/%m/%Y")}</text>\
  81 + svg_content+=f'<text x="0%" y="37%" fontsize="2em" fill="black">Period (P{current_period.id})</text>' + current_period_svg
77 82  
78 83 if future_period is not None:
79   - future_period_svg = f'<rect id="future_period" x="45%" y=60% width="20%" height="10%" style="fill:rgb(230, 244, 177);stroke-width:1"></rect>\
80   - <text class="date_text" x="45%" y="75%" fontsize="2em" fill="black">{future_period.start_date.strftime("%d/%m/%Y")}</text>\
81   - <text class="date_text" x="60%" y="75%" fontsize="2e" fill="black">{future_period.end_date.strftime("%d/%m/%Y")}</text>'
82   - svg_content+=f'<text x="0%" y="67%" fontsize="2em" fill="black">Future(P{future_period.id})</text>' + future_period_svg
  84 + future_period_svg = f'<rect class="future_period" x="45%" y=60% width="20%" height="10%" style="fill:rgb(230, 244, 177);stroke-width:1"></rect>\
  85 + <rect class="future_period" x="5%" y=60% width="40%" height="10%" style="fill:silver;stroke-width:1"></rect>\
  86 + <text x="52%" y="67%" fontsize="2em" fill="black">Exploitation</text>'
  87 + #<text class="date_text" x="45%" y="75%" fontsize="2em" fill="black">{future_period.start_date.strftime("%d/%m/%Y")}</text>\
  88 + #<text class="date_text" x="60%" y="75%" fontsize="2e" fill="black">{future_period.end_date.strftime("%d/%m/%Y")}</text>\
  89 + svg_content+=f'<text x="0%" y="67%" fontsize="2em" fill="black">Period(P{future_period.id})</text>' + future_period_svg
83 90  
84 91 return svg_content
85 92  
86 93 def get_proposal_svg_timeline(period):
87   - proposal_svg = f'<svg id="proposal_detail" width="80vw" height="30vh"> <rect x="5%" y=5% width="20%" height="10%" style="fill:silver;stroke-width:1"></rect>\
  94 + proposal_svg = f'<svg id="proposal_detail" width="80vw" height="30vh"> <rect x="5%" y=5% width="19.9%" height="10%" style="fill:silver;stroke-width:1"></rect>\
88 95 <text class="date_text" x="5%" y="35%" fontsize="2em" fill="black">{period.submission_start_date.strftime("%d/%m/%Y")}</text>\
89 96 <text x="12%" y="12%" fontsize="2em" fill="black">Proposal submission</text>'
90 97  
91   - proposal_svg += f'<rect id="proposal_detail" x="25%" y=5% width="20%" height="10%" style="fill:silver;stroke-width:1"></rect>\
  98 + proposal_svg += f'<rect id="proposal_detail" x="25%" y=5% width="19.9%" height="10%" style="fill:silver;stroke-width:1"></rect>\
92 99 <text class="date_text" x="25%" y="35%" fontsize="2em" fill="black">{period.submission_end_date.strftime("%d/%m/%Y")}</text>\
93 100 <text x="32%" y="12%" fontsize="2em" fill="black">Evaluation</text>'
94 101  
95   - proposal_svg += f'<rect id="proposal_detail" x="45%" y=5% width="20%" height="10%" style="fill:silver;stroke-width:1"></rect>\
  102 + proposal_svg += f'<rect id="proposal_detail" x="45%" y=5% width="19.9%" height="10%" style="fill:silver;stroke-width:1"></rect>\
96 103 <text class="date_text" x="45%" y="35%" fontsize="2em" fill="black">{period.unit_pi_validation_start_date.strftime("%d/%m/%Y")}</text>\
97 104 <text x="52%" y="12%" fontsize="2em" fill="black">Validation</text>'
98 105  
99 106 information_start_date = period.start_date + relativedelta(days=-10)
100   - proposal_svg += f'<rect id="proposal_detail" x="65%" y=5% width="20%" height="10%" style="fill:silver;stroke-width:1"></rect>\
  107 + proposal_svg += f'<rect id="proposal_detail" x="65%" y=5% width="19.9%" height="10%" style="fill:silver;stroke-width:1"></rect>\
101 108 <text class="date_text" x="65%" y="35%" fontsize="2em" fill="black">{information_start_date.strftime("%d/%m/%Y")}</text>\
102 109 <text class="date_text" x="80%" y="35%" fontsize="2em" fill="black">{period.start_date.strftime("%d/%m/%Y")}</text>\
103   - <text x="72%" y="12%" fontsize="2em" fill="black">Information</text>'
  110 + <text x="72%" y="12%" fontsize="2em" fill="black">Notification</text>'
104 111 proposal_svg +=f'<text x="0%" y="12%" fontsize="2em" fill="black">P{period.id}</text>'
105 112 proposal_svg += "</svg>"
106 113 return proposal_svg
107 114 \ No newline at end of file
... ...
src/core/pyros_django/scientific_program/templates/scientific_program/create_scientific_program_period.html
... ... @@ -29,11 +29,13 @@
29 29 {% endif %}
30 30  
31 31 {% endfor %}
32   - <label> Invite user(s) with their email adress: (list them separated by a ","):</label>
33   - <div class="fieldWrapper">
34   - <textarea id="users" name="users">{{ list_of_emails|default_if_none:"" }}</textarea>
35   - {{ error_message }}
36   - </div>
  32 + {% if not is_sp_reproposed %}
  33 + <label> Invite user(s) with their email adress: (list them separated by a ","):</label>
  34 + <div class="fieldWrapper">
  35 + <textarea id="users" name="users">{{ list_of_emails|default_if_none:"" }}</textarea>
  36 + {{ error_message }}
  37 + </div>
  38 + {% endif %}
37 39 {% comment %}
38 40  
39 41 <div>
... ...
src/core/pyros_django/scientific_program/templates/scientific_program/index.html
... ... @@ -349,7 +349,7 @@ p{
349 349 <a href="{% url 'list_scientific_program_period_repropose' %}">Submit again an existing SP but for a new Exploitation Period (new instance)</a>
350 350 </li>
351 351 {% endif %}
352   - {% if CAN_VIEW_SP %}
  352 + {% if CAN_VIEW_SP and not CAN_VIEW_ALL_SP %}
353 353 <p> SP list </p>
354 354 <li>
355 355 <a href="{% url 'own_scientific_program_list' %}">View my own submitted SP(s)</a>
... ... @@ -404,7 +404,7 @@ $(&#39;#current_period&#39;).click( function(){
404 404 $("#detail_proposal_period").hide();
405 405 });
406 406  
407   -$('#future_period').click( function(){
  407 +$('.future_period').click( function(){
408 408 console.log('future_period');
409 409 $("#detail_svg").toggle();
410 410 $("#detail_previous_period").hide();
... ...
src/core/pyros_django/scientific_program/templates/scientific_program/period_detail.html
... ... @@ -42,10 +42,17 @@
42 42 {% endwith %}
43 43 </tr>
44 44 {% endfor %}
  45 + {% if not sp_of_user %}
  46 + {% for sp_period in sp_periods %}
  47 + {% if sp_period.scientific_program.sp_pi == request.user %}
  48 + <td><a href="{% url 'detail_scientific_program_period' id_sp=sp_period.scientific_program.id id_period=period.id %}">{{ sp_period.scientific_program.name }}</a></td>
  49 + {% endif %}
  50 + {% endfor %}
  51 + {% endif %}
45 52  
46 53 {% elif USER_LEVEL|ifinlist:"Admin,Unit-PI,TAC,Unit board" or is_period_public %}
47   - {{ scientific_programs }}
48   - {% for sp in scientific_programs %}
  54 +
  55 + {% for sp_period in sp_periods %}
49 56 <tr>
50 57 <td><a href="{% url 'detail_scientific_program_period' id_sp=sp_period.scientific_program.id id_period=period.id %}">{{ sp_period.scientific_program.name }}</a></td>
51 58 <td>{{ sp.description_short }}</td>
... ...
src/core/pyros_django/scientific_program/templates/scientific_program/scientific_program_detail.html
... ... @@ -11,44 +11,46 @@
11 11 <p><strong>Institute: </strong><a href=" {% url 'detail_institute' scientific_program.institute.id %}">{{ scientific_program.institute }}</a></p>
12 12 <p><strong>SP PI: </strong><a href=" {% url 'user_detail' sp_pi.id %}">{{ sp_pi.username }}</a></p>
13 13 {% if sp_periods %}
14   - <table class="table table-bordered table-hover table-striped tablesorter" style="font-family: 'Montserra', sans-serif;">
15   - <thead>
16   - <tr>
17   - <th>Periods <i class="fa fa-sort"></i></th>
18   - </tr>
19   - </thead>
20   - <tbody>
21   - {% for sp_period in sp_periods %}
22   - <tr><td><a href=" {% url 'detail_scientific_program_period' id_sp=scientific_program.id id_period=sp_period.period.pk %}" >{{ sp_period.period }} </a> </td></tr>
23   - {% endfor %}
24   - </tbody>
25   - </table></p>
  14 + <table class="table table-bordered table-hover table-striped tablesorter" style="font-family: 'Montserra', sans-serif;">
  15 + <thead>
  16 + <tr>
  17 + <th>Periods <i class="fa fa-sort"></i></th>
  18 + </tr>
  19 + </thead>
  20 + <tbody>
  21 + {% for sp_period in sp_periods %}
  22 + <tr><td><a href=" {% url 'detail_scientific_program_period' id_sp=scientific_program.id id_period=sp_period.period.pk %}" >{{ sp_period.period }} </a> </td></tr>
  23 + {% endfor %}
  24 + </tbody>
  25 + </table></p>
26 26 {% endif %}
27 27  
28   - {% if USER_LEVEL|ifinlist:"Admin,Unit-PI" or request.user == sp_pi and USER_LEVEL == "Observer" %}
29   - {% if CAN_REPROPOSE_SP %}
30   - <a class="btn btn-info" href="{% url 'create_scientific_program_period' id_SP=scientific_program.id %}"> Repropose this SP for a new exploitation period </a>
31   - {% endif %}
32   - {% if sp_periods == None %}
33   - <button type="button" class="btn btn-danger open-modal" id="open_modal_delete">Delete</button>
34   - {# start of modal #}
35   - <div id="modal_delete">
36   - <span id="close_modal_delete" class="close">x</span>
  28 + {% if CAN_REPROPOSE_SP %}
  29 + {% with sp_periods|first as last_sp_period %}
  30 + {% if last_sp_period %}
  31 + <a class="btn btn-info" href="{% url 'repropose_scientific_program' id_SP=scientific_program.id id_SP_Period=last_sp_period.period.id %}"> Repropose this SP for a new exploitation period </a>
  32 + {% endif %}
  33 + {% endwith %}
  34 + {% endif %}
  35 + {% if CAN_DELETE_SP %}
  36 + <button type="button" class="btn btn-danger open-modal" id="open_modal_delete">Delete</button>
  37 + {# start of modal #}
  38 + <div id="modal_delete">
  39 + <span id="close_modal_delete" class="close">x</span>
  40 +
  41 + <form class="modal-content" action="{% url "delete_scientific_program" scientific_program.id %}" method="post">
  42 + {% csrf_token %}
  43 + <div class="container">
  44 + <h1> Delete Account </h1>
  45 + <p>Are you sure you want to delete this scientific program ?</p>
37 46  
38   - <form class="modal-content" action="{% url "delete_scientific_program" scientific_program.id %}" method="post">
39   - {% csrf_token %}
40   - <div class="container">
41   - <h1> Delete Account </h1>
42   - <p>Are you sure you want to delete this scientific program ?</p>
43   -
44   - <div class="clearfix">
45   - <button type="button" class="btn btn-info" id="cancel_modal_delete_btn">Cancel</button>
46   - <input class="btn btn-danger delete_modal_delete_btn" type="submit" value="Delete scientific program">
47   - </div>
  47 + <div class="clearfix">
  48 + <button type="button" class="btn btn-info" id="cancel_modal_delete_btn">Cancel</button>
  49 + <input class="btn btn-danger delete_modal_delete_btn" type="submit" value="Delete scientific program">
48 50 </div>
49   - </form>
50   - </div>
51   - {% endif %}
  51 + </div>
  52 + </form>
  53 + </div>
52 54 {% endif %}
53 55 {# end of modal #}
54 56 <a href="{% url "index_scientific_program" %}" class="btn btn-info" role="button">Return to index of scientific programs</a>
... ...
src/core/pyros_django/scientific_program/templates/scientific_program/scientific_program_period_detail.html
... ... @@ -18,23 +18,27 @@
18 18 <p><strong>Nominal quota: </strong>{{ sp_period.quota_nominal }}</p>
19 19 <p><strong>Over quota duration: </strong>{{ sp_period.over_quota_duration }}</p>
20 20 <p><strong>Token: </strong>{{ sp_period.token }}</p>
21   - {% if sp_period.referee1 != None %}
22   - <p><strong>Vote referee 1: </strong>{{ sp_period.vote_referee1 }} </p> <p> <strong> Reason: </strong> {{ sp_period.reason_referee1 }} </p> <p> <strong> Referee 1: </strong>{{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p>
23   - {% endif %}
24   - {% if sp_period.referee2 != None %}
25   - <p><strong>Vote referee 2: </strong>{{ sp_period.vote_referee1 }} </p> <p> <strong> Reason: </strong> {{ sp_period.reason_referee1 }} </p> <p> <strong> Referee 2: </strong> {{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p>
  21 + {% if CAN_VIEW_TAC_VOTES %}
  22 + {% if sp_period.referee1 != None %}
  23 + <p><strong>Vote referee 1: </strong>{{ sp_period.vote_referee1 }} </p> <p> <strong> Reason: </strong> {{ sp_period.reason_referee1 }} </p> <p> <strong> Referee 1: </strong>{{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p>
  24 + {% endif %}
  25 + {% if sp_period.referee2 != None %}
  26 + <p><strong>Vote referee 2: </strong>{{ sp_period.vote_referee1 }} </p> <p> <strong> Reason: </strong> {{ sp_period.reason_referee1 }} </p> <p> <strong> Referee 2: </strong> {{ sp_period.referee1.first_name }} {{ sp_period.referee1.last_name}} </p>
  27 + {% endif %}
26 28 {% endif %}
27 29 {% if sp_period.status in "Accepted,Rejected" %}
28 30 <p><strong>Quota allocated: </strong>{{ sp_period.quota_allocated }}</p>
29 31 <p><strong>Over quota duration allocated: </strong>{{ sp_period.over_quota_duration_allocated }}</p>
30 32 <p><strong>Token allocated: </strong>{{ sp_period.token_allocated }}</p>
31 33 {% endif %}
32   - <p><strong>Guests: </strong></p>
33   - <ul>
34   - {% for guest in guests %}
35   - <li><p> {{ guest }} </p></li>
36   - {% endfor %}
37   - </ul>
  34 + {% if guests %}
  35 + <p><strong>Guests: </strong></p>
  36 + <ul>
  37 + {% for guest in guests %}
  38 + <li><p> {{ guest }} </p></li>
  39 + {% endfor %}
  40 + </ul>
  41 + {% endif %}
38 42 <table class="table table-bordered table-hover table-striped tablesorter" style="font-family: 'Montserra', sans-serif;">
39 43 <thead>
40 44 <tr>
... ... @@ -61,7 +65,7 @@
61 65 <div id="modal_delete">
62 66 <span id="close_modal_delete" class="close">x</span>
63 67  
64   - <form class="modal-content" action="{% url "delete_scientific_program" scientific_program.id %}" method="post">
  68 + <form class="modal-content" action="{% url "delete_scientific_program_period" scientific_program.id sp_period.period.id %}" method="post">
65 69 {% csrf_token %}
66 70 <div class="container">
67 71 <h1> Delete Account </h1>
... ...
src/core/pyros_django/scientific_program/urls.py
... ... @@ -13,7 +13,8 @@ urlpatterns = [
13 13 path('proposal_list', views.proposal_list,name='proposal_list'),
14 14 path('detail_scientific_program_period/<int:id_sp>/<int:id_period>', views.detail_scientific_program_period,name='detail_scientific_program_period'),
15 15 path('edit_scientific_program_period/<int:id_sp>/<int:id_period>', views.edit_scientific_program_period,name='edit_scientific_program_period'),
16   - path('delete_scientific_program/<int:id>/', views.delete_proposal,name='delete_scientific_program'),
  16 + path('delete_scientific_program_period/<int:id_sp>/<int:id_period>', views.delete_scientific_program_period,name='delete_scientific_program_period'),
  17 + path('delete_scientific_program/<int:id_sp>', views.delete_scientific_program,name='delete_scientific_program'),
17 18 path('submit_proposal/<int:id>/',views.submit_proposal,name="submit_proposal"),
18 19 path('scientific_program_list', views.scientific_program_list,name='scientific_program_list'),
19 20 path('own_scientific_program_list', views.own_scientific_program_list,name='own_scientific_program_list'),
... ...
src/core/pyros_django/scientific_program/views.py
... ... @@ -100,6 +100,7 @@ def create_scientific_program_period(request,id_SP,id_SP_Period=None):
100 100 SP = ScientificProgram.objects.get(id=id_SP)
101 101 today = timezone.now().date()
102 102 next_periods = Period.objects.filter(submission_start_date__lte=today,submission_end_date__gt=today, start_date__gt=today)
  103 + is_sp_reproposed = False
103 104 if SP.sp_pi != request.user:
104 105 HttpResponseRedirect(reverse("index_scientific_program"))
105 106 SP_Period_form = SP_PeriodForm()
... ... @@ -114,12 +115,13 @@ def create_scientific_program_period(request,id_SP,id_SP_Period=None):
114 115 period_id_to_be_excluded = SP_Period.objects.filter(scientific_program=sp_period_template.scientific_program).values_list("period",flat=True)
115 116 next_periods = Period.objects.filter(submission_start_date__lte=today,submission_end_date__gt=today, start_date__gt=today).exclude(id__in=period_id_to_be_excluded)
116 117 SP_Period_form = SP_PeriodForm(initial=initial_data)
  118 + is_sp_reproposed = True
117 119 #next_period = Period.objects.filter(
118 120 # start_date__gt=today).order_by("start_date").first()
119 121 error = False
120 122 error_message = ""
121 123 list_of_emails = None
122   -
  124 + recipient_list = []
123 125 if request.POST:
124 126 SP_Period_form = SP_PeriodForm(request.POST)
125 127 if SP_Period_form.is_valid():
... ... @@ -129,40 +131,41 @@ def create_scientific_program_period(request,id_SP,id_SP_Period=None):
129 131 user_instance = PyrosUser.objects.get(id=int(user))
130 132 SP_Period_User.objects.create(SP_Period=sp_period,user=user_instance,is_SP_PI=False)
131 133 """
132   - if request.POST.get("users"):
  134 + if not is_sp_reproposed and request.POST.get("users"):
133 135 regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
134 136 list_of_emails = request.POST.get("users").strip()
135   - recipient_list = []
  137 +
136 138 for email in list_of_emails.split(","):
137 139 if re.fullmatch(regex, email):
138 140 recipient_list.append(email)
139 141 else:
140 142 error = True
141 143 error_message = f"Invalid mail for {email}"
142   - if not error:
143   - institute = request.user.institute
144   - selected_period = Period.objects.get(id=request.POST.get("period"))
145   -
146   - current_sp = ScientificProgram.objects.get(id=id_SP)
147   - sp_period = SP_Period_form.save(commit=False)
148   - sp_period.period = selected_period
149   - #sp_period.period = next_period
150   - sp_period.scientific_program = current_sp
151   - sp_period.save()
152   -
153   - sp_pi_user = PyrosUser.objects.get(id=request.user.id)
154   - #SP_Period_User.objects.create(
155   - # SP_Period=sp_period, user=sp_pi_user, is_SP_PI=True)
156   - domain = settings.DEFAULT_DOMAIN
157   - url = f"{domain}{reverse('sp_register',args=(current_sp.pk,sp_period.period.pk))}"
158   - mail_subject = '[PyROS CC] New registration to an observing proposal'
159   - mail_message = (f"Hi,\n\nYou were invited to join an observing proposal that as been submitted using PyROS.\n"
160   - f"The name of the proposal is {current_sp.name} and his PI is {sp_pi_user.first_name} {sp_pi_user.last_name}.\n"
161   - f"To accept this invitation, click on the following link : {url}\n"
162   - "You might be asked to login first and will be redirected to the proposal page.\n"
163   - "If the redirection doesn't work, click again on the link after you've logged in.\n"
164   - "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."
165   - "\n\nCordially,\n\nPyROS Control Center")
  144 + if not error:
  145 + institute = request.user.institute
  146 + selected_period = Period.objects.get(id=request.POST.get("period"))
  147 +
  148 + current_sp = ScientificProgram.objects.get(id=id_SP)
  149 + sp_period = SP_Period_form.save(commit=False)
  150 + sp_period.period = selected_period
  151 + #sp_period.period = next_period
  152 + sp_period.scientific_program = current_sp
  153 + sp_period.save()
  154 +
  155 + sp_pi_user = PyrosUser.objects.get(id=request.user.id)
  156 + #SP_Period_User.objects.create(
  157 + # SP_Period=sp_period, user=sp_pi_user, is_SP_PI=True)
  158 + domain = settings.DEFAULT_DOMAIN
  159 + url = f"{domain}{reverse('sp_register',args=(current_sp.pk,sp_period.period.pk))}"
  160 + mail_subject = '[PyROS CC] New registration to an observing proposal'
  161 + mail_message = (f"Hi,\n\nYou were invited to join an observing proposal that as been submitted using PyROS.\n"
  162 + f"The name of the proposal is {current_sp.name} and his PI is {sp_pi_user.first_name} {sp_pi_user.last_name}.\n"
  163 + f"To accept this invitation, click on the following link : {url}\n"
  164 + "You might be asked to login first and will be redirected to the proposal page.\n"
  165 + "If the redirection doesn't work, click again on the link after you've logged in.\n"
  166 + "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."
  167 + "\n\nCordially,\n\nPyROS Control Center")
  168 + if len(recipient_list) > 0:
166 169 for recipient in recipient_list:
167 170 guest = SP_Period_Guest.objects.create(
168 171 SP_Period=sp_period, email=recipient)
... ... @@ -173,13 +176,21 @@ def create_scientific_program_period(request,id_SP,id_SP_Period=None):
173 176 recipient_list=[recipient],
174 177 fail_silently=False,
175 178 )
176   - return HttpResponseRedirect(reverse("own_scientific_program_list"))
  179 + if is_sp_reproposed:
  180 + previous_sp_periods = SP_Period.objects.filter(scientific_program=current_sp,period__lt=selected_period)
  181 + users_of_previous_period_for_sp = SP_Period_User.objects.filter(SP_Period__in=previous_sp_periods).values_list("user",flat=True)
  182 + for user in users_of_previous_period_for_sp:
  183 + SP_Period_User.objects.create(SP_Period=sp_period,user=PyrosUser.objects.get(id=user))
  184 + return HttpResponseRedirect(reverse("detail_scientific_program",args=[current_sp.id]))
  185 +
  186 + #return HttpResponseRedirect(reverse("own_scientific_program_list"))
177 187 return render(request, "scientific_program/create_scientific_program_period.html", {
178 188 "scientific_program":SP,
179 189 "SP_PeriodForm": SP_Period_form,
180 190 "error_message": error_message,
181 191 "list_of_emails": list_of_emails,
182   - "next_periods": next_periods
  192 + "next_periods": next_periods,
  193 + "is_sp_reproposed":is_sp_reproposed
183 194 })
184 195  
185 196  
... ... @@ -209,18 +220,27 @@ def create_scientific_program_with_template(request, id_SP):
209 220 def list_scientific_program_period_repropose(request):
210 221 select_message = "Submit again an existing SP but for a new exploitation period (instance)"
211 222 none_message = f"There is no existing SP to be submitted for a new exploitation period"
212   - # Note : ~Q mean not query
213 223 sp_where_user_is_sp_pi = ScientificProgram.objects.filter(sp_pi=request.user)
  224 + if sp_where_user_is_sp_pi is None:
  225 + return HttpResponseRedirect(reverse("index_scientific_program"))
  226 + sp_period_of_user = SP_Period.objects.filter(scientific_program__in=sp_where_user_is_sp_pi)
  227 + if not sp_period_of_user and sp_where_user_is_sp_pi is None:
  228 + return HttpResponseRedirect(reverse("index_scientific_program"))
214 229 #sp_of_user = SP_Period_User.objects.filter(user=request.user.id).values_list("SP_Period",flat=True)
215 230 #sp_period_of_user = SP_Period.objects.filter(scientific_program__in=sp_where_user_is_sp_pi,status=SP_Period.STATUSES_ACCEPTED).values("scientific_program","id").annotate("scientific_program")
216 231 #sp_period_active_of_user = SP_Period.objects.filter(scientific_program__in=sp_where_user_is_sp_pi,period=Period.objects.currently_active_period())
217 232 sp_period_of_user = []
  233 + today = timezone.now().date()
  234 + next_proposal_periods = Period.objects.filter(submission_start_date__lte=today,submission_end_date__gt=today, start_date__gt=today).values_list("id",flat=True)
218 235 for sp in sp_where_user_is_sp_pi:
219   - max_period_of_sp = SP_Period.objects.filter(scientific_program=sp).aggregate(Max("period"))["period__max"]
220   - if max_period_of_sp != None:
221   - sp_period_of_user.append(SP_Period.objects.get(scientific_program=sp,period=max_period_of_sp))
  236 + is_sp_associated_with_period = SP_Period.objects.filter(scientific_program=sp).count() > 0
  237 + if is_sp_associated_with_period:
  238 + # get latest period that has been associated with that SP
  239 + max_period_of_sp = SP_Period.objects.filter(scientific_program=sp).aggregate(Max("period"))["period__max"]
  240 + if max_period_of_sp not in next_proposal_periods:
  241 + # this SP can be reproposed for a next proposal period
  242 + sp_period_of_user.append(SP_Period.objects.get(scientific_program=sp,period=max_period_of_sp))
222 243 #sp_period_active_of_user = SP_Period.objects.filter(scientific_program__in=sp_where_user_is_sp_pi).annotate(Max("period")).distinct("scientific_program_id")
223   - print(sp_period_of_user)
224 244 list_of_sp = []
225 245 are_there_only_sp = False
226 246 # SP_Period where user is a SP_PI
... ... @@ -423,13 +443,18 @@ def detail_scientific_program(request, id):
423 443  
424 444 if request.session.get("role") == "Observer" and sp_pi != request.user and not SP_Period_User.objects.filter(SP_Period__in=sp_periods, user=PyrosUser.objects.get(id=request.user.id)).exists() :
425 445 return HttpResponseRedirect(reverse("index_scientific_program"))
426   -
427   - CAN_REPROPOSE_SP = sp_pi == request.user and request.session.get("role") == "Observer"
  446 + today = timezone.now().date()
  447 + next_proposal_period = Period.objects.filter(
  448 + start_date__gt=today, submission_start_date__lte=today).order_by("start_date").first()
  449 + is_future_period_in_proposal = next_proposal_period.id not in sp_periods.values_list("period",flat=True)
  450 + CAN_REPROPOSE_SP = sp_pi == request.user and request.session.get("role") == "Observer" and is_future_period_in_proposal
  451 + CAN_DELETE_SP = not sp_periods and sp_pi == request.user and request.session.get("role") == "Observer" or request.session.get("role") in ("Admin","Unit-PI","Unit-board")
428 452 return render(request, 'scientific_program/scientific_program_detail.html', {
429 453 'scientific_program': scientific_program,
430 454 "sp_pi": sp_pi,
431 455 "sp_periods": sp_periods,
432   - "CAN_REPROPOSE_SP":CAN_REPROPOSE_SP
  456 + "CAN_REPROPOSE_SP": CAN_REPROPOSE_SP,
  457 + "CAN_DELETE_SP": CAN_DELETE_SP
433 458 })
434 459  
435 460  
... ... @@ -454,6 +479,7 @@ def detail_scientific_program_period(request, id_sp, id_period):
454 479 CAN_TAC_EVALUATE = request.session.get("role") in ("Admin","TAC") and sp_period.status == SP_Period.STATUSES_SUBMITTED
455 480 CAN_UNIT_PI_VALIDATE = request.session.get("role") in ("Unit-PI","Unit-board") and sp_period.status == SP_Period.STATUSES_EVALUATED
456 481 CAN_OBSERVER_DELETE = request.session.get("role") in ("Admin","Observer") and sp_period.status == SP_Period.STATUSES_DRAFT
  482 + CAN_VIEW_TAC_VOTES = request.session.get("role") in ("Admin","Unit-PI","Unit-board")
457 483 return render(request, 'scientific_program/scientific_program_period_detail.html', {
458 484 'scientific_program': scientific_program,
459 485 "users": users_informations,
... ... @@ -466,7 +492,7 @@ def detail_scientific_program_period(request, id_sp, id_period):
466 492 "CAN_UNIT_PI_VALIDATE": CAN_UNIT_PI_VALIDATE,
467 493 "CAN_OBSERVER_DELETE": CAN_OBSERVER_DELETE,
468 494 "is_proposal": True,
469   -
  495 + "CAN_VIEW_TAC_VOTES": CAN_VIEW_TAC_VOTES
470 496 })
471 497  
472 498  
... ... @@ -482,17 +508,30 @@ def submit_proposal(request, id):
482 508  
483 509 @level_required("Admin", "Unit-PI", "Observer")
484 510 @login_required()
485   -def delete_proposal(request, id):
486   - scientific_program = get_object_or_404(ScientificProgram, pk=int(id))
487   - sp_period = SP_Period.objects.get(
488   - scientific_program=scientific_program, status=SP_Period.STATUSES_DRAFT)
489   - sp_period_users = SP_Period_User.objects.filter(SP_Period=sp_period)
490   - sp_period_guests = SP_Period_Guest.objects.filter(SP_Period=sp_period)
491   - sp_period_guests.delete()
492   - sp_period_users.delete()
493   - sp_period.delete()
  511 +def delete_scientific_program(request, id_sp):
  512 + scientific_program = get_object_or_404(ScientificProgram, pk=id_sp)
  513 + sp_periods = SP_Period.objects.filter(scientific_program=scientific_program)
  514 + if sp_periods:
  515 + for sp_period in sp_periods:
  516 + SP_Period_User.objects.filter(SP_Period=sp_period).delete()
  517 + SP_Period_Guest.objects.filter(SP_Period=sp_period).delete()
  518 + sp_periods.delete()
494 519 scientific_program.delete()
495   - return HttpResponseRedirect(reverse('proposal_list'))
  520 + return HttpResponseRedirect(reverse('index_scientific_program'))
  521 +
  522 +@level_required("Admin", "Unit-PI", "Observer")
  523 +@login_required()
  524 +def delete_scientific_program_period(request, id_sp, id_period):
  525 + scientific_program = get_object_or_404(ScientificProgram, pk=id_sp)
  526 + sp_period = SP_Period.objects.get(
  527 + scientific_program=scientific_program, period=Period.objects.get(id=id_period))
  528 + if sp_period.status == SP_Period.STATUSES_DRAFT:
  529 + sp_period_users = SP_Period_User.objects.filter(SP_Period=sp_period)
  530 + sp_period_guests = SP_Period_Guest.objects.filter(SP_Period=sp_period)
  531 + sp_period_guests.delete()
  532 + sp_period_users.delete()
  533 + sp_period.delete()
  534 + return HttpResponseRedirect(reverse('detail_scientific_program',args=[scientific_program.id]))
496 535  
497 536  
498 537 @login_required
... ...