# -*- coding: utf-8 -*-
##############################################################################
#
# ScoDoc
#
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""
Module scolar: vues de .../ScoDoc/ """
]
tf = TrivialFormulator(
request.base_url,
scu.get_request_args(),
(
("adresse_id", {"input_type": "hidden"}),
("etudid", {"input_type": "hidden"}),
(
"email",
{
"size": 40,
"title": "e-mail",
"explanation": "adresse institutionnelle",
},
),
(
"emailperso",
{
"size": 40,
"title": "e-mail",
"explanation": "adresse personnelle",
},
),
(
"domicile",
{"size": 65, "explanation": "numéro, rue", "title": "Adresse"},
),
("codepostaldomicile", {"size": 6, "title": "Code postal"}),
("villedomicile", {"size": 20, "title": "Ville"}),
("paysdomicile", {"size": 20, "title": "Pays"}),
("", {"input_type": "separator", "default": " "}),
("telephone", {"size": 13, "title": "Téléphone"}),
("telephonemobile", {"size": 13, "title": "Mobile"}),
),
initvalues=adr,
submitlabel="Valider le formulaire",
)
dest_url = url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return flask.redirect(dest_url)
else:
if adrs:
sco_etud.adresse_edit(cnx, args=tf[2])
else:
sco_etud.adresse_create(cnx, args=tf[2])
logdb(cnx, method="changeCoordonnees", etudid=etudid)
return flask.redirect(dest_url)
# --- Gestion des groupes:
sco_publish(
"/affect_groups",
sco_groups_edit.affect_groups,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/XMLgetGroupsInPartition", sco_groups.XMLgetGroupsInPartition, Permission.ScoView
)
sco_publish(
"/formsemestre_partition_list",
sco_groups.formsemestre_partition_list,
Permission.ScoView,
)
sco_publish("/setGroups", sco_groups.setGroups, Permission.ScoView, methods=["POST"])
sco_publish(
"/group_rename",
sco_groups.group_rename,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/groups_auto_repartition",
sco_groups.groups_auto_repartition,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/edit_partition_form",
sco_groups.edit_partition_form,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/partition_delete",
sco_groups.partition_delete,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/partition_set_attr",
sco_groups.partition_set_attr,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/partition_move",
sco_groups.partition_move,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/partition_set_name",
sco_groups.partition_set_name,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/partition_rename",
sco_groups.partition_rename,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/partition_create",
sco_groups.partition_create,
Permission.ScoView, # controle d'access ad-hoc
methods=["GET", "POST"],
)
# Nouvel éditeur de partitions et groupe, @SebL Jul 2022
@bp.route("/partition_editor", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def partition_editor(formsemestre_id: int):
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
formsemestre.setup_parcours_groups()
H = [
html_sco_header.sco_header(
cssstyles=["css/partition_editor.css"],
javascripts=[
"js/partition_editor.js",
],
page_title=f"Partitions de {formsemestre.titre_annee()}",
init_datatables=True,
),
f""" Photo actuelle (%(photoloc)s):
"""
% etud,
sco_photos.etud_photo_html(etud, title="photo actuelle"),
""" Le fichier ne doit pas dépasser 500Ko (recadrer l'image, format "portrait" de préférence). L'image sera automagiquement réduite pour obtenir une hauteur de 90 pixels. Erreur: {err_msg} Confirmer la suppression de la photo de {etud.nom_disp()} ? Attention: il y a des décisions de jury déjà prises ! Confirmer l'annulation de la {operation_name} ? En général, il est recommandé d'importer les
étudiants depuis Apogée ou via un fichier Excel (menu Inscriptions
dans le semestre).
N'utilisez ce formulaire au cas par cas que pour les cas particuliers
ou si votre établissement n'utilise pas d'autre logiciel de gestion des
inscriptions.
L'étudiant créé ne sera pas inscrit.
Pensez à l'inscrire dans un semestre ! %s Pas d'informations d'Apogée " + A + F
elif tf[0] == -1:
return "\n".join(H) + tf[1] + " " + A + F
# return '\n'.join(H) + ' "
+ A
+ F
)
# log('NbHomonyms=%s' % NbHomonyms)
if not tf[2]["dont_check_homonyms"] and NbHomonyms > 0:
return (
"\n".join(H)
+ tf_error_message(
"""Attention: il y a déjà un étudiant portant des noms et prénoms proches. Vous pouvez forcer la présence d'un homonyme en cochant "autoriser les homonymes" en bas du formulaire."""
)
+ tf[1]
+ " "
+ A
+ F
)
if not edit:
etud = sco_etud.create_etud(cnx, args=tf[2])
etudid = etud["etudid"]
else:
# modif d'un etudiant
sco_etud.etudident_edit(cnx, tf[2])
etud = sco_etud.etudident_list(cnx, {"etudid": etudid})[0]
sco_etud.fill_etuds_info([etud])
# Inval semesters with this student:
to_inval = [s["formsemestre_id"] for s in etud["sems"]]
for formsemestre_id in to_inval:
sco_cache.invalidate_formsemestre(
formsemestre_id=formsemestre_id
) # > etudident_create_or_edit
#
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
@bp.route("/etudident_delete", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func
def etudident_delete(etudid, dialog_confirmed=False):
"Delete a student"
cnx = ndb.GetDBConnexion()
etuds = sco_etud.etudident_list(cnx, {"etudid": etudid})
if not etuds:
raise ScoValueError("Étudiant inexistant !")
else:
etud = etuds[0]
sco_etud.fill_etuds_info([etud])
if not dialog_confirmed:
return scu.confirm_dialog(
"""Opérations effectuées sur l'étudiant %(nomprenom)s
" % etud,
filename="log_" + scu.make_filename(etud["nomprenom"]),
html_next_section=f"""
""",
preferences=sco_preferences.SemPreferences(),
)
return tab.make_page(format=format)
# ---------- PAGE ACCUEIL (listes) --------------
@bp.route("/", alias=True)
@bp.route("/index_html")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def index_html(showcodes=0, showsemtable=0):
return sco_dept.index_html(showcodes=showcodes, showsemtable=showsemtable)
@bp.route("/install_info")
@scodoc
@permission_required(Permission.ScoView)
def install_info():
"""Information on install status (html str)"""
return sco_up_to_date.is_up_to_date()
@bp.route("/dept_news")
@scodoc
@permission_required(Permission.ScoView)
def dept_news():
"Affiche table des dernières opérations"
return render_template(
"dept_news.j2", title=f"Opérations {g.scodoc_dept}", sco=ScoData()
)
@bp.route("/dept_news_json")
@scodoc
@permission_required(Permission.ScoView)
def dept_news_json():
"Table des news du département"
start = request.args.get("start", type=int)
length = request.args.get("length", type=int)
log(f"dept_news_json( start={start}, length={length})")
query = ScolarNews.query.filter_by(dept_id=g.scodoc_dept_id)
# search
search = request.args.get("search[value]")
if search:
query = query.filter(
db.or_(
ScolarNews.authenticated_user.like(f"%{search}%"),
ScolarNews.text.like(f"%{search}%"),
)
)
total_filtered = query.count()
# sorting
order = []
i = 0
while True:
col_index = request.args.get(f"order[{i}][column]")
if col_index is None:
break
col_name = request.args.get(f"columns[{col_index}][data]")
if col_name not in ["date", "type", "authenticated_user"]:
col_name = "date"
descending = request.args.get(f"order[{i}][dir]") == "desc"
col = getattr(ScolarNews, col_name)
if descending:
col = col.desc()
order.append(col)
i += 1
if order:
query = query.order_by(*order)
# pagination
query = query.offset(start).limit(length)
data = [news.to_dict() for news in query]
# response
return {
"data": data,
"recordsFiltered": total_filtered,
"recordsTotal": ScolarNews.query.count(),
"draw": request.args.get("draw", type=int),
}
sco_publish(
"/trombino", sco_trombino.trombino, Permission.ScoView, methods=["GET", "POST"]
)
sco_publish(
"/pdf_trombino_tours", sco_trombino_tours.pdf_trombino_tours, Permission.ScoView
)
sco_publish(
"/pdf_feuille_releve_absences",
sco_trombino_tours.pdf_feuille_releve_absences,
Permission.ScoView,
)
sco_publish(
"/trombino_copy_photos",
sco_trombino.trombino_copy_photos,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/groups_export_annotations",
sco_groups_exports.groups_export_annotations,
Permission.ScoView,
)
@bp.route("/groups_view")
@scodoc
@permission_required_compat_scodoc7(Permission.ScoView)
@scodoc7func
def groups_view(
group_ids=(),
format="html",
# Options pour listes:
with_codes=0,
etat=None,
with_paiement=0, # si vrai, ajoute colonnes infos paiement droits et finalisation inscription (lent car interrogation portail)
with_archives=0, # ajoute colonne avec noms fichiers archivés
with_annotations=0,
with_bourse=0,
formsemestre_id=None,
):
return sco_groups_view.groups_view(
group_ids=group_ids,
format=format,
# Options pour listes:
with_codes=with_codes,
etat=etat,
with_paiement=with_paiement,
with_archives=with_archives,
with_annotations=with_annotations,
with_bourse=with_bourse,
formsemestre_id=formsemestre_id,
)
sco_publish(
"/export_groups_as_moodle_csv",
sco_groups_view.export_groups_as_moodle_csv,
Permission.ScoView,
)
# -------------------------- INFOS SUR ETUDIANTS --------------------------
@bp.route("/getEtudInfo")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def getEtudInfo(etudid=False, code_nip=False, filled=False, format=None):
"""infos sur un etudiant (API)
On peut specifier etudid ou code_nip
ou bien cherche dans les arguments de la requête: etudid, code_nip, code_ine
(dans cet ordre).
"""
etud = sco_etud.get_etud_info(etudid=etudid, code_nip=code_nip, filled=filled)
if format is None:
return etud
else:
return scu.sendResult(etud, name="etud", format=format)
sco_publish(
"/search_etud_in_dept",
sco_find_etud.search_etud_in_dept,
Permission.ScoView,
methods=["GET", "POST"],
)
@bp.route("/search_etud_by_name")
@bp.route("/Notes/search_etud_by_name") # for JS apis
@scodoc
@permission_required(Permission.ScoView)
def search_etud_by_name():
term = request.args["term"]
data = sco_find_etud.search_etud_by_name(term)
return jsonify(data)
# XMLgetEtudInfos était le nom dans l'ancienne API ScoDoc 6
@bp.route("/etud_info", methods=["GET", "POST"]) # pour compat anciens clients PHP)
@bp.route(
"/XMLgetEtudInfos", methods=["GET", "POST"]
) # pour compat anciens clients PHP)
@bp.route(
"/Absences/XMLgetEtudInfos", methods=["GET", "POST"]
) # pour compat anciens clients PHP
@bp.route(
"/Notes/XMLgetEtudInfos", methods=["GET", "POST"]
) # pour compat anciens clients PHP
@scodoc
@permission_required_compat_scodoc7(Permission.ScoView)
@scodoc7func
def etud_info(etudid=None, format="xml"):
"Donne les informations sur un etudiant"
if not format in ("xml", "json"):
raise ScoValueError("format demandé non supporté par cette fonction.")
t0 = time.time()
args = make_etud_args(etudid=etudid)
cnx = ndb.GetDBConnexion()
etuds = sco_etud.etudident_list(cnx, args)
if not etuds:
# etudiant non trouvé: message d'erreur
d = {
"etudid": etudid,
"nom": "?",
"nom_usuel": "",
"prenom": "?",
"civilite": "?",
"sexe": "?", # for backward compat
"email": "?",
"emailperso": "",
"error": "code etudiant inconnu",
}
return scu.sendResult(
d, name="etudiant", format=format, force_outer_xml_tag=False
)
d = {}
etud = etuds[0]
sco_etud.fill_etuds_info([etud])
etud["date_naissance_iso"] = ndb.DateDMYtoISO(etud["date_naissance"])
for a in (
"etudid",
"code_nip",
"code_ine",
"nom",
"nom_usuel",
"prenom",
"nomprenom",
"prenom_etat_civil",
"email",
"emailperso",
"domicile",
"codepostaldomicile",
"villedomicile",
"paysdomicile",
"telephone",
"telephonemobile",
"fax",
"bac",
"specialite",
"annee_bac",
"nomlycee",
"villelycee",
"codepostallycee",
"codelycee",
"date_naissance_iso",
):
d[a] = etud[a] # ne pas quoter car ElementTree.tostring quote déjà
d["civilite"] = etud["civilite_str"] # exception: ne sort pas la civilite brute
d["civilite_etat_civil"] = etud[
"civilite_etat_civil_str"
] # exception: ne sort pas la civilite brute
d["sexe"] = d["civilite"] # backward compat pour anciens clients
d["photo_url"] = sco_photos.etud_photo_url(etud)
sem = etud["cursem"]
if sem:
sco_groups.etud_add_group_infos(etud, sem["formsemestre_id"] if sem else None)
d["insemestre"] = [
{
"current": "1",
"formsemestre_id": sem["formsemestre_id"],
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
"date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
"etat": sem["ins"]["etat"],
"groupes": etud["groupes"], # slt pour semestre courant
}
]
else:
d["insemestre"] = []
for sem in etud["sems"]:
if sem != etud["cursem"]:
d["insemestre"].append(
{
"formsemestre_id": sem["formsemestre_id"],
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]),
"date_fin": ndb.DateDMYtoISO(sem["date_fin"]),
"etat": sem["ins"]["etat"],
}
)
log("etud_info (%gs)" % (time.time() - t0))
return scu.sendResult(
d, name="etudiant", format=format, force_outer_xml_tag=False, quote_xml=False
)
# -------------------------- FICHE ETUDIANT --------------------------
sco_publish("/ficheEtud", sco_page_etud.ficheEtud, Permission.ScoView)
sco_publish(
"/etud_upload_file_form",
sco_archives_etud.etud_upload_file_form,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/etud_delete_archive",
sco_archives_etud.etud_delete_archive,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/etud_get_archived_file",
sco_archives_etud.etud_get_archived_file,
Permission.ScoView,
)
sco_publish(
"/etudarchive_import_files_form",
sco_archives_etud.etudarchive_import_files_form,
Permission.ScoView,
methods=["GET", "POST"],
)
sco_publish(
"/etudarchive_generate_excel_sample",
sco_archives_etud.etudarchive_generate_excel_sample,
Permission.ScoView,
)
# Debouche / devenir etudiant
sco_publish(
"/itemsuivi_suppress",
sco_debouche.itemsuivi_suppress,
Permission.ScoEtudChangeAdr,
methods=["GET", "POST"],
)
sco_publish(
"/itemsuivi_create",
sco_debouche.itemsuivi_create,
Permission.ScoEtudChangeAdr,
methods=["GET", "POST"],
)
sco_publish(
"/itemsuivi_set_date",
sco_debouche.itemsuivi_set_date,
Permission.ScoEtudChangeAdr,
methods=["GET", "POST"],
)
sco_publish(
"/itemsuivi_set_situation",
sco_debouche.itemsuivi_set_situation,
Permission.ScoEtudChangeAdr,
methods=["GET", "POST"],
)
sco_publish(
"/itemsuivi_list_etud", sco_debouche.itemsuivi_list_etud, Permission.ScoView
)
sco_publish("/itemsuivi_tag_list", sco_debouche.itemsuivi_tag_list, Permission.ScoView)
sco_publish(
"/itemsuivi_tag_search", sco_debouche.itemsuivi_tag_search, Permission.ScoView
)
sco_publish(
"/itemsuivi_tag_set",
sco_debouche.itemsuivi_tag_set,
Permission.ScoEtudChangeAdr,
methods=["GET", "POST"],
)
@bp.route("/doAddAnnotation", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudAddAnnotations)
@scodoc7func
def doAddAnnotation(etudid, comment):
"ajoute annotation sur etudiant"
_ = Identite.get_etud(etudid) # check existence
if comment:
cnx = ndb.GetDBConnexion()
sco_etud.etud_annotations_create(
cnx,
args={
"etudid": etudid,
"comment": comment,
"author": current_user.user_name,
},
)
logdb(cnx, method="addAnnotation", etudid=etudid)
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
@bp.route("/doSuppressAnnotation", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def doSuppressAnnotation(etudid, annotation_id):
"""Suppression annotation."""
if not sco_permissions_check.can_suppress_annotation(annotation_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
cnx = ndb.GetDBConnexion()
annos = sco_etud.etud_annotations_list(cnx, args={"id": annotation_id})
if len(annos) != 1:
raise ScoValueError("annotation inexistante !")
anno = annos[0]
log("suppress annotation: %s" % str(anno))
logdb(cnx, method="SuppressAnnotation", etudid=etudid)
sco_etud.etud_annotations_delete(cnx, annotation_id)
flash("Annotation supprimée")
return flask.redirect(
url_for(
"scolar.ficheEtud",
scodoc_dept=g.scodoc_dept,
etudid=etudid,
)
)
@bp.route("/form_change_coordonnees", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudChangeAdr)
@scodoc7func
def form_change_coordonnees(etudid):
"edit coordonnees etudiant"
etud = Identite.get_etud(etudid)
cnx = ndb.GetDBConnexion()
adrs = sco_etud.adresse_list(cnx, {"etudid": etudid})
if adrs:
adr = adrs[0]
else:
adr = {} # no data for this student
H = [
f"""{html_sco_header.sco_header(
page_title=f"Changement coordonnées de {etud.nomprenom}"
)}
Changement des coordonnées de {etud.nomprenom}
""",
render_template(
"scolar/partition_editor.j2",
formsemestre=formsemestre,
read_only=not sco_groups.sco_permissions_check.can_change_groups(
formsemestre_id
),
),
html_sco_header.sco_footer(),
]
return "\n".join(H)
# Future page pour répartition parcours
@bp.route("/students_groups_auto_assignment", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def students_groups_auto_assignment(formsemestre_id: int):
"""En cours de dev."""
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
H = [
html_sco_header.sco_header(
page_title=f"DEV",
),
f"""
""",
render_template(
"scolar/students_groups_auto_assignment.j2",
formsemestre=formsemestre,
),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@bp.route("/create_partition_parcours", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def create_partition_parcours(formsemestre_id):
"""Création d'une partitions nommée "Parcours" (PARTITION_PARCOURS)
avec un groupe par parcours."""
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
formsemestre.setup_parcours_groups()
return flask.redirect(
url_for(
"scolar.edit_partition_form",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)
)
sco_publish("/etud_info_html", sco_page_etud.etud_info_html, Permission.ScoView)
# --- Gestion des photos:
sco_publish("/get_photo_image", sco_photos.get_photo_image, Permission.ScoView)
sco_publish("/etud_photo_html", sco_photos.etud_photo_html, Permission.ScoView)
@bp.route("/etud_photo_orig_page")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
def etud_photo_orig_page(etudid=None):
"Page with photo in orig. size"
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
H = [
html_sco_header.sco_header(page_title=etud["nomprenom"]),
"%s
" % etud["nomprenom"],
'",
html_sco_header.sco_footer(),
]
return "\n".join(H)
@bp.route("/form_change_photo", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudChangeAdr)
@scodoc7func
def form_change_photo(etudid=None):
"""Formulaire changement photo étudiant"""
etud = sco_etud.get_etud_info(filled=True)[0]
if sco_photos.etud_photo_is_local(etud):
etud["photoloc"] = "dans ScoDoc"
else:
etud["photoloc"] = "externe"
H = [
html_sco_header.sco_header(page_title="Changement de photo"),
"""Changement de la photo de %(nomprenom)s
{operation_name} de {etud.nomprenom} ({formsemestre.titre_mois()})
Création d'un étudiant
Modification d\'un étudiant (fiche)
'
% url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
initvalues = sco_etud.etudident_list(cnx, {"etudid": etudid})
assert len(initvalues) == 1
initvalues = initvalues[0]
submitlabel = "Modifier les données"
vals = scu.get_request_args()
nom = vals.get("nom", None)
if nom is None:
nom = initvalues.get("nom", None)
if nom is None:
infos = []
else:
prenom = vals.get("prenom", "")
if vals.get("tf_submitted", False) and not prenom:
prenom = initvalues.get("prenom", "")
infos = sco_portal_apogee.get_infos_apogee(nom, prenom)
if infos:
formatted_infos = [
"""
"""
]
nanswers = len(infos)
nmax = 10 # nb max de reponse montrées
infos = infos[:nmax]
for i in infos:
formatted_infos.append("
")
m = "%d étudiants trouvés" % nanswers
if len(infos) != nanswers:
m += " (%d montrés)" % len(infos)
A = """")
for k in i.keys():
if k != "nip":
item = "
Informations Apogée
annulation
' + F
else:
# form submission
if edit:
etudid = tf[2]["etudid"]
else:
etudid = None
ok, NbHomonyms = sco_etud.check_nom_prenom(
cnx, nom=tf[2]["nom"], prenom=tf[2]["prenom"], etudid=etudid
)
if not ok:
return (
"\n".join(H)
+ tf_error_message("Nom ou prénom invalide")
+ tf[1]
+ "Confirmer la suppression de l'étudiant {e[nomprenom]} ?
Prenez le temps de vérifier que vous devez vraiment supprimer cet étudiant !
Cette opération irréversible efface toute trace de l'étudiant: inscriptions, notes, absences... dans tous les semestres qu'il a fréquenté.
Dans la plupart des cas, vous avez seulement besoin de le
Vérifier la fiche de {e[nomprenom]}
""".format( e=etud, fiche_url=url_for( "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid ), ), dest_url="", cancel_url=url_for( "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid ), OK="Supprimer définitivement cet étudiant", parameters={"etudid": etudid}, ) log("etudident_delete: etudid=%(etudid)s nomprenom=%(nomprenom)s" % etud) # delete in all tables ! tables = [ "notes_appreciations", "scolar_autorisation_inscription", "scolar_formsemestre_validation", "scolar_events", "notes_notes_log", "notes_notes", "notes_moduleimpl_inscription", "notes_formsemestre_inscription", "group_membership", "etud_annotations", "scolog", "admissions", "adresse", "absences", "absences_notifications", "billet_absence", ] cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) for table in tables: cursor.execute("delete from %s where etudid=%%(etudid)s" % table, etud) cursor.execute("delete from identite where id=%(etudid)s", etud) cnx.commit() # Inval semestres où il était inscrit: to_inval = [s["formsemestre_id"] for s in etud["sems"]] for formsemestre_id in to_inval: sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id) # > flash("Étudiant supprimé !") return flask.redirect(scu.ScoURL()) @bp.route("/check_group_apogee") @scodoc @permission_required(Permission.ScoEtudInscrit) @scodoc7func def check_group_apogee(group_id, etat=None, fix=False, fixmail=False): """Verification des codes Apogee et mail de tout un groupe. Si fix == True, change les codes avec Apogée. XXX A re-écrire pour API 2: prendre liste dans l'étape et vérifier à partir de cela. """ etat = etat or None members, group, _, sem, _ = sco_groups.get_group_infos(group_id, etat=etat) formsemestre_id = group["formsemestre_id"] cnx = ndb.GetDBConnexion() H = [ html_sco_header.html_sem_header( "Étudiants du %s" % (group["group_name"] or "semestre") ), 'Nom | Nom usuel | Prénom | NIP (ScoDoc) | Apogée | |
---|---|---|---|---|---|
%s | %s | %s | %s | %s | %s |
Retour au semestre """ % ( request.base_url, formsemestre_id, scu.strnone(group_id), scu.strnone(etat), formsemestre_id, ) ) H.append( """
Retour au semestre """ % ( request.base_url, formsemestre_id, scu.strnone(group_id), scu.strnone(etat), formsemestre_id, ) ) return "\n".join(H) + html_sco_header.sco_footer() @bp.route("/form_students_import_excel", methods=["GET", "POST"]) @scodoc @permission_required(Permission.ScoEtudInscrit) @scodoc7func def form_students_import_excel(formsemestre_id=None): "formulaire import xls" formsemestre_id = int(formsemestre_id) if formsemestre_id else None if formsemestre_id: sem = sco_formsemestre.get_formsemestre(formsemestre_id) dest_url = url_for( "notes.formsemestre_status", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, ) else: sem = None dest_url = scu.ScoURL() if sem and not sem["etat"]: raise ScoValueError("Modification impossible: semestre verrouille") H = [ html_sco_header.sco_header(page_title="Import etudiants"), """
A utiliser pour importer de nouveaux étudiants (typiquement au premier semestre).
Si les étudiants à inscrire sont déjà dans un autre semestre, utiliser le menu "Inscriptions (passage des étudiants) depuis d'autres semestres à partir du semestre destination.
Si vous avez un portail Apogée, il est en général préférable d'importer les étudiants depuis Apogée, via le menu "Synchroniser avec étape Apogée".
L'opération se déroule en deux étapes. Dans un premier temps, vous téléchargez une feuille Excel type. Vous devez remplir cette feuille, une ligne décrivant chaque étudiant. Ensuite, vous indiquez le nom de votre fichier dans la case "Fichier Excel" ci-dessous, et cliquez sur "Télécharger" pour envoyer au serveur votre liste.
""", ] # ' if sem: H.append( """Les étudiants importés seront inscrits dans le semestre %s
""" % sem["titremois"] ) else: H.append( """Pour inscrire directement les étudiants dans un semestre de formation, il suffit d'indiquer le code de ce semestre (qui doit avoir été créé au préalable). Cliquez ici pour afficher les codes
""" % (scu.ScoURL()) ) H.append("""Le fichier Excel décrivant les étudiants doit comporter les colonnes suivantes.
Les colonnes peuvent être placées dans n'importe quel ordre, mais le titre exact (tel que ci-dessous) doit être sur la première ligne.
Les champs avec un astérisque (*) doivent être présents (nulls non autorisés).
Attribut | Type | Description | |
%s | %s | %s | %s |