forked from ScoDoc/ScoDoc
code cleaning
This commit is contained in:
parent
4925a57f8a
commit
c91aabee8d
File diff suppressed because it is too large
Load Diff
|
@ -1,352 +1,353 @@
|
|||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gestion scolarite IUT
|
||||
#
|
||||
# Copyright (c) 1999 - 2022 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
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Gestion des images logos (nouveau ScoDoc 9.1)
|
||||
|
||||
Les logos sont `logo_header.<ext>` et `logo_footer.<ext>`
|
||||
avec `ext` membre de LOGOS_IMAGES_ALLOWED_TYPES (= jpg, png)
|
||||
|
||||
SCODOC_LOGOS_DIR /opt/scodoc-data/config/logos
|
||||
"""
|
||||
import glob
|
||||
import imghdr
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from flask import abort, current_app, url_for
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from app import Departement, ScoValueError
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from PIL import Image as PILImage
|
||||
|
||||
GLOBAL = "_" # category for server level logos
|
||||
|
||||
|
||||
def find_logo(logoname, dept_id=None, strict=False, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""
|
||||
"Recherche un logo 'name' existant.
|
||||
Deux strategies:
|
||||
si strict:
|
||||
reherche uniquement dans le département puis si non trouvé au niveau global
|
||||
sinon
|
||||
On recherche en local au dept d'abord puis si pas trouvé recherche globale
|
||||
quelque soit la stratégie, retourne None si pas trouvé
|
||||
:param logoname: le nom recherche
|
||||
:param dept_id: l'id du département dans lequel se fait la recherche (None si global)
|
||||
:param strict: stratégie de recherche (strict = False => dept ou global)
|
||||
:param prefix: le prefix utilisé (parmi scu.LOGO_FILE_PREFIX / scu.BACKGROUND_FILE_PREFIX)
|
||||
:return: un objet Logo désignant le fichier image trouvé (ou None)
|
||||
"""
|
||||
logo = Logo(logoname, dept_id, prefix).select()
|
||||
if logo is None and not strict:
|
||||
logo = Logo(logoname=logoname, dept_id=None, prefix=prefix).select()
|
||||
return logo
|
||||
|
||||
|
||||
def delete_logo(name, dept_id=None):
|
||||
"""Delete all files matching logo (dept_id, name) (including all allowed extensions)
|
||||
Args:
|
||||
name: The name of the logo
|
||||
dept_id: the dept_id (if local). Use None to destroy globals logos
|
||||
"""
|
||||
logo = find_logo(logoname=name, dept_id=dept_id)
|
||||
while logo is not None:
|
||||
os.unlink(logo.select().filepath)
|
||||
logo = find_logo(logoname=name, dept_id=dept_id)
|
||||
|
||||
|
||||
def write_logo(stream, name, dept_id=None):
|
||||
"""Crée le fichier logo sur le serveur.
|
||||
Le suffixe du fichier (parmi LOGO_IMAGES_ALLOWED_TYPES) est déduit du contenu du stream"""
|
||||
Logo(logoname=name, dept_id=dept_id).create(stream)
|
||||
|
||||
|
||||
def rename_logo(old_name, new_name, dept_id):
|
||||
logo = find_logo(old_name, dept_id, True)
|
||||
logo.rename(new_name)
|
||||
|
||||
|
||||
def list_logos():
|
||||
"""Crée l'inventaire de tous les logos existants.
|
||||
L'inventaire se présente comme un dictionnaire de dictionnaire de Logo:
|
||||
[None][name] pour les logos globaux
|
||||
[dept_id][name] pour les logos propres à un département (attention id numérique du dept)
|
||||
Les départements sans logos sont absents du résultat
|
||||
"""
|
||||
inventory = {None: _list_dept_logos()} # logos globaux (header / footer)
|
||||
for dept in Departement.query.filter_by(visible=True).all():
|
||||
logos_dept = _list_dept_logos(dept_id=dept.id)
|
||||
if logos_dept:
|
||||
inventory[dept.id] = _list_dept_logos(dept.id)
|
||||
return inventory
|
||||
|
||||
|
||||
def _list_dept_logos(dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""Inventorie toutes les images existantes pour un niveau (GLOBAL ou un département).
|
||||
retourne un dictionnaire de Logo [logoname] -> Logo
|
||||
les noms des fichiers concernés doivent être de la forme: <rep>/<prefix><name>.<suffixe>
|
||||
<rep> : répertoire de recherche (déduit du dept_id)
|
||||
<prefix>: le prefix (LOGO_FILE_PREFIX pour les logos)
|
||||
<suffix>: un des suffixes autorisés
|
||||
:param dept_id: l'id du departement concerné (si None -> global)
|
||||
:param prefix: le préfixe utilisé
|
||||
:return: le résultat de la recherche ou None si aucune image trouvée
|
||||
"""
|
||||
allowed_ext = "|".join(scu.LOGOS_IMAGES_ALLOWED_TYPES)
|
||||
# parse filename 'logo_<logoname>.<ext> . be carefull: logoname may include '.'
|
||||
filename_parser = re.compile(f"{prefix}(([^.]*.)+)({allowed_ext})")
|
||||
logos = {}
|
||||
path_dir = Path(scu.SCODOC_LOGOS_DIR)
|
||||
if dept_id:
|
||||
path_dir = Path(
|
||||
os.path.sep.join(
|
||||
[scu.SCODOC_LOGOS_DIR, scu.LOGOS_DIR_PREFIX + str(dept_id)]
|
||||
)
|
||||
)
|
||||
if path_dir.exists():
|
||||
for entry in path_dir.iterdir():
|
||||
if os.access(path_dir.joinpath(entry).absolute(), os.R_OK):
|
||||
result = filename_parser.match(entry.name)
|
||||
if result:
|
||||
logoname = result.group(1)[
|
||||
:-1
|
||||
] # retreive logoname from filename (less final dot)
|
||||
logos[logoname] = Logo(logoname=logoname, dept_id=dept_id).select()
|
||||
return logos if len(logos.keys()) > 0 else None
|
||||
|
||||
|
||||
class Logo:
|
||||
"""Responsable des opérations (select, create), du calcul des chemins et url
|
||||
ainsi que de la récupération des informations sur un logo.
|
||||
Usage:
|
||||
logo existant: Logo(<name>, <dept_id>, ...).select() (retourne None si fichier non trouvé)
|
||||
logo en création: Logo(<name>, <dept_id>, ...).create(stream)
|
||||
Les attributs filename, filepath, get_url() ne devraient pas être utilisés avant les opérations
|
||||
select ou save (le format n'est pas encore connu à ce moement là)
|
||||
"""
|
||||
|
||||
def __init__(self, logoname, dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""Initialisation des noms et département des logos.
|
||||
if prefix = None on recherche simplement une image 'logoname.*'
|
||||
Le format est renseigné au moment de la lecture (select) ou de la création (create) de l'objet
|
||||
"""
|
||||
self.logoname = secure_filename(logoname)
|
||||
if not self.logoname:
|
||||
self.logoname = "*** *** nom de logo invalide *** à changer ! *** ***"
|
||||
self.scodoc_dept_id = dept_id
|
||||
self.prefix = prefix or ""
|
||||
if self.scodoc_dept_id:
|
||||
self.dirpath = os.path.sep.join(
|
||||
[
|
||||
scu.SCODOC_LOGOS_DIR,
|
||||
scu.LOGOS_DIR_PREFIX + secure_filename(str(dept_id)),
|
||||
]
|
||||
)
|
||||
else:
|
||||
self.dirpath = scu.SCODOC_LOGOS_DIR
|
||||
self.basepath = os.path.sep.join(
|
||||
[self.dirpath, self.prefix + secure_filename(self.logoname)]
|
||||
)
|
||||
# next attributes are computed by the select function
|
||||
self.suffix = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.filepath = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.filename = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.size = "Not initialized: call the select or create function before access"
|
||||
self.aspect_ratio = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.density = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.mm = "Not initialized: call the select or create function before access"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Logo(logoname='{self.logoname}', filename='{self.filename}')"
|
||||
|
||||
def _set_format(self, fmt):
|
||||
self.suffix = fmt
|
||||
self.filepath = self.basepath + "." + fmt
|
||||
self.filename = self.logoname + "." + fmt
|
||||
|
||||
def _ensure_directory_exists(self):
|
||||
"create enclosing directory if necessary"
|
||||
if not Path(self.dirpath).exists():
|
||||
current_app.logger.info("sco_logos creating directory %s", self.dirpath)
|
||||
os.mkdir(self.dirpath)
|
||||
|
||||
def create(self, stream):
|
||||
img_type = guess_image_type(stream)
|
||||
if img_type not in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
raise ScoValueError("type d'image invalide")
|
||||
self._set_format(img_type)
|
||||
self._ensure_directory_exists()
|
||||
filename = self.basepath + "." + self.suffix
|
||||
with open(filename, "wb") as f:
|
||||
f.write(stream.read())
|
||||
current_app.logger.info("sco_logos.store_image %s", self.filename)
|
||||
# erase other formats if they exists
|
||||
for suffix in set(scu.LOGOS_IMAGES_ALLOWED_TYPES) - set([img_type]):
|
||||
try:
|
||||
os.unlink(self.basepath + "." + suffix)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
def _read_info(self, img):
|
||||
"""computes some properties from the real image
|
||||
aspect_ratio assumes that x_density and y_density are equals
|
||||
"""
|
||||
x_size, y_size = img.size
|
||||
self.density = img.info.get("dpi", None)
|
||||
unit = 1
|
||||
if self.density is None: # no dpi found try jfif infos
|
||||
self.density = img.info.get("jfif_density", None)
|
||||
unit = img.info.get("jfif_unit", 0) # 0 = no unit ; 1 = inch ; 2 = mm
|
||||
if self.density is not None:
|
||||
x_density, y_density = self.density
|
||||
if unit != 0 and x_density != 0 and y_density != 0:
|
||||
unit2mm = [0, 1 / 0.254, 0.1][unit]
|
||||
x_mm = round(x_size * unit2mm / x_density, 2)
|
||||
y_mm = round(y_size * unit2mm / y_density, 2)
|
||||
self.mm = (x_mm, y_mm)
|
||||
else:
|
||||
self.mm = None
|
||||
else:
|
||||
self.mm = None
|
||||
self.size = (x_size, y_size)
|
||||
self.aspect_ratio = round(float(x_size) / y_size, 2)
|
||||
|
||||
def select(self):
|
||||
"""
|
||||
Récupération des données pour un logo existant
|
||||
il doit exister un et un seul fichier image parmi de suffixe/types autorisés
|
||||
(sinon on prend le premier trouvé)
|
||||
cette opération permet d'affiner le format d'un logo de format inconnu
|
||||
"""
|
||||
for suffix in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
path = Path(self.basepath + "." + suffix)
|
||||
if path.exists():
|
||||
self._set_format(suffix)
|
||||
with open(self.filepath, "rb") as f:
|
||||
img = PILImage.open(f)
|
||||
self._read_info(img)
|
||||
return self
|
||||
return None
|
||||
|
||||
def get_url(self):
|
||||
"""Retourne l'URL permettant d'obtenir l'image du logo"""
|
||||
return url_for(
|
||||
"scodoc.get_logo",
|
||||
dept_id=self.scodoc_dept_id,
|
||||
name=self.logoname,
|
||||
global_if_not_found=False,
|
||||
)
|
||||
|
||||
def get_url_small(self):
|
||||
"""Retourne l'URL permettant d'obtenir l'image du logo sous forme de miniature"""
|
||||
return url_for(
|
||||
"scodoc.get_logo_small",
|
||||
dept_id=self.scodoc_dept_id,
|
||||
name=self.logoname,
|
||||
global_if_not_found=False,
|
||||
)
|
||||
|
||||
def get_usage(self):
|
||||
if self.mm is None:
|
||||
return f'<logo name="{self.logoname}" width="?? mm" height="?? mm">'
|
||||
else:
|
||||
return f'<logo name="{self.logoname}" width="{self.mm[0]}mm">'
|
||||
|
||||
def last_modified(self):
|
||||
path = Path(self.filepath)
|
||||
dt = path.stat().st_mtime
|
||||
return path.stat().st_mtime
|
||||
|
||||
def rename(self, new_name):
|
||||
"""Change le nom (pas le département)
|
||||
Les éléments non utiles ne sont pas recalculés (car rechargés lors des accès ultérieurs)
|
||||
"""
|
||||
old_path = Path(self.filepath)
|
||||
self.logoname = secure_filename(new_name)
|
||||
if not self.logoname:
|
||||
self.logoname = "*** *** nom de logo invalide *** à changer ! *** ***"
|
||||
else:
|
||||
new_path = os.path.sep.join(
|
||||
[self.dirpath, self.prefix + self.logoname + "." + self.suffix]
|
||||
)
|
||||
old_path.rename(new_path)
|
||||
|
||||
|
||||
def guess_image_type(stream) -> str:
|
||||
"guess image type from header in stream"
|
||||
header = stream.read(512)
|
||||
stream.seek(0)
|
||||
fmt = imghdr.what(None, header)
|
||||
if not fmt:
|
||||
return None
|
||||
return fmt if fmt != "jpeg" else "jpg"
|
||||
|
||||
|
||||
def make_logo_local(logoname, dept_name):
|
||||
depts = Departement.query.filter_by(acronym=dept_name).all()
|
||||
if len(depts) == 0:
|
||||
print(f"no dept {dept_name} found. aborting")
|
||||
return
|
||||
if len(depts) > 1:
|
||||
print(f"several depts {dept_name} found. aborting")
|
||||
return
|
||||
dept = depts[0]
|
||||
print(f"Move logo {logoname}' from global to {dept.acronym}")
|
||||
old_path_wild = f"/opt/scodoc-data/config/logos/logo_{logoname}.*"
|
||||
new_dir = f"/opt/scodoc-data/config/logos/logos_{dept.id}"
|
||||
logos = glob.glob(old_path_wild)
|
||||
# checks that there is non local already present
|
||||
for logo in logos:
|
||||
filename = os.path.split(logo)[1]
|
||||
new_name = os.path.sep.join([new_dir, filename])
|
||||
if os.path.exists(new_name):
|
||||
print("local version of global logo already exists. aborting")
|
||||
return
|
||||
# create new__dir if necessary
|
||||
if not os.path.exists(new_dir):
|
||||
print(f"- create {new_dir} directory")
|
||||
os.mkdir(new_dir)
|
||||
# move global logo (all suffixes) to local dir note: pre existent file (logo_XXX.*) in local dir does not
|
||||
# prevent operation if there is no conflict with moved files
|
||||
# At this point everything is ok so we can do files manipulation
|
||||
for logo in logos:
|
||||
shutil.move(logo, new_dir)
|
||||
# print(f"moved {n_moves}/{n} etuds")
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gestion scolarite IUT
|
||||
#
|
||||
# Copyright (c) 1999 - 2022 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
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Gestion des images logos (nouveau ScoDoc 9.1)
|
||||
|
||||
Les logos sont `logo_header.<ext>` et `logo_footer.<ext>`
|
||||
avec `ext` membre de LOGOS_IMAGES_ALLOWED_TYPES (= jpg, png)
|
||||
|
||||
SCODOC_LOGOS_DIR /opt/scodoc-data/config/logos
|
||||
"""
|
||||
import glob
|
||||
import imghdr
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from flask import current_app, url_for
|
||||
from PIL import Image as PILImage
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from app import log
|
||||
from app.models import Departement
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
|
||||
GLOBAL = "_" # category for server level logos
|
||||
|
||||
|
||||
def find_logo(logoname, dept_id=None, strict=False, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""
|
||||
"Recherche un logo 'name' existant.
|
||||
Deux strategies:
|
||||
si strict:
|
||||
reherche uniquement dans le département puis si non trouvé au niveau global
|
||||
sinon
|
||||
On recherche en local au dept d'abord puis si pas trouvé recherche globale
|
||||
quelque soit la stratégie, retourne None si pas trouvé
|
||||
:param logoname: le nom recherche
|
||||
:param dept_id: l'id du département dans lequel se fait la recherche (None si global)
|
||||
:param strict: stratégie de recherche (strict = False => dept ou global)
|
||||
:param prefix: le prefix utilisé (parmi scu.LOGO_FILE_PREFIX / scu.BACKGROUND_FILE_PREFIX)
|
||||
:return: un objet Logo désignant le fichier image trouvé (ou None)
|
||||
"""
|
||||
logo = Logo(logoname, dept_id, prefix).select()
|
||||
if logo is None and not strict:
|
||||
logo = Logo(logoname=logoname, dept_id=None, prefix=prefix).select()
|
||||
return logo
|
||||
|
||||
|
||||
def delete_logo(name, dept_id=None):
|
||||
"""Delete all files matching logo (dept_id, name) (including all allowed extensions)
|
||||
Args:
|
||||
name: The name of the logo
|
||||
dept_id: the dept_id (if local). Use None to destroy globals logos
|
||||
"""
|
||||
logo = find_logo(logoname=name, dept_id=dept_id)
|
||||
while logo is not None:
|
||||
os.unlink(logo.select().filepath)
|
||||
logo = find_logo(logoname=name, dept_id=dept_id)
|
||||
|
||||
|
||||
def write_logo(stream, name, dept_id=None):
|
||||
"""Crée le fichier logo sur le serveur.
|
||||
Le suffixe du fichier (parmi LOGO_IMAGES_ALLOWED_TYPES) est déduit du contenu du stream"""
|
||||
Logo(logoname=name, dept_id=dept_id).create(stream)
|
||||
|
||||
|
||||
def rename_logo(old_name, new_name, dept_id):
|
||||
logo = find_logo(old_name, dept_id, True)
|
||||
logo.rename(new_name)
|
||||
|
||||
|
||||
def list_logos():
|
||||
"""Crée l'inventaire de tous les logos existants.
|
||||
L'inventaire se présente comme un dictionnaire de dictionnaire de Logo:
|
||||
[None][name] pour les logos globaux
|
||||
[dept_id][name] pour les logos propres à un département (attention id numérique du dept)
|
||||
Les départements sans logos sont absents du résultat
|
||||
"""
|
||||
inventory = {None: _list_dept_logos()} # logos globaux (header / footer)
|
||||
for dept in Departement.query.filter_by(visible=True).all():
|
||||
logos_dept = _list_dept_logos(dept_id=dept.id)
|
||||
if logos_dept:
|
||||
inventory[dept.id] = _list_dept_logos(dept.id)
|
||||
return inventory
|
||||
|
||||
|
||||
def _list_dept_logos(dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""Inventorie toutes les images existantes pour un niveau (GLOBAL ou un département).
|
||||
retourne un dictionnaire de Logo [logoname] -> Logo
|
||||
les noms des fichiers concernés doivent être de la forme: <rep>/<prefix><name>.<suffixe>
|
||||
<rep> : répertoire de recherche (déduit du dept_id)
|
||||
<prefix>: le prefix (LOGO_FILE_PREFIX pour les logos)
|
||||
<suffix>: un des suffixes autorisés
|
||||
:param dept_id: l'id du departement concerné (si None -> global)
|
||||
:param prefix: le préfixe utilisé
|
||||
:return: le résultat de la recherche ou None si aucune image trouvée
|
||||
"""
|
||||
allowed_ext = "|".join(scu.LOGOS_IMAGES_ALLOWED_TYPES)
|
||||
# parse filename 'logo_<logoname>.<ext> . be carefull: logoname may include '.'
|
||||
filename_parser = re.compile(f"{prefix}(([^.]*.)+)({allowed_ext})")
|
||||
logos = {}
|
||||
path_dir = Path(scu.SCODOC_LOGOS_DIR)
|
||||
if dept_id:
|
||||
path_dir = Path(
|
||||
os.path.sep.join(
|
||||
[scu.SCODOC_LOGOS_DIR, scu.LOGOS_DIR_PREFIX + str(dept_id)]
|
||||
)
|
||||
)
|
||||
if path_dir.exists():
|
||||
for entry in path_dir.iterdir():
|
||||
if os.access(path_dir.joinpath(entry).absolute(), os.R_OK):
|
||||
result = filename_parser.match(entry.name)
|
||||
if result:
|
||||
logoname = result.group(1)[
|
||||
:-1
|
||||
] # retreive logoname from filename (less final dot)
|
||||
logos[logoname] = Logo(logoname=logoname, dept_id=dept_id).select()
|
||||
return logos if len(logos.keys()) > 0 else None
|
||||
|
||||
|
||||
class Logo:
|
||||
"""Responsable des opérations (select, create), du calcul des chemins et url
|
||||
ainsi que de la récupération des informations sur un logo.
|
||||
Usage:
|
||||
logo existant: Logo(<name>, <dept_id>, ...).select() (retourne None si fichier non trouvé)
|
||||
logo en création: Logo(<name>, <dept_id>, ...).create(stream)
|
||||
Les attributs filename, filepath, get_url() ne devraient pas être utilisés avant les opérations
|
||||
select ou save (le format n'est pas encore connu à ce moement là)
|
||||
"""
|
||||
|
||||
def __init__(self, logoname, dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""Initialisation des noms et département des logos.
|
||||
if prefix = None on recherche simplement une image 'logoname.*'
|
||||
Le format est renseigné au moment de la lecture (select) ou de la création (create) de l'objet
|
||||
"""
|
||||
self.logoname = secure_filename(logoname)
|
||||
if not self.logoname:
|
||||
self.logoname = "*** *** nom de logo invalide *** à changer ! *** ***"
|
||||
self.scodoc_dept_id = dept_id
|
||||
self.prefix = prefix or ""
|
||||
if self.scodoc_dept_id:
|
||||
self.dirpath = os.path.sep.join(
|
||||
[
|
||||
scu.SCODOC_LOGOS_DIR,
|
||||
scu.LOGOS_DIR_PREFIX + secure_filename(str(dept_id)),
|
||||
]
|
||||
)
|
||||
else:
|
||||
self.dirpath = scu.SCODOC_LOGOS_DIR
|
||||
self.basepath = os.path.sep.join(
|
||||
[self.dirpath, self.prefix + secure_filename(self.logoname)]
|
||||
)
|
||||
# next attributes are computed by the select function
|
||||
self.suffix = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.filepath = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.filename = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.size = "Not initialized: call the select or create function before access"
|
||||
self.aspect_ratio = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.density = (
|
||||
"Not initialized: call the select or create function before access"
|
||||
)
|
||||
self.mm = "Not initialized: call the select or create function before access"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Logo(logoname='{self.logoname}', filename='{self.filename}')"
|
||||
|
||||
def _set_format(self, fmt):
|
||||
self.suffix = fmt
|
||||
self.filepath = self.basepath + "." + fmt
|
||||
self.filename = self.logoname + "." + fmt
|
||||
|
||||
def _ensure_directory_exists(self):
|
||||
"create enclosing directory if necessary"
|
||||
if not Path(self.dirpath).exists():
|
||||
current_app.logger.info("sco_logos creating directory %s", self.dirpath)
|
||||
os.mkdir(self.dirpath)
|
||||
|
||||
def create(self, stream):
|
||||
img_type = guess_image_type(stream)
|
||||
if img_type not in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
raise ScoValueError("type d'image invalide")
|
||||
self._set_format(img_type)
|
||||
self._ensure_directory_exists()
|
||||
filename = self.basepath + "." + self.suffix
|
||||
with open(filename, "wb") as f:
|
||||
f.write(stream.read())
|
||||
current_app.logger.info("sco_logos.store_image %s", self.filename)
|
||||
# erase other formats if they exists
|
||||
for suffix in set(scu.LOGOS_IMAGES_ALLOWED_TYPES) - set([img_type]):
|
||||
try:
|
||||
os.unlink(self.basepath + "." + suffix)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
def _read_info(self, img):
|
||||
"""computes some properties from the real image
|
||||
aspect_ratio assumes that x_density and y_density are equals
|
||||
"""
|
||||
x_size, y_size = img.size
|
||||
self.density = img.info.get("dpi", None)
|
||||
unit = 1
|
||||
if self.density is None: # no dpi found try jfif infos
|
||||
self.density = img.info.get("jfif_density", None)
|
||||
unit = img.info.get("jfif_unit", 0) # 0 = no unit ; 1 = inch ; 2 = mm
|
||||
if self.density is not None:
|
||||
x_density, y_density = self.density
|
||||
if unit != 0 and x_density != 0 and y_density != 0:
|
||||
unit2mm = [0, 1 / 0.254, 0.1][unit]
|
||||
x_mm = round(x_size * unit2mm / x_density, 2)
|
||||
y_mm = round(y_size * unit2mm / y_density, 2)
|
||||
self.mm = (x_mm, y_mm)
|
||||
else:
|
||||
self.mm = None
|
||||
else:
|
||||
self.mm = None
|
||||
self.size = (x_size, y_size)
|
||||
self.aspect_ratio = round(float(x_size) / y_size, 2)
|
||||
|
||||
def select(self):
|
||||
"""
|
||||
Récupération des données pour un logo existant
|
||||
il doit exister un et un seul fichier image parmi de suffixe/types autorisés
|
||||
(sinon on prend le premier trouvé)
|
||||
cette opération permet d'affiner le format d'un logo de format inconnu
|
||||
"""
|
||||
for suffix in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
path = Path(self.basepath + "." + suffix)
|
||||
if path.exists():
|
||||
self._set_format(suffix)
|
||||
with open(self.filepath, "rb") as f:
|
||||
img = PILImage.open(f)
|
||||
self._read_info(img)
|
||||
return self
|
||||
return None
|
||||
|
||||
def get_url(self):
|
||||
"""Retourne l'URL permettant d'obtenir l'image du logo"""
|
||||
return url_for(
|
||||
"scodoc.get_logo",
|
||||
dept_id=self.scodoc_dept_id,
|
||||
name=self.logoname,
|
||||
global_if_not_found=False,
|
||||
)
|
||||
|
||||
def get_url_small(self):
|
||||
"""Retourne l'URL permettant d'obtenir l'image du logo sous forme de miniature"""
|
||||
return url_for(
|
||||
"scodoc.get_logo_small",
|
||||
dept_id=self.scodoc_dept_id,
|
||||
name=self.logoname,
|
||||
global_if_not_found=False,
|
||||
)
|
||||
|
||||
def get_usage(self):
|
||||
if self.mm is None:
|
||||
return f'<logo name="{self.logoname}" width="?? mm" height="?? mm">'
|
||||
else:
|
||||
return f'<logo name="{self.logoname}" width="{self.mm[0]}mm">'
|
||||
|
||||
def last_modified(self):
|
||||
path = Path(self.filepath)
|
||||
dt = path.stat().st_mtime
|
||||
return path.stat().st_mtime
|
||||
|
||||
def rename(self, new_name):
|
||||
"""Change le nom (pas le département)
|
||||
Les éléments non utiles ne sont pas recalculés (car rechargés lors des accès ultérieurs)
|
||||
"""
|
||||
old_path = Path(self.filepath)
|
||||
self.logoname = secure_filename(new_name)
|
||||
if not self.logoname:
|
||||
self.logoname = "*** *** nom de logo invalide *** à changer ! *** ***"
|
||||
else:
|
||||
new_path = os.path.sep.join(
|
||||
[self.dirpath, self.prefix + self.logoname + "." + self.suffix]
|
||||
)
|
||||
old_path.rename(new_path)
|
||||
|
||||
|
||||
def guess_image_type(stream) -> str:
|
||||
"guess image type from header in stream"
|
||||
header = stream.read(512)
|
||||
stream.seek(0)
|
||||
fmt = imghdr.what(None, header)
|
||||
if not fmt:
|
||||
return None
|
||||
return fmt if fmt != "jpeg" else "jpg"
|
||||
|
||||
|
||||
def make_logo_local(logoname, dept_name):
|
||||
depts = Departement.query.filter_by(acronym=dept_name).all()
|
||||
if len(depts) == 0:
|
||||
log(f"no dept {dept_name} found. aborting")
|
||||
return
|
||||
if len(depts) > 1:
|
||||
log(f"several depts {dept_name} found. aborting")
|
||||
return
|
||||
dept = depts[0]
|
||||
log(f"Move logo {logoname}' from global to {dept.acronym}")
|
||||
old_path_wild = f"/opt/scodoc-data/config/logos/logo_{logoname}.*"
|
||||
new_dir = f"/opt/scodoc-data/config/logos/logos_{dept.id}"
|
||||
logos = glob.glob(old_path_wild)
|
||||
# checks that there is non local already present
|
||||
for logo in logos:
|
||||
filename = os.path.split(logo)[1]
|
||||
new_name = os.path.sep.join([new_dir, filename])
|
||||
if os.path.exists(new_name):
|
||||
log("local version of global logo already exists. aborting")
|
||||
return
|
||||
# create new__dir if necessary
|
||||
if not os.path.exists(new_dir):
|
||||
log(f"- create {new_dir} directory")
|
||||
os.mkdir(new_dir)
|
||||
# move global logo (all suffixes) to local dir note: pre existent file (logo_XXX.*) in local dir does not
|
||||
# prevent operation if there is no conflict with moved files
|
||||
# At this point everything is ok so we can do files manipulation
|
||||
for logo in logos:
|
||||
shutil.move(logo, new_dir)
|
||||
# log(f"moved {n_moves}/{n} etuds")
|
||||
|
|
758
sco_version.py
758
sco_version.py
|
@ -1,379 +1,379 @@
|
|||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.3.47"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
SCONEWS = """
|
||||
<h4>Année 2022</h4>
|
||||
<ul>
|
||||
<li>ScoDoc 9.3</li>
|
||||
<ul>
|
||||
<li>Nouvelle API REST pour connecter ScoDoc à d'autres applications<li>
|
||||
<li>Module de gestion des relations avec les entreprises</li>
|
||||
<li>Prise en charge des parcours BUT</li>
|
||||
<li>Association des UEs aux compétences du référentiel</li>
|
||||
<li>Jury BUT1</li>
|
||||
</ul>
|
||||
|
||||
<h4>Année 2021</h4>
|
||||
<ul>
|
||||
<li>ScoDoc 9.2:
|
||||
<ul>
|
||||
<li>Tableau récap. complet pour BUT et autres formations.</li>
|
||||
<li>Tableau état évaluations</li>
|
||||
<li>Export des trombinoscope en document docx</li>
|
||||
<li>Très nombreux correctifs</li>
|
||||
</ul>
|
||||
<li>ScoDoc 9.1.75: bulletins BUT pdf</li>
|
||||
<li>ScoDoc 9.1.50: nombreuses amélioration gestion BUT</li>
|
||||
<li>ScoDoc 9.1: gestion des formations par compétences, type BUT.</li>
|
||||
<li>ScoDoc 9.0: nouvelle architecture logicielle (Flask/Python3/Debian 11)</li>
|
||||
<li>Version mobile (en test)</li>
|
||||
<li>Évaluations de type "deuxième session"</li>
|
||||
<li>Gestion du genre neutre (pas d'affichage de la civilité)</li>
|
||||
<li>Diverses corrections (PV de jurys, ...)</li>
|
||||
<li>Modernisation du code Python</li>
|
||||
</ul>
|
||||
<h4>Année 2020</h4>
|
||||
<ul>
|
||||
<li>Corrections d'erreurs, améliorations saisie absences et affichage bulletins</li>
|
||||
<li>Nouveau site <a href="https://scodoc.org" target="_blank" rel="noopener noreferrer">scodoc.org</a> pour la documentation</li>
|
||||
<li>Enregistrement de semestres extérieurs</li>
|
||||
<li>Améliorations PV de Jury</li>
|
||||
<li>Contributions J.-M. Place: aide au diagnostic problèmes export Apogée
|
||||
</li>
|
||||
</ul>
|
||||
<h4>Année 2019</h4>
|
||||
<ul>
|
||||
<li>Support Linux Debian 10</li>
|
||||
<li>Petites améliorations: import groupes, droits de suppression notes pour vacataires, etc.</li>
|
||||
<li>Exports listes pour Moodle</li>
|
||||
<li>Fonction facilitant l'envoi de données pour l'assistance technique</li>
|
||||
</ul>
|
||||
<h4>Année 2018</h4>
|
||||
<ul>
|
||||
<li>Affichage date finalisation inscription Apogée</li>
|
||||
<li>Co-responsables de semestres</li>
|
||||
<li>Amélioration page d'accueil département</li>
|
||||
<li>Corrections diverses et petites améliorations</li>
|
||||
<li>Avis de poursuites d'études plus robustes et configurables</li>
|
||||
</ul>
|
||||
<h4>Année 2017</h4>
|
||||
<ul>
|
||||
<li>Bonus/Malus sur les moyennes d'UE</li>
|
||||
<li>Enregistrement des informations sur le devenir de l'étudiant</li>
|
||||
<li>Export global des résultats d'un département (utile pour les rapports d'évaluation)</li>
|
||||
<li>Compatibilité Linux Debian 9, et modernisation de certains composants</li>
|
||||
<li>Génération des avis de poursuite d'études</li>
|
||||
<li>Toilettage page liste programme pédagogiques</li>
|
||||
<li>Amélioration exports résultats vers Apogée</li>
|
||||
<li>Amélioration calcul des ECTS</li>
|
||||
<li>Possibilité d'utilisation des adresses mail personnelles des étudiant</li>
|
||||
<li>Corrections diverses</li>
|
||||
</ul>
|
||||
|
||||
<h4>Année 2016</h4>
|
||||
<ul>
|
||||
<li>Import des données d'admissions depuis fichiers APB ou autres</li>
|
||||
<li>Nouveau formulaire saisie des notes</li>
|
||||
<li>Export des résultats vers Apogée pour un ensemble de semestre</li>
|
||||
<li>Enregistrement du classement lors de l'admission</li>
|
||||
<li>Modification du calcul des coefficients des UE capitalisées</li>
|
||||
</ul>
|
||||
|
||||
<h4>Année 2015</h4>
|
||||
<ul>
|
||||
<li>Exports fichiers Apogée</li>
|
||||
<li>Recherche étudiants sur plusieurs départements</li>
|
||||
<li>Corrections diverses</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Année 2014</h4>
|
||||
<ul>
|
||||
<li>Nouvelle interface pour listes groupes, photos, feuilles d'émargement.</li>
|
||||
</ul>
|
||||
<h4>Année 2013</h4>
|
||||
<ul>
|
||||
<li>Modernisation de nombreux composants logiciels (ScoDoc 7)</li>
|
||||
<li>Saisie des absences par matières</li>
|
||||
</ul>
|
||||
<h4>Année 2012</h4>
|
||||
<ul>
|
||||
<li>Table lycées d'origine avec carte google</li>
|
||||
<li>Amélioration des PV de jury (logos, ...)</li>
|
||||
<li>Accélération du code de calcul des semestres</li>
|
||||
<li>Changement documentation en ligne (nouveau site web)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Année 2011</h4>
|
||||
<ul>
|
||||
<li>Amélioration de la présentation des bulletins de notes, et possibilité de définir de nouveaux formats</li>
|
||||
<li>Possibilité de modifier les moyennes d'UE via un "bonus" (sport/culture)</li>
|
||||
<li>Ajout parcours spécifique pour UCAC (Cameroun)</li>
|
||||
<li>Possibilité d'indiquer des mentions sur les PV</li>
|
||||
<li>Evaluations de "rattrapage"</li>
|
||||
<li>Support pour installation en Linux Debian "Squeeze"</li>
|
||||
<li>Corrections diverses</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Novembre 2010</h4>
|
||||
<ul>
|
||||
<li>Possibilité d'indiquer des évaluations avec publication immédiate des notes (même si incomplètes)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Octobre 2010</h4>
|
||||
<ul>
|
||||
<li>Nouvelle API JSON</li>
|
||||
<li>Possibilité d'associer 2 étapes Apogée au même semestre</li>
|
||||
<li>Table "poursuite études"</li>
|
||||
<li>Possibilité d'envoyer un mail auto aux étudiants absentéistes<li>
|
||||
</ul>
|
||||
|
||||
<h4>Août 2010</h4>
|
||||
<ul>
|
||||
<li>Définitions de parcours (DUT, LP, ...) avec prise en compte des spécificités (par ex., certaines barres d'UE différentes en LP)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Avril - Juin 2010</h4>
|
||||
<ul>
|
||||
<li>Formules utilisateur pour le calcul des moyennes d'UE</li>
|
||||
<li>Nouveau système de notification des absences par mail</li>
|
||||
<li>Affichage optionnel des valeurs mini et maxi des moyennes sur les bulletins</li>
|
||||
<li>Nouveau code de décision jury semestre: "RAT" : en attente de rattrapage</li>
|
||||
</ul>
|
||||
|
||||
<h4>Janvier 2010</h4>
|
||||
<ul>
|
||||
<li>Nouveau menu "Groupes" pour faciliter la prise en main</li>
|
||||
<li>Possibilité de définir des règles ad hoc de calcul des moyennes de modules (formules)</li>
|
||||
<li>Possibilité d'inclure des images (logos) dans les bulletins PDF</li>
|
||||
<li>Bandeau "provisoire" sur les bulletins en cours de semestre</li>
|
||||
<li>Possibilite de valider (capitaliser) une UE passee hors ScoDoc</li>
|
||||
<li>Amelioration de l'édition des programmes (formations)</li>
|
||||
<li>Nombreuses améliorations mineures</li>
|
||||
</ul>
|
||||
|
||||
<h4>Novembre 2009</h4>
|
||||
<ul>
|
||||
<li>Gestion des partitions et groupes en nombres quelconques</li>
|
||||
<li>Nouvelle gestion des photos</li>
|
||||
<lI>Imports d'étudiants excel incrémentaux</li>
|
||||
<li>Optimisations et petites améliorations</li>
|
||||
</ul>
|
||||
|
||||
<h4>Septembre 2009</h4>
|
||||
<ul>
|
||||
<li>Traitement de "billets d'absences" (saisis par les étudiants sur le portail)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juin 2009</h4>
|
||||
<ul>
|
||||
<li>Nouveau système plus flexibles de gestion des préférences (ou "paramètres")</li>
|
||||
<li>Possiblité d'associer une nouvelle version de programme à un semestre</li>
|
||||
<li>Corrections et améliorations diverses</h4>
|
||||
</ul>
|
||||
|
||||
<h4>Juillet 2008: version 6.0</h4>
|
||||
<ul>
|
||||
<li>Installeur automatisé pour Linux</li>
|
||||
<li>Amélioration ergonomie (barre menu pages semestres)</li>
|
||||
<li>Refonte fiche étudiant (parcours)</li>
|
||||
<li>Archivage des documents (PV)</li>
|
||||
<li>Nouvel affichage des notes des évaluations</li>
|
||||
<li>Nombreuses corrections et améliorations</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juin 2008</h4>
|
||||
<ul>
|
||||
<li>Rangs sur les bulletins</li>
|
||||
</ul>
|
||||
|
||||
<h4>Février 2008</h4>
|
||||
<ul>
|
||||
<li>Statistiques et suivis de cohortes (chiffres et graphes)</li>
|
||||
<li>Nombreuses petites corrections suites aux jurys de janvier</li>
|
||||
</ul>
|
||||
|
||||
<h4>Janvier 2008</h4>
|
||||
<ul>
|
||||
<li>Personnalisation des régles de calculs notes d'option (sport, culture)</li>
|
||||
<li>Edition de PV de jury individuel</li>
|
||||
</ul>
|
||||
|
||||
<h4>Novembre 2007</h4>
|
||||
<ul>
|
||||
<li>Vérification des absences aux évaluations</li>
|
||||
<li>Import des photos depuis portail, trombinoscopes en PDF</li>
|
||||
</ul>
|
||||
|
||||
<h4>Septembre 2007</h4>
|
||||
<ul>
|
||||
<li>Importation des etudiants depuis étapes Apogée</li>
|
||||
<li>Inscription de groupes à des modules (options ou parcours)</li>
|
||||
<li>Listes de étapes Apogée (importées du portail)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juillet 2007</h4>
|
||||
<ul>
|
||||
<li>Import utilisateurs depuis Excel</li>
|
||||
<li>Nouvelle gestion des passage d'un semestre à l'autre</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juin 2007: version 5.0</h4>
|
||||
<ul>
|
||||
<li>Suivi des parcours et règles de décision des jurys DUT</li>
|
||||
<li>Capitalisation des UEs</li>
|
||||
<li>Edition des PV de jurys et courriers aux étudiants</li>
|
||||
<li>Feuilles (excel) pour préparation jurys</li>
|
||||
<li>Nombreuses petites améliorations</li>
|
||||
</ul>
|
||||
|
||||
<h4>Avril 2007</h4>
|
||||
<ul>
|
||||
<li>Paramètres de mise en page des bulletins en PDF</li>
|
||||
</ul>
|
||||
|
||||
<h4>Février 2007</h4>
|
||||
|
||||
<ul>
|
||||
<li>Possibilité de ne <em>pas</em> publier les bulletins sur le portail</li>
|
||||
<li>Gestion des notes "en attente" (publication d'évaluations sans correction de toutes les copies)</li>
|
||||
<li>Amélioration formulaire saisie absences, saisie absences par semestre.</li>
|
||||
</ul>
|
||||
|
||||
<h4>Janvier 2007</h4>
|
||||
<ul>
|
||||
<li>Possibilité d'initialiser les notes manquantes d'une évaluation</li>
|
||||
<li>Recupération des codes NIP depuis Apogée</li>
|
||||
<li>Gestion des compensations inter-semestre DUT (en cours de développement)</li>
|
||||
<li>Export trombinoscope en archive zip</li>
|
||||
</ul>
|
||||
|
||||
<h4>Octobre 2006</h4>
|
||||
<ul>
|
||||
<li>Réorganisation des pages d'accueil</li>
|
||||
<li>Ajout des "nouvelles" (dernières opérations), avec flux RSS</li>
|
||||
<li>Import/Export XML des formations, duplication d'une formation (versions)</li>
|
||||
<li>Bulletins toujours sur une seule feuille (passage à ReportLab 2.0)</li>
|
||||
<li>Suppression d'un utilisateur</il>
|
||||
</ul>
|
||||
<h4>Septembre 2006</h4>
|
||||
<ul>
|
||||
<li>Page pour suppression des groupes.</li>
|
||||
<li>Amélioration gestion des utilisateurs</li>
|
||||
<li>"Verrouillage" des semestres</li>
|
||||
<li>Liste d'enseignants (chargés de TD) associés à un module (et pouvant saisir des notes)</li>
|
||||
<li>Noms de types de groupes (TD, TP, ...) modifiables</li>
|
||||
<li>Tableau rudimentaire donnant la répartition des bacs dans un semestre</li>
|
||||
<li>Amélioration mise en page des listes au format excel</li>
|
||||
<li>Annulation des démissions</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juillet 2006</h4>
|
||||
<ul>
|
||||
<li>Dialogue permettant au directeur des études de modifier
|
||||
les options d'un semestre</li>
|
||||
<li>Option pour ne pas afficher les UE validées sur les bulletins</li>
|
||||
</ul>
|
||||
|
||||
<h4>30 juin 2006</h4>
|
||||
<ul>
|
||||
<li>Option pour ne pas afficher les décisions sur les bulletins</li>
|
||||
<li>Génération feuilles pour préparation jury</li>
|
||||
<li>Gestion des modules optionnels</li>
|
||||
<li>Prise en compte note "activités culturelles ou sportives"</li>
|
||||
<li>Amélioration tableau de bord semestre</li>
|
||||
<li>Import listes étudiants depuis Excel (avec code Apogée)</li>
|
||||
</ul>
|
||||
|
||||
<h4>12 juin 2006</h4>
|
||||
<ul>
|
||||
<li>Formulaire dynamique d'affectation aux groupes</li>
|
||||
<li>Tri des tableaux (listes, récapitulatif)</li>
|
||||
<li>Export XML des infos sur un etudiant et des groupes</li>
|
||||
</ul>
|
||||
|
||||
<h4>12 mai 2006</h4>
|
||||
<ul>
|
||||
<li>Possibilité de suppression d'un semestre</li>
|
||||
<li>Export XML du tableau recapitulatif des notes du semestre</li>
|
||||
<li>Possibilité de supression d'une formation complète</li>
|
||||
</ul>
|
||||
|
||||
<h4>24 avril 2006</h4>
|
||||
<ul>
|
||||
<li>Export bulletins en XML (expérimental)</li>
|
||||
<li>Flag "gestion_absence" sur les semestres de formation</li>
|
||||
</ul>
|
||||
|
||||
<h4>4 mars 2006</h4>
|
||||
<ul>
|
||||
<li>Formulaire d'inscription au semestre suivant.</li>
|
||||
<li>Format "nombre" dans les feuilles excel exportées.</li>
|
||||
</ul>
|
||||
|
||||
<h4>23 février 2006</h4>
|
||||
<ul>
|
||||
<li>Décisions jury sur bulletins.</li>
|
||||
</ul>
|
||||
|
||||
<h4>17 janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Ajout et édition d'appréciations sur les bulletins.</li>
|
||||
</ul>
|
||||
<h4>12 janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Envoi des bulletins en PDF par mail aux étudiants.</li>
|
||||
</ul>
|
||||
|
||||
<h4>6 janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Affichage des ex-aequos.</li>
|
||||
<li>Classeurs bulletins PDF en différentes versions.</li>
|
||||
<li>Corrigé gestion des notes des démissionnaires.</li>
|
||||
</ul>
|
||||
|
||||
<h4>1er janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Import du projet dans Subversion / LIPN.</li>
|
||||
<li>Lecture des feuilles de notes Excel.</li>
|
||||
</ul>
|
||||
|
||||
<h4>31 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Listes générées au format Excel au lieu de CSV.</li>
|
||||
<li>Bug fix (création/saisie evals).</li>
|
||||
</ul>
|
||||
|
||||
<h4>29 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Affichage des moyennes de chaque groupe dans tableau de bord module.
|
||||
</ul>
|
||||
|
||||
<h4>26 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Révision inscription/édition <em>individuelle</em> d'étudiants.</li>
|
||||
<li>Amélioration fiche étudiant (cosmétique, liste formations, actions).</li>
|
||||
<li>Listings notes d'évaluations anonymes (utilité douteuse ?).</li>
|
||||
<li>Amélioration formulaire saisie notes ('enter' -> champ suivant).</li>
|
||||
</ul>
|
||||
|
||||
<h4>24 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Génération de bulletins PDF
|
||||
</li>
|
||||
<li>Suppression de notes (permet donc de supprimer une évaluation)
|
||||
</li>
|
||||
<li>Bulletins en versions courtes (seulement moyennes de chaque module), longues
|
||||
(toutes les notes) et intermédiaire (moyenne de chaque module plus notes dans les évaluations sélectionnées).
|
||||
</li>
|
||||
<li>Notes moyennes sous les barres en rouge dans le tableau récapitulatif (seuil=10 sur la moyenne générale, et 8 sur chaque UE).
|
||||
</li>
|
||||
<li>Colonne "groupe de TD" dans le tableau récapitulatif des notes.
|
||||
</ul>
|
||||
"""
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.3.48"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
SCONEWS = """
|
||||
<h4>Année 2022</h4>
|
||||
<ul>
|
||||
<li>ScoDoc 9.3</li>
|
||||
<ul>
|
||||
<li>Nouvelle API REST pour connecter ScoDoc à d'autres applications<li>
|
||||
<li>Module de gestion des relations avec les entreprises</li>
|
||||
<li>Prise en charge des parcours BUT</li>
|
||||
<li>Association des UEs aux compétences du référentiel</li>
|
||||
<li>Jury BUT1</li>
|
||||
</ul>
|
||||
|
||||
<h4>Année 2021</h4>
|
||||
<ul>
|
||||
<li>ScoDoc 9.2:
|
||||
<ul>
|
||||
<li>Tableau récap. complet pour BUT et autres formations.</li>
|
||||
<li>Tableau état évaluations</li>
|
||||
<li>Export des trombinoscope en document docx</li>
|
||||
<li>Très nombreux correctifs</li>
|
||||
</ul>
|
||||
<li>ScoDoc 9.1.75: bulletins BUT pdf</li>
|
||||
<li>ScoDoc 9.1.50: nombreuses amélioration gestion BUT</li>
|
||||
<li>ScoDoc 9.1: gestion des formations par compétences, type BUT.</li>
|
||||
<li>ScoDoc 9.0: nouvelle architecture logicielle (Flask/Python3/Debian 11)</li>
|
||||
<li>Version mobile (en test)</li>
|
||||
<li>Évaluations de type "deuxième session"</li>
|
||||
<li>Gestion du genre neutre (pas d'affichage de la civilité)</li>
|
||||
<li>Diverses corrections (PV de jurys, ...)</li>
|
||||
<li>Modernisation du code Python</li>
|
||||
</ul>
|
||||
<h4>Année 2020</h4>
|
||||
<ul>
|
||||
<li>Corrections d'erreurs, améliorations saisie absences et affichage bulletins</li>
|
||||
<li>Nouveau site <a href="https://scodoc.org" target="_blank" rel="noopener noreferrer">scodoc.org</a> pour la documentation</li>
|
||||
<li>Enregistrement de semestres extérieurs</li>
|
||||
<li>Améliorations PV de Jury</li>
|
||||
<li>Contributions J.-M. Place: aide au diagnostic problèmes export Apogée
|
||||
</li>
|
||||
</ul>
|
||||
<h4>Année 2019</h4>
|
||||
<ul>
|
||||
<li>Support Linux Debian 10</li>
|
||||
<li>Petites améliorations: import groupes, droits de suppression notes pour vacataires, etc.</li>
|
||||
<li>Exports listes pour Moodle</li>
|
||||
<li>Fonction facilitant l'envoi de données pour l'assistance technique</li>
|
||||
</ul>
|
||||
<h4>Année 2018</h4>
|
||||
<ul>
|
||||
<li>Affichage date finalisation inscription Apogée</li>
|
||||
<li>Co-responsables de semestres</li>
|
||||
<li>Amélioration page d'accueil département</li>
|
||||
<li>Corrections diverses et petites améliorations</li>
|
||||
<li>Avis de poursuites d'études plus robustes et configurables</li>
|
||||
</ul>
|
||||
<h4>Année 2017</h4>
|
||||
<ul>
|
||||
<li>Bonus/Malus sur les moyennes d'UE</li>
|
||||
<li>Enregistrement des informations sur le devenir de l'étudiant</li>
|
||||
<li>Export global des résultats d'un département (utile pour les rapports d'évaluation)</li>
|
||||
<li>Compatibilité Linux Debian 9, et modernisation de certains composants</li>
|
||||
<li>Génération des avis de poursuite d'études</li>
|
||||
<li>Toilettage page liste programme pédagogiques</li>
|
||||
<li>Amélioration exports résultats vers Apogée</li>
|
||||
<li>Amélioration calcul des ECTS</li>
|
||||
<li>Possibilité d'utilisation des adresses mail personnelles des étudiant</li>
|
||||
<li>Corrections diverses</li>
|
||||
</ul>
|
||||
|
||||
<h4>Année 2016</h4>
|
||||
<ul>
|
||||
<li>Import des données d'admissions depuis fichiers APB ou autres</li>
|
||||
<li>Nouveau formulaire saisie des notes</li>
|
||||
<li>Export des résultats vers Apogée pour un ensemble de semestre</li>
|
||||
<li>Enregistrement du classement lors de l'admission</li>
|
||||
<li>Modification du calcul des coefficients des UE capitalisées</li>
|
||||
</ul>
|
||||
|
||||
<h4>Année 2015</h4>
|
||||
<ul>
|
||||
<li>Exports fichiers Apogée</li>
|
||||
<li>Recherche étudiants sur plusieurs départements</li>
|
||||
<li>Corrections diverses</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Année 2014</h4>
|
||||
<ul>
|
||||
<li>Nouvelle interface pour listes groupes, photos, feuilles d'émargement.</li>
|
||||
</ul>
|
||||
<h4>Année 2013</h4>
|
||||
<ul>
|
||||
<li>Modernisation de nombreux composants logiciels (ScoDoc 7)</li>
|
||||
<li>Saisie des absences par matières</li>
|
||||
</ul>
|
||||
<h4>Année 2012</h4>
|
||||
<ul>
|
||||
<li>Table lycées d'origine avec carte google</li>
|
||||
<li>Amélioration des PV de jury (logos, ...)</li>
|
||||
<li>Accélération du code de calcul des semestres</li>
|
||||
<li>Changement documentation en ligne (nouveau site web)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Année 2011</h4>
|
||||
<ul>
|
||||
<li>Amélioration de la présentation des bulletins de notes, et possibilité de définir de nouveaux formats</li>
|
||||
<li>Possibilité de modifier les moyennes d'UE via un "bonus" (sport/culture)</li>
|
||||
<li>Ajout parcours spécifique pour UCAC (Cameroun)</li>
|
||||
<li>Possibilité d'indiquer des mentions sur les PV</li>
|
||||
<li>Evaluations de "rattrapage"</li>
|
||||
<li>Support pour installation en Linux Debian "Squeeze"</li>
|
||||
<li>Corrections diverses</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Novembre 2010</h4>
|
||||
<ul>
|
||||
<li>Possibilité d'indiquer des évaluations avec publication immédiate des notes (même si incomplètes)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Octobre 2010</h4>
|
||||
<ul>
|
||||
<li>Nouvelle API JSON</li>
|
||||
<li>Possibilité d'associer 2 étapes Apogée au même semestre</li>
|
||||
<li>Table "poursuite études"</li>
|
||||
<li>Possibilité d'envoyer un mail auto aux étudiants absentéistes<li>
|
||||
</ul>
|
||||
|
||||
<h4>Août 2010</h4>
|
||||
<ul>
|
||||
<li>Définitions de parcours (DUT, LP, ...) avec prise en compte des spécificités (par ex., certaines barres d'UE différentes en LP)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Avril - Juin 2010</h4>
|
||||
<ul>
|
||||
<li>Formules utilisateur pour le calcul des moyennes d'UE</li>
|
||||
<li>Nouveau système de notification des absences par mail</li>
|
||||
<li>Affichage optionnel des valeurs mini et maxi des moyennes sur les bulletins</li>
|
||||
<li>Nouveau code de décision jury semestre: "RAT" : en attente de rattrapage</li>
|
||||
</ul>
|
||||
|
||||
<h4>Janvier 2010</h4>
|
||||
<ul>
|
||||
<li>Nouveau menu "Groupes" pour faciliter la prise en main</li>
|
||||
<li>Possibilité de définir des règles ad hoc de calcul des moyennes de modules (formules)</li>
|
||||
<li>Possibilité d'inclure des images (logos) dans les bulletins PDF</li>
|
||||
<li>Bandeau "provisoire" sur les bulletins en cours de semestre</li>
|
||||
<li>Possibilite de valider (capitaliser) une UE passee hors ScoDoc</li>
|
||||
<li>Amelioration de l'édition des programmes (formations)</li>
|
||||
<li>Nombreuses améliorations mineures</li>
|
||||
</ul>
|
||||
|
||||
<h4>Novembre 2009</h4>
|
||||
<ul>
|
||||
<li>Gestion des partitions et groupes en nombres quelconques</li>
|
||||
<li>Nouvelle gestion des photos</li>
|
||||
<lI>Imports d'étudiants excel incrémentaux</li>
|
||||
<li>Optimisations et petites améliorations</li>
|
||||
</ul>
|
||||
|
||||
<h4>Septembre 2009</h4>
|
||||
<ul>
|
||||
<li>Traitement de "billets d'absences" (saisis par les étudiants sur le portail)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juin 2009</h4>
|
||||
<ul>
|
||||
<li>Nouveau système plus flexibles de gestion des préférences (ou "paramètres")</li>
|
||||
<li>Possiblité d'associer une nouvelle version de programme à un semestre</li>
|
||||
<li>Corrections et améliorations diverses</h4>
|
||||
</ul>
|
||||
|
||||
<h4>Juillet 2008: version 6.0</h4>
|
||||
<ul>
|
||||
<li>Installeur automatisé pour Linux</li>
|
||||
<li>Amélioration ergonomie (barre menu pages semestres)</li>
|
||||
<li>Refonte fiche étudiant (parcours)</li>
|
||||
<li>Archivage des documents (PV)</li>
|
||||
<li>Nouvel affichage des notes des évaluations</li>
|
||||
<li>Nombreuses corrections et améliorations</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juin 2008</h4>
|
||||
<ul>
|
||||
<li>Rangs sur les bulletins</li>
|
||||
</ul>
|
||||
|
||||
<h4>Février 2008</h4>
|
||||
<ul>
|
||||
<li>Statistiques et suivis de cohortes (chiffres et graphes)</li>
|
||||
<li>Nombreuses petites corrections suites aux jurys de janvier</li>
|
||||
</ul>
|
||||
|
||||
<h4>Janvier 2008</h4>
|
||||
<ul>
|
||||
<li>Personnalisation des régles de calculs notes d'option (sport, culture)</li>
|
||||
<li>Edition de PV de jury individuel</li>
|
||||
</ul>
|
||||
|
||||
<h4>Novembre 2007</h4>
|
||||
<ul>
|
||||
<li>Vérification des absences aux évaluations</li>
|
||||
<li>Import des photos depuis portail, trombinoscopes en PDF</li>
|
||||
</ul>
|
||||
|
||||
<h4>Septembre 2007</h4>
|
||||
<ul>
|
||||
<li>Importation des etudiants depuis étapes Apogée</li>
|
||||
<li>Inscription de groupes à des modules (options ou parcours)</li>
|
||||
<li>Listes de étapes Apogée (importées du portail)</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juillet 2007</h4>
|
||||
<ul>
|
||||
<li>Import utilisateurs depuis Excel</li>
|
||||
<li>Nouvelle gestion des passage d'un semestre à l'autre</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juin 2007: version 5.0</h4>
|
||||
<ul>
|
||||
<li>Suivi des parcours et règles de décision des jurys DUT</li>
|
||||
<li>Capitalisation des UEs</li>
|
||||
<li>Edition des PV de jurys et courriers aux étudiants</li>
|
||||
<li>Feuilles (excel) pour préparation jurys</li>
|
||||
<li>Nombreuses petites améliorations</li>
|
||||
</ul>
|
||||
|
||||
<h4>Avril 2007</h4>
|
||||
<ul>
|
||||
<li>Paramètres de mise en page des bulletins en PDF</li>
|
||||
</ul>
|
||||
|
||||
<h4>Février 2007</h4>
|
||||
|
||||
<ul>
|
||||
<li>Possibilité de ne <em>pas</em> publier les bulletins sur le portail</li>
|
||||
<li>Gestion des notes "en attente" (publication d'évaluations sans correction de toutes les copies)</li>
|
||||
<li>Amélioration formulaire saisie absences, saisie absences par semestre.</li>
|
||||
</ul>
|
||||
|
||||
<h4>Janvier 2007</h4>
|
||||
<ul>
|
||||
<li>Possibilité d'initialiser les notes manquantes d'une évaluation</li>
|
||||
<li>Recupération des codes NIP depuis Apogée</li>
|
||||
<li>Gestion des compensations inter-semestre DUT (en cours de développement)</li>
|
||||
<li>Export trombinoscope en archive zip</li>
|
||||
</ul>
|
||||
|
||||
<h4>Octobre 2006</h4>
|
||||
<ul>
|
||||
<li>Réorganisation des pages d'accueil</li>
|
||||
<li>Ajout des "nouvelles" (dernières opérations), avec flux RSS</li>
|
||||
<li>Import/Export XML des formations, duplication d'une formation (versions)</li>
|
||||
<li>Bulletins toujours sur une seule feuille (passage à ReportLab 2.0)</li>
|
||||
<li>Suppression d'un utilisateur</il>
|
||||
</ul>
|
||||
<h4>Septembre 2006</h4>
|
||||
<ul>
|
||||
<li>Page pour suppression des groupes.</li>
|
||||
<li>Amélioration gestion des utilisateurs</li>
|
||||
<li>"Verrouillage" des semestres</li>
|
||||
<li>Liste d'enseignants (chargés de TD) associés à un module (et pouvant saisir des notes)</li>
|
||||
<li>Noms de types de groupes (TD, TP, ...) modifiables</li>
|
||||
<li>Tableau rudimentaire donnant la répartition des bacs dans un semestre</li>
|
||||
<li>Amélioration mise en page des listes au format excel</li>
|
||||
<li>Annulation des démissions</li>
|
||||
</ul>
|
||||
|
||||
<h4>Juillet 2006</h4>
|
||||
<ul>
|
||||
<li>Dialogue permettant au directeur des études de modifier
|
||||
les options d'un semestre</li>
|
||||
<li>Option pour ne pas afficher les UE validées sur les bulletins</li>
|
||||
</ul>
|
||||
|
||||
<h4>30 juin 2006</h4>
|
||||
<ul>
|
||||
<li>Option pour ne pas afficher les décisions sur les bulletins</li>
|
||||
<li>Génération feuilles pour préparation jury</li>
|
||||
<li>Gestion des modules optionnels</li>
|
||||
<li>Prise en compte note "activités culturelles ou sportives"</li>
|
||||
<li>Amélioration tableau de bord semestre</li>
|
||||
<li>Import listes étudiants depuis Excel (avec code Apogée)</li>
|
||||
</ul>
|
||||
|
||||
<h4>12 juin 2006</h4>
|
||||
<ul>
|
||||
<li>Formulaire dynamique d'affectation aux groupes</li>
|
||||
<li>Tri des tableaux (listes, récapitulatif)</li>
|
||||
<li>Export XML des infos sur un etudiant et des groupes</li>
|
||||
</ul>
|
||||
|
||||
<h4>12 mai 2006</h4>
|
||||
<ul>
|
||||
<li>Possibilité de suppression d'un semestre</li>
|
||||
<li>Export XML du tableau recapitulatif des notes du semestre</li>
|
||||
<li>Possibilité de supression d'une formation complète</li>
|
||||
</ul>
|
||||
|
||||
<h4>24 avril 2006</h4>
|
||||
<ul>
|
||||
<li>Export bulletins en XML (expérimental)</li>
|
||||
<li>Flag "gestion_absence" sur les semestres de formation</li>
|
||||
</ul>
|
||||
|
||||
<h4>4 mars 2006</h4>
|
||||
<ul>
|
||||
<li>Formulaire d'inscription au semestre suivant.</li>
|
||||
<li>Format "nombre" dans les feuilles excel exportées.</li>
|
||||
</ul>
|
||||
|
||||
<h4>23 février 2006</h4>
|
||||
<ul>
|
||||
<li>Décisions jury sur bulletins.</li>
|
||||
</ul>
|
||||
|
||||
<h4>17 janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Ajout et édition d'appréciations sur les bulletins.</li>
|
||||
</ul>
|
||||
<h4>12 janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Envoi des bulletins en PDF par mail aux étudiants.</li>
|
||||
</ul>
|
||||
|
||||
<h4>6 janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Affichage des ex-aequos.</li>
|
||||
<li>Classeurs bulletins PDF en différentes versions.</li>
|
||||
<li>Corrigé gestion des notes des démissionnaires.</li>
|
||||
</ul>
|
||||
|
||||
<h4>1er janvier 2006</h4>
|
||||
<ul>
|
||||
<li>Import du projet dans Subversion / LIPN.</li>
|
||||
<li>Lecture des feuilles de notes Excel.</li>
|
||||
</ul>
|
||||
|
||||
<h4>31 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Listes générées au format Excel au lieu de CSV.</li>
|
||||
<li>Bug fix (création/saisie evals).</li>
|
||||
</ul>
|
||||
|
||||
<h4>29 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Affichage des moyennes de chaque groupe dans tableau de bord module.
|
||||
</ul>
|
||||
|
||||
<h4>26 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Révision inscription/édition <em>individuelle</em> d'étudiants.</li>
|
||||
<li>Amélioration fiche étudiant (cosmétique, liste formations, actions).</li>
|
||||
<li>Listings notes d'évaluations anonymes (utilité douteuse ?).</li>
|
||||
<li>Amélioration formulaire saisie notes ('enter' -> champ suivant).</li>
|
||||
</ul>
|
||||
|
||||
<h4>24 décembre 2005</h4>
|
||||
<ul>
|
||||
<li>Génération de bulletins PDF
|
||||
</li>
|
||||
<li>Suppression de notes (permet donc de supprimer une évaluation)
|
||||
</li>
|
||||
<li>Bulletins en versions courtes (seulement moyennes de chaque module), longues
|
||||
(toutes les notes) et intermédiaire (moyenne de chaque module plus notes dans les évaluations sélectionnées).
|
||||
</li>
|
||||
<li>Notes moyennes sous les barres en rouge dans le tableau récapitulatif (seuil=10 sur la moyenne générale, et 8 sur chaque UE).
|
||||
</li>
|
||||
<li>Colonne "groupe de TD" dans le tableau récapitulatif des notes.
|
||||
</ul>
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue