udata 9.2.2.dev31578__py2.py3-none-any.whl → 9.2.2.dev31630__py2.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/csv.py CHANGED
@@ -1,4 +1,5 @@
1
- from udata.core.discussions.models import Discussion
1
+ # for backwards compatibility (see https://github.com/opendatateam/udata/pull/3152)
2
+ from udata.core.discussions.csv import DiscussionCsvAdapter # noqa: F401
2
3
  from udata.frontend import csv
3
4
 
4
5
  from .models import Dataset, Resource
@@ -90,18 +91,3 @@ class ResourcesCsvAdapter(csv.NestedAdapter):
90
91
  ("preview_url", lambda o: o.preview_url or False),
91
92
  )
92
93
  attribute = "resources"
93
-
94
-
95
- @csv.adapter(Discussion)
96
- class DiscussionCsvAdapter(csv.Adapter):
97
- fields = (
98
- "id",
99
- "user",
100
- "subject",
101
- "title",
102
- ("size", lambda o: len(o.discussion)),
103
- ("messages", lambda o: "\n".join(msg.content for msg in o.discussion)),
104
- "created",
105
- "closed",
106
- "closed_by",
107
- )
@@ -6,6 +6,10 @@ from flask_security import current_user
6
6
 
7
7
  from udata.api import API, api, fields
8
8
  from udata.auth import admin_permission
9
+ from udata.core.dataservices.models import Dataservice
10
+ from udata.core.dataset.models import Dataset
11
+ from udata.core.organization.models import Organization
12
+ from udata.core.reuse.models import Reuse
9
13
  from udata.core.spam.api import SpamAPIMixin
10
14
  from udata.core.spam.fields import spam_fields
11
15
  from udata.core.user.api_fields import user_ref_fields
