udata 11.0.2.dev20__py3-none-any.whl → 11.0.2.dev22__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- udata/core/activity/models.py +3 -11
- udata/core/dataservices/rdf.py +4 -7
- udata/harvest/tests/test_dcat_backend.py +45 -1
- udata/tests/test_activity.py +17 -0
- udata/utils.py +32 -1
- {udata-11.0.2.dev20.dist-info → udata-11.0.2.dev22.dist-info}/METADATA +1 -1
- {udata-11.0.2.dev20.dist-info → udata-11.0.2.dev22.dist-info}/RECORD +11 -11
- {udata-11.0.2.dev20.dist-info → udata-11.0.2.dev22.dist-info}/WHEEL +0 -0
- {udata-11.0.2.dev20.dist-info → udata-11.0.2.dev22.dist-info}/entry_points.txt +0 -0
- {udata-11.0.2.dev20.dist-info → udata-11.0.2.dev22.dist-info}/licenses/LICENSE +0 -0
- {udata-11.0.2.dev20.dist-info → udata-11.0.2.dev22.dist-info}/top_level.txt +0 -0
udata/core/activity/models.py
CHANGED
|
@@ -9,7 +9,7 @@ from mongoengine.signals import post_save
|
|
|
9
9
|
from udata.api_fields import get_fields
|
|
10
10
|
from udata.auth import current_user
|
|
11
11
|
from udata.mongo import db
|
|
12
|
-
from udata.utils import get_field_value_from_path
|
|
12
|
+
from udata.utils import filter_changed_fields, get_field_value_from_path
|
|
13
13
|
|
|
14
14
|
from .signals import new_activity
|
|
15
15
|
|
|
@@ -130,16 +130,8 @@ class Auditable(object):
|
|
|
130
130
|
cls.on_create.send(document)
|
|
131
131
|
elif len(changed_fields):
|
|
132
132
|
previous = getattr(document, "_previous_changed_fields", None)
|
|
133
|
-
#
|
|
134
|
-
|
|
135
|
-
# We compare them one by one with the previous value stored in _previous_changed_fields.
|
|
136
|
-
# See https://github.com/opendatateam/udata/pull/3412 for more context.
|
|
137
|
-
document.reload()
|
|
138
|
-
changed_fields = [
|
|
139
|
-
field
|
|
140
|
-
for field in changed_fields
|
|
141
|
-
if previous[field] != get_field_value_from_path(document, field)
|
|
142
|
-
]
|
|
133
|
+
# Filter changed_fields since mongoengine raises some false positive occurences
|
|
134
|
+
changed_fields = filter_changed_fields(document, previous, changed_fields)
|
|
143
135
|
if changed_fields:
|
|
144
136
|
cls.on_update.send(document, changed_fields=changed_fields, previous=previous)
|
|
145
137
|
if getattr(document, "deleted_at", None) or getattr(document, "deleted", None):
|
udata/core/dataservices/rdf.py
CHANGED
|
@@ -32,7 +32,7 @@ def dataservice_from_rdf(
|
|
|
32
32
|
remote_url_prefix: str | None = None,
|
|
33
33
|
) -> Dataservice:
|
|
34
34
|
"""
|
|
35
|
-
Create or update a
|
|
35
|
+
Create or update a dataservice from a RDF/DCAT graph
|
|
36
36
|
"""
|
|
37
37
|
if node is None: # Assume first match is the only match
|
|
38
38
|
node = graph.value(predicate=RDF.type, object=DCAT.DataService)
|
|
@@ -57,7 +57,6 @@ def dataservice_from_rdf(
|
|
|
57
57
|
contact_point for role in roles for contact_point in role
|
|
58
58
|
] or dataservice.contact_points
|
|
59
59
|
|
|
60
|
-
datasets = []
|
|
61
60
|
for dataset_node in d.objects(DCAT.servesDataset):
|
|
62
61
|
id = dataset_node.value(DCT.identifier)
|
|
63
62
|
dataset = next(
|
|
@@ -71,11 +70,9 @@ def dataservice_from_rdf(
|
|
|
71
70
|
None,
|
|
72
71
|
)
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if datasets:
|
|
78
|
-
dataservice.datasets = datasets
|
|
73
|
+
# We append the dataset to the list of the current attached ones if not already attached
|
|
74
|
+
if dataset is not None and dataset not in dataservice.datasets:
|
|
75
|
+
dataservice.datasets.append(dataset)
|
|
79
76
|
|
|
80
77
|
license = rdf_value(d, DCT.license)
|
|
81
78
|
if license is not None:
|
|
@@ -8,8 +8,9 @@ from flask import current_app
|
|
|
8
8
|
from lxml import etree
|
|
9
9
|
from rdflib import Graph
|
|
10
10
|
|
|
11
|
+
from udata.core.dataservices.factories import DataserviceFactory
|
|
11
12
|
from udata.core.dataservices.models import Dataservice
|
|
12
|
-
from udata.core.dataset.factories import LicenseFactory, ResourceSchemaMockData
|
|
13
|
+
from udata.core.dataset.factories import DatasetFactory, LicenseFactory, ResourceSchemaMockData
|
|
13
14
|
from udata.core.dataset.rdf import dataset_from_rdf
|
|
14
15
|
from udata.core.organization.factories import OrganizationFactory
|
|
15
16
|
from udata.harvest.models import HarvestJob
|
|
@@ -187,6 +188,49 @@ class DcatBackendTest:
|
|
|
187
188
|
== "https://data.paris2024.org/api/explore/v2.1/console"
|
|
188
189
|
)
|
|
189
190
|
|
|
191
|
+
def test_harvest_dataservices_keep_attached_associated_datasets(self, rmock):
|
|
192
|
+
"""It should update the existing list of dataservice.datasets and not overwrite existing ones"""
|
|
193
|
+
rmock.get("https://example.com/schemas", json=ResourceSchemaMockData.get_mock_data())
|
|
194
|
+
|
|
195
|
+
filename = "bnodes.xml"
|
|
196
|
+
url = mock_dcat(rmock, filename)
|
|
197
|
+
org = OrganizationFactory()
|
|
198
|
+
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
199
|
+
|
|
200
|
+
previously_attached_dataset = DatasetFactory()
|
|
201
|
+
previously_harvested_dataset = DatasetFactory(
|
|
202
|
+
harvest={
|
|
203
|
+
"remote_id": "2",
|
|
204
|
+
"domain": source.domain,
|
|
205
|
+
"source_id": str(source.id),
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
existing_dataservice = DataserviceFactory(
|
|
209
|
+
# Two datasets are already attached, the first one NOT connected via harvesting
|
|
210
|
+
# when the second one is connected with dcat:servesDataset in harvest graph
|
|
211
|
+
datasets=[
|
|
212
|
+
previously_attached_dataset,
|
|
213
|
+
previously_harvested_dataset,
|
|
214
|
+
],
|
|
215
|
+
harvest={
|
|
216
|
+
"remote_id": "https://data.paris2024.org/api/explore/v2.1/",
|
|
217
|
+
"domain": source.domain,
|
|
218
|
+
"source_id": str(source.id),
|
|
219
|
+
},
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
actions.run(source)
|
|
223
|
+
|
|
224
|
+
existing_dataservice.reload()
|
|
225
|
+
|
|
226
|
+
assert len(Dataservice.objects) == 1
|
|
227
|
+
assert existing_dataservice.title == "Explore API v2"
|
|
228
|
+
assert (
|
|
229
|
+
len(existing_dataservice.datasets) == 2 + 1
|
|
230
|
+
) # The previsouly harvested dataset, the previously attached one and a new harvested dataset
|
|
231
|
+
assert previously_attached_dataset in existing_dataservice.datasets
|
|
232
|
+
assert previously_harvested_dataset in existing_dataservice.datasets
|
|
233
|
+
|
|
190
234
|
def test_harvest_dataservices_ignore_accessservices(self, rmock):
|
|
191
235
|
rmock.get("https://example.com/schemas", json=ResourceSchemaMockData.get_mock_data())
|
|
192
236
|
|
udata/tests/test_activity.py
CHANGED
|
@@ -26,6 +26,7 @@ class FakeAuditableSubject(Auditable, db.Document):
|
|
|
26
26
|
tags = field(db.TagListField())
|
|
27
27
|
some_date = field(db.DateField())
|
|
28
28
|
daterange_embedded = field(db.EmbeddedDocumentField(db.DateRange))
|
|
29
|
+
some_list = field(db.ListField(db.StringField()))
|
|
29
30
|
embedded_list = field(db.ListField(db.EmbeddedDocumentField("FakeEmbedded")))
|
|
30
31
|
ref_list = field(db.ListField(db.ReferenceField("FakeSubject")))
|
|
31
32
|
not_auditable = field(db.StringField(), auditable=False)
|
|
@@ -128,6 +129,7 @@ class AuditableTest(WebTestMixin, DBTestMixin, TestCase):
|
|
|
128
129
|
tags=["some", "tags"],
|
|
129
130
|
some_date=date(2020, 1, 1),
|
|
130
131
|
daterange_embedded={"start": date(2020, 1, 1), "end": date(2020, 12, 31)},
|
|
132
|
+
some_list=["some", "list"],
|
|
131
133
|
embedded_list=[FakeEmbedded(name=f"fake_embedded_{i}") for i in range(3)],
|
|
132
134
|
ref_list=[FakeSubject.objects.create(name=f"fake_ref_{i}") for i in range(3)],
|
|
133
135
|
not_auditable="original",
|
|
@@ -150,6 +152,10 @@ class AuditableTest(WebTestMixin, DBTestMixin, TestCase):
|
|
|
150
152
|
with assert_emit(post_save, FakeAuditableSubject.on_update):
|
|
151
153
|
fake.save()
|
|
152
154
|
|
|
155
|
+
fake.some_list = ["other", "list"]
|
|
156
|
+
with assert_emit(post_save, FakeAuditableSubject.on_update):
|
|
157
|
+
fake.save()
|
|
158
|
+
|
|
153
159
|
fake.embedded_list[1].name = "other"
|
|
154
160
|
with assert_emit(post_save, FakeAuditableSubject.on_update):
|
|
155
161
|
fake.save()
|
|
@@ -181,6 +187,13 @@ class AuditableTest(WebTestMixin, DBTestMixin, TestCase):
|
|
|
181
187
|
fake.reload()
|
|
182
188
|
self.assertEqual(fake.some_date, date(2027, 7, 7))
|
|
183
189
|
|
|
190
|
+
# 3. Reordering of some elements in a list
|
|
191
|
+
fake.some_list = ["list", "other"]
|
|
192
|
+
with assert_not_emit(FakeAuditableSubject.on_update):
|
|
193
|
+
fake.save()
|
|
194
|
+
fake.reload()
|
|
195
|
+
self.assertEqual(fake.some_list, ["list", "other"])
|
|
196
|
+
|
|
184
197
|
# The deletion should trigger a delete signal
|
|
185
198
|
with assert_not_emit(FakeAuditableSubject.on_update):
|
|
186
199
|
fake.delete()
|
|
@@ -192,6 +205,7 @@ class AuditableTest(WebTestMixin, DBTestMixin, TestCase):
|
|
|
192
205
|
tags=["some", "tags"],
|
|
193
206
|
some_date=date(2020, 1, 1),
|
|
194
207
|
daterange_embedded={"start": date(2020, 1, 1), "end": date(2020, 12, 31)},
|
|
208
|
+
some_list=["some", "list"],
|
|
195
209
|
embedded_list=[FakeEmbedded(name=f"fake_embedded_{i}") for i in range(3)],
|
|
196
210
|
ref_list=[FakeSubject.objects.create(name=f"fake_ref_{i}") for i in range(3)],
|
|
197
211
|
not_auditable="original",
|
|
@@ -204,6 +218,7 @@ class AuditableTest(WebTestMixin, DBTestMixin, TestCase):
|
|
|
204
218
|
"name",
|
|
205
219
|
"tags",
|
|
206
220
|
"some_date",
|
|
221
|
+
"some_list",
|
|
207
222
|
"daterange_embedded.start",
|
|
208
223
|
"daterange_embedded.end",
|
|
209
224
|
"embedded_list.1.name",
|
|
@@ -214,6 +229,7 @@ class AuditableTest(WebTestMixin, DBTestMixin, TestCase):
|
|
|
214
229
|
self.assertEqual(args[1]["previous"]["some_date"], date(2020, 1, 1))
|
|
215
230
|
self.assertEqual(args[1]["previous"]["daterange_embedded.start"], date(2020, 1, 1))
|
|
216
231
|
self.assertEqual(args[1]["previous"]["daterange_embedded.end"], date(2020, 12, 31))
|
|
232
|
+
self.assertEqual(args[1]["previous"]["some_list"], ["some", "list"])
|
|
217
233
|
self.assertEqual(args[1]["previous"]["embedded_list.1.name"], "fake_embedded_1")
|
|
218
234
|
|
|
219
235
|
with assert_emit(FakeAuditableSubject.on_update, assertions_callback=check_signal_update):
|
|
@@ -222,6 +238,7 @@ class AuditableTest(WebTestMixin, DBTestMixin, TestCase):
|
|
|
222
238
|
fake.some_date = date(2027, 7, 7)
|
|
223
239
|
fake.daterange_embedded.start = date(2017, 7, 7)
|
|
224
240
|
fake.daterange_embedded.end = date(2027, 7, 7)
|
|
241
|
+
fake.some_list = ["other", "list"]
|
|
225
242
|
fake.embedded_list[1].name = "other"
|
|
226
243
|
# Modification of a reference document should not be taken into account in changed_fields
|
|
227
244
|
fake.ref_list[1].name = "other"
|
udata/utils.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import hashlib
|
|
2
|
+
import itertools
|
|
2
3
|
import math
|
|
3
4
|
import re
|
|
5
|
+
from collections import Counter
|
|
4
6
|
from datetime import date, datetime
|
|
5
7
|
from math import ceil
|
|
6
|
-
from typing import Any
|
|
8
|
+
from typing import Any, Hashable
|
|
7
9
|
from uuid import UUID, uuid4
|
|
8
10
|
from xml.sax.saxutils import escape
|
|
9
11
|
|
|
@@ -53,6 +55,35 @@ def get_field_value_from_path(document, field_path: str):
|
|
|
53
55
|
return doc_field
|
|
54
56
|
|
|
55
57
|
|
|
58
|
+
def filter_changed_fields(document, previous, changed_fields: list[str]):
|
|
59
|
+
# Make sure that changed fields have actually changed.
|
|
60
|
+
# We compare the document values once it has been reloaded.
|
|
61
|
+
# It may have been cleaned or normalized when saved to mongo.
|
|
62
|
+
# We compare the field values one by one with the previous value stored in _previous_changed_fields.
|
|
63
|
+
# We also ignore reordering in the case of list, ex tags or contact points.
|
|
64
|
+
# See https://github.com/opendatateam/udata/pull/3412 for more context.
|
|
65
|
+
document.reload()
|
|
66
|
+
filtered_changed_fields = []
|
|
67
|
+
for field in changed_fields:
|
|
68
|
+
previous_value = previous[field]
|
|
69
|
+
current_value = get_field_value_from_path(document, field)
|
|
70
|
+
# Filter out special case of list reordering, does not support unhashable types
|
|
71
|
+
if (
|
|
72
|
+
isinstance(previous_value, list)
|
|
73
|
+
and isinstance(current_value, list)
|
|
74
|
+
and all(
|
|
75
|
+
isinstance(value, Hashable)
|
|
76
|
+
for value in itertools.chain(previous_value, current_value)
|
|
77
|
+
)
|
|
78
|
+
):
|
|
79
|
+
if Counter(previous_value) != Counter(current_value):
|
|
80
|
+
filtered_changed_fields.append(field)
|
|
81
|
+
# Direct comparison for the rest of the fields
|
|
82
|
+
elif previous_value != current_value:
|
|
83
|
+
filtered_changed_fields.append(field)
|
|
84
|
+
return filtered_changed_fields
|
|
85
|
+
|
|
86
|
+
|
|
56
87
|
FIRST_CAP_RE = re.compile("(.)([A-Z][a-z]+)")
|
|
57
88
|
ALL_CAP_RE = re.compile("([a-z0-9])([A-Z])")
|
|
58
89
|
UUID_LENGTH = 36
|
|
@@ -18,7 +18,7 @@ udata/tasks.py,sha256=Sv01dhvATtq_oHOBp3J1j1VT1HQe0Pab7zxwIeIdKoo,5122
|
|
|
18
18
|
udata/terms.md,sha256=nFx978tUQ3vTEv6POykXaZvcQ5e_gcvmO4ZgcfbSWXo,187
|
|
19
19
|
udata/tracking.py,sha256=WOcqA1RlHN8EPFuEc2kNau54mec4-pvi-wUFrMXevzg,345
|
|
20
20
|
udata/uris.py,sha256=1wOrsxu6lmZJ1h4634kNHjqOjaOO0D5cIWKF_v_Gtn4,4264
|
|
21
|
-
udata/utils.py,sha256=
|
|
21
|
+
udata/utils.py,sha256=mtosjF91SPuSM-63EyxjLLnxs5DT0iSBdz-ECNeTYGU,11128
|
|
22
22
|
udata/worker.py,sha256=K-Wafye5-uXP4kQlffRKws2J9YbJ6m6n2QjcVsY8Nsg,118
|
|
23
23
|
udata/wsgi.py,sha256=MY8en9K9eDluvJYUxTdzqSDoYaDgCVZ69ZcUvxAvgqA,77
|
|
24
24
|
udata/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -57,7 +57,7 @@ udata/core/linkable.py,sha256=8YuTzZo5Y36CVIYxESC-EPJ-zKsQTRbi4OIZTp-wWig,587
|
|
|
57
57
|
udata/core/owned.py,sha256=OQT7wdk7dAqGvWDiJRVkKJxerDc9_Io910nvLmfBAVI,5561
|
|
58
58
|
udata/core/activity/__init__.py,sha256=dLTseBmVYbQzxB7OR7Dz0LtIwEjNQvgUSR7SwfInb68,399
|
|
59
59
|
udata/core/activity/api.py,sha256=dwdEkJxxwbQ2PsCSnv6fEE8Ps1FiJ4fEHpTog_gk4Rs,4291
|
|
60
|
-
udata/core/activity/models.py,sha256=
|
|
60
|
+
udata/core/activity/models.py,sha256=wJ5SyvTJXsaTUbJRQGK1ZvkJCG8bmlHSDrro1afsrhM,4926
|
|
61
61
|
udata/core/activity/signals.py,sha256=Io2A43as3yR-DZ5R3wzM_bTpn528pxWsZDUFZ9xtj2Y,191
|
|
62
62
|
udata/core/activity/tasks.py,sha256=F3PY12dnpT5Z8VuYfuOyDP6VPKPJmq1Sm4lSiPfmUCA,1498
|
|
63
63
|
udata/core/badges/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -88,7 +88,7 @@ udata/core/dataservices/csv.py,sha256=HWI2JrN_Vuw0te9FHlJ6eyqcRcKHOKXuzg45D4Ti6F
|
|
|
88
88
|
udata/core/dataservices/factories.py,sha256=pKVoArNSCIbvGA-cWUc7vr8TmjYsUvOXzzcuUB5JyF4,964
|
|
89
89
|
udata/core/dataservices/models.py,sha256=Kkgf_9TfuuijtckoD6c4mM6bN7DZq508HmL_vUkNoqI,12384
|
|
90
90
|
udata/core/dataservices/permissions.py,sha256=98zM_R4v2ZtRubflB7ajaVQz-DVc-pZBMgtKUYy34oI,169
|
|
91
|
-
udata/core/dataservices/rdf.py,sha256=
|
|
91
|
+
udata/core/dataservices/rdf.py,sha256=lbOeH-2IG_4zz6WQtD4L_ADq9iZfdSQLAiutanxd8gU,8023
|
|
92
92
|
udata/core/dataservices/search.py,sha256=Tt7CUqb49Rl4hvkfGO3AiNs1Oc7HhTeBp80xQK8wwXc,4911
|
|
93
93
|
udata/core/dataservices/tasks.py,sha256=fHG1r5ymfJRXJ_Lug6je3VKZoK30XKXE2rQ8x0R-jUk,1068
|
|
94
94
|
udata/core/dataset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -318,7 +318,7 @@ udata/harvest/tests/person.jsonld,sha256=I7Ynh-PQlNeD51I1LrCgYOEjhL-WBeb65xzIE_s
|
|
|
318
318
|
udata/harvest/tests/test_actions.py,sha256=d5TTFTbs4PdBydWICqDtfoeo3zLyzcNDzv4aMH9spxo,25881
|
|
319
319
|
udata/harvest/tests/test_api.py,sha256=gSuICkPy3KVRUhHAyudXVf_gLwiB7SoriUp3DLXWDdA,21611
|
|
320
320
|
udata/harvest/tests/test_base_backend.py,sha256=ow8ecGtD836mUqyPWYjkS5nx0STyT5RMLgBdDyOhts4,19233
|
|
321
|
-
udata/harvest/tests/test_dcat_backend.py,sha256=
|
|
321
|
+
udata/harvest/tests/test_dcat_backend.py,sha256=8w_wZepgkb0_j9Ah5f4qMwJIpuH7bTqmc4u5MaaP7G4,50551
|
|
322
322
|
udata/harvest/tests/test_filters.py,sha256=PT2qopEIoXsqi8MsNDRuhNH7jGXiQo8r0uJrCOUd4aM,2465
|
|
323
323
|
udata/harvest/tests/test_models.py,sha256=f9NRR2_S4oZFgF8qOumg0vv-lpnEBJbI5vNtcwFdSqM,831
|
|
324
324
|
udata/harvest/tests/test_notifications.py,sha256=MMzTzkv-GXMNFeOwAi31rdTsAXyLCLOSna41zOtaJG0,816
|
|
@@ -631,7 +631,7 @@ udata/tests/helpers.py,sha256=VP8cz13WBxkMoTUbOwnMV2-bzE4nVHI6l_z6m3u1DIE,5975
|
|
|
631
631
|
udata/tests/models.py,sha256=5oTC-cgKSL0sUdlqjUiJ6U8-YZBQanObb-MhZhQIV3M,238
|
|
632
632
|
udata/tests/plugin.py,sha256=QDY6fqjozck1_KrNGqN4wkIUAdACpAnUDOW6GTjKmqQ,12480
|
|
633
633
|
udata/tests/schemas.json,sha256=szM1jDpkogfOG4xWbjIGjLgG8l9-ZyE3JKQtecJyD1E,4990
|
|
634
|
-
udata/tests/test_activity.py,sha256=
|
|
634
|
+
udata/tests/test_activity.py,sha256=6VpJbSNPSwBOcPXg1gHZF7kpEMzpvfd9TOvyqePQzRA,9871
|
|
635
635
|
udata/tests/test_api_fields.py,sha256=NCUTtOMEaTM5-tK-YUxhjEud2IPIDOHR3vbZWAQdECg,12786
|
|
636
636
|
udata/tests/test_cors.py,sha256=b_pyxKeIyqhnsXxXryPf4d0V0QxaLQ1P_VjY89Q_j3g,3233
|
|
637
637
|
udata/tests/test_dcat_commands.py,sha256=fDAnAjkja8AXw_qzaAWnVTgglkBAvK2mjPMHUCtqrrU,919
|
|
@@ -764,9 +764,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=U0abG-nBwCIoYxRZNsc4KOLeIRSqTV
|
|
|
764
764
|
udata/translations/pt/LC_MESSAGES/udata.po,sha256=eCG35rMzYLHXyLbsnLSexS1g0N_K-WpNHqrt_8y6I4E,48590
|
|
765
765
|
udata/translations/sr/LC_MESSAGES/udata.mo,sha256=IBcCAdmcvkeK7ZeRBNRI-wJ0jzWNM0eXM5VXAc1frWI,28692
|
|
766
766
|
udata/translations/sr/LC_MESSAGES/udata.po,sha256=yFxHEEB4behNwQ7JnyoYheiCKLNnMS_NV4guzgyzWcE,55332
|
|
767
|
-
udata-11.0.2.
|
|
768
|
-
udata-11.0.2.
|
|
769
|
-
udata-11.0.2.
|
|
770
|
-
udata-11.0.2.
|
|
771
|
-
udata-11.0.2.
|
|
772
|
-
udata-11.0.2.
|
|
767
|
+
udata-11.0.2.dev22.dist-info/licenses/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
|
|
768
|
+
udata-11.0.2.dev22.dist-info/METADATA,sha256=TjMQIGWeKQ9tIiKPyr0UxYK0XFjjBU9hGQiJAxrQj0I,6769
|
|
769
|
+
udata-11.0.2.dev22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
770
|
+
udata-11.0.2.dev22.dist-info/entry_points.txt,sha256=v2u12qO11i2lyLNIp136WmLJ-NHT-Kew3Duu8J-AXPM,614
|
|
771
|
+
udata-11.0.2.dev22.dist-info/top_level.txt,sha256=EF6CE6YSHd_og-8LCEA4q25ALUpWVe8D0okOLdMAE3A,6
|
|
772
|
+
udata-11.0.2.dev22.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|