diff --git a/advlabdb/__init__.py b/advlabdb/__init__.py
index bd5b34c..aa33310 100644
--- a/advlabdb/__init__.py
+++ b/advlabdb/__init__.py
@@ -8,3 +8,4 @@ db = SQLAlchemy(app)
from advlabdb import routes
from advlabdb import models
+from advlabdb import forms
diff --git a/advlabdb/forms.py b/advlabdb/forms.py
new file mode 100644
index 0000000..6bce158
--- /dev/null
+++ b/advlabdb/forms.py
@@ -0,0 +1,17 @@
+from flask_wtf import FlaskForm
+from wtforms import StringField, PasswordField, SubmitField, BooleanField
+from wtforms.validators import DataRequired, Length, Email, EqualTo
+
+
+class RegistrationForm(FlaskForm):
+ email = StringField("Email",
+ validators=[DataRequired(), Email()])
+ admin = BooleanField("Admin")
+ submit = SubmitField("Sign Up")
+
+
+class LoginForm(FlaskForm):
+ email = StringField("Email",
+ validators=[DataRequired(), Email()])
+ password = PasswordField("Password", validators=[DataRequired()])
+ submit = SubmitField("Login")
diff --git a/advlabdb/models.py b/advlabdb/models.py
index 2886a76..954928e 100644
--- a/advlabdb/models.py
+++ b/advlabdb/models.py
@@ -51,7 +51,7 @@ class GroupExperiment(db.Model):
class Experiment(db.Model):
id = db.Column(db.Integer, primary_key=True)
- number = db.Column(db.Integer, nullable=False)
+ number = db.Column(db.Integer, nullable=False, unique=True)
name = db.Column(db.String(200), nullable=False)
description = db.Column(db.Text, nullable=True)
room = db.Column(db.String(100), nullable=False)
diff --git a/advlabdb/routes.py b/advlabdb/routes.py
index 646e131..dcdf7b7 100644
--- a/advlabdb/routes.py
+++ b/advlabdb/routes.py
@@ -1,10 +1,13 @@
from advlabdb import app
-from flask import render_template, request, url_for
+from flask import render_template, request, url_for, flash, redirect
+from werkzeug.security import check_password_hash, generate_password_hash
from advlabdb.utils import *
from advlabdb.models import *
+from advlabdb.forms import *
activeSemester_id = 0
+
@app.context_processor
def util_processor():
def semesterDropDownItems():
@@ -17,6 +20,7 @@ def util_processor():
return dict(semesterDropDownItems=semesterDropDownItems,
activeSemesterLabel=Semester.query.get(activeSemester_id).label)
+
@app.route("/")
def index():
global activeSemester_id
@@ -24,6 +28,7 @@ def index():
page = "index"
return render_template(page + ".html", navbarItems=navbarItems(page))
+
@app.route("/students")
def students():
semester = Semester.query.get(activeSemester_id)
@@ -51,6 +56,7 @@ def students():
return render_template(page + ".html", navbarItems=navbarItems(page),
tables=tables, tablesLabels=tablesLabels)
+
@app.route("/assistants")
def assistants():
headerAndDataList = [["First name", "row.first_name"],
@@ -71,6 +77,7 @@ def assistants():
return render_template(page + ".html", navbarItems=navbarItems(page),
table=table)
+
@app.route("/experiments")
def experiments():
semester = Semester.query.get(activeSemester_id)
@@ -78,8 +85,8 @@ def experiments():
tables = []
tablesLabels = []
- headerAndDataList = [["Name", "row.experiment.name"],
- ["Number", "row.number"],
+ headerAndDataList = [["Number", "row.experiment.number"],
+ ["Name", "row.experiment.name"],
["Assistants", "row.assistants"],
["Groups with this ex.", "[gEx.group.number for gEx in row.group_experiments]"]]
@@ -92,11 +99,13 @@ def experiments():
return render_template(page + ".html", navbarItems=navbarItems(page),
tables=tables, tablesLabels=tablesLabels)
+
@app.route("/appointments")
def appointments():
page = "appointments"
return render_template(page + ".html", navbarItems=navbarItems(page))
+
@app.route("/groups")
def groups():
semester = Semester.query.get(activeSemester_id)
@@ -119,6 +128,7 @@ def groups():
return render_template(page + ".html", navbarItems=navbarItems(page),
tables=tables, tablesLabels=tablesLabels)
+
@app.route("/users")
def users():
headerAndDataList = [["Email", "row.email"],
@@ -132,6 +142,7 @@ def users():
return render_template(page + ".html", navbarItems=navbarItems(page),
table=table)
+
@app.route("/set_semester", methods=["GET"])
def set_semester():
global activeSemester_id
@@ -139,6 +150,7 @@ def set_semester():
page = "index"
return render_template(page + ".html", navbarItems=navbarItems(page))
+
@app.route("/semesters")
def semesters():
headerAndDataList = [["Label", "row.label"],
@@ -150,3 +162,19 @@ def semesters():
page = "semesters"
return render_template(page + ".html", navbarItems=navbarItems(page),
table=table)
+
+
+@app.route("/register", methods=["GET", "POST"])
+def register():
+ form = RegistrationForm()
+ if form.validate_on_submit():
+ password = randomPassword()
+ passwordHash = generate_password_hash(password).decode("utf-8")
+ email = form.email.data
+ admin = form.admin.data
+ user = User(email=email, password_hash=passwordHash, is_admin=admin)
+ db.session.add(user)
+ db.session.commit()
+ return render_template("registered.html", title="Registered",
+ email=email, password=password, admin=admin)
+ return render_template("register.html", title="Register", form=form)
diff --git a/advlabdb/templates/layout.html b/advlabdb/templates/layout.html
index e5ce9af..e65ccbd 100644
--- a/advlabdb/templates/layout.html
+++ b/advlabdb/templates/layout.html
@@ -48,6 +48,16 @@
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+ {% for category, message in messages %}
+
+ {{ message }}
+
+ {% endfor %}
+ {% endif %}
+ {% endwith %}
+
{% block content %}{% endblock content %}
diff --git a/advlabdb/templates/register.html b/advlabdb/templates/register.html
new file mode 100644
index 0000000..59c28f9
--- /dev/null
+++ b/advlabdb/templates/register.html
@@ -0,0 +1,32 @@
+{% extends "layout.html" %}
+{% block content %}
+
+{% endblock content %}
diff --git a/advlabdb/templates/registered.html b/advlabdb/templates/registered.html
new file mode 100644
index 0000000..25e9643
--- /dev/null
+++ b/advlabdb/templates/registered.html
@@ -0,0 +1,17 @@
+{% extends "layout.html" %}
+{% block content %}
+
+ Registered as
+ {% if admin %}
+ Admin
+ {% else %}
+ Assistant
+ {% endif %}
+ !
+
+
+ Email: {{email}}
+
+ Random password: {{password}}
+
+{% endblock content %}
diff --git a/advlabdb/utils.py b/advlabdb/utils.py
index 59472e3..a498e19 100644
--- a/advlabdb/utils.py
+++ b/advlabdb/utils.py
@@ -61,3 +61,7 @@ data-export-types="['json', 'xml', 'csv', 'txt', 'sql', 'pdf']">
def appointmentDate(date):
return date.strftime("%a %d.%m.%Y")
+
+
+def randomPassword():
+ return ''.join(random.choice(string.ascii_letters + string.digits) for i in range(12))
diff --git a/poetry.lock b/poetry.lock
index acad46f..e05f3d8 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -6,6 +6,33 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+[[package]]
+name = "dnspython"
+version = "2.1.0"
+description = "DNS toolkit"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+dnssec = ["cryptography (>=2.6)"]
+doh = ["requests", "requests-toolbelt"]
+idna = ["idna (>=2.1)"]
+curio = ["curio (>=1.2)", "sniffio (>=1.1)"]
+trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"]
+
+[[package]]
+name = "email-validator"
+version = "1.1.2"
+description = "A robust email syntax and deliverability validation library for Python 2.x/3.x."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+dnspython = ">=1.15.0"
+idna = ">=2.0.0"
+
[[package]]
name = "flask"
version = "1.1.2"
@@ -37,6 +64,19 @@ python-versions = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*"
Flask = ">=0.10"
SQLAlchemy = ">=0.8.0"
+[[package]]
+name = "flask-wtf"
+version = "0.14.3"
+description = "Simple integration of Flask and WTForms."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+Flask = "*"
+itsdangerous = "*"
+WTForms = "*"
+
[[package]]
name = "greenlet"
version = "1.0.0"
@@ -48,6 +88,14 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
[package.extras]
docs = ["sphinx"]
+[[package]]
+name = "idna"
+version = "3.1"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.4"
+
[[package]]
name = "itsdangerous"
version = "1.1.0"
@@ -121,16 +169,40 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"]
watchdog = ["watchdog"]
+[[package]]
+name = "wtforms"
+version = "2.3.3"
+description = "A flexible forms validation and rendering library for Python web development."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+MarkupSafe = "*"
+
+[package.extras]
+email = ["email-validator"]
+ipaddress = ["ipaddress"]
+locale = ["Babel (>=1.3)"]
+
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
-content-hash = "b70473e564b6804729520b3b372cc9a43b926230c9466a09eb35ba6ae7f0f1b0"
+content-hash = "fbc39d76724ec0d0a66b7a193602669141da0e11c39d33cf3ad429233aee1046"
[metadata.files]
click = [
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
]
+dnspython = [
+ {file = "dnspython-2.1.0-py3-none-any.whl", hash = "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216"},
+ {file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"},
+]
+email-validator = [
+ {file = "email-validator-1.1.2.tar.gz", hash = "sha256:1a13bd6050d1db4475f13e444e169b6fe872434922d38968c67cea9568cce2f0"},
+ {file = "email_validator-1.1.2-py2.py3-none-any.whl", hash = "sha256:094b1d1c60d790649989d38d34f69e1ef07792366277a2cf88684d03495d018f"},
+]
flask = [
{file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"},
{file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"},
@@ -139,6 +211,10 @@ flask-sqlalchemy = [
{file = "Flask-SQLAlchemy-2.5.1.tar.gz", hash = "sha256:2bda44b43e7cacb15d4e05ff3cc1f8bc97936cc464623424102bfc2c35e95912"},
{file = "Flask_SQLAlchemy-2.5.1-py2.py3-none-any.whl", hash = "sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390"},
]
+flask-wtf = [
+ {file = "Flask-WTF-0.14.3.tar.gz", hash = "sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720"},
+ {file = "Flask_WTF-0.14.3-py2.py3-none-any.whl", hash = "sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2"},
+]
greenlet = [
{file = "greenlet-1.0.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:1d1d4473ecb1c1d31ce8fd8d91e4da1b1f64d425c1dc965edc4ed2a63cfa67b2"},
{file = "greenlet-1.0.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:cfd06e0f0cc8db2a854137bd79154b61ecd940dce96fad0cba23fe31de0b793c"},
@@ -184,6 +260,10 @@ greenlet = [
{file = "greenlet-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:5d69bbd9547d3bc49f8a545db7a0bd69f407badd2ff0f6e1a163680b5841d2b0"},
{file = "greenlet-1.0.0.tar.gz", hash = "sha256:719e169c79255816cdcf6dccd9ed2d089a72a9f6c42273aae12d55e8d35bdcf8"},
]
+idna = [
+ {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"},
+ {file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"},
+]
itsdangerous = [
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
@@ -286,3 +366,7 @@ werkzeug = [
{file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"},
{file = "Werkzeug-1.0.1.tar.gz", hash = "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"},
]
+wtforms = [
+ {file = "WTForms-2.3.3-py2.py3-none-any.whl", hash = "sha256:7b504fc724d0d1d4d5d5c114e778ec88c37ea53144683e084215eed5155ada4c"},
+ {file = "WTForms-2.3.3.tar.gz", hash = "sha256:81195de0ac94fbc8368abbaf9197b88c4f3ffd6c2719b5bf5fc9da744f3d829c"},
+]
diff --git a/pyproject.toml b/pyproject.toml
index 4bb487f..44aa1a4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,6 +9,8 @@ python = "^3.8"
Flask = "^1.1.2"
Flask-SQLAlchemy = "^2.5.1"
SQLAlchemy = "^1.4.4"
+Flask-WTF = "^0.14.3"
+email-validator = "^1.1.2"
[tool.poetry.dev-dependencies]