@@ -73,8 +77,15 @@ comment_discussion_fields = api.model(
73
77
  discussion_page_fields = api.model("DiscussionPage", fields.pager(discussion_fields))
74
78
 
75
79
  parser = api.parser()
80
+ sorting_keys: list[str] = ["created", "title", "closed"]
81
+ sorting_choices: list[str] = sorting_keys + ["-" + k for k in sorting_keys]
76
82
  parser.add_argument(
77
- "sort", type=str, default="-created", location="args", help="The sorting attribute"
83
+ "sort",
84
+ type=str,
85
+ default="-created",
86
+ choices=sorting_choices,
87
+ location="args",
88
+ help="The field (and direction) on which sorting apply",
78
89
  )
79
90
  parser.add_argument(
80
91
  "closed",
@@ -85,6 +96,9 @@ parser.add_argument(
85
96
  parser.add_argument(
86
97
  "for", type=str, location="args", action="append", help="Filter discussions for a given subject"
87
98
  )
99
+ parser.add_argument(
100
+ "org", type=str, location="args", help="Filter discussions for a given organization"
101
+ )
88
102
  parser.add_argument("user", type=str, location="args", help="Filter discussions created by a user")
89
103
  parser.add_argument("page", type=int, default=1, location="args", help="The page to fetch")
90
104
  parser.add_argument(
@@ -198,6 +212,15 @@ class DiscussionsAPI(API):
198
212
  discussions = Discussion.objects
199
213
  if args["for"]:
200
214
  discussions = discussions.generic_in(subject=args["for"])
215
+ if args["org"]:
216
+ org = Organization.objects.get_or_404(id=id_or_404(args["org"]))
217
+ if not org:
218
+ api.abort(404, "Organization does not exist")
219
+ reuses = Reuse.objects(organization=org).only("id")
220
+ datasets = Dataset.objects(organization=org).only("id")
221
+ dataservices = Dataservice.objects(organization=org).only("id")
222
+ subjects = list(reuses) + list(datasets) + list(dataservices)
223
+ discussions = discussions(subject__in=subjects)
201
224
  if args["user"]:
202
225
  discussions = discussions(discussion__posted_by=ObjectId(args["user"]))
203
226
  if args["closed"] is False:
@@ -0,0 +1,22 @@
1
+ from udata.frontend import csv
2
+
3
+ from .models import Discussion
4
+
5
+
6
+ @csv.adapter(Discussion)
7
+ class DiscussionCsvAdapter(csv.Adapter):
8
+ fields = (
9
+ "id",
10
+ "user",
11
+ "subject",
12
+ ("subject_class", "subject._class_name"),
13
+ ("subject_id", "subject.id"),
14
+ "title",
15
+ ("size", lambda o: len(o.discussion)),
16
+ ("participants", lambda o: ",".join(set(str(msg.posted_by.id) for msg in o.discussion))),
17
+ ("messages", lambda o: "\n".join(msg.content.replace("\n", " ") for msg in o.discussion)),
18
+ "created",
19
+ "closed",
20
+ ("closed_by_fullname", "closed_by"),
21
+ ("closed_by_id", "closed_by.id"),
22
+ )
@@ -2,8 +2,11 @@ from datetime import datetime
2
2
 
3
3
  import pytest
4
4
  from flask import url_for
5
+ from werkzeug.test import TestResponse
5
6
 
7
+ from udata.core.dataservices.factories import DataserviceFactory
6
8
  from udata.core.dataset.factories import DatasetFactory
9
+ from udata.core.discussions.factories import DiscussionFactory
7
10
  from udata.core.discussions.metrics import update_discussions_metric # noqa
8
11
  from udata.core.discussions.models import Discussion, Message
9
12
  from udata.core.discussions.notifications import discussions_notifications
@@ -19,8 +22,11 @@ from udata.core.discussions.tasks import (
19
22
  notify_new_discussion_comment,
20
23
  )
21
24
  from udata.core.organization.factories import OrganizationFactory
25
+ from udata.core.organization.models import Organization
26
+ from udata.core.reuse.factories import ReuseFactory
22
27
  from udata.core.spam.signals import on_new_potential_spam
23
28
  from udata.core.user.factories import AdminFactory, UserFactory
29
+ from udata.core.user.models import User
24
30
  from udata.models import Dataset, Member
25
31
  from udata.tests.helpers import capture_mails
26
32
  from udata.utils import faker
@@ -358,6 +364,61 @@ class DiscussionsTest(APITestCase):
358
364
 
359
365
  self.assertEqual(len(response.json["data"]), len(discussions))
360
366
 
367
+ def assertIdIn(self, json_data: dict, id_: str) -> None:
368
+ for item in json_data:
369
+ if item["id"] == id_:
370
+ return
371
+ self.fail(f"id {id_} not in {json_data}")
372
+
373
+ def test_list_discussions_org_does_not_exist(self) -> None:
374
+ response: TestResponse = self.get(url_for("api.discussions", org="bad org id"))
375
+ self.assert404(response)
376
+
377
+ def test_list_discussions_org(self) -> None:
378
+ organization: Organization = OrganizationFactory()
379
+ user: User = UserFactory()
380
+ _discussion: Discussion = DiscussionFactory(user=user)
381
+ dataset = DatasetFactory(organization=organization)
382
+ dataservice = DataserviceFactory(organization=organization)
383
+ reuse = ReuseFactory(organization=organization)
384
+ discussion_for_dataset: Discussion = DiscussionFactory(subject=dataset, user=user)
385
+ discussion_for_dataservice: Discussion = DiscussionFactory(subject=dataservice, user=user)
386
+ discussion_for_reuse: Discussion = DiscussionFactory(subject=reuse, user=user)
387
+
388
+ response: TestResponse = self.get(url_for("api.discussions", org=organization.id))
389
+ self.assert200(response)
390
+ self.assertEqual(len(response.json["data"]), 3)
391
+ self.assertIdIn(response.json["data"], str(discussion_for_dataset.id))
392
+ self.assertIdIn(response.json["data"], str(discussion_for_dataservice.id))
393
+ self.assertIdIn(response.json["data"], str(discussion_for_reuse.id))
394
+
395
+ def test_list_discussions_sort(self) -> None:
396
+ user: User = UserFactory()
397
+ sorting_keys_dict: dict = {
398
+ "title": ["aaa", "bbb"],
399
+ "created": ["2023-12-12", "2024-01-01"],
400
+ "closed": ["2023-12-12", "2024-01-01"],
401
+ }
402
+ for sorting_key, values in sorting_keys_dict.items():
403
+ discussion1: Discussion = DiscussionFactory(user=user, **{sorting_key: values[0]})
404
+ discussion2: Discussion = DiscussionFactory(user=user, **{sorting_key: values[1]})
405
+
406
+ response: TestResponse = self.get(url_for("api.discussions", sort=sorting_key))
407
+ self.assert200(response)
408
+ self.assertEqual(len(response.json["data"]), 2)
409
+ self.assertEqual(response.json["data"][0]["id"], str(discussion1.id))
410
+ self.assertEqual(response.json["data"][1]["id"], str(discussion2.id))
411
+
412
+ # Reverse sort
413
+ response: TestResponse = self.get(url_for("api.discussions", sort="-" + sorting_key))
414
+ self.assert200(response)
415
+ self.assertEqual(len(response.json["data"]), 2)
416
+ self.assertEqual(response.json["data"][0]["id"], str(discussion2.id))
417
+ self.assertEqual(response.json["data"][1]["id"], str(discussion1.id))
418
+
419
+ # Clean slate
420
+ Discussion.objects.delete()
421
+
361
422
  def test_list_discussions_user(self):
362
423
  dataset = DatasetFactory()
363
424
  discussions = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: udata
3
- Version: 9.2.2.dev31578
3
+ Version: 9.2.2.dev31630
4
4
  Summary: Open data portal
5
5
  Home-page: https://github.com/opendatateam/udata
6
6
  Author: Opendata Team
@@ -140,7 +140,8 @@ It is collectively taken care of by members of the
140
140
 
141
141
  ## Current (in progress)
142
142
 
143
- - Nothing yet
143
+ - Add a filter on organization and document sort parameters in the `/discussions` endpoint [#3147](https://github.com/opendatateam/udata/pull/3147)
144
+ - Move discussion catalog creation and add fields [#3152](https://github.com/opendatateam/udata/pull/3152)
144
145
 
145
146
  ## 9.2.1 (2024-09-23)
146
147
 
@@ -94,7 +94,7 @@ udata/core/dataset/api_fields.py,sha256=ZF24FhKYe5jlV8jXG6YR0Hko9WOuV0446FAlLkEg
94
94
  udata/core/dataset/apiv2.py,sha256=VTE4eYx5udzOdHDUkD7TYpuCtppZMKp1okTSEE1fcgI,16925
95
95
  udata/core/dataset/commands.py,sha256=__hPAk_6iHtgMnEG51ux0vbNWJHxUjXhi1ukH4hF5jY,3714
96
96
  udata/core/dataset/constants.py,sha256=pkOvrdNBq3k1ojJcv6oSg7kK1IUtb3PqLni-YJ3rKSY,2880
97
- udata/core/dataset/csv.py,sha256=XMl8MaYCEyGmkCZqRSWrGRxCht2C9e3aQYGwDQLXYT0,3618
97
+ udata/core/dataset/csv.py,sha256=8vCJKCnmIun2_N5x4bvCYCk-jVXzHxhRHYrToiCObpA,3383
98
98
  udata/core/dataset/events.py,sha256=LEL9_iMTZ0bZlli2TdkQtV8pCRbFQBd91Hqh_WCW84c,3444
99
99
  udata/core/dataset/exceptions.py,sha256=uKiayLSpSzsnLvClObS6hOO0qXEqvURKN7_w8eimQNU,498
100
100
  udata/core/dataset/factories.py,sha256=fRDWDlybR_ud4pDs1-ntWuYHKtV9LMHeBOBp2SmTT6M,9006
@@ -108,8 +108,9 @@ udata/core/dataset/signals.py,sha256=WN4sV-lJlNsRkhcnhoy0SYJvCoYmK_5QFYZd1u-h4gs
108
108
  udata/core/dataset/tasks.py,sha256=yDPK2oKSzTXfST8Up7vMd13XPEK5r6iapq7hUIow6BI,8493
109
109
  udata/core/discussions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
110
  udata/core/discussions/actions.py,sha256=seKxsKut6xLkobfC2Yy2wpD_-flsT8HcFPcN6SDpz6k,789
111
- udata/core/discussions/api.py,sha256=Fjt6F04jRHwYrtMYcokjfDxBQqRJWgyh1H5jrDyLtvA,8376
111
+ udata/core/discussions/api.py,sha256=YsVz7JB7RazAeiydxGe1q1t8sIpxCM_w1b71oT9oN_8,9422
112
112
  udata/core/discussions/constants.py,sha256=nbZgXESpg0TykIGPxW8xUtHtk7TwQoyOL0Ky4U4B7c8,27
113
+ udata/core/discussions/csv.py,sha256=jYD_313Jos1YGwlNzai-pg79HPhLfxiWlIFV4TU7eI4,696
113
114
  udata/core/discussions/factories.py,sha256=CgQaUmmyDu90XtyBsqXVa-TmZMrN7Dwuu1Cnr5wNrVc,350
114
115
  udata/core/discussions/forms.py,sha256=CalmOTslnfBY-SJatvqN2UjFJ-1S1rF57sbW8UbvmPE,838
115
116
  udata/core/discussions/metrics.py,sha256=WMalLO9GEHXlqE2WbZX-HQUpvte6cRwuHuoL96QwWSg,354
@@ -580,7 +581,7 @@ udata/tests/plugin.py,sha256=dT3KfQiVYwLM61TbB894wzTelnlPIsCHaiPEH-Jirfk,11580
580
581
  udata/tests/schemas.json,sha256=szM1jDpkogfOG4xWbjIGjLgG8l9-ZyE3JKQtecJyD1E,4990
581
582
  udata/tests/test_activity.py,sha256=x-pDK6VW9wAG0uxYRZQ3DWTRjfCU729iaMGMJb1rWYU,3195
582
583
  udata/tests/test_cors.py,sha256=b_pyxKeIyqhnsXxXryPf4d0V0QxaLQ1P_VjY89Q_j3g,3233
583
- udata/tests/test_discussions.py,sha256=UN2mlZp-h3m3HqdF_nz9WOxwPKgXok47E2Rrp2dknYI,30464
584
+ udata/tests/test_discussions.py,sha256=Qo7nhIUY7YAATZfWl5Mgo0XhngGwFULgqEoMhsHZtFE,33587
584
585
  udata/tests/test_i18n.py,sha256=u60344JNRG_8s0t89ghXtQ1FbF4TayEHBzuBFxqnQ_Y,3152
585
586
  udata/tests/test_linkchecker.py,sha256=W8jrwKYXM8wWXZFjiaBwpWGRBhZ8bsSHGHzL9voDN7U,10218
586
587
  udata/tests/test_mail.py,sha256=ijcrniawbvftm5UF8KDApBoxMHyZwULAA25LrnLmeI8,1629
@@ -697,9 +698,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=WpPzAqVd2Onv_kz45ULUySKPLrpjcc
697
698
  udata/translations/pt/LC_MESSAGES/udata.po,sha256=18Op9RUITewoDRewlOdYzzq6gjsf1lsvepACV1d7zxs,44976
698
699
  udata/translations/sr/LC_MESSAGES/udata.mo,sha256=NIYRNhVoETZUvIvWm3cCW7DtMBAnS2vXzZjMF5ZzD_c,28500
699
700
  udata/translations/sr/LC_MESSAGES/udata.po,sha256=rQB-4V4WJ7bURj6g2j653vItr5TMHadcLQxec7_fDmg,51545
700
- udata-9.2.2.dev31578.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
701
- udata-9.2.2.dev31578.dist-info/METADATA,sha256=A2qsgIGm1QV5SmTZqksgcyjwSYuG5jEt6DwbmfD_0i8,130894
702
- udata-9.2.2.dev31578.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
703
- udata-9.2.2.dev31578.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
704
- udata-9.2.2.dev31578.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
705
- udata-9.2.2.dev31578.dist-info/RECORD,,
701
+ udata-9.2.2.dev31630.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
702
+ udata-9.2.2.dev31630.dist-info/METADATA,sha256=VMRLK7oaR65QgEmsJoIEIx-xJqy0Itm71wlAE9_RuHo,131135
703
+ udata-9.2.2.dev31630.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
704
+ udata-9.2.2.dev31630.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
705
+ udata-9.2.2.dev31630.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
706
+ udata-9.2.2.dev31630.dist-info/RECORD,,