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

Improve consortium management #38

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions api/admin/controller/library_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from api.admin.problem_details import *
from api.circulation_manager import CirculationManager
from api.config import Configuration
from api.ekirjasto_consortium import EkirjastoConsortiumMonitor
from api.lanes import create_default_lanes
from core.configuration.library import LibrarySettings
from core.model import (
Expand All @@ -31,6 +32,7 @@
from core.model.announcements import SETTING_NAME as ANNOUNCEMENT_SETTING_NAME
from core.model.announcements import Announcement
from core.model.library import LibraryLogo
from core.scripts import RunMonitorScript
from core.util.problem_detail import ProblemDetail, ProblemError


Expand Down Expand Up @@ -159,6 +161,11 @@ def process_post(self) -> Response:
# Trigger a site configuration change
site_configuration_has_changed(self._db)

# Finland, update municipality list if consortium is defined
consortium = library.settings.kirkanta_consortium_slug
if consortium and consortium != "disabled":
RunMonitorScript(EkirjastoConsortiumMonitor, library=library).run()

if is_new:
# Now that the configuration settings are in place, create
# a default set of lanes.
Expand Down
26 changes: 16 additions & 10 deletions api/ekirjasto_consortium.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from core.metadata_layer import TimestampData
from core.model.library import Library
from core.monitor import Monitor
from core.util.cache import memoize


class KirkantaConsortium(BaseModel):
Expand Down Expand Up @@ -95,7 +96,11 @@ class EkirjastoConsortiumMonitor(Monitor):
_KIRKANTA_JUNK_CITIES: list[str] = ["Äävekaupunki"]

# Manually managed list of Kirkanta city names don't match with Koodistopalvelu
KIRKANTA_CITY_NAME_ALIASES: dict[str, str] = {"Pedersöre": "Pedersören kunta"}
_KIRKANTA_CITY_NAME_ALIASES: dict[str, str] = {"Pedersöre": "Pedersören kunta"}

def __init__(self, _db, collection=None, library: Library | None = None):
super().__init__(_db, collection)
self.library = library

def run_once(self, progress):
kirkanta_consortiums = self._fetch_kirkanta_consortiums()
Expand All @@ -114,8 +119,7 @@ def run_once(self, progress):
f"will be assigned to the default library."
)

libraries = self._db.query(Library).all()

libraries = [self.library] if self.library else self._db.query(Library).all()
for library in libraries:
self._synchronize_library(
library, kirkanta_consortiums, kirkanta_cities, koodisto_concept_codes
Expand All @@ -127,26 +131,26 @@ def run_once(self, progress):
f"The following Kirkanta city names were "
f"not found in Koodistopalvelu: {missing_cities}. "
f"Please add the missing city names manually to "
f"KIRKANTA_CITY_NAME_ALIASES."
f"_KIRKANTA_CITY_NAME_ALIASES."
)
return TimestampData(
achievements=f"Kirkanta synchronization done!",
)

def _fetch_kirkanta_cities(self) -> list[KirkantaCity]:
return self._get(
return EkirjastoConsortiumMonitor._get(
KirkantaCities, f"{self._KIRKANTA_API_URL}/city", {"limit": 9999}
).items

def _fetch_kirkanta_consortiums(self) -> list[KirkantaConsortium]:
return self._get(
return EkirjastoConsortiumMonitor._get(
KirkantaConsortiums,
f"{self._KIRKANTA_API_URL}/consortium",
{"status": "ACTIVE", "limit": 9999},
).items

def _fetch_koodisto_concept_codes(self, page: int = 1) -> list[KoodistoConceptCode]:
response: KoodistoConceptCodes = self._get(
response: KoodistoConceptCodes = EkirjastoConsortiumMonitor._get(
KoodistoConceptCodes,
f"{self._KOODISTO_API_URL}/classifications/{self._KOODISTO_CLASSIFICATION_ID}/conceptcodes",
{"pageSize": self._KOODISTO_MAX_ALLOWED_PAGESIZE, "page": page},
Expand Down Expand Up @@ -252,7 +256,7 @@ def _verify_all_kirkanta_cities_found_in_koodisto(
if missing_names:
logging.warning(
"The following Kirkanta city names are not found in Koodistopalvelu: %s. "
"Please add the missing city name manually to KIRKANTA_CITY_NAME_ALIASES.",
"Please add the missing city name manually to _KIRKANTA_CITY_NAME_ALIASES.",
str(missing_names),
)
return missing_names
Expand All @@ -270,7 +274,7 @@ def _to_koodisto_code(
if koodisto_city_name == kirkanta_city.name:
return code

alias: str | None = self.KIRKANTA_CITY_NAME_ALIASES.get(
alias: str | None = self._KIRKANTA_CITY_NAME_ALIASES.get(
kirkanta_city.name,
)
if alias and koodisto_city_name == alias:
Expand All @@ -280,7 +284,9 @@ def _to_koodisto_code(

R = TypeVar("R", bound=BaseModel)

def _get(self, response_type: type[R], url: str, params=None) -> R:
@memoize(ttls=3600)
@staticmethod
def _get(response_type: type[R], url: str, params=None) -> R:
"""Perform HTTP GET request and parse the response into a pydantic model of type R"""
try:
response = requests.get(url, params)
Expand Down
27 changes: 20 additions & 7 deletions core/configuration/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ class LibrarySettings(BaseSettings):
kirkanta_consortium_slug: str | None = FormField(
None,
form=LibraryConfFormItem(
label="Synchronize consortium data form Kirkanta",
description="If selected, the list of municipalities is updated automatically from Kirkanta. "
"Has no effect if configured for the default library.",
label="Consortium",
description="If selected, the municipalities are kept automatically "
"in sync with Kirkanta. Has no effect if set for the default library.",
category="Kirkanta Synchronization",
type=ConfigurationFormItemType.SELECT,
# The keys here should match the Kirkanta consortium slug
Expand Down Expand Up @@ -193,10 +193,9 @@ class LibrarySettings(BaseSettings):
form=LibraryConfFormItem(
label="The municipalities belonging to this consortium",
type=ConfigurationFormItemType.LIST,
format="municipality-code",
description="Each value should be a valid "
'<a href="https://koodistopalvelu.kanta.fi/codeserver/pages/classification-view-page.xhtml?classificationKey=362&versionKey=440" target="_blank">'
"municipality code</a>.",
"municipality code</a>. This list is populated automatically if the consortium is selected.",
category="Kirkanta Synchronization",
level=Level.ALL_ACCESS,
),
Expand Down Expand Up @@ -736,8 +735,22 @@ def validate_municipalities(
cls, value: list[str] | None, field: ModelField
) -> list[str] | None:
"""Verify that municipality IDs are valid."""
# TODO: implement validation
return value
if not value:
return value

for code in value:
if not code.isdigit():
raise SettingsValidationError(
problem_detail=UNKNOWN_LANGUAGE.detailed(
f'"{cls.get_form_field_label(field.name)}": "{code}" is not a valid language code.'
)
)

return cls._remove_duplicates(value)

@classmethod
def _remove_duplicates(cls, values: list[str]):
return list(set(values))

@validator(
"large_collection_languages",
Expand Down
Loading