Skip to content

Commit

Permalink
Merge pull request #30 from dinesh-aot/project_table
Browse files Browse the repository at this point in the history
project table created
  • Loading branch information
dinesh-aot authored Aug 22, 2024
2 parents 6cdbf32 + cf8cec9 commit 87b1a39
Show file tree
Hide file tree
Showing 13 changed files with 3,358 additions and 5 deletions.
3,133 changes: 3,133 additions & 0 deletions compliance-api/migrations/seed_data/project.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""project table creation
Revision ID: d36894553777
Revises: 774870c99c95
Create Date: 2024-08-21 12:57:47.220758
"""
from alembic import op
import sqlalchemy as sa
import json
import os


# revision identifiers, used by Alembic.
revision = 'd36894553777'
down_revision = '774870c99c95'
branch_labels = None
depends_on = None

def load_json_file(file_name):
"""Load JSON data from a file."""
file_path = os.path.join(os.path.dirname(__file__), file_name)
with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file)

def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
projects_table = op.create_table('projects',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('description', sa.String(), nullable=True),
sa.Column('ea_certificate', sa.String(length=255), nullable=True),
sa.Column('proponent_name', sa.String(), nullable=False),
sa.Column('created_date', sa.DateTime(), nullable=False),
sa.Column('updated_date', sa.DateTime(), nullable=True),
sa.Column('created_by', sa.String(length=100), nullable=False),
sa.Column('updated_by', sa.String(length=100), nullable=True),
sa.Column('is_active', sa.Boolean(), server_default='t', nullable=False),
sa.Column('is_deleted', sa.Boolean(), server_default='f', nullable=False),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('case_files', schema=None) as batch_op:
batch_op.alter_column('lead_officer_id',
existing_type=sa.INTEGER(),
comment='The lead officer who created the case file',
existing_nullable=True)
batch_op.create_foreign_key('case_files_project_id_projects_id_fkey', 'projects', ['project_id'], ['id'])
projects_data = load_json_file("../seed_data/project.json")
op.bulk_insert(projects_table, projects_data)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('case_files', schema=None) as batch_op:
batch_op.drop_constraint('case_files_project_id_projects_id_fkey', type_='foreignkey')
batch_op.alter_column('lead_officer_id',
existing_type=sa.INTEGER(),
comment=None,
existing_comment='The lead officer who created the case file',
existing_nullable=True)
op.drop_table('projects')
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions compliance-api/src/compliance_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
from .case_file import CASE_FILE_INITIATION_MAP, CaseFile, CaseFileInitiationEnum, CaseFileOfficer
from .db import db, ma, migrate
from .position import Position
from .project import Project
from .staff_user import PERMISSION_MAP, PermissionEnum, StaffUser
4 changes: 4 additions & 0 deletions compliance-api/src/compliance_api/models/case_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class CaseFile(BaseModel):
)
project_id = Column(
Integer,
ForeignKey("projects.id", name="case_files_project_id_projects_id_fkey"),
nullable=False,
comment="The unique identifier of the project associated with the case file",
)
Expand Down Expand Up @@ -74,6 +75,9 @@ class CaseFile(BaseModel):
lead_officer = relationship(
"StaffUser", foreign_keys=[lead_officer_id], lazy="joined"
)
project = relationship(
"Project", foreign_keys=[project_id], lazy="joined"
)
case_file_officers = relationship(
"CaseFileOfficer",
back_populates="case_file",
Expand Down
23 changes: 23 additions & 0 deletions compliance-api/src/compliance_api/models/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Project Model."""

from sqlalchemy import Column, Integer, String

from .base_model import BaseModel


class Project(BaseModel):
"""Project Model Class."""

__tablename__ = "projects"

id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
description = Column(String, nullable=True)
ea_certificate = Column(String(255), nullable=True, default=None)
proponent_name = Column(String, nullable=False)

def __setattr__(self, key, value):
"""Set attribute value."""
if hasattr(self, key):
raise AttributeError(f"Cannot modify {key}. This class is read-only.")
super().__setattr__(key, value)
2 changes: 2 additions & 0 deletions compliance-api/src/compliance_api/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from .case_file import API as CASE_FILE_API
from .ops import API as OPS_API
from .position import API as POSITION_API
from .project import API as PROJECT_API
from .staff_user import API as USER_API


