Skip to content

Commit e5f1253

Browse files
authored
Merge pull request #123 from Nathan-Etave/feat/administration
PR: Merge de feat/administration dans dev
2 parents a80d552 + c436378 commit e5f1253

File tree

16 files changed

+636
-184
lines changed

16 files changed

+636
-184
lines changed

app/__init__.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from app.models.sous_dossier import SOUS_DOSSIER
2121
from app.models.tag import TAG
2222
from app.models.utilisateur import UTILISATEUR
23+
from app.utils import check_notitications, get_total_file_count
2324

2425

2526
crsf = CSRFProtect()
@@ -80,6 +81,11 @@ def handle_process_status_message(message):
8081
pubsub.subscribe(**{'process_status': handle_process_status_message})
8182
pubsub.run_in_thread(sleep_time=0.5)
8283

84+
@app.context_processor
85+
def utility_processor():
86+
return dict(get_total_file_count=get_total_file_count,
87+
check_notitications=check_notitications)
88+
8389
app.register_blueprint(register_bp, url_prefix='/inscription')
8490
app.register_blueprint(notifications_bp, url_prefix='/notifications')
8591
app.register_blueprint(login_bp, url_prefix='/connexion')
@@ -93,10 +99,10 @@ def handle_process_status_message(message):
9399

94100
def fill_db():
95101
if not ROLE.query.all():
102+
db.session.add(ROLE(nom_Role="ADMIN"))
96103
db.session.add(ROLE(nom_Role="RCH4"))
97104
db.session.add(ROLE(nom_Role="RCH3"))
98-
db.session.add(ROLE(nom_Role="RCH2"))
99-
db.session.add(ROLE(nom_Role="RCH1"))
105+
db.session.add(ROLE(nom_Role="RCH1/2"))
100106
db.session.commit()
101107

102108
if not DOSSIER.query.all():
@@ -107,16 +113,19 @@ def fill_db():
107113
db.session.add(DOSSIER(nom_Dossier="Support formation", priorite_Dossier=5, couleur_Dossier="#ffccff"))
108114
db.session.add(DOSSIER(nom_Dossier="Mémoire", priorite_Dossier=6, couleur_Dossier="#cc99ff"))
109115
db.session.add(DOSSIER(nom_Dossier="Thèse", priorite_Dossier=7, couleur_Dossier="#ccccff"))
110-
db.session.add(DOSSIER(nom_Dossier="À trier", priorite_Dossier=8, couleur_Dossier="#99ccff"))
111-
db.session.add(DOSSIER(nom_Dossier="Confidentiel", priorite_Dossier=9, couleur_Dossier="#ccffff"))
116+
db.session.add(DOSSIER(nom_Dossier="À trier", priorite_Dossier=8, couleur_Dossier="#ccffff"))
117+
db.session.add(DOSSIER(nom_Dossier="Archive", priorite_Dossier=9223372036854775807, couleur_Dossier="#d3d7d8"))
112118
db.session.commit()
113119

114120
if not db.session.query(A_ACCES).all():
115121
for dossier in DOSSIER.query.all():
116122
db.session.execute(A_ACCES.insert().values(id_Role=1, id_Dossier=dossier.id_Dossier))
123+
db.session.execute(A_ACCES.insert().values(id_Role=2, id_Dossier=dossier.id_Dossier))
124+
db.session.execute(A_ACCES.insert().values(id_Role=3, id_Dossier=dossier.id_Dossier))
125+
db.session.execute(A_ACCES.insert().values(id_Role=4, id_Dossier=dossier.id_Dossier))
117126
db.session.commit()
118127

119128
if not UTILISATEUR.query.all():
120-
db.session.add(UTILISATEUR(nom_Utilisateur="admin", prenom_Utilisateur="admin", email_Utilisateur="admin@admin.fr", mdp_Utilisateur="$2b$12$sOih7qRKimxwqJXITajOfO.Twyg.lModCMYSrgxLpxGompCQjjM56", est_Actif_Utilisateur=True, id_Role=1))
129+
db.session.add(UTILISATEUR(nom_Utilisateur="Administrateur", prenom_Utilisateur="", email_Utilisateur="admin@admin.fr", mdp_Utilisateur="$2b$12$sOih7qRKimxwqJXITajOfO.Twyg.lModCMYSrgxLpxGompCQjjM56", telephone_Utilisateur="", est_Actif_Utilisateur=True, id_Role=1))
121130
# password: O]SxR=rBv%
122131
db.session.commit()

