udata 14.5.1.dev6__py3-none-any.whl → 14.5.1.dev9__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.
udata/core/dataset/api.py CHANGED
@@ -328,17 +328,33 @@ class DatasetListAPI(API):
328
328
  @ns.route("/recent.atom", endpoint="recent_datasets_atom_feed")
329
329
  class DatasetsAtomFeedAPI(API):
330
330
  @api.doc("recent_datasets_atom_feed")
331
+ @api.expect(dataset_parser.parser)
331
332
  def get(self):
333
+ args = dataset_parser.parse()
334
+ queryset = Dataset.objects.visible()
335
+ queryset = DatasetApiParser.parse_filters(queryset, args)
336
+
337
+ q = args.get("q").strip() if args.get("q") else ""
338
+ has_filters = any(
339
+ args.get(k)
340
+ for k in ["q", "tag", "license", "organization", "owner", "format", "badge", "topic"]
341
+ )
342
+
343
+ if q:
344
+ title = _("Datasets search: {q}").format(q=q)
345
+ elif has_filters:
346
+ title = _("Filtered datasets")
347
+ else:
348
+ title = _("Latest datasets")
349
+
332
350
  feed = Atom1Feed(
333
- _("Latest datasets"),
351
+ title,
334
352
  description=None,
335
353
  feed_url=request.url,
336
354
  link=request.url_root,
337
355
  )
338
356
 
339
- datasets: list[Dataset] = get_rss_feed_list(
340
- Dataset.objects.visible(), "created_at_internal"
341
- )
357
+ datasets: list[Dataset] = get_rss_feed_list(queryset, "created_at_internal")
342
358
 
343
359
  for dataset in datasets:
344
360
  author_name = None
