Skip to content

Commit

Permalink
Moved notes to work for status and issues (#1215)
Browse files Browse the repository at this point in the history
changed approval to issue_description
  • Loading branch information
saravanpa-aot authored Nov 10, 2023
1 parent 8dd6902 commit f846b1f
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""columns moves to work
Revision ID: 74531216e674
Revises: 73c6150268e5
Create Date: 2023-11-10 10:19:54.637944
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '74531216e674'
down_revision = '73c6150268e5'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('work_issue_updates', schema=None) as batch_op:
batch_op.add_column(sa.Column('is_approved', sa.Boolean(), nullable=False, server_default='false'))
batch_op.add_column(sa.Column('approved_by', sa.String(length=255), nullable=True))

with op.batch_alter_table('work_issue_updates_history', schema=None) as batch_op:
batch_op.add_column(sa.Column('is_approved', sa.Boolean(), autoincrement=False, nullable=False, server_default='false'))
batch_op.add_column(sa.Column('approved_by', sa.String(length=255), autoincrement=False, nullable=True))

with op.batch_alter_table('work_issues', schema=None) as batch_op:
batch_op.drop_column('is_approved')
batch_op.drop_column('approved_by')

with op.batch_alter_table('work_issues_history', schema=None) as batch_op:
batch_op.drop_column('is_approved')
batch_op.drop_column('approved_by')

with op.batch_alter_table('work_statuses', schema=None) as batch_op:
batch_op.drop_column('notes')

with op.batch_alter_table('work_statuses_history', schema=None) as batch_op:
batch_op.alter_column('posted_date',
existing_type=sa.DATE(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
autoincrement=False)
batch_op.drop_column('notes')

with op.batch_alter_table('works', schema=None) as batch_op:
batch_op.add_column(sa.Column('status_notes', sa.Text(), nullable=True))
batch_op.add_column(sa.Column('issue_notes', sa.Text(), nullable=True))

with op.batch_alter_table('works_history', schema=None) as batch_op:
batch_op.add_column(sa.Column('status_notes', sa.Text(), autoincrement=False, nullable=True))
batch_op.add_column(sa.Column('issue_notes', sa.Text(), autoincrement=False, nullable=True))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('works_history', schema=None) as batch_op:
batch_op.drop_column('issue_notes')
batch_op.drop_column('status_notes')

with op.batch_alter_table('works', schema=None) as batch_op:
batch_op.drop_column('issue_notes')
batch_op.drop_column('status_notes')

with op.batch_alter_table('work_statuses_history', schema=None) as batch_op:
batch_op.add_column(sa.Column('notes', sa.TEXT(), autoincrement=False, nullable=True))
batch_op.alter_column('posted_date',
existing_type=sa.DateTime(timezone=True),
type_=sa.DATE(),
existing_nullable=False,
autoincrement=False)

with op.batch_alter_table('work_statuses', schema=None) as batch_op:
batch_op.add_column(sa.Column('notes', sa.TEXT(), autoincrement=False, nullable=True))

with op.batch_alter_table('work_issues_history', schema=None) as batch_op:
batch_op.add_column(sa.Column('approved_by', sa.VARCHAR(length=255), autoincrement=False, nullable=True))
batch_op.add_column(sa.Column('is_approved', sa.BOOLEAN(), autoincrement=False, nullable=False, server_default='false'))

with op.batch_alter_table('work_issues', schema=None) as batch_op:
batch_op.add_column(sa.Column('approved_by', sa.VARCHAR(length=255), autoincrement=False, nullable=True))
batch_op.add_column(sa.Column('is_approved', sa.BOOLEAN(), autoincrement=False, nullable=False, server_default='false'))

with op.batch_alter_table('work_issue_updates_history', schema=None) as batch_op:
batch_op.drop_column('approved_by')
batch_op.drop_column('is_approved')

with op.batch_alter_table('work_issue_updates', schema=None) as batch_op:
batch_op.drop_column('approved_by')
batch_op.drop_column('is_approved')

# ### end Alembic commands ###
4 changes: 2 additions & 2 deletions epictrack-api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ greenlet==3.0.1
gunicorn==21.2.0
idna==3.4
importlib-metadata==6.8.0
importlib-resources==6.1.0
importlib-resources==6.1.1
itsdangerous==2.1.2
jsonschema-specifications==2023.7.1
jsonschema==4.19.2
Expand All @@ -52,7 +52,7 @@ pytz==2023.3.post1
referencing==0.30.2
reportlab==3.6.12
requests==2.31.0
rpds-py==0.10.6
rpds-py==0.12.0
rsa==4.9
six==1.16.0
typing_extensions==4.8.0
Expand Down
2 changes: 2 additions & 0 deletions epictrack-api/src/api/models/work.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class Work(BaseModelVersioned):
project_tracking_number = Column(String(255), nullable=True, default=None)
work_tracking_number = Column(String(255), nullable=True, default=None)
first_nation_notes = Column(String)
status_notes = Column(Text)
issue_notes = Column(Text)

start_date = Column(DateTime(timezone=True))
anticipated_decision_date = Column(DateTime(timezone=True))
Expand Down
5 changes: 4 additions & 1 deletion epictrack-api/src/api/models/work_issue_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
"""Model to handle all operations related to Issues."""

from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from sqlalchemy.orm import relationship

from .base_model import BaseModelVersioned
Expand All @@ -27,6 +27,9 @@ class WorkIssueUpdates(BaseModelVersioned):
id = Column(Integer, primary_key=True, autoincrement=True)
description = Column(String(2000), nullable=False)

is_approved = Column(Boolean(), default=False, nullable=True)
approved_by = Column(String(255), default=None, nullable=True)

work_issue_id = Column(ForeignKey('work_issues.id'), nullable=False)
work_issue = relationship('WorkIssues', back_populates='updates')

Expand Down
2 changes: 0 additions & 2 deletions epictrack-api/src/api/models/work_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ class WorkIssues(BaseModelVersioned):
is_high_priority = Column(Boolean(), default=False, nullable=False)
start_date = Column(DateTime(timezone=True), nullable=False)
expected_resolution_date = Column(DateTime(timezone=True), nullable=True)
is_approved = Column(Boolean(), default=False, nullable=False)
approved_by = Column(String(255), default=None, nullable=True)

work_id = Column(ForeignKey('works.id'), nullable=False)
work = relationship('Work', foreign_keys=[work_id], lazy='select')
Expand Down
3 changes: 1 addition & 2 deletions epictrack-api/src/api/models/work_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from typing import List

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text, DateTime, desc
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, DateTime, desc
from sqlalchemy.orm import relationship

from .base_model import BaseModelVersioned
Expand All @@ -29,7 +29,6 @@ class WorkStatus(BaseModelVersioned):

id = Column(Integer, primary_key=True, autoincrement=True)
description = Column(String(2000), nullable=False)
notes = Column(Text)
posted_date = Column(DateTime(timezone=True), nullable=False)
posted_by = Column(String(100), nullable=True)
work_id = Column(ForeignKey('works.id'), nullable=False)
Expand Down
18 changes: 17 additions & 1 deletion epictrack-api/src/api/resources/work.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from api.utils import auth, profiletime
from api.utils.util import cors_preflight


API = Namespace("works", description="Works")


Expand Down Expand Up @@ -317,6 +316,23 @@ def patch(work_id):
return res.WorkResourceResponseSchema().dump(work), HTTPStatus.OK


@cors_preflight("PATCH")
@API.route("/<int:work_id>/notes", methods=["PATCH", "OPTIONS"])
class WorkNotes(Resource):
"""Endpoints to handle work related notes"""

@staticmethod
@cors.crossdomain(origin="*")
@auth.require
@profiletime
def patch(work_id):
"""Save the notes to corresponding work"""
req.WorkIdPathParameterSchema().load(request.view_args)
notes = req.WorkNotesBodySchema().load(API.payload)
work = WorkService.save_notes(work_id, notes)
return res.WorkResourceResponseSchema().dump(work), HTTPStatus.OK


@cors_preflight("GET, PUT")
@API.route("/first-nations/<int:work_nation_id>", methods=["GET", "PUT", "OPTIONS"])
class WorkFirstNation(Resource):
Expand Down
15 changes: 6 additions & 9 deletions epictrack-api/src/api/resources/work_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def put(work_id, issue_id):
return res.WorkIssuesResponseSchema().dump(work_issues), HTTPStatus.CREATED


@API.route("/<int:issue_id>/issue_update", methods=["POST"])
@API.route("/<int:issue_id>/update", methods=["POST"])
class WorkIssueUpdate(Resource):
"""Endpoint resource to manage updates for a specific issue."""

Expand All @@ -84,20 +84,17 @@ def post(work_id, issue_id):


@cors_preflight("PATCH")
@API.route("/<int:issue_id>/approve", methods=["PATCH", "OPTIONS"])
@API.route("/<int:issue_id>/update/<int:update_id>/approve", methods=["PATCH", "OPTIONS"])
class ApproveIssues(Resource):
"""Endpoint resource to manage approving of work status."""

@staticmethod
@cors.crossdomain(origin="*")
@auth.require
@profiletime
def patch(work_id, issue_id):
# pylint: disable=unused-argument
def patch(work_id, issue_id, update_id):
"""Approve a work status."""
existing_work_issues = WorkIssuesService.find_work_issue_by_id(work_id, issue_id)
if existing_work_issues is None:
return {"message": "Work issues not found"}, HTTPStatus.NOT_FOUND
approved_work_issues = WorkIssuesService.approve_work_issues(issue_id, update_id)

approved_work_issues = WorkIssuesService.approve_work_issues(existing_work_issues)

return res.WorkIssuesResponseSchema().dump(approved_work_issues), HTTPStatus.OK
return res.WorkIssueUpdatesResponseSchema().dump(approved_work_issues), HTTPStatus.OK
16 changes: 0 additions & 16 deletions epictrack-api/src/api/resources/work_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,3 @@ def patch(work_id, status_id):
approved_work_status = WorkStatusService.approve_work_status(existing_work_status)

return res.WorkStatusResponseSchema().dump(approved_work_status), HTTPStatus.OK


@cors_preflight("PATCH")
@API.route("/<int:status_id>/notes", methods=["PATCH", "OPTIONS"])
class StatusNotes(Resource):
"""Endpoints to handle status notes"""

@staticmethod
@cors.crossdomain(origin="*")
@auth.require
@profiletime
def patch(work_id, status_id):
"""Save the notes to corresponding work"""
notes = req.WorkStatusNotesBodySchema().load(API.payload)["notes"]
work_status = WorkStatusService.save_notes(work_id, status_id, notes)
return res.WorkStatusResponseSchema().dump(work_status), HTTPStatus.OK
2 changes: 1 addition & 1 deletion epictrack-api/src/api/schemas/request/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@
WorkBodyParameterSchema, WorkExistenceQueryParamSchema, WorkFirstNationImportBodyParamSchema,
WorkFirstNationNotesBodySchema, WorkIdPathParameterSchema, WorkIdPhaseIdPathParameterSchema,
WorkPlanDownloadQueryParamSchema, WorkTypeIdQueryParamSchema, WorkStatusParameterSchema,
WorkIssuesParameterSchema, WorkIssuesUpdateSchema, WorkStatusNotesBodySchema)
WorkIssuesParameterSchema, WorkIssuesUpdateSchema, WorkNotesBodySchema)
from .act_section_request import ActSectionQueryParameterSchema
10 changes: 8 additions & 2 deletions epictrack-api/src/api/schemas/request/work_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,17 @@ class WorkIssuesUpdateSchema(WorkIssuesParameterSchema):
)


