summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNico Geyso <nico.geyso@fu-berlin.de>2014-12-09 23:59:54 +0100
committerNico Geyso <nico.geyso@fu-berlin.de>2014-12-09 23:59:54 +0100
commit274d1790239f52440137cc922018a87af32a4ab7 (patch)
tree43149197b724f95af49c483194f2498b13f10ae2
parent79788b3590aba6384611a2c29012bbd821c290ee (diff)
downloadklausuren-274d1790239f52440137cc922018a87af32a4ab7.tar.gz
klausuren-274d1790239f52440137cc922018a87af32a4ab7.tar.bz2
klausuren-274d1790239f52440137cc922018a87af32a4ab7.zip
Refactoring
-rw-r--r--app/__init__.py48
-rw-r--r--app/backend.py56
-rw-r--r--app/exts.py4
-rw-r--r--app/forms.py30
-rw-r--r--app/main.py114
-rw-r--r--app/static/style.css (renamed from app/static/style_v3.css)11
-rw-r--r--app/templates/403.html2
-rw-r--r--app/templates/404.html16
-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.html29
-rw-r--r--app/templates/index.html5
-rw-r--r--app/templates/module_show.html30
-rw-r--r--app/templates/upload.html32
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');
});
}