diff --git a/main.py b/main.py index b62c3aa..d5c06d1 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,4 @@ -from flask import render_template, redirect, url_for, session, g, request, flash, abort +from flask import render_template, redirect, url_for, session, g, request, flash, abort, Response from flask_restful import Api from markdown import markdown from datetime import datetime, timedelta @@ -265,8 +265,8 @@ def upload_questions(): with form.file.data.stream as ff: x = json.load(ff) for question in x: - q = Question(id=question['num'], title=question['statement'], text=question['extra'], - points=question['points'], number=question['num'] * 10) + q = Question(id=question['num'], title=question['statement'], points=question['points'], + text=question['extra'] or None, number=question['num'] * 10) for i, answer in enumerate(question['choices']): a = Answer(text=answer, is_correct=(i == question['correct']), question_id=question['num']) @@ -274,6 +274,9 @@ def upload_questions(): db.session.add(q) db.session.commit() flash('Успешно импортировано!', 'success') + else: + for err in form.file.errors: + flash(err) return redirect(url_for('manage_questions')) @@ -290,5 +293,23 @@ def delete_question(question_id): return redirect(back('manage_questions')) +@app.route('/admin/questions/export') +@admin_required +def export_questions(): + questions = Question.query.all() + data = [] + for q in questions: + d = {'num': q.id, 'statement': q.title, 'extra': q.text or '', 'choices': [], + 'points': q.points} + for i, a in enumerate(q.answers): + d['choices'].append(a.text) + if a.is_correct: + d['correct'] = i + data.append(d) + json_data = json.dumps(data, indent=4) + return Response(response=json_data, mimetype='application/json', + headers={'Content-Disposition': 'attachment; filename="tasks.json"'}) + + if __name__ == '__main__': app.run() diff --git a/static/css/style.css b/static/css/style.css index 2631edf..c082480 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,6 +1,6 @@ @import url('https://fonts.googleapis.com/css?family=Roboto'); -.question-card, .mb4 { +.question-card, .bottom-space { margin-bottom: 4rem; } diff --git a/static/js/script.js b/static/js/script.js index 6ff6074..733bbcf 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -33,6 +33,13 @@ function uploadAllAnswers() { } } +function destructive_confirm(ev) { + if (!confirm('Это — деструктивная операция. Продолжить?')) { + ev.preventDefault(); + ev.stopPropagation(); + } +} + $('textarea').on('input', function (){ $(this).height(0).height(this.scrollHeight); }); @@ -41,11 +48,7 @@ $(document).ready(function () { $('textarea').trigger('input'); }); -$('.destructive-confirm').click(function (ev) { - if (!confirm('Это — деструктивная операция. Продолжить?')) { - ev.preventDefault(); - ev.stopPropagation(); - } -}); +$('a.destructive-confirm').click(destructive_confirm); +$('form.destructive-confirm').submit(destructive_confirm); $('#noscript').hide(); diff --git a/templates/question_form.html b/templates/question_form.html index cac08a8..646ab34 100644 --- a/templates/question_form.html +++ b/templates/question_form.html @@ -1,4 +1,4 @@ -