app/administration/routes.py

Lines changed: 135 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from app.administration import bp
66
from app.tasks import process_file
77
from unidecode import unidecode
8-
from flask import render_template, request, current_app, Response
8+
from flask import render_template, request, current_app, Response, jsonify
9+
from flask_login import current_user
910
from werkzeug.utils import secure_filename
1011
from flask_login import login_required
1112
from app.decorators import admin_required
@@ -52,14 +53,27 @@ def upload():
5253
folder_id = json_data.get("folderId")
5354
file_data = json_data.get("data")
5455
filename = unidecode(secure_filename(json_data.get("filename"))).lower()
55-
user_tags = ' '.join(json_data.get("tags").replace(' ', ';').split(';'))
56+
existing_file = FICHIER.query.filter_by(nom_Fichier=filename).first()
57+
force = json_data.get("force")
58+
if existing_file is not None:
59+
if force:
60+
delete_file(str(existing_file.id_Fichier))
61+
else:
62+
return jsonify({'filename': filename,
63+
'existingFolder': existing_file.DOSSIER_.nom_Dossier,
64+
'attemptedFolder': DOSSIER.query.get(folder_id).nom_Dossier,
65+
'existingFileAuthorFirstName': existing_file.AUTEUR.prenom_Utilisateur,
66+
'existingFileAuthorLastName': existing_file.AUTEUR.nom_Utilisateur,
67+
'existingFileDate': existing_file.date_Fichier.strftime('%d/%m/%Y à %H:%M')}), 409
68+
user_tags = unidecode(' '.join(json_data.get("tags").replace(' ', ';').split(';'))).lower()
5669
storage_directory = os.path.join(current_app.root_path, "storage")
5770
if not os.path.exists(f"{storage_directory}/{folder_id}"):
5871
os.makedirs(f"{storage_directory}/{folder_id}")
5972
file = FICHIER(
6073
id_Dossier=folder_id,
6174
nom_Fichier=filename,
6275
extension_Fichier=filename.split(".")[-1],
76+
id_Utilisateur=current_user.id_Utilisateur,
6377
)
6478
db.session.add(file)
6579
db.session.commit()
@@ -98,33 +112,6 @@ def connect():
98112
)
99113

100114

