Browse Source

Remove numeric ID, make name identifier. Fix errors

master
Burathar 4 years ago
parent
commit
320615f9ff
  1. 19
      biscd/biscd/froms.py
  2. 17
      biscd/biscd/models/user.py
  3. 111
      biscd/biscd/models/yaml_serializable.py
  4. 16
      biscd/biscd/routes.py

19
biscd/biscd/froms.py

@ -2,7 +2,7 @@ from flask_wtf import FlaskForm
from wtforms import SubmitField, StringField, PasswordField, BooleanField from wtforms import SubmitField, StringField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Email, EqualTo, Length, ValidationError from wtforms.validators import DataRequired, Email, EqualTo, Length, ValidationError
from .models import User from .models import User, Project
class LoginForm(FlaskForm): class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()]) username = StringField('Username', validators=[DataRequired()])
@ -11,23 +11,28 @@ class LoginForm(FlaskForm):
submit = SubmitField('Sign In') submit = SubmitField('Sign In')
class RegistrationForm(FlaskForm): class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()]) username = StringField('Username', validators=[DataRequired(), Length(min=4, max=64)])
email = StringField('Email', validators=[DataRequired(), Email()]) email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired(), Length(min=10, max=128)])
password2 = PasswordField( password2 = PasswordField(
'Repeat Password', validators=[DataRequired(), EqualTo('password')]) 'Repeat Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Register') submit = SubmitField('Register')
def validate_username(self, username): def validate_username(self, username):
user = User(username.data) user = User.get(name=username.data)
if user is not None: if not any(user):
raise ValidationError('Please use a different username.') raise ValidationError('Please use a different username.')
def validate_email(self, email): def validate_email(self, email):
user = User.query.filter_by(email=email.data).first() user = User.get(email=email.data)
if user is not None: if not any(user):
raise ValidationError('Please use a different email adress.') raise ValidationError('Please use a different email adress.')
class NewProjectForm(FlaskForm): class NewProjectForm(FlaskForm):
projectname = StringField('Project Name', validators=[DataRequired()]) projectname = StringField('Project Name', validators=[DataRequired()])
submit = SubmitField('Add Project') submit = SubmitField('Add Project')
def validate_projectname(self, projectname):
project = Project.get(name=projectname.data)
if not any(project):
raise ValidationError('Please use a different projectname.')

17
biscd/biscd/models/user.py

@ -16,18 +16,13 @@ class User(YamlSerializable):
def _yaml_object_name(self): def _yaml_object_name(self):
return 'users' return 'users'
def __init__(self, id=None, name=None, email=None, password=None, password_hash=None): def __init__(self, name=None, email=None):
super().__init__(id)
self.name = name self.name = name
self.password_hash = set_password(password, password_hash)
self.email = email self.email = email
self.password_hash = None
def set_password(password, password_hash): def set_password(self, password):
if password_hash: self.password_hash = generate_password_hash(password)
return password_hash
if password:
return generate_password_hash(password)
return None
def check_password(self, password): def check_password(self, password):
if not password: if not password:
@ -35,5 +30,5 @@ class User(YamlSerializable):
return check_password_hash(self.password_hash, password) return check_password_hash(self.password_hash, password)
@login.user_loader @login.user_loader
def load_user(id): def load_user(name):
return super.get(int(id)) return super.get(name)

111
biscd/biscd/models/yaml_serializable.py

