Merge branch 'master' of https://github.com/ariasia/OLA_Mirror
This commit is contained in:
commit
8c97b49a67
|
@ -1,2 +1,2 @@
|
|||
.idea
|
||||
|
||||
.directory
|
||||
|
|
|
@ -20,20 +20,13 @@ GET -> Get the current logged user, return None if no one is connected
|
|||
Out:
|
||||
200 -> USER = <USER_OBJECT>|null : Dictionary containing user infos or null
|
||||
|
||||
|
||||
########################
|
||||
Redirect to cas auth (/login)
|
||||
########################
|
||||
Redirect to cas auth
|
||||
|
||||
|
||||
########################
|
||||
UserAPI (api/user)
|
||||
########################
|
||||
POST -> Create a user if it not already exists
|
||||
In:
|
||||
CASid = Login of the user caught from the CAS authentication
|
||||
role = Role of the user (can be concatenated with -) 1=secrétaire, 2=resp_formation, 3=tuteur_univ, 4=étudiant
|
||||
email = Email and login of the user (must be unique)
|
||||
role = Role of the user (can be concatenated with -) 1=secrétaire, 2=resp_formation, 3=tuteur_univ, 4=étudiant, 5=tuteur_ent
|
||||
Out:
|
||||
200 -> UID = <USER_ID> : The user already exists with the id USER_ID
|
||||
201 -> UID = <USER_ID> : The user has been successfully created with the id USER_ID
|
||||
|
@ -41,17 +34,67 @@ POST -> Create a user if it not already exists
|
|||
|
||||
PUT -> Modify an existing user
|
||||
In: (Suffix = /byuid/<USER_ID>)
|
||||
CASid = Login of the user caught from the CAS authentication
|
||||
role = Role of the user (can be concatenated with -) 1=secrétaire, 2=resp_formation, 3=tuteur_univ, 4=étudiant
|
||||
role = Role of the user (can be concatenated with -) 1=secrétaire, 2=resp_formation, 3=tuteur_univ, 4=étudiant, 5=tuteur_ent
|
||||
phone = Phone number of the user (00.00.00.00.00)
|
||||
email = Email of the user
|
||||
email = Email of the user (must be unique)
|
||||
Out:
|
||||
200 -> UID = <USER_ID> : The user has been modified sucessfully with the id USER_ID
|
||||
400 -> ERROR = "One or more parameters are missing !" : Bad request
|
||||
405 -> ERROR = "This user doesn't exists !" : Bad USER_ID provided
|
||||
405 -> ERROR = "A user with this email already exists !" : A user with this email already exists
|
||||
|
||||
GET -> Getting specified user infos
|
||||
In: (Suffixes = /byuid/<USER_ID> | /bylogin/<USER_LOGIN> | /byemail/<USER_EMAIL>)
|
||||
In: (Suffixes = /byuid/<USER_ID> | /byemail/<USER_EMAIL>)
|
||||
Out:
|
||||
200 -> USER = <USER_OBJECT>|null : Dictionary containing user infos or null
|
||||
|
||||
########################
|
||||
GroupAPI (api/group)
|
||||
########################
|
||||
POST -> Create a group if it not already exists
|
||||
In:
|
||||
name = Name of the group (must be unique)
|
||||
year = Parameter setting the year
|
||||
class_short = Parameter setting the short name of the class
|
||||
class_long = Parameter setting the full name of the class
|
||||
department = Parameter setting the name of the class's department
|
||||
resp_id = UID of the group's responsible
|
||||
sec_id = UID of the group's secretary
|
||||
Out:
|
||||
200 -> GID = <GROUP_ID> : The group already exists with the id GROUP_ID
|
||||
201 -> GID = <GROUP_ID> : The group has been successfully created with the id GROUP_ID
|
||||
400 -> ERROR = "One or more parameters are missing" : Bad request
|
||||
400 -> ERROR = "The user with id <USER_ID> doesn't exists !" : The given USER_ID for resp_id or sec_id is not found
|
||||
|
||||
PUT -> Modify an existing group
|
||||
In: (Suffix = /bygid/<GROUP_ID>)
|
||||
name = Name of the group (must be unique)
|
||||
year = Parameter setting the year
|
||||
class_short = Parameter setting the short name of the class
|
||||
class_long = Parameter setting the full name of the class
|
||||
department = Parameter setting the name of the class's department
|
||||
resp_id = UID of the group's responsible
|
||||
sec_id = UID of the group's secretary
|
||||
Out:
|
||||
200 -> GID = <GROUP_ID> : The group has been modified sucessfully with the id GROUP_ID
|
||||
400 -> ERROR = "One or more parameters are missing !" : Bad request
|
||||
400 -> ERROR = "The user with id <USER_ID> doesn't exists !" : The given USER_ID for resp_id or sec_id is not found
|
||||
405 -> ERROR = "This group doesn't exists !" : Bad GROUP_ID provided
|
||||
405 -> ERROR = "A group with this name already exists !" : A group with this name already exists
|
||||
|
||||
GET -> Getting specified group infos
|
||||
In: (Suffixes = /bygid/<GROUP_ID> | /byname/<GROUP_NAME> )
|
||||
Out:
|
||||
200 -> GROUP = <GROUP_OBJECT>|null : Dictionary containing group infos or null
|
||||
|
||||
OPTIONS -> Add pairs of users (student/tutor) to the group
|
||||
In:
|
||||
pairs -> Table of pairs student's uid/tutor's uid (ex: [[1,2],[3,2]])
|
||||
Out:
|
||||
200 -> RESULT = "Pairs added successfully"
|
||||
400 -> ERROR = "One or more parameters are missing !" : Bad request
|
||||
400 -> ERROR = "The user with id <USER_ID> doesn't exists !" : The given USER_ID for student or tutor is not found
|
||||
400 -> ERROR = "A student must have the 'student' role !" : The given USER_ID for student doesn't have the "student" role (4)
|
||||
400 -> ERROR = "A student can't be a tutor !" : The given USER_ID for tutor have the "student" role (4) and so can't be a tutor
|
||||
405 -> ERROR = "This group doesn't exists !" : Bad GROUP_ID provided
|
||||
409 -> ERROR = "Pairs are incorrectly formed !" : Bad syntax in pairs table
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,67 @@
|
|||
Page 1 :
|
||||
|
||||
MASTER : {{ nom_master }}
|
||||
|
||||
{{ nom_complet_master }}
|
||||
|
||||
Année universitaire :
|
||||
|
||||
{{ annee_1 }}
|
||||
-
|
||||
{{ annee_2 }}
|
||||
|
||||
De : {{ nom_prenom }}
|
||||
|
||||
mail : {{ email }}
|
||||
|
||||
tel : {{ telephone }}
|
||||
|
||||
Alternant à : {{ entreprise }}
|
||||
|
||||
tuteur/tutrice pédagogique : {{ tuteur_pedagogique }}
|
||||
|
||||
Tuteur / Tutrice entreprise : {{ tuteur_entreprise }}
|
||||
|
||||
|
||||
Page 2 :
|
||||
Alternant
|
||||
Type de contrat :
|
||||
{{ type_contrat_apprentissage | X }}
|
||||
{{ type_contrat_professionnalisation | X }}
|
||||
{{ type_contrat_stage | X }}
|
||||
|
||||
Début de contrat : {{ debut_contrat }}
|
||||
Fin de contrat : {{ fin_contrat }}
|
||||
tel : {{ telephone }}
|
||||
mail : {{ email }}
|
||||
compostant de formation : {{ compostant_formation }}
|
||||
responsable tuteur_pedagogique de la formation : {{ responsable_pedagogique_formation }}
|
||||
tel : {{ tel_responsable_pedagogique_formation }}
|
||||
mail : {{ mail_responsable_pedagogique_formation }}
|
||||
|
||||
tuteur pédagogique :
|
||||
{{ tel_tuteur_pedagogique }}
|
||||
{{ mail_tuteur_pedagogique }}
|
||||
|
||||
tuteur entreprise :
|
||||
{{ tuteur_entreprise }}
|
||||
entreprise : {{ entreprise }}
|
||||
adresse lieu Alternance : {{ adresse_entreprise }}
|
||||
tel : {{ tel_tuteur_entreprise }}
|
||||
mail : {{ mail_tuteur_entreprise }}
|
||||
|
||||
Page 4 :
|
||||
poste occupé : {{ poste_occupe }}
|
||||
{{ poste_occupe_2 }}
|
||||
|
||||
Pentreprise :
|
||||
{{ n_periode }}
|
||||
{{ debut_periode }}
|
||||
{{ fin_periode }}
|
||||
{{ travaux_entreprise }}
|
||||
{{ remarque_tuteur }}
|
||||
|
||||
bilan_periode :
|
||||
|
||||
{{ n_periode }}
|
||||
{{ bilan_periode }}
|
|
@ -31,3 +31,4 @@ coverage.xml
|
|||
|
||||
#Config files
|
||||
config.py
|
||||
/app/OLA_RESSOURCES/
|
||||
|
|
|
@ -37,11 +37,13 @@ CREATE TABLE IF NOT EXISTS `GROUP`
|
|||
|
||||
CREATE TABLE IF NOT EXISTS `USER`
|
||||
(
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`login` VARCHAR(128) NOT NULL,
|
||||
`role` VARCHAR(10) NOT NULL,
|
||||
email VARCHAR(256) NOT NULL,
|
||||
phone VARCHAR(15),
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`role` VARCHAR(10) NOT NULL,
|
||||
email VARCHAR(128) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
psw VARCHAR(256) DEFAULT NULL,
|
||||
hash VARCHAR(128),
|
||||
phone VARCHAR(15),
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = INNODB;
|
||||
|
||||
|
@ -56,19 +58,17 @@ CREATE TABLE IF NOT EXISTS TUTORSHIP
|
|||
|
||||
CREATE TABLE IF NOT EXISTS LIVRET
|
||||
(
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
tutorship_id BIGINT,
|
||||
etutor_name VARCHAR(128) NOT NULL,
|
||||
etutor_email VARCHAR(256) NOT NULL,
|
||||
etutor_phone VARCHAR(15) NOT NULL,
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
tutorship_id BIGINT NOT NULL,
|
||||
etutor_id BIGINT NOT NULL,
|
||||
company_name VARCHAR(256) NOT NULL,
|
||||
company_address VARCHAR(512) NOT NULL,
|
||||
contract_type INT NOT NULL,
|
||||
contract_start DATE NOT NULL,
|
||||
contract_end DATE NOT NULL,
|
||||
contract_type INT NOT NULL,
|
||||
contract_start DATE NOT NULL,
|
||||
contract_end DATE NOT NULL,
|
||||
ressources_dir VARCHAR(512),
|
||||
opened TINYINT(1) NOT NULL,
|
||||
expire DATE NOT NULL,
|
||||
opened TINYINT(1) NOT NULL,
|
||||
expire DATE NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = INNODB;
|
||||
|
||||
|
@ -85,14 +85,6 @@ CREATE TABLE IF NOT EXISTS PERIOD
|
|||
PRIMARY KEY (id)
|
||||
) ENGINE = INNODB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS HASHTABLE
|
||||
(
|
||||
token VARCHAR(255) NOT NULL,
|
||||
exipre DATE NOT NULL,
|
||||
period_id BIGINT NOT NULL,
|
||||
PRIMARY KEY(token)
|
||||
) ENGINE = INNODB;
|
||||
|
||||
|
||||
# Create FKs
|
||||
ALTER TABLE `GROUP`
|
||||
|
@ -127,6 +119,10 @@ REFERENCES TUTORSHIP (id)
|
|||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
;
|
||||
|
||||
ALTER TABLE LIVRET
|
||||
ADD FOREIGN KEY (etutor_id)
|
||||
REFERENCES `USER` (id);
|
||||
|
||||
ALTER TABLE LIVRET
|
||||
ADD FOREIGN KEY (tutorship_id)
|
||||
|
@ -140,14 +136,10 @@ ALTER TABLE PERIOD
|
|||
REFERENCES LIVRET (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE HASHTABLE
|
||||
ADD FOREIGN KEY (period_id)
|
||||
REFERENCES PERIOD (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
;
|
||||
|
||||
|
||||
# Create Indexes
|
||||
CREATE INDEX tutor_email ON LIVRET(etutor_email);
|
||||
CREATE INDEX user_login ON `USER`(`login`);
|
||||
CREATE UNIQUE INDEX user_email
|
||||
ON `USER` (`email`);
|
||||
CREATE UNIQUE INDEX user_hash
|
||||
ON `USER` (`hash`);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
USE OLA;
|
||||
INSERT INTO SETTINGS VALUES ('BASE_DIRECTORY', '/OLA_RESSOURCES/', 'Répertoire base pour le dépot des fichiers');
|
||||
INSERT INTO SETTINGS VALUES ('TEMPLATES_DIRECTORY', '/OLA_TEMPLATES/', 'Répertoire base pour le dépot des fichiers');
|
||||
INSERT INTO SETTINGS VALUES ('OLA_URL', 'ola.univ-tlse2.fr/', 'URL de l application');
|
||||
|
||||
INSERT INTO `USER` VALUES (1, '1', 'sec@univ-tlse2.fr', 'Secrétaire', DEFAULT, 'aZeRtYuIoP', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (2, '4', 'etu1@univ-tlse2.fr', 'Etudiant 1', DEFAULT, 'qSdFgHjKlM', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (3, '4', 'etu2@univ-tlse2.fr', 'Etudiant 2', DEFAULT, 'wXcVbN', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (4, '4', 'etu3@univ-tlse2.fr', 'Etudiant 3', DEFAULT, 'pOiUyTrEzA', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (5, '2-3', 'resp@univ-tlse2.fr', 'Responsable', DEFAULT, 'mLkJhGfDsQ', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (6, '3', 'tut@univ-tlse2.fr', 'Tuteur Pédagogique', DEFAULT, 'nBvCxW', '01.23.45.67.89');
|
||||
|
||||
INSERT INTO `GROUP` VALUES
|
||||
(1, 'M2_ICE_2016-2017_TEST', '2017', 'Master2 ICE', 'Master 2 Informatique Collaborative en Entreprise',
|
||||
'Sciences du chômage proffessionnel', 5, 1,
|
||||
'/home/dan/PycharmProjects/OLA/backend/app/OLA_RESSOURCES/M2_ICE_2016-2017_TEST');
|
||||
INSERT INTO `GROUP` VALUES
|
||||
(2, 'M1_ICE_2016-2017_TEST', '2017', 'Master1 ICE', 'Master 1 Informatique Collaborative en Entreprise',
|
||||
'Sciences du chômage proffessionnel', 5, 1,
|
||||
'/home/dan/PycharmProjects/OLA/backend/app/OLA_RESSOURCES/M1_ICE_2016-2017_TEST');
|
||||
|
||||
INSERT INTO TUTORSHIP VALUES (DEFAULT, 1, 5, 2);
|
||||
INSERT INTO TUTORSHIP VALUES (DEFAULT, 2, 5, 4);
|
||||
INSERT INTO TUTORSHIP VALUES (DEFAULT, 1, 6, 3);
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
USE OLA;
|
||||
INSERT INTO SETTINGS VALUES ('URL_BASE_DIRECTORY', '/OLA_RESSOURCES/', 'Répertoire base pour le dépot des fichiers');
|
||||
INSERT INTO SETTINGS VALUES ('OLA_URL', 'ola.univ-tlse2.fr/', 'URL de l application');
|
||||
|
||||
INSERT INTO `USER` VALUES (1, 'sec', '1', 'sec@univ-tlse2.fr', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (2, 'etu1', '4', 'etu1@univ-tlse2.fr', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (3, 'etu2', '4', 'etu2@univ-tlse2.fr', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (4, 'etu3', '4', 'etu3@univ-tlse2.fr', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (5, 'resp', '2-3', 'resp@univ-tlse2.fr', '01.23.45.67.89');
|
||||
INSERT INTO `USER` VALUES (6, 'tut', '3', 'tut@univ-tlse2.fr', '01.23.45.67.89');
|
||||
|
||||
INSERT INTO `GROUP` VALUES (1, 'M2_ICE_2016-2017_TEST', '2017', 'Master2 ICE', 'Master 2 Informatique Collaborative en Entreprise', 'Sciences du chômage proffessionnel', 5, 1, '/home/dan/PycharmProjects/OLA/backend/app/OLA_RESSOURCES/M2_ICE_2016-2017_TEST');
|
||||
INSERT INTO `GROUP` VALUES (2, 'M1_ICE_2016-2017_TEST', '2017', 'Master1 ICE', 'Master 1 Informatique Collaborative en Entreprise', 'Sciences du chômage proffessionnel', 5, 1, '/home/dan/PycharmProjects/OLA/backend/app/OLA_RESSOURCES/M1_ICE_2016-2017_TEST');
|
||||
|
||||
INSERT INTO TUTORSHIP VALUES (DEFAULT, 1, 2, 5);
|
||||
INSERT INTO TUTORSHIP VALUES (DEFAULT, 2, 4, 5);
|
||||
INSERT INTO TUTORSHIP VALUES (DEFAULT, 1, 3, 6);
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
import os
|
||||
|
||||
from flask_restful import Resource, request
|
||||
|
||||
from app.api import mailsModels
|
||||
from app.model import *
|
||||
from app.utils import *
|
||||
|
||||
|
||||
class GroupAPI(Resource):
|
||||
"""
|
||||
Group Api Resource
|
||||
"""
|
||||
|
||||
def post(self):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['name', 'year', 'class_short', 'class_long', 'department', 'resp_id', 'sec_id'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
name = args['name']
|
||||
year = args['year']
|
||||
class_short = args['class_short']
|
||||
class_long = args['class_long']
|
||||
department = args['department']
|
||||
resp_id = args['resp_id']
|
||||
sec_id = args['sec_id']
|
||||
res_dir = getParam('BASE_DIRECTORY') + name + "/"
|
||||
mails = []
|
||||
|
||||
group = getGroup(name=name)
|
||||
if group is not None:
|
||||
return {"GID": group["id"]}, 200
|
||||
|
||||
user = getUser(uid=resp_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(resp_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_RESP_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("RESP_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "2" not in user['role'].split('-'):
|
||||
role = user['role'] + "-2"
|
||||
query = USER.update().values(role=role).where(USER.c.id == resp_id)
|
||||
query.execute()
|
||||
|
||||
user = getUser(uid=sec_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(sec_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_SEC_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("SEC_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "1" not in user['role'].split('-'):
|
||||
role = user['role'] + "-1"
|
||||
query = USER.update().values(role=role).where(USER.c.id == sec_id)
|
||||
query.execute()
|
||||
|
||||
query = GROUP.insert().values(name=name, year=year, class_short=class_short, class_long=class_long,
|
||||
department=department, resp_id=resp_id, sec_id=sec_id, ressources_dir=res_dir)
|
||||
res = query.execute()
|
||||
os.mkdir(res_dir)
|
||||
|
||||
for m in mails:
|
||||
addr = m[0]
|
||||
mail = m[1]
|
||||
send_mail(mail[0], addr, mail[1])
|
||||
|
||||
return {"GID": res.lastrowid}, 201
|
||||
|
||||
def put(self, gid):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['name', 'year', 'class_short', 'class_long', 'department', 'resp_id', 'sec_id'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
name = args['name'].replace(" ", "_").replace("/", "-")
|
||||
year = args['year']
|
||||
class_short = args['class_short']
|
||||
class_long = args['class_long']
|
||||
department = args['department']
|
||||
resp_id = args['resp_id']
|
||||
sec_id = args['sec_id']
|
||||
res_dir = getParam('BASE_DIRECTORY') + name + "/"
|
||||
mails = []
|
||||
|
||||
group = getGroup(gid=gid)
|
||||
if group is None:
|
||||
return {"ERROR": "This group does not exists !"}, 405
|
||||
|
||||
group2 = getGroup(name=name)
|
||||
if group2 is not None:
|
||||
return {"ERROR": "A group with this name already exists !"}, 405
|
||||
|
||||
user = getUser(uid=resp_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(resp_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_RESP_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("RESP_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "2" not in user['role'].split('-'):
|
||||
role = user['role'] + "-2"
|
||||
query = USER.update().values(role=role).where(USER.c.id == resp_id)
|
||||
query.execute()
|
||||
|
||||
user = getUser(uid=sec_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(sec_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_SEC_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("SEC_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "1" not in user['role'].split('-'):
|
||||
role = user['role'] + "-1"
|
||||
query = USER.update().values(role=role).where(USER.c.id == sec_id)
|
||||
query.execute()
|
||||
|
||||
query = GROUP.update().values(name=name, year=year, class_short=class_short, class_long=class_long,
|
||||
department=department, resp_id=resp_id, sec_id=sec_id, ressources_dir=res_dir) \
|
||||
.where(GROUP.c.id == gid)
|
||||
query.execute()
|
||||
|
||||
if group["ressources_dir"] != res_dir:
|
||||
os.rename(group["ressources_dir"], res_dir)
|
||||
|
||||
for m in mails:
|
||||
addr = m[0]
|
||||
mail = m[1]
|
||||
send_mail(mail[0], addr, mail[1])
|
||||
|
||||
return {"GID": gid}, 200
|
||||
|
||||
def get(self, gid=0, name=""):
|
||||
if gid > 0:
|
||||
return {'GROUP': getGroup(gid=gid)}, 200
|
||||
elif name != "":
|
||||
return {'GROUP': getGroup(name=name)}, 200
|
||||
|
||||
def options(self, gid):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['pairs'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
pairs = args["pairs"]
|
||||
|
||||
group = getGroup(gid=gid)
|
||||
if group is None:
|
||||
return {"ERROR": "This group does not exists !"}, 405
|
||||
|
||||
for p in pairs:
|
||||
try:
|
||||
stud = getUser(uid=p[0])
|
||||
if stud is None:
|
||||
return {"ERROR": "The user with id " + str(p[0]) + " does not exists !"}, 400
|
||||
elif stud['role'] != "4":
|
||||
return {"ERROR": "A student must have the 'student' role !"}, 400
|
||||
|
||||
tutor = getUser(uid=p[1])
|
||||
if tutor is None:
|
||||
return {"ERROR": "The user with id " + str(p[1]) + " does not exists !"}, 400
|
||||
elif tutor['role'] == "4":
|
||||
return {"ERROR": "A student can't be a tutor !"}, 400
|
||||
elif "3" not in tutor['role'].split('-'):
|
||||
role = tutor['role'] + "-3"
|
||||
query = USER.update().values(role=role).where(USER.c.id == p[1])
|
||||
query.execute()
|
||||
except IndexError:
|
||||
return {"ERROR": "Pairs are incorrectly formed !"}, 409
|
||||
|
||||
query = TUTORSHIP.insert().values(group_id=gid, student_id=p[0], ptutor_id=p[1])
|
||||
query.execute()
|
||||
|
||||
query = USER.select(USER.c.id == stud["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_STUD_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("STUD_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
send_mail(mail[0], stud["email"], mail[1])
|
||||
|
||||
return {"RESULT": "Pairs added successfully"}, 200
|
|
@ -0,0 +1,221 @@
|
|||
import os
|
||||
|
||||
from flask_restful import Resource, request
|
||||
|
||||
from app.api import mailsModels
|
||||
from app.model import *
|
||||
from app.utils import *
|
||||
|
||||
|
||||
class LivretAPI(Resource):
|
||||
"""
|
||||
Livret Api Resource
|
||||
"""
|
||||
|
||||
def post(self):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['name', 'year', 'class_short', 'class_long', 'department', 'resp_id', 'sec_id'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
name = args['name']
|
||||
year = args['year']
|
||||
class_short = args['class_short']
|
||||
class_long = args['class_long']
|
||||
department = args['department']
|
||||
resp_id = args['resp_id']
|
||||
sec_id = args['sec_id']
|
||||
res_dir = getParam('BASE_DIRECTORY') + name + "/"
|
||||
mails = []
|
||||
|
||||
group = getGroup(name=name)
|
||||
if group is not None:
|
||||
return {"GID": group["id"]}, 200
|
||||
|
||||
user = getUser(uid=resp_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(resp_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_RESP_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("RESP_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "2" not in user['role'].split('-'):
|
||||
role = user['role'] + "-2"
|
||||
query = USER.update().values(role=role).where(USER.c.id == resp_id)
|
||||
query.execute()
|
||||
|
||||
user = getUser(uid=sec_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(sec_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_SEC_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("SEC_OF_GROUP", {"GROUP": name,
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "1" not in user['role'].split('-'):
|
||||
role = user['role'] + "-1"
|
||||
query = USER.update().values(role=role).where(USER.c.id == sec_id)
|
||||
query.execute()
|
||||
|
||||
query = GROUP.insert().values(name=name, year=year, class_short=class_short, class_long=class_long,
|
||||
department=department, resp_id=resp_id, sec_id=sec_id, ressources_dir=res_dir)
|
||||
res = query.execute()
|
||||
os.mkdir(res_dir)
|
||||
|
||||
for m in mails:
|
||||
addr = m[0]
|
||||
mail = m[1]
|
||||
send_mail(mail[0], addr, mail[1])
|
||||
|
||||
return {"GID": res.lastrowid}, 201
|
||||
|
||||
def put(self, gid):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['name', 'year', 'class_short', 'class_long', 'department', 'resp_id', 'sec_id'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
name = args['name'].replace(" ", "_").replace("/", "-")
|
||||
year = args['year']
|
||||
class_short = args['class_short']
|
||||
class_long = args['class_long']
|
||||
department = args['department']
|
||||
resp_id = args['resp_id']
|
||||
sec_id = args['sec_id']
|
||||
res_dir = getParam('BASE_DIRECTORY') + name + "/"
|
||||
mails = []
|
||||
|
||||
group = getGroup(gid=gid)
|
||||
if group is None:
|
||||
return {"ERROR": "This group does not exists !"}, 405
|
||||
|
||||
group2 = getGroup(name=name)
|
||||
if group2 is not None:
|
||||
return {"ERROR": "A group with this name already exists !"}, 405
|
||||
|
||||
user = getUser(uid=resp_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(resp_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_RESP_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("RESP_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "2" not in user['role'].split('-'):
|
||||
role = user['role'] + "-2"
|
||||
query = USER.update().values(role=role).where(USER.c.id == resp_id)
|
||||
query.execute()
|
||||
|
||||
user = getUser(uid=sec_id)
|
||||
if user is None:
|
||||
return {"ERROR": "The user with id " + str(sec_id) + " does not exists !"}, 400
|
||||
else:
|
||||
query = USER.select(USER.c.id == user["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_SEC_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("SEC_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
mails.append((user["email"], mail))
|
||||
if "1" not in user['role'].split('-'):
|
||||
role = user['role'] + "-1"
|
||||
query = USER.update().values(role=role).where(USER.c.id == sec_id)
|
||||
query.execute()
|
||||
|
||||
query = GROUP.update().values(name=name, year=year, class_short=class_short, class_long=class_long,
|
||||
department=department, resp_id=resp_id, sec_id=sec_id, ressources_dir=res_dir) \
|
||||
.where(GROUP.c.id == gid)
|
||||
query.execute()
|
||||
|
||||
if group["ressources_dir"] != res_dir:
|
||||
os.rename(group["ressources_dir"], res_dir)
|
||||
|
||||
for m in mails:
|
||||
addr = m[0]
|
||||
mail = m[1]
|
||||
send_mail(mail[0], addr, mail[1])
|
||||
|
||||
return {"GID": gid}, 200
|
||||
|
||||
def get(self, gid=0, name=""):
|
||||
if gid > 0:
|
||||
return {'GROUP': getGroup(gid=gid)}, 200
|
||||
elif name != "":
|
||||
return {'GROUP': getGroup(name=name)}, 200
|
||||
|
||||
def options(self, gid):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['pairs'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
pairs = args["pairs"]
|
||||
|
||||
group = getGroup(gid=gid)
|
||||
if group is None:
|
||||
return {"ERROR": "This group does not exists !"}, 405
|
||||
|
||||
for p in pairs:
|
||||
try:
|
||||
stud = getUser(uid=p[0])
|
||||
if stud is None:
|
||||
return {"ERROR": "The user with id " + str(p[0]) + " does not exists !"}, 400
|
||||
elif stud['role'] != "4":
|
||||
return {"ERROR": "A student must have the 'student' role !"}, 400
|
||||
|
||||
tutor = getUser(uid=p[1])
|
||||
if tutor is None:
|
||||
return {"ERROR": "The user with id " + str(p[1]) + " does not exists !"}, 400
|
||||
elif tutor['role'] == "4":
|
||||
return {"ERROR": "A student can't be a tutor !"}, 400
|
||||
elif "3" not in tutor['role'].split('-'):
|
||||
role = tutor['role'] + "-3"
|
||||
query = USER.update().values(role=role).where(USER.c.id == p[1])
|
||||
query.execute()
|
||||
except IndexError:
|
||||
return {"ERROR": "Pairs are incorrectly formed !"}, 409
|
||||
|
||||
query = TUTORSHIP.insert().values(group_id=gid, student_id=p[0], ptutor_id=p[1])
|
||||
query.execute()
|
||||
|
||||
query = USER.select(USER.c.id == stud["id"])
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res.hash is not None and len(res.hash) > 0:
|
||||
mail = mailsModels.getMailContent("NEW_STUD_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL') + "registration/"
|
||||
+ res.hash})
|
||||
else:
|
||||
mail = mailsModels.getMailContent("STUD_OF_GROUP", {"GROUP": group["name"],
|
||||
"URL": getParam('OLA_URL')})
|
||||
|
||||
send_mail(mail[0], stud["email"], mail[1])
|
||||
|
||||
return {"RESULT": "Pairs added successfully"}, 200
|
|
@ -0,0 +1,51 @@
|
|||
from hashlib import sha256
|
||||
|
||||
from flask import session, request
|
||||
from flask_restful import Resource
|
||||
|
||||
from app.core import app
|
||||
from app.model import USER, getUser
|
||||
from app.utils import checkParams
|
||||
|
||||
|
||||
class LoginAPI(Resource):
|
||||
"""
|
||||
Login Api Resource
|
||||
"""
|
||||
|
||||
def post(self):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['email', 'password'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
email = args['email']
|
||||
psw = args['password']
|
||||
password = sha256(psw.encode('utf-8')).hexdigest()
|
||||
|
||||
if "user" in session and session["user"] is not None:
|
||||
return {'AUTH_RESULT': 'ALREADY_LOGGED'}, 201
|
||||
|
||||
query = USER.select(USER.c.email == email)
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
|
||||
if app.config['TESTING']:
|
||||
if res is not None and psw == email:
|
||||
user = getUser(uid=res.id)
|
||||
session['user'] = user
|
||||
return {'AUTH_RESULT': 'OK'}, 200
|
||||
else:
|
||||
session['user'] = None
|
||||
return {'AUTH_RESULT': 'AUTHENTICATION_FAILED'}, 401
|
||||
else:
|
||||
if res is not None and password != "" and password == res.psw:
|
||||
user = getUser(uid=res.id)
|
||||
session['user'] = user
|
||||
return {'AUTH_RESULT': 'OK'}, 200
|
||||
else:
|
||||
session['user'] = None
|
||||
return {'AUTH_RESULT': 'AUTHENTICATION_FAILED'}, 401
|
||||
|
||||
def delete(self):
|
||||
session['user'] = None
|
||||
return {'AUTH_RESULT': 'OK'}, 200
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import os
|
||||
|
||||
from flask import request
|
||||
from flask_restful import Resource
|
||||
from flask_restful.reqparse import RequestParser
|
||||
from model import getParam
|
||||
|
||||
from app.model import getGroup
|
||||
from app.tools.LibPdf import delete_file, upload_file, allowed_file
|
||||
|
||||
|
||||
class PdfAPI(Resource):
|
||||
"""
|
||||
Pdf Api Resource
|
||||
"""
|
||||
|
||||
def delete(self):
|
||||
parser = RequestParser()
|
||||
parser.add_argument('templateName', required=True, help="Template name is required !")
|
||||
args = parser.parse_args()
|
||||
|
||||
if ".." in args:
|
||||
return {"msg": ".. not allowed in path"}, 400
|
||||
|
||||
delete_file(os.path.join(getParam('TEMPLATES_DIRECTORY'), args['templateName']))
|
||||
|
||||
def post(self):
|
||||
"""
|
||||
Upload d'un template
|
||||
:return:
|
||||
"""
|
||||
parser = RequestParser()
|
||||
parser.add_argument('groupeName', required=True, help="id/name groupe cannot be blank!")
|
||||
args = parser.parse_args()
|
||||
|
||||
group = getGroup(args['groupe'])
|
||||
file = request.files['file']
|
||||
|
||||
if file.filename == '':
|
||||
return {"message": "Fichier non trouve"}, 400
|
||||
|
||||
if file and allowed_file(file.filename):
|
||||
upload_file(file, group["ressources_dir"])
|
|
@ -1,7 +1,9 @@
|
|||
from hashlib import sha256
|
||||
|
||||
from flask_restful import Resource, request
|
||||
|
||||
from app.model import *
|
||||
from app.utils import checkParams
|
||||
from app.utils import checkParams, get_random_string
|
||||
|
||||
|
||||
class UserAPI(Resource):
|
||||
|
@ -11,45 +13,60 @@ class UserAPI(Resource):
|
|||
|
||||
def post(self):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['CASid', 'role'], args):
|
||||
if not checkParams(['role', 'email', 'name'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
CASid = args['CASid']
|
||||
role = args['role']
|
||||
email = self.getEmailFromCAS(CASid)
|
||||
email = args['email']
|
||||
name = args['name']
|
||||
phone = None
|
||||
user = getUser(login=CASid)
|
||||
user = getUser(email=email)
|
||||
hashpass = get_random_string()
|
||||
while hashExists(hashpass):
|
||||
hashpass = get_random_string()
|
||||
|
||||
if user is not None:
|
||||
return {"UID": user["id"]}, 200
|
||||
|
||||
query = USER.insert().values(login=CASid, email=email, role=role, phone=phone)
|
||||
query = USER.insert().values(email=email, role=role, phone=phone, name=name, hash=hashpass)
|
||||
res = query.execute()
|
||||
return {"UID": res.lastrowid}, 201
|
||||
|
||||
def put(self, uid):
|
||||
args = request.get_json(cache=False, force=True)
|
||||
if not checkParams(['CASid', 'role', 'email', 'phone'], args):
|
||||
if not checkParams(['role', 'email', 'phone', 'name', 'password', 'firstname'], args):
|
||||
return {"ERROR": "One or more parameters are missing !"}, 400
|
||||
|
||||
role = args['role']
|
||||
email = args['email']
|
||||
phone = args['phone']
|
||||
firstname = args['firstname']
|
||||
name = args['name']
|
||||
psw = args['password']
|
||||
|
||||
name = firstname.title() + " " + name.upper()
|
||||
# TODO : Lors de l'ajout des fiches d'absence ca sera ça le critère de recherche + le groupe
|
||||
|
||||
if psw is None or len(psw) < 8:
|
||||
return {"ERROR": "Password can't be empty or less than 8 characters !"}, 400
|
||||
|
||||
password = sha256(psw.encode('utf-8')).hexdigest()
|
||||
|
||||
if getUser(uid=uid) is None:
|
||||
return {"ERROR": "This user doesn't exists !"}, 405
|
||||
|
||||
CASid = args['CASid']
|
||||
role = args['role']
|
||||
email = args['email']
|
||||
phone = args['phone']
|
||||
query = USER.update().values(login=CASid, email=email, role=role, phone=phone).where(USER.c.id == uid)
|
||||
if getUser(email=email) is not None:
|
||||
return {"ERROR": "A user with this email already exists !"}, 405
|
||||
|
||||
query = USER.update().values(email=email, role=role, phone=phone, name=name, psw=password, hash=None) \
|
||||
.where(USER.c.id == uid)
|
||||
query.execute()
|
||||
return {"UID": uid}, 200
|
||||
|
||||
def get(self, uid=0, login="", email=""):
|
||||
def get(self, uid=0, email="", hashcode=""):
|
||||
if uid > 0:
|
||||
return {'USER': getUser(uid=uid)}, 200
|
||||
elif login != "":
|
||||
return {'USER': getUser(login=login)}, 200
|
||||
elif email != "":
|
||||
return {'USER': getUser(email=email)}, 200
|
||||
|
||||
@staticmethod
|
||||
def getEmailFromCAS(CASid):
|
||||
return ""
|
||||
elif hashcode != "":
|
||||
return {'USER': getUser(hashcode=hashcode)}, 200
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from flask import session
|
||||
from flask_restful import Resource
|
||||
|
||||
from app.model import *
|
||||
|
||||
|
||||
class UserInfoAPI(Resource):
|
||||
"""
|
||||
|
@ -8,5 +10,24 @@ class UserInfoAPI(Resource):
|
|||
"""
|
||||
|
||||
def get(self):
|
||||
user = session["user"]
|
||||
user = session.get("user", None)
|
||||
return {'USER': user}, 200
|
||||
|
||||
|
||||
class UserGroupsAPI(Resource):
|
||||
"""
|
||||
UserGroups Api Resource
|
||||
"""
|
||||
|
||||
def get(self):
|
||||
user = session.get("user", None)
|
||||
if user is not None:
|
||||
subquery = LIVRET.select().distinct().with_only_columns([LIVRET.c.tutorship_id])
|
||||
query = TUTORSHIP.select(
|
||||
and_(TUTORSHIP.c.student_id == user["id"], TUTORSHIP.c.id.notin_(subquery))).distinct()
|
||||
res = query.execute()
|
||||
liste = []
|
||||
for r in res:
|
||||
liste.append(r.group_id)
|
||||
|
||||
return {'GROUP_LIST': liste}, 200
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
from flask import session
|
||||
from flask_restful import Resource
|
||||
from flask_restful.reqparse import RequestParser
|
||||
|
||||
from app.model import *
|
||||
from app.core import cas
|
||||
|
||||
class LoginAPI(Resource):
|
||||
"""
|
||||
Login Api Resource
|
||||
"""
|
||||
|
||||
def get(self):
|
||||
if "user" in session and session["user"] is not None:
|
||||
return {'AUTH_RESULT': 'ALREADY_LOGGED'}, 201
|
||||
userInfo = self.getUserInfoFromCAS()
|
||||
|
||||
if userInfo is not None:
|
||||
user = getUser(login=userInfo['login'])
|
||||
if user is not None and isUserAllowed(user["id"]):
|
||||
session['user'] = user
|
||||
return {'AUTH_RESULT': 'OK'}, 200
|
||||
else:
|
||||
session['user'] = None
|
||||
return {'AUTH_RESULT': 'NOT_ALLOWED'}, 403
|
||||
else:
|
||||
session['user'] = None
|
||||
return {'AUTH_RESULT': 'AUTHENTICATION_FAILED'}, 401
|
||||
|
||||
def delete(self):
|
||||
session['user'] = None
|
||||
return {'AUTH_RESULT': 'OK'}, 200
|
||||
|
||||
def getUserInfoFromCAS(self):
|
||||
if cas.username is not None:
|
||||
return {"login": cas.username}
|
||||
else:
|
||||
return None
|
|
@ -0,0 +1,53 @@
|
|||
_NEW_STUD_OF_GROUP = ("Votre compte OLA a été créé !", "Bonjour,<br/><p>Votre compte vient d'être créé dans l'Outil du "
|
||||
"Livret de l'Alternant dans le groupe <b>#GROUPE</b>. Vous pouvez dès "
|
||||
"maintenant l'activer, puis créer un livret en vous rendant à l'adresse : <br/>"
|
||||
"<a href='#URL'>#URL</a></p><p>Bonne journée !</p>")
|
||||
|
||||
_STUD_OF_GROUP = (
|
||||
"Vous avez été ajouté à un groupe OLA !", "Bonjour,<br/><p>Votre compte vient d'être ajouté dans l'Outil du "
|
||||
"Livret de l'Alternant au groupe <b>#GROUPE</b>. Vous pouvez dès "
|
||||
"maintenant créer un livret en vous rendant à l'adresse : <br/>"
|
||||
"<a href='#URL'>#URL</a></p><p>Bonne journée !</p>")
|
||||
|
||||
_NEW_RESP_OF_GROUP = ("Votre compte OLA a été créé !", "Bonjour,<br/><p>Votre compte vient d'être créé dans l'Outil du "
|
||||
"Livret de l'Alternant en tant que responsable du groupe <b>#GROUPE</b>. Vous pouvez dès "
|
||||
"maintenant l'activer, en vous rendant à l'adresse : <br/>"
|
||||
"<a href='#URL'>#URL</a></p><p>Bonne journée !</p>")
|
||||
|
||||
_RESP_OF_GROUP = (
|
||||
"Vous avez été ajouté à un groupe OLA !", "Bonjour,<br/><p>Votre compte vient d'être ajouté dans l'Outil du "
|
||||
"Livret de l'Alternant en tant que responsable du groupe <b>#GROUPE</b>. Vous pouvez dès "
|
||||
"maintenant y accéder en vous rendant à l'adresse : <br/>"
|
||||
"<a href='#URL'>#URL</a></p><p>Bonne journée !</p>")
|
||||
|
||||
_NEW_SEC_OF_GROUP = ("Votre compte OLA a été créé !", "Bonjour,<br/><p>Votre compte vient d'être créé dans l'Outil du "
|
||||
"Livret de l'Alternant en tant que secrétaire du groupe <b>#GROUPE</b>. Vous pouvez dès "
|
||||
"maintenant l'activer, en vous rendant à l'adresse : <br/>"
|
||||
"<a href='#URL'>#URL</a></p><p>Bonne journée !</p>")
|
||||
|
||||
_SEC_OF_GROUP = (
|
||||
"Vous avez été ajouté à un groupe OLA !", "Bonjour,<br/><p>Votre compte vient d'être ajouté dans l'Outil du "
|
||||
"Livret de l'Alternant en tant que secrétaire du groupe <b>#GROUPE</b>. Vous pouvez dès "
|
||||
"maintenant y accéder en vous rendant à l'adresse : <br/>"
|
||||
"<a href='#URL'>#URL</a></p><p>Bonne journée !</p>")
|
||||
|
||||
|
||||
def getMailContent(mail_type, args):
|
||||
if mail_type == "NEW_STUD_OF_GROUP":
|
||||
mail = _NEW_STUD_OF_GROUP
|
||||
elif mail_type == "STUD_OF_GROUP":
|
||||
mail = _STUD_OF_GROUP
|
||||
elif mail_type == "NEW_RESP_OF_GROUP":
|
||||
mail = _NEW_RESP_OF_GROUP
|
||||
elif mail_type == "RESP_OF_GROUP":
|
||||
mail = _RESP_OF_GROUP
|
||||
elif mail_type == "NEW_SEC_OF_GROUP":
|
||||
mail = _NEW_SEC_OF_GROUP
|
||||
elif mail_type == "SEC_OF_GROUP":
|
||||
mail = _SEC_OF_GROUP
|
||||
else:
|
||||
raise Exception("Unknown mail type !")
|
||||
|
||||
for key, value in args.items():
|
||||
mail[1].replace("#" + key, value)
|
||||
return mail
|
|
@ -31,6 +31,7 @@ class Config:
|
|||
CAS_LOGIN_ROUTE = "/login"
|
||||
CAS_LOGOUT_ROUTE = "/logout"
|
||||
CAS_VALIDATE_ROUTE = "/serviceValidate"
|
||||
MAILER = True
|
||||
|
||||
|
||||
|
||||
|
@ -49,3 +50,4 @@ class Test(Config):
|
|||
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
SQLALCHEMY_DATABASE_URI = \
|
||||
'sqlite:///' + os.path.join(BASE_DIR, '../test.db')
|
||||
MAILER = False
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import importlib
|
||||
from datetime import timedelta
|
||||
|
||||
from flask_cas import CAS
|
||||
|
||||
from app.config import Config
|
||||
from flask import Flask, session, redirect
|
||||
from flask_cas import CAS
|
||||
from flask_restful import Api
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy import MetaData
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
from app.config import Config
|
||||
|
||||
# initialization Flask
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config.ACTIVE_CONFIG)
|
||||
|
@ -20,6 +20,7 @@ app.permanent_session_lifetime = \
|
|||
minutes=app.config['SESSION_VALIDITY_DURATION_WITHOUT_ACTIVITY_MIN']
|
||||
)
|
||||
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
session.modified = True
|
||||
|
@ -37,6 +38,7 @@ api = Api(app)
|
|||
# Cas Flask
|
||||
cas = CAS(app)
|
||||
|
||||
|
||||
@app.route('/redirect')
|
||||
def after_login():
|
||||
return redirect("/api/login")
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
from sqlalchemy import Table
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy import and_
|
||||
|
||||
from app.core import meta, db, Base
|
||||
from app.core import meta, Base
|
||||
|
||||
USER = Table('USER', meta, autoload=False)
|
||||
SETTINGS = Table('SETTINGS', meta, autoload=False)
|
||||
HASHTABLE = Table('HASHTABLE', meta, autoload=False)
|
||||
GROUP = Table('GROUP', meta, autoload=False)
|
||||
TUTORSHIP = Table('TUTORSHIP', meta, autoload=False)
|
||||
PERIOD = Table('PERIOD', meta, autoload=False)
|
||||
|
@ -13,24 +12,26 @@ LIVRET = Table('LIVRET', meta, autoload=False)
|
|||
|
||||
user_class = Base.classes.USER
|
||||
settings_class = Base.classes.SETTINGS
|
||||
hashtable_class = Base.classes.HASHTABLE
|
||||
group_class = Base.classes.GROUP
|
||||
tutorship_class = Base.classes.TUTORSHIP
|
||||
period_class = Base.classes.PERIOD
|
||||
livret_class = Base.classes.LIVRET
|
||||
|
||||
|
||||
def getUser(uid=0, login="", email=""):
|
||||
def getParam(key):
|
||||
query = SETTINGS.select(SETTINGS.c.key == key)
|
||||
rows = query.execute()
|
||||
return rows.first().value
|
||||
|
||||
|
||||
def getUser(uid=0, email="", hashcode=""):
|
||||
res = None
|
||||
|
||||
if uid == 0 and login == "" and email == "":
|
||||
if uid == 0 and email == "" and hashcode == "":
|
||||
raise Exception("getUser must be called with one argument !")
|
||||
else:
|
||||
if uid != 0:
|
||||
res = db.session.query(user_class).get(uid)
|
||||
|
||||
elif login != "":
|
||||
query = USER.select(USER.c.login == login)
|
||||
query = USER.select(USER.c.id == uid)
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
|
||||
|
@ -39,14 +40,54 @@ def getUser(uid=0, login="", email=""):
|
|||
rows = query.execute()
|
||||
res = rows.first()
|
||||
|
||||
elif hashcode != "":
|
||||
query = USER.select(USER.c.hash == hashcode)
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
|
||||
if res is not None:
|
||||
return {"id": res.id, "login": res.login, "email": res.email, "role": res.role, "phone": res.phone}
|
||||
return {"id": res.id, "email": res.email, "role": res.role, "phone": res.phone, "name": res.name}
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def isUserAllowed(uid):
|
||||
query = db.session.query(group_class, tutorship_class).join(tutorship_class) \
|
||||
.filter(or_(tutorship_class.student_id == uid, group_class.resp_id == uid))
|
||||
res = query.all()
|
||||
return res is not None and len(res) > 0
|
||||
def getGroup(gid=0, name=""):
|
||||
res = None
|
||||
|
||||
if gid == 0 and name == "":
|
||||
raise Exception("getGroup must be called with one argument !")
|
||||
else:
|
||||
if gid != 0:
|
||||
query = GROUP.select(GROUP.c.id == gid)
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
|
||||
elif name != "":
|
||||
query = GROUP.select(GROUP.c.name == name)
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
|
||||
if res is not None:
|
||||
return {"id": res.id, "name": res.name, "year": res.year, "class_short": res.class_short,
|
||||
"class_long": res.class_long, "department": res.department, "resp_id": getUser(uid=res.resp_id),
|
||||
"sec_id": getUser(uid=res.sec_id), "ressources_dir": res.ressources_dir}
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def getTutorshipForStudent(gid, student):
|
||||
query = TUTORSHIP.select(and_(TUTORSHIP.c.group_id == gid, TUTORSHIP.c.student_id == student))
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
if res is not None:
|
||||
return {"id": res.id, "group_id": getGroup(gid=res.group_id), "student_id": getUser(uid=res.student_id),
|
||||
"ptutor_id": getUser(uid=res.ptutor_id)}
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def hashExists(test):
|
||||
query = USER.select(USER.c.hash == test)
|
||||
rows = query.execute()
|
||||
res = rows.first()
|
||||
return res is not None
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import os
|
||||
from PyPDF2 import PdfFileReader, PdfFileMerger
|
||||
|
||||
|
||||
def fusion_fichiers(chemin_merge_pdf, nom_merge_pdf, liste_de_pdf):
|
||||
"""
|
||||
Permet de fusionner un ensemble de pdf
|
||||
:param chemin_merge_pdf: chemin ou l'on souhaite fusioner l'ensemble des pdf
|
||||
:param nom_merge_pdf: nom que l'on souhaite donner au fichier pdf final
|
||||
:param liste_de_pdf: liste de pdf avec leur chemin inclu
|
||||
:return:
|
||||
"""
|
||||
merger = PdfFileMerger()
|
||||
for filename in liste_de_pdf:
|
||||
merger.append(PdfFileReader(os.path.join(filename), "rb"))
|
||||
merger.write(os.path.join(chemin_merge_pdf, nom_merge_pdf))
|
||||
|
||||
|
||||
def get_pdf_from_directory(chemin_des_pdf):
|
||||
"""
|
||||
Permet de récuperer l'ensemble des pdf d'un chemin
|
||||
:param chemin_des_pdf:
|
||||
:return:
|
||||
"""
|
||||
return [f for f in os.listdir(chemin_des_pdf) if f.endswith("pdf")]
|
|
@ -1,25 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
""" Script python qui remplie les pdf basés sur un template jinja. """
|
||||
|
||||
import os
|
||||
|
||||
from pdfjinja import PdfJinja
|
||||
|
||||
|
||||
def remplir_template(dirname_template, pdf_template, dirname_output_file, pdf_output, dictionnaire):
|
||||
"""
|
||||
Fonction qui permet de remplir un pdf template
|
||||
:param dirname_template: chemin du fichier de template
|
||||
:param pdf_template: nom du fichier de template
|
||||
:param dirname_output_file: chemin des pdf généré
|
||||
:param pdf_output: nom du fichier pdf à générer
|
||||
:param dictionnaire: dictionnaire contenant le nom des textfields des pdf ainsi que leurs valeurs
|
||||
:return:
|
||||
"""
|
||||
template_pdf_file = os.path.join(dirname_template, pdf_template)
|
||||
template_pdf = PdfJinja(template_pdf_file)
|
||||
|
||||
rendered_pdf = template_pdf(dictionnaire)
|
||||
|
||||
output_file = os.path.join(dirname_output_file, pdf_output)
|
||||
rendered_pdf.write(open(output_file, 'wb'))
|
|
@ -0,0 +1,69 @@
|
|||
import os
|
||||
|
||||
from PyPDF2 import PdfFileReader, PdfFileMerger
|
||||
from pdfjinja import PdfJinja
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
|
||||
def fusion_fichiers(chemin_merge_pdf, nom_merge_pdf, liste_de_pdf):
|
||||
"""
|
||||
Permet de fusionner un ensemble de pdf
|
||||
:param chemin_merge_pdf: chemin ou l'on souhaite fusioner l'ensemble des pdf
|
||||
:param nom_merge_pdf: nom que l'on souhaite donner au fichier pdf final
|
||||
:param liste_de_pdf: liste de pdf avec leur chemin inclu
|
||||
:return:
|
||||
"""
|
||||
merger = PdfFileMerger()
|
||||
for filename in liste_de_pdf:
|
||||
merger.append(PdfFileReader(os.path.join(filename), "rb"))
|
||||
merger.write(os.path.join(chemin_merge_pdf, nom_merge_pdf))
|
||||
|
||||
|
||||
def get_pdf_from_directory(chemin_des_pdf):
|
||||
"""
|
||||
Permet de récuperer l'ensemble des pdf d'un chemin
|
||||
:param chemin_des_pdf:
|
||||
:return:
|
||||
"""
|
||||
return [f for f in os.listdir(chemin_des_pdf) if f.endswith("pdf")]
|
||||
|
||||
|
||||
def remplir_template(dirname_template, pdf_template, dirname_output_file, pdf_output, dictionnaire):
|
||||
"""
|
||||
Fonction qui permet de remplir un pdf template
|
||||
:param dirname_template: chemin du fichier de template
|
||||
:param pdf_template: nom du fichier de template
|
||||
:param dirname_output_file: chemin des pdf généré
|
||||
:param pdf_output: nom du fichier pdf à générer
|
||||
:param dictionnaire: dictionnaire contenant le nom des textfields des pdf ainsi que leurs valeurs
|
||||
:return:
|
||||
"""
|
||||
template_pdf_file = os.path.join(dirname_template, pdf_template)
|
||||
template_pdf = PdfJinja(template_pdf_file)
|
||||
|
||||
rendered_pdf = template_pdf(dictionnaire)
|
||||
|
||||
output_file = os.path.join(dirname_output_file, pdf_output)
|
||||
rendered_pdf.write(open(output_file, 'wb'))
|
||||
|
||||
|
||||
def allowed_file(filename):
|
||||
allowed_extensions = {'pdf'}
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in allowed_extensions
|
||||
|
||||
|
||||
def upload_file(file_to_upload, upload_folder):
|
||||
"""
|
||||
rep de l'etu avec id
|
||||
:param file:
|
||||
:param upload_folder:
|
||||
:return:
|
||||
"""
|
||||
file_to_upload.save(os.path.join(upload_folder, secure_filename(file_to_upload.filename)))
|
||||
|
||||
|
||||
|
||||
def delete_file(pdf_path):
|
||||
if os.path.exists(pdf_path):
|
||||
os.remove(pdf_path)
|
|
@ -1,12 +1,17 @@
|
|||
from app.api.GroupAPI import GroupAPI
|
||||
from app.api.LivretAPI import LivretAPI
|
||||
from app.api.LoginAPI import LoginAPI
|
||||
from app.api.UserAPI import UserAPI
|
||||
from app.api.UserInfoAPI import UserInfoAPI
|
||||
from app.api.UserInfoAPI import UserInfoAPI, UserGroupsAPI
|
||||
from app.api.exampleapi import SomeApi
|
||||
from app.api.loginAPI import LoginAPI
|
||||
from app.core import api
|
||||
|
||||
# Some Api resource
|
||||
api.add_resource(SomeApi, '/api/someapi', '/api/someapi/<int:id>')
|
||||
api.add_resource(LoginAPI, '/api/login')
|
||||
api.add_resource(UserAPI, '/api/user', '/api/user/byuid/<int:uid>', '/api/user/bylogin/<string:login>',
|
||||
'/api/user/byemail/<string:email>')
|
||||
api.add_resource(UserInfoAPI, '/api/userInfo')
|
||||
api.add_resource(UserGroupsAPI, '/api/userGroups')
|
||||
api.add_resource(UserAPI, '/api/user', '/api/user/byuid/<int:uid>', '/api/user/byemail/<string:email>',
|
||||
'/api/user/byhash/<string:hashcode>')
|
||||
api.add_resource(GroupAPI, '/api/group', '/api/group/bygid/<int:gid>', '/api/group/byname/<string:name>')
|
||||
api.add_resource(LivretAPI, '/api/livret', '/api/livret/byuid/<int:uid>')
|
||||
|
|
|
@ -3,8 +3,12 @@ import string
|
|||
from hashlib import sha512
|
||||
|
||||
from flask import json
|
||||
from mailer import Mailer
|
||||
from mailer import Message
|
||||
from sqlalchemy.ext.declarative import DeclarativeMeta
|
||||
|
||||
from app.core import app
|
||||
|
||||
SIMPLE_CHARS = string.ascii_letters + string.digits
|
||||
|
||||
|
||||
|
@ -57,3 +61,12 @@ def new_alchemy_encoder(revisit_self=False, fields_to_expand=[]):
|
|||
def checkParams(wanted, args):
|
||||
inter = [elt for elt in wanted if elt in args]
|
||||
return len(inter) == len(wanted)
|
||||
|
||||
|
||||
def send_mail(subject, to, html):
|
||||
if app.config['MAILER']:
|
||||
message = Message(From="ola.noreply@univ-tlse2.fr", To=to, charset="utf-8")
|
||||
message.Subject = subject
|
||||
message.Html = html
|
||||
sender = Mailer('localhost') # TODO: Mettre le SMTP de la fac ici
|
||||
sender.send(message)
|
||||
|
|
|
@ -3,6 +3,7 @@ flask-script < 2.1
|
|||
flask-sqlalchemy < 2.2
|
||||
flask-restful < 0.4
|
||||
flask-cas
|
||||
mailer
|
||||
mysqlclient < 1.4
|
||||
pdfjinja < 1.1
|
||||
PyPDF2 < 1.27
|
||||
|
|
|
@ -46,7 +46,7 @@ class AuthTestCase(unittest.TestCase):
|
|||
return self.app.post('/api/login',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
login=email,
|
||||
email=email,
|
||||
password=password
|
||||
)
|
||||
), content_type='application/json')
|
||||
|
@ -58,34 +58,30 @@ class AuthTestCase(unittest.TestCase):
|
|||
return self.app.delete('/api/login')
|
||||
|
||||
def test_login_logout(self):
|
||||
rv = self.login('admin', 'admin')
|
||||
rv = self.login('admin@admin.com', 'admin@admin.com')
|
||||
self.assertEqual(rv.status_code, 200, 'Login as admin Failed')
|
||||
|
||||
rv = self.login('admin', 'admin')
|
||||
rv = self.login('admin@admin.com', 'admin@admin.com')
|
||||
self.assertEqual(rv.status_code, 201, 'Login as admin succeed but should have already been done')
|
||||
|
||||
rv = self.getUserInfo()
|
||||
self.assertEqual(rv.status_code, 200, 'Getting user info failed')
|
||||
self.assertEqual({"id": getUser(login="admin")["id"], "login": "admin", "email": "admin@admin.com", "role": 4,
|
||||
self.assertEqual({"id": getUser(login="admin")["id"], "login": "admin", "email": "admin@admin.com", "role": "4",
|
||||
"phone": "00.00.00.00.00"}, json.loads(rv.data)['USER'], 'Invalid user info')
|
||||
|
||||
rv = self.logout()
|
||||
self.assertEqual(rv.status_code, 200, 'Logout Failed')
|
||||
|
||||
rv = self.login('adminx', 'admin')
|
||||
self.assertEqual(rv.status_code, 401, 'Authentication from CAS has not failed for the invalid user xadmin !')
|
||||
rv = self.login('adminx@admin.com', 'admin@admin.com')
|
||||
self.assertEqual(rv.status_code, 401, 'Authentication not failed for the invalid user!')
|
||||
|
||||
rv = self.getUserInfo()
|
||||
self.assertEqual(rv.status_code, 200, 'Getting user info failed')
|
||||
self.assertIsNone(json.loads(rv.data)['USER'], 'User info should be None')
|
||||
|
||||
rv = self.login('admin', 'adminx')
|
||||
rv = self.login('admin@admin.com', 'admin@admin.comx')
|
||||
self.assertEqual(rv.status_code, 401,
|
||||
'Authentication from CAS has not failed for the invalid password xadmin !')
|
||||
|
||||
rv = self.login('toto', 'toto')
|
||||
self.assertEqual(rv.status_code, 403, 'Authentication shouldn\'t be allowed for user toto !')
|
||||
|
||||
'Authenticationnot failed for the invalid password !')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import unittest
|
||||
|
||||
from flask import json
|
||||
|
||||
from app.core import app
|
||||
from app.model import USER, user_class, GROUP, group_class
|
||||
|
||||
|
||||
class GroupTestCase(unittest.TestCase):
|
||||
uid = None
|
||||
uid2 = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
query = USER.insert().values(email="admin@admin.com", role="4", phone="00.00.00.00.00", name="admin",
|
||||
hash="toto")
|
||||
res = query.execute()
|
||||
cls.uid = res.lastrowid
|
||||
query = USER.insert().values(email="adminx@admin.com", role="3", phone="00.00.00.00.00", name="adminx",
|
||||
hash="zozo")
|
||||
res = query.execute()
|
||||
cls.uid2 = res.lastrowid
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
query = GROUP.delete().where(group_class.name == "group_test")
|
||||
query.execute()
|
||||
query = GROUP.delete().where(group_class.name == "group_test2")
|
||||
query.execute()
|
||||
query = USER.delete().where(user_class.email == "admin@admin.com")
|
||||
query.execute()
|
||||
query = USER.delete().where(user_class.email == "adminx@admin.com")
|
||||
query.execute()
|
||||
|
||||
def setUp(self):
|
||||
self.app = app.test_client()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def create_group(self, name, year, class_short, class_long, department, resp_id, sec_id):
|
||||
return self.app.post('/api/group',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
name=name,
|
||||
year=year,
|
||||
class_short=class_short,
|
||||
class_long=class_long,
|
||||
department=department,
|
||||
resp_id=resp_id,
|
||||
sec_id=sec_id
|
||||
)
|
||||
), content_type='application/json')
|
||||
|
||||
def getGroupByID(self, GID):
|
||||
return self.app.get('/api/group/bygid/' + str(GID))
|
||||
|
||||
def getGroupByName(self, name):
|
||||
return self.app.get('/api/group/byname/' + name)
|
||||
|
||||
def change_group(self, GID, name, year, class_short, class_long, department, resp_id, sec_id):
|
||||
return self.app.put('/api/group/bygid/' + str(GID),
|
||||
data=json.dumps(
|
||||
dict(
|
||||
name=name,
|
||||
year=year,
|
||||
class_short=class_short,
|
||||
class_long=class_long,
|
||||
department=department,
|
||||
resp_id=resp_id,
|
||||
sec_id=sec_id
|
||||
)
|
||||
), content_type='application/json')
|
||||
|
||||
def test_group(self):
|
||||
rv = self.create_group('group_test', '2017', 'GT', 'GROUP_TEST', 'TESTING', self.uid, self.uid2)
|
||||
self.assertEqual(rv.status_code, 201, 'Creating group Failed')
|
||||
gid = json.loads(rv.data)['GID']
|
||||
self.assertIsNotNone(gid)
|
||||
|
||||
rv = self.create_group('group_test', '2017', 'GT', 'GROUP_TEST', 'TESTING', self.uid, self.uid2)
|
||||
self.assertEqual(rv.status_code, 200, 'Group is supposed to already exist')
|
||||
gid2 = json.loads(rv.data)['GID']
|
||||
self.assertEqual(gid, gid2, "The GID must be the same !")
|
||||
|
||||
rv = self.getGroupByID(gid)
|
||||
self.assertEqual(rv.status_code, 200, 'Getting group failed by ID')
|
||||
group = json.loads(rv.data)['GROUP']
|
||||
self.assertIsNotNone(group)
|
||||
|
||||
rv = self.getGroupByName("group_test")
|
||||
self.assertEqual(rv.status_code, 200, 'Getting group failed by Name')
|
||||
group2 = json.loads(rv.data)['GROUP']
|
||||
self.assertEqual(group, group2, "Group by name must be the same !")
|
||||
|
||||
rv = self.change_group(gid, 'group_test2', '2018', 'GT2', 'GROUP_TEST2', 'TESTING2', self.uid2, self.uid)
|
||||
self.assertEqual(rv.status_code, 200, 'Group modification failed !')
|
||||
gid3 = json.loads(rv.data)['GID']
|
||||
self.assertEqual(gid, gid3, "GIDs doesn't match !")
|
||||
|
||||
rv = self.getGroupByName('group_test2')
|
||||
self.assertEqual(rv.status_code, 200, 'Getting modified group failed by Name')
|
||||
group4 = json.loads(rv.data)['GROUP']
|
||||
self.assertIsNotNone(group4, "Modified group shouldn't be None !")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -0,0 +1,88 @@
|
|||
import unittest
|
||||
|
||||
from flask import json
|
||||
|
||||
from app.core import app
|
||||
from app.model import USER, user_class
|
||||
|
||||
|
||||
class UserTestCase(unittest.TestCase):
|
||||
uid = None
|
||||
gid = None
|
||||
tid = None
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
query = USER.delete().where(user_class.email == "admin@admin.com")
|
||||
query.execute()
|
||||
query = USER.delete().where(user_class.email == "adminx@admin.com")
|
||||
query.execute()
|
||||
|
||||
def setUp(self):
|
||||
self.app = app.test_client()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def create_user(self, email, role, name):
|
||||
return self.app.post('/api/user',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
email=email,
|
||||
role=role,
|
||||
name=name
|
||||
)
|
||||
), content_type='application/json')
|
||||
|
||||
def getUserByID(self, UID):
|
||||
return self.app.get('/api/user/byuid/' + str(UID))
|
||||
|
||||
def getUserByEmail(self, email):
|
||||
return self.app.get('/api/user/byemail/' + email)
|
||||
|
||||
def change_user(self, UID, email, role, phone, name, password):
|
||||
return self.app.put('/api/user/byuid/' + str(UID),
|
||||
data=json.dumps(
|
||||
dict(
|
||||
role=role,
|
||||
email=email,
|
||||
phone=phone,
|
||||
name=name,
|
||||
password=password
|
||||
)
|
||||
), content_type='application/json')
|
||||
|
||||
def test_user(self):
|
||||
rv = self.create_user('admin@admin.com', '4', 'Admin')
|
||||
self.assertEqual(rv.status_code, 201, 'Creating user Failed')
|
||||
uid = json.loads(rv.data)['UID']
|
||||
self.assertIsNotNone(uid)
|
||||
|
||||
rv = self.create_user('admin@admin.com', '4', 'Admin')
|
||||
self.assertEqual(rv.status_code, 200, 'User is supposed to already exist')
|
||||
uid2 = json.loads(rv.data)['UID']
|
||||
self.assertEqual(uid, uid2, "The UID must be the same !")
|
||||
|
||||
rv = self.getUserByID(uid)
|
||||
self.assertEqual(rv.status_code, 200, 'Getting user failed by ID')
|
||||
user = json.loads(rv.data)['USER']
|
||||
self.assertIsNotNone(user)
|
||||
|
||||
rv = self.getUserByEmail("admin@admin.com")
|
||||
self.assertEqual(rv.status_code, 200, 'Getting user failed by email')
|
||||
user3 = json.loads(rv.data)['USER']
|
||||
self.assertEqual(user, user3, "User by email must be the same !")
|
||||
|
||||
rv = self.change_user(uid, 'adminx@admin.com', '3', '11.11.11.11.11', 'Adminx', 'password')
|
||||
self.assertEqual(rv.status_code, 200, 'User modification failed !')
|
||||
uid3 = json.loads(rv.data)['UID']
|
||||
self.assertEqual(uid, uid3, "UIDs doesn't match !")
|
||||
|
||||
rv = self.getUserByEmail("adminx@admin.com")
|
||||
self.assertEqual(rv.status_code, 200, 'Getting modified user failed by Email')
|
||||
user4 = json.loads(rv.data)['USER']
|
||||
self.assertIsNotNone(user4, "Modified user shouldn't be None !")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,9 +1,7 @@
|
|||
import os
|
||||
import unittest
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from builtins import print
|
||||
from pathlib import Path
|
||||
|
||||
from app.tools.FusionPdf import fusion_fichiers, get_pdf_from_directory
|
||||
|
Binary file not shown.
|
@ -0,0 +1,20 @@
|
|||
import os
|
||||
import unittest
|
||||
|
||||
from werkzeug.datastructures import FileStorage
|
||||
|
||||
from app.tools.LibPdf import upload_file
|
||||
|
||||
|
||||
class TestFusionTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.datadir = os.path.join(os.path.dirname(__file__))
|
||||
|
||||
def test_fusion(self):
|
||||
with open("page1.pdf", 'rb') as fp:
|
||||
file = FileStorage(fp)
|
||||
upload_file(file, "upload")
|
||||
|
||||
# self.assertTrue(Path(self.datadir + "/testFusion.pdf").is_file(), "Pdf fusionne inexistant")
|
||||
# self.assertTrue(len(get_pdf_from_directory(self.datadir)) > 0, "pdf non trouve")
|
||||
# os.remove(self.datadir + "/testFusion.pdf")
|
|
@ -3,9 +3,8 @@ import unittest
|
|||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
|
||||
from pdfjinja import PdfJinja
|
||||
|
||||
from app.tools.InsertTemplate import remplir_template
|
||||
from pdfjinja import PdfJinja
|
||||
|
||||
|
||||
class InsertTemplateTestCase(unittest.TestCase):
|
|
@ -46,6 +46,8 @@
|
|||
<script src="bower_components/angular-material/angular-material.js"></script>
|
||||
<script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
|
||||
<script src="bower_components/angular-material-icons/angular-material-icons.min.js"></script>
|
||||
<script src="bower_components/underscore/underscore.js"></script>
|
||||
<script src="bower_components/angular-file-upload/dist/angular-file-upload.min.js"></script>
|
||||
<!-- endbower -->
|
||||
<!-- endbuild -->
|
||||
|
||||
|
@ -54,6 +56,10 @@
|
|||
<script src="scripts/controllers/login.js"></script>
|
||||
<script src="scripts/controllers/studentSpace.js"></script>
|
||||
<script src="scripts/controllers/responsableFormationSpace.js"></script>
|
||||
<script src="scripts/controllers/administrationSpace.js"></script>
|
||||
<script src="scripts/controllers/administrationDialog.js"></script>
|
||||
|
||||
<script src="scripts/services/Filters.js"></script>
|
||||
<!-- endbuild -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -14,7 +14,8 @@ var app = angular.module('clientApp', [
|
|||
'ngSanitize',
|
||||
'ngMaterial',
|
||||
'ui.router',
|
||||
'ngMdIcons'
|
||||
'ngMdIcons',
|
||||
'angularFileUpload'
|
||||
]);
|
||||
|
||||
app.config(function ($stateProvider, $urlRouterProvider) {
|
||||
|
@ -37,10 +38,16 @@ app.config(function ($stateProvider, $urlRouterProvider) {
|
|||
templateUrl: 'views/studentSpace.html',
|
||||
controller: 'StudentSpaceCtrl'
|
||||
})
|
||||
|
||||
|
||||
.state('responsableFormationSpace', {
|
||||
url: '/espace-formation',
|
||||
templateUrl: 'views/responsableFormationSpace.html',
|
||||
controller: 'ResponsableFormationSpaceCtrl'
|
||||
})
|
||||
|
||||
.state('administrationSpace', {
|
||||
url: '/espace-secretariat',
|
||||
templateUrl: 'views/administrationSpace.html',
|
||||
controller: 'AdministrationSpaceCtrl'
|
||||
});
|
||||
});
|
|
@ -0,0 +1,104 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name frontendApp.controller:AdministrationDialogCtrl
|
||||
* @description
|
||||
* # AdministrationDialogCtrl
|
||||
* Controller of the frontendApp
|
||||
*/
|
||||
angular.module('clientApp')
|
||||
.controller('AdministrationDialogCtrl', function ($scope, $state, FileUploader, $mdDialog, fileNameFilter, illegalFileNamesFilter, type) {
|
||||
|
||||
console.log(type);
|
||||
|
||||
// Public methods -------------------
|
||||
|
||||
$scope.hide = function () {
|
||||
$mdDialog.hide();
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$mdDialog.cancel();
|
||||
};
|
||||
|
||||
$scope.answer = function (answer) {
|
||||
$mdDialog.hide(answer);
|
||||
};
|
||||
|
||||
$scope.allDocumentsAreIllegal = function() {
|
||||
return (fileNameFilter(uploader.queue, type).length === 0);
|
||||
};
|
||||
|
||||
$scope.areThereIllegalFiles = function() {
|
||||
return (illegalFileNamesFilter(uploader.queue, type).length !== 0);
|
||||
};
|
||||
|
||||
var uploader = $scope.uploader = new FileUploader({
|
||||
url: 'upload.php'
|
||||
});
|
||||
|
||||
// Private methods ------------------
|
||||
|
||||
// FILTERS
|
||||
|
||||
// a sync filter
|
||||
uploader.filters.push({
|
||||
name: 'syncFilter',
|
||||
fn: function (item /*{File|FileLikeObject}*/, options) {
|
||||
console.log('syncFilter');
|
||||
return this.queue.length < 10;
|
||||
}
|
||||
});
|
||||
|
||||
// an async filter
|
||||
uploader.filters.push({
|
||||
name: 'asyncFilter',
|
||||
fn: function (item /*{File|FileLikeObject}*/, options, deferred) {
|
||||
console.log('asyncFilter');
|
||||
setTimeout(deferred.resolve, 1e3);
|
||||
}
|
||||
});
|
||||
|
||||
// CALLBACKS
|
||||
|
||||
uploader.onWhenAddingFileFailed = function (item /*{File|FileLikeObject}*/, filter, options) {
|
||||
console.info('onWhenAddingFileFailed', item, filter, options);
|
||||
};
|
||||
uploader.onAfterAddingFile = function (fileItem) {
|
||||
console.info('onAfterAddingFile', fileItem);
|
||||
};
|
||||
uploader.onAfterAddingAll = function (addedFileItems) {
|
||||
console.info('onAfterAddingAll', addedFileItems);
|
||||
};
|
||||
uploader.onBeforeUploadItem = function (item) {
|
||||
console.info('onBeforeUploadItem', item);
|
||||
};
|
||||
uploader.onProgressItem = function (fileItem, progress) {
|
||||
console.info('onProgressItem', fileItem, progress);
|
||||
};
|
||||
uploader.onProgressAll = function (progress) {
|
||||
console.info('onProgressAll', progress);
|
||||
};
|
||||
uploader.onSuccessItem = function (fileItem, response, status, headers) {
|
||||
console.info('onSuccessItem', fileItem, response, status, headers);
|
||||
};
|
||||
uploader.onErrorItem = function (fileItem, response, status, headers) {
|
||||
console.info('onErrorItem', fileItem, response, status, headers);
|
||||
};
|
||||
uploader.onCancelItem = function (fileItem, response, status, headers) {
|
||||
console.info('onCancelItem', fileItem, response, status, headers);
|
||||
};
|
||||
uploader.onCompleteItem = function (fileItem, response, status, headers) {
|
||||
console.info('onCompleteItem', fileItem, response, status, headers);
|
||||
};
|
||||
uploader.onCompleteAll = function () {
|
||||
console.info('onCompleteAll');
|
||||
};
|
||||
|
||||
console.info('uploader', uploader);
|
||||
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,125 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name frontendApp.controller:AdministrationSpaceCtrl
|
||||
* @description
|
||||
* # AdministrationSpaceCtrl
|
||||
* Controller of the frontendApp
|
||||
*/
|
||||
angular.module('clientApp')
|
||||
.controller('AdministrationSpaceCtrl', function ($scope, $state, $mdDialog, FileUploader) {
|
||||
|
||||
angular.extend($scope, {
|
||||
logout,
|
||||
deleteAbsence,
|
||||
deleteTrackingSheet,
|
||||
importAbsences,
|
||||
importVisitSheets
|
||||
})
|
||||
|
||||
init();
|
||||
|
||||
// Public methods -------------------
|
||||
|
||||
function logout() {
|
||||
$state.go('login');
|
||||
}
|
||||
|
||||
function deleteAbsence(groupIndex, periodIndex, absenceIndex) {
|
||||
$scope.formationGroups[groupIndex].formattedAbsences[periodIndex].absences.splice(absenceIndex, 1);
|
||||
}
|
||||
|
||||
function deleteTrackingSheet(groupIndex, trackingSheetIndex) {
|
||||
$scope.formationGroups[groupIndex].trackingSheets.splice(trackingSheetIndex, 1);
|
||||
}
|
||||
|
||||
function importVisitSheets(ev) {
|
||||
$mdDialog.show({
|
||||
controller: 'AdministrationDialogCtrl',
|
||||
templateUrl: 'import-fiches-visite',
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: ev,
|
||||
clickOutsideToClose: true,
|
||||
fullscreen: 'false',
|
||||
locals : { type : 'visit'}
|
||||
})
|
||||
.then(function (answer) {
|
||||
$scope.status = 'You said the information was "' + answer + '".';
|
||||
}, function () {
|
||||
$scope.status = 'You cancelled the dialog.';
|
||||
});
|
||||
}
|
||||
|
||||
function importAbsences(ev) {
|
||||
$mdDialog.show({
|
||||
controller: 'AdministrationDialogCtrl',
|
||||
templateUrl: 'import-fiches-absences',
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: ev,
|
||||
clickOutsideToClose: true,
|
||||
fullscreen: 'false',
|
||||
locals : { type : 'absence'}
|
||||
})
|
||||
.then(function (answer) {
|
||||
$scope.status = 'You said the information was "' + answer + '".';
|
||||
}, function () {
|
||||
$scope.status = 'You cancelled the dialog.';
|
||||
});
|
||||
}
|
||||
|
||||
// Private methods ------------------
|
||||
|
||||
function init() {
|
||||
var formationGroups = [{
|
||||
label: "Master2 ICE",
|
||||
absences: [
|
||||
{ id: 1, title: "Absence_Matthieu_Penchenat_P1" },
|
||||
{ id: 2, title: "Absence_Renan_Husson_P1" },
|
||||
{ id: 3, title: "Absence_Renan_Husson_P2" },
|
||||
{ id: 1, title: "Absence_Renan_Husson_P3" },
|
||||
{ id: 2, title: "Absence_Matthieu_Penchenat_P2" },
|
||||
{ id: 3, title: "Absence_Matthieu_Penchenat_P3" },
|
||||
{ id: 1, title: "Absence_Quentin_Rouland_P1" },
|
||||
{ id: 2, title: "Absence_Quentin_Rouland_P2" },
|
||||
{ id: 3, title: "Absence_Quentin_Rouland_P3" },
|
||||
{ id: 1, title: "Absence_Sitan_Coulibaly_P1" },
|
||||
{ id: 2, title: "Absence_Sitan_Coulibaly_P2" },
|
||||
{ id: 3, title: "Absence_Sitan_Coulibaly_P3" }
|
||||
],
|
||||
trackingSheets: [
|
||||
{ id: 3, fileName: "FicheVisite_Sitan_Coulibaly_1" },
|
||||
{ id: 2, fileName: "FicheVisite_Sitan_Coulibaly_2" },
|
||||
{ id: 1, fileName: "FicheVisite_Sitan_Coulibaly_3" }
|
||||
]
|
||||
}, {
|
||||
label: "Master1 ISMAG",
|
||||
absences: [
|
||||
{ id: 1, title: "Absence_Matthieu_Penchenat_P1" },
|
||||
{ id: 2, title: "Absence_Matthieu_Penchenat_P2" },
|
||||
{ id: 3, title: "Absence_Matthieu_Penchenat_P3" }
|
||||
],
|
||||
trackingSheets: [
|
||||
{ id: 3, fileName: "FicheVisite_Renan_Husson_1" },
|
||||
{ id: 2, fileName: "FicheVisite_Renan_Husson_2" },
|
||||
{ id: 1, fileName: "FicheVisite_Renan_Husson_3" }
|
||||
]
|
||||
}];
|
||||
|
||||
$scope.formationGroups = formationGroups.map(function (formationGroup) {
|
||||
formationGroup.formattedAbsences = reformatAbsences(formationGroup.absences);
|
||||
return formationGroup;
|
||||
});
|
||||
}
|
||||
|
||||
function reformatAbsences(absences) {
|
||||
var myObj = _.groupBy(absences, function (absence) { return absence.title.split('_').pop(); });
|
||||
|
||||
return _.map(myObj, function (value, index) {
|
||||
return { period: index, absences: value };
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,28 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
|
||||
angular.module('clientApp')
|
||||
.filter('fileName', function () {
|
||||
|
||||
return function (queue, type) {
|
||||
|
||||
var reg = (type === 'absence') ?/^Absence_[A-Z][a-z]*_[A-Z][a-z]*_P\d*.pdf$/ : /^Visite_[A-Z][a-z]*_[A-Z][a-z]*_\d*.pdf$/;
|
||||
return queue.filter(function (item) {
|
||||
return reg.test(item.file.name);
|
||||
});
|
||||
};
|
||||
})
|
||||
.filter('illegalFileNames', function () {
|
||||
|
||||
return function (queue, type) {
|
||||
|
||||
var reg = (type === 'absence') ?/^Absence_[A-Z][a-z]*_[A-Z][a-z]*_P\d*.pdf$/ : /^Visite_[A-Z][a-z]*_[A-Z][a-z]*_\d*.pdf$/;
|
||||
return queue.filter(function (item) {
|
||||
return !reg.test(item.file.name);
|
||||
}).map(function(item) {
|
||||
return item.file.name;
|
||||
});
|
||||
};
|
||||
});
|
||||
})();
|
|
@ -74,4 +74,24 @@ md-tab-data{
|
|||
padding: 8px;
|
||||
/* border: 1px solid; */
|
||||
background-color: yellowgreen;
|
||||
}
|
||||
}
|
||||
|
||||
.well {
|
||||
padding: 15px;
|
||||
margin: 5px;
|
||||
background-color: #e0e0e0;
|
||||
border: dotted 3px lightgray;
|
||||
}
|
||||
|
||||
.note {
|
||||
padding: 15px;
|
||||
margin: 5px;
|
||||
background-color: #a5d6a7;
|
||||
}
|
||||
|
||||
|
||||
.error {
|
||||
padding: 15px;
|
||||
margin: 5px;
|
||||
background-color: #ef9a9a;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
<div layout="column">
|
||||
<md-toolbar>
|
||||
|
||||
<div class="md-toolbar-tools">
|
||||
<h2 md-truncate flex>Bienvenue Isabelle Michu</h2>
|
||||
|
||||
<md-button ng-click="logout()">
|
||||
Se déconnecter
|
||||
</md-button>
|
||||
</div>
|
||||
|
||||
</md-toolbar>
|
||||
</div>
|
||||
|
||||
<div layout="row" layout-margin>
|
||||
|
||||
<div ng-cloak flex="100">
|
||||
<md-content>
|
||||
<md-tabs md-dynamic-height md-border-bottom>
|
||||
<md-tab ng-repeat="formationGroup in formationGroups" label="{{formationGroup.label}}">
|
||||
|
||||
<div layout="row" layout-margin>
|
||||
<md-card flex="50">
|
||||
<md-card-title>
|
||||
<md-card-title-text>
|
||||
<span class="md-headline">Fiches d'absence</span>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
<md-content flex layout-padding>
|
||||
<div style="height: 350px; overflow: auto">
|
||||
<div ng-repeat="formattedAbsence in formationGroup.formattedAbsences">
|
||||
<md-subheader class="md-no-sticky">Période {{formattedAbsence.period}}</md-subheader>
|
||||
<md-list-item ng-repeat="absence in formattedAbsence.absences">
|
||||
<p>{{absence.title}}</p>
|
||||
<ng-md-icon icon="delete" style="fill: grey" size="24" ng-click="deleteAbsence($parent.$parent.$index, $parent.$index, $index)"></ng-md-icon>
|
||||
</md-list-item>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<center>
|
||||
<md-button class="md-raised md-primary" ng-click="importAbsences($event)">Importer des fiches d'absences</md-button>
|
||||
</center>
|
||||
</md-content>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<md-card flex="50">
|
||||
<md-card-title>
|
||||
<md-card-title-text>
|
||||
<span class="md-headline">Fiches de visite</span>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
<md-content flex layout-padding>
|
||||
<div style="height: 350px; overflow: auto">
|
||||
<md-list-item ng-repeat="sheet in formationGroup.trackingSheets">
|
||||
<p>{{sheet.fileName}}</p>
|
||||
<ng-md-icon icon="delete" style="fill: grey" size="24" ng-click="deleteTrackingSheet($parent.$index, $index)"></ng-md-icon>
|
||||
</md-list-item>
|
||||
</div>
|
||||
<br />
|
||||
<center>
|
||||
<md-button class="md-raised md-primary" ng-click="importVisitSheets($event)">Importer des fiches de visite</md-button>
|
||||
</center>
|
||||
</md-content>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
</md-content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/ng-template" id="import-fiches-absences">
|
||||
|
||||
<md-dialog aria-label="Importer des fiches d'absences">
|
||||
<form ng-cloak>
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2>Importer des fiches d'absences</h2>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
|
||||
<md-dialog-content>
|
||||
<div class="md-dialog-content">
|
||||
|
||||
<div layout="row">
|
||||
|
||||
<div flex>
|
||||
|
||||
<h3>Importer des fiches d'absences</h3>
|
||||
<input type="file" nv-file-select="" accept=".pdf" uploader="uploader" multiple /><br/><br/>
|
||||
|
||||
<p class="note">
|
||||
<ng-md-icon icon="warning" size="20"></ng-md-icon>
|
||||
Note d'utilisation : chaque fiche d'absence doit respecter une règle de nommage.<br/> Nom
|
||||
de fichier : Absence_Prenom_Nom_PN.pdf<br/>
|
||||
<i>Le N de PN étant un numéro - exemple : (P1, P2, P3, etc.)</i>
|
||||
</p>
|
||||
|
||||
<div ng-if="uploader.queue.length !== 0">
|
||||
<h3>Liste des documents : </h3>
|
||||
<p>Nombre de documents : {{ uploader.queue.length }}</p>
|
||||
|
||||
<div ng-if="areThereIllegalFiles()" style="background-color: #ef9a9a; padding: 2px 10px 2px 10px;margin: 10px 0 10px 0">
|
||||
<p>
|
||||
Les documents suivants ne respectent pas la règle de nommage :
|
||||
</p>
|
||||
<ul>
|
||||
<li ng-repeat="fileName in uploader.queue | illegalFileNames:'absence'">{{fileName}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-if="!allDocumentsAreIllegal()">
|
||||
<table class="bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">Nom</th>
|
||||
<th ng-show="uploader.isHTML5">Taille</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in uploader.queue | fileName:'absence'">
|
||||
<td><strong>{{ item.file.name }}</strong></td>
|
||||
<td ng-show="uploader.isHTML5" nowrap>{{ item.file.size/1024/1024|number:2 }} MB</td>
|
||||
<td class="text-center">
|
||||
<span ng-show="item.isSuccess">
|
||||
<ng-md-icon icon="done" size="24"></ng-md-icon>
|
||||
</span>
|
||||
<span ng-show="item.isCancel">
|
||||
<ng-md-icon icon="cancel" size="24"></ng-md-icon>
|
||||
</span>
|
||||
<span ng-show="item.isError">
|
||||
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon>
|
||||
</span>
|
||||
</td>
|
||||
<td nowrap>
|
||||
<md-button type="button" class="md-raised md-primary" ng-click="item.upload()" ng-disabled="item.isReady || item.isUploading || item.isSuccess">
|
||||
<ng-md-icon icon="file_upload" size="24"></ng-md-icon> Charger
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised" ng-click="item.cancel()" ng-disabled="!item.isUploading">
|
||||
<ng-md-icon icon="cancel" size="24"></ng-md-icon> Annuler
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised md-warn" ng-click="item.remove()">
|
||||
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon> Supprimer
|
||||
</md-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style='margin-top : 40px'>
|
||||
<div>
|
||||
Niveau de progression :
|
||||
<md-progress-linear md-mode="determinate" value="{{uploader.progress}}"></md-progress-linear>
|
||||
</div>
|
||||
<md-button type="button" class="md-raised md-primary" ng-click="uploader.uploadAll()" ng-disabled="!uploader.getNotUploadedItems().length">
|
||||
<ng-md-icon icon="file_upload" size="24"></ng-md-icon> Tout charger
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised" ng-click="uploader.cancelAll()" ng-disabled="!uploader.isUploading">
|
||||
<ng-md-icon icon="cancel" size="24"></ng-md-icon> Tout annuler
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised md-warn" ng-click="uploader.clearQueue()" ng-disabled="!uploader.queue.length">
|
||||
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon> Tout supprimer
|
||||
</md-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions layout="row">
|
||||
<span flex></span>
|
||||
<md-button ng-click="answer('useful')">
|
||||
Fermer
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</form>
|
||||
</md-dialog>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="import-fiches-visite">
|
||||
|
||||
<md-dialog aria-label="Importer des fiches de visite">
|
||||
<form ng-cloak>
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2>Importer des fiches de visite</h2>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
|
||||
<md-dialog-content>
|
||||
<div class="md-dialog-content">
|
||||
|
||||
<div layout="row">
|
||||
|
||||
<div flex>
|
||||
|
||||
<h3>Importer des fiches de visite</h3>
|
||||
<input type="file" nv-file-select="" accept=".pdf" uploader="uploader" multiple /><br/><br/>
|
||||
|
||||
<p class="note">
|
||||
<ng-md-icon icon="warning" size="20"></ng-md-icon>
|
||||
Note d'utilisation : chaque fiche de visite doit respecter une règle de nommage.<br/> Nom
|
||||
de fichier : Visite_Prenom_Nom_N.pdf<br/>
|
||||
<i>Le N étant un numéro.</i>
|
||||
</p>
|
||||
|
||||
<div ng-if="uploader.queue.length !== 0">
|
||||
<h3>Liste des documents : </h3>
|
||||
<p>Nombre de documents : {{ uploader.queue.length }}</p>
|
||||
|
||||
<div ng-if="areThereIllegalFiles()" style="background-color: #ef9a9a; padding: 2px 10px 2px 10px;margin: 10px 0 10px 0">
|
||||
<p>
|
||||
Les documents suivants ne respectent pas la règle de nommage :
|
||||
</p>
|
||||
<ul>
|
||||
<li ng-repeat="fileName in uploader.queue | illegalFileNames:'visit'">{{fileName}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-if="!allDocumentsAreIllegal()">
|
||||
<table class="bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">Nom</th>
|
||||
<th ng-show="uploader.isHTML5">Taille</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in uploader.queue | fileName:'visit'">
|
||||
<td><strong>{{ item.file.name }}</strong></td>
|
||||
<td ng-show="uploader.isHTML5" nowrap>{{ item.file.size/1024/1024|number:2 }} MB</td>
|
||||
<td class="text-center">
|
||||
<span ng-show="item.isSuccess">
|
||||
<ng-md-icon icon="done" size="24"></ng-md-icon>
|
||||
</span>
|
||||
<span ng-show="item.isCancel">
|
||||
<ng-md-icon icon="cancel" size="24"></ng-md-icon>
|
||||
</span>
|
||||
<span ng-show="item.isError">
|
||||
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon>
|
||||
</span>
|
||||
</td>
|
||||
<td nowrap>
|
||||
<md-button type="button" class="md-raised md-primary" ng-click="item.upload()" ng-disabled="item.isReady || item.isUploading || item.isSuccess">
|
||||
<ng-md-icon icon="file_upload" size="24"></ng-md-icon> Charger
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised" ng-click="item.cancel()" ng-disabled="!item.isUploading">
|
||||
<ng-md-icon icon="cancel" size="24"></ng-md-icon> Annuler
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised md-warn" ng-click="item.remove()">
|
||||
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon> Supprimer
|
||||
</md-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style='margin-top : 40px'>
|
||||
<div>
|
||||
Niveau de progression :
|
||||
<md-progress-linear md-mode="determinate" value="{{uploader.progress}}"></md-progress-linear>
|
||||
</div>
|
||||
<md-button type="button" class="md-raised md-primary" ng-click="uploader.uploadAll()" ng-disabled="!uploader.getNotUploadedItems().length">
|
||||
<ng-md-icon icon="file_upload" size="24"></ng-md-icon> Tout charger
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised" ng-click="uploader.cancelAll()" ng-disabled="!uploader.isUploading">
|
||||
<ng-md-icon icon="cancel" size="24"></ng-md-icon> Tout annuler
|
||||
</md-button>
|
||||
<md-button type="button" class="md-raised md-warn" ng-click="uploader.clearQueue()" ng-disabled="!uploader.queue.length">
|
||||
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon> Tout supprimer
|
||||
</md-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions layout="row">
|
||||
<span flex></span>
|
||||
<md-button ng-click="answer('useful')">
|
||||
Fermer
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</form>
|
||||
</md-dialog>
|
||||
</script>
|
|
@ -8,6 +8,8 @@
|
|||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
|
||||
<md-button ui-sref="administrationSpace">Log to administration</md-button>
|
||||
<p class="p-home">
|
||||
The titles of Washed Out's breakthrough song and the first single from Paracosm share the two most important words in Ernest
|
||||
Greene's musical language: feel it. It's a simple request, as well...
|
||||
|
|
|
@ -13,37 +13,209 @@
|
|||
</div>
|
||||
|
||||
<div layout="row" layout-align="center none">
|
||||
<div ng-controller="AppCtrl" class="sample" layout="column" ng-cloak>
|
||||
<md-content class="md-padding">
|
||||
<md-tabs md-selected="selectedIndex" md-border-bottom md-autoselect>
|
||||
<md-tab ng-repeat="tab in tabs"
|
||||
ng-disabled="tab.disabled"
|
||||
label="{{tab.title}}">
|
||||
<div class="demo-tab tab{{$index%4}}" style="padding: 25px; text-align: center;">
|
||||
<div ng-bind="tab.content"></div>
|
||||
<br/>
|
||||
<md-button class="md-primary md-raised" ng-click="removeTab( tab )" ng-disabled="tabs.length <= 1">Remove Tab</md-button>
|
||||
<md-content flex="80">
|
||||
|
||||
<div ng-cloak>
|
||||
<md-content>
|
||||
<md-tabs md-dynamic-height md-border-bottom>
|
||||
<md-tab label="périodes">
|
||||
<md-content class="md-padding">
|
||||
|
||||
|
||||
<div layout="row" layout-align="end">
|
||||
<md-button class="md-raised" ng-click="exportBooklet()">Exporter le livret</md-button>
|
||||
</div>
|
||||
|
||||
<md-card ng-repeat="period in periods">
|
||||
<md-card-title>
|
||||
<md-card-title-text>
|
||||
<h2 class="md-display-1">Période {{period.number}}</h2>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
|
||||
|
||||
<div layout="column" layout-fill>
|
||||
<div flex class="green darken-2">
|
||||
<h3 class="md-headline" style="padding-left : 10px">
|
||||
Université
|
||||
<md-button class="md-icon-button" ng-click="toggleAccordion(period.university.icon, 'false', $index)" aria-label="call">
|
||||
<ng-md-icon icon="{{period.university.icon}}" style="fill: blue" size="24"></ng-md-icon>
|
||||
</md-button>
|
||||
</h3>
|
||||
|
||||
<md-content flex layout-padding ng-if="isOpenAccordion(period.university.icon)">
|
||||
<p>Lorem ipsum dolor sit amet, ne quod novum mei. Sea omnium invenire mediocrem at, in lobortis conclusionemque
|
||||
nam. Ne deleniti appetere reprimique pro, inani labitur disputationi te sed. At vix sale omnesque,
|
||||
id pro labitur reformidans accommodare, cum labores honestatis eu. Nec quem lucilius in, eam praesent
|
||||
reformidans no. Sed laudem aliquam ne.</p><br/>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>Commentaire de période</label>
|
||||
<textarea ng-model="period.university.comment" md-maxlength="150" rows="5" md-select-on-focus></textarea>
|
||||
</md-input-container>
|
||||
|
||||
<div layout="row" layout-align="end">
|
||||
<md-button class="md-raised">Enregistrer</md-button>
|
||||
</div>
|
||||
</md-content>
|
||||
</div>
|
||||
|
||||
<div flex class="green darken-3">
|
||||
<h3 class="md-headline" style="padding-left : 10px">
|
||||
Entreprise
|
||||
<md-button class="md-icon-button" ng-click="toggleAccordion(period.company.icon, 'true', $index)" aria-label="call">
|
||||
<ng-md-icon icon="{{period.company.icon}}" style="fill: blue" size="24"></ng-md-icon>
|
||||
</md-button>
|
||||
</h3>
|
||||
|
||||
<md-content flex layout-padding ng-if="isOpenAccordion(period.company.icon)">
|
||||
<p>Lorem ipsum dolor sit amet, ne quod novum mei. Sea omnium invenire mediocrem at, in lobortis conclusionemque
|
||||
nam. Ne deleniti appetere reprimique pro, inani labitur disputationi te sed. At vix sale omnesque,
|
||||
id pro labitur reformidans accommodare, cum labores honestatis eu. Nec quem lucilius in, eam praesent
|
||||
reformidans no. Sed laudem aliquam ne.</p>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>Commentaire de période</label>
|
||||
<textarea ng-model="period.company.comment" md-maxlength="150" rows="5" md-select-on-focus></textarea>
|
||||
</md-input-container>
|
||||
|
||||
<div layout="row" layout-align="end">
|
||||
<md-button class="md-raised">Enregistrer</md-button>
|
||||
</div>
|
||||
</md-content>
|
||||
</div>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
</md-content>
|
||||
</md-tab>
|
||||
<md-tab label="données administratives">
|
||||
<md-content class="md-padding">
|
||||
<md-content layout-padding>
|
||||
<form name="projectForm">
|
||||
<md-card>
|
||||
<md-card-title>
|
||||
<md-card-title-text>
|
||||
<h3 class="md-headline">Informations personnelles</h3>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
|
||||
<div layout="row">
|
||||
<md-input-container flex="50">
|
||||
<label>Prénom</label>
|
||||
<input required name="studentFirstName" ng-model="studentFirstName">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="50">
|
||||
<label>Nom</label>
|
||||
<input required name="studentLastName" ng-model="studentLastName">
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<div layout="row">
|
||||
<md-input-container flex="50">
|
||||
<label>Type de contrat / d'engagement</label>
|
||||
<md-select name="type" ng-model="contractType" required>
|
||||
<md-option value="Apprentissage">Apprentissage</md-option>
|
||||
<md-option value="Professionnalisation">Professionnalisation</md-option>
|
||||
<md-option value="Stage">Stage</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="25">
|
||||
<label>Début de contrat</label>
|
||||
<md-datepicker ng-model="beginContractDate"></md-datepicker>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="25">
|
||||
<label>Fin de contrat</label>
|
||||
<md-datepicker ng-model="endContractDate"></md-datepicker>
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<div layout="row">
|
||||
<md-input-container flex="50">
|
||||
<label>Email</label>
|
||||
<input required type="email" name="studentEmail" ng-model="studentEmail" />
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="50">
|
||||
<label>Téléphone</label>
|
||||
<input required type="text" name="studentPhoneNumber" ng-model="studentPhoneNumber" />
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<md-card>
|
||||
<md-card-title>
|
||||
<md-card-title-text>
|
||||
<h3 class="md-headline">Tuteur pédagogique</h3>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>Email</label>
|
||||
<input required type="email" name="universityEmail" ng-model="universityEmail" />
|
||||
</md-input-container>
|
||||
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<md-card>
|
||||
<md-card-title>
|
||||
<md-card-title-text>
|
||||
<h3 class="md-headline">Tuteur entreprise</h3>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
|
||||
<div layout="row">
|
||||
<md-input-container flex="50">
|
||||
<label>Prénom</label>
|
||||
<input required name="companyFirstName" ng-model="companyFirstName">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="50">
|
||||
<label>Nom</label>
|
||||
<input required name="companyLastName" ng-model="companyLastName">
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<div layout="row">
|
||||
<md-input-container flex="50">
|
||||
<label>Nom de l'entreprise</label>
|
||||
<input name="companyName" ng-model="companyName">
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container flex="50">
|
||||
<label>Lieu de l'alternance</label>
|
||||
<input name="companyAddress" ng-model="companyAddress">
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<md-input-container class="md-block">
|
||||
<label>Email</label>
|
||||
<input required type="email" name="companyEmail" ng-model="companyEmail" />
|
||||
</md-input-container>
|
||||
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<div layout="row" layout-align="end">
|
||||
<md-button class="md-raised">Enregistrer</md-button>
|
||||
</div>
|
||||
</form>
|
||||
</md-content>
|
||||
</md-tab>
|
||||
|
||||
</md-tabs>
|
||||
</md-content>
|
||||
</div>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
</md-content>
|
||||
|
||||
<form ng-submit="addTab(tTitle,tContent)" layout="column" class="md-padding" style="padding-top: 0;">
|
||||
<div layout="row" layout-sm="column">
|
||||
<div flex style="position: relative;">
|
||||
<h2 class="md-subhead" style="position: absolute; bottom: 0; left: 0; margin: 0; font-weight: 500; text-transform: uppercase; line-height: 35px; white-space: nowrap;">Add a new Tab:</h2>
|
||||
</div>
|
||||
<md-input-container>
|
||||
<label for="label">Label</label>
|
||||
<input type="text" id="label" ng-model="tTitle">
|
||||
</md-input-container>
|
||||
<md-input-container>
|
||||
<label for="content">Content</label>
|
||||
<input type="text" id="content" ng-model="tContent">
|
||||
</md-input-container>
|
||||
<md-button class="add-tab md-primary md-raised" ng-disabled="!tTitle || !tContent" type="submit" style="margin-right: 0;">Add Tab</md-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</md-content>
|
||||
</div>
|
|
@ -8,7 +8,9 @@
|
|||
"angular-sanitize": "^1.4.0",
|
||||
"angular-material": "^1.1.3",
|
||||
"angular-ui-router": "^0.4.2",
|
||||
"angular-material-icons": "^0.7.1"
|
||||
"angular-material-icons": "^0.7.1",
|
||||
"underscore": "^1.8.3",
|
||||
"angular-file-upload": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "^1.4.0"
|
||||
|
|
|
@ -29,6 +29,8 @@ module.exports = function(config) {
|
|||
'bower_components/angular-material/angular-material.js',
|
||||
'bower_components/angular-ui-router/release/angular-ui-router.js',
|
||||
'bower_components/angular-material-icons/angular-material-icons.min.js',
|
||||
'bower_components/underscore/underscore.js',
|
||||
'bower_components/angular-file-upload/dist/angular-file-upload.min.js',
|
||||
'bower_components/angular-mocks/angular-mocks.js',
|
||||
// endbower
|
||||
'app/scripts/**/*.js',
|
||||
|
|
Reference in New Issue