101-
@socketio.on("trash_file", namespace="/administration")
102-
def trash_file(data):
103-
try:
104-
file_id = data.get("fileId")
105-
folder_id = data.get("folderId")
106-
with InterProcessLock(f"{current_app.root_path}/whoosh.lock"):
107-
Whoosh().delete_document(file_id)
108-
file = FICHIER.query.get(file_id)
109-
db.session.delete(file)
110-
os.remove(
111-
os.path.join(
112-
current_app.root_path,
113-
"storage",
114-
folder_id,
115-
f"{file_id}.{file.extension_Fichier}",
116-
)
117-
)
118-
db.session.commit()
119-
socketio.emit("file_deleted", data, namespace="/administration")
120-
except Exception as e:
121-
socketio.emit(
122-
"file_deletion_failed",
123-
{**data, "error": str(e)},
124-
namespace="/administration",
125-
)
126-
127-
128115
@socketio.on("search_files", namespace="/administration")
129116
def search_files(data):
130117
search_query = data.get("query")
@@ -203,8 +190,11 @@ def create_folder(data):
203190
parent_folder_id = data.get('parentFolderId')
204191
folder_roles = data.get('folderRoles')
205192
folder_color = data.get('folderColor')
206-
last_priority = db.session.query(db.func.max(DOSSIER.priorite_Dossier)).scalar()
207-
folder = DOSSIER(nom_Dossier=folder_name, priorite_Dossier=last_priority + 1, couleur_Dossier=folder_color)
193+
folder_priority = data.get('folderPriority')
194+
folders_to_update = DOSSIER.query.filter(DOSSIER.priorite_Dossier >= folder_priority).filter(DOSSIER.id_Dossier != 10).all()
195+
for folder_to_update in folders_to_update:
196+
folder_to_update.priorite_Dossier += 1
197+
folder = DOSSIER(nom_Dossier=folder_name, priorite_Dossier=folder_priority, couleur_Dossier=folder_color)
208198
db.session.add(folder)
209199
try:
210200
db.session.commit()
@@ -244,11 +234,25 @@ def modify_folder(data):
244234
folder_name = data.get('folderName')
245235
parent_folder_id = data.get('parentFolderId')
246236
folder_roles = data.get('folderRoles')
237+
folder_priority = data.get('folderPriority')
247238
folder_color = data.get('folderColor')
248239
folder = DOSSIER.query.get(folder_id)
249240
folder.nom_Dossier = folder_name
250241
folder.couleur_Dossier = folder_color
242+
if folder.priorite_Dossier != int(folder_priority):
243+
if int(folder_priority) > folder.priorite_Dossier:
244+
folders_to_update = DOSSIER.query.filter(DOSSIER.priorite_Dossier > folder.priorite_Dossier).filter(DOSSIER.priorite_Dossier <= int(folder_priority)).filter(DOSSIER.id_Dossier != 10).all()
245+
for folder_to_update in folders_to_update:
246+
folder_to_update.priorite_Dossier -= 1
247+
else:
248+
folders_to_update = DOSSIER.query.filter(DOSSIER.priorite_Dossier >= int(folder_priority)).filter(DOSSIER.priorite_Dossier < folder.priorite_Dossier).filter(DOSSIER.id_Dossier != 10).all()
249+
for folder_to_update in folders_to_update:
250+
folder_to_update.priorite_Dossier += 1
251+
folder.priorite_Dossier = folder_priority
251252
try:
253+
if parent_folder_id == folder_id:
254+
socketio.emit('folder_not_modified', {'error': 'Un classeur ne peut pas être son propre parent.'}, namespace='/administration')
255+
return
252256
if parent_folder_id != 0:
253257
db.session.execute(SOUS_DOSSIER.delete().where(SOUS_DOSSIER.c.id_Dossier_Enfant == folder_id))
254258
db.session.execute(SOUS_DOSSIER.insert().values(id_Dossier_Parent=parent_folder_id, id_Dossier_Enfant=folder_id))
@@ -277,4 +281,103 @@ def modify_folder(data):
277281
socketio.emit('folder_not_modified', {'error': str(e)}, namespace='/administration')
278282
return
279283
db.session.commit()
280-
socketio.emit('folder_modified', {'folderId': folder_id, 'folderName': folder_name, 'folderColor': folder_color}, namespace='/administration')
284+
socketio.emit('folder_modified', {'folderId': folder_id, 'folderName': folder_name, 'folderColor': folder_color}, namespace='/administration')
285+
286+
@socketio.on('delete_folder', namespace='/administration')
287+
def delete_folder(data):
288+
folder = DOSSIER.query.get(data.get('folderId'))
289+
if folder.id_Dossier == 10:
290+
socketio.emit('folder_not_deleted', {'error': 'Le classeur d\'archive ne peut pas être supprimé.'}, namespace='/administration')
291+
return
292+
if len(folder.DOSSIER_) > 0:
293+
socketio.emit('folder_not_deleted', {'error': 'Le classeur contient des sous-classeurs.'}, namespace='/administration')
294+
return
295+
if len(folder.FICHIER) > 0:
296+
socketio.emit('folder_not_deleted', {'error': 'Le classeur contient des fichiers.'}, namespace='/administration')
297+
return
298+
try:
299+
db.session.execute(SOUS_DOSSIER.delete().where(SOUS_DOSSIER.c.id_Dossier_Enfant == folder.id_Dossier))
300+
db.session.execute(SOUS_DOSSIER.delete().where(SOUS_DOSSIER.c.id_Dossier_Parent == folder.id_Dossier))
301+
db.session.commit()
302+
except Exception as e:
303+
db.session.rollback()
304+
socketio.emit('folder_not_deleted', {'error': str(e)}, namespace='/administration')
305+
return
306+
try:
307+
db.session.execute(A_ACCES.delete().where(A_ACCES.c.id_Dossier == folder.id_Dossier))
308+
db.session.commit()
309+
except Exception as e:
310+
db.session.rollback()
311+
socketio.emit('folder_not_deleted', {'error': str(e)}, namespace='/administration')
312+
return
313+
deleted_priority = folder.priorite_Dossier
314+
db.session.delete(folder)
315+
db.session.commit()
316+
folders_to_update = DOSSIER.query.filter(DOSSIER.priorite_Dossier > deleted_priority).filter(DOSSIER.id_Dossier != 10).all()
317+
for folder_to_update in folders_to_update:
318+
folder_to_update.priorite_Dossier -= 1
319+
db.session.commit()
320+
socketio.emit('folder_deleted', {'folderId': folder.id_Dossier}, namespace='/administration')
321+
322+
323+
@socketio.on('archive_folders', namespace='/administration')
324+
def archive_folders(data):
325+
folder_ids = data.get('folderIds')
326+
try:
327+
for folder_id in folder_ids:
328+
database_folder = DOSSIER.query.get(folder_id)
329+
if folder_id == '10':
330+
pass
331+
elif database_folder.DOSSIER != [] and database_folder.DOSSIER[0].id_Dossier == 10:
332+
pass
333+
elif database_folder.DOSSIER != [] and str(database_folder.DOSSIER[0].id_Dossier) in folder_ids:
334+
pass
335+
else:
336+
if database_folder.DOSSIER != []:
337+
db.session.execute(SOUS_DOSSIER.delete().where(SOUS_DOSSIER.c.id_Dossier_Enfant == folder_id))
338+
db.session.execute(SOUS_DOSSIER.insert().values(id_Dossier_Parent=10, id_Dossier_Enfant=folder_id))
339+
db.session.commit()
340+
except Exception as e:
341+
db.session.rollback()
342+
socketio.emit('folders_not_archived', {'error': str(e)}, namespace='/administration')
343+
return
344+
socketio.emit('folders_archived', {'folderIds': folder_ids}, namespace='/administration')
345+
346+
@socketio.on('delete_files', namespace='/administration')
347+
def delete_files(data):
348+
file_ids = data.get('fileIds')
349+
try:
350+
for file_id in file_ids:
351+
with InterProcessLock(f"{current_app.root_path}/whoosh.lock"):
352+
Whoosh().delete_document(file_id)
353+
database_file = FICHIER.query.get(file_id)
354+
db.session.delete(database_file)
355+
os.remove(
356+
os.path.join(
357+
current_app.root_path,
358+
"storage",
359+
str(database_file.id_Dossier),
360+
f"{file_id}.{database_file.extension_Fichier}",
361+
)
362+
)
363+
db.session.commit()
364+
except Exception as e:
365+
db.session.rollback()
366+
socketio.emit('files_not_deleted', {'error': str(e)}, namespace='/administration')
367+
return
368+
socketio.emit('files_deleted', {'fileIds': file_ids}, namespace='/administration')
369+
370+
def delete_file(file_id):
371+
with InterProcessLock(f"{current_app.root_path}/whoosh.lock"):
372+
Whoosh().delete_document(file_id)
373+
database_file = FICHIER.query.get(file_id)
374+
db.session.delete(database_file)
375+
os.remove(
376+
os.path.join(
377+
current_app.root_path,
378+
"storage",
379+
str(database_file.id_Dossier),
380+
f"{file_id}.{database_file.extension_Fichier}",
381+
)
382+
)
383+
db.session.commit()

