diff --git a/ref-test/app/admin/views.py b/ref-test/app/admin/views.py index c2adfcb..f35b028 100644 --- a/ref-test/app/admin/views.py +++ b/ref-test/app/admin/views.py @@ -5,12 +5,13 @@ from ..tools.forms import get_dataset_choices, get_time_options, send_errors_to_ from ..tools.data import check_is_json, validate_json from ..tools.test import answer_options, get_correct_answers -from flask import Blueprint, jsonify, render_template, redirect, request, session +from flask import abort, Blueprint, jsonify, render_template, redirect, request, send_file, session from flask.helpers import flash, url_for from flask_login import current_user, login_required from datetime import date, datetime from json import loads +from os import path import secrets admin = Blueprint( @@ -207,10 +208,13 @@ def _questions(): if form.validate_on_submit(): upload = form.data_file.data if not check_is_json(upload): return jsonify({'error': 'Invalid file. Please upload a JSON file.'}), 400 - if not validate_json(upload): return jsonify({'error': 'The data in the file is invalid.'}), 400 + upload.stream.seek(0) + data = loads(upload.read()) + if not validate_json(data=data): return jsonify({'error': 'The data in the file is invalid.'}), 400 new_dataset = Dataset() + new_dataset.set_name(request.form.get('name')) success, message = new_dataset.create( - upload = upload, + data = data, default = request.form.get('default') ) if success: return jsonify({'success': message}), 200 @@ -220,18 +224,25 @@ def _questions(): data = Dataset.query.all() return render_template('/admin/settings/questions.html', form=form, data=data) -@admin.route('/settings/questions/edit/', methods=['POST']) +@admin.route('/settings/questions/delete/', methods=['POST']) @login_required def _edit_questions(): id = request.get_json()['id'] action = request.get_json()['action'] - if action not in ['defailt', 'delete']: return jsonify({'error': 'Invalid action.'}), 400 + if not action == 'delete': return jsonify({'error': 'Invalid action.'}), 400 dataset = Dataset.query.filter_by(id=id).first() if action == 'delete': success, message = dataset.delete() - elif action == 'default': success, message = dataset.make_default() if success: return jsonify({'success': message}), 200 return jsonify({'error': message}), 400 +@admin.route('/settings/questions/download//') +@login_required +def _download(id:str): + dataset = Dataset.query.filter_by(id=id).first() + if not dataset: return abort(404) + data_path = path.abspath(dataset.get_file()) + return send_file(data_path, as_attachment=True, attachment_filename=f'{dataset.get_name()}.json') + @admin.route('/tests//', methods=['GET']) @admin.route('/tests/', methods=['GET']) @login_required diff --git a/ref-test/app/models/dataset.py b/ref-test/app/models/dataset.py index d021e0f..d3dc485 100644 --- a/ref-test/app/models/dataset.py +++ b/ref-test/app/models/dataset.py @@ -1,4 +1,5 @@ from ..extensions import db +from ..tools.encryption import decrypt, encrypt from ..tools.logs import write from flask import flash @@ -7,7 +8,7 @@ from flask_login import current_user from werkzeug.utils import secure_filename from datetime import datetime -from json import dump, loads +from json import dump from os import path, remove from pathlib import Path from uuid import uuid4 @@ -15,13 +16,16 @@ from uuid import uuid4 class Dataset(db.Model): id = db.Column(db.String(36), primary_key=True) + name = db.Column(db.String(128), nullable=False) tests = db.relationship('Test', backref='dataset') creator_id = db.Column(db.String(36), db.ForeignKey('user.id')) date = db.Column(db.DateTime, nullable=False) default = db.Column(db.Boolean, default=False, nullable=True) + accessed = db.Column(db.DateTime, nullable=True) + locked = db.Column(db.Boolean, default=False, nullable=True) def __repr__(self): - return f' was added.' + return f'.' @property def generate_id(self): raise AttributeError('generate_id is not a readable attribute.') @@ -29,6 +33,14 @@ class Dataset(db.Model): generate_id.setter def generate_id(self): self.id = uuid4().hex + @property + def set_name(self): raise AttributeError('set_name is not a readable attribute.') + + set_name.setter + def set_name(self, name:str): self.name = encrypt(name) + + def get_name(self): return decrypt(self.name) + def make_default(self): for dataset in Dataset.query.all(): dataset.default = False @@ -43,7 +55,7 @@ class Dataset(db.Model): message = 'Cannot delete the default dataset.' flash(message, 'error') return False, message - if Dataset.query.all().count() == 1: + if Dataset.query.count() == 1: message = 'Cannot delete the only dataset.' flash(message, 'error') return False, message @@ -56,23 +68,19 @@ class Dataset(db.Model): db.session.commit() return True, 'Dataset deleted.' - def create(self, upload, default:bool=False): + def create(self, data:list, default:bool=False): self.generate_id() timestamp = datetime.now() - filename = secure_filename('.'.join([self.id,'json'])) - data = Path(app.config.get('DATA')) - file_path = path.join(data, 'questions', filename) - upload.stream.seek(0) - questions = loads(upload.read()) + file_path = self.get_file() with open(file_path, 'w') as file: - dump(questions, file, indent=2) + dump(data, file, indent=2) self.date = timestamp self.creator = current_user if default: self.make_default() - write('system.log', f'New dataset {self.id} added by {current_user.get_username()}.') + write('system.log', f'New dataset {self.get_name()} added by {current_user.get_username()}.') db.session.add(self) db.session.commit() - return True, 'Dataset uploaded.' + return True, 'Dataset created.' def check_file(self): filename = secure_filename('.'.join([self.id,'json'])) @@ -85,4 +93,16 @@ class Dataset(db.Model): filename = secure_filename('.'.join([self.id,'json'])) data = Path(app.config.get('DATA')) file_path = path.join(data, 'questions', filename) - return file_path \ No newline at end of file + return file_path + + def update(self, data:list=None, default:bool=False): + self.date = datetime.now() + if default: self.make_default() + file_path = self.get_file() + with open(file_path, 'w') as file: + dump(data, file, indent=2) + write('system.log', f'Dataset {self.id} edited by {current_user.get_username()}.') + flash(f'Dataset {self.name} successfully edited.', 'success') + db.session.add(self) + db.session.commit() + return True, 'Dataset successfully edited.' \ No newline at end of file