2021-05-29 18:22:51 +02:00
|
|
|
# -*- coding: UTF-8 -*
|
|
|
|
|
|
|
|
|
|
|
|
"""Application Flask: ScoDoc
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
from pprint import pprint as pp
|
2021-09-13 23:06:42 +02:00
|
|
|
import re
|
2021-05-31 09:57:23 +02:00
|
|
|
import sys
|
2021-05-29 18:22:51 +02:00
|
|
|
|
|
|
|
import click
|
|
|
|
import flask
|
2021-06-24 10:59:03 +02:00
|
|
|
from flask.cli import with_appcontext
|
2021-09-13 16:11:33 +02:00
|
|
|
|
2021-08-10 12:57:38 +02:00
|
|
|
from app import create_app, cli, db
|
|
|
|
from app import initialize_scodoc_database
|
|
|
|
from app import clear_scodoc_cache
|
2021-09-13 16:11:33 +02:00
|
|
|
from app import models
|
2021-06-24 10:59:03 +02:00
|
|
|
|
2021-05-29 18:22:51 +02:00
|
|
|
from app.auth.models import User, Role, UserRole
|
2021-08-18 19:04:43 +02:00
|
|
|
from app.models import ScoPreference
|
2021-09-13 16:11:33 +02:00
|
|
|
from app.scodoc.sco_permissions import Permission
|
2021-06-14 18:08:52 +02:00
|
|
|
from app.views import notes, scolar, absences
|
2021-08-14 18:54:32 +02:00
|
|
|
import tools
|
2021-05-29 18:22:51 +02:00
|
|
|
|
2021-08-19 23:12:42 +02:00
|
|
|
from config import RunningConfig
|
2021-05-29 18:22:51 +02:00
|
|
|
|
2021-08-19 23:12:42 +02:00
|
|
|
app = create_app(RunningConfig)
|
2021-05-29 18:22:51 +02:00
|
|
|
cli.register(app)
|
|
|
|
|
|
|
|
|
|
|
|
@app.shell_context_processor
|
|
|
|
def make_shell_context():
|
2021-07-25 22:31:59 +02:00
|
|
|
from app.scodoc import notesdb as ndb
|
2021-07-29 10:19:00 +02:00
|
|
|
from app.scodoc import sco_utils as scu
|
2021-08-01 09:52:28 +02:00
|
|
|
from flask_login import login_user, logout_user, current_user
|
2021-08-19 21:30:22 +02:00
|
|
|
import app as mapp # le package app
|
2021-08-01 09:52:28 +02:00
|
|
|
|
2021-05-29 18:22:51 +02:00
|
|
|
return {
|
|
|
|
"db": db,
|
|
|
|
"User": User,
|
|
|
|
"Role": Role,
|
|
|
|
"UserRole": UserRole,
|
2021-09-13 16:11:33 +02:00
|
|
|
"Permission": Permission,
|
2021-06-14 18:08:52 +02:00
|
|
|
"notes": notes,
|
|
|
|
"scolar": scolar,
|
2021-07-25 22:31:59 +02:00
|
|
|
"ndb": ndb,
|
2021-07-29 10:19:00 +02:00
|
|
|
"scu": scu,
|
2021-05-29 18:22:51 +02:00
|
|
|
"pp": pp,
|
|
|
|
"flask": flask,
|
|
|
|
"current_app": flask.current_app,
|
2021-08-01 09:52:28 +02:00
|
|
|
"current_user": current_user,
|
|
|
|
"login_user": login_user,
|
|
|
|
"logout_user": logout_user,
|
|
|
|
"ctx": app.test_request_context(),
|
2021-08-07 15:20:30 +02:00
|
|
|
"models": models,
|
2021-08-19 21:30:22 +02:00
|
|
|
"mapp": mapp,
|
2021-05-29 18:22:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-01 09:52:28 +02:00
|
|
|
# ctx.push()
|
|
|
|
# login_user(admin)
|
|
|
|
|
|
|
|
|
2021-05-29 18:22:51 +02:00
|
|
|
@app.cli.command()
|
2021-08-27 17:03:47 +02:00
|
|
|
def sco_db_init(): # sco-db-init
|
2021-08-09 23:23:11 +02:00
|
|
|
"""Initialize the database.
|
|
|
|
Starts from an existing database and create all
|
|
|
|
the necessary SQL tables and functions.
|
|
|
|
"""
|
2021-08-19 23:43:14 +02:00
|
|
|
if not app.config.get("SCODOC_ADMIN_MAIL"):
|
|
|
|
sys.stderr.write(
|
|
|
|
"""La variable SCODOC_ADMIN_MAIL n'est pas positionnée: vérifier votre .env"""
|
|
|
|
)
|
|
|
|
return 100
|
2021-08-09 23:23:11 +02:00
|
|
|
initialize_scodoc_database()
|
2021-05-29 18:22:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
2021-05-31 09:57:23 +02:00
|
|
|
def user_db_clear():
|
2021-08-09 23:23:11 +02:00
|
|
|
"""Erase all users and roles from the database !"""
|
2021-05-31 09:57:23 +02:00
|
|
|
click.echo("Erasing the users database !")
|
|
|
|
_clear_users_db()
|
2021-05-29 18:22:51 +02:00
|
|
|
|
|
|
|
|
2021-05-31 09:57:23 +02:00
|
|
|
def _clear_users_db():
|
2021-05-29 18:22:51 +02:00
|
|
|
"""Erase (drop) all tables of users database !"""
|
2021-08-09 23:23:11 +02:00
|
|
|
click.confirm(
|
|
|
|
"This will erase all users and roles.\nAre you sure you want to continue?",
|
|
|
|
abort=True,
|
|
|
|
)
|
2021-05-29 18:22:51 +02:00
|
|
|
db.reflect()
|
2021-08-09 23:23:11 +02:00
|
|
|
try:
|
|
|
|
db.session.query(UserRole).delete()
|
|
|
|
db.session.query(User).delete()
|
|
|
|
db.session.query(User).delete()
|
|
|
|
db.session.commit()
|
|
|
|
except:
|
|
|
|
db.session.rollback()
|
|
|
|
raise
|
2021-05-31 09:57:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
|
|
|
@click.argument("username")
|
|
|
|
@click.argument("role")
|
|
|
|
@click.argument("dept")
|
2021-08-08 09:50:10 +02:00
|
|
|
@click.option("-n", "--nom", "nom")
|
|
|
|
@click.option("-p", "--prenom", "prenom")
|
|
|
|
def user_create(username, role, dept, nom=None, prenom=None): # user-create
|
2021-05-31 09:57:23 +02:00
|
|
|
"Create a new user"
|
|
|
|
r = Role.get_named_role(role)
|
|
|
|
if not r:
|
2021-07-05 21:56:28 +02:00
|
|
|
sys.stderr.write("user_create: role {r} does not exists\n".format(r=role))
|
2021-05-31 09:57:23 +02:00
|
|
|
return 1
|
2021-06-26 21:57:54 +02:00
|
|
|
u = User.query.filter_by(user_name=username).first()
|
2021-05-31 09:57:23 +02:00
|
|
|
if u:
|
2021-07-05 21:56:28 +02:00
|
|
|
sys.stderr.write("user_create: user {u} already exists\n".format(u=u))
|
2021-05-31 09:57:23 +02:00
|
|
|
return 2
|
2021-07-05 21:56:28 +02:00
|
|
|
if dept == "@all":
|
|
|
|
dept = None
|
2021-08-08 09:50:10 +02:00
|
|
|
u = User(user_name=username, dept=dept, nom=nom, prenom=prenom)
|
2021-05-31 09:57:23 +02:00
|
|
|
u.add_role(r, dept)
|
|
|
|
db.session.add(u)
|
|
|
|
db.session.commit()
|
|
|
|
click.echo(
|
2021-06-26 21:57:54 +02:00
|
|
|
"created user, login: {u.user_name}, with role {r} in dept. {dept}".format(
|
2021-05-31 09:57:23 +02:00
|
|
|
u=u, r=r, dept=dept
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
|
|
|
@click.argument("username")
|
|
|
|
@click.password_option()
|
2021-07-05 21:56:28 +02:00
|
|
|
def user_password(username, password=None): # user-password
|
2021-05-31 09:57:23 +02:00
|
|
|
"Set (or change) user's password"
|
|
|
|
if not password:
|
|
|
|
sys.stderr.write("user_password: missing password")
|
|
|
|
return 1
|
2021-06-26 21:57:54 +02:00
|
|
|
u = User.query.filter_by(user_name=username).first()
|
2021-05-31 09:57:23 +02:00
|
|
|
if not u:
|
2021-09-13 16:11:33 +02:00
|
|
|
sys.stderr.write(f"user_password: user {username} does not exists\n")
|
2021-05-31 09:57:23 +02:00
|
|
|
return 1
|
|
|
|
|
|
|
|
u.set_password(password)
|
|
|
|
db.session.add(u)
|
|
|
|
db.session.commit()
|
2021-09-13 16:11:33 +02:00
|
|
|
click.echo(f"changed password for user {u}")
|
|
|
|
|
|
|
|
|
2021-09-13 23:06:42 +02:00
|
|
|
@app.cli.command()
|
|
|
|
@click.argument("rolename")
|
|
|
|
@click.argument("permissions", nargs=-1)
|
|
|
|
def create_role(rolename, permissions): # create-role
|
|
|
|
"""Create a new role"""
|
|
|
|
# Check rolename
|
|
|
|
if not re.match(r"^[a-zA-Z0-9]+$", rolename):
|
|
|
|
sys.stderr.write(f"create_role: invalid rolename {rolename}\n")
|
|
|
|
return 1
|
|
|
|
# Check permissions
|
|
|
|
permission_list = []
|
|
|
|
for permission_name in permissions:
|
|
|
|
perm = Permission.get_by_name(permission_name)
|
|
|
|
if not perm:
|
|
|
|
sys.stderr.write(f"create_role: invalid permission name {perm}\n")
|
|
|
|
sys.stderr.write(
|
|
|
|
f"\tavailable permissions: {', '.join([ name for name in Permission.permission_by_name])}.\n"
|
|
|
|
)
|
|
|
|
return 1
|
|
|
|
permission_list.append(perm)
|
|
|
|
|
|
|
|
role = Role.query.filter_by(name=rolename).first()
|
|
|
|
if role:
|
|
|
|
sys.stderr.write(f"create_role: role {rolename} already exists\n")
|
|
|
|
return 1
|
|
|
|
|
|
|
|
role = Role(name=rolename)
|
|
|
|
for perm in permission_list:
|
|
|
|
role.add_permission(perm)
|
|
|
|
db.session.add(role)
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
|
2021-09-13 16:11:33 +02:00
|
|
|
@app.cli.command()
|
|
|
|
@click.argument("rolename")
|
|
|
|
@click.option("-a", "--add", "addpermissionname")
|
|
|
|
@click.option("-r", "--remove", "removepermissionname")
|
|
|
|
def edit_role(rolename, addpermissionname=None, removepermissionname=None): # edit-role
|
|
|
|
"""Add [-a] and/or remove [-r] a permission to/from a role.
|
|
|
|
In ScoDoc, permissions are not associated to users but to roles.
|
|
|
|
Each user has a set of roles in each departement.
|
|
|
|
|
|
|
|
Example: `flask edit-role -a ScoEditApo Ens`
|
|
|
|
"""
|
|
|
|
if addpermissionname:
|
2021-09-13 23:06:42 +02:00
|
|
|
perm_to_add = Permission.get_by_name(addpermissionname)
|
|
|
|
if not perm_to_add:
|
2021-09-13 16:11:33 +02:00
|
|
|
sys.stderr.write(
|
|
|
|
f"edit_role: permission {addpermissionname} does not exists\n"
|
|
|
|
)
|
|
|
|
return 1
|
|
|
|
else:
|
|
|
|
perm_to_add = None
|
|
|
|
if removepermissionname:
|
2021-09-13 23:06:42 +02:00
|
|
|
perm_to_remove = Permission.get_by_name(removepermissionname)
|
|
|
|
if not perm_to_remove:
|
2021-09-13 16:11:33 +02:00
|
|
|
sys.stderr.write(
|
|
|
|
f"edit_role: permission {removepermissionname} does not exists\n"
|
|
|
|
)
|
|
|
|
return 1
|
|
|
|
else:
|
|
|
|
perm_to_remove = None
|
|
|
|
role = Role.query.filter_by(name=rolename).first()
|
|
|
|
if not role:
|
|
|
|
sys.stderr.write(f"edit_role: role {rolename} does not exists\n")
|
|
|
|
return 1
|
|
|
|
if perm_to_add:
|
|
|
|
role.add_permission(perm_to_add)
|
|
|
|
click.echo(f"adding permission {addpermissionname} to role {rolename}")
|
|
|
|
if perm_to_remove:
|
|
|
|
role.remove_permission(perm_to_remove)
|
|
|
|
click.echo(f"removing permission {removepermissionname} from role {rolename}")
|
|
|
|
if perm_to_add or perm_to_remove:
|
|
|
|
db.session.add(role)
|
|
|
|
db.session.commit()
|
2021-06-24 10:59:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
|
|
|
@click.argument("dept")
|
2021-08-13 09:31:49 +02:00
|
|
|
def delete_dept(dept): # delete-dept
|
2021-08-13 00:34:58 +02:00
|
|
|
"""Delete existing departement"""
|
2021-08-19 21:30:22 +02:00
|
|
|
from app.scodoc import notesdb as ndb
|
|
|
|
from app.scodoc import sco_dept
|
|
|
|
|
2021-08-13 00:34:58 +02:00
|
|
|
click.confirm(
|
|
|
|
f"""Attention: Cela va effacer toutes les données du département {dept}
|
|
|
|
(étudiants, notes, formations, etc)
|
|
|
|
Voulez-vous vraiment continuer ?
|
|
|
|
""",
|
|
|
|
abort=True,
|
|
|
|
)
|
|
|
|
db.reflect()
|
2021-08-19 21:30:22 +02:00
|
|
|
ndb.open_db_connection()
|
2021-08-13 00:34:58 +02:00
|
|
|
d = models.Departement.query.filter_by(acronym=dept).first()
|
|
|
|
if d is None:
|
2021-09-04 11:37:46 +02:00
|
|
|
sys.stderr.write(f"Erreur: le departement {dept} n'existe pas !\n")
|
2021-08-13 00:34:58 +02:00
|
|
|
return 2
|
2021-08-19 21:30:22 +02:00
|
|
|
sco_dept.delete_dept(d.id)
|
2021-08-13 00:34:58 +02:00
|
|
|
db.session.commit()
|
2021-06-24 10:59:03 +02:00
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
|
|
|
@click.argument("dept")
|
2021-08-13 09:31:49 +02:00
|
|
|
def create_dept(dept): # create-dept
|
2021-06-24 10:59:03 +02:00
|
|
|
"Create new departement"
|
2021-08-13 00:34:58 +02:00
|
|
|
d = models.Departement(acronym=dept)
|
2021-08-18 19:04:43 +02:00
|
|
|
p1 = ScoPreference(name="DeptName", value=dept, departement=d)
|
|
|
|
db.session.add(p1)
|
2021-08-13 00:34:58 +02:00
|
|
|
db.session.add(d)
|
|
|
|
db.session.commit()
|
2021-06-24 10:59:03 +02:00
|
|
|
return 0
|
|
|
|
|
|
|
|
|
2021-07-05 00:07:17 +02:00
|
|
|
@app.cli.command()
|
|
|
|
@with_appcontext
|
2021-08-14 18:54:32 +02:00
|
|
|
def import_scodoc7_users(): # import-scodoc7-users
|
2021-09-13 23:06:42 +02:00
|
|
|
"""Import users defined in ScoDoc7 postgresql database into ScoDoc 9
|
2021-07-05 00:07:17 +02:00
|
|
|
The old database SCOUSERS must be alive and readable by the current user.
|
2021-07-27 16:07:03 +02:00
|
|
|
This script is typically run as unix user "scodoc".
|
|
|
|
The original SCOUSERS database is left unmodified.
|
2021-07-05 00:07:17 +02:00
|
|
|
"""
|
2021-08-14 18:54:32 +02:00
|
|
|
messages = tools.import_scodoc7_user_db()
|
|
|
|
click.echo("----")
|
|
|
|
click.echo(f"import terminé: {len(messages)} warnings\n")
|
|
|
|
click.echo("\n".join(messages) + "\n")
|
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
|
|
|
@click.argument("dept")
|
2021-08-20 09:57:38 +02:00
|
|
|
@click.argument("dept_db_name")
|
2021-08-14 18:54:32 +02:00
|
|
|
@with_appcontext
|
2021-08-20 09:57:38 +02:00
|
|
|
def import_scodoc7_dept(dept: str, dept_db_name: str): # import-scodoc7-dept
|
|
|
|
"""Import département ScoDoc 7: dept: InfoComm, dept_db_name: SCOINFOCOMM"""
|
|
|
|
dept_db_uri = f"postgresql:///{dept_db_name}"
|
|
|
|
tools.import_scodoc7_dept(dept, dept_db_uri)
|
2021-07-29 10:30:13 +02:00
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
|
|
|
@with_appcontext
|
|
|
|
def clear_cache(): # clear-cache
|
|
|
|
"""Clear ScoDoc cache
|
|
|
|
This cache (currently Redis) is persistent between invocation
|
2021-09-13 23:06:42 +02:00
|
|
|
and it may be necessary to clear it during development or tests.
|
2021-07-29 10:30:13 +02:00
|
|
|
"""
|
2021-08-10 12:57:38 +02:00
|
|
|
clear_scodoc_cache()
|
2021-07-29 10:30:13 +02:00
|
|
|
click.echo("Redis caches flushed.")
|
2021-09-13 23:06:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
def recursive_help(cmd, parent=None):
|
|
|
|
ctx = click.core.Context(cmd, info_name=cmd.name, parent=parent)
|
|
|
|
print(cmd.get_help(ctx))
|
|
|
|
print()
|
|
|
|
commands = getattr(cmd, "commands", {})
|
|
|
|
for sub in commands.values():
|
|
|
|
recursive_help(sub, ctx)
|
|
|
|
|
|
|
|
|
|
|
|
@app.cli.command()
|
|
|
|
def dumphelp():
|
|
|
|
recursive_help(app.cli)
|