Skip to content

Commit fbb9669

Browse files
committed
Update default language to pull from central config
1 parent 304ce05 commit fbb9669

2 files changed

Lines changed: 144 additions & 66 deletions

File tree

api/src/api/services/engagement_service.py

Lines changed: 79 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ class EngagementService:
4444
"""Engagement management service."""
4545

4646
otherdateformat = '%Y-%m-%d'
47-
default_language_code = 'en'
4847
JSONScalar = Union[str, int, float, bool, None]
4948
JSONValue = Union[
5049
JSONScalar,
@@ -146,7 +145,9 @@ def _get_scope_options(user_roles, has_team_access):
146145
# If user has VIEW_ENGAGEMENT role, e.g. TEAM MEMBER, return scope options to include assigned
147146
# engagements and public engagements
148147
return EngagementScopeOptions(
149-
engagement_status_ids=[Status.Published.value, Status.Closed.value],
148+
engagement_status_ids=[
149+
Status.Published.value, Status.Closed.value
150+
],
150151
include_assigned=True,
151152
)
152153
if Role.VIEW_ASSIGNED_ENGAGEMENTS.value in user_roles:
@@ -175,7 +176,9 @@ def publish_scheduled_engagements():
175176
engagements = EngagementModel.publish_scheduled_engagements_due()
176177

177178
if not engagements:
178-
current_app.logger.info('There are no engagements scheduled for publication')
179+
current_app.logger.info(
180+
'There are no engagements scheduled for publication'
181+
)
179182
return None
180183

181184
current_app.logger.info('Engagements published: %s', engagements)
@@ -186,7 +189,9 @@ def publish_scheduled_engagements():
186189
SourceAction.PUBLISHED.value,
187190
True,
188191
)
189-
current_app.logger.info('Engagement published added to email queue: %s', engagement.id)
192+
current_app.logger.info(
193+
'Engagement published added to email queue: %s', engagement.id
194+
)
190195
return engagements
191196

192197
@staticmethod
@@ -198,7 +203,9 @@ def create_engagement(request_json: dict):
198203
eng_model = EngagementService._create_engagement_model(request_json)
199204

200205
if request_json.get('status_block'):
201-
EngagementService._create_eng_status_block(eng_model.id, request_json)
206+
EngagementService._create_eng_status_block(
207+
eng_model.id, request_json
208+
)
202209

203210
# Always sync languages so an English translation row is created immediately.
204211
# If the caller didn't request specific languages, default to English-only.
@@ -257,7 +264,9 @@ def _save_or_update_eng_block(engagement_id, status_block):
257264
survey_status = survey_block.get('survey_status')
258265
survey_block_text = survey_block.get('block_text')
259266
status_block: EngagementStatusBlockModel = (
260-
EngagementStatusBlockModel.get_by_status(engagement_id, survey_status)
267+
EngagementStatusBlockModel.get_by_status(
268+
engagement_id, survey_status
269+
)
261270
)
262271
# If the status block exists, update it. Otherwise, create a new one.
263272
if status_block:
@@ -301,7 +310,9 @@ def _save_or_update_surveys(engagement_id, surveys):
301310
engagement_id=engagement_id,
302311
is_hidden=survey.get('is_hidden', False),
303312
is_template=survey.get('is_template', False),
304-
generate_dashboard=survey.get('generate_dashboard', True),
313+
generate_dashboard=survey.get(
314+
'generate_dashboard', True
315+
),
305316
)
306317
)
307318

@@ -328,7 +339,9 @@ def _validate_and_assign_survey(survey_id: int, engagement_id: int):
328339
raise ValueError('selected survey does not exist')
329340

330341
if survey.engagement_id != engagement_id:
331-
raise ValueError('selected survey does not belong to this engagement')
342+
raise ValueError(
343+
'selected survey does not belong to this engagement'
344+
)
332345

333346
return survey_id
334347

@@ -357,14 +370,16 @@ def edit_engagement(data: dict):
357370
requested_languages = data.pop('languages', None)
358371
suggested_engagements = data.pop('suggested_engagements', None)
359372
if suggested_engagements is None:
360-
suggested_engagements = data.pop('suggested_engagements_input', None)
373+
suggested_engagements = data.pop(
374+
'suggested_engagements_input', None)
361375
# Defensive: relationship keys are not valid for SQL UPDATE mappings.
362376
data.pop('suggested_engagement_links', None)
363377
epic_fields = 'end_date' in data or 'start_date' in data
364378
selected_survey_id = data.get('selected_survey_id', None)
365379
engagement_id = data.get('id', None)
366380
authorization.check_auth(
367-
one_of_roles=(MembershipType.TEAM_MEMBER.name, Role.EDIT_ENGAGEMENT.value),
381+
one_of_roles=(MembershipType.TEAM_MEMBER.name,
382+
Role.EDIT_ENGAGEMENT.value),
368383
engagement_id=engagement_id,
369384
)
370385

