2022-06-20 01:08:57 +00:00
from flask import flash , redirect , request , url_for
2022-08-09 12:46:48 +00:00
from flask_admin import Admin as FlaskAdmin
2022-02-23 18:37:09 +00:00
from flask_admin import expose
2022-09-12 15:59:54 +00:00
from flask_admin . contrib . sqla . fields import QuerySelectField
2022-09-11 18:05:09 +00:00
from flask_admin . menu import MenuLink
2022-06-27 21:27:09 +00:00
from flask_admin . model . template import EndpointLinkRowAction
2022-08-15 20:22:36 +00:00
from flask_login import current_user
from flask_security . changeable import admin_change_password
2022-06-20 01:08:57 +00:00
from flask_wtf import FlaskForm
2022-09-19 12:24:00 +00:00
from markupsafe import Markup
2022-09-12 15:59:54 +00:00
from wtforms . validators import DataRequired
2021-07-30 00:50:49 +00:00
2022-06-01 21:57:28 +00:00
from . advlabdb_independent_funs import (
deep_getattr ,
2022-06-30 17:44:35 +00:00
experiment_marks_missing_formatter ,
2022-06-01 21:57:28 +00:00
flashRandomPassword ,
str_formatter ,
)
2022-08-09 12:46:48 +00:00
from . custom_classes import (
SecureAssistantBaseView ,
SecureAssistantIndexView ,
SecureAssistantModelView ,
)
2022-07-03 15:11:33 +00:00
from . exceptions import ModelViewException
2022-06-20 01:08:57 +00:00
from . forms import assistant_group_experiment_form_factory
2022-06-17 18:56:14 +00:00
from . model_dependent_funs import (
generate_new_password_field ,
2022-06-26 22:20:30 +00:00
parse_selection_mark_field ,
2022-06-17 18:56:14 +00:00
user_info_fields ,
)
2022-06-20 01:08:57 +00:00
from . model_independent_funs import randomPassword , reportBadAttempt
2022-09-12 15:59:54 +00:00
from . models import Assistant , GroupExperiment , Semester , SemesterExperiment , User , db
2022-08-09 12:46:48 +00:00
assistantSpace = FlaskAdmin (
name = " Assistant@AdvLabDB " ,
url = " /assistant " ,
template_mode = " bootstrap4 " ,
index_view = SecureAssistantIndexView ( name = " Home " , url = " /assistant " , endpoint = " assistant " ) ,
)
2021-07-30 00:50:49 +00:00
2022-06-17 20:20:42 +00:00
class AssistantGroupExperimentView ( SecureAssistantModelView ) :
2022-09-19 12:24:00 +00:00
def is_accessible ( self ) :
2022-09-19 13:24:36 +00:00
if not super ( ) . is_accessible ( ) :
return False
2022-09-19 12:24:00 +00:00
active_semester = current_user . active_semester
2022-09-19 13:24:36 +00:00
2022-09-19 12:24:00 +00:00
if active_semester . done :
2022-09-19 13:24:36 +00:00
semester_changed = current_user . set_last_semester_as_active ( )
if not semester_changed :
flash (
Markup (
2022-09-21 14:52:04 +00:00
f " Active semester { active_semester } is set as done. Therefore, you are not allowed to view or edit any marks in this semester. You should change your active semester in <a href= ' /user-settings ' >user settings</a> if possible. "
2022-09-19 13:24:36 +00:00
) ,
" danger " ,
)
return False
2022-09-19 12:24:00 +00:00
2022-09-19 13:24:36 +00:00
return True
2022-09-19 12:24:00 +00:00
2022-06-17 20:20:42 +00:00
column_display_actions = True
column_list = [
" semester_experiment.experiment " ,
" group.number " ,
2022-06-27 20:12:52 +00:00
" group.part_students " ,
" appointments " ,
2022-06-27 22:58:14 +00:00
" experiment_marks_missing " ,
2022-06-17 20:20:42 +00:00
" note " ,
]
column_labels = {
" semester_experiment.experiment " : " Experiment " ,
" group.number " : " Group number " ,
2022-06-27 20:12:52 +00:00
" group.part_students " : " Students " ,
2022-06-17 20:20:42 +00:00
}
2022-06-27 20:12:52 +00:00
2022-06-28 01:53:37 +00:00
column_default_sort = ( " experiment_marks_missing " , True )
column_extra_row_actions = [
EndpointLinkRowAction (
icon_class = " fa fa-pencil " ,
2022-06-28 14:57:55 +00:00
endpoint = " assistant_group_experiment.form_view " ,
2022-06-28 01:53:37 +00:00
title = " Edit " ,
)
]
2022-07-02 22:48:05 +00:00
@staticmethod
2022-06-27 20:12:52 +00:00
def part_students_formatter ( view , context , model , name ) :
part_students = deep_getattr ( model , name )
if part_students is not None :
2022-07-02 14:46:02 +00:00
return " , " . join ( str ( part_student . student ) for part_student in part_students )
2022-06-27 20:12:52 +00:00
2022-06-28 01:53:37 +00:00
return " "
2022-06-27 20:12:52 +00:00
2022-07-02 22:48:05 +00:00
@staticmethod
2022-06-27 20:12:52 +00:00
def appointments_formatter ( view , context , model , name ) :
appointments = deep_getattr ( model , name )
if appointments is not None :
2022-07-02 14:46:02 +00:00
return " , " . join ( str ( appointment . date ) for appointment in appointments )
2022-06-27 20:12:52 +00:00
2022-06-28 01:53:37 +00:00
return " "
2022-06-17 20:20:42 +00:00
column_formatters = {
" semester_experiment.experiment " : str_formatter ,
2022-06-27 20:12:52 +00:00
" group.part_students " : part_students_formatter ,
" appointments " : appointments_formatter ,
2022-06-28 01:53:37 +00:00
" experiment_marks_missing " : experiment_marks_missing_formatter ,
2022-06-17 20:20:42 +00:00
}
def query_modifier ( self , query ) :
return (
query . join ( SemesterExperiment )
. where ( SemesterExperiment . semester == current_user . active_semester )
. join ( SemesterExperiment . assistants )
. where ( Assistant . user == current_user )
)
2022-06-20 01:08:57 +00:00
@expose ( " /form/ " , methods = ( " GET " , " POST " ) )
def form_view ( self ) :
group_experiment_id_str = request . args . get ( " id " )
try :
group_experiment = db . session . get ( GroupExperiment , int ( group_experiment_id_str ) )
2022-07-03 15:11:33 +00:00
except Exception :
2022-08-09 12:46:48 +00:00
red = url_for ( " main.index " ) + " assistant/group_experiment "
2022-06-20 01:08:57 +00:00
flash ( " No valid group experiment id " )
return redirect ( red )
if group_experiment not in self . get_query ( ) :
reportBadAttempt ( " Assistant {current_user} tried to edit {group_experiment} " )
2022-09-21 14:52:04 +00:00
self . handle_view_exception ( ModelViewException ( " Unauthorized action! " ) )
return redirect ( self . url )
2022-06-20 01:08:57 +00:00
form , appointments , experiment_marks = assistant_group_experiment_form_factory ( current_user , group_experiment )
num_appointments = len ( appointments )
appointment_fields = [
getattr ( form , f " appointment_ { appointment_num } " ) for appointment_num in range ( 1 , num_appointments + 1 )
]
num_experiment_marks = len ( experiment_marks )
experiment_mark_students = [ experiment_mark . part_student . student for experiment_mark in experiment_marks ]
oral_experiment_mark_fields = [
getattr ( form , f " oral_experiment_mark_ { experiment_mark_num } " )
for experiment_mark_num in range ( 1 , num_experiment_marks + 1 )
]
protocol_experiment_mark_fields = [
getattr ( form , f " protocol_experiment_mark_ { experiment_mark_num } " )
for experiment_mark_num in range ( 1 , num_experiment_marks + 1 )
]
if form . validate_on_submit ( ) :
2022-06-30 17:49:38 +00:00
any_final_experiment_mark_changed = False
2022-06-26 22:20:30 +00:00
try :
for ind , appointment in enumerate ( appointments ) :
appointment . date = appointment_fields [ ind ] . data
2022-06-20 01:08:57 +00:00
2022-06-26 22:20:30 +00:00
for ind , experiment_mark in enumerate ( experiment_marks ) :
2022-06-27 21:27:09 +00:00
form_oral_mark = parse_selection_mark_field ( oral_experiment_mark_fields [ ind ] )
form_protocol_mark = parse_selection_mark_field ( protocol_experiment_mark_fields [ ind ] )
if (
form_oral_mark != experiment_mark . oral_mark
or form_protocol_mark != experiment_mark . protocol_mark
) :
2022-06-30 17:49:38 +00:00
final_experiment_mark_changed = experiment_mark . set_oral_protocol_mark (
form_oral_mark ,
form_protocol_mark ,
call_update_experiment_marks_missing = False ,
)
if final_experiment_mark_changed :
any_final_experiment_mark_changed = True
2022-06-27 21:27:09 +00:00
experiment_mark . assistant = current_user . assistant
2022-06-30 17:49:38 +00:00
if any_final_experiment_mark_changed :
2022-06-30 15:53:46 +00:00
group_experiment . update_experiment_marks_missing ( )
2022-06-28 11:05:57 +00:00
2022-06-26 22:20:30 +00:00
group_experiment . note = form . note . data
db . session . commit ( )
2022-08-09 12:46:48 +00:00
red = url_for ( " main.index " ) + " assistant/group_experiment "
2022-06-26 22:20:30 +00:00
return redirect ( red )
except Exception as ex :
flash ( str ( ex ) , " error " )
db . session . rollback ( )
2022-06-20 01:08:57 +00:00
2022-06-27 20:12:52 +00:00
final_experiment_marks = [ experiment_mark . final_experiment_mark for experiment_mark in experiment_marks ]
2022-06-20 01:08:57 +00:00
return self . render (
2022-09-11 12:55:53 +00:00
" assistant_group_experiment_form.jinja.html " ,
2022-06-20 01:08:57 +00:00
form = form ,
2022-09-19 13:49:57 +00:00
semester_experiment = group_experiment . semester_experiment ,
2022-06-20 01:08:57 +00:00
group_number = group_experiment . group . number ,
appointment_fields = appointment_fields ,
experiment_mark_zip = zip (
2022-06-27 20:12:52 +00:00
experiment_mark_students ,
oral_experiment_mark_fields ,
protocol_experiment_mark_fields ,
final_experiment_marks ,
2022-06-20 01:08:57 +00:00
) ,
)
2022-06-17 20:20:42 +00:00
2021-11-30 00:47:37 +00:00
class AssistantUserView ( SecureAssistantModelView ) :
2022-06-20 01:08:57 +00:00
class EditForm ( FlaskForm ) :
2022-09-12 15:59:54 +00:00
@staticmethod
def semesterQueryFactory ( ) :
# Show only last two semesters to assistants
2023-11-02 17:09:10 +00:00
return Semester . query . order_by ( Semester . id . desc ( ) ) . where ( Semester . done is False ) . limit ( 2 )
2022-09-12 15:59:54 +00:00
active_semester = QuerySelectField (
" Active Semester " ,
query_factory = semesterQueryFactory ,
validators = [ DataRequired ( ) ] ,
default = Semester . lastSemester ,
2022-09-20 13:09:03 +00:00
description = " You should change the active semester to the last semester. Do not forget to click save! Only last two semesters are shown that are not set as done. " ,
2022-09-12 15:59:54 +00:00
)
2022-06-17 18:56:14 +00:00
phone_number , mobile_phone_number , building , room = user_info_fields ( )
2022-02-23 19:36:29 +00:00
2022-06-17 18:56:14 +00:00
generate_new_password = generate_new_password_field ( )
2022-02-23 19:36:29 +00:00
can_edit = True
2022-05-30 13:38:20 +00:00
can_view_details = True
2022-02-23 19:36:29 +00:00
column_display_actions = True
2022-07-02 22:48:05 +00:00
column_sortable_list : list [ str ] = [ ]
2021-11-30 00:47:37 +00:00
column_list = [
" email " ,
" phone_number " ,
" mobile_phone_number " ,
" building " ,
2022-02-23 19:36:29 +00:00
" room " ,
2021-11-30 00:47:37 +00:00
" assistant.semester_experiments " ,
]
column_labels = {
" assistant.semester_experiments " : " Semester Experiments " ,
}
2022-05-21 16:30:23 +00:00
def query_modifier ( self , query ) :
return query . where ( User . id == current_user . id )
2021-11-30 00:47:37 +00:00
2022-02-23 19:36:29 +00:00
def on_model_change ( self , form , model , is_created ) :
if form . generate_new_password . data :
password = randomPassword ( )
2022-06-28 15:01:42 +00:00
flashRandomPassword ( model . email , password )
2022-02-23 19:36:29 +00:00
admin_change_password ( model , password , notify = False ) # Password is automatically hashed with this function
2021-11-30 00:47:37 +00:00
2022-02-23 18:37:09 +00:00
class AssistantDocsView ( SecureAssistantBaseView ) :
2022-03-04 02:49:02 +00:00
@expose ( " / " )
2022-02-23 18:37:09 +00:00
def index ( self ) :
2022-09-11 12:55:53 +00:00
return self . render ( " docs/docs.jinja.html " , role = " assistant " )
2022-02-23 18:37:09 +00:00
2022-08-09 12:46:48 +00:00
def init_assistant_model_views ( app ) :
assistantSpace . add_view ( AssistantGroupExperimentView ( GroupExperiment , url = " group_experiment " ) )
assistantSpace . add_view ( AssistantDocsView ( name = " Docs " , url = " docs " ) )
2021-07-30 22:12:37 +00:00
2022-09-21 14:52:04 +00:00
# Don't add to menu
# Has to be placed before assistantSpace.init_app
assistantSpace . _views . append ( AssistantUserView ( User , url = " user " ) )
assistantSpace . add_link ( MenuLink ( name = " User settings " , url = " /user-settings " ) )
2022-09-11 18:05:09 +00:00
assistantSpace . add_link ( MenuLink ( name = " Logout " , url = " /logout " ) )
2022-09-21 14:52:04 +00:00
assistantSpace . init_app ( app )