@@ -332,7 +332,7 @@ dataset_fields = api.model(
332
332
  "id": fields.String(description="The dataset identifier", readonly=True),
333
333
  "title": fields.String(description="The dataset title", required=True),
334
334
  "acronym": fields.String(description="An optional dataset acronym"),
335
- "slug": fields.String(description="The dataset permalink string", required=True),
335
+ "slug": fields.String(description="The dataset permalink string", readonly=True),
336
336
  "description": fields.Markdown(
337
337
  description="The dataset description in markdown", required=True
338
338
  ),
@@ -108,7 +108,7 @@ dataset_fields = apiv2.model(
108
108
  "id": fields.String(description="The dataset identifier", readonly=True),
109
109
  "title": fields.String(description="The dataset title", required=True),
110
110
  "acronym": fields.String(description="An optional dataset acronym"),
111
- "slug": fields.String(description="The dataset permalink string", required=True),
111
+ "slug": fields.String(description="The dataset permalink string", readonly=True),
112
112
  "description": fields.Markdown(
113
113
  description="The dataset description in markdown", required=True
114
114
  ),
@@ -14,7 +14,7 @@ org_ref_fields = api.inherit(
14
14
  "name": fields.String(description="The organization name", readonly=True),
15
15
  "acronym": fields.String(description="The organization acronym"),
16
16
  "slug": fields.String(
17
- description="The organization string used as permalink", required=True
17
+ description="The organization string used as permalink", readonly=True
18
18
  ),
19
19
  "uri": fields.String(
20
20
  attribute=lambda o: o.self_api_url(),
@@ -122,12 +122,12 @@ member_fields = api.model(
122
122
  org_fields = api.model(
123
123
  "Organization",
124
124
  {
125
- "id": fields.String(description="The organization identifier", required=True),
125
+ "id": fields.String(description="The organization identifier", readonly=True),
126
126
  "name": fields.String(description="The organization name", required=True),
127
127
  "acronym": fields.String(description="The organization acronym"),
128
128
  "url": fields.String(description="The organization website URL"),
129
129
  "slug": fields.String(
130
- description="The organization string used as permalink", required=True
130
+ description="The organization string used as permalink", readonly=True
131
131
  ),
132
132
  "description": fields.Markdown(
133
133
  description="The organization description in Markdown", required=True
@@ -9,7 +9,7 @@ user_ref_fields = api.inherit(
9
9
  {
10
10
  "first_name": fields.String(description="The user first name", readonly=True),
11
11
  "last_name": fields.String(description="The user larst name", readonly=True),
12
- "slug": fields.String(description="The user permalink string", required=True),
12
+ "slug": fields.String(description="The user permalink string", readonly=True),
13
13
  "uri": fields.String(
14
14
  attribute=lambda u: u.self_api_url(),
15
15
  description="The API URI for this user",
@@ -35,8 +35,8 @@ from udata.core.organization.api_fields import member_email_with_visibility_chec
35
35
  user_fields = api.model(
36
36
  "User",
37
37
  {
38
- "id": fields.String(description="The user identifier", required=True),
39
- "slug": fields.String(description="The user permalink string", required=True),
38
+ "id": fields.String(description="The user identifier", readonly=True),
39
+ "slug": fields.String(description="The user permalink string", readonly=True),
40
40
  "first_name": fields.String(description="The user first name", required=True),
41
41
  "last_name": fields.String(description="The user last name", required=True),
42
42
  "email": fields.Raw(
udata/rdf.py CHANGED
@@ -356,7 +356,13 @@ def theme_labels_from_rdf(rdf):
356
356
 
357
357
 
358
358
  def themes_from_rdf(rdf):
359
- tags = [tag.toPython() for tag in rdf.objects(DCAT.keyword)]
359
+ tags = []
360
+ for tag in rdf.objects(DCAT.keyword):
361
+ if isinstance(tag, RdfResource):
362
+ # dcat:keyword should be Literal, not a Resource/URIRef
363
+ log.warning(f"Ignoring dcat:keyword with URI value: {tag.identifier}")
364
+ continue
365
+ tags.append(tag.toPython())
360
366
  tags += theme_labels_from_rdf(rdf)
361
367
  return list(set(tags))
362
368
 
@@ -1555,6 +1555,44 @@ class DatasetsFeedAPItest(APITestCase):
1555
1555
  entry = feed.entries[0]
1556
1556
  assert uris.validate(entry["id"])
1557
1557
 
1558
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=0)
1559
+ def test_recent_feed_with_organization_filter(self):
1560
+ org1 = OrganizationFactory()
1561
+ org2 = OrganizationFactory()
1562
+ DatasetFactory(title="Dataset Org1", organization=org1, resources=[ResourceFactory()])
1563
+ DatasetFactory(title="Dataset Org2", organization=org2, resources=[ResourceFactory()])
1564
+
1565
+ response = self.get(url_for("api.recent_datasets_atom_feed", organization=str(org1.id)))
1566
+ self.assert200(response)
1567
+
1568
+ feed = feedparser.parse(response.data)
1569
+ self.assertEqual(len(feed.entries), 1)
1570
+ self.assertEqual(feed.entries[0].title, "Dataset Org1")
1571
+
1572
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=0)
1573
+ def test_recent_feed_with_tag_filter(self):
1574
+ DatasetFactory(title="Tagged", tags=["transport"], resources=[ResourceFactory()])
1575
+ DatasetFactory(title="Not Tagged", tags=["other"], resources=[ResourceFactory()])
1576
+
1577
+ response = self.get(url_for("api.recent_datasets_atom_feed", tag="transport"))
1578
+ self.assert200(response)
1579
+
1580
+ feed = feedparser.parse(response.data)
1581
+ self.assertEqual(len(feed.entries), 1)
1582
+ self.assertEqual(feed.entries[0].title, "Tagged")
1583
+
1584
+ @pytest.mark.options(DELAY_BEFORE_APPEARING_IN_RSS_FEED=0)
1585
+ def test_recent_feed_with_search_query(self):
1586
+ DatasetFactory(title="Transport public", resources=[ResourceFactory()])
1587
+ DatasetFactory(title="Environnement", resources=[ResourceFactory()])
1588
+
1589
+ response = self.get(url_for("api.recent_datasets_atom_feed", q="transport"))
1590
+ self.assert200(response)
1591
+
1592
+ feed = feedparser.parse(response.data)
1593
+ self.assertEqual(len(feed.entries), 1)
1594
+ self.assertEqual(feed.entries[0].title, "Transport public")
1595
+
1558
1596
 
1559
1597
  class DatasetBadgeAPITest(APITestCase):
1560
1598
  @classmethod
@@ -755,6 +755,22 @@ class RdfToDatasetTest(PytestOnlyDBTestCase):
755
755
  assert isinstance(dataset, Dataset)
756
756
  assert set(dataset.tags) == set(tags + themes)
757
757
 
758
+ def test_keyword_as_uriref(self):
759
+ """Regression test: keywords can be URIRef instead of Literal in some DCAT feeds."""
760
+ node = BNode()
761
+ g = Graph()
762
+
763
+ g.add((node, RDF.type, DCAT.Dataset))
764
+ g.add((node, DCT.title, Literal(faker.sentence())))
765
+ g.add((node, DCAT.keyword, Literal("literal-tag")))
766
+ g.add((node, DCAT.keyword, URIRef("http://example.org/keyword/uriref-tag")))
767
+
768
+ dataset = dataset_from_rdf(g)
769
+ dataset.validate()
770
+
771
+ assert isinstance(dataset, Dataset)
772
+ assert "literal-tag" in dataset.tags
773
+
758
774
  def test_parse_null_frequency(self):
759
775
  assert frequency_from_rdf(None) is None
760
776
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: udata
3
- Version: 14.5.1.dev6
3
+ Version: 14.5.1.dev9
4
4
  Summary: Open data portal
5
5
  Author-email: Opendata Team <opendatateam@data.gouv.fr>
6
6
  Maintainer-email: Opendata Team <opendatateam@data.gouv.fr>
@@ -6,7 +6,7 @@ udata/errors.py,sha256=E8W7b4PH7c5B85g_nsUMt8fHqMVpDFOZFkO6wMPl6bA,117
6
6
  udata/factories.py,sha256=MoklZnU8iwNL25dm3JsoXhoQs1PQWSVYL1WvcUBtJqM,492
7
7
  udata/i18n.py,sha256=R7WdU0EZslj5xEHdqbyQ4iOORGjiqcVvQFCnmQEkpHA,2602
8
8
  udata/mail.py,sha256=-5OjGWqW7X5EuLLsJfhdqIrjlBGJ3T0V_wVLKc9OyPQ,3722
9
- udata/rdf.py,sha256=pUD7VbmhHo9cgmBbaYUd142nmB0R7VKsMXTkZWJ62jY,23825
9
+ udata/rdf.py,sha256=Utj7qNJJP5oaRRnTSXx3pjujrRETesP9F6cZPDLUrSs,24065
10
10
  udata/routing.py,sha256=3YQu9L81QYe4NxPSR_qyXe5WQLOgtm3sYKrkRxoxY-4,7741
11
11
  udata/sentry.py,sha256=j_6PSHV1id21KFX1XvpQR-Ur4d24310HgIq7MynEZ2Q,2887
12
12
  udata/settings.py,sha256=tk9VyAxg-umX3QW8hfFRsdA683IHxrEOBV-lJOWScGg,22490
@@ -97,9 +97,9 @@ udata/core/dataservices/tasks.py,sha256=DgOsTjQMx3poI0DdLPlTysRVNhf9dYNg-Zc_ut2S
97
97
  udata/core/dataset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
98
  udata/core/dataset/actions.py,sha256=mX6xox0PiMrbcAPZ3VZsI26rfM-ciYfEXxN6sqqImKA,1222
99
99
  udata/core/dataset/activities.py,sha256=eGxMUnC47YHxTgcls6igQ3qP7cYgwFtPfj0asCylGsI,3315
100
- udata/core/dataset/api.py,sha256=07uiI9sF3ebGjMyNMFmbu0r-_NPt0B1zk7g9rTePBwc,35987
101
- udata/core/dataset/api_fields.py,sha256=tmC9zpsMUc3ZNsrYYRBbioGNo12YmTZ0lT3i14n7B0w,18591
102
- udata/core/dataset/apiv2.py,sha256=6nI_R2Uv-PUl3fyI3EPsW8Ppk5GiXvKSdA3HYondiW0,21576
100
+ udata/core/dataset/api.py,sha256=-86CS5U8UZUe6qMAVkRYwDa04OxxVOwkxSWNiMsZVzo,36538
101
+ udata/core/dataset/api_fields.py,sha256=k_80CjLwQS4mdDQwwissxu0_NzAxKKFb3TCKQtLGSvc,18591
102
+ udata/core/dataset/apiv2.py,sha256=lpQtEXvIc1ZbYI4Yy7CCUqxDWqsoCNauHlQ4OtYAofU,21576
103
103
  udata/core/dataset/commands.py,sha256=3mKSdJ-M7ggdG29AVn77C4ouZanbYoqkTaGQoBKOp3s,3471
104
104
  udata/core/dataset/constants.py,sha256=cwJ5v4DbSIarlMIqmUFQSAEyeV6v86NL3a0-2j0yHlw,6984
105
105
  udata/core/dataset/csv.py,sha256=mNU5cuuNzrMgFF7Z0AKN8clsv458tpJHX1_pzAjpd-4,3653
@@ -153,7 +153,7 @@ udata/core/metrics/tasks.py,sha256=5hVSvBFF2-k_MZGn1XrzEWLgRp3TlrZJtPjX1Y_niS4,4
153
153
  udata/core/organization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
154
154
  udata/core/organization/activities.py,sha256=Mw4-R8Q6G745IZnCDgrj7h2ax2crGYRhZtcewSK6_Ok,1213
155
155
  udata/core/organization/api.py,sha256=RGyxe5LRqkyk5HzsXRB9r6z_YhGVqNeKkiaEgMaOvU8,22908
156
- udata/core/organization/api_fields.py,sha256=UlDYS62Wt1capSO0rYf6-m_rza3lCmpTuA93R-sV9w8,7776
156
+ udata/core/organization/api_fields.py,sha256=NXZc__i31ntun3Zt4OGt2EdpodbtQvHqzuta-TskNPY,7776
157
157
  udata/core/organization/apiv2.py,sha256=HVZmfO-Cw9hlMPcHKAC1HnSmPXeWpBz-hqWUDl2Bhs8,2983
158
158
  udata/core/organization/commands.py,sha256=DsRAtFDZvTciYNsUWumQWdn0jnNmKW-PwfIHUUZoBb8,1591
159
159
  udata/core/organization/constants.py,sha256=fncNtA-vFrRM22K1Wo6iYu9DQZjzknYxH6TIYfxM9kA,563
@@ -254,7 +254,7 @@ udata/core/topic/tasks.py,sha256=_OrPfuZ9rKENp4p4IkoyV-Yqp9bxI3nNxhpfKrbgl-w,357
254
254
  udata/core/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
255
255
  udata/core/user/activities.py,sha256=d91G45XEgoxYUJetkSjVolSM-nqmNSuM5b-6geayGE4,3338
256
256
  udata/core/user/api.py,sha256=xO6Bsc_5IHH8DV7y2gmT-sTWhBVvIeRJOvXXv4hAoQE,13903
257
- udata/core/user/api_fields.py,sha256=fx3rv8YmovIKJpZ8ldtcUOAWuJHEELDYa4HwdaQOAnA,5986
257
+ udata/core/user/api_fields.py,sha256=lzhB380tCesjesHkXWc9CwFq4E0ThJFbYwRAHUBtNKA,5986
258
258
  udata/core/user/apiv2.py,sha256=4eNsvJjb4ChJQFrXtVbkOtAvXEcQcQpZf8vkEbriXRA,1125
259
259
  udata/core/user/commands.py,sha256=8dyowvylgYqdScgzVGgr61MWLshlS6-UopZGAMvp4zA,3539
260
260
  udata/core/user/constants.py,sha256=aTluhTR2RGZ_cdG7-mkEoT5Ndbg8BNUwwzCOld0aLMY,77
@@ -475,7 +475,7 @@ udata/tests/api/test_auth_api.py,sha256=vLyDU1hdF2QEwpNTNUgP2QPUjL0y61lzj5Viaaj9
475
475
  udata/tests/api/test_base_api.py,sha256=jbT9s_YYuh-37xR98VMS5mAHHIL0g-6F_kkhFBb4E3A,2272
476
476
  udata/tests/api/test_contact_points.py,sha256=QmnTmJZ5oIohsUH_E_wfFalRx-Fx91jO7gs7PErpLqQ,8690
477
477
  udata/tests/api/test_dataservices_api.py,sha256=UPMH6OEEczYvebyV7thf7LjrEYFctgIm1TuJV1VC-OI,29914
478
- udata/tests/api/test_datasets_api.py,sha256=ue8ncXBfOf3XR1ZK3YwtoWqONSk2dsAzrRIWFXIj9kM,111967
478
+ udata/tests/api/test_datasets_api.py,sha256=twJki_rjq8SHteOVqPsFc58pKuzyhlo2a9Lr4qTOCeI,113761
479
479
  udata/tests/api/test_fields.py,sha256=OW85Z5MES5HeWOpapeem8OvR1cIcrqW-xMWpdZO4LZ8,1033
480
480
  udata/tests/api/test_follow_api.py,sha256=4nFXG5pZ_Hf2PJ4KEdHJX_uggjc9RpB8v0fidkAcw9I,5792
481
481
  udata/tests/api/test_me_api.py,sha256=MiKN9P75h2HyMxepuxt2SiLs3Yhgl22iQXKbvTR42sg,13796
@@ -507,7 +507,7 @@ udata/tests/dataset/test_dataset_actions.py,sha256=8d-6AUKCt3Nnb_uEaztV0BzNYFDyq
507
507
  udata/tests/dataset/test_dataset_commands.py,sha256=e-tLIveg_Vp7nfOd5VhcPSiUQZ-VBv2no2lAu-2j6BE,820
508
508
  udata/tests/dataset/test_dataset_events.py,sha256=tKCQ55y_pc0wOKv2B2iej5dDxWalguU9FLtr6J2y8hE,3682
509
509
  udata/tests/dataset/test_dataset_model.py,sha256=YWhQ6RxVdgUrQbtvwATzypyl8XyNpdDjHDetyK37chU,35795
510
- udata/tests/dataset/test_dataset_rdf.py,sha256=_7QDypmPQvL4DzrOgsVfkbL-hW5RrIftctIz3O0wMEw,58199
510
+ udata/tests/dataset/test_dataset_rdf.py,sha256=RVIib5ah3B_wpTj9swbx4XSzUGyYj9_6kP_n416bnSQ,58782
511
511
  udata/tests/dataset/test_dataset_recommendations.py,sha256=UMwAsLHs6_XA1vp7-lnTjaPOc9E6zQYqw9VIuSCNUtk,7102
512
512
  udata/tests/dataset/test_dataset_tasks.py,sha256=6YmDBJUv3pIPNFj6DvY7FLsa4k7XxGeQ7BgOLrJOeTY,4564
513
513
  udata/tests/dataset/test_resource_preview.py,sha256=387FEdNHWZyEeOlmSETMIFlnhVuKQ-U4o2RkWgxXwik,2208
@@ -580,9 +580,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=n7ZHvruSL9hIPoSl4aCkGeC52LZYeg
580
580
  udata/translations/pt/LC_MESSAGES/udata.po,sha256=24CsHDZ84nqTMr-cOvOZ-LNYsokLQNyQchI41o3Cq9M,49765
581
581
  udata/translations/sr/LC_MESSAGES/udata.mo,sha256=pw3gsvr8lPQJZvX9Jo8ymu59I3L6-rrpX2Fqy0Nu5r4,20441
582
582
  udata/translations/sr/LC_MESSAGES/udata.po,sha256=1h8akWRpcQ1uD5zezqjp-Q-gAld5_93MkJL4BRlqKjQ,54738
583
- udata-14.5.1.dev6.dist-info/licenses/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
584
- udata-14.5.1.dev6.dist-info/METADATA,sha256=qbQHMoJGGldXV--7F7oWanV4xdfGPPbMr49r5vGsnLs,4358
585
- udata-14.5.1.dev6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
586
- udata-14.5.1.dev6.dist-info/entry_points.txt,sha256=XwrEzP-n_6CKnwTsrNHzyCTWbMwg2FkvxVVB686f_C0,476
587
- udata-14.5.1.dev6.dist-info/top_level.txt,sha256=EF6CE6YSHd_og-8LCEA4q25ALUpWVe8D0okOLdMAE3A,6
588
- udata-14.5.1.dev6.dist-info/RECORD,,
583
+ udata-14.5.1.dev9.dist-info/licenses/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
584
+ udata-14.5.1.dev9.dist-info/METADATA,sha256=HzZLmJ05MmH_cM_Dfph767hBvXEDJ7l-PC0lZ1mZ4bA,4358
585
+ udata-14.5.1.dev9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
586
+ udata-14.5.1.dev9.dist-info/entry_points.txt,sha256=XwrEzP-n_6CKnwTsrNHzyCTWbMwg2FkvxVVB686f_C0,476
587
+ udata-14.5.1.dev9.dist-info/top_level.txt,sha256=EF6CE6YSHd_og-8LCEA4q25ALUpWVe8D0okOLdMAE3A,6
588
+ udata-14.5.1.dev9.dist-info/RECORD,,