class WorkStatusNotesBodySchema(RequestBodyParameterSchema):
"""Work status notes body parameter schema"""
class WorkNotesBodySchema(RequestBodyParameterSchema):
"""Work notes body parameter schema"""

notes = fields.Str(
metadata={"description": "Work status notes"},
validate=validate.Length(min=1),
required=True,
)

note_type = fields.Str(
metadata={"description": "Type of work status notes"},
validate=validate.OneOf(['status_notes', 'issue_notes']), # Add your predefined types
required=True,
)
5 changes: 5 additions & 0 deletions epictrack-api/src/api/schemas/response/work_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ class Meta(
responsible_epd = fields.Nested(StaffSchema, exclude=("position",), dump_only=True)
work_lead = fields.Nested(StaffSchema, exclude=("position",), dump_only=True)
staff = fields.Nested(WorkStaffRoleReponseSchema(many=True), dump_default=[])
work_state = fields.Method("get_work_state")

def get_work_state(self, obj: Work) -> str:
"""Return the work state"""
return obj.work_state.value if obj.work_state else None


class WorkPhaseAdditionalInfoResponseSchema(Schema):
Expand Down
23 changes: 23 additions & 0 deletions epictrack-api/src/api/services/work.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,29 @@ def save_first_nation_notes(cls, work_id: int, notes: str) -> Work:
work.save()
return work

@classmethod
def save_notes(cls, work_id: int, notes_payload: dict) -> Work:
"""Save notes to the given column in the work."""
# if column name cant map the type in the UI , add it here..
note_type_mapping = {
'first_nation': 'first_nation_notes',
}

work = cls.find_by_id(work_id)
notes = notes_payload.get("notes")
note_type = notes_payload.get("note_type")

if hasattr(work, note_type):
setattr(work, note_type, notes)
else:
mapped_column = note_type_mapping.get(note_type)
if mapped_column is None:
raise ResourceExistsError(f"No work note type {note_type} nation association found")
setattr(work, mapped_column, notes)

work.save()
return work

@classmethod
def find_work_first_nation(cls, work_nation_id: int) -> IndigenousWork:
"""Find work indigenous nation by id"""
Expand Down
19 changes: 11 additions & 8 deletions epictrack-api/src/api/services/work_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"""Service to manage Work status."""
from typing import Dict, List

from api.models import WorkIssueUpdates as WorkIssueUpdatesModel
from api.exceptions import ResourceNotFoundError
from api.models import WorkIssueUpdates as WorkIssueUpdatesModel
from api.models import WorkIssues as WorkIssuesModel
from api.utils import TokenInfo

Expand Down Expand Up @@ -71,17 +71,20 @@ def add_work_issue_update(cls, work_id, issue_id, description_data: List[str]):
return WorkIssuesModel.find_by_id(issue_id)

@classmethod
def approve_work_issues(cls, work_issues):
def approve_work_issues(cls, issue_id, update_id):
"""Approve a work status."""
if work_issues.is_approved:
return work_issues
results = WorkIssueUpdatesModel.find_by_params({"id": update_id, "work_issue_id": issue_id})
if not results:
raise ResourceNotFoundError("Work issue Description doesnt exist")

work_issues.is_approved = True
work_issues.approved_by = TokenInfo.get_username()
work_issue_update: WorkIssueUpdatesModel = results[0]

work_issues.save()
work_issue_update.is_approved = True
work_issue_update.approved_by = TokenInfo.get_username()

return work_issues
work_issue_update.save()

return work_issue_update

@classmethod
def edit_issue_update(cls, work_id, issue_id, issue_data):
Expand Down
13 changes: 1 addition & 12 deletions epictrack-api/src/api/services/work_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""Service to manage Work status."""
from http import HTTPStatus
from typing import Dict
from api.exceptions import ResourceNotFoundError

from api.exceptions import BusinessError
from api.models import WorkStatus as WorkStatusModel
from api.utils import TokenInfo
Expand Down Expand Up @@ -62,17 +62,6 @@ def update_work_status(cls, work_status: WorkStatusModel, work_status_data: dict

return work_status

@classmethod
def save_notes(cls, work_id, status_id, notes: str) -> WorkStatusModel:
"""Save a work status note."""
work_status: WorkStatusModel = WorkStatusService.find_work_status_by_id(work_id, status_id)
if not work_status:
raise ResourceNotFoundError("Work status not found")
work_status.notes = notes
work_status.save()

return work_status

@classmethod
def approve_work_status(cls, work_status):
"""Approve a work status."""
Expand Down

0 comments on commit f846b1f

Please sign in to comment.