@ -1,4 +1,5 @@
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from flask import abort
import yaml import yaml
class MyMeta(metaclass=ABCMeta): class MyMeta(metaclass=ABCMeta):
@ -15,24 +16,6 @@ class MyMeta(metaclass=ABCMeta):
class YamlSerializable(object): class YamlSerializable(object):
__metaclass__ = MyMeta __metaclass__ = MyMeta
required_attributes = ['name'] required_attributes = ['name']
_id_counter = 0
@classmethod
def initialize(cls):
ymlserializables = cls._get_all_from_file()
cls._id_counter = max(ymlserializable.id for ymlserializable in ymlserializables) + 1
@abstractmethod
def __init__(self, id = None):
self.id = self.set_id(id)
@classmethod
def set_id(cls, id):
if id is not None:
return id
id = cls._id_counter
cls._id_counter += 1
return id
@classmethod @classmethod
@property @property
@ -53,37 +36,58 @@ class YamlSerializable(object):
ymlserializable_dict.pop('name') ymlserializable_dict.pop('name')
return {self.name: ymlserializable_dict} return {self.name: ymlserializable_dict}
def save(self): def save(self, overwrite=True):
# pylint: disable=no-member # pylint: disable=no-member
if self.name is None: if self.name is None:
raise TypeError("Name cannot be None") raise TypeError("Name cannot be None")
ymlserializables = self._get_all_from_file() ymlsls = self._get_all_from_file()
if self.name in ([*ymlserializable][0] for ymlserializable in ymlserializables): if self.name in ([*ymlsl][0] for ymlsl in ymlsls):
ymlserializables[self.name] = self.config_dict if overwrite:
ymlsls[self.name] = self.config_dict
else: else:
ymlserializables.append(self.config_dict) raise ValueError(f"A {type(self).__name__} with name {self.name} already exists!")
print(ymlserializables) else:
self._save_all_to_file(ymlserializables) ymlsls.append(self.config_dict)
print(ymlsls)
self._save_all_to_file(ymlsls)
@classmethod @classmethod
def get(cls, identifier): def first_or_404(cls, **kwargs):
if isinstance(identifier, int): ymlsl = next(cls.get(**kwargs), None)
id = identifier if ymlsl is None:
ymlserializable_dict = next( abort(404)
ymlserializable for ymlserializable in else:
cls._get_all_from_file() if int(ymlserializable['id']) == id return ymlsl
)
return cls._ymlserializable_from_dict(ymlserializable_dict)
if isinstance(identifier, str):
name = identifier
ymlserializable_dict = next(
ymlserializable for ymlserializable in
cls._get_all_from_file() if [*ymlserializable][0] == name
)
return cls._ymlserializable_from_dict(ymlserializable_dict)
return None @classmethod
def get(cls, **kwargs):
"""Returns any matching instances
Filters all saved instances by specified properties.
All remaining items are returned as a list.
"""
if not any(kwargs):
return []
ymlsl_dicts = cls._get_all_from_file()
for key, value in kwargs.items():
# 'name' has to be evaluated separately; 'name' is the key of the entire object
if key == 'name':
ymlsl_dicts = (ymlsl_dict for ymlsl_dict in ymlsl_dicts
if [*ymlsl_dict][0] == value)
# For other keys, filter out any item that does not contain a key,
# or that not match the key's value
ymlsl_dicts = (ymlsl_dict for ymlsl_dict in ymlsl_dicts if ymlsl_dict.key == value)
# After each iteration: if no item is left, return None
if not any(ymlsl_dicts):
return []
ymlsls = []
for ymlsl_dict in ymlsl_dicts:
ymlsls.append(cls._ymlserializable_from_dict(ymlsl_dict))
return ymlsls
@classmethod @classmethod
def _ymlserializable_from_dict(cls, ymldict): def _ymlserializable_from_dict(cls, ymldict):
@ -100,33 +104,30 @@ class YamlSerializable(object):
ymldict['name'] = ymlserializable_name ymldict['name'] = ymlserializable_name
# Create empty instance # Create empty instance
ymlserializable = cls() ymlsl = cls()
# Fill instance with dict # Fill instance with dict
ymlserializable.__dict__ = ymldict ymlsl.__dict__ = ymldict
return ymlserializable return ymlsl
@classmethod @classmethod
def list(cls): def list(cls):
ymlserializables = cls._get_all_from_file() ymlsls = cls._get_all_from_file()
ymlserializables_list = [] ymlserializables_list = []
for ymlserializable in ymlserializables: for ymlsl in ymlsls:
name = [*ymlserializable][0] name = [*ymlsl][0]
ymlserializables_list.append(name) ymlserializables_list.append(name)
return ymlserializables_list return ymlserializables_list
@classmethod @classmethod
def _get_all_from_file(cls): def _get_all_from_file(cls):
with open(cls._storage_file) as file: with open(cls._storage_file) as file:
ymlserializables = yaml.load(file, yaml.FullLoader).get(cls._yaml_object_name) ymlsls = yaml.load(file, yaml.FullLoader).get(cls._yaml_object_name)
highest_id = max(ymlserializable.id for ymlserializable in ymlserializables) + 1
if highest_id > cls._id_counter: cls._id_counter = highest_id
return ymlserializables return ymlsls
@classmethod @classmethod
def _save_all_to_file(cls, ymlserializables): def _save_all_to_file(cls, ymlsls):
ymlserializables_object = {cls._yaml_object_name : ymlserializables} ymlserializables_object = {cls._yaml_object_name : ymlsls}
with open(cls._storage_file, 'w') as file: with open(cls._storage_file, 'w') as file:
yaml.dump(ymlserializables_object, file) yaml.dump(ymlserializables_object, file)

