# Standard imports from datetime import date, datetime from dateutil.relativedelta import relativedelta import matplotlib.pyplot as plt import re,io,urllib,base64 # Django imports from django.http.response import HttpResponse from django.shortcuts import render, redirect, get_object_or_404, reverse from django.contrib.auth import authenticate, login from django.contrib.auth.decorators import login_required from django.http import HttpResponseRedirect from django.conf import settings from django.core.mail import send_mail from django.db.models import Max from django.utils import timezone from django.db.models import Q,F from django.views.decorators.csrf import csrf_exempt from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger # Project imports from config.pyros.config_pyros import ConfigPyros from .functions import get_global_svg_timeline, get_svg_timeline, get_proposal_svg_timeline from user_manager.models import ScientificProgram, Institute, Period, SP_Period_User, SP_Period, PyrosUser, SP_Period_Guest, ScienceTheme #, UserLevel from routine_manager.models import Sequence #from src.core.pyros_django import scientific_program from .forms import PeriodForm, ScienceThemeForm, ScientificProgramForm, InstituteForm, SP_PeriodForm,TACAssociationForm from src.core.pyros_django.dashboard.decorator import level_required # Scientific program CRUD #NB_ELEMENT_PER_PAGE = configpyros(os.environ["pyros_config_file"]).pyros_config.get("general").get("nb_element_per_page") @login_required @level_required("Admin", "Observer","TAC","Unit-PI","Unit-board") def index_scientific_program(request): today = timezone.now().date() """ does_next_period_exist = Period.objects.filter( start_date__gt=today).order_by("start_date").first() != None future_period = Period.objects.filter( start_date__gt=today, submission_start_date__lte=today).order_by("start_date").first() previous_period = Period.objects.filter( end_date__lt=today, property_of_data_end_date__lt=today).first() current_period = Period.objects.exploitation_period() evaluation_periods = Period.objects.filter( start_date__gte=today, submission_end_date__lte=today,unit_pi_validation_start_date__gt=today) #validation_periods = Period.objects.filter( # start_date__gte=today, submission_end_date__gt=today,unit_pi_validation_start_date__gte=today,start_date__lt=F("start_date") + relativedelta(days=-5)) validation_periods = Period.objects.filter( start_date__gte=today, submission_end_date__gt=today,unit_pi_validation_start_date__gte=today) """ does_next_period_exist = Period.objects.next_period() != None current_period = Period.objects.exploitation_period() future_period = Period.objects.filter(start_date__gte=current_period.end_date).first() previous_period = Period.objects.previous_periods().first() evaluation_periods = Period.objects.evaluation_periods() validation_periods = Period.objects.validation_periods() sp_to_be_evaluated = SP_Period.objects.filter(period__in=evaluation_periods,status__in=(SP_Period.STATUSES_SUBMITTED,SP_Period.STATUSES_EVALUATED)).count() > 0 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 does_next_period_need_tac_association = Period.objects.next_period() in Period.objects.submission_periods() # date_diff_today_end_of_property will give a value to make # the date of today shift through the time line if previous_period: temp = today - previous_period.start_date date_diff_today_end_of_property = temp.days * 0.85 else: temp = today - current_period.start_date date_diff_today_end_of_property = (temp.days / 8) + 15 CAN_VALIDATE_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") and sp_to_be_validated CAN_EVALUATE_SP = request.session.get("role") in ("Admin","TAC") and sp_to_be_evaluated CAN_SUBMIT_SP = request.session.get("role") in ("Admin","Observer") and does_next_period_exist CAN_VIEW_SP = request.session.get("role") in ("Admin","Observer") CAN_VIEW_ALL_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") CAN_VIEW_EXPLOITATION_PERIOD = request.session.get("role") in ("Admin","Observer","Unit-PI","Unit-board") CAN_CREATE_EXPLOITATION_PERIOD = request.session.get("role") in ("Admin","Unit-PI","Unit-board") CAN_ASSOCIATE_TAC_TO_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") and does_next_period_need_tac_association svg = get_svg_timeline(previous_period,current_period,future_period) global_svg = get_global_svg_timeline(previous_period,current_period,future_period) #global_svg = get_global_svg_timeline(current_period,current_period,future_period) return render(request, "scientific_program/index.html", { "base_template": "base.html", "previous_period": previous_period, "current_period": current_period, "future_period": future_period, "today": today, "date_diff_today_end_of_property": date_diff_today_end_of_property, "svg":svg, "global_svg":global_svg, "CAN_VALIDATE_SP": CAN_VALIDATE_SP, "CAN_EVALUATE_SP": CAN_EVALUATE_SP, "CAN_SUBMIT_SP":CAN_SUBMIT_SP, "CAN_VIEW_SP": CAN_VIEW_SP, "CAN_VIEW_EXPLOITATION_PERIOD": CAN_VIEW_EXPLOITATION_PERIOD, "CAN_CREATE_EXPLOITATION_PERIOD": CAN_CREATE_EXPLOITATION_PERIOD, "CAN_VIEW_ALL_SP": CAN_VIEW_ALL_SP, "CAN_ASSOCIATE_TAC_TO_SP":CAN_ASSOCIATE_TAC_TO_SP }) @login_required @level_required("Admin", "Observer") def create_scientific_program(request): SP_form = ScientificProgramForm() if request.POST: SP_form = ScientificProgramForm(request.POST) if SP_form.is_valid(): institute = request.user.institute temp_SP = SP_form.save(commit=False) if institute == "CNES": temp_SP.is_auto_validated = True temp_SP.institute = Institute.objects.get(name=institute) temp_SP.status = SP_Period.STATUSES_DRAFT temp_SP.sp_pi = request.user temp_SP.save() return HttpResponseRedirect(reverse("create_scientific_program_period",args=[temp_SP.id])) return render(request, "scientific_program/create_scientific_program.html", { "SPForm": SP_form, }) @level_required("Admin", "Observer") @login_required def create_scientific_program_period(request,id_SP,id_SP_Period=None): SP = ScientificProgram.objects.get(id=id_SP) today = timezone.now().date() #next_periods = Period.objects.filter(submission_start_date__lte=today,submission_end_date__gt=today, start_date__gt=today) next_periods = Period.objects.submission_periods() is_sp_reproposed = False if SP.sp_pi != request.user: HttpResponseRedirect(reverse("index_scientific_program")) SP_Period_form = SP_PeriodForm() if id_SP_Period != None: sp_period_template = SP_Period.objects.get(id=id_SP_Period) initial_data = { "quota_minimal":sp_period_template.quota_minimal, "quota_nominal":sp_period_template.quota_nominal, "over_quota_duration":sp_period_template.over_quota_duration, "token":sp_period_template.token } period_id_to_be_excluded = SP_Period.objects.filter(scientific_program=sp_period_template.scientific_program).values_list("period",flat=True) next_periods = Period.objects.submission_periods().exclude(id__in=period_id_to_be_excluded) SP_Period_form = SP_PeriodForm(initial=initial_data) is_sp_reproposed = True #next_period = Period.objects.filter( # start_date__gt=today).order_by("start_date").first() error = False error_message = "" list_of_emails = None recipient_list = [] if request.POST: SP_Period_form = SP_PeriodForm(request.POST) if SP_Period_form.is_valid(): """ if request.POST.getlist("user"): for user in request.POST.getlist("user"): user_instance = PyrosUser.objects.get(id=int(user)) SP_Period_User.objects.create(SP_Period=sp_period,user=user_instance,is_SP_PI=False) """ if not is_sp_reproposed and request.POST.get("users"): regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' list_of_emails = request.POST.get("users").strip() for email in list_of_emails.split(","): if re.fullmatch(regex, email): recipient_list.append(email) else: error = True error_message = f"Invalid mail for {email}" if not error: institute = request.user.institute selected_period = Period.objects.get(id=request.POST.get("period")) current_sp = ScientificProgram.objects.get(id=id_SP) sp_period = SP_Period_form.save(commit=False) sp_period.period = selected_period #sp_period.period = next_period sp_period.scientific_program = current_sp sp_period.save() sp_pi_user = PyrosUser.objects.get(id=request.user.id) #SP_Period_User.objects.create( # SP_Period=sp_period, user=sp_pi_user, is_SP_PI=True) domain = settings.DEFAULT_DOMAIN url = f"{domain}{reverse('sp_register',args=(current_sp.pk,sp_period.period.pk))}" mail_subject = '[PyROS CC] New registration to an observing proposal' mail_message = (f"Hi,\n\nYou were invited to join an observing proposal that as been submitted using PyROS.\n" 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" f"To accept this invitation, click on the following link : {url}\n" "You might be asked to login first and will be redirected to the proposal page.\n" "If the redirection doesn't work, click again on the link after you've logged in.\n" "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." "\n\nCordially,\n\nPyROS Control Center") if len(recipient_list) > 0: for recipient in recipient_list: guest = SP_Period_Guest.objects.create( SP_Period=sp_period, email=recipient) send_mail( mail_subject, mail_message, from_email=None, recipient_list=[recipient], fail_silently=False, ) if is_sp_reproposed: previous_sp_periods = SP_Period.objects.filter(scientific_program=current_sp,period__lt=selected_period) users_of_previous_period_for_sp = SP_Period_User.objects.filter(SP_Period__in=previous_sp_periods).values_list("user",flat=True).distinct() for user in users_of_previous_period_for_sp: SP_Period_User.objects.create(SP_Period=sp_period,user=PyrosUser.objects.get(id=user)) return HttpResponseRedirect(reverse("detail_scientific_program",args=[current_sp.id])) #return HttpResponseRedirect(reverse("own_scientific_program_list")) return render(request, "scientific_program/create_scientific_program_period.html", { "scientific_program":SP, "SP_PeriodForm": SP_Period_form, "error_message": error_message, "list_of_emails": list_of_emails, "next_periods": next_periods, "is_sp_reproposed":is_sp_reproposed }) @login_required @level_required("Admin", "Observer") def create_scientific_program_with_template(request, id_SP): SP_form = ScientificProgramForm(instance=ScientificProgram.objects.get(id=id_SP)) if request.POST: SP_form = ScientificProgramForm(request.POST) if SP_form.is_valid(): institute = request.user.institute temp_SP = SP_form.save(commit=False) temp_SP.institute = Institute.objects.get(name=institute) temp_SP.status = SP_Period.STATUSES_DRAFT temp_SP.sp_pi = request.user temp_SP.save() return HttpResponseRedirect(reverse("create_scientific_program_period",args=[temp_SP.id])) return render(request, "scientific_program/create_scientific_program.html", { "SPForm": SP_form, }) @login_required @level_required("Admin","Unit-PI","Unit-board") def list_drafted_scientific_program(request): select_message = "Select an SP to associate TAC users to it for the incomming evaluation time" none_message = f"There is no existing SP to be associated with TAC users for a new exploitation period" non_autovalidated_sp = ScientificProgram.objects.exclude(is_auto_validated=True) #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") 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") return render(request, "scientific_program/list_of_scientific_program_select.html", { "select_message": select_message, "none_message": none_message, "list_of_sp": sp_periods_without_tac, "associate_tac_to_sp":True }) @login_required @level_required("Admin", "Observer") def list_scientific_program_period_repropose(request): select_message = "Submit again an existing SP but for a new exploitation period (instance)" none_message = f"There is no existing SP to be submitted for a new exploitation period" sp_where_user_is_sp_pi = ScientificProgram.objects.filter(sp_pi=request.user) if sp_where_user_is_sp_pi is None: return HttpResponseRedirect(reverse("index_scientific_program")) sp_period_of_user = SP_Period.objects.filter(scientific_program__in=sp_where_user_is_sp_pi) if not sp_period_of_user and sp_where_user_is_sp_pi is None: return HttpResponseRedirect(reverse("index_scientific_program")) #sp_of_user = SP_Period_User.objects.filter(user=request.user.id).values_list("SP_Period",flat=True) #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") #sp_period_active_of_user = SP_Period.objects.filter(scientific_program__in=sp_where_user_is_sp_pi,period=Period.objects.exploitation_period()) sp_period_of_user = [] # sp_to_be_proposed = list of Scientific Program that are NOT linked to a Period list_of_sp_to_be_proposed = [] today = timezone.now().date() #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) next_proposal_periods = Period.objects.submission_periods().values_list("id",flat=True) for sp in sp_where_user_is_sp_pi: is_sp_associated_with_period = SP_Period.objects.filter(scientific_program=sp).count() > 0 if is_sp_associated_with_period: # get latest period that has been associated with that SP max_period_of_sp = SP_Period.objects.filter(scientific_program=sp).aggregate(Max("period"))["period__max"] # get all periods where this SP is associated periods_of_sp = SP_Period.objects.filter(scientific_program=sp).values("period") if max_period_of_sp : # remove the periods where this sp is associated next_proposal_periods = next_proposal_periods.exclude(id__in=periods_of_sp) if next_proposal_periods.count() > 0: # this SP can be reproposed for a next proposal period sp_period_of_user.append(SP_Period.objects.get(scientific_program=sp,period=max_period_of_sp)) else: list_of_sp_to_be_proposed.append(sp) #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") list_of_sp = [] are_there_only_sp = False # SP_Period where user is a SP_PI list_of_sp = sp_period_of_user if len(list_of_sp) == 0 and len(list_of_sp_to_be_proposed) == 0: list_of_sp = ScientificProgram.objects.filter(sp_pi=request.user).exclude(id__in=SP_Period.objects.all().values("scientific_program")) # This boolean will tell if we're using ScientificProgram objects within the view or SP_Period objects are_there_only_sp = True return render(request, "scientific_program/list_of_scientific_program_select.html", { "select_message": select_message, "none_message": none_message, "list_of_sp": list_of_sp, "list_of_sp_to_be_proposed":list_of_sp_to_be_proposed, "list_of_sp_to_be_reproposed": True, "are_there_only_sp":are_there_only_sp }) @login_required @level_required("Admin", "Observer") def list_scientific_program_as_template(request): select_message = "Please select a SP from the list below as template:" none_message = f"There is no SP to be used as template" sp_of_user = SP_Period_User.objects.filter(user=request.user.id).values_list("SP_Period",flat=True) list_of_sp = [] are_there_only_sp = False for sp_period in sp_of_user: list_of_sp.append(SP_Period.objects.get(id=sp_period).order_by("-id")) if len(list_of_sp) == 0: list_of_sp = ScientificProgram.objects.filter(sp_pi=request.user).order_by("-id") are_there_only_sp = True return render(request, "scientific_program/list_of_scientific_program_select.html", { "select_message": select_message, "none_message": none_message, "list_of_sp": list_of_sp, "list_of_sp_as_template": True, "are_there_only_sp":are_there_only_sp }) @login_required @level_required("Admin", "TAC") def list_submitted_scientific_program(request): select_message = "Please select a SP from the list below for evaluation:" today = timezone.now().date() #next_period = Period.objects.filter( # start_date__gte=today, submission_end_date__lte=today,unit_pi_validation_start_date_gt=today).first() next_period = Period.objects.evaluation_periods().first() none_message = f"There is no SP to be evaluated for next exploitation period {next_period}" # Note : ~Q mean not query #list_of_sp = SP_Period.objects.filter(Q(referee1=None) & ~Q(referee2=request.user) | ~Q( # referee1=request.user) & Q(referee2=None), status=SP_Period.STATUSES_SUBMITTED, period=next_period) list_of_sp = SP_Period.objects.filter(status=SP_Period.STATUSES_SUBMITTED, period=next_period) list_of_evaluated_sp = [] for sp in list_of_sp: if sp.referee1 == request.user or sp.referee2 == request.user: list_of_evaluated_sp.append(sp) return render(request, "scientific_program/list_of_scientific_program_select.html", { "select_message": select_message, "none_message": none_message, "list_of_sp": list_of_sp, "list_of_evaluated_sp": list_of_evaluated_sp }) @login_required @level_required("Admin", "Unit-PI","Unit-board") def list_evaluated_scientific_program(request): select_message = "Please select a SP from the list below for validation:" today = timezone.now().date() #next_period = Period.objects.filter( # start_date__gte=today, submission_start_date__lte=today).first() next_period = Period.objects.validation_periods().first() none_message = f"There is no SP to be validated for next exploitation period {next_period}" list_of_sp = SP_Period.objects.filter( status=SP_Period.STATUSES_EVALUATED, period=next_period) list_of_validated_sp = [] for sp in list_of_sp: if sp.quota_allocated > 0 and sp.is_valid == SP_Period.IS_VALID_ACCEPTED or sp.is_valid == SP_Period.IS_VALID_REJECTED: list_of_validated_sp.append(sp) return render(request, "scientific_program/list_of_scientific_program_select.html", { "select_message": select_message, "none_message": none_message, "list_of_sp": list_of_sp, "list_of_validated_sp": list_of_validated_sp }) @login_required def accept_sp_invitation(request, id_SP, id_period): """ An invitation to join an SP is valid until the end of Exploitation period """ sp_period = SP_Period.objects.get(scientific_program=id_SP, period=id_period) current_user = PyrosUser.objects.get(username=request.user) is_user_guest_of_sp = SP_Period_Guest.objects.filter( SP_Period=sp_period, email=current_user.email).exists() today = timezone.now().date() period = Period.objects.get(id=id_period) if is_user_guest_of_sp and period.end_date > today: new_user = SP_Period_User.objects.create( SP_Period=sp_period, user=current_user) if new_user: SP_Period_Guest.objects.get( SP_Period=sp_period, email=current_user.email).delete() return HttpResponseRedirect(reverse("detail_scientific_program_period", kwargs={"id_sp": id_SP, "id_period": id_period})) return HttpResponseRedirect(reverse("index")) @login_required @level_required("Admin", "Unit-PI", "Unit-board") def associate_tac_to_scientific_program(request,id_sp,id_period): scientific_program = ScientificProgram.objects.get(id=id_sp) period = Period.objects.get(id=id_period) sp_period = SP_Period.objects.get(scientific_program=scientific_program,period=period) form = TACAssociationForm(request.POST or None,instance=sp_period) tac_users_of_science_theme = PyrosUser.objects.filter(user_level__name="TAC",referee_themes=scientific_program.science_theme) form.fields["referee1"].queryset = tac_users_of_science_theme form.fields["referee2"].queryset = tac_users_of_science_theme if request.POST: form = TACAssociationForm(request.POST) form.fields["referee1"].queryset = tac_users_of_science_theme form.fields["referee2"].queryset = tac_users_of_science_theme if form.is_valid(): referee1 = PyrosUser.objects.get(id=request.POST.get("referee1")) referee2 = PyrosUser.objects.get(id=request.POST.get("referee2")) sp_period.referee1 = referee1 sp_period.referee2 = referee2 sp_period.save() return HttpResponseRedirect(reverse("list_drafted_scientific_program")) return render(request,"scientific_program/associate_tac_to_sp.html",{ "form":form }) @level_required("Admin", "Unit-PI", "Observer", "TAC") @login_required def edit_scientific_program_period(request, id_sp, id_period): scientific_program = get_object_or_404(ScientificProgram, pk=id_sp) SPForm = ScientificProgramForm( request.POST or None, instance=scientific_program) error = False error_message = "" today = timezone.now().date() sp_period = SP_Period.objects.get( scientific_program=scientific_program, period=Period.objects.get(id=id_period)) period=Period.objects.get(id=id_period) SP_Period_form = SP_PeriodForm(request.POST or None, instance=sp_period) sp_pi = scientific_program.sp_pi if request.session.get("role") == "Observer" and request.user.id != sp_pi.id: return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) guests = list(SP_Period_Guest.objects.filter( SP_Period=sp_period).values_list("email", flat=True)) user_of_sp = SP_Period_User.objects.filter( SP_Period__exact=sp_period) users_informations = PyrosUser.objects.filter( id__in=user_of_sp.values_list("user", flat=True)) guests = ",".join(guests) error = False error_message = "" list_of_emails = None recipient_list = [] if request.POST: if SPForm.is_valid() and sp_period.status == SP_Period.STATUSES_DRAFT: SPForm.save() if SP_Period_form.is_valid(): if sp_period.status == SP_Period.STATUSES_DRAFT: SP_period = SP_Period_form.save() # can send invitation until the end of exploitation if request.POST.get("users") and today < period.end_date: # Invite user to join the SP regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' list_of_emails = request.POST.get("users").strip() for email in list_of_emails.split(","): if re.fullmatch(regex, email): recipient_list.append(email) else: error = True error_message = f"Invalid mail for {email}" if not error: domain = settings.DEFAULT_DOMAIN url = f"{domain}{reverse('sp_register',args=(scientific_program.pk,sp_period.period.pk))}" mail_subject = '[PyROS CC] New registration to an observing proposal' mail_message = (f"Hi,\n\nYou were invited to join an observing proposal that as been submitted using PyROS.\n" f"The name of the proposal is {scientific_program.name} and his PI is {sp_pi.first_name} {sp_pi.last_name}.\n" f"To accept this invitation, click on the following link : {url}\n" "You might be asked to login first and will be redirected to the proposal page.\n" "If the redirection doesn't work, click again on the link after you've logged in.\n" "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." "\n\nCordially,\n\nPyROS Control Center") if len(recipient_list) > 0: try: # Delete previous guests (resetting list of guests) SP_Period_Guest.objects.filter(SP_Period=sp_period).delete() finally: for recipient in recipient_list: guest = SP_Period_Guest.objects.create( SP_Period=sp_period, email=recipient) send_mail( mail_subject, mail_message, from_email=None, recipient_list=[recipient], fail_silently=False, ) else: return render(request, 'scientific_program/scientific_program_period_detail_edit.html', { 'id_sp': id_sp, "id_period": id_period, "SPForm": SPForm, "period": sp_period.period, "guests": guests, "users_of_sp_period": users_informations, "SP_PeriodForm": SP_Period_form, "sp_period": sp_period, "error_message":error_message }) # save changes on user list return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) if SP_Period_form.is_valid() and request.session.get("role") == "TAC" and sp_period.status != SP_Period.STATUSES_DRAFT: # vote TAC if sp_period.referee1 == request.user: print("vote referee 1 ") SP_Period_form.save(commit=False) SP_Period_form.vote_referee1 = request.POST.get( "vote_referee1") SP_Period_form.reason_referee1 = request.POST.get( "reason_referee1") SP_Period_form.save() #if sp_period.referee2 != None: #sp_period.status = SP_Period.STATUSES_EVALUATED sp_period.save() if sp_period.referee2 == request.user: print("vote referee 2 ") SP_Period_form.save(commit=False) SP_Period_form.vote_referee2 = request.POST.get( "vote_referee2") SP_Period_form.reason_referee2 = request.POST.get( "reason_referee2") SP_Period_form.save() #if sp_period.referee1 != None: #sp_period.status = SP_Period.STATUSES_EVALUATED sp_period.save() return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) 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]: #if request.POST.get("is_valid") != None: #sp_period.status = request.POST.get("is_valid") SP_Period_form.save() return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) else: print(SP_Period_form.errors) return redirect('detail_scientific_program_period', id_sp=scientific_program.id, id_period=id_period) CAN_VIEW_TAC_VOTES = request.session.get("role") in ("Admin","Unit-PI","Unit-board") return render(request, 'scientific_program/scientific_program_period_detail_edit.html', { 'id_sp': id_sp, "id_period": id_period, "SPForm": SPForm, "period": sp_period.period, "guests": guests, "users_of_sp_period": users_informations, "SP_PeriodForm": SP_Period_form, "sp_period": sp_period, "error_message":error_message, "CAN_VIEW_TAC_VOTES":CAN_VIEW_TAC_VOTES }) @level_required("Admin", "Unit-PI", "Observer", "TAC") @login_required def detail_scientific_program(request, id): scientific_program = get_object_or_404(ScientificProgram, pk=id) sp_periods = SP_Period.objects.filter( scientific_program=scientific_program).order_by("-id") sp_pi = scientific_program.sp_pi 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() : return HttpResponseRedirect(reverse("index_scientific_program")) today = timezone.now().date() #next_proposal_period = Period.objects.filter( # start_date__gt=today, submission_start_date__lte=today).order_by("start_date").first() next_proposal_period = Period.objects.filter(start_date__gte=Period.objects.exploitation_period().end_date).first() if next_proposal_period: is_future_period_in_proposal = next_proposal_period.id not in sp_periods.values_list("period",flat=True) else: is_future_period_in_proposal = False CAN_REPROPOSE_SP = sp_pi == request.user and request.session.get("role") == "Observer" and is_future_period_in_proposal 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")) CAN_VIEW_IS_AUTOVALIDATED = sp_pi == request.user and request.session.get("role") == "Observer" or request.session.get("role") in ("Admin","Unit-PI","Unit-board") return render(request, 'scientific_program/scientific_program_detail.html', { 'scientific_program': scientific_program, "sp_pi": sp_pi, "sp_periods": sp_periods, "CAN_REPROPOSE_SP": CAN_REPROPOSE_SP, "CAN_DELETE_SP": CAN_DELETE_SP, "CAN_VIEW_IS_AUTOVALIDATED": CAN_VIEW_IS_AUTOVALIDATED }) @level_required("Admin", "Unit-PI", "Observer") @login_required def detail_scientific_program_period(request, id_sp, id_period): scientific_program = get_object_or_404(ScientificProgram, pk=id_sp) sp_period = SP_Period.objects.get( scientific_program=scientific_program, period=Period.objects.get(id=id_period)) user_of_sp = SP_Period_User.objects.filter( SP_Period=sp_period) users_informations = PyrosUser.objects.filter( id__in=user_of_sp.values_list("user", flat=True)) sp_pi = scientific_program.sp_pi guests = list(SP_Period_Guest.objects.filter( SP_Period=sp_period).values_list("email", flat=True)) sequences = Sequence.objects.filter(scientific_program=scientific_program,period_id=id_period).order_by("-id") page = request.GET.get('page', 1) sequences_paginator = Paginator(sequences,settings.NB_ELEMENT_PER_PAGE) try: sequences = sequences_paginator.page(page) except PageNotAnInteger: sequences = sequences_paginator.page(1) except EmptyPage: sequences = sequences_paginator.page(sequences_paginator.num_pages) # User is observer and not from this SP if request.session.get("role") == "Observer": if sp_pi!=request.user and not SP_Period_User.objects.filter(SP_Period=sp_period, user=PyrosUser.objects.get(id=request.user.id)).exists(): return HttpResponseRedirect(reverse("index_scientific_program")) CAN_OBSERVER_EDIT = request.session.get("role") in ("Admin","Observer") and sp_pi == request.user and sp_period.status == SP_Period.STATUSES_DRAFT CAN_TAC_EVALUATE = request.session.get("role") in ("Admin","TAC") and sp_period.status == SP_Period.STATUSES_SUBMITTED CAN_UNIT_PI_VALIDATE = request.session.get("role") in ("Unit-PI","Unit-board") and sp_period.status == SP_Period.STATUSES_EVALUATED CAN_OBSERVER_DELETE = request.session.get("role") in ("Admin","Observer") and sp_period.status == SP_Period.STATUSES_DRAFT CAN_VIEW_TAC_VOTES = request.session.get("role") in ("Admin","Unit-PI","Unit-board") CAN_SUBMIT_PROPOSAL = request.user == sp_pi and request.session.get("role") == "Observer" and sp_period.status == SP_Period.STATUSES_DRAFT return render(request, 'scientific_program/scientific_program_period_detail.html', { 'scientific_program': scientific_program, "users": users_informations, "sp_pi": sp_pi, "period": sp_period.period, "guests": guests, "sp_period": sp_period, "sequences": sequences, "CAN_OBSERVER_EDIT": CAN_OBSERVER_EDIT, "CAN_TAC_EVALUATE": CAN_TAC_EVALUATE, "CAN_UNIT_PI_VALIDATE": CAN_UNIT_PI_VALIDATE, "CAN_OBSERVER_DELETE": CAN_OBSERVER_DELETE, "is_proposal": True, "CAN_VIEW_TAC_VOTES": CAN_VIEW_TAC_VOTES, "CAN_SUBMIT_PROPOSAL": CAN_SUBMIT_PROPOSAL }) @level_required("Admin", "Unit-PI", "Observer") @login_required() def submit_proposal(request, id): sp_period = SP_Period.objects.get(id=id) if sp_period.status == SP_Period.STATUSES_DRAFT: sp_period.status = SP_Period.STATUSES_SUBMITTED sp_period.save() return HttpResponseRedirect(reverse("detail_scientific_program_period", args=[sp_period.scientific_program.id, sp_period.period.id])) @level_required("Admin", "Unit-PI", "Observer") @login_required() def delete_scientific_program(request, id_sp): scientific_program = get_object_or_404(ScientificProgram, pk=id_sp) sp_periods = SP_Period.objects.filter(scientific_program=scientific_program) if sp_periods: # can't delete return HttpResponseRedirect(reverse("detail_scientific_program", args=[scientific_program.id ])) else: scientific_program.delete() return HttpResponseRedirect(reverse('index_scientific_program')) @level_required("Admin", "Unit-PI", "Observer") @login_required() def delete_scientific_program_period(request, id_sp, id_period): scientific_program = get_object_or_404(ScientificProgram, pk=id_sp) sp_period = SP_Period.objects.get( scientific_program=scientific_program, period=Period.objects.get(id=id_period)) if sp_period.status == SP_Period.STATUSES_DRAFT: # Delete only if in draft status sp_period_users = SP_Period_User.objects.filter(SP_Period=sp_period) sp_period_guests = SP_Period_Guest.objects.filter(SP_Period=sp_period) sp_period_guests.delete() sp_period_users.delete() sp_period.delete() return HttpResponseRedirect(reverse('detail_scientific_program',args=[scientific_program.id])) @login_required @level_required("Admin", "Unit-PI", "Observer", "Unit-board") def scientific_program_list(request): sp_of_user = [] # get sp_period where the period is an active period and are accepted scientific_programs = ScientificProgram.objects.all().order_by("-id") CAN_VIEW_ALL_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") CAN_VIEW_HIS_SP = request.session.get("role") == "Observer" if not CAN_VIEW_ALL_SP: # get all SP associated to that user sp_periods_of_user = SP_Period_User.objects.filter(user=request.user.id).values_list("SP_Period",flat=True) list_of_sp_id = SP_Period.objects.filter(id__in=sp_periods_of_user).values("scientific_program") sp_of_user = ScientificProgram.objects.filter(id__in=list_of_sp_id) return render(request, 'scientific_program/scientific_programs.html', { 'scientific_programs': scientific_programs, "sp_of_user": sp_of_user, "CAN_VIEW_ALL_SP": CAN_VIEW_ALL_SP, "CAN_VIEW_HIS_SP": CAN_VIEW_HIS_SP }) @login_required @level_required("Observer") def own_scientific_program_list(request): sp_of_user = [] # get sp_period where the period is an active period and are accepted scientific_programs = ScientificProgram.objects.all().order_by("-id") CAN_VIEW_ALL_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") CAN_VIEW_HIS_SP = request.session.get("role") == "Observer" if (len(scientific_programs) > 0): # get all SP where user is sp_pi sp_of_user = ScientificProgram.objects.filter(sp_pi=request.user).order_by("-id") else: scientific_programs = None return render(request, 'scientific_program/scientific_programs.html', { 'scientific_programs': scientific_programs, "sp_of_user": sp_of_user, "CAN_VIEW_ALL_SP": CAN_VIEW_ALL_SP, "CAN_VIEW_HIS_SP": CAN_VIEW_HIS_SP }) #Get Proposal timeline of period @level_required("Admin", "Unit-PI", "Observer", "TAC") @login_required @csrf_exempt def get_proposal_timeline(request): if request.POST.get("period_id"): return HttpResponse(get_proposal_svg_timeline(Period.objects.get(id=request.POST["period_id"]),request.POST["period"])) # Institute CRUD @login_required @level_required("Admin", "Unit-PI","Unit-board") def create_institute(request): form = InstituteForm(request.POST) if request.POST: if form.is_valid(): form.save() return HttpResponseRedirect(reverse("institute_list")) form = InstituteForm() return render(request, "scientific_program/create_institute.html", {"form": form}) @level_required("Admin", "Unit-PI","Unit-board") @login_required def edit_institute(request, id): edit = get_object_or_404(Institute, pk=id) form = InstituteForm(request.POST or None, instance=edit) if form.is_valid(): form.save() return redirect('detail_institute', id=id) return render(request, 'scientific_program/institute_detail_edit.html', {'id': id, 'form': form}) @level_required("Admin", "Unit-PI", "Observer", "Operator", "TAC", "Management board member") @login_required def detail_institute(request, id): institute = get_object_or_404(Institute, pk=id) representative_member = PyrosUser.objects.get( is_institute_representative=True, institute=institute) scientific_programs = ScientificProgram.objects.filter(institute=institute) CAN_EDIT_INSTITUTE = request.session.get("role") in ("Admin", "Unit-PI","Unit-board") CAN_DELETE_INSTITUTE = request.session.get("role") in ("Admin", "Unit-PI","Unit-board") return render(request, 'scientific_program/institute_detail.html', { 'institute': institute, "representative_member": representative_member, "scientific_programs": scientific_programs, "CAN_EDIT_INSTITUTE": CAN_EDIT_INSTITUTE, "CAN_DELETE_INSTITUTE": CAN_DELETE_INSTITUTE }) @level_required("Admin", "Unit-PI","Unit-board") @login_required() def delete_institute(request, id): institute = get_object_or_404(Institute, pk=int(id)) institute.delete() return HttpResponseRedirect(reverse('institute_list')) @login_required @level_required("Admin", "Unit-PI", "Observer", "Operator", "TAC", "Management board member") def institute_list(request): institutes = Institute.objects.all() CAN_ADD_INSTITUTE = request.session.get("role") in ("Admin", "Unit-PI", "Unit-board") return render(request, 'scientific_program/institutes.html', { 'institutes': institutes, "CAN_ADD_INSTITUTE": CAN_ADD_INSTITUTE }) # Exploitation Periods CRUD @login_required @level_required("Admin", "Unit-PI", "Unit-board") def create_period(request): form = PeriodForm() today = timezone.now().date() possible_start_date = Period.objects.latest_period().end_date possible_end_date = possible_start_date + \ relativedelta(months=+6) possible_start_date_as_datetime = possible_start_date possible_end_date_as_datetime = possible_start_date + relativedelta( months=+6) possible_start_date = datetime.strftime( possible_start_date, "%d/%m/%Y") possible_end_date = datetime.strftime(possible_end_date, "%d/%m/%Y") possible_submission_start_date = possible_start_date_as_datetime + \ relativedelta(months=-6) possible_submission_start_date = datetime.strftime( possible_submission_start_date, "%d/%m/%Y") possible_submission_end_date = possible_start_date_as_datetime + \ relativedelta(months=-1) + relativedelta(days=-15) possible_submission_end_date = datetime.strftime( possible_submission_end_date, "%d/%m/%Y") possible_validation_start_date = possible_start_date_as_datetime + \ relativedelta(days=-15) possible_validation_start_date = datetime.strftime( possible_validation_start_date, "%d/%m/%Y") possible_property_end_date = possible_end_date_as_datetime + \ relativedelta(years=+1) possible_property_end_date = datetime.strftime( possible_property_end_date, "%d/%m/%Y") possible_accessibility_end_date = possible_end_date_as_datetime + \ relativedelta(years=+11) possible_accessibility_end_date = datetime.strftime( possible_accessibility_end_date, "%d/%m/%Y") past_periods = [] active_period = Period.objects.exploitation_period() periods = Period.objects.all().order_by("-start_date") if active_period: periods.exclude(id=active_period.id) for period in periods: if active_period != None: past_periods.append(period) error = False error_message = "" if request.POST: start_date = datetime.strptime( request.POST.get("start_date_picker"), "%d/%m/%Y") end_date = start_date + relativedelta(days=int(request.POST["exploitation_duration"])) """ end_date = datetime.strptime( request.POST.get("end_date_picker"), "%d/%m/%Y") submission_start_date = datetime.strptime( request.POST.get("submission_start_date_picker"), "%d/%m/%Y") submission_end_date = datetime.strptime( request.POST.get("submission_end_date_picker"), "%d/%m/%Y") unit_pi_start_validation_date = datetime.strptime( request.POST.get("unit_pi_start_validation_date_picker"), "%d/%m/%Y") property_data_end_date = datetime.strptime( request.POST.get("property_data_end_date_picker"), "%d/%m/%Y") accessibility_data_end_date = datetime.strptime( request.POST.get("accessibility_data_end_date_picker"), "%d/%m/%Y") """ # most common check if start_date != end_date and end_date > start_date: # get all future periods future_periods = Period.objects.filter( start_date__gte=today).order_by("-start_date") for future_period in future_periods: # check if submitted period isn't covering an existing period if start_date.date() < future_period.end_date and end_date.date() > future_period.start_date: error_message = f"Period already covered ({future_period})" error = True break """ if submission_start_date > unit_pi_start_validation_date: error_message += f"submission start date ({submission_start_date}) should start before Unit-PI validation date ({unit_pi_start_validation_date})" error = True if submission_end_date > unit_pi_start_validation_date: error_message += f"submission end date ({submission_end_date}) should end before Unit-PI validation date ({unit_pi_start_validation_date})" error = True if unit_pi_start_validation_date > start_date: error_message += f"Unit-PI validation date ({unit_pi_start_validation_date}) should end before period start date ({start_date})" error = True if property_data_end_date < end_date: error_message += f"Property data end date ({property_data_end_date}) should start after period end date ({end_date})" error = True if accessibility_data_end_date < property_data_end_date: error_message += f"Accessibility data end date ({accessibility_data_end_date}) should start after property data end date ({property_data_end_date})" error = True """ if not error: new_period = Period.objects.create() new_period.start_date = start_date new_period.submission_duration = request.POST["submission_duration"] new_period.evaluation_duration = request.POST["evaluation_duration"] new_period.validation_duration = request.POST["validation_duration"] new_period.notification_duration = request.POST["notification_duration"] new_period.exploitation_duration = request.POST["exploitation_duration"] new_period.property_of_data_duration = request.POST["property_of_data_duration"] new_period.data_accessibility_duration = request.POST["data_accessibility_duration"] """ new_period.end_date = end_date new_period.submission_start_date = submission_start_date new_period.submission_end_date = submission_end_date new_period.unit_pi_validation_start_date = unit_pi_start_validation_date new_period.property_of_data_end_date = property_data_end_date new_period.data_accessibility_end_date = accessibility_data_end_date """ new_period.save() return HttpResponseRedirect(reverse("period_list")) return render(request, "scientific_program/create_period.html", { "error_message": error_message, "error": error, "past_periods": past_periods, "active_period": active_period, "possible_start_date": possible_start_date, "possible_end_date": possible_end_date, "possible_submission_start_date": possible_submission_start_date, "possible_submission_end_date": possible_submission_end_date, "possible_validation_start_date": possible_validation_start_date, "possible_property_end_date": possible_property_end_date, "possible_accessibility_end_date": possible_accessibility_end_date, "form":form }) @login_required @level_required("Admin", "Unit-PI","Unit-board") def edit_period(request, id): edit = get_object_or_404(Period, pk=id) today = timezone.now().date() form = PeriodForm(instance=edit) past_periods = [] active_period = Period.objects.exploitation_period() periods = Period.objects.all().order_by("-start_date").exclude(id=active_period.id) for period in periods: if active_period != None: past_periods.append(period) error = False error_message = "" if request.POST: start_date = datetime.strptime( request.POST.get("start_date_picker"), "%d/%m/%Y") end_date = start_date + relativedelta(days=request.POST["exploitation_duration"]) """ end_date = datetime.strptime( request.POST.get("end_date_picker"), "%d/%m/%Y") submission_start_date = datetime.strptime( request.POST.get("submission_start_date_picker"), "%d/%m/%Y") submission_end_date = datetime.strptime( request.POST.get("submission_end_date_picker"), "%d/%m/%Y") unit_pi_start_validation_date = datetime.strptime( request.POST.get("unit_pi_start_validation_date_picker"), "%d/%m/%Y") property_data_end_date = datetime.strptime( request.POST.get("property_data_end_date_picker"), "%d/%m/%Y") accessibility_data_end_date = datetime.strptime( request.POST.get("accessibility_data_end_date_picker"), "%d/%m/%Y") """ # most common check if start_date != end_date and end_date > start_date: # get all future periods future_periods = Period.objects.filter( start_date__gte=today).order_by("-start_date").exclude(id=edit.id) for future_period in future_periods: # check if submitted period isn't covering an existing period if start_date.date() < future_period.end_date and end_date.date() > future_period.start_date: error_message = f"Period already covered ({future_period})" error = True break """ if submission_start_date > unit_pi_start_validation_date: error_message += f"submission start date ({submission_start_date}) should start before Unit-PI validation date ({unit_pi_start_validation_date})" error = True if submission_end_date > unit_pi_start_validation_date: error_message += f"submission end date ({submission_end_date}) should end before Unit-PI validation date ({unit_pi_start_validation_date})" error = True if unit_pi_start_validation_date > start_date: error_message += f"Unit-PI validation date ({unit_pi_start_validation_date}) should end before period start date ({start_date})" error = True if property_data_end_date < end_date: error_message += f"Property data end date ({property_data_end_date}) should start after period end date ({end_date})" error = True if accessibility_data_end_date < property_data_end_date: error_message += f"Accessibility data end date ({accessibility_data_end_date}) should start after property data end date ({property_data_end_date})" error = True """ if not error: edit.start_date = start_date edit.submission_duration = request.POST["submission_duration"] edit.evaluation_duration = request.POST["evaluation_duration"] edit.validation_duration = request.POST["validation_duration"] edit.notification_duration = request.POST["notification_duration"] edit.exploitation_duration = request.POST["exploitation_duration"] edit.property_of_data_duration = request.POST["property_of_data_duration"] edit.data_accessibility_duration = request.POST["data_accessibility_duration"] """ edit.end_date = end_date edit.submission_start_date = submission_start_date edit.submission_end_date = submission_end_date edit.unit_pi_validation_start_date = unit_pi_start_validation_date edit.property_of_data_end_date = property_data_end_date edit.data_accessibility_end_date = accessibility_data_end_date """ edit.save() return redirect('detail_period', id=id) return render(request, 'scientific_program/period_detail_edit.html', { 'id': id, 'period': edit, "error_message": error_message, "error": error, "past_periods": past_periods, "active_period": active_period, "form":form }) @login_required @level_required("Admin", "Unit-PI", "Observer") def detail_period(request, id): period = get_object_or_404(Period, pk=id) sp_periods = SP_Period.objects.filter(period=period) sp_of_user = SP_Period_User.objects.filter( user=request.user.id, SP_Period__in=sp_periods) today = timezone.now().date() is_period_public = period.end_date < today and period.start_date < today CAN_VIEW_ALL_SP = request.session.get("role") in ("Admin","Unit-PI","Unit-board") CAN_VIEW_HIS_SP = request.session.get("role") == "Observer" CAN_EDIT_PERIOD = request.session.get("role") in ("Admin","Unit-PI","Unit-board") CAN_DELETE_PERIOD = request.session.get("role") in ("Admin","Unit-PI","Unit-board") and not sp_periods return render(request, 'scientific_program/period_detail.html', { 'period': period, "sp_periods": sp_periods, "sp_of_user": sp_of_user, "is_period_public": is_period_public, "CAN_VIEW_ALL_SP": CAN_VIEW_ALL_SP, "CAN_VIEW_HIS_SP": CAN_VIEW_HIS_SP, "CAN_EDIT_PERIOD": CAN_EDIT_PERIOD, "CAN_DELETE_PERIOD": CAN_DELETE_PERIOD }) @login_required() @level_required("Admin", "Unit-PI","Unit-board") def delete_period(request, id): period = get_object_or_404(Period, pk=int(id)) period.delete() return HttpResponseRedirect(reverse('period_list')) @login_required @level_required("Admin", "Unit-PI", "Observer", "Unit-board") def period_list(request): today = timezone.now().date() periods = Period.objects.all().order_by("-start_date") future_periods = [] past_periods = [] active_period = Period.objects.exploitation_period() CAN_ADD_PERIOD = request.session.get("role") in ("Admin","Unit-PI","Unit-board") for period in periods: if period == active_period: future_periods.insert(0, period) else: if period.start_date >= today and period.end_date >= today: future_periods.append(period) else: past_periods.append(period) return render(request, 'scientific_program/periods.html', { 'past_periods': past_periods, "future_periods": future_periods, "CAN_ADD_PERIOD": CAN_ADD_PERIOD }) # Science Theme CRUD @login_required @level_required("Admin","Unit-PI","Unit-board") def add_science_theme(request): form = ScienceThemeForm() if request.POST: form = ScienceThemeForm(request.POST) if form.is_valid(): form.save() return HttpResponseRedirect(reverse("science_theme_list")) else: return render(request,"scientific_program/create_science_theme.html",{ "form":form }) return render(request,"scientific_program/create_science_theme.html",{ "form":form }) @login_required @level_required("Admin","Unit-PI","TAC") def science_theme_list(request): science_themes = ScienceTheme.objects.all() CAN_ADD_SCIENCE_THEME = request.session.get("role") in ("Admin","Unit-PI","TAC") return render(request,"scientific_program/science_theme_list.html",{ "science_themes":science_themes, "CAN_ADD_SCIENCE_THEME": CAN_ADD_SCIENCE_THEME }) @login_required @level_required("Admin","Unit-PI","Unit-board") def detail_science_theme(request,id): science_theme = get_object_or_404(ScienceTheme,pk=id) all_scientific_programs = ScientificProgram.objects.filter(science_theme=science_theme) sp_of_user = None CAN_VIEW_ALL_SP = request.session.get("role") in ("Admin","Unit-PI","TAC") CAN_EDIT_SCIENCE_THEME = request.session.get("role") in ("Admin","Unit-PI","Unit-board") percentage_of_user_of_that_theme = (PyrosUser.objects.filter(referee_themes__in=[science_theme]).count() / PyrosUser.objects.all().count()) * 100 if not CAN_VIEW_ALL_SP: sp_periods_of_user = SP_Period_User.objects.filter(user=request.user).values_list("SP_Period",flat=True) if sp_periods_of_user: sp_of_user = SP_Period.objects.filter(id__in=sp_periods_of_user).values_list("scientific_program",flat=True) sp_of_user = ScientificProgram.objects.filter(id__in=sp_of_user) # current member is a member of at least, one scientific program if sp_of_user != None: sp_of_user = ScientificProgram.objects.filter(sp_pi=request.user).union(sp_of_user) else: sp_of_user = ScientificProgram.objects.filter(sp_pi=request.user) CAN_DELETE_SCIENCE_THEME = request.session.get("role") in ("Admin","Unit-PI","Unit-board") and not all_scientific_programs return render(request,"scientific_program/science_theme_detail.html",{ "science_theme": science_theme, "all_scientific_programs" : all_scientific_programs, "sp_of_user": sp_of_user, "CAN_VIEW_ALL_SP": CAN_VIEW_ALL_SP, "CAN_EDIT_SCIENCE_THEME": CAN_EDIT_SCIENCE_THEME, "CAN_DELETE_SCIENCE_THEME": CAN_DELETE_SCIENCE_THEME, "percentage_of_user_of_that_theme":percentage_of_user_of_that_theme }) @login_required @level_required("Admin","Unit-PI","Unit-board") def edit_science_theme(request,id): edit = get_object_or_404(ScienceTheme, pk=id) form = ScienceThemeForm(request.POST or None, instance=edit) if form.is_valid(): form.save() return redirect('detail_science_theme', id=id) return render(request,"scientific_program/edit_science_theme.html",{ "edit":edit, "form":form }) @login_required @level_required("Admin","Unit-PI","Unit-board") def delete_science_theme(request,id): science_theme = get_object_or_404(ScienceTheme, pk=int(id)) can_delete_science_theme = ScientificProgram.objects.filter(science_theme=science_theme).count() == 0 if can_delete_science_theme: science_theme.delete() return HttpResponseRedirect(reverse('science_theme_list')) return HttpResponseRedirect(reverse('detail_science_theme',args=[science_theme.id])) def test_tac_auto(request): next_period = Period.objects.next_period() themes = ScienceTheme.objects.all() tac_users = PyrosUser.objects.filter(user_level__name="TAC") sp_id = SP_Period.objects.filter(period=next_period).filter(Q(referee1=None)|Q(referee2=None)).values("scientific_program") sp = ScientificProgram.objects.filter(id__in=sp_id).order_by("name") matrix = associate_tac_sp_auto(themes,tac_users,sp) fig,ax = plt.subplots(1,1) plt.xlabel("scientific programs") plt.ylabel("Number of available TAC users") plt.pcolor(matrix,edgecolors='k', linewidths=2) plt.colorbar(ticks=[0,1]) ax.set_xticklabels(sp.values_list("name",flat=True)) plt.yticks([i for i in range(len(tac_users))],tac_users.values_list("last_name",flat=True),rotation="45") #ax.set_yticklabels() buf = io.BytesIO() fig.savefig(buf,format="png") buf.seek(0) string = base64.b64encode(buf.read()) uri = urllib.parse.quote(string) return render(request,"scientific_program/test_tac_auto.html",{"data":uri})