Skip to content

Commit 63af7ac

Browse files
Merge branch 'master' of github.com:GeoNode/geonode into modular
2 parents a2bf0f0 + bde9c9d commit 63af7ac

File tree

29 files changed

+314
-217
lines changed

29 files changed

+314
-217
lines changed

geonode/base/api/filters.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ def filter_queryset(self, request, queryset, view):
138138
# advertised
139139
# if superuser, all resources will be visible, otherwise only the advertised one and
140140
# the resource which the user is owner will be returned
141+
142+
if getattr(view, "action", None) == "retrieve":
143+
return queryset
144+
141145
user = request.user
142146
try:
143147
_filter = request.query_params.get("advertised", "None")

geonode/base/apps.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,11 @@ class BaseAppConfig(NotificationsAppConfigBase, AppConfig):
4242
_("Resource is created"),
4343
),
4444
)
45+
46+
def ready(self):
47+
"""Finalize setup"""
48+
from geonode.base.signals import connect_signals
49+
50+
connect_signals()
51+
52+
super(BaseAppConfig, self).ready()

geonode/base/fixtures/test_xml.xml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,18 @@
251251
</gmd:role>
252252
</gmd:CI_ResponsibleParty>
253253
</gmd:pointOfContact>
254-
255254

256-
255+
<gmd:resourceMaintenance>
256+
<gmd:MD_MaintenanceInformation>
257+
<gmd:maintenanceAndUpdateFrequency>
258+
<gmd:MD_MaintenanceFrequencyCode
259+
codeSpace="ISOTC211/19115"
260+
codeList="http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml#MD_MaintenanceFrequencyCode"
261+
codeListValue="daily">daily</gmd:MD_MaintenanceFrequencyCode>
262+
</gmd:maintenanceAndUpdateFrequency>
263+
</gmd:MD_MaintenanceInformation>
264+
</gmd:resourceMaintenance>
265+
257266
<gmd:graphicOverview>
258267
<gmd:MD_BrowseGraphic>
259268
<gmd:fileName>
Lines changed: 52 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import logging
2-
from datetime import datetime
32

4-
from cachetools import FIFOCache
53
from django.db import connection
4+
from django.utils.translation import get_language, gettext as _
65

76
from geonode.base.models import ThesaurusKeywordLabel, Thesaurus
87

9-
108
logger = logging.getLogger(__name__)
119

1210
I18N_THESAURUS_IDENTIFIER = "labels-i18n"
@@ -47,6 +45,7 @@ def get_localized_tkeywords(lang, thesaurus_identifier: str):
4745
return sorted(ret.values(), key=lambda i: i["about"].lower())
4846

4947

48+
# TODO: deprecate and use LabelResolver.gettext()
5049
def get_localized_label(lang, about):
5150
# look for override
5251
ovr_qs = ThesaurusKeywordLabel.objects.filter(
@@ -65,41 +64,46 @@ def get_localized_label(lang, about):
6564
)
6665

6766

68-
class I18nCache:
67+
class I18nCacheEntry:
68+
def __init__(self):
69+
# the date field of the thesaurus when it was last loaded, it's used for the expiration check
70+
self.date: str | None = None
71+
self.caches: dict = {} # the caches for this language
6972

70-
DATA_KEY_SCHEMA = "schema"
71-
DATA_KEY_LABELS = "labels"
73+
74+
class I18nCache:
75+
"""
76+
Caches language related data.
77+
Synch is performed via date field in the "labels-i18n" thesaurus.
78+
"""
7279

7380
def __init__(self):
74-
# the cache has the lang as key, and various info in the dict value:
75-
# - date: the date field of the thesaurus when it was last loaded, it's used for the expiration check
76-
# - labels: the keyword labels from the i18n thesaurus
77-
# - schema: the localized json schema
78-
# FIFO bc we want to renew the data once in a while
79-
self.cache = FIFOCache(16)
81+
# the cache has the lang as key, and I18nCacheEntry as a value:
82+
self.lang_cache = {}
8083