@@ -379,9 +394,11 @@ def edit_engagement(data: dict):
379394
if data:
380395
if selected_survey_id:
381396
data['selected_survey_id'] = \
382-
EngagementService._validate_and_assign_survey(selected_survey_id, engagement_id)
397+
EngagementService._validate_and_assign_survey(
398+
selected_survey_id, engagement_id)
383399

384-
updated_engagement = EngagementModel.edit_engagement(data, commit=False)
400+
updated_engagement = EngagementModel.edit_engagement(
401+
data, commit=False)
385402

386403
if not updated_engagement:
387404
raise ValueError(engagement)
@@ -395,10 +412,12 @@ def edit_engagement(data: dict):
395412
)
396413

397414
if suggested_engagements is not None:
398-
EngagementService._sync_suggestions(engagement, suggested_engagements)
415+
EngagementService._sync_suggestions(
416+
engagement, suggested_engagements)
399417

400418
if requested_languages is not None:
401-
EngagementService._sync_translation_languages(engagement_id, requested_languages)
419+
EngagementService._sync_translation_languages(
420+
engagement_id, requested_languages)
402421

403422
db.session.commit()
404423
except (BusinessException, ValueError, ValidationError, SQLAlchemyError):
@@ -424,7 +443,8 @@ def _sync_suggestions(engagement: EngagementModel, suggestions_data):
424443
if r.suggested_engagement_id is not None
425444
}
426445

427-
normalized = SuggestedEngagementSyncItemSchema(many=True).load(suggestions_data)
446+
normalized = SuggestedEngagementSyncItemSchema(
447+
many=True).load(suggestions_data)
428448
now = datetime.now(timezone.utc)
429449
ordered: list[SuggestedEngagementModel] = []
430450

@@ -460,10 +480,13 @@ def _normalize_language_codes(language_codes: object) -> list[str]:
460480
if not isinstance(language_codes, list):
461481
raise ValueError('languages must be a list of language codes')
462482

483+
default_language_code = EngagementService._get_default_language_code()
484+
463485
normalized_codes: list[str] = []
464486
for code in language_codes:
465487
if not isinstance(code, str):
466-
raise ValueError('languages must only contain string language codes')
488+
raise ValueError(
489+
'languages must only contain string language codes')
467490

468491
normalized = code.strip().lower()
469492
if not normalized:
@@ -472,32 +495,48 @@ def _normalize_language_codes(language_codes: object) -> list[str]:
472495
if normalized not in normalized_codes:
473496
normalized_codes.append(normalized)
474497

475-
# English is always implicitly available.
476-
if EngagementService.default_language_code not in normalized_codes:
477-
normalized_codes.append(EngagementService.default_language_code)
498+
# The default language is always implicitly available.
499+
if default_language_code not in normalized_codes:
500+
normalized_codes.append(default_language_code)
478501

479502
return normalized_codes
480503

504+
@staticmethod
505+
def _get_default_language_code() -> str:
506+
default_language = (
507+
(current_app.config.get('DEFAULT_LANGUAGE')
508+
if has_app_context() else None)
509+
or os.getenv('DEFAULT_LANGUAGE')
510+
or 'en'
511+
)
512+
return default_language.strip().lower()
513+
481514
@staticmethod
482515
def _sync_translation_languages(engagement_id: int, language_codes: object) -> None:
483-
normalized_codes = EngagementService._normalize_language_codes(language_codes)
484-
translation_codes = set(normalized_codes) # Includes English — all languages get a translation row.
516+
normalized_codes = EngagementService._normalize_language_codes(
517+
language_codes)
518+
# Includes English — all languages get a translation row.
519+
translation_codes = set(normalized_codes)
485520

486521
requested_languages = (
487-
LanguageModel.query.filter(LanguageModel.code.in_(translation_codes)).all()
522+
LanguageModel.query.filter(
523+
LanguageModel.code.in_(translation_codes)).all()
488524
if translation_codes
489525
else []
490526
)
491527