app/file_handler/routes.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
import time
2+
import os
13
from app.file_handler import bp
2-
from flask import send_from_directory, abort, current_app, request
4+
from flask import send_from_directory, abort, current_app, request, send_file
35
from app.models.fichier import FICHIER
46
from flask_login import current_user, login_required
57
from app import socketio
68
from app.extensions import db
79
from app.models.favoris import FAVORIS
10+
from app.utils import FileDownloader
11+
from app.decorators import admin_required
12+
from threading import Timer
813

9-
@bp.route('/dossier/<int:folder_id>/fichier/<int:file_id>', methods=['GET'])
14+
@bp.route('/classeur/<int:folder_id>/fichier/<int:file_id>', methods=['GET'])
1015
@login_required
1116
def file(folder_id, file_id):
1217
file = FICHIER.query.get(file_id)
@@ -32,4 +37,21 @@ def get_files_details(data):
3237
dict_file['id_Dossier'] = file.DOSSIER_.id_Dossier
3338
dict_file['is_favorite'] = file.id_Fichier in favoris_ids
3439
files.append(dict_file)
35-
socketio.emit('files_details', files, namespace='/file_handler')
40+
socketio.emit('files_details', files, namespace='/file_handler')
41+
42+
43+
@bp.route('/download/archive', methods=['POST'])
44+
@login_required
45+
@admin_required
46+
def download_archive():
47+
zip_path = FileDownloader().create_zip(request.json['fileIds'])
48+
delete_file_after_delay(43200, zip_path)
49+
return send_file(zip_path, 'archive.zip', as_attachment=True)
50+
51+
def delete_file_after_delay(delay, path):
52+
def delete_file():
53+
try:
54+
os.remove(path)
55+
except FileNotFoundError:
56+
pass
57+
Timer(delay, delete_file).start()

