diff options
author | Nico Geyso <nico.geyso@fu-berlin.de> | 2014-12-09 23:59:54 +0100 |
---|---|---|
committer | Nico Geyso <nico.geyso@fu-berlin.de> | 2014-12-09 23:59:54 +0100 |
commit | 274d1790239f52440137cc922018a87af32a4ab7 (patch) | |
tree | 43149197b724f95af49c483194f2498b13f10ae2 /app | |
parent | 79788b3590aba6384611a2c29012bbd821c290ee (diff) | |
download | klausuren-274d1790239f52440137cc922018a87af32a4ab7.tar.gz klausuren-274d1790239f52440137cc922018a87af32a4ab7.tar.bz2 klausuren-274d1790239f52440137cc922018a87af32a4ab7.zip |
Refactoring
Diffstat (limited to 'app')
-rw-r--r-- | app/__init__.py | 48 | ||||
-rw-r--r-- | app/backend.py | 56 | ||||
-rw-r--r-- | app/exts.py | 4 | ||||
-rw-r--r-- | app/forms.py | 30 | ||||
-rw-r--r-- | app/main.py | 114 | ||||
-rw-r--r-- | app/static/style.css (renamed from app/static/style_v3.css) | 11 | ||||
-rw-r--r-- | app/templates/403.html | 2 | ||||
-rw-r--r-- | app/templates/404.html | 16 | ||||
-rw-r--r-- | app/templates/_layout.html (renamed from app/templates/layout.html) | 19 | ||||
-rw-r--r-- | app/templates/courses.html (renamed from app/templates/module_list.html) | 6 | ||||
-rw-r--r-- | app/templates/exams.html | 29 | ||||
-rw-r--r-- | app/templates/index.html | 5 | ||||
-rw-r--r-- | app/templates/module_show.html | 30 | ||||
-rw-r--r-- | app/templates/upload.html | 32 |
14 files changed, 205 insertions, 197 deletions
diff --git a/app/__init__.py b/app/__init__.py index 6c6b08c..7022d2b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -2,10 +2,6 @@ from flask import Flask, render_template, g from .main import main -from .backend import Storage -import sys -reload(sys) -sys.setdefaultencoding('utf-8') def create_app(config=None): """Creates the Flask app.""" @@ -13,10 +9,8 @@ def create_app(config=None): configure_app(app) configure_error_handlers(app) - init_app(app) - for blueprint in [main]: - app.register_blueprint(blueprint) + app.register_blueprint(main) return app @@ -36,43 +30,13 @@ def configure_app(app): app.logger.addHandler(file_handler) -def init_app(app): - import os - @app.before_request - def init(): - g.studies = {} - for i, study in enumerate(app.config['STUDIES'].items()): - abbr = study[0] - g.studies[abbr] = Storage(os.path.join('app', 'static','studies', abbr)) - - modules = app.config['STUDIES'][study[0]] - # extend module list with git values - for module in g.studies[abbr].get_modules(): - # check if module is already listed - if all(map(lambda (k,v): v != module, modules)): - slug = module.decode('ascii', errors='ignore') - app.config['STUDIES'][study[0]].append((slug, module)) - - -## populate Module-List -#fit = {} -#for i, study in enumerate(app.config['STUDIES'].items()): -# abbr = study[0] -# fit[abbr] = Fit(os.path.join('static','studies',abbr + '.git')) -# -# modules = app.config['STUDIES'][study[0]] -# # extend module list with git values -# for module in fit[abbr].get_modules(): -# # check if module is already listed -# if all(map(lambda (k,v): v != module, modules)): -# slug = module.decode('ascii', errors='ignore') -# app.config['STUDIES'][study[0]].append((slug, module)) - def configure_error_handlers(app): @app.route('/forbidden') - @app.errorhandler(400) @app.errorhandler(403) + def forbidden(e): + return render_template('403.html', error=e), e.code + + @app.errorhandler(400) @app.errorhandler(404) def errorhandler(e): - return render_template('error.html', error=e), e.code - + return render_template('404.html', error=e), e.code diff --git a/app/backend.py b/app/backend.py index 6c59edd..0dbebb8 100644 --- a/app/backend.py +++ b/app/backend.py @@ -2,38 +2,48 @@ import os import magic +from functools import partial class Storage: - def __init__(self, root_path): - self.root = root_path + def __init__(self, path_rel, path_app): + self.path = path_app + self.root = path_rel def _join(self, *arg): - return os.path.join(self.root, *arg) + return os.path.join(self.path, *arg) - def get_file(self, module, year, name): - with open(self._join(module, year, name), 'r') as f: - data = f.read() - mime = magic.Magic(mime=True) - mime_type = mime.from_buffer(data[:1024]) - return mime_type, data + def get_courses(self): + """ Lists all courses of a study """ + return [o.decode('utf-8') for o in os.listdir(self.path) + if os.path.isdir(self._join(o))] - def get_modules(self): - return [o for o in os.listdir(self.root) if os.path.isdir(self._join(o))] + def exam_exists(self, course, year, name): + """ Exists if an exam (file) exists """ + return os.path.exists(self._join(course, year, name)) - def get_module(self, module): - for root, dirs, files in os.walk(self._join(module)): + def add_exam(self, course, year, filename, data): + # create course dir with year if it does not exist already + dir_name = self._join(course, year) + if not os.path.exists(dir_name): + os.makedirs(dir_name) + + # save exam + path = self._join(course, year, filename) + with open(path, 'wb') as f: + f.write(data) + + def get_exams(self, name): + """ Lists all exams of a given course """ + # loop over all directories which do not contain any subdirs + for root, dirs, files in os.walk(self._join(name)): if len(dirs) == 0: + # metainformation is encoded in path: course/year/exam.pdf splitted = root.split(os.path.sep) if len(splitted) > 1: year = splitted[-1] - module = splitted[-2] + course = splitted[-2] if year.isdigit(): - yield (year, files) - - def add_file(self, module, year, filename, data): - dir_name = self._join(module, year) - if not os.path.exists(dir_name): - os.makedirs(dir_name) - path = self._join(module, year, filename) - with open(path, 'wb') as f: - f.write(data) + # yield entries as tuples (name, path) grouped by years + func = partial(os.path.join, self.root, course, year) + entries = [(f, func(f)) for f in files] + yield (year, entries) diff --git a/app/exts.py b/app/exts.py deleted file mode 100644 index 7d42edc..0000000 --- a/app/exts.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- - -from flask.ext.sqlalchemy import SQLAlchemy -db = SQLAlchemy() diff --git a/app/forms.py b/app/forms.py index e562df2..2332179 100644 --- a/app/forms.py +++ b/app/forms.py @@ -6,36 +6,32 @@ from flask.ext.wtf import Form from wtforms import TextField, FileField, SelectField, validators from wtforms.validators import ValidationError +year_start = date.today().year +year_end = current_app.config['FORM_START_YEAR']-1 +choices = [(str(x),x) for x in xrange(year_start, year_end, -1)] class UploadForm(Form): """ Upload Form class for validation """ study = TextField('Studiengang') exam = FileField('Klausur') - module = SelectField('Kurs') - module_new = TextField('Modulname', validators=[validators.Optional(), - validators.Length(min=5)]) - year = SelectField( - 'Jahr', - validators=[validators.Required()], - choices = [ (str(x),x) for x in - #xrange(date.today().year, current_app.config['FORM_START_YEAR']-1, -1) - xrange(date.today().year, 2000, -1) - ] - ) + course = SelectField('Kurs') + course_new = TextField('Modulname', validators=[validators.Optional(), + validators.Length(min=5)]) + year = SelectField('Jahr', validators=[validators.Required()], + choices = choices) def validate_exam(form, field): exts = current_app.config['ALLOWED_EXTENSIONS'] ext = map(field.data.filename.endswith, exts) - if not any(ext): raise ValidationError(u'Ungültiger Dateityp') if field.data.content_length > current_app.config['MAX_CONTENT_LENGTH']: raise ValidationError(u'Zu große Datei') - def validate_module(form, field): - modules = dict(current_app.config['STUDIES'][form.study.data]) - data = form.module.data - if data not in modules or data == '': - raise ValidationError(u'Bitte wähle ein Modul!') + def validate_course(form, field): + courses = dict(current_app.config['STUDIES'][form.study.data]) + data = form.course.data + if data not in courses or data == '': + raise ValidationError(u'Bitte wähle einen Kurs!') diff --git a/app/main.py b/app/main.py index cf6a99e..ef4e549 100644 --- a/app/main.py +++ b/app/main.py @@ -4,32 +4,59 @@ import os, sys from flask import Blueprint, render_template, request, flash, redirect,\ url_for, current_app, g from werkzeug import secure_filename -from .forms import UploadForm +from wtforms.validators import ValidationError +from .backend import Storage main = Blueprint('main', __name__) +def get_studies(): + """ + Add all existing courses of backend to study list. + This list is used to fill form values with courses. + """ + studies = getattr(g, '_studies', None) + if studies is None: + studies = g._studies = {} + it = current_app.config['STUDIES'].items() + for i, (abbr, courses) in enumerate(it): + path_rel = os.path.join('studies', abbr) + path_app = os.path.join('app', 'static', path_rel) + studies[abbr] = Storage(path_rel, path_app) + + # iterate over all courses in our backend + for course in g._studies[abbr].get_courses(): + # check if course is already listed + entry = (course.encode('ascii', errors='ignore'), course) + if entry not in courses: + current_app.config['STUDIES'][abbr].append(entry) + + current_app.config['STUDIES'][abbr].sort() + return studies + @main.route('/<study>/upload/', methods=['GET', 'POST']) -@main.route('/<study>/upload/<module>', methods=['GET', 'POST']) -def upload(study, module = None): +@main.route('/<study>/upload/<course>', methods=['GET', 'POST']) +def upload(study, course = None): + from .forms import UploadForm form = UploadForm() form.study.data = study - form.module.choices = current_app.config['STUDIES'][study] - if 'new' not in dict(form.module.choices): - form.module.choices.append(('', u'---')) - form.module.choices.append(('new', u'neues Modul hinzufügen')) - + # dynamically fill form values + form.course.choices = current_app.config['STUDIES'][study] + if 'new' not in dict(form.course.choices): + form.course.choices.append(('', u'---')) + form.course.choices.append(('new', u'neuen Kurs hinzufügen')) if form.validate_on_submit(): - if form.module.data == 'new': - module = form.module_new.data - slug = module.encode('ascii', errors='ignore') - i = len(current_app.config['STUDIES'][study]) - 2 - current_app.config['STUDIES'][study].insert(i, (slug,module)) + if form.course.data == 'new': + course = form.course_new.data + slug = course.encode('ascii', errors='ignore') + current_app.config['STUDIES'][study].append((slug,course)) + current_app.config['STUDIES'][study].sort() + else: - module = dict(current_app.config['STUDIES'][study])[form.module.data] + course = dict(current_app.config['STUDIES'][study])[form.course.data] year = form.year.data filename = secure_filename(form.exam.data.filename) @@ -38,45 +65,42 @@ def upload(study, module = None): except: data = form.exam.data.stream.read() - g.studies[study].add_file(module, year, filename, data) - flash("Datei %s gespeichert." % filename) - - return redirect(url_for('.study_index', study = study, module = module)) - - try: form.module.data = [k for (k,v) in form.module.choices if v == module][0] - except: pass - - return render_template('upload.html', - study = study, form = form, module=module) - + backend = get_studies()[study] + if not backend.exam_exists(course, year, filename): + backend.add_exam(course, year, filename, data) + flash(u'Datei %s gespeichert.' % filename) + return redirect(url_for('.courses_show', study = study, course = course)) + else: + flash(u'Datei mit gleichem Namen existiert schon!', 'error') + try: + form.course.data = [k for (k,v) in form.course.choices if v == course][0] + except: + pass -@main.route('/<study>/files/<module>/<year>/<filename>') -def study_show(study, module, year, filename): - mime_type, data = g.studies[study].get_file(module,year,filename) - header = { 'Content-Type' : mime_type } - return data, 200, header + return render_template('upload.html', study = study, form = form, + course = course) -@main.route('/<study>/modules/') -@main.route('/<study>/modules/<module>') -def study_index(study, module=None): - if module: - entries = sorted(g.studies[study].get_module(module), reverse=True) - return render_template('module_show.html', - study = study, module=module, entries=entries - ) +@main.route('/<study>/courses/') +@main.route('/<study>/courses/<course>') +def courses_show(study, course = None): + """ Lists all courses or exams for a course """ + backend = get_studies()[study] + if course: + entries = sorted(backend.get_exams(course), reverse = True) + return render_template('exams.html', study = study, course = course, + entries=entries) - modules = g.studies[study].get_modules() - return render_template('module_list.html', study = study, modules=modules) + courses = sorted(backend.get_courses()) + return render_template('courses.html', study = study, courses = courses) @main.route('/') def index(): + """ Lists all course of studies """ get_img_path = lambda x: os.path.join('studies', x, 'logo.png') - studies = [(name,get_img_path(name)) for name,m in current_app.config['STUDIES'].items()] + it = current_app.config['STUDIES'].items() + studies = [(name, get_img_path(name)) for name,m in it] - return render_template( - 'index.html', - studies = studies - ) + return render_template('index.html', studies = studies) diff --git a/app/static/style_v3.css b/app/static/style.css index 6907658..7075a7a 100644 --- a/app/static/style_v3.css +++ b/app/static/style.css @@ -86,11 +86,16 @@ ul.flashes { ul.flashes li { list-style-type: none; - border: 2px solid green; - background-color: #0D5; padding: 5px; padding: 0px; text-align: center; + border: 2px solid green; + background-color: #0D5; +} + +ul.flashes li.error { + border: 2px solid red; + background-color: rgb(255, 116, 116); } a, a:visited { @@ -123,7 +128,7 @@ select { background-color: #fff; } -#module_new { +#course_new { width: 200px; margin-left: 10px; padding: 5px 5px; diff --git a/app/templates/403.html b/app/templates/403.html index 5f2317f..1569421 100644 --- a/app/templates/403.html +++ b/app/templates/403.html @@ -1,4 +1,4 @@ -{% extends "layout.html" %} +{% extends "_layout.html" %} {% block body %} <h2>Fehler 403 - Zugang verboten</h2> diff --git a/app/templates/404.html b/app/templates/404.html new file mode 100644 index 0000000..a1526ff --- /dev/null +++ b/app/templates/404.html @@ -0,0 +1,16 @@ +{% extends "_layout.html" %} +{% block body %} +<h2>Fehler 403 - Zugang verboten</h2> + +<dl> + <dt>Code</dt> + <dd>{{error.code}}</dd> + + <dt>Beschreibung</dt> + <dd>{{error.description}}</dd> +</dl> + +<p> + <a href="{{url_for('main.index')}}">zur Startseite</a> +</p> +{% endblock %} diff --git a/app/templates/layout.html b/app/templates/_layout.html index 14ec7c5..5eaef3f 100644 --- a/app/templates/layout.html +++ b/app/templates/_layout.html @@ -2,8 +2,7 @@ <html> <head> <meta charset="utf-8" /> - <link rel="stylesheet" type="text/css" media="all" href="{{url_for('static', - filename='style_v3.css')}}" /> + <link rel="stylesheet" type="text/css" media="all" href="{{url_for('static', filename='style.css')}}" /> <script type="text/javascript" src="{{url_for('static',filename='jquery-1.8.0.min.js')}}"></script> <title>Fit</title> </head> @@ -17,21 +16,21 @@ <div id="sub-header"> {% if study %} <p> - {% if not request.base_url.endswith(url_for('.upload', study=study, module = module))%} - {% if not request.base_url.endswith(url_for('errorhandler')) %} - <a href="{{url_for('.upload', study=study, module=module)}}">neue Klausur hochladen</a> + {% if not request.base_url.endswith(url_for('.upload', study=study, course= course))%} + {% if not request.base_url.endswith(url_for('forbidden')) %} + <a href="{{url_for('.upload', study=study, course=course)}}">neue Klausur hochladen</a> {% endif %} {% else %} - <a href="{{url_for('.study_index', study=study, module=module)}}">zurück</a> + <a href="{{url_for('.courses_show', study=study, course=course)}}">zurück</a> {% endif %} </p> - <h2><a href="{{url_for('.study_index', study=study)}}">{{study.capitalize()}}</a></h2> + <h2><a href="{{url_for('.courses_show', study=study)}}">{{study.capitalize()}}</a></h2> {% endif %} <ul class="flashes"> - {% with messages = get_flashed_messages() %} - {% for message in messages %} - <li>{{ message }}</li> + {% with messages = get_flashed_messages(with_categories=true) %} + {% for category, message in messages %} + <li class="{{ category }}">{{ message }}</li> {% endfor %} {% endwith %} </ul> diff --git a/app/templates/module_list.html b/app/templates/courses.html index 9469020..f315e11 100644 --- a/app/templates/module_list.html +++ b/app/templates/courses.html @@ -1,11 +1,11 @@ -{% extends "layout.html" %} +{% extends "_layout.html" %} {% block body %} <h3>Klausuren</h3> <ul> - {% for module in modules %} + {% for course in courses %} <li> - <a href="{{url_for('.study_index', study = study, module=module)}}">{{module}}</a> + <a href="{{url_for('.courses_show', study = study, course=course)}}">{{course}}</a> </li> {% else %} <li>Keine Klausuren bisher hochgeladen!</li> diff --git a/app/templates/exams.html b/app/templates/exams.html new file mode 100644 index 0000000..7e4af54 --- /dev/null +++ b/app/templates/exams.html @@ -0,0 +1,29 @@ +{% macro render_courses_list(courses, entries) %} +<ul> +{% for year,files in entries %} + <li> + {{year}} + <ul> + {% for name, path in files %} + <li> + <a href="{{url_for('static', filename = path)}}"> + {{name|truncate(40,True)}} + </a> + </li> + {% endfor %} + </ul> + </li> +{% else %} + <li>Keine Einträge bisher</li> +{% endfor %} +</ul> +{% endmacro %} + +{% extends "_layout.html" %} +{% block body %} + <div id="exams"> + <h3>{{courses}}</h3> + + {{ render_courses_list(courses, entries)}} + </div> +{% endblock %} diff --git a/app/templates/index.html b/app/templates/index.html index 9d090c3..8ab2c06 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,4 +1,4 @@ -{% extends "layout.html" %} +{% extends "_layout.html" %} {% block body %} <h2>Studiengänge</h2> @@ -6,7 +6,7 @@ <ul id="studies"> {% for name,img_path in studies|sort %} <li> - <a href="{{url_for('main.study_index', study=name)}}"> + <a href="{{url_for('main.courses_show', study=name)}}"> <img src="{{url_for('static',filename=img_path)}}" alt="{{name}}" /> {{name.capitalize()}} </a> @@ -16,5 +16,4 @@ {% endfor %} </ul> </div> - {% endblock %} diff --git a/app/templates/module_show.html b/app/templates/module_show.html deleted file mode 100644 index e31da24..0000000 --- a/app/templates/module_show.html +++ /dev/null @@ -1,30 +0,0 @@ -{% macro render_module_list(module, entries) %} -<ul id="module-list"> -{% for year,files in entries %} - <li> - {{year}} - <ul> - {% for name in files %} - <li> - <a href="{{url_for('.study_show', study = study, module=module, year=year, filename=name)}}"> - {{name|truncate(40,True)}} - </a> - </li> - {% endfor %} - </ul> - </li> -{% else %} - <li>Keine Einträge bisher</li> -{% endfor %} -</ul> -{% endmacro %} - -{% extends "layout.html" %} -{% block body %} - <div id="module-index"> - <h3>{{module}}</h3> - - {{ render_module_list(module, entries)}} - </div> - -{% endblock %} diff --git a/app/templates/upload.html b/app/templates/upload.html index 0e00354..e19a113 100644 --- a/app/templates/upload.html +++ b/app/templates/upload.html @@ -20,17 +20,17 @@ {{ render_fields(field, None) }} {% endmacro %} -{% extends "layout.html" %} +{% extends "_layout.html" %} {% block body %} <h3>neue Klausur hochladen</h3> <div id="upload"> <form method="POST" enctype="multipart/form-data" - action="{{url_for('.upload', study=study, module=module)}}"> + action="{{url_for('.upload', study=study, course=course)}}"> {{ form.csrf_token }} {{ render_field(form.exam) }} - {{ render_fields(form.module, form.module_new, placeholder='Modulname') }} + {{ render_fields(form.course, form.course_new, placeholder='Modulname') }} {{ render_field(form.year) }} <p> @@ -38,39 +38,39 @@ </p> </form> - <div id="module-index"></div> + <div id="exams"></div> </div> <script type="text/javascript"> $(document).ready(function() { - var elem = $('#module_new'); - var module = $('#module'); + var elem = $('#course_new'); + var course = $('#course'); - var update_modules = function() { - var text = module.find("option:selected").text(); - $.get('/{{study}}/modules/' + text, function(data) { - $('#module-index').replaceWith($(data).find('#module-index')); + var update_courses = function() { + var text = course.find("option:selected").text(); + $.get('/{{study}}/courses/' + text, function(data) { + $('#exams').replaceWith($(data).find('#exams')); }); }; - update_modules(); + update_courses(); - if(module.val() != 'new') + if(course.val() != 'new') elem.hide(); - module.change(function(){ + course.change(function(){ var current = $(this); var value = current.val(); var text = current.find("option:selected").text(); if(value == 'new') { - $('#module-index').fadeOut('fast', function() { elem.fadeIn(); }); + $('#exams').fadeOut('fast', function() { elem.fadeIn(); }); } else { elem.fadeOut('fast', function() { if(value != '') - update_modules(); + update_courses(); else - $('#module-index').fadeOut('fast'); + $('#exams').fadeOut('fast'); }); } |