Expand Down Expand Up @@ -67,3 +68,4 @@
API.add_namespace(POSITION_API)
API.add_namespace(AGENCY_API)
API.add_namespace(CASE_FILE_API)
API.add_namespace(PROJECT_API)
67 changes: 67 additions & 0 deletions compliance-api/src/compliance_api/resources/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""API endpoints for managing project resource."""

from http import HTTPStatus

from flask_restx import Namespace, Resource

from compliance_api.auth import auth
from compliance_api.exceptions import ResourceNotFoundError
from compliance_api.schemas import ProjectSchema
from compliance_api.services import ProjectService
from compliance_api.utils.util import cors_preflight

from .apihelper import Api as ApiHelper


API = Namespace("projects", description="Endpoints for Project Management")
project_list_model = ApiHelper.convert_ma_schema_to_restx_model(
API, ProjectSchema(), "ProjectListSchema"
)


@cors_preflight("GET, OPTIONS")
@API.route("", methods=["POST", "GET", "OPTIONS"])
class Projects(Resource):
"""Resource for managing projects."""

@staticmethod
@API.response(code=200, description="Success", model=[project_list_model])
@ApiHelper.swagger_decorators(API, endpoint_description="Fetch all agencies")
@auth.require
def get():
"""Fetch all projects."""
projects = ProjectService.get_all_projects()
project_list_schema = ProjectSchema(many=True)
return project_list_schema.dump(projects), HTTPStatus.OK


@cors_preflight("GET, OPTIONS")
@API.route("/<int:project_id>", methods=["GET", "OPTIONS"])
@API.doc(params={"project_id": "The unique identifier of project"})
class Project(Resource):
"""Resource for managing a single project."""

@staticmethod
@auth.require
@ApiHelper.swagger_decorators(API, endpoint_description="Fetch a project by id")
@API.response(code=200, model=project_list_model, description="Success")
@API.response(404, "Not Found")
def get(project_id):
"""Fetch an project by id."""
project = ProjectService.get_project_by_id(project_id)
if not project:
raise ResourceNotFoundError(f"Project with {project_id} not found")
return ProjectSchema().dump(project), HTTPStatus.OK
1 change: 1 addition & 0 deletions compliance-api/src/compliance_api/schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
from .agency import AgencyCreateSchema, AgencySchema
from .case_file import CaseFileCreateSchema, CaseFileSchema
from .common import KeyValueSchema
from .project import ProjectSchema
from .staff_user import StaffUserCreateSchema, StaffUserSchema
16 changes: 13 additions & 3 deletions compliance-api/src/compliance_api/schemas/case_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from compliance_api.models import CaseFile, CaseFileInitiationEnum, CaseFileOfficer

from .base_schema import AutoSchemaBase, BaseSchema
from .project import ProjectSchema
from .staff_user import StaffUserSchema


Expand All @@ -43,12 +44,21 @@ class Meta(AutoSchemaBase.Meta): # pylint: disable=too-few-public-methods
include_fk = True

lead_officer = fields.Nested(StaffUserSchema, dump_only=True)
project = fields.Nested(
ProjectSchema,
dump_only=True,
exclude=["description", "ea_certificate", "proponent_name", "is_active"],
)

@post_dump
def handle_initiation_enum(self, data, **kwargs): # pylint: disable=unused-argument, no-self-use
def handle_initiation_enum(
self, data, **kwargs
): # pylint: disable=unused-argument, no-self-use
"""Convert the initiation enum to its string representation."""
if 'initiation' in data and isinstance(data['initiation'], CaseFileInitiationEnum):
data['initiation'] = data['initiation'].value
if "initiation" in data and isinstance(
data["initiation"], CaseFileInitiationEnum
):
data["initiation"] = data["initiation"].value
return data


Expand Down
30 changes: 30 additions & 0 deletions compliance-api/src/compliance_api/schemas/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Project Schema."""
from marshmallow import EXCLUDE

from compliance_api.models.project import Project as ProjectModel

from .base_schema import AutoSchemaBase


class ProjectSchema(AutoSchemaBase): # pylint: disable=too-many-ancestors
"""Project schema."""

class Meta(AutoSchemaBase.Meta): # pylint: disable=too-few-public-methods
"""Exclude unknown fields in the deserialized output."""

unknown = EXCLUDE
model = ProjectModel
include_fk = True
1 change: 1 addition & 0 deletions compliance-api/src/compliance_api/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
from .agency import AgencyService
from .case_file import CaseFileService
from .position import PositionService
from .project import ProjectService
from .staff_user import StaffUserService
4 changes: 2 additions & 2 deletions compliance-api/src/compliance_api/services/agency.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ class AgencyService:

@classmethod
def get_agency_by_id(cls, agency_id):
"""Get user by id."""
"""Get agency by id."""
agency = AgencyModel.find_by_id(agency_id)
return agency

@classmethod
def get_all_agencies(cls):
"""Get all users."""
"""Get all agencies."""
users = AgencyModel.get_all()
return users

Expand Down
18 changes: 18 additions & 0 deletions compliance-api/src/compliance_api/services/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Service for project resource management."""
from compliance_api.models import Project as ProjectModel


class ProjectService:
"""Project management service."""

@classmethod
def get_project_by_id(cls, project_id):
"""Get project by id."""
project = ProjectModel.find_by_id(project_id)
return project

@classmethod
def get_all_projects(cls):
"""Get all projects."""
projects = ProjectModel.get_all()
return projects

0 comments on commit 87b1a39

Please sign in to comment.