app/models/fichier.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
from typing import List, Optional
2-
from sqlalchemy import Integer, String, ForeignKeyConstraint, Index
2+
from sqlalchemy import Integer, String, ForeignKeyConstraint, Index, DateTime
33
from sqlalchemy.orm import relationship, mapped_column, Mapped
44
from app.extensions import db
55

66
class FICHIER(db.Model):
77
__tablename__ = 'FICHIER'
88
__table_args__ = (
99
ForeignKeyConstraint(['id_Dossier'], ['DOSSIER.id_Dossier'], name='fk_Fichier_Dossier'),
10-
Index('fk_Fichier_Dossier', 'id_Dossier')
10+
ForeignKeyConstraint(['id_Utilisateur'], ['UTILISATEUR.id_Utilisateur'], name='fk_Fichier_Utilisateur'),
11+
Index('fk_Fichier_Dossier', 'id_Dossier'),
12+
Index('fk_Fichier_Utilisateur', 'id_Utilisateur')
1113
)
1214

1315
id_Fichier = mapped_column(Integer, primary_key=True)
1416
id_Dossier = mapped_column(Integer)
1517
nom_Fichier = mapped_column(String(255))
1618
extension_Fichier = mapped_column(String(255))
19+
date_Fichier = mapped_column(DateTime, default=db.func.current_timestamp())
20+
id_Utilisateur = mapped_column(Integer)
1721

1822
def to_dict(self):
1923
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
2024

2125
DOSSIER_: Mapped[Optional['DOSSIER']] = relationship('DOSSIER', back_populates='FICHIER')
2226
UTILISATEUR_: Mapped[List['UTILISATEUR']] = relationship('UTILISATEUR', secondary='FAVORIS', back_populates='FICHIER_')
27+
AUTEUR: Mapped[Optional['UTILISATEUR']] = relationship('UTILISATEUR', back_populates='FICHIER_')
2328
A_TAG: Mapped[List['A_TAG']] = relationship('A_TAG', uselist=True, back_populates='FICHIER_')
2429
NOTIFICATION: Mapped[List['NOTIFICATION']] = relationship('NOTIFICATION', uselist=True, back_populates='FICHIER_')

app/models/utilisateur.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def to_dict(self):
2727
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
2828

2929
def is_admin(self):
30-
return self.id_Role == 1
30+
return self.id_Role != 4
3131

3232
FICHIER_: Mapped[List['FICHIER']] = relationship('FICHIER', secondary='FAVORIS', back_populates='UTILISATEUR_')
3333
ROLE_: Mapped[Optional['ROLE']] = relationship('ROLE', back_populates='UTILISATEUR')

0 commit comments

Comments
 (0)