16
biscd/biscd/routes.py

@ -1,8 +1,9 @@
from flask import render_template, flash, abort, redirect, request, url_for, url_parse from flask import render_template, flash, abort, redirect, request, url_for
from flask_login import current_user, login_user, logout_user, login_required from flask_login import current_user, login_user, logout_user, login_required
from werkzeug.urls import url_parse
from biscd import app from biscd import app
from .models import Project, User from .models import Project, User
from .froms import NewProjectForm, LoginForm from .froms import NewProjectForm, LoginForm, RegistrationForm
@app.route('/', methods=['GET', 'POST']) @app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST']) @app.route('/index', methods=['GET', 'POST'])
@ -10,7 +11,7 @@ from .froms import NewProjectForm, LoginForm
def index(): def index():
form = NewProjectForm() form = NewProjectForm()
if form.validate_on_submit(): if form.validate_on_submit():
project = Project(form.projectname.data) project = Project.first_or_404(name=form.projectname.data)
project.save() project.save()
flash('You added a project!') flash('You added a project!')
project_names = Project.list() project_names = Project.list()
@ -22,7 +23,7 @@ def login():
return redirect(url_for('index')) return redirect(url_for('index'))
form = LoginForm() form = LoginForm()
if form.validate_on_submit(): if form.validate_on_submit():
user = User.get(form.username.data) user = User.first_or_404(name=form.username.data)
if user is None or not user.check_password(form.password.data): if user is None or not user.check_password(form.password.data):
flash('Invalid username or password') flash('Invalid username or password')
return redirect(url_for('login')) return redirect(url_for('login'))
@ -44,10 +45,9 @@ def register():
return redirect(url_for('index')) return redirect(url_for('index'))
form = RegistrationForm() form = RegistrationForm()
if form.validate_on_submit(): if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data) user = User(name=form.username.data, email=form.email.data)
user.set_password(form.password.data) user.set_password(form.password.data)
db.session.add(user) user.save()
db.session.commit()
flash('Congratulations, you are now a registered user!') flash('Congratulations, you are now a registered user!')
return redirect(url_for('login')) return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form) return render_template('register.html', title='Register', form=form)
@ -57,7 +57,7 @@ def register():
@login_required @login_required
def project_dashboard(project_name): def project_dashboard(project_name):
print(project_name) print(project_name)
project = Project.get(project_name) project = Project.get(name=project_name)
if project is None: if project is None:
abort(404) abort(404)
return render_template('project.html', project=project) return render_template('project.html', project=project)

Loading…
Cancel
Save