hanze/muizenval

first commit (e9b1769d171918c95d16d73ec7059376fcf61c67)
Repositories

commit e9b1769d171918c95d16d73ec7059376fcf61c67
Author: Friedel Schön <[email protected]>
Date:   Wed, 20 Apr 2022 12:03:41 +0200

first commit

Diffstat:
A.gitignore2++
Apgmles/forms.py85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/models.py36++++++++++++++++++++++++++++++++++++
Apgmles/routes.py287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/server.py16++++++++++++++++
Apgmles/site.db0
Apgmles/static/.DS_Store0
Apgmles/static/main.css146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/static/profile_pics/2e32b4c96a8d8f10.jpg0
Apgmles/static/profile_pics/43ed4d3be14daf90.jpg0
Apgmles/static/profile_pics/4895353c1045b870.jpg0
Apgmles/static/profile_pics/4c4083a8362b3c0a.jpg0
Apgmles/static/profile_pics/53998538edf2342c.png0
Apgmles/static/profile_pics/6809465dffb2e5ba.jpg0
Apgmles/static/profile_pics/7798432669b8b3ac.jpg0
Apgmles/static/profile_pics/85ed1b444539873d.png0
Apgmles/static/profile_pics/b6e1c53325f88b74.png0
Apgmles/static/profile_pics/default.jpg0
Apgmles/templates/about.html31+++++++++++++++++++++++++++++++
Apgmles/templates/account.html84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/templates/admin.html28++++++++++++++++++++++++++++
Apgmles/templates/admin_user.html58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/templates/course.html38++++++++++++++++++++++++++++++++++++++
Apgmles/templates/course_overview.html49+++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/templates/index.html33+++++++++++++++++++++++++++++++++
Apgmles/templates/layout.html112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/templates/login.html53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/templates/new_course.html106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apgmles/templates/register.html72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Areadme.md79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arun-server.py4++++
Aserver/app.py1+
Atest-client.py0
33 files changed, 1320 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +__pycache__ +\ No newline at end of file diff --git a/pgmles/forms.py b/pgmles/forms.py @@ -0,0 +1,85 @@ +from flask_login import current_user +from flask_wtf import FlaskForm +from flask_wtf.file import FileAllowed, FileField +from wtforms import BooleanField, HiddenField, PasswordField, SelectField, StringField, SubmitField, TextAreaField +from wtforms.validators import DataRequired, Email, EqualTo, Length, ValidationError + +from .models import User + +""" registration form for register.html """ +class RegistrationForm(FlaskForm): + username = StringField('Naam', validators=[ DataRequired(), Length(min=2, max=20) ]) + email = StringField('E-Mail', validators=[ DataRequired(), Email() ]) + password = PasswordField('Wachtwoord', validators=[ DataRequired() ]) + confirm_password = PasswordField('Wachtwoord herhalen', validators=[ DataRequired(), EqualTo('password') ]) + submit = SubmitField('Registeren') + + """ validates whether username is already in use """ + def validate_username(self, username): + if User.query.filter_by(username=username.data).first(): + raise ValidationError('Deze gebruikersnaam bestaat al, kies een andere.') + + """ validates whether e-mail is already in use """ + def validate_email(self, email): + if User.query.filter_by(email=email.data).first(): + raise ValidationError('Deze e-mail bestaat al, log in als dat uw e-mail is') + + +""" login form for login.html """ +class LoginForm(FlaskForm): + email = StringField('E-Mail', validators=[ DataRequired(), Email() ]) + password = PasswordField('Wachtwoord', validators=[ DataRequired() ]) + remember = BooleanField('Herinneren') + submit = SubmitField('Inloggen') + + +""" update account form for account.html """ +class UpdateAccountForm(FlaskForm): + username = StringField('Naam', validators=[ DataRequired(), Length(min=2, max=20) ]) + email = StringField('E-Mail', validators=[ DataRequired(), Email() ]) + password = PasswordField('Wachtwoord', validators=[]) + confirm_password = PasswordField('Wachtwoord herhalen', validators=[ EqualTo('password') ]) + picture = FileField('Profielfoto bewerken', validators=[ FileAllowed(['jpg', 'png']) ]) + submit = SubmitField('Bewerken') + + """ validates whether username is already in use """ + def validate_username(self, username): + if username.data != current_user.username and User.query.filter_by(username=username.data).first(): + raise ValidationError('Deze gebruikersnaam bestaat al, kies een andere.') + + """ validates whether e-mail is already in use """ + def validate_email(self, email): + if email.data != current_user.email and User.query.filter_by(email=email.data).first(): + raise ValidationError('Deze e-mail bestaat al, log in als dat uw e-mail is') + +""" update/new course form for new_course.html """ +class NewCourseForm(FlaskForm): + name = StringField('Naam', validators=[ DataRequired(), Length(min=1, max=100) ]) + description = TextAreaField('Beschrijving', validators=[ DataRequired() ]) + teacher_id = SelectField('Docent', validators=[ DataRequired() ], coerce=int) + weekday = SelectField('Weekdag', choices=list(enumerate([ 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag', 'Zondag' ]))) + start = StringField('Begin', validators=[ DataRequired() ]) + end = StringField('Einde', validators=[ DataRequired() ]) + location = StringField('Locatie', validators=[ DataRequired(), Length(min=1, max=100) ]) + submit = SubmitField('Versturen') + +""" subscribe form for course.html """ +class SubscribeForm(FlaskForm): + lang_id = HiddenField() + submit = SubmitField('Inschrijven') + + +""" unsubscribe form for course.html """ +class UnsubscribeForm(FlaskForm): + lang_id = HiddenField() + submit = SubmitField('Uitschrijven') + +""" search form for admin.html """ +class SearchForm(FlaskForm): + username = StringField('Naam', validators=[ DataRequired(), Length(min=2, max=20)]) + submit = SubmitField('Zoeken') + +""" account-settings form for admin_user.html """ +class AdminForm(FlaskForm): + type = SelectField('Type', choices=[('client', 'Klant'), ('teacher', 'Docent'), ('admin', 'Administrator')]) + submit = SubmitField('Bewerken') diff --git a/pgmles/models.py b/pgmles/models.py @@ -0,0 +1,36 @@ +from datetime import datetime + +from flask_login import UserMixin + +from .server import db, login_manager + +""" function to load a user from database """ +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + +""" user-struct for 'user'-database """ +class User(db.Model, UserMixin): + id = db.Column(db.Integer, primary_key=True) + type = db.Column(db.String(6), nullable=False, default="client") + username = db.Column(db.String(20), unique=True, nullable=False) + email = db.Column(db.String(120), unique=True, nullable=False) + image_file = db.Column(db.String(20), nullable=False, default='default.jpg') + password = db.Column(db.String(60), nullable=False) + +""" course-struct for 'course'-database """ +class Course(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + description = db.Column(db.Text, nullable=False) + teacher_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + weekday = db.Column(db.Integer, nullable=False) + start = db.Column(db.String(10), nullable=False, default=datetime.utcnow) + end = db.Column(db.String(10), nullable=False, default=datetime.utcnow) + location = db.Column(db.String(120), nullable=False) + +""" course-member-struct for 'coursemember'-database """ +class CourseMember(db.Model): + id = db.Column(db.Integer, primary_key=True) + course_id = db.Column(db.Integer, db.ForeignKey('course.id'), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) diff --git a/pgmles/routes.py b/pgmles/routes.py @@ -0,0 +1,287 @@ +import os +import secrets + +from flask import flash, redirect, render_template, request, url_for, abort +from flask_login import current_user, login_required, login_user, logout_user +from PIL import Image +from calendar import Calendar as Month +from datetime import datetime + +from .server import app, bcrypt, db +from .forms import LoginForm, NewCourseForm, AdminForm, RegistrationForm, SearchForm, SubscribeForm, UnsubscribeForm, UpdateAccountForm +from .models import Course, CourseMember, User + + +""" calendar-function to calculate days, etc. for calendar """ +def make_calendar(): + weekdays = list(enumerate(['Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za', 'Zo'])) + + courses = [ None, None, None, None, None, None, None ] + if current_user.is_authenticated: + subscriptions = [ cm.course_id for cm in CourseMember.query.filter_by(user_id=current_user.id) ] + courses = [ ' +\n'.join([ c.name for c in Course.query.filter_by(weekday=i) if c.id in subscriptions ]) for i in range(7) ] + + today = datetime.today() + m = Month() + + rows = [] + for days in m.monthdayscalendar(today.year, today.month): + rows.append([ (i, d, courses[i]) for i, d in enumerate(days) ]) + + return { 'weekdays': weekdays, 'rows': rows } + +""" index.html (home-page) route """ [email protected]("/") +def index(): + courses = Course.query.all() + subscriptions = [] + teachers = User.query.filter_by(type='teacher') + if current_user.is_authenticated: + subscriptions = [ cm.course_id for cm in CourseMember.query.filter_by(user_id=current_user.id) ] + return render_template('index.html', calendar=make_calendar(), courses=courses, subs=subscriptions, teachers=teachers) + +""" about.html route """ [email protected]("/about") +def about(): + return render_template('about.html', calendar=make_calendar(), title='Over ons') + +""" register.html route """ [email protected]("/register", methods=['GET', 'POST']) +def register(): + if current_user.is_authenticated: + flash('U bent al ingelogd', 'warning') + return redirect('/') + form = RegistrationForm() + if form.validate_on_submit(): + hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8') + user = User(username=form.username.data, email=form.email.data, password=hashed_password) + db.session.add(user) + db.session.commit() + flash('Uw profiel werd toegevoegd! U kan nu inloggen.', 'success') + return redirect(url_for('login')) + return render_template('register.html', calendar=make_calendar(), title='Registeren', form=form) + +""" login.html route """ [email protected]("/login", methods=['GET', 'POST']) +def login(): + if current_user.is_authenticated: + flash('U bent al ingelogd', 'warning') + return redirect('/') + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(email=form.email.data).first() + if user and bcrypt.check_password_hash(user.password, form.password.data): + login_user(user, remember=form.remember.data) + if bcrypt.check_password_hash(user.password, form.email.data): + flash('Wij zullen aanbevelen uw wachtwoord weer te veranderen', 'warning') + next_page = request.args.get('next') + return redirect(next_page if next_page else '/') + else: + flash('Kon niet inloggen, is uw e-mail en wachtwoord juist?', 'danger') + return render_template('login.html', calendar=make_calendar(), title='Inloggen', form=form) + +""" logout route """ [email protected]("/logout") +def logout(): + logout_user() + return redirect('/') + +""" save-picture function for account.html """ +def save_picture(form_picture): + random_hex = secrets.token_hex(8) + _, f_ext = os.path.splitext(form_picture.filename) + picture_fn = random_hex + f_ext + picturepath = os.path.join(app.root_path, 'static/profile_pics', picture_fn) + + output_size = (125, 125) + i = Image.open(form_picture) + i.thumbnail(output_size) + i.save(picturepath) + + return picture_fn + +""" account.html route """ [email protected]("/user/self", methods=[ 'GET', 'POST' ]) +@login_required +def account(): + form = UpdateAccountForm() + if form.validate_on_submit(): + current_user.username = form.username.data + current_user.email = form.email.data + if form.picture.data: + picture_file = save_picture(form.picture.data) + current_user.image_file = picture_file + if form.password.data: + current_user.password = bcrypt.generate_password_hash(form.password.data).decode('utf-8') + db.session.commit() + flash('Uw profiel is bewerkt!', 'success') + return redirect(url_for('account')) + elif request.method == 'GET': + form.username.data = current_user.username + form.email.data = current_user.email + image_file = url_for('static', filename='profile_pics/' + current_user.image_file) + return render_template('account.html', calendar=make_calendar(), title='Profiel', image_file=image_file, form=form) + +""" course.html (course-info) route """ [email protected]("/course/<int:course_id>", methods=[ 'GET', 'POST' ]) +def course(course_id): + sub_form = SubscribeForm() + unsub_form = UnsubscribeForm() + teachers = User.query.filter_by(type='teacher') + subscribed = None + if current_user.is_authenticated: + subscribed = CourseMember.query.filter_by(user_id=current_user.id, course_id=course_id).first() + + if sub_form.validate_on_submit() and not subscribed: + course = CourseMember(user_id=current_user.id, course_id=course_id) + db.session.add(course) + db.session.commit() + flash('U bent nu ingeschreven!', 'success') + return redirect('/') + + if unsub_form.validate_on_submit() and subscribed: + db.session.delete(subscribed) + db.session.commit() + flash('U bent nu uitgeschreven!', 'success') + return redirect('/') + + course = Course.query.get_or_404(course_id) + return render_template('course.html', calendar=make_calendar(), title=course.name, course=course, sub_form=sub_form, unsub_form=unsub_form, subscribed=subscribed is not None, teachers=teachers) + +""" course_overview.html route """ [email protected]("/courses") +@login_required +def course_overview(): + if current_user.type not in [ "admin", "teacher" ]: + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + courses = [ (c, User.query.filter_by(id=c.teacher_id).first() ) for c in Course.query.all() ] + return render_template('course_overview.html', calendar=make_calendar(), legend='Lesoverzicht', courses=courses) + +""" new_course.html route """ [email protected]("/course/new", methods=['GET', 'POST']) +@login_required +def new_course(): + if current_user.type not in [ "admin", "teacher" ]: + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + form = NewCourseForm() + form.teacher_id.choices = [ (g.id, g.username) for g in User.query.filter_by(type='teacher') ] + if form.validate_on_submit(): + course = Course(name=form.name.data, description=form.description.data, teacher_id=form.teacher_id.data, weekday=form.weekday.data, start=form.start.data, end=form.end.data, location=form.location.data) + db.session.add(course) + db.session.commit() + flash('De les is toegevoegd!', 'success') + return redirect(url_for('course_overview')) + return render_template('new_course.html', calendar=make_calendar(), legend='Nieuwe les aanmaken', form=form) + +""" new_course.html (update course) route """ [email protected]("/course/<int:course_id>/update", methods=['GET', 'POST']) +@login_required +def update_course(course_id): + if current_user.type not in [ "admin", "teacher" ]: + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + form = NewCourseForm() + form.teacher_id.choices = [ (g.id, g.username) for g in User.query.filter_by(type='teacher') ] + course = Course.query.get_or_404(course_id) + if form.validate_on_submit(): + course.name = form.name.data + course.description = form.description.data + course.teacher_id = form.teacher_id.data + course.weekday = form.weekday.data + course.start = form.start.data + course.end = form.end.data + course.location = form.location.data + db.session.commit() + flash('De les is bewerkt!', 'success') + return redirect(url_for('course_overview')) + elif request.method == 'GET': + form.name.data = course.name + form.description.data = course.description + form.teacher_id.data = course.teacher_id + form.weekday.data = course.weekday + form.start.data = course.start + form.end.data = course.end + form.location.data = course.location + return render_template('new_course.html', calendar=make_calendar(), form=form, legend='Les aanpassen') + +""" delete-course route """ [email protected]("/course/<int:course_id>/delete", methods=['GET','POST']) +@login_required +def delete_course(course_id): + if current_user.type not in [ "admin", "teacher" ]: + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + course = Course.query.get_or_404(course_id) + db.session.delete(course) + db.session.commit() + return redirect(url_for('course_overview')) + +""" admin.html route """ [email protected]("/users", methods=['GET','POST']) +@login_required +def admin(): + if current_user.type != "admin": + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + form = SearchForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user == None: + flash(f'Geen gebrukers gevonden met de gebruikersnaam: {form.username.data}!', 'danger') + else: + flash(f'Gebruiker gevonden met gebruikersnaam: {form.username.data}!', 'success') + return redirect(url_for('admin_user', user_id= user.id)) + return render_template('admin.html', calendar=make_calendar(), form=form) + +""" account-admin route """ [email protected]("/user/<int:user_id>", methods=['GET','POST']) +@login_required +def admin_user(user_id): + if current_user.type != "admin": + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + form = AdminForm() + user = User.query.filter_by(id=user_id).first() + image_file = url_for('static', filename='profile_pics/' + user.image_file) + if form.validate_on_submit(): + user.type = form.type.data + db.session.commit() + flash(f'De gebruiker {user.username} is nu een {user.type}', 'success') + return redirect(url_for('admin')) + elif request.method == 'GET': + form.type.data = user.type + return render_template('admin_user.html', calendar=make_calendar(), form=form, user=user, image_file=image_file) + +""" delete-user route """ [email protected]("/user/<int:user_id>/delete", methods=['GET','POST']) +@login_required +def delete_user(user_id): + if current_user.type != "admin": + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + user = User.query.get_or_404(user_id) + db.session.delete(user) + db.session.commit() + flash(f'De gebruiker {user.username} werd verwijdert', 'success') + return redirect(url_for('admin')) + +""" reset user's password route """ [email protected]("/user/<int:user_id>/reset", methods=['GET','POST']) +@login_required +def reset_user(user_id): + if current_user.type != "admin": + flash('U mag deze website niet bereiken', 'error') + return redirect('/') + user = User.query.get_or_404(user_id) + user.password = bcrypt.generate_password_hash(user.email).decode('utf-8') + db.session.commit() + flash(f'{user.username}\'s is nu zijn/haar e-mail', 'success') + return redirect(url_for('admin')) + +""" 404 not found handler """ [email protected](404) +def not_found(error): + flash(f"Deze pagina werd niet gevonden", 'danger') + return index() # geen redirect om de '/bla' te houden diff --git a/pgmles/server.py b/pgmles/server.py @@ -0,0 +1,16 @@ +from flask import Flask +from flask_bcrypt import Bcrypt +from flask_login import LoginManager +from flask_sqlalchemy import SQLAlchemy + +app = Flask(__name__) +app.config['SECRET_KEY'] = '5791628bb0b13ce0c676dfde280ba245' +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db' +db = SQLAlchemy(app) +bcrypt = Bcrypt(app) +login_manager = LoginManager(app) +login_manager.login_view = 'login' +login_manager.login_message_category = 'info' + +# to run 'routes.py' and make the routes available +from .routes import * diff --git a/pgmles/site.db b/pgmles/site.db Binary files differ. diff --git a/pgmles/static/.DS_Store b/pgmles/static/.DS_Store Binary files differ. diff --git a/pgmles/static/main.css b/pgmles/static/main.css @@ -0,0 +1,145 @@ +body { + background: #fafafa; + color: #333333; + margin-top: 5rem; +} + +h1, h2, h3, h4, h5, h6 { + color: #444444; +} + +.bg-steel { + background-color: #33bc85; +} + +.site-header .navbar-nav .nav-link { + color: #eeeeee; +} + +.site-header .navbar-nav .nav-link:hover { + color: #ffffff; + text-decoration: underline; +} + +.site-header .navbar-nav .nav-link.active { + font-weight: 500; +} + +.content-section { + background: #ffffff; + padding: 10px 20px; + border: 1px solid #dddddd; + border-radius: 3px; + margin-bottom: 20px; +} + +.article-title { + color: #444444; +} + +a.article-title:hover { + color: #428bca; + text-decoration: none; +} + +.article-content { + white-space: pre-line; +} + +.article-img { + height: 65px; + width: 65px; + margin-right: 16px; +} + +.article-metadata { + padding-bottom: 1px; + margin-bottom: 4px; + border-bottom: 1px solid #e3e3e3 +} + +.article-metadata a:hover { + color: #333; + text-decoration: none; +} + +.article-svg { + width: 25px; + height: 25px; + vertical-align: middle; +} + +.account-img { + height: 125px; + width: 125px; + margin-right: 20px; + margin-bottom: 16px; +} + +.account-heading { + font-size: 2.5rem; +} + +.calendar { + background-color: #fafafa; + width: 100%; + color: #818182; + border: #dddddd solid 1px; +} + +.calendar .weekend { + background-color: #eee; +} + +.calendar th { + border-bottom: #ddd solid 1px; +} + +.calendar th, td { + /*border: #333 solid 1px;*/ + text-align: center; + width: 100px; + position: relative; +} + +.calendar .course { + background: rgb(84, 230, 84); + border: #444 solid 1px; + color: #444 +} + +.calendar td .hover-day { + visibility: hidden; + background-color: #444; + color: #eee; + text-align: center; + border-radius: 6px; + padding: 5px 0; + + width: 120px; + bottom: 100%; + left: 50%; + margin-left: -60px; /* Use half of the width (120/2 = 60), to center the tooltip */ + padding: 5px 0; + + + /* Position the tooltip */ + position: absolute; + z-index: 1; +} + +.calendar td .hover-day::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #444 transparent transparent transparent; +} + + +.calendar td:hover .hover-day { + visibility: visible; +} +\ No newline at end of file diff --git a/pgmles/static/profile_pics/2e32b4c96a8d8f10.jpg b/pgmles/static/profile_pics/2e32b4c96a8d8f10.jpg Binary files differ. diff --git a/pgmles/static/profile_pics/43ed4d3be14daf90.jpg b/pgmles/static/profile_pics/43ed4d3be14daf90.jpg Binary files differ. diff --git a/pgmles/static/profile_pics/4895353c1045b870.jpg b/pgmles/static/profile_pics/4895353c1045b870.jpg Binary files differ. diff --git a/pgmles/static/profile_pics/4c4083a8362b3c0a.jpg b/pgmles/static/profile_pics/4c4083a8362b3c0a.jpg Binary files differ. diff --git a/pgmles/static/profile_pics/53998538edf2342c.png b/pgmles/static/profile_pics/53998538edf2342c.png Binary files differ. diff --git a/pgmles/static/profile_pics/6809465dffb2e5ba.jpg b/pgmles/static/profile_pics/6809465dffb2e5ba.jpg Binary files differ. diff --git a/pgmles/static/profile_pics/7798432669b8b3ac.jpg b/pgmles/static/profile_pics/7798432669b8b3ac.jpg Binary files differ. diff --git a/pgmles/static/profile_pics/85ed1b444539873d.png b/pgmles/static/profile_pics/85ed1b444539873d.png Binary files differ. diff --git a/pgmles/static/profile_pics/b6e1c53325f88b74.png b/pgmles/static/profile_pics/b6e1c53325f88b74.png Binary files differ. diff --git a/pgmles/static/profile_pics/default.jpg b/pgmles/static/profile_pics/default.jpg Binary files differ. diff --git a/pgmles/templates/about.html b/pgmles/templates/about.html @@ -0,0 +1,30 @@ +{% extends "layout.html" %} +{% block content %} +<article class="media content-section"> + <div class="media-body"> + <h2>Over ons</h2> + <p> + Wij staan voor educatie in Nederland! <i>IT Academy Nederland (ITAN)</i> + <!-- Zo heet de opdrachtgever in opdracht schriftelijk vaardigheden --> + geeft lessen aan verschillende mensen in heel Nederland, maar tot nu toe nooit aan ouderen. + </p> + <p> + Daarom heeft ITAN in samenwerking met <i>Groningen-Drenthe Systems (GDS)</i> deze pagina ontwikkeld. Zodat + ook ouderen zich online in kunnen schrijven voor cursussen; de mogelijkheid hebben + in hun vrije tijd iets nieuws te leren. + </p> + <p> + Omdat vele pensioenaten vroeger in BASIC, COBOL of Assembly op hun Commandore 64 programmeerden. Is ons doel + om dit + op te pakken en te en hier op voort te bouwen met moderne talen zoals Python, Java of JavaScript. Natuurlijk + bieden wij ook + verschillende cursussen voor beginners aan. + </p> + <p> + U bent hartelijk welkom en wij hopen u in ons lessen te kunnen zien!<br> + <a href="{{ url_for('register') }}">Klik hier</a>, om een profiel aan te maken en voor lessen in te + schrijven. + </p> + </div> +</article> +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/account.html b/pgmles/templates/account.html @@ -0,0 +1,83 @@ +{% extends "layout.html" %} +{% block content %} +<div class="content-section"> + <div class="media"> + <img class="rounded-circle account-img" src="{{ image_file }}"> + <div class="media-body"> + <h2 class="account-heading">{{ current_user.username }}</h2> + <p class="text-secondary">{{ current_user.email }}</p> + </div> + </div> + <form method="POST" action="" enctype="multipart/form-data"> + {{ form.hidden_tag() }} + <fieldset class="form-group"> + <legend class="border-bottom mb-4">Informatie</legend> + <div class="form-group"> + {{ form.username.label(class="form-control-label") }} + + {% if form.username.errors %} + {{ form.username(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.username.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.username(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.email.label(class="form-control-label") }} + {% if form.email.errors %} + {{ form.email(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.email.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.email(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.password.label(class="form-control-label") }} + {% if form.password.errors %} + {{ form.password(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.password.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.password(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.confirm_password.label(class="form-control-label") }} + {% if form.confirm_password.errors %} + {{ form.confirm_password(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.confirm_password.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.confirm_password(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.picture.label() }} + {{ form.picture(class="form-control-file") }} + {% if form.picture.errors %} + {% for error in form.picture.errors %} + <span class="text-danger">{{ error }}</span></br> + {% endfor %} + {% endif %} + </div> + </fieldset> + <div class="form-group"> + {{ form.submit(class="btn btn-outline-info") }} + </div> + </form> +</div> +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/admin.html b/pgmles/templates/admin.html @@ -0,0 +1,27 @@ +{% extends "layout.html" %} +{% block content %} +<h1>Rechten bewerken!</h1> +<div class="content-section"> + <form method="POST" action=""> + {{ form.hidden_tag() }} + <fieldset class="form-group"> + <legend class="border-bottom mb-4">Zoeken</legend> + <div class="form-group"> + {{ form.username.label(class="form-control-label") }} + {% if form.username.errors %} + {{ form.username(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.username.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.username(class="form-control form-control-lg") }} + {% endif %} + </div> + </fieldset> + <div class="form-group"> + {{ form.submit(class="btn btn-outline-info") }} + </div> +</div> +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/admin_user.html b/pgmles/templates/admin_user.html @@ -0,0 +1,57 @@ +{% extends "layout.html" %} +{% block content %} +<div class="media"> + <img class="rounded-circle account-img" src="{{ image_file }}"> + <div class="media-body"> + <h2 class="account-heading">{{ user.username }}</h2> + <p class="text-secondary">{{ user.email }}</p> + </div> +</div> +<form method="POST" action=""> + {{ form.hidden_tag() }} + <fieldset class="form-group"> + <legend class="border-bottom mb-4">Rechten bewerken!</legend> + <div class="form-group"> + {{ form.type.label(class="form-control-label") }} + {% if form.type.errors %} + {{ form.type(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.type.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.type(class="form-control form-control-lg") }} + {% endif %} + </div> + </fieldset> + <div class="form-group"> + <button type="button" class="btn btn-danger btn-sm m-1" data-toggle="modal" + data-target="#deleteModal">Verwijderen</button> + <a class="btn btn-danger btn-sm m-1" href="{{ url_for('reset_user', user_id=user.id) }}">Wachtwoord + terugzetten</a> + <br><br> + {{ form.submit(class="btn btn-outline-info") }} + </div> +</form> +<!-- Modal --> +<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" + aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="deleteModalLabel">Profiel verwijderen?</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">&times;</span> + </button> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Sluiten</button> + <form action="{{ url_for('delete_user', user_id=user.id) }}" method="POST"> + <input class="btn btn-danger" type="submit" value="Delete"> + </form> + </div> + </div> + </div> +</div> +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/course.html b/pgmles/templates/course.html @@ -0,0 +1,37 @@ +{% extends "layout.html" %} +{% block content %} +<article class="media content-section"> + <div class="media-body"> + <h2><a class="article-title" href="{{url_for('course', course_id=course.id)}}"> + {{ course.name }} + {{ '(subscribed)' if course.id in subs }} + </a></h2> + <p><i>{{ course.description }}</i></p> + <p>wordt gegeven door {% for teacher in teachers if teacher.id == course.teacher_id %}{{ teacher.username }},{% + endfor %}</p> + <p>elke {{ ['maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag', 'zondag'][course.weekday] }} + {{ course.start }} uur t/m {{ course.end }} uur op locatie: <b>{{ course.location }}</b></p> + </div> + <div class="article-metadata"> + {% if current_user.is_authenticated %} + {% if not subscribed %} + <form method="POST" action=""> + {{ sub_form.hidden_tag() }} + <div class="form-group"> + {{ sub_form.submit(class="btn btn-outline-info") }} + </div> + </form> + {% else %} + <form method="POST" action=""> + {{ unsub_form.hidden_tag() }} + <div class="form-group"> + {{ unsub_form.submit(class="btn btn-outline-info") }} + </div> + </form> + {%endif%} + {% else %} + <p><a href="{{ url_for('login') }}">Inloggen om in te schrijven</a></p> + {% endif %} + </div> +</article> +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/course_overview.html b/pgmles/templates/course_overview.html @@ -0,0 +1,48 @@ +{% extends "layout.html" %} +{% block content %} +<article class="media content-section"> + <legend class="border-bottom mb-4"> + <h1>{{ legend }}</h1> + </legend> +</article> +<article class="media content-section"> + <div class="media-body"> + <div> + <a class="btn btn-secondary btn-sm mt-1 mb-1" href="{{ url_for('new_course') }}">Nieuwe les</a> + </div> + </div> +</article> +{% for course, teacher in courses %} +<article class="media content-section"> + <div class="media-body"> + <h2><a class="article-title" href="{{url_for('course', course_id=course.id)}}">{{course.name}} <small>by {{ + teacher.username }}</small></a></h2> + <div> + <a class="btn btn-secondary btn-sm mt-1 mb-1" + href="{{ url_for('update_course', course_id = course.id) }}">Bewerken</a> + <button type="button" class="btn btn-danger btn-sm m-1" data-toggle="modal" + data-target="#deleteModal">Verwijderen</button> + </div> + </div> +</article> +<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" + aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="deleteModalLabel">Les verwijderen?</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">&times;</span> + </button> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Sluiten</button> + <form action="{{ url_for('delete_course', course_id=course.id) }}" method="POST"> + <input class="btn btn-danger" type="submit" value="Delete"> + </form> + </div> + </div> + </div> +</div> +{% endfor %} +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/index.html b/pgmles/templates/index.html @@ -0,0 +1,32 @@ +{% extends "layout.html" %} +{% block content %} +<article class="media content-section"> + <div class="media-body"> + <h2>Dit zijn ons lessen</h2> + <p> + U kan makkelijk op de titel van een les klikken om in te schrijven! + </p> + </div> +</article> +{% for course in courses %} +<article class="media content-section"> + <div class="media-body"> + <h3><a class="article-title" href="{{url_for('course', course_id=course.id)}}"> + {{ course.name }} + {{ '(ingescheven)' if course.id in subs }} + </a></h3> + <p><i>{{ course.description }}</i></p> + <p> + wordt gegeven door + {% for teacher in teachers if teacher.id == course.teacher_id %} + {{ teacher.username }}, + {% endfor %} + </p> + <p> + elke {{ ['maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag', 'zondag'][course.weekday] }} + {{ course.start }} uur t/m {{ course.end }} uur op locatie: <b>{{ course.location }}</b> + </p> + </div> +</article> +{% endfor %} +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/layout.html b/pgmles/templates/layout.html @@ -0,0 +1,111 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" + integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> + + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css') }}"> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='openmoji/openmoji.css') }}"> + + {% if title %} + <title>Programmeerles voor ouderen - {{ title }}</title> + {% else %} + <title>Programmeerles voor ouderen</title> + {% endif %} +</head> + +<body> + <header class="site-header"> + <nav class="navbar navbar-expand-md navbar-dark bg-steel fixed-top"> + <div class="container"> + <a class="navbar-brand mr-4" href="/">Programmeerles voor ouderen</a> + <div class="collapse navbar-collapse" id="navbarToggle"> + <div class="navbar-nav mr-auto"> + <a class="nav-item nav-link" href="{{ url_for('about') }}">Over ons</a> + </div> + <!-- Navbar Right Side --> + <div class="navbar-nav"> + {% if current_user.is_authenticated %} + <a class="nav-item nav-link" href="{{ url_for('logout') }}">Uitloggen</a> + {% else %} + <a class="nav-item nav-link" href="{{ url_for('login') }}">Inloggen</a> + <a class="nav-item nav-link" href="{{ url_for('register') }}">Registeren</a> + {% endif %} + </div> + </div> + </div> + </nav> + </header> + <main role="main" class="container"> + <div class="row"> + <div class="col-md-8"> + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + <div class="alert alert-{{ category }}"> + {{ message }} + </div> + {% endfor %} + {% endif %} + {% endwith %} + {% block content %}{% endblock %} + </div> + <div class="col-md-4"> + <div class="content-section"> + <h3>Welkom <b>{{ current_user.username if current_user.is_authenticated else 'gast' }}</b>!</h3> + {% if current_user.is_authenticated %} + <p class='text-muted'> + <ul class="list-group"> + <li class="list-group-item list-group-item-light"><a + href="{{ url_for('account') }}">Instellingen</a></li> + {% if current_user.type == 'teacher' or current_user.type == 'admin' %} + <li class="list-group-item list-group-item-light"><a + href="{{ url_for('course_overview') }}">Lesoverzicht</a></li> + {% endif %} + {% if current_user.type == 'admin' %} + <li class="list-group-item list-group-item-light"><a href="{{ url_for('admin') }}">Profielen + bewerken</a></li> + {% endif %} + </ul> + </p> + {% endif %} + {% if current_user.is_authenticated %} + <table class='calendar'> + <tr> + {% for d, day in calendar['weekdays'] %} + <th class="{{ 'weekend' if d >= 5 }}">{{ day }}</th> + {% endfor %} + </tr> + {% for row in calendar['rows'] %} + <tr> + {% for d, day, course in row %} + <td class="{{ 'weekend' if d >= 5 }} {{ 'course' if course and day }}"> + {{ day if day }} + {% if course %}<span class='hover-day'>{{ course }}</span>{% endif %} + </td> + {% endfor %} + </tr> + {% endfor %} + </table> + {% endif %} + </div> + </div> + </div> + </main> + + <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" + integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" + crossorigin="anonymous"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" + integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" + crossorigin="anonymous"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" + integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" + crossorigin="anonymous"></script> +</body> + +</html> +\ No newline at end of file diff --git a/pgmles/templates/login.html b/pgmles/templates/login.html @@ -0,0 +1,52 @@ +{% extends "layout.html" %} +{% block content %} +<div class="content-section"> + <form method="POST" action=""> + {{ form.hidden_tag() }} + <fieldset class="form-group"> + <legend class="border-bottom mb-4">Inloggen</legend> + <div class="form-group"> + {{ form.email.label(class="form-control-label") }} + {% if form.email.errors %} + {{ form.email(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.email.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.email(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.password.label(class="form-control-label") }} + {% if form.password.errors %} + {{ form.password(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.password.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.password(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-check"> + {{ form.remember(class="form-check-input") }} + {{ form.remember.label(class="form-check-label") }} + </div> + </fieldset> + <div class="form-group"> + {{ form.submit(class="btn btn-outline-info") }} + </div> + <small class="text-muted ml-2"> + Wachtwoord vergeten? Neem contact met een administrator. + </small> + </form> +</div> +<div class="border-top pt-3"> + <small class="text-muted"> + Een profiel nodig? <a class="ml-2" href="{{ url_for('register') }}">Nu inschrijven!</a> + </small> +</div> +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/new_course.html b/pgmles/templates/new_course.html @@ -0,0 +1,105 @@ +{% extends "layout.html" %} +{% block content %} +<div class="content-section"> + <form method="POST" action=""> + {{ form.hidden_tag() }} + <fieldset class="form-group"> + <legend class="border-bottom mb-4"><h1>{{ legend }}</h1></legend> + <div class="form-group"> + {{ form.name.label(class="form-control-label") }} + {% if form.name.errors %} + {{ form.name(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.name.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.name(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.description.label(class="form-control-label") }} + {% if form.description.errors %} + {{ form.description(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.description.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.description(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.teacher_id.label(class="form-control-label") }} + {% if form.teacher_id.errors %} + {{ form.teacher_id(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.teacher_id.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.teacher_id(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.weekday.label(class="form-control-label") }} + {% if form.weekday.errors %} + {{ form.weekday(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.weekday.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.weekday(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.start.label(class="form-control-label") }} + {% if form.start.errors %} + {{ form.start(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.start.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.start(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.end.label(class="form-control-label") }} + {% if form.end.errors %} + {{ form.end(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.end.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.end(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.location.label(class="form-control-label") }} + {% if form.location.errors %} + {{ form.location(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.location.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.location(class="form-control form-control-lg") }} + {% endif %} + </div> + </fieldset> + <div class="form-group"> + {{ form.submit(class="btn btn-outline-info") }} + </div> + </form> +</div> +{% endblock content %} +\ No newline at end of file diff --git a/pgmles/templates/register.html b/pgmles/templates/register.html @@ -0,0 +1,71 @@ +{% extends "layout.html" %} +{% block content %} +<div class="content-section"> + <form method="POST" action=""> + {{ form.hidden_tag() }} + <fieldset class="form-group"> + <legend class="border-bottom mb-4">Nog vandaag meedoen!</legend> + <div class="form-group"> + {{ form.username.label(class="form-control-label") }} + + {% if form.username.errors %} + {{ form.username(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.username.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.username(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.email.label(class="form-control-label") }} + {% if form.email.errors %} + {{ form.email(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.email.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.email(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.password.label(class="form-control-label") }} + {% if form.password.errors %} + {{ form.password(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.password.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.password(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.confirm_password.label(class="form-control-label") }} + {% if form.confirm_password.errors %} + {{ form.confirm_password(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.confirm_password.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.confirm_password(class="form-control form-control-lg") }} + {% endif %} + </div> + </fieldset> + <div class="form-group"> + {{ form.submit(class="btn btn-outline-info") }} + </div> + </form> +</div> +<div class="border-top pt-3"> + <small class="text-muted">Heb jij al een account? <a class="ml-2" + href="{{ url_for('login') }}">Inloggen!</a></small> +</div> +{% endblock content %} +\ No newline at end of file diff --git a/readme.md b/readme.md @@ -0,0 +1,79 @@ +# PROGRAMMEERLES VOOR OUDEREN + +[Repository](https://github.com/MoiBaguette/Webtechnologie-Project) + +## De server runnen + +Dit is een dev-server, dus run je met `debug=True`-flag! + +*Als onze website zo goed is, om het in production te runnen, verwijder het `debug=True` :beers:* + +**Deze repository clonen:** +``` +$ git clone https://github.com/MoiBaguette/Webtechnologie-Project +``` + +**Alle afhankelijkheden installeren:** +``` +$ pip3 install flask wtforms flask_sqlalchemy flask-wtf email_validator flask-bcrypt flask-login pillow +``` + +**De server runnen:** +``` +$ python run.py +``` + + +## Uitleg + +### Bestanden + +| bestand | route | beschrikbaar als<sup>1</sup> | beschrijving | +|-----------------------|-----------------------------|------------------------------|--------------------------------------------------------| +| index.html | / | gast | home-pagina | +| about.html | /about | gast | over ons | +| register.html | /register | gast | registeren van een gebruiker<sup>2</sup> | +| login.html | /login | gast | inloggen van een gebruiker<sup>2,3</sup> | +| | /logout | klant | uitloggen van een gebruiker | +| course_overview.html | /courses | docent | lessen bewerken/verwijderen | +| new_course.html | /course/new | docent | nieuwe les aanmaken | +| course.html | /course/`:course_id` | klant | les informatie | +| new_course.html | /course/`:course_id`/update | docent | les instellingen | +| | /course/`:course_id`/delete | docent | les verwijderen | +| admin.html | /users | admin | gebruiker overzicht<sup>4</sup> | +| account.html | /user/self | klant | profiel instellingen | +| admin_user.html | /user/`:user_id` | admin | gebruiker instellingen | +| | /user/`:user_id`/delete | admin | gebruiker verwijderen | +| | /user/`:user_id`/reset | admin | gebruikers wachtwoord terugzetten<sup>5</sup> | +| index.html | *not found* | | 404 page not found handler | +| **overige bestanden** | | | | +| forms.py | | | alle forms voor de websites | +| models.py | | | alle database structs, om alle tabellen te beschrijven | +| routes.py | | | alle routen en endpoints | +| server.py | | | de server initialatie, database etc. | +| site.db | | | hoofd-database voor users, courses etc. | +| .gitignore | | | om git te stoppen, \__pycache__ mee up te laden | +| run.py | | | om de server te runnen | +| layout.html | | | de basis layout voor alle routen | +| static/main.css | | | de basis stylesheet voor alle routen | +| static/profile_pics | | | map met alle profielfoto's | + +> <sup>1</sup> de hierachie is: gast (niet ingelogd), klant, docent, admin<br> +> dus kan een gast het minste bereiken, een klant ook kan alles bereiken wat gast mag etc. + +> <sup>2</sup> als hij al ingelogd is, wordt weer naar `/` redirect + +> <sup>3</sup> jij kan een `?next=` parameters geven, dan wordt na het inloggen daarheen redirect + +> <sup>4</sup> bij gebruiker zoeken moet de naam overeinkomen met de gebruikers naam, nog geen echte zoek-functie + +> <sup>5</sup> betekent: zijn wachtwoord is dan gelijk aan zijn e-mail om in te loggen en zijn wachtwoord weer te veranderen, als iemand zijn wachtwoord is vergeten + + +## Test Profielen + +| type | e-mail | password | +|---------|-------------------|----------| +| klant | [email protected] | `hallo` | +| docent | [email protected] | `hallo` | +| admin | [email protected] | `hallo` | diff --git a/run-server.py b/run-server.py @@ -0,0 +1,4 @@ +from pgmles.server import app + +if __name__ == '__main__': + app.run(debug=True) diff --git a/server/app.py b/server/app.py @@ -0,0 +1 @@ +import flask diff --git a/test-client.py b/test-client.py