Skip to content

Commit

Permalink
Add video widget crud and UI component
Browse files Browse the repository at this point in the history
  • Loading branch information
jadmsaadaot committed Jul 11, 2023
1 parent 9351d32 commit fa466b9
Show file tree
Hide file tree
Showing 17 changed files with 474 additions and 30 deletions.
52 changes: 52 additions & 0 deletions met-api/migrations/versions/47fc88fe0477_video_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Add video widget
Revision ID: 47fc88fe0477
Revises: b3b5c66cea4b
Create Date: 2023-07-11 10:44:35.980432
"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '47fc88fe0477'
down_revision = 'b3b5c66cea4b'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('widget_video',
sa.Column('created_date', sa.DateTime(), nullable=False),
sa.Column('updated_date', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('widget_id', sa.Integer(), nullable=True),
sa.Column('engagement_id', sa.Integer(), nullable=True),
sa.Column('video_url', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('created_by', sa.String(length=50), nullable=True),
sa.Column('updated_by', sa.String(length=50), nullable=True),
sa.ForeignKeyConstraint(['engagement_id'], ['engagement.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['widget_id'], ['widget.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
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': 7, 'name': 'Video', 'description': 'Add a link to a hosted video and link preview'}
])
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('widget_video')

conn = op.get_bind()

conn.execute('DELETE FROM widget_type WHERE id=7')
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions met-api/src/met_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@
from .email_queue import EmailQueue
from .engagement_slug import EngagementSlug
from .report_setting import ReportSetting
from .widget_video import WidgetVideo
17 changes: 8 additions & 9 deletions met-api/src/met_api/models/widget_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,23 @@ class WidgetVideo(BaseModel): # pylint: disable=too-few-public-methods, too-man
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
widget_id = db.Column(db.Integer, ForeignKey('widget.id', ondelete='CASCADE'), nullable=True)
engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE'), nullable=True)
video_url = db.Column(db.String(), nullable=False)
video_url = db.Column(db.String(255), nullable=False)
description = db.Column(db.Text())

@classmethod
def get_video(cls, widget_id) -> list[WidgetVideo]:
"""Get video."""
widget_video = db.session.query(WidgetVideo) \
.filter(WidgetVideo.widget_id == widget_id) \
.first()
.all()
return widget_video

@classmethod
def update_video(cls, widget_id, video_data: dict) -> WidgetVideo:
def update_video(cls, video_widget_id, video_data: dict) -> WidgetVideo:
"""Update video."""
query = WidgetVideo.query.filter_by(WidgetVideo.widget_id == widget_id)
widget_video: WidgetVideo = query.first()
if not widget_video:
return video_data
query.update(video_data)
db.session.commit()
widget_video: WidgetVideo = WidgetVideo.query.get(video_widget_id)
if widget_video:
for key, value in video_data.items():
setattr(widget_video, key, value)
widget_video.save()
return widget_video
2 changes: 2 additions & 0 deletions met-api/src/met_api/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from .tenant import API as TENANT_API
from .engagement_slug import API as ENGAGEMENT_SLUG_API
from .report_setting import API as REPORT_SETTING_API
from .widget_video import API as WIDGET_VIDEO_API

__all__ = ('API_BLUEPRINT',)

Expand Down Expand Up @@ -81,3 +82,4 @@
API.add_namespace(WIDGET_MAPS_API, path='/widgets/<int:widget_id>/maps')
API.add_namespace(ENGAGEMENT_SLUG_API, path='/slugs')
API.add_namespace(REPORT_SETTING_API)
API.add_namespace(WIDGET_VIDEO_API, path='/widgets/<int:widget_id>/videos')
26 changes: 25 additions & 1 deletion met-api/src/met_api/resources/widget_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

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_video import WidgetVideoSchema
from met_api.services.widget_video_service import WidgetVideoService
from met_api.utils.roles import Role
Expand All @@ -41,7 +42,7 @@ def get(widget_id):
"""Get video widget."""
try:
widget_video = WidgetVideoService().get_video(widget_id)
return WidgetVideoSchema().dump(widget_video), HTTPStatus.OK
return WidgetVideoSchema().dump(widget_video, many=True), HTTPStatus.OK
except BusinessException as err:
return str(err), err.status_code

Expand All @@ -63,8 +64,31 @@ def post(widget_id):
def patch(widget_id):
"""Update video widget."""
request_json = request.get_json()
valid_format, errors = schema_utils.validate(request_json, 'video_widget_update')
if not valid_format:
return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST
try:
widget_video = WidgetVideoService().update_video(widget_id, request_json)
return WidgetVideoSchema().dump(widget_video), HTTPStatus.OK
except BusinessException as err:
return str(err), err.status_code

@cors_preflight('PATCH')
@API.route('/<int:video_widget_id>')
class Video(Resource):
"""Resource for managing video widgets."""

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.has_one_of_roles([Role.EDIT_ENGAGEMENT.value])
def patch(widget_id, video_widget_id):
"""Update video widget."""
request_json = request.get_json()
valid_format, errors = schema_utils.validate(request_json, 'video_widget_update')
if not valid_format:
return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST
try:
widget_video = WidgetVideoService().update_video(widget_id, video_widget_id, request_json)
return WidgetVideoSchema().dump(widget_video), HTTPStatus.OK
except BusinessException as err:
return str(err), err.status_code
31 changes: 31 additions & 0 deletions met-api/src/met_api/schemas/schemas/video_widget_update.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://met.gov.bc.ca/.well_known/schemas/video_widget_update",
"type": "object",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": {},
"examples": [
{
"description": "A video widget description",
"video_url": "https://www.youtube.com"
}
],
"required": [],
"properties": {
"description": {
"$id": "#/properties/description",
"type": "string",
"title": "Video description",
"description": "The description of this video.",
"examples": ["A video widget description"]
},
"video_url": {
"$id": "#/properties/video_url",
"type": "string",
"title": "Video url",
"description": "The url link to this video.",
"examples": ["https://www.youtube.com"]
}
}
}
17 changes: 8 additions & 9 deletions met-api/src/met_api/services/widget_video_service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
"""Service for Widget Video management."""
from http import HTTPStatus

from met_api.exceptions.business_exception import BusinessException
from met_api.models.widget_video import WidgetVideo as WidgetVideoModel


Expand All @@ -23,14 +20,16 @@ def create_video(widget_id, video_details: dict):
return widget_video

@staticmethod
def update_video(widget_id, request_json):
def update_video(widget_id, video_widget_id, video_data):
"""Update video widget."""
widget_video: WidgetVideoModel = WidgetVideoModel.get_video(widget_id)
widget_video: WidgetVideoModel = WidgetVideoModel.find_by_id(video_widget_id)
if not widget_video:
raise KeyError('Video widget not found')

if widget_video.widget_id != widget_id:
raise BusinessException(
error='Invalid widgets and video',
status_code=HTTPStatus.BAD_REQUEST)
return WidgetVideoModel.update_video(widget_id, request_json)
raise ValueError('Invalid widgets and video')

return WidgetVideoModel.update_video(widget_video.id, video_data)

@staticmethod
def _create_video_model(widget_id, video_data: dict):
Expand Down
5 changes: 5 additions & 0 deletions met-web/src/apiManager/endpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ const Endpoints = {
CREATE: `${AppConfig.apiUrl}/widgets/widget_id/maps`,
SHAPEFILE_PREVIEW: `${AppConfig.apiUrl}/shapefile`,
},
VideoWidgets: {
GET: `${AppConfig.apiUrl}/widgets/widget_id/videos`,
CREATE: `${AppConfig.apiUrl}/widgets/widget_id/videos`,
UPDATE: `${AppConfig.apiUrl}/widgets/widget_id/videos/video_widget_id`,
},
Tenants: {
GET: `${AppConfig.apiUrl}/tenants/tenant_id`,
},
Expand Down
Loading

0 comments on commit fa466b9

Please sign in to comment.