-
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.
[To Main] - DESENG-544 - Multi-language - Created engagement content …
…translation tables & API routes (#2459) * DESENG-544: Engagement Content translation model and service file * DESENG-544: Engagement Content translation test model and test service file * DESENG-544: Content translation api lint fixed * Migration table version updated
- Loading branch information
1 parent
34fd91d
commit a0be3d6
Showing
15 changed files
with
858 additions
and
24 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
43 changes: 43 additions & 0 deletions
43
met-api/migrations/versions/495d2dbe19b8_enagement_content_translation.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,43 @@ | ||
"""Enagement Content Translation | ||
Revision ID: 495d2dbe19b8 | ||
Revises: e4d15a1af865 | ||
Create Date: 2024-04-10 14:20:23.777834 | ||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
from sqlalchemy.dialects import postgresql | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = '495d2dbe19b8' | ||
down_revision = 'e4d15a1af865' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade(): | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
op.create_table('engagement_content_translation', | ||
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('language_id', sa.Integer(), nullable=False), | ||
sa.Column('engagement_content_id', sa.Integer(), nullable=False), | ||
sa.Column('content_title', sa.String(length=50), nullable=False), | ||
sa.Column('custom_text_content', sa.Text(), nullable=True), | ||
sa.Column('custom_json_content', postgresql.JSON(astext_type=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_content_id'], ['engagement_content.id'], ondelete='CASCADE'), | ||
sa.ForeignKeyConstraint(['language_id'], ['language.id'], ondelete='CASCADE'), | ||
sa.PrimaryKeyConstraint('id'), | ||
sa.UniqueConstraint('engagement_content_id', 'language_id', name='_engagement_content_language_uc') | ||
) | ||
# ### end Alembic commands ### | ||
|
||
|
||
def downgrade(): | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
op.drop_table('engagement_content_translation') | ||
# ### end Alembic commands ### |
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
101 changes: 101 additions & 0 deletions
101
met-api/src/met_api/models/engagement_content_translation.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,101 @@ | ||
"""Engagement Content translation model class. | ||
Manages the Engagement Content Translations. | ||
""" | ||
|
||
from __future__ import annotations | ||
from sqlalchemy import UniqueConstraint | ||
from sqlalchemy.dialects.postgresql import JSON | ||
from sqlalchemy.exc import IntegrityError | ||
from .base_model import BaseModel | ||
from .db import db | ||
|
||
|
||
class EngagementContentTranslation(BaseModel): | ||
"""Definition of the Engagement Content Translation entity.""" | ||
|
||
__tablename__ = 'engagement_content_translation' | ||
|
||
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
language_id = db.Column(db.Integer, db.ForeignKey('language.id', ondelete='CASCADE'), nullable=False) | ||
engagement_content_id = db.Column( | ||
db.Integer, db.ForeignKey('engagement_content.id', ondelete='CASCADE'), nullable=False | ||
) | ||
content_title = db.Column(db.String(50), unique=False, nullable=False) | ||
custom_text_content = db.Column(db.Text, unique=False, nullable=True) | ||
custom_json_content = db.Column(JSON, unique=False, nullable=True) | ||
|
||
# Add a unique constraint on engagement_content_id and language_id | ||
# A engagement content has only one version in a particular language | ||
__table_args__ = (UniqueConstraint('engagement_content_id', 'language_id', name='_engagement_content_language_uc'),) | ||
|
||
@classmethod | ||
def get_translations_by_content_and_language(cls, engagement_content_id=None, language_id=None): | ||
""" | ||
Retrieve engagement content translations by content ID and language ID. | ||
:param engagement_content_id: ID of the engagement content. | ||
:param language_id: ID of the language. | ||
:return: List of engagement content translations matching the criteria. | ||
""" | ||
query = cls.query | ||
if engagement_content_id is not None: | ||
query = query.filter(cls.engagement_content_id == engagement_content_id) | ||
if language_id is not None: | ||
query = query.filter(cls.language_id == language_id) | ||
|
||
return query.all() | ||
|
||
@classmethod | ||
def create_engagement_content_translation(cls, data): | ||
""" | ||
Create a new EngagementContentTranslation record. | ||
:param data: Dictionary containing the fields for EngagementContentTranslation. | ||
:return: EngagementContentTranslation instance. | ||
""" | ||
try: | ||
new_translation = cls( | ||
language_id=data['language_id'], | ||
engagement_content_id=data['engagement_content_id'], | ||
content_title=data['content_title'], | ||
custom_text_content=data.get('custom_text_content'), | ||
custom_json_content=data.get('custom_json_content') | ||
) | ||
db.session.add(new_translation) | ||
db.session.commit() | ||
return new_translation | ||
except IntegrityError as e: | ||
db.session.rollback() | ||
raise e | ||
|
||
@classmethod | ||
def update_engagement_content_translation(cls, translation_id, data): | ||
""" | ||
Update an existing EngagementContentTranslation record. | ||
:param translation_id: ID of the EngagementContentTranslation to update. | ||
:param data: Dictionary of fields to update. | ||
:return: Updated EngagementContentTranslation instance. | ||
""" | ||
translation = cls.find_by_id(translation_id) | ||
if translation: | ||
for key, value in data.items(): | ||
setattr(translation, key, value) | ||
db.session.commit() | ||
return translation | ||
|
||
@classmethod | ||
def delete_engagement_content_translation(cls, translation_id): | ||
""" | ||
Delete an EngagementContentTranslation record. | ||
:param translation_id: ID of the EngagementContentTranslation to delete. | ||
:return: Boolean indicating successful deletion. | ||
""" | ||
translation = cls.find_by_id(translation_id) | ||
if translation: | ||
db.session.delete(translation) | ||
db.session.commit() | ||
return True | ||
return False |
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
120 changes: 120 additions & 0 deletions
120
met-api/src/met_api/resources/engagement_content_translation.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,120 @@ | ||
"""API endpoints for managing an engagement content translation resource.""" | ||
|
||
from http import HTTPStatus | ||
|
||
from flask import jsonify, request | ||
from flask_cors import cross_origin | ||
from flask_restx import Namespace, Resource | ||
from marshmallow import ValidationError | ||
|
||
from met_api.auth import jwt as _jwt | ||
from met_api.schemas import utils as schema_utils | ||
from met_api.schemas.engagement_content_translation import EngagementContentTranslationSchema | ||
from met_api.services.engagement_content_translation_service import EngagementContentTranslationService | ||
from met_api.exceptions.business_exception import BusinessException | ||
from met_api.utils.util import allowedorigins, cors_preflight | ||
|
||
API = Namespace('engagement_content_translation', description='Endpoints for Engagement Content Translation Management') | ||
|
||
|
||
@cors_preflight('GET, POST, OPTIONS') | ||
@API.route('/language/<language_id>') | ||
class EngagementContentTranslationsByLanguage(Resource): | ||
"""Resource for managing engagement content translations.""" | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
def get(content_id, language_id): | ||
"""Fetch content translations based on content_id AND language_id.""" | ||
translations = EngagementContentTranslationService().get_translations_by_content_and_language( | ||
content_id, language_id | ||
) | ||
return jsonify(translations), HTTPStatus.OK | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
@_jwt.requires_auth | ||
def post(): | ||
"""Add new engagement content translation.""" | ||
request_json = request.get_json() | ||
valid_format, errors = schema_utils.validate(request_json, 'engagement_content_translation') | ||
if not valid_format: | ||
return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST | ||
|
||
pre_populate = request_json.get('pre_populate', True) | ||
|
||
try: | ||
translation = EngagementContentTranslationSchema().load(request_json) | ||
created_translation = EngagementContentTranslationService().create_engagement_content_translation( | ||
translation, pre_populate | ||
) | ||
return jsonify(created_translation), HTTPStatus.CREATED | ||
except (ValidationError, BusinessException) as err: | ||
return {'message': str(err)}, HTTPStatus.BAD_REQUEST | ||
|
||
|
||
@cors_preflight('GET, PATCH, DELETE, OPTIONS') | ||
@API.route('/<int:translation_id>') | ||
class EditEngagementContentTranslation(Resource): | ||
"""Resource for updating or deleting an engagement content translation.""" | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
def get(translation_id, **_): | ||
"""Get engagement content translation by id.""" | ||
translation = EngagementContentTranslationService().get_engagement_content_translation_by_id(translation_id) | ||
if translation: | ||
return jsonify(translation), HTTPStatus.OK | ||
return {'message': 'Translation not found'}, HTTPStatus.NOT_FOUND | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
@_jwt.requires_auth | ||
def patch(translation_id, **_): | ||
"""Update engagement content translation.""" | ||
translation_data = request.get_json() | ||
try: | ||
updated_translation = EngagementContentTranslationService().update_engagement_content_translation( | ||
translation_id, translation_data | ||
) | ||
return jsonify(updated_translation), HTTPStatus.OK | ||
except (ValidationError, BusinessException) as err: | ||
return {'message': str(err)}, HTTPStatus.BAD_REQUEST | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
@_jwt.requires_auth | ||
def delete(translation_id, **_): | ||
"""Remove engagement content translation.""" | ||
try: | ||
EngagementContentTranslationService().delete_engagement_content_translation(translation_id) | ||
return {'message': 'Translation successfully removed'}, HTTPStatus.NO_CONTENT | ||
except BusinessException as err: | ||
return {'message': str(err)}, HTTPStatus.BAD_REQUEST | ||
|
||
|
||
@cors_preflight('GET, POST, OPTIONS') | ||
@API.route('/') | ||
class EngagementContentTranslations(Resource): | ||
"""Resource for managing engagement content translations.""" | ||
|
||
@staticmethod | ||
@cross_origin(origins=allowedorigins()) | ||
@_jwt.requires_auth | ||
def post(**_): | ||
"""Add new engagement content translation.""" | ||
request_json = request.get_json() | ||
valid_format, errors = schema_utils.validate(request_json, 'engagement_content_translation') | ||
if not valid_format: | ||
return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST | ||
|
||
pre_populate = request_json.get('pre_populate', True) | ||
|
||
try: | ||
translation = EngagementContentTranslationSchema().load(request_json) | ||
created_translation = EngagementContentTranslationService().create_engagement_content_translation( | ||
translation, pre_populate | ||
) | ||
return jsonify(created_translation), HTTPStatus.CREATED | ||
except (ValidationError, BusinessException) as err: | ||
return {'message': str(err)}, HTTPStatus.BAD_REQUEST |
19 changes: 19 additions & 0 deletions
19
met-api/src/met_api/schemas/engagement_content_translation.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,19 @@ | ||
"""Schema for engagement content translation.""" | ||
|
||
from marshmallow import EXCLUDE, Schema, fields | ||
|
||
|
||
class EngagementContentTranslationSchema(Schema): | ||
"""Engagement content translation schema.""" | ||
|
||
class Meta: # pylint: disable=too-few-public-methods | ||
"""Meta class to exclude unknown fields.""" | ||
|
||
unknown = EXCLUDE | ||
|
||
id = fields.Int(data_key='id') | ||
language_id = fields.Int(data_key='language_id', required=True) | ||
engagement_content_id = fields.Int(data_key='engagement_content_id', required=True) | ||
content_title = fields.Str(data_key='content_title', required=True) | ||
custom_text_content = fields.Str(data_key='custom_text_content') | ||
custom_json_content = fields.Str(data_key='custom_json_content') |
53 changes: 53 additions & 0 deletions
53
met-api/src/met_api/schemas/schemas/engagement_content_translation.json
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,53 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema", | ||
"$id": "https://met.gov.bc.ca/.well_known/schemas/engagement_content_translation", | ||
"type": "object", | ||
"title": "Engagement Content Translation Schema", | ||
"description": "Schema for Engagement Content Translation data structure.", | ||
"default": {}, | ||
"examples": [ | ||
{ | ||
"language_id": 1, | ||
"engagement_content_id": 1, | ||
"content_title": "Sample Title", | ||
"custom_text_content": "Sample text content", | ||
"custom_json_content": {}, | ||
"pre_populate": true | ||
} | ||
], | ||
"required": ["language_id", "engagement_content_id", "content_title"], | ||
"properties": { | ||
"language_id": { | ||
"$id": "#/properties/language_id", | ||
"type": "number", | ||
"title": "Language ID", | ||
"description": "The ID of the language for the translation." | ||
}, | ||
"engagement_content_id": { | ||
"$id": "#/properties/engagement_content_id", | ||
"type": "number", | ||
"title": "Engagement Content ID", | ||
"description": "The ID of the engagement content being translated." | ||
}, | ||
"content_title": { | ||
"$id": "#/properties/content_title", | ||
"type": "string", | ||
"title": "Content Title", | ||
"description": "The title of the engagement content." | ||
}, | ||
"custom_text_content": { | ||
"$id": "#/properties/custom_text_content", | ||
"type": "string", | ||
"title": "Custom Text Content", | ||
"description": "Custom textual content of the translation.", | ||
"nullable": true | ||
}, | ||
"custom_json_content": { | ||
"$id": "#/properties/custom_json_content", | ||
"type": "object", | ||
"title": "Custom JSON Content", | ||
"description": "Custom JSON structured content of the translation.", | ||
"nullable": true | ||
} | ||
} | ||
} |
Oops, something went wrong.