492528
requested_codes = {language.code for language in requested_languages}
493529
missing_codes = translation_codes - requested_codes
494530
if missing_codes:
495-
raise ValueError(f'Invalid language code(s): {", ".join(sorted(missing_codes))}')
531+
raise ValueError(
532+
f'Invalid language code(s): {", ".join(sorted(missing_codes))}')
496533

497-
requested_language_ids = {language.id for language in requested_languages}
534+
requested_language_ids = {
535+
language.id for language in requested_languages}
498536

499537
existing_translations = (
500-
EngagementTranslation.query.filter_by(engagement_id=engagement_id).all()
538+
EngagementTranslation.query.filter_by(
539+
engagement_id=engagement_id).all()
501540
)
502541
existing_by_language_id = {
503542
translation.language_id: translation for translation in existing_translations
@@ -519,7 +558,8 @@ def _sync_translation_languages(engagement_id: int, language_codes: object) -> N
519558
@staticmethod
520559
def validate_fields(data):
521560
"""Validate all fields."""
522-
empty_fields = [not data[field] for field in ['name', 'start_date', 'end_date']]
561+
empty_fields = [not data[field]
562+
for field in ['name', 'start_date', 'end_date']]
523563

524564
if data['start_date'] > data['end_date']:
525565
raise ValueError('Start date cannot be after End date')
@@ -565,7 +605,8 @@ def _send_closeout_emails(engagement: EngagementModel) -> None:
565605
@staticmethod
566606
def _render_email_template(engagement: EngagementModel, lang_code):
567607
template = Template.get_template('email_engagement_closeout.html')
568-
dashboard_path = EngagementService._get_dashboard_path(engagement, lang_code)
608+
dashboard_path = EngagementService._get_dashboard_path(
609+
engagement, lang_code)
569610
engagement_url = notification.get_tenant_site_url(
570611
engagement.tenant_id, dashboard_path
571612
)
@@ -596,7 +637,8 @@ def _get_tenant_name(tenant_id):
596637

597638
@staticmethod
598639
def _get_dashboard_path(engagement: EngagementModel, lang_code):
599-
engagement_slug = EngagementSlugModel.find_by_engagement_id(engagement.id)
640+
engagement_slug = EngagementSlugModel.find_by_engagement_id(
641+
engagement.id)
600642
paths = current_app.config['PATH_CONFIG']
601643
if engagement_slug:
602644
return paths['ENGAGEMENT']['DASHBOARD_SLUG'].format(
@@ -609,7 +651,8 @@ def _get_dashboard_path(engagement: EngagementModel, lang_code):
609651
@classmethod
610652
def delete(cls, engagement_id: int):
611653
"""Delete an existing engagement and its translations."""
612-
one_of_roles = (Role.SUPER_ADMIN.value, Role.UNPUBLISH_ENGAGEMENT.value)
654+
one_of_roles = (Role.SUPER_ADMIN.value,
655+
Role.UNPUBLISH_ENGAGEMENT.value)
613656
authorization.check_auth(one_of_roles=one_of_roles)
614657

615658
current_env = (
@@ -620,7 +663,8 @@ def delete(cls, engagement_id: int):
620663
).strip().lower()
621664

622665
if current_env in ('prod', 'production'):
623-
abort(HTTPStatus.FORBIDDEN, 'Cannot delete an engagement in production environment')
666+
abort(HTTPStatus.FORBIDDEN,
667+
'Cannot delete an engagement in production environment')
624668

625669
engagement = EngagementModel.find_by_id(engagement_id)
626670
if not engagement:
@@ -629,11 +673,13 @@ def delete(cls, engagement_id: int):
629673
raise ValueError('Cannot delete an engagement that is published')
630674

631675
try:
632-
for translation in (EngagementTranslation.get_available_translation_languages(engagement.id) or []):
633-
EngagementTranslation.delete_engagement_translation(translation.id)
676+
for translation in (EngagementTranslation.get_available_translations(engagement.id) or []):
677+
EngagementTranslation.delete_engagement_translation(
678+
translation.id)
634679
deleted = EngagementModel.delete_engagement(engagement_id)
635680
except ValueError as exc:
636681
raise ValueError(str(exc)) from exc
637682
except SQLAlchemyError as e:
638-
raise RuntimeError('Database error while deleting engagement', e) from e
683+
raise RuntimeError(
684+
'Database error while deleting engagement', e) from e
639685
return {'id': deleted.id if hasattr(deleted, 'id') else engagement_id}

0 commit comments

Comments
 (0)