diff --git a/app/database/models.py b/app/database/models.py index 3d6aaa2..3f433a0 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -1,9 +1,18 @@ +import enum +from datetime import datetime + +from pytz import timezone from sqlalchemy import Text from sqlalchemy.dialects.mysql import TINYINT, INTEGER from sqlalchemy.orm import validates from app.utils.errors import InvalidArgumentError from app.database import db +from app.utils.validators import is_valid_email, has_valid_length, \ + is_valid_phone + +PASSWORD_LENGTH_MINIMUM = 4 +PASSWORD_LENGTH_MAXIMUM = 8 class Library(db.Model): @@ -41,3 +50,76 @@ def validate_not_empty(self, key, field): def __repr__(self): return '' % self.name + + +class SessionEnum(enum.Enum): + first = "09:00" + second = "10:00" + + +def now_at_seoul(): + return datetime.now(tz=timezone('Asia/Seoul')) + + +class TimestampMixin(object): + created_at = db.Column(db.DateTime, + nullable=False, + default=now_at_seoul) + updated_at = db.Column(db.DateTime, + nullable=True, + onupdate=now_at_seoul) + + +class Speaker(db.Model, TimestampMixin): + _id = db.Column('id', db.Integer, primary_key=True, + autoincrement=True) + name = db.Column(db.String(30), nullable=False) + email = db.Column(db.String(30), nullable=False, unique=True) + phone = db.Column(db.String(30), nullable=False) + + password = db.Column(db.String(30), nullable=False) + is_email_verified = db.Column(db.Boolean, default=False) + email_sended_at = db.Column(db.DateTime, nullable=True) + session_time = db.Column(db.Enum(SessionEnum), nullable=False) + library_id = db.Column(INTEGER(11, unsigned=True), + db.ForeignKey('Libraries.id'), + nullable=False) + library = db.relationship('Library', lazy=True, + backref=db.backref('speakers', lazy=True)) + + @validates('name') + def validate_not_empty(self, key, field): + if not field: + raise InvalidArgumentError("{} must be not empty.".format(key)) + + return field + + @validates('email') + def validate_email_format(self, key, field): + if not is_valid_email(field): + raise InvalidArgumentError( + "{} must be fitted in email format.".format(key)) + + return field + + @validates('password') + def validate_password_length(self, key, field): + if not has_valid_length(field, + PASSWORD_LENGTH_MINIMUM, + PASSWORD_LENGTH_MAXIMUM): + raise InvalidArgumentError( + "{}'s length must be {0} ~ {0}.".format( + key, PASSWORD_LENGTH_MINIMUM, PASSWORD_LENGTH_MAXIMUM)) + + return field + + @validates('phone') + def validate_phone_format(self, key, field): + if not is_valid_phone(field): + raise InvalidArgumentError( + "{} must be fitted in phone number format.".format(key)) + + return field + + def __repr__(self): + return '<%r %r>' % (self.__class__.__name__, self.name) diff --git a/app/utils/validators.py b/app/utils/validators.py new file mode 100644 index 0000000..addc592 --- /dev/null +++ b/app/utils/validators.py @@ -0,0 +1,10 @@ +def is_valid_email(field): + return True + + +def has_valid_length(field, length_min, length_max): + return True + + +def is_valid_phone(field): + return True diff --git a/migrations/versions/6c0fbb516dfd_.py b/migrations/versions/6c0fbb516dfd_.py new file mode 100644 index 0000000..c2c91cb --- /dev/null +++ b/migrations/versions/6c0fbb516dfd_.py @@ -0,0 +1,51 @@ +"""empty message + +Revision ID: 6c0fbb516dfd +Revises: ad3c95a8a551 +Create Date: 2018-04-11 23:34:31.356913 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '6c0fbb516dfd' +down_revision = 'ad3c95a8a551' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('speaker', + sa.Column('created_at', sa.DateTime(), nullable=False), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=30), nullable=False), + sa.Column('email', sa.String(length=30), nullable=False), + sa.Column('phone', sa.String(length=30), nullable=False), + sa.Column('password', sa.String(length=30), nullable=False), + sa.Column('is_email_verified', sa.Boolean(), nullable=True), + sa.Column('email_sended_at', sa.DateTime(), nullable=True), + sa.Column('session_time', sa.Enum('first', 'second', name='sessionenum'), nullable=False), + sa.Column('library_id', mysql.INTEGER(display_width=11, unsigned=True), nullable=False), + sa.ForeignKeyConstraint(['library_id'], ['Libraries.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + op.alter_column('Libraries', 'name', + existing_type=mysql.VARCHAR(length=20), + nullable=True) + op.drop_index('name', table_name='Libraries') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_index('name', 'Libraries', ['name'], unique=True) + op.alter_column('Libraries', 'name', + existing_type=mysql.VARCHAR(length=20), + nullable=False) + op.drop_table('speaker') + # ### end Alembic commands ###