-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
1,418 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
met-api/migrations/versions/3e4dc76a96ab_added_the_timeline_widget.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
"""Added the Timeline Widget. | ||
Revision ID: 3e4dc76a96ab | ||
Revises: 02ff8ecc6b91 | ||
Create Date: 2023-12-05 17:04:46.304368 | ||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
from sqlalchemy.dialects import postgresql | ||
from met_api.constants.timeline_event_status import TimelineEventStatus | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = '3e4dc76a96ab' | ||
down_revision = '02ff8ecc6b91' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade(): | ||
op.create_table('widget_timeline', | ||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False), | ||
sa.Column('engagement_id', sa.Integer(), nullable=False), | ||
sa.Column('title', sa.String(length=255), nullable=True), | ||
sa.Column('description', sa.Text(), nullable=True), | ||
sa.ForeignKeyConstraint(['engagement_id'], ['engagement.id'], ondelete='CASCADE'), | ||
sa.PrimaryKeyConstraint('id'), | ||
) | ||
op.create_table('timeline_event', | ||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False), | ||
sa.Column('engagement_id', sa.Integer(), nullable=False), | ||
sa.Column('timeline_id', sa.Integer(), nullable=False), | ||
sa.Column('status', sa.Enum(TimelineEventStatus), nullable=True), | ||
sa.Column('position', sa.Integer(), nullable=True), | ||
sa.Column('description', sa.Text(), nullable=True), | ||
sa.Column('time', sa.String(length=255), nullable=True), | ||
sa.ForeignKeyConstraint(['engagement_id'], ['engagement.id'], ondelete='CASCADE'), | ||
sa.ForeignKeyConstraint(['timeline_id'], ['widget_timeline.id'], ondelete='CASCADE'), | ||
sa.PrimaryKeyConstraint('id'), | ||
) | ||
|
||
def downgrade(): | ||
op.drop_table('widget_timeline') | ||
op.drop_table('timeline_event') |
27 changes: 27 additions & 0 deletions
27
met-api/migrations/versions/4114001e1a4c_add_widget_id_to_widget_timeline_and_.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
"""Add widget_id to widget_timeline and timeline_event tables | ||
Revision ID: 4114001e1a4c | ||
Revises: c09e77fde608 | ||
Create Date: 2023-12-11 15:46:30.773046 | ||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
from sqlalchemy.dialects import postgresql | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = '4114001e1a4c' | ||
down_revision = 'c09e77fde608' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade(): | ||
op.add_column('widget_timeline', sa.Column('widget_id', sa.Integer())) | ||
op.create_foreign_key('timeline_widget_fk', 'widget_timeline', 'widget', ['widget_id'], ['id'], ondelete='CASCADE') | ||
op.add_column('timeline_event', sa.Column('widget_id', sa.Integer())) | ||
op.create_foreign_key('event_widget_fk', 'timeline_event', 'widget', ['widget_id'], ['id'], ondelete='CASCADE') | ||
|
||
def downgrade(): | ||
op.drop_column('widget_timeline', 'widget_id') | ||
op.drop_column('timeline_event', 'widget_id') |
61 changes: 61 additions & 0 deletions
61
met-api/migrations/versions/c09e77fde608_added_enum_value_for_timeline_widget.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
"""Added enum value for Timeline Widget. | ||
Revision ID: c09e77fde608 | ||
Revises: 3e4dc76a96ab | ||
Create Date: 2023-12-06 11:46:20.934373 | ||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
from sqlalchemy.dialects import postgresql | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = 'c09e77fde608' | ||
down_revision = '3e4dc76a96ab' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade(): | ||
op.add_column('timeline_event', sa.Column('created_date', sa.DateTime(), nullable=False)) | ||
op.add_column('timeline_event', sa.Column('updated_date', sa.DateTime(), nullable=True)) | ||
op.add_column('timeline_event', sa.Column('created_by', sa.String(length=50), nullable=True)) | ||
op.add_column('timeline_event', sa.Column('updated_by', sa.String(length=50), nullable=True)) | ||
|
||
op.add_column('widget_timeline', sa.Column('created_date', sa.DateTime(), nullable=False)) | ||
op.add_column('widget_timeline', sa.Column('updated_date', sa.DateTime(), nullable=True)) | ||
op.add_column('widget_timeline', sa.Column('created_by', sa.String(length=50), nullable=True)) | ||
op.add_column('widget_timeline', sa.Column('updated_by', sa.String(length=50), nullable=True)) | ||
|
||
widget_type_table = sa.table('widget_type', | ||
sa.Column('id', sa.Integer), | ||
sa.Column('name', sa.String), | ||
sa.Column('description', sa.String)) | ||
|
||
op.bulk_insert( | ||
widget_type_table, | ||
[ | ||
{ | ||
'id': 8, | ||
'name': 'CAC Form', | ||
'description': 'Add a CAC Form to your project', | ||
}, | ||
{ | ||
'id': 9, | ||
'name': 'Timeline', | ||
'description': 'Create a timeline for a series of events', | ||
}, | ||
] | ||
) | ||
|
||
def downgrade(): | ||
op.drop_column('widget_timeline', 'updated_by') | ||
op.drop_column('widget_timeline', 'created_by') | ||
op.drop_column('widget_timeline', 'updated_date') | ||
op.drop_column('widget_timeline', 'created_date') | ||
op.drop_column('timeline_event', 'updated_by') | ||
op.drop_column('timeline_event', 'created_by') | ||
op.drop_column('timeline_event', 'updated_date') | ||
op.drop_column('timeline_event', 'created_date') | ||
op.delete(widget_type_table).filter_by(id=8) | ||
op.delete(widget_type_table).filter_by(id=9) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"""Constants of timeline events.""" | ||
from enum import IntEnum | ||
|
||
|
||
class TimelineEventStatus(IntEnum): | ||
"""Enum of timeline event status status.""" | ||
|
||
Pending = 1 | ||
InProgress = 2 | ||
Completed = 3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"""Timeline Event model class. | ||
Manages the timeline events | ||
""" | ||
from __future__ import annotations | ||
|
||
from sqlalchemy.sql.schema import ForeignKey | ||
from met_api.constants.timeline_event_status import TimelineEventStatus | ||
|
||
from .base_model import BaseModel | ||
from .db import db | ||
|
||
|
||
class TimelineEvent(BaseModel): | ||
"""Definition of the TimelineEvent entity.""" | ||
|
||
__tablename__ = 'timeline_event' | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE'), nullable=True) | ||
widget_id = db.Column(db.Integer, ForeignKey('widget.id', ondelete='CASCADE'), nullable=True) | ||
timeline_id = db.Column(db.Integer, ForeignKey('widget_timeline.id', ondelete='CASCADE'), nullable=True) | ||
status = db.Column(db.Enum(TimelineEventStatus), nullable=False) | ||
position = db.Column(db.Integer, nullable=False) | ||
description = db.Column(db.Text(), nullable=True) | ||
time = db.Column(db.String(255), nullable=True) | ||
|
||
@classmethod | ||
def delete_event(cls, timeline_id): | ||
"""Delete timeline.""" | ||
timeline_event = db.session.query(TimelineEvent) \ | ||
.filter(TimelineEvent.timeline_id == timeline_id) | ||
timeline_event.delete() | ||
db.session.commit() | ||
|
||
@classmethod | ||
def get_timeline_events(cls, timeline_id) -> list[TimelineEvent]: | ||
"""Get timeline event.""" | ||
timeline_event = db.session.query(TimelineEvent) \ | ||
.filter(TimelineEvent.timeline_id == timeline_id) \ | ||
.all() | ||
return timeline_event | ||
|
||
@classmethod | ||
def update_timeline_event(cls, timeline_id, event_data: dict) -> TimelineEvent: | ||
"""Update timeline event.""" | ||
timeline_event: TimelineEvent = TimelineEvent.query.get(timeline_id) | ||
if timeline_event: | ||
for key, value in event_data.items(): | ||
setattr(timeline_event, key, value) | ||
timeline_event.save() | ||
return timeline_event |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
"""WidgetTimeline model class. | ||
Manages the timeline widget | ||
""" | ||
from __future__ import annotations | ||
from typing import Optional | ||
from sqlalchemy.sql.schema import ForeignKey | ||
from met_api.models.timeline_event import TimelineEvent | ||
from met_api.services.timeline_event_service import TimelineEventService | ||
from .base_model import BaseModel | ||
from .db import db | ||
|
||
class WidgetTimeline(BaseModel): # pylint: disable=too-few-public-methods, too-many-instance-attributes | ||
"""Definition of the Timeline entity.""" | ||
|
||
__tablename__ = 'widget_timeline' | ||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE'), nullable=True) | ||
widget_id = db.Column(db.Integer, ForeignKey('widget.id', ondelete='CASCADE'), nullable=True) | ||
title = db.Column(db.String(255), nullable=True) | ||
description = db.Column(db.Text(), nullable=True) | ||
|
||
# Relationship to timeline_event | ||
events = db.relationship(TimelineEvent, backref='widget_timeline', lazy=True) | ||
|
||
@classmethod | ||
def get_timeline(cls, timeline_id) -> list[WidgetTimeline]: | ||
"""Get timeline.""" | ||
widget_timeline = db.session.query(WidgetTimeline) \ | ||
.filter(WidgetTimeline.widget_id == timeline_id) \ | ||
.all() | ||
return widget_timeline | ||
|
||
@classmethod | ||
def update_timeline(cls, timeline_id, timeline_data: dict) -> Optional[WidgetTimeline or None]: | ||
"""Update timeline.""" | ||
TimelineEvent.delete_event(timeline_id) | ||
widget_timeline: WidgetTimeline = WidgetTimeline.query.get(timeline_id) | ||
if widget_timeline: | ||
widget_timeline.title = timeline_data.get('title') | ||
widget_timeline.description = timeline_data.get('description') | ||
for event in timeline_data.get('events', []): | ||
TimelineEventService.create_timeline_event(timeline_id, event) | ||
widget_timeline.save() | ||
return widget_timeline |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# Copyright © 2021 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 a timeline widget resource.""" | ||
from http import HTTPStatus | ||
|
||
from flask import jsonify, request | ||
from flask_cors import cross_origin | ||
from flask_restx import Namespace, Resource | ||
|
||
from met_api.auth import jwt as _jwt | ||
from met_api.exceptions.business_exception import BusinessException | ||
from met_api.schemas import utils as schema_utils | ||
from met_api.schemas.widget_timeline import WidgetTimelineSchema | ||
from met_api.services.widget_timeline_service import WidgetTimelineService | ||
from met_api.utils.util import allowedorigins, cors_preflight | ||
|
||
|
||
API = Namespace('widget_timelines', description='Endpoints for Timeline Widget Management') | ||
"""Widget Timelines""" | ||
|
||
|
||
@cors_preflight('GET, POST') | ||
@API.route('') | ||
class Timelines(Resource): | ||
"""Resource for managing timeline widgets.""" | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
def get(widget_id): | ||
"""Get timeline widget.""" | ||
try: | ||
widget_timeline = WidgetTimelineService().get_timeline(widget_id) | ||
return jsonify(WidgetTimelineSchema().dump(widget_timeline, many=True)), HTTPStatus.OK | ||
except BusinessException as err: | ||
return str(err), err.status_code | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
@_jwt.requires_auth | ||
def post(widget_id): | ||
"""Create timeline widget.""" | ||
try: | ||
request_json = request.get_json() | ||
widget_timeline = WidgetTimelineService().create_timeline(widget_id, request_json) | ||
return WidgetTimelineSchema().dump(widget_timeline), HTTPStatus.OK | ||
except BusinessException as err: | ||
return str(err), err.status_code | ||
|
||
|
||
@cors_preflight('PATCH') | ||
@API.route('/<int:timeline_widget_id>') | ||
class Timeline(Resource): | ||
"""Resource for managing timeline widgets.""" | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
@_jwt.requires_auth | ||
def patch(widget_id, timeline_widget_id): | ||
"""Update timeline widget.""" | ||
request_json = request.get_json() | ||
valid_format, errors = schema_utils.validate(request_json, 'timeline_widget_update') | ||
if not valid_format: | ||
return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST | ||
try: | ||
widget_timeline = WidgetTimelineService().update_timeline(widget_id, timeline_widget_id, request_json) | ||
return WidgetTimelineSchema().dump(widget_timeline), HTTPStatus.OK | ||
except BusinessException as err: | ||
return str(err), err.status_code |
Oops, something went wrong.