From 441d8fb6134c30a9b51bf3a1e4a844551cc73655 Mon Sep 17 00:00:00 2001 From: gazev Date: Sun, 17 Nov 2024 03:42:15 +0000 Subject: [PATCH] refactor: initial db schema --- app/api/members/routes.py | 25 ++--- app/models/models.py | 105 ++++++++++-------- app/services/member_service.py | 31 +++--- ...tion.py => 4d286a329466_init_migration.py} | 13 +-- 4 files changed, 86 insertions(+), 88 deletions(-) rename migrations/versions/{e096044621db_init_migration.py => 4d286a329466_init_migration.py} (89%) diff --git a/app/api/members/routes.py b/app/api/members/routes.py index 128c031..c127b6a 100644 --- a/app/api/members/routes.py +++ b/app/api/members/routes.py @@ -30,19 +30,19 @@ def create_member(): mandatory_schema = { "type": "object", "properties": { - "ist_id": {"type": "string"}, - "member_number": {"type": "number"}, - "name": {"type": "string"}, "username": {"type": "string"}, "password": {"type": "string"}, - "join_date": {"type": "string"}, - "course": {"type": "string"}, + "ist_id": {"type": "string"}, + "name": {"type": "string"}, "email": {"type": "string"}, + "course": {"type": "string"}, + "member_number": {"type": "number"}, + "join_date": {"type": "string"}, "exit_date": {"type": "string"}, "extra": {"type": "string"}, "description": {"type": "string"}, }, - "required": ["ist_id", "member_number", "name", "username", "password", "join_date", "course", "email"], + "required": ["username", "password", "ist_id", "name", "email", "course", "member_number", "join_date"], "additionalProperties": False } json_data = request.json @@ -315,19 +315,14 @@ def register(): mandatory_schema = { "type": "object", "properties": { - "ist_id": {"type": "string"}, - "member_number": {"type": "number"}, - "name": {"type": "string"}, "username": {"type": "string"}, "password": {"type": "string"}, - "join_date": {"type": "string"}, - "course": {"type": "string"}, + "ist_id": {"type": "string"}, + "name": {"type": "string"}, "email": {"type": "string"}, - "exit_date": {"type": "string"}, - "extra": {"type": "string"}, - "description": {"type": "string"}, + "course": {"type": "string"}, }, - "required": ["ist_id", "member_number", "name", "username", "password", "join_date", "course", "email"], + "required": ["username", "password", "ist_id", "name", "email", "course"], "additionalProperties": False } diff --git a/app/models/models.py b/app/models/models.py index 936f058..474f40c 100644 --- a/app/models/models.py +++ b/app/models/models.py @@ -28,11 +28,11 @@ class Member(db.Model): username = db.Column(db.String, primary_key=True, unique=True, nullable=False) _password = db.Column("password", db.String, nullable=False) ist_id = db.Column(db.String, nullable=False, unique=True) - member_number = db.Column(db.Integer, unique=True, nullable=False) name = db.Column(db.String, nullable=False) - join_date = db.Column(db.String, nullable=False) - course = db.Column(db.String, nullable=False) email = db.Column(db.String, nullable=False) + course = db.Column(db.String, nullable=False) + member_number = db.Column(db.Integer) + join_date = db.Column(db.String) exit_date = db.Column(db.String) description = db.Column(db.String) extra = db.Column(db.String) @@ -61,11 +61,11 @@ def __init__( username: str, password: str, ist_id: str, - member_number: int, name: str, - join_date: str, - course: str, email: str, + course: str, + member_number: int, + join_date: str, exit_date: str, description: str, extra: str, @@ -74,11 +74,11 @@ def __init__( self.username = username self.password = password self.ist_id = ist_id - self.member_number = member_number self.name = name - self.join_date = join_date - self.course = course self.email = email + self.course = course + self.member_number = member_number + self.join_date = join_date self.exit_date = exit_date self.description = description self.extra = extra @@ -95,47 +95,55 @@ def check_invariants(self): raise ValueError("Invalid username, only allowed characters in the ranges a-z A-Z and 0-9") # password - if not isinstance(self.password, str) or not self.password: - raise ValueError("Field 'password' must be a non-empty string.") + if not isinstance(self.password, str) or not self.password or \ + len(self.password) > 255: + raise ValueError("Field 'password' must be a non-empty string with max 255 characters.") # ist_id - if not isinstance(self.ist_id, str) or not self.ist_id: - raise ValueError("Field 'ist_id' must be a non-empty string.") + if not isinstance(self.ist_id, str) or not self.ist_id or \ + len(self.ist_id < 4) or len(self.ist_id) > 20 or \ + not self.ist_id.startswith("ist1"): + raise ValueError("Field 'ist_id' must be a valid IST student number.") + + # name + if not isinstance(self.name, str) or not self.name or \ + len(self.name) > 255: + raise ValueError("Field 'name' must be a non-empty string with max 255 characters.") + + # email + if not isinstance(self.email, str) or not self.email or \ + len(self.email) > 255: + raise ValueError("Field 'email' must be a non-empty string with max 255 characters.") + + # course + if not isinstance(self.course, str) or not self.course or \ + len(self.course) > 10: + raise ValueError("Field 'course' must be a non-empty string with max 8 characters.") # member_number - if not isinstance(self.member_number, int) or self.member_number < 0: + if not isinstance(self.member_number, int) or self.member_number <= 0: raise ValueError("Field 'member_number' must be a positive integer.") - # name - if not isinstance(self.name, str) or not self.name: - raise ValueError("Field 'name' must be a non-empty string.") - # join_date - if not isinstance(self.join_date, str) or not self.join_date: - raise ValueError("Field 'join_date' must be a non-empty string.") + if not isinstance(self.join_date, str): + raise ValueError("Field 'join_date' must be a string.") _validate_date_string(self.join_date, "join_date") - - # course - if not isinstance(self.course, str) or not self.course: - raise ValueError("Field 'course' must be a non-empty string.") - # email - if not isinstance(self.email, str) or not self.email: - raise ValueError("Field 'email' must be a non-empty string.") - # exit_date if not isinstance(self.exit_date, str): raise ValueError("Field 'exit_date' must be a string.") if self.exit_date != "": _validate_date_string(self.exit_date, "exit_date") - # description - if not isinstance(self.description, str): - raise ValueError("Field 'description' must be a string.") + # description + if not isinstance(self.description, str) or \ + len(self.description) > 512: + raise ValueError("Field 'description' must be a string with max 512 characters.") # extra - if not isinstance(self.extra, str): - raise ValueError("Field 'extra' must be a string.") + if not isinstance(self.extra, str) or \ + len(self.exit_date) > 512: + raise ValueError("Field 'extra' must be a string with max 512 characters.") # roles if not isinstance(self.roles, list) or (len(self.roles) == 1 and self.roles[0] == ""): @@ -146,15 +154,15 @@ def check_invariants(self): def to_dict(self): return { + "username": self.username, "ist_id": self.ist_id, - "member_number": self.member_number, "name": self.name, - "username": self.username, + "course": self.course, + "email": self.email, + "member_number": self.member_number, "join_date": self.join_date, "exit_date": self.exit_date, - "course": self.course, "description": self.description, - "email": self.email, "extra": self.extra, "roles": self.roles, } @@ -190,25 +198,25 @@ def post_init(self): def check_invariants(self): # name - if not isinstance(self.name, str) or not self.name: - raise ValueError("name must be a non-empty string.") - if len(self.name) > 255: - raise ValueError("Invalid name, please keep it shorter...") + if not isinstance(self.name, str) or not self.name or \ + len(self.name) > 255: + raise ValueError("Field 'name' must be a non-empty string with max 255 characters.") if not re.match(r"^[a-zA-Z0-9-_~]+$", self.name): raise ValueError("Invalid username, only allowed characters in the ranges a-z A-Z and 0-9 and ' ' '-' '_' '~'") # state - if not isinstance(self.state, str) or not self.state: - raise ValueError("'username' must be a non-empty string.") + if not isinstance(self.state, str) or not self.state: # TODO add states enum + raise ValueError("Field 'state' must be a non-empty string.") if not isinstance(self.start_date, str) or not self.start_date: - raise ValueError("'start_date' must be a non-empty string.") + raise ValueError("Field 'start_date' must be a non-empty string.") # start_date _validate_date_string(self.start_date, "start") # description - if not isinstance(self.description, str): - raise ValueError("'description' must be a string.") + if not isinstance(self.description, str) or \ + len(self.description) > 512: + raise ValueError("Field 'description' must be a string with max 512 characters") def to_dict(self): return { @@ -249,8 +257,9 @@ def check_invariants(self): _validate_date_string(self.entry_date, "entry_date") # contributinos - if not isinstance(self.contributions, str): - raise ValueError("Field 'contributions' must be a string.") + if not isinstance(self.contributions, str) or \ + len(self.contributions) > 512: + raise ValueError("Field 'contributions' must be a string with max 512 characters.") # exit_date if not isinstance(self.exit_date, str): diff --git a/app/services/member_service.py b/app/services/member_service.py index 1e7e917..f50fdac 100644 --- a/app/services/member_service.py +++ b/app/services/member_service.py @@ -13,11 +13,11 @@ def create_member( username: str, password: str, ist_id: str, - member_number: int, name: str, - join_date: str, - course: str, email: str, + course: str, + member_number: int, + join_date: str, exit_date: str = "", description: str = "", extra: str = "", @@ -26,11 +26,11 @@ def create_member( username=username, password=password, ist_id=ist_id, - member_number=member_number, name=name, - join_date=join_date, - course=course, email=email, + course=course, + member_number=member_number, + join_date=join_date, exit_date=exit_date, description=description, extra=extra, @@ -118,27 +118,22 @@ def create_applicant( username: str, password: str, ist_id: str, - member_number: int, name: str, - join_date: str, - course: str, email: str, - exit_date: str = "", - description: str = "", - extra: str = "", + course: str, ) -> Member : new_member = Member( username=username, password=password, ist_id=ist_id, - member_number=member_number, name=name, - join_date=join_date, - course=course, email=email, - exit_date=exit_date, - description=description, - extra=extra, + course=course, + member_number=0, + join_date="", + exit_date="", + description="", + extra="", roles=["applicant"] ) db.session.add(new_member) diff --git a/migrations/versions/e096044621db_init_migration.py b/migrations/versions/4d286a329466_init_migration.py similarity index 89% rename from migrations/versions/e096044621db_init_migration.py rename to migrations/versions/4d286a329466_init_migration.py index 376e627..1b6d5c3 100644 --- a/migrations/versions/e096044621db_init_migration.py +++ b/migrations/versions/4d286a329466_init_migration.py @@ -1,8 +1,8 @@ """init migration -Revision ID: e096044621db +Revision ID: 4d286a329466 Revises: -Create Date: 2024-11-14 23:41:44.681174 +Create Date: 2024-11-17 03:37:52.272638 """ from alembic import op @@ -10,7 +10,7 @@ # revision identifiers, used by Alembic. -revision = 'e096044621db' +revision = '4d286a329466' down_revision = None branch_labels = None depends_on = None @@ -22,18 +22,17 @@ def upgrade(): sa.Column('username', sa.String(), nullable=False), sa.Column('password', sa.String(), nullable=False), sa.Column('ist_id', sa.String(), nullable=False), - sa.Column('member_number', sa.Integer(), nullable=False), sa.Column('name', sa.String(), nullable=False), - sa.Column('join_date', sa.String(), nullable=False), - sa.Column('course', sa.String(), nullable=False), sa.Column('email', sa.String(), nullable=False), + sa.Column('course', sa.String(), nullable=False), + sa.Column('member_number', sa.Integer(), nullable=True), + sa.Column('join_date', sa.String(), nullable=True), sa.Column('exit_date', sa.String(), nullable=True), sa.Column('description', sa.String(), nullable=True), sa.Column('extra', sa.String(), nullable=True), sa.Column('roles', sa.String(), nullable=True), sa.PrimaryKeyConstraint('username'), sa.UniqueConstraint('ist_id'), - sa.UniqueConstraint('member_number'), sa.UniqueConstraint('username') ) op.create_table('projects',