8184
def get_entry(self, lang, data_key):
8285
"""
8386
returns date:str, data
8487
date is needed for checking the entry freshness when setting info
8588
data may be None if not cached or expired
8689
"""
87-
cached_entry = self.cache.get(lang, None)
90+
cached_entry: I18nCacheEntry = self.lang_cache.get(lang, None)
8891

92+
# TODO: thesaurus date check should be done only after a given time interval from last check
8993
thesaurus_date = ( # may be none if thesaurus does not exist
9094
Thesaurus.objects.filter(identifier=I18N_THESAURUS_IDENTIFIER).values_list("date", flat=True).first()
9195
)
9296
if cached_entry:
93-
if thesaurus_date == cached_entry["date"]:
97+
if thesaurus_date == cached_entry.date:
9498
# only return cached data if thesaurus has not been modified
95-
return thesaurus_date, cached_entry.get(data_key, None)
99+
return thesaurus_date, cached_entry.caches.get(data_key, None)
96100
else:
97101
logger.info(f"Schema for {lang}:{data_key} needs to be recreated")
98102

99103
return thesaurus_date, None
100104

101105
def set(self, lang: str, data_key: str, data: dict, request_date: str):
102-
cached_entry: dict = self.cache.setdefault(lang, {})
106+
cached_entry: I18nCacheEntry = self.lang_cache.setdefault(lang, I18nCacheEntry())
103107

104108
latest_date = (
105109
Thesaurus.objects.filter(identifier=I18N_THESAURUS_IDENTIFIER).values_list("date", flat=True).first()
@@ -108,60 +112,48 @@ def set(self, lang: str, data_key: str, data: dict, request_date: str):
108112
if request_date == latest_date:
109113
# no changes after processing, set the info right away
110114
logger.debug(f"Caching lang:{lang} key:{data_key} date:{request_date}")
111-
cached_entry.update({"date": latest_date, data_key: data})
115+
cached_entry.date = latest_date
116+
cached_entry.caches[data_key] = data
112117
else:
113118
logger.warning(
114119
f"Cache will not be updated for lang:{lang} key:{data_key} reqdate:{request_date} latest:{latest_date}"
115120
)
116121

117-
def get_labels(self, lang):
118-
date, labels = self.get_entry(lang, self.DATA_KEY_LABELS)
119-
if labels is None:
120-
labels = {}
121-
for i in get_localized_tkeywords(lang, I18N_THESAURUS_IDENTIFIER):
122-
about = i["about"]
123-
if about.endswith(OVR_SUFFIX) and not i["label"]:
124-
# we don't want default values for override entries
125-
continue
126-
labels[about] = i["label"] or i["default"]
127-
self.set(lang, self.DATA_KEY_LABELS, labels, date)
128-
return labels
129-
130-
def clear_schema_cache(self):
122+
def clear(self):
131123
logger.info("Clearing schema cache")
132-
while True:
133-
try:
134-
self.cache.popitem()
135-
except KeyError:
136-
return
124+
self.lang_cache.clear()
137125

138126

139-
def thesaurus_changed(sender, instance, **kwargs):
140-
if instance.identifier == I18N_THESAURUS_IDENTIFIER:
141-
if hasattr(instance, "_signal_handled"): # avoid signal recursion
142-
return
143-
logger.debug(f"Thesaurus changed: {instance.identifier}")
144-
_update_thesaurus_date()
127+
class LabelResolver:
128+
CACHE_KEY_LABELS = "labels"
145129

130+
def gettext(self, key, lang=None, fallback=True):
131+
"""
132+
Return the translated text in the label Thesaurus, falling back to the PO/MO translation.
133+
If fallback=False only search in the label Thesaurus, and may return None if not found
134+
"""
135+
lang = lang or get_language()
136+
# TODO: implement the OVR search
137+
return self.get_labels(lang).get(key, None) or (_(key) if fallback else None)
146138

147-
def thesaurusk_changed(sender, instance, **kwargs):
148-
if instance.thesaurus.identifier == I18N_THESAURUS_IDENTIFIER:
149-
logger.debug(f"ThesaurusKeyword changed: {instance.about} ALT:{instance.alt_label}")
150-
_update_thesaurus_date()
151-
139+
def get_labels(self, lang):
140+
date, labels = i18nCache.get_entry(lang, self.CACHE_KEY_LABELS)
141+
if labels is None:
142+
labels = self._create_labels_cache(lang)
143+
i18nCache.set(lang, self.CACHE_KEY_LABELS, labels, date)
144+
return labels
152145

153-
def thesauruskl_changed(sender, instance, **kwargs):
154-
if instance.keyword.thesaurus.identifier == I18N_THESAURUS_IDENTIFIER:
155-
logger.debug(
156-
f"ThesaurusKeywordLabel changed: {instance.keyword.about} ALT:{instance.keyword.alt_label} L:{instance.lang}"
157-
)
158-
_update_thesaurus_date()
146+
def _create_labels_cache(self, lang):
147+
labels = {}
148+
for i in get_localized_tkeywords(lang, I18N_THESAURUS_IDENTIFIER):
149+
about = i["about"]
150+
if about.endswith(OVR_SUFFIX) and not i["label"]:
151+
# we don't want default values for override entries
152+
continue
153+
labels[about] = i["label"] or i["default"]
154+
return labels
159155

160156

161-
def _update_thesaurus_date():
162-
logger.debug("Updating label thesaurus date")
163-
# update timestamp to invalidate other processes also
164-
i18n_thesaurus = Thesaurus.objects.get(identifier=I18N_THESAURUS_IDENTIFIER)
165-
i18n_thesaurus.date = datetime.now().replace(microsecond=0).isoformat()
166-
i18n_thesaurus._signal_handled = True
167-
i18n_thesaurus.save()
157+
i18nCache = I18nCache()
158+
labelResolver = LabelResolver()
159+
gettext = labelResolver.gettext

geonode/base/signals.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import logging
2+
from datetime import datetime
3+
4+
from django.db.models.signals import post_save
5+
6+
from geonode.base.i18n import I18N_THESAURUS_IDENTIFIER
7+
from geonode.base.models import Thesaurus, ThesaurusKeyword, ThesaurusKeywordLabel
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def connect_signals():
13+
logger.debug("Setting up signal connections...")
14+
post_save.connect(thesaurus_changed, sender=Thesaurus, weak=False, dispatch_uid="metadata_reset_t")
15+
post_save.connect(thesaurusk_changed, sender=ThesaurusKeyword, weak=False, dispatch_uid="metadata_reset_tk")
16+
post_save.connect(thesauruskl_changed, sender=ThesaurusKeywordLabel, weak=False, dispatch_uid="metadata_reset_tkl")
17+
logger.debug("Signal connections set")
18+
19+
20+
def thesaurus_changed(sender, instance, **kwargs):
21+
if instance.identifier == I18N_THESAURUS_IDENTIFIER:
22+
if hasattr(instance, "_signal_handled"): # avoid signal recursion
23+
return
24+
logger.debug(f"Thesaurus changed: {instance.identifier}")
25+
_update_thesaurus_date()
26+
27+
28+
def thesaurusk_changed(sender, instance, **kwargs):
29+
if instance.thesaurus.identifier == I18N_THESAURUS_IDENTIFIER:
30+
logger.debug(f"ThesaurusKeyword changed: {instance.about} ALT:{instance.alt_label}")
31+
_update_thesaurus_date()
32+
33+
34+
def thesauruskl_changed(sender, instance, **kwargs):
35+
if instance.keyword.thesaurus.identifier == I18N_THESAURUS_IDENTIFIER:
36+
logger.debug(
37+
f"ThesaurusKeywordLabel changed: {instance.keyword.about} ALT:{instance.keyword.alt_label} L:{instance.lang}"
38+
)
39+
_update_thesaurus_date()
40+
41+
42+
def _update_thesaurus_date():
43+
logger.debug("Updating label thesaurus date")
44+
# update timestamp to invalidate other processes also
45+
i18n_thesaurus = Thesaurus.objects.get(identifier=I18N_THESAURUS_IDENTIFIER)
46+
i18n_thesaurus.date = datetime.now().replace(microsecond=0).isoformat()
47+
i18n_thesaurus._signal_handled = True
48+
i18n_thesaurus.save()

0 commit comments

Comments
 (0)