Browse Source

Pre-remove numeric ID

master
Burathar 4 years ago
parent
commit
87443ba73a
  1. 6
      biscd/biscd/froms.py
  2. 3
      biscd/biscd/models/__init__.py
  3. 1
      biscd/biscd/models/project.py
  4. 30
      biscd/biscd/models/user.py
  5. 82
      biscd/biscd/models/yaml_serializable.py
  6. 6
      biscd/biscd/routes.py

6
biscd/biscd/froms.py

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
from flask_wtf import FlaskForm
from wtforms import SubmitField, StringField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Email, EqualTo, Length
from wtforms.validators import DataRequired, Email, EqualTo, Length, ValidationError
from .models import User
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
@ -17,7 +19,7 @@ class RegistrationForm(FlaskForm): @@ -17,7 +19,7 @@ class RegistrationForm(FlaskForm):
submit = SubmitField('Register')
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
user = User(username.data)
if user is not None:
raise ValidationError('Please use a different username.')

3
biscd/biscd/models/__init__.py

@ -1,3 +1,6 @@ @@ -1,3 +1,6 @@
from .project import Project
from .user import User
from .yaml_serializable import YamlSerializable
User.initialize()
Project.initialize()

1
biscd/biscd/models/project.py

@ -8,6 +8,7 @@ class Project(YamlSerializable): @@ -8,6 +8,7 @@ class Project(YamlSerializable):
return Path(config.config_dir()) / 'projects.yaml'
@property
@classmethod
def _yaml_object_name(self):
return 'projects'

30
biscd/biscd/models/user.py

@ -1,17 +1,39 @@ @@ -1,17 +1,39 @@
from pathlib import Path
from werkzeug.security import generate_password_hash, check_password_hash
from biscd import config
from biscd.models import YamlSerializable
from .yaml_serializable import YamlSerializable
from biscd import login
class User(YamlSerializable):
class Project(YamlSerializable):
@property
def _storage_file(self):
return Path(config.config_dir()) / 'users.yaml'
@property
@classmethod
def _yaml_object_name(self):
return 'users'
def __init__(self, name=None, password=None, email=None):
def __init__(self, id=None, name=None, email=None, password=None, password_hash=None):
super().__init__(id)
self.name = name
self.password = password
self.password_hash = set_password(password, password_hash)
self.email = email
def set_password(password, password_hash):
if password_hash:
return password_hash
if password:
return generate_password_hash(password)
return None
def check_password(self, password):
if not password:
return False
return check_password_hash(self.password_hash, password)
@login.user_loader
def load_user(id):
return super.get(int(id))

82
biscd/biscd/models/yaml_serializable.py

@ -5,36 +5,56 @@ class MyMeta(metaclass=ABCMeta): @@ -5,36 +5,56 @@ class MyMeta(metaclass=ABCMeta):
required_attributes = []
def __call__(self, *args, **kwargs):
# pylint: disable=no-member
obj = super(MyMeta, self).__call__(*args, **kwargs)
for attr_name in obj.required_attributes:
if not getattr(obj, attr_name):
raise ValueError('required attribute (%s) not set' % attr_name)
return obj
class YamlSerializable(object, metaclass=MyMeta):
class YamlSerializable(object):
__metaclass__ = MyMeta
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):
pass
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
@property
@abstractmethod
def _storage_file(self):
def _storage_file(cls):
pass
@classmethod
@property
@abstractmethod
def _yaml_object_name(self):
def _yaml_object_name(cls):
pass
@property
def config_dict(self):
# pylint: disable=no-member
ymlserializable_dict = self.__dict__.copy()
ymlserializable_dict.pop('name')
return {self.name: ymlserializable_dict}
def save(self):
# pylint: disable=no-member
if self.name is None:
raise TypeError("Name cannot be None")
ymlserializables = self._get_all_from_file()
@ -46,19 +66,45 @@ class YamlSerializable(object, metaclass=MyMeta): @@ -46,19 +66,45 @@ class YamlSerializable(object, metaclass=MyMeta):
self._save_all_to_file(ymlserializables)
@classmethod
def get(cls, name):
ymlserializable_dict = next(ymlserializable for ymlserializable in cls._get_all_from_file() if [*ymlserializable][0] == name)
if ymlserializable_dict is None:
def get(cls, identifier):
if isinstance(identifier, int):
id = identifier
ymlserializable_dict = next(
ymlserializable for ymlserializable in
cls._get_all_from_file() if int(ymlserializable['id']) == id
)
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 _ymlserializable_from_dict(cls, ymldict):
if ymldict is None:
return None
ymlserializable_name = [*ymlserializable_dict][0]
ymlserializable_dict = ymlserializable_dict.get(ymlserializable_name)
ymlserializable_dict['name'] = ymlserializable_name
# Extract the name
ymlserializable_name = [*ymldict][0]
# Step into object
ymldict = ymldict.get(ymlserializable_name)
# Add name to dict
ymldict['name'] = ymlserializable_name
# Create empty instance
ymlserializable = cls()
ymlserializable.__dict__ = ymlserializable_dict
return ymlserializable
# Fill instance with dict
ymlserializable.__dict__ = ymldict
return ymlserializable
@classmethod
def list(cls):
@ -72,11 +118,15 @@ class YamlSerializable(object, metaclass=MyMeta): @@ -72,11 +118,15 @@ class YamlSerializable(object, metaclass=MyMeta):
@classmethod
def _get_all_from_file(cls):
with open(cls._storage_file) as file:
ymlserializables = yaml.load(file, yaml.FullLoader).get(_yaml_object_name)
return ymlserializables
ymlserializables = 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
@classmethod
def _save_all_to_file(cls, ymlserializables):
ymlserializables_object = {_yaml_object_name : ymlserializables}
ymlserializables_object = {cls._yaml_object_name : ymlserializables}
with open(cls._storage_file, 'w') as file:
yaml.dump(ymlserializables_object, file)

6
biscd/biscd/routes.py

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
from flask import render_template, flash, abort
from flask import render_template, flash, abort, redirect, request, url_for, url_parse
from flask_login import current_user, login_user, logout_user, login_required
from biscd import app
from .models import Project
from .models import Project, User
from .froms import NewProjectForm, LoginForm
@app.route('/', methods=['GET', 'POST'])
@ -22,7 +22,7 @@ def login(): @@ -22,7 +22,7 @@ def login():
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
user = User.get(form.username.data)
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))

Loading…
Cancel
Save