diff --git a/app/decorators.py b/app/decorators.py index c62e98accf..57f561b86d 100644 --- a/app/decorators.py +++ b/app/decorators.py @@ -29,7 +29,7 @@ class ZUser(object): def __str__(self): return self.username - def has_permission(self, perm, context): + def has_permission(self, perm, dept=None): """check if this user as the permission `perm` in departement given by `g.scodoc_dept`. """ @@ -42,7 +42,7 @@ class ZRequest(object): def __init__(self): self.URL = request.base_url.encode( "utf-8" - ) # necessaire pour ScoDoc 8 en Python 2 + ) # necessaire pour ScoDoc 8 en Python 2 #sco8 self.URL0 = self.URL self.BASE0 = request.url_root.encode("utf-8") self.QUERY_STRING = request.query_string.encode("utf-8") diff --git a/app/scodoc/debug.py b/app/scodoc/debug.py index 12aa300e06..a619eede76 100644 --- a/app/scodoc/debug.py +++ b/app/scodoc/debug.py @@ -98,7 +98,7 @@ class FakeUser: def __str__(self): return self.name - def has_permission(self, op, dept): + def has_permission(self, op, dept=None): return True def has_role(self, role): diff --git a/scodoc.py b/scodoc.py index 7d757ddd9d..25559d07aa 100755 --- a/scodoc.py +++ b/scodoc.py @@ -9,13 +9,15 @@ from __future__ import print_function +import os from pprint import pprint as pp import sys import click import flask - +from flask.cli import with_appcontext from app import create_app, cli, db + from app.auth.models import User, Role, UserRole from app.views import notes, scolar, absences @@ -120,3 +122,47 @@ def user_password(username, password=None): db.session.add(u) db.session.commit() click.echo("changed password for user {}".format(u)) + + +@app.cli.command() +@click.argument("dept") +def sco_delete_dept(dept): + "Delete existing departement" + if os.getuid() != 0: + sys.stderr.write("sco_delete_dept: must be run by root\n") + return 1 + if os.system('cd config; ./delete_dept.sh -n "{}"'.format(dept)): + sys.stderr.write("error deleting dept " + dept) + return 1 + return 0 + + +@app.cli.command() +@click.argument("dept") +def sco_create_dept(dept): + "Create new departement" + if os.getuid() != 0: + sys.stderr.write("sco_create_dept: must be run by root\n") + return 1 + if os.system('cd config; ./create_dept.sh -n "{}"'.format(dept)): + sys.stderr.write("error deleting dept " + dept) + return 1 + return 0 + + +@app.cli.command() +@click.argument("filename") +@with_appcontext +def test_interactive(filename=None): + "Run interactive test" + import flask_login + from app import decorators + + click.echo("Executing {}".format(filename)) + with app.test_request_context(""): + u = User.query.first() + flask_login.login_user(u) + REQUEST = decorators.ZRequest() + exec(open(filename).read()) + + click.echo("Done.") diff --git a/scotests/sco_fake_gen.py b/scotests/sco_fake_gen.py index 3d8c6911e6..0d297ae0f4 100644 --- a/scotests/sco_fake_gen.py +++ b/scotests/sco_fake_gen.py @@ -16,29 +16,36 @@ import collections import pprint import random +import scodoc_manager +from app.scodoc import notesdb as ndb +from app.scodoc import sco_codes_parcours +from app.scodoc import sco_edit_formation +from app.scodoc import sco_edit_matiere +from app.scodoc import sco_edit_module +from app.scodoc import sco_edit_ue +from app.scodoc import sco_etud +from app.scodoc import sco_evaluations +from app.scodoc import sco_formations +from app.scodoc import sco_formsemestre +from app.scodoc import sco_formsemestre_inscriptions +from app.scodoc import sco_formsemestre_validation +from app.scodoc import sco_moduleimpl +from app.scodoc import sco_saisie_notes +from app.scodoc import sco_synchro_etuds +from app.scodoc import sco_utils as scu +from app.scodoc.debug import REQUEST +from app.scodoc.notes_log import log +from app.scodoc.sco_exceptions import ScoValueError + random.seed(12345) # tests reproductibles -from debug import REQUEST -import sco_utils -import notesdb as ndb -from notes_log import log -from sco_exceptions import ScoValueError -import scolars -import sco_formsemestre -import sco_formsemestre_inscriptions -import sco_formsemestre_validation -import sco_moduleimpl -import sco_synchro_etuds -import sco_edit_formation -import sco_edit_ue -import sco_codes_parcours -import sco_saisie_notes - -DEMO_DIR = sco_utils.SCO_SRC_DIR + "/scotests/demo/" +DEMO_DIR = scu.SCO_SRC_DIR + "/scotests/demo/" NOMS = [x.strip() for x in open(DEMO_DIR + "/noms.txt").readlines()] PRENOMS_H = [x.strip() for x in open(DEMO_DIR + "/prenoms-h.txt").readlines()] PRENOMS_F = [x.strip() for x in open(DEMO_DIR + "/prenoms-f.txt").readlines()] +PRENOMS_X = [x.strip() for x in open(DEMO_DIR + "/prenoms-x.txt").readlines()] + # nb: en python2, les chaines ci-dessus sont en utf8 @@ -119,7 +126,7 @@ class ScoFake: nom = r_nom if not prenom: prenom = r_prenom - etud = scolars.create_etud(self.context, cnx, args=locals(), REQUEST=REQUEST) + etud = sco_etud.create_etud(self.context, cnx, args=locals(), REQUEST=REQUEST) inscription = "2020" # pylint: disable=unused-variable sco_synchro_etuds.do_import_etud_admission( self.context, cnx, etud["etudid"], locals() @@ -139,8 +146,10 @@ class ScoFake: """Crée une formation""" if not acronyme: acronyme = "TEST" + str(random.randint(100000, 999999)) - oid = self.context.do_formation_create(locals(), REQUEST=REQUEST) - oids = self.context.formation_list(args={"formation_id": oid}) + oid = sco_edit_formation.do_formation_create( + self.context, locals(), REQUEST=REQUEST + ) + oids = sco_formations.formation_list(self.context, formation_id=oid) if not oids: raise ScoValueError("formation not created !") return oids[0] @@ -162,16 +171,16 @@ class ScoFake: """Crée une UE""" if numero is None: numero = sco_edit_ue.next_ue_numero(self.context, formation_id, 0) - oid = self.context.do_ue_create(locals(), REQUEST) - oids = self.context.do_ue_list(args={"ue_id": oid}) + oid = sco_edit_ue.do_ue_create(self.context, locals(), REQUEST) + oids = sco_edit_ue.do_ue_list(self.context, args={"ue_id": oid}) if not oids: raise ScoValueError("ue not created !") return oids[0] @logging_meth def create_matiere(self, ue_id=None, titre=None, numero=None): - oid = self.context.do_matiere_create(locals(), REQUEST) - oids = self.context.do_matiere_list(args={"matiere_id": oid}) + oid = sco_edit_matiere.do_matiere_create(self.context, locals(), REQUEST) + oids = sco_edit_matiere.do_matiere_list(self.context, args={"matiere_id": oid}) if not oids: raise ScoValueError("matiere not created !") return oids[0] @@ -195,8 +204,8 @@ class ScoFake: code_apogee=None, module_type=None, ): - oid = self.context.do_module_create(locals(), REQUEST) - oids = self.context.do_module_list(args={"module_id": oid}) + oid = sco_edit_module.do_module_create(self.context, locals(), REQUEST) + oids = sco_edit_module.do_module_list(self.context, args={"module_id": oid}) if not oids: raise ScoValueError("module not created ! (oid=%s)" % oid) return oids[0] @@ -223,7 +232,7 @@ class ScoFake: etapes=None, responsables=["bach"], ): - oid = self.context.do_formsemestre_create(locals(), REQUEST) + oid = sco_formsemestre.do_formsemestre_create(self.context, locals(), REQUEST) # oids = self.context.do_formsemestre_list(args={"formsemestre_id": oid}) oids = sco_formsemestre.do_formsemestre_list( self.context, args={"formsemestre_id": oid} @@ -277,8 +286,10 @@ class ScoFake: ): args = locals() del args["self"] - oid = self.context.do_evaluation_create(**args) - oids = self.context.do_evaluation_list(args={"evaluation_id": oid}) + oid = sco_evaluations.do_evaluation_create(self.context, **args) + oids = sco_evaluations.do_evaluation_list( + self.context, args={"evaluation_id": oid} + ) if not oids: raise ScoValueError("evaluation not created !") return oids[0] @@ -418,3 +429,8 @@ class ScoFake: assidu=assidu, REQUEST=REQUEST, ) + + +# band aid for #sco8 dev (temporaire en attendant Users) +class Sco8Context(object): + Users = scodoc_manager.FakeUsers() diff --git a/scotests/test_basic.py b/scotests/test_basic.py index 80f177b74a..228998851a 100644 --- a/scotests/test_basic.py +++ b/scotests/test_basic.py @@ -6,26 +6,29 @@ Création 10 étudiants, formation, semestre, inscription etudiant, creation 1 evaluation, saisie 10 notes. Utiliser comme: - scotests/scointeractive.sh -r TEST00 scotests/test_basic.py + flask test-interactive scotests/test_basic.py """ import random -# La variable context est définie par le script de lancement -# l'affecte ainsi pour évietr les warnins pylint: -context = context # pylint: disable=undefined-variable -REQUEST = REQUEST # pylint: disable=undefined-variable -import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error -import sco_utils -import sco_abs -import sco_abs_views -import sco_bulletins -import sco_evaluations -import sco_codes_parcours -import sco_parcours_dut -import sco_formsemestre_validation +from flask import g -G = sco_fake_gen.ScoFake(context.Notes) +from app import decorators +import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error +from app.scodoc import sco_utils as scu +from app.scodoc import sco_abs +from app.scodoc import sco_abs_views +from app.scodoc import sco_bulletins +from app.scodoc import sco_evaluations +from app.scodoc import sco_codes_parcours +from app.scodoc import sco_parcours_dut +from app.scodoc import sco_formsemestre_validation + + +context = sco_fake_gen.Sco8Context() +g.scodoc_dept = "TEST00" + +G = sco_fake_gen.ScoFake(context) G.verbose = False # --- Création d'étudiants @@ -78,10 +81,10 @@ for etud in etuds: # --- Vérifie que les notes sont prises en compte: b = sco_bulletins.formsemestre_bulletinetud_dict( - context.Notes, sem["formsemestre_id"], etud["etudid"], REQUEST=REQUEST + context, sem["formsemestre_id"], etud["etudid"], REQUEST=REQUEST ) # Toute les notes sont saisies, donc eval complète -etat = sco_evaluations.do_evaluation_etat(context.Notes, e["evaluation_id"]) +etat = sco_evaluations.do_evaluation_etat(context, e["evaluation_id"]) assert etat["evalcomplete"] # Un seul module, donc moy gen == note module assert b["ues"][0]["cur_moy_ue_txt"] == b["ues"][0]["modules"][0]["mod_moy_txt"] @@ -91,7 +94,6 @@ assert ( == b["ues"][0]["modules"][0]["evaluations"][0]["note_txt"] ) - # --- Une autre évaluation e2 = G.create_evaluation( moduleimpl_id=mi["moduleimpl_id"], @@ -105,16 +107,16 @@ for etud in etuds[:5]: evaluation=e2, etud=etud, note=float(random.randint(0, 20)) ) # Cette éval n'est pas complète -etat = sco_evaluations.do_evaluation_etat(context.Notes, e2["evaluation_id"]) +etat = sco_evaluations.do_evaluation_etat(context, e2["evaluation_id"]) assert etat["evalcomplete"] == False # la première éval est toujours complète: -etat = sco_evaluations.do_evaluation_etat(context.Notes, e["evaluation_id"]) +etat = sco_evaluations.do_evaluation_etat(context, e["evaluation_id"]) assert etat["evalcomplete"] # Modifie l'évaluation 2 pour "prise en compte immédiate" e2["publish_incomplete"] = "1" sco_evaluations.do_evaluation_edit(context, REQUEST, e2) -etat = sco_evaluations.do_evaluation_etat(context.Notes, e2["evaluation_id"]) +etat = sco_evaluations.do_evaluation_etat(context, e2["evaluation_id"]) assert etat["evalcomplete"] == False assert etat["nb_att"] == 0 # il n'y a pas de notes (explicitement) en attente assert etat["evalattente"] # mais l'eval est en attente (prise en compte immédiate) @@ -124,7 +126,7 @@ for etud in etuds[5:]: nb_changed, nb_suppress, existing_decisions = G.create_note( evaluation=e2, etud=etud, note=float(random.randint(0, 20)) ) -etat = sco_evaluations.do_evaluation_etat(context.Notes, e2["evaluation_id"]) +etat = sco_evaluations.do_evaluation_etat(context, e2["evaluation_id"]) assert etat["evalcomplete"] assert etat["nb_att"] == 0 assert not etat["evalattente"] # toutes les notes sont présentes @@ -133,7 +135,7 @@ assert not etat["evalattente"] # toutes les notes sont présentes etudid = etuds[0]["etudid"] _ = sco_abs_views.doSignaleAbsence( - context.Absences, + context, "15/01/2020", "18/01/2020", demijournee=2, @@ -142,7 +144,7 @@ _ = sco_abs_views.doSignaleAbsence( ) _ = sco_abs_views.doJustifAbsence( - context.Absences, + context, "17/01/2020", "18/01/2020", demijournee=2, @@ -150,9 +152,9 @@ _ = sco_abs_views.doJustifAbsence( REQUEST=REQUEST, ) -a = sco_abs.getAbsSemEtud(context.Absences, sem, etudid) -assert a.CountAbs() == 3 -assert a.CountAbsJust() == 1 +a = sco_abs.getAbsSemEtud(context, sem, etudid) +assert a.CountAbs() == 6, "incorrect CountAbs (%d)" % a.CountAbs() +assert a.CountAbsJust() == 2, "incorrect CountAbsJust (%s)" % a.CountAbsJust() # --- Permission saisie notes et décisions de jury, avec ou sans démission ou défaillance # on n'a pas encore saisi de décisions @@ -160,7 +162,7 @@ assert not sco_parcours_dut.formsemestre_has_decisions(context, sem["formsemestr # Saisie d'un décision AJ, non assidu etudid = etuds[-1]["etudid"] sco_parcours_dut.formsemestre_validate_ues( - context.Notes, + context, sem["formsemestre_id"], etudid, sco_codes_parcours.AJ, @@ -168,12 +170,12 @@ sco_parcours_dut.formsemestre_validate_ues( REQUEST=REQUEST, ) assert sco_parcours_dut.formsemestre_has_decisions( - context.Notes, sem["formsemestre_id"] -) + context, sem["formsemestre_id"] +), "décisions manquantes" # Suppression de la décision sco_formsemestre_validation.formsemestre_validation_suppress_etud( - context.Notes, sem["formsemestre_id"], etudid + context, sem["formsemestre_id"], etudid ) assert not sco_parcours_dut.formsemestre_has_decisions( - context.Notes, sem["formsemestre_id"] -) + context, sem["formsemestre_id"] +), "décisions non effacées"