muizenval

Observe mouse traps remotely
Log | Files | Refs

commit 04b5110ed27af33fe39097e800a429eb89943a56
parent abdfbba06bdd861faa37847da1cf554e9a808b58
Author: NibbaNamedHassan <[email protected]>
Date:   Thu, 12 May 2022 13:51:27 +0200

Merge branch 'master' of https://github.com/friedelschoen/muizenval.io

Diffstat:
Acreate-db.py | 5+++++
Mreadme.md | 2++
Mserver/app.py | 2++
Mserver/forms.py | 30+++++++++++++++++++++++++-----
Mserver/models.py | 37+++++++++++++++++++++++++++----------
Mserver/routes.py | 13++++++++++++-
Mserver/site.db | 0
Mserver/static/main.css | 1+
Aserver/templates/dashboard.html | 46++++++++++++++++++++++++++++++++++++++++++++++
Mserver/templates/index.html | 21---------------------
Mserver/templates/layout.html | 6+++---
Mserver/templates/register.html | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest.py | 4++++
13 files changed, 205 insertions(+), 40 deletions(-)

diff --git a/create-db.py b/create-db.py @@ -0,0 +1,5 @@ +from server.app import db +import server.models + +db.create_all() +db.session.commit() diff --git a/readme.md b/readme.md @@ -1,5 +1,7 @@ # REPOSITORY VOOR ONS IOT-PROJECT (5GRONINGEN) +> Hamdi Hassan, Loes Hoogstra, Gerco van Woudenberg, Friedel Schon + ## De server runnen Dit is een dev-server, dus run je met `debug=True`-flag! diff --git a/server/app.py b/server/app.py @@ -3,9 +3,11 @@ from flask_bcrypt import Bcrypt from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy + app = Flask(__name__) app.config['SECRET_KEY'] = 'iot_project' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) bcrypt = Bcrypt(app) login_manager = LoginManager(app) diff --git a/server/forms.py b/server/forms.py @@ -1,17 +1,23 @@ -from flask_login import current_user +import re from flask_wtf import FlaskForm from flask_wtf.file import FileAllowed, FileField -from wtforms import BooleanField, HiddenField, PasswordField, SelectField, StringField, SubmitField, TextAreaField +from wtforms import BooleanField, HiddenField, PasswordField, SelectField, StringField, SubmitField, IntegerField from wtforms.validators import DataRequired, Email, EqualTo, Length, ValidationError -from .models import User +from .models import User, UserType """ registration form for register.html """ class RegistrationForm(FlaskForm): - name = StringField('Naam', validators=[ DataRequired(), Length(min=2, max=20) ]) + name = StringField('Naam', validators=[ DataRequired(), Length(min=5, max=20) ]) email = StringField('E-Mail', validators=[ DataRequired(), Email() ]) password = PasswordField('Wachtwoord', validators=[ DataRequired() ]) confirm_password = PasswordField('Wachtwoord herhalen', validators=[ DataRequired(), EqualTo('password') ]) + phone = StringField('Telefoon', validators=[ DataRequired(), Length(min=5) ]) + street = StringField('Straat', validators=[ DataRequired() ]) + housenumber = IntegerField('Huisnummer', validators=[ DataRequired() ]) + postcode = StringField('Postcode', validators=[ DataRequired() ]) + place = StringField('Plaats', validators=[ DataRequired() ]) + catcher_code = StringField('VangerCode', validators=[ DataRequired(), Length(min=5, max=5) ]) submit = SubmitField('Registeren') """ validates whether name is already in use """ @@ -22,7 +28,21 @@ class RegistrationForm(FlaskForm): """ 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') + raise ValidationError('Deze e-mail bestaat al, log in als dat uw e-mail is.') + + def validate_phone(self, phone): + for c in phone.data: + if c not in '0123456789 -': + raise ValidationError('Dit belnummer is niet geldig.') + + def validate_postcode(self, code): + if len(code.data) != 6 or not code.data[0:4].isnumeric() or not code.data[4:6].isalpha(): + raise ValidationError('De postcode is niet geldig.') + + + def validate_catcher_code(self, code): + if not User.query.filter_by(type=UserType.CATCHER, catcher_code=code.data).first(): + raise ValidationError('De rattenvanger is niet bekennt, hebt u de code juist ingevoerd?') """ login form for login.html """ diff --git a/server/models.py b/server/models.py @@ -1,5 +1,4 @@ -from datetime import datetime - +from enum import Enum from flask_login import UserMixin from .app import db, login_manager @@ -10,23 +9,41 @@ def load_user(user_id): return User.query.get(int(user_id)) +class UserType(Enum): + ADMIN = 0 + MANAGER = 1 + TECHNICIAN = 2 + CATCHER = 3 + CLIENT = 4 + class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) - type = db.Column(db.String(7), nullable=False, default="guest") # guest, client, catcher, admin + type = db.Column(db.Enum(UserType)) email = db.Column(db.String(120), unique=True, nullable=False) name = db.Column(db.String(20), unique=True, nullable=False) password = db.Column(db.String(60), nullable=False) image_file = db.Column(db.String(20), nullable=False, default='default.jpg') + phone = db.Column(db.Text, nullable=False) + address = db.Column(db.Text) + + manager = db.Column(db.Integer, db.ForeignKey('user.id')) # set if technician, catcher, user + catcher_code = db.Column(db.String(5)) # set if catcher + catcher = db.Column(db.Integer, db.ForeignKey('user.id')) # set if user -class Home(db.Model): - id = db.Column(db.Integer, primary_key=True) - owner = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) - catcher = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) - adress = db.Column(db.Text, nullable=False) class Trap(db.Model): id = db.Column(db.Integer, primary_key=True) mac = db.Column(db.String(16), unique=True, nullable=False) - home = db.Column(db.Integer, db.ForeignKey('home.id'), nullable=False) - last_heartbeat = db.Column(db.Integer, nullable=True, default=0) + name = db.Column(db.Text) + owner = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + last_heartbeat = db.Column(db.DateTime, nullable=True, default=0) caught = db.Column(db.Boolean, nullable=False, default=False) + + def pretty_mac(self): + upper = self.mac.upper() + return ':'.join([ upper[i] + upper[i+1] for i in range(0, len(upper), 2) ]) + + def status_color(self): + if self.caught: + return '#f4a900' + return 'currentColor' diff --git a/server/routes.py b/server/routes.py @@ -1,3 +1,4 @@ +from operator import or_ import os import secrets @@ -9,7 +10,7 @@ from datetime import datetime from .app import app, bcrypt, db from .forms import LoginForm, RegistrationForm, UpdateAccountForm -from .models import User +from .models import Trap, User, UserType """ index.html (home-page) route """ @@ -104,6 +105,16 @@ def account(): image_file = url_for('static', filename='profile_pics/' + current_user.image_file) return render_template('account.html', title='Profiel', image_file=image_file, form=form) [email protected]('/dashboard') +@login_required +def dashboard(): + query = [ current_user ] + if current_user.type == UserType.CATCHER: + query += list(User.query.filter_by(catcher=current_user.id)) + + traps = [ trap for user in query for trap in Trap.query.filter_by(owner=user.id) ] + + return render_template('dashboard.html', title='Dashboard', traps=traps) """ 404 not found handler """ @app.errorhandler(404) diff --git a/server/site.db b/server/site.db Binary files differ. diff --git a/server/static/main.css b/server/static/main.css @@ -8,6 +8,7 @@ body { code { font-family: 'Source Code Pro'; + color: inherit; } h1, h2, h3, h4, h5, h6 { diff --git a/server/templates/dashboard.html b/server/templates/dashboard.html @@ -0,0 +1,45 @@ +{% extends "layout.html" %} +{% block content %} +<article class="media content-section"> + <div class="media-body"> + <h2><b>Dit zijn uw muizenvallen</b></h2> + <p> + Klik op de titel van een muizenval om de instellingen in te zien! + </p> + </div> +</article> +{% for home, trap in traps %} +<article class="media content-section"> + <div class="media-body"> + <h3><a class="article-title" href="#"> + <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="{{ trap.status_color() }}" + class="bi bi-circle-fill" viewBox="0 0 20 20"> + <circle cx="10" cy="10" r="10" /> + </svg> + - + {% if trap.name %} + {{ trap.name }} + {% else %} + <code>[{{ trap.pretty_mac() }}]</code> + {% endif %} + <small>van {{ home.owner_class().name }}</small> + </a> + </h3> + {% if trap.name %} + <p> + <code>[{{ trap.pretty_mac() }}]</code> + </p> + {% endif %} + <p> + onderhouden door <b>{{ home.catcher_class().name }}</b> + </p> + <p> + <a class="article-title" href="#"> + {{ home.street }} {{ home.number }}<br> + {{ home.zipcode }} {{ home.place }} + </a> + </p> + </div> +</article> +{% endfor %} +{% endblock content %} +\ No newline at end of file diff --git a/server/templates/index.html b/server/templates/index.html @@ -8,25 +8,4 @@ </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.name }}, - {% 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/server/templates/layout.html b/server/templates/layout.html @@ -9,7 +9,6 @@ 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') }}"> <!-- Google Font: Source Sans Pro --> <link rel="preconnect" href="https://fonts.googleapis.com"> @@ -29,10 +28,11 @@ <nav class="navbar navbar-expand-md fixed-top"> <div class="container"> <a class="navbar-brand mr-4" href="/"> - <code>muizenval.io</code> + <b><code>muizenval.io</code></b> </a> <div class="collapse navbar-collapse" id="navbarToggle"> <div class="navbar-nav mr-auto"> + <a class="nav-item nav-link" href="#">Shop</a> <a class="nav-item nav-link" href="{{ url_for('about') }}">Over ons</a> <a class="nav-item nav-link" href="{{ url_for('producten') }}">Producten</a> </div> @@ -87,7 +87,7 @@ </li> {% if current_user.type in [ 'client', 'catcher' ] %} <li class="list-group-item list-group-item-light"> - <a href="#">Uw muizenvallen</a> + <a href="{{ url_for('dashboard') }}">Uw muizenvallen</a> </li> {% endif %} {% if current_user.type == 'catcher' %} diff --git a/server/templates/register.html b/server/templates/register.html @@ -58,6 +58,84 @@ {{ form.confirm_password(class="form-control form-control-lg") }} {% endif %} </div> + <div class="form-group"> + {{ form.phone.label(class="form-control-label") }} + {% if form.phone.errors %} + {{ form.phone(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.phone.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.phone(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.street.label(class="form-control-label") }} + {% if form.street.errors %} + {{ form.street(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.street.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.street(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.housenumber.label(class="form-control-label") }} + {% if form.housenumber.errors %} + {{ form.housenumber(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.housenumber.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.housenumber(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.postcode.label(class="form-control-label") }} + {% if form.postcode.errors %} + {{ form.postcode(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.postcode.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.postcode(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.place.label(class="form-control-label") }} + {% if form.place.errors %} + {{ form.place(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.place.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.place(class="form-control form-control-lg") }} + {% endif %} + </div> + <div class="form-group"> + {{ form.catcher_code.label(class="form-control-label") }} + {% if form.catcher_code.errors %} + {{ form.catcher_code(class="form-control form-control-lg is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.catcher_code.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.catcher_code(class="form-control form-control-lg") }} + {% endif %} + </div> </fieldset> <div class="form-group"> {{ form.submit(class="btn btn-outline-info") }} diff --git a/test.py b/test.py @@ -0,0 +1,4 @@ +code = '9939a3' + +if len(code) != 6 or not code[0:4].isnumeric() or not code[4:6].isalpha(): + print('invalid')