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 %} +
+
+ {{form.hidden_tag()}} +
+
+ {{form.email.label(class="form-control-label")}} + + {% if form.email.errors %} + {{form.email(class="form-control form-control-lg is-invalid")}} +
+ {% for error in form.email.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{form.email(class="form-control form-control-lg")}} + {% endif %} +
+
+ {{form.admin(class="form-check-input")}} + {{form.admin.label(class="form-check-label")}} +
+
+
+
+ {{form.submit(class="btn btn-outline-info")}} +
+
+
+{% 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]