Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDBELGA-884] fix: Only copy configured custom CVs from Event to Planning #2095

Merged
merged 4 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
CYPRESS_SCREENSHOTS_FOLDER: /tmp/cypress
- name: Upload screenshots
if: ${{ failure() }}
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: screenshots-e2e-${{ matrix.e2e }}
path: /tmp/cypress/**/*.png
1 change: 0 additions & 1 deletion server/planning/content_profiles/profiles/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ def __init__(
"type": "string",
"required": True,
"nullable": True,
"allowed": [],
},
"service": {"nullable": True},
"parent": {"nullable": True},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8; -*-
# This file is part of Superdesk.
# For the full copyright and license information, please see the
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license
#
# Author : MarkLark86
# Creation: 2024-09-20 14:13

from superdesk.commands.data_updates import BaseDataUpdate


class DataUpdate(BaseDataUpdate):
resource = "planning_types"

def forwards(self, mongodb_collection, mongodb_database):
for resource_type in ["event", "planning"]:
original_profile = mongodb_collection.find_one({"name": resource_type})
if not original_profile:
# No need to process this Profile if the defaults are currently used
continue

try:
schema = original_profile["schema"]
schema["subject"]["schema"]["schema"]["scheme"].pop("allowed", None)
except (KeyError, TypeError):
# ``subject`` or ``allowed`` is not currently set, no need to fix it
continue

mongodb_collection.update({"name": resource_type}, {"$set": {"schema": schema}})

def backwards(self, mongodb_collection, mongodb_database):
pass
2 changes: 1 addition & 1 deletion server/planning/events/events_post.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def post_related_plannings(self, plannings, new_post_state):
try:
planning_post_service.post([doc], related_planning=True)
except Exception as e:
failed_planning_ids.append({"_id": doc["planning"], "error": e.description})
failed_planning_ids.append({"_id": doc["planning"], "error": getattr(e, "description", str(e))})
return failed_planning_ids
for planning in plannings:
if not planning.get("pubstatus") and planning.get("state") in [
Expand Down
21 changes: 21 additions & 0 deletions server/planning/events/events_sync/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from typing import List, Dict, Any
from dataclasses import dataclass

from planning.content_profiles.utils import ContentProfileData


@dataclass
class SyncItemData:
Expand All @@ -34,3 +36,22 @@ class SyncData:
class VocabsSyncData:
coverage_states: Dict[str, Dict[str, str]]
genres: Dict[str, Dict[str, str]]


def get_enabled_subjects(item: Dict[str, Any], profile: ContentProfileData) -> List[Dict[str, Any]]:
"""Returns the list of subjects (including custom_vocabularies) if they're enabled in Planning profile

:param item: The source item where the subjects are coming from
:param profile: The Planning ContentProfile to determine enabled fields & vocabularies
:return: A list containing the supported subjects and custom_vocabularies for Planning items
"""

if not item.get("subject") or not {"subject", "custom_vocabularies"} & profile.enabled_fields:
return []

try:
cv_schemes = profile.profile["schema"]["custom_vocabularies"]["vocabularies"]
except (KeyError, TypeError):
cv_schemes = []

return [subject for subject in item["subject"] if not subject.get("scheme") or subject.get("scheme") in cv_schemes]
6 changes: 3 additions & 3 deletions server/planning/events/events_sync/embedded_planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from planning.types import Event, EmbeddedPlanning, EmbeddedCoverageItem, Planning, Coverage, StringFieldTranslation
from planning.content_profiles.utils import AllContentProfileData

from .common import VocabsSyncData
from .common import VocabsSyncData, get_enabled_subjects

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -48,8 +48,6 @@ def create_new_plannings_from_embedded_planning(
if field in profiles.planning.enabled_fields
)

planning_fields.add("subject")

multilingual_enabled = profiles.events.is_multilingual and profiles.planning.is_multilingual
translations: List[StringFieldTranslation] = []
if multilingual_enabled and "language" in planning_fields and len(event.get("translations") or []):
Expand Down Expand Up @@ -103,6 +101,8 @@ def map_event_to_planning_translation(translation: StringFieldTranslation):
# The Event item contains a value for this field (excluding ``None``), use that
new_planning[field] = event.get(field)

new_planning["subjects"] = get_enabled_subjects(event, profiles.planning)

if "description_text" in profiles.planning.enabled_fields and event.get("definition_short"):
new_planning["description_text"] = event.get("definition_short")

Expand Down
5 changes: 4 additions & 1 deletion server/planning/events/events_sync/planning_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from planning.types import StringFieldTranslation
from planning.content_profiles.utils import AllContentProfileData

from .common import SyncData
from .common import SyncData, get_enabled_subjects


def get_normalised_field_value(item, field):
Expand Down Expand Up @@ -144,6 +144,9 @@ def sync_existing_planning_item(
if field in coverage_sync_fields:
_sync_coverage_field(sync_data, field, profiles)

if sync_data.planning.updates.get("subject"):
sync_data.planning.updates["subject"] = get_enabled_subjects(sync_data.planning.updates, profiles.planning)

if sync_data.update_translations:
translations: List[StringFieldTranslation] = []
for field in sync_data.planning.updated_translations.keys():
Expand Down
3 changes: 2 additions & 1 deletion server/planning/types/content_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license

from typing import TypedDict, Dict
from typing import TypedDict, Dict, List


class ContentFieldSchema(TypedDict, total=False):
multilingual: bool
field_type: str
planning_auto_publish: bool # Only available in ``related_plannings`` field
vocabularies: List[str] # Only available in ``custom_vocabularies`` field


class ContentFieldEditor(TypedDict):
Expand Down
6 changes: 6 additions & 0 deletions server/planning/validate/planning_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ def _validate_multilingual(self, multilingual, field, value):
"""
pass

def _validate_vocabularies(self, vocabularies, field, value):
"""
{'type': 'list', 'nullable': True}
"""
pass


class PlanningValidateResource(Resource):
endpoint_name = "planning_validator"
Expand Down
Loading