udata 9.0.1.dev29716__py2.py3-none-any.whl → 9.0.1.dev29728__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.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

udata/api/__init__.py CHANGED
@@ -334,6 +334,7 @@ def init_app(app):
334
334
  import udata.core.organization.apiv2 # noqa
335
335
  import udata.core.followers.api # noqa
336
336
  import udata.core.jobs.api # noqa
337
+ import udata.core.reports.api # noqa
337
338
  import udata.core.site.api # noqa
338
339
  import udata.core.tags.api # noqa
339
340
  import udata.core.topic.api # noqa
udata/api_fields.py CHANGED
@@ -38,6 +38,9 @@ def convert_db_to_field(key, field, info = {}):
38
38
  constructor = restx_fields.String
39
39
  params['min_length'] = field.min_length
40
40
  params['max_length'] = field.max_length
41
+ params['enum'] = field.choices
42
+ elif isinstance(field, mongo_fields.ObjectIdField):
43
+ constructor = restx_fields.String
41
44
  elif isinstance(field, mongo_fields.FloatField):
42
45
  constructor = restx_fields.Float
43
46
  params['min'] = field.min # TODO min_value?
File without changes
@@ -0,0 +1,43 @@
1
+ from flask import request
2
+ from flask_login import current_user
3
+ import mongoengine
4
+
5
+ from udata.api import api, API, fields
6
+ from udata.api_fields import patch
7
+ from .models import Report
8
+ from .constants import reports_reasons_translations
9
+
10
+ ns = api.namespace('reports', 'User reported objects related operations (beta)')
11
+
12
+ @ns.route('/', endpoint='reports')
13
+ class ReportsAPI(API):
14
+ @api.doc('list_reports')
15
+ @api.expect(Report.__index_parser__)
16
+ @api.marshal_with(Report.__page_fields__)
17
+ def get(self):
18
+ query = Report.objects
19
+
20
+ return Report.apply_sort_filters_and_pagination(query)
21
+
22
+ @api.secure
23
+ @api.doc('create_report', responses={400: 'Validation error'})
24
+ @api.expect(Report.__write_fields__)
25
+ @api.marshal_with(Report.__read_fields__, code=201)
26
+ def post(self):
27
+ report = patch(Report(), request)
28
+ report.by = current_user._get_current_object()
29
+
30
+ try:
31
+ report.save()
32
+ except mongoengine.errors.ValidationError as e:
33
+ api.abort(400, e.message)
34
+
35
+ return report, 201
36
+
37
+
38
+ @ns.route('/reasons/', endpoint='reports_reasons')
39
+ class ReportsReasonsAPI(API):
40
+ @api.doc('list_reports_reasons')
41
+ @ns.response(200, "dictionnary of available reasons associated with their labels", fields.Raw)
42
+ def get(self):
43
+ return reports_reasons_translations()
@@ -0,0 +1,28 @@
1
+
2
+ from udata.core.dataset.models import Dataset
3
+ from udata.i18n import lazy_gettext as _
4
+
5
+
6
+ REASON_SPAM = 'spam'
7
+ REASON_PERSONAL_DATA = 'personal_data'
8
+ REASON_EXPLICIT_CONTENT = 'explicit_content'
9
+ REASON_ILLEGAL_CONTENT = 'illegal_content'
10
+ REASON_SECURITY = 'security'
11
+ REASON_OTHERS = 'others'
12
+
13
+ def reports_reasons_translations():
14
+ '''
15
+ This is a function to avoid creating the dict with a wrong lang
16
+ at the start of the app.
17
+ '''
18
+ return {
19
+ REASON_SPAM: _('Spam'),
20
+ REASON_PERSONAL_DATA: _('Personal data'),
21
+ REASON_EXPLICIT_CONTENT: _('Explicit content'),
22
+ REASON_ILLEGAL_CONTENT: _('Illegal content'),
23
+ REASON_SECURITY: _('Security'),
24
+ REASON_OTHERS: _('Others'),
25
+ }
26
+
27
+ REPORT_REASONS_CHOICES = [key for key, _ in reports_reasons_translations().items()]
28
+ REPORTABLE_MODELS = [Dataset]
@@ -0,0 +1,57 @@
1
+ from datetime import datetime
2
+ from mongoengine import signals, NULLIFY
3
+
4
+ from udata.api_fields import field, generate_fields
5
+ from udata.core.dataset.models import Dataset
6
+ from udata.core.user.models import User
7
+ from udata.mongo import db
8
+ from udata.core.user.api_fields import user_ref_fields
9
+
10
+ from .constants import REPORT_REASONS_CHOICES, REPORTABLE_MODELS
11
+
12
+ @generate_fields()
13
+ class Report(db.Document):
14
+ by = field(
15
+ db.ReferenceField(User, reverse_delete_rule=NULLIFY),
16
+ nested_fields=user_ref_fields,
17
+ description="Only set if a user was connected when reporting an element.",
18
+ readonly=True,
19
+ allow_null=True,
20
+ )
21
+
22
+ object_type = field(
23
+ db.StringField(choices=[m.__name__ for m in REPORTABLE_MODELS])
24
+ )
25
+ object_id = field(
26
+ db.ObjectIdField()
27
+ )
28
+ object_deleted_at = field(
29
+ db.DateTimeField(),
30
+ allow_null=True,
31
+ readonly=True,
32
+ )
33
+
34
+ reason = field(
35
+ db.StringField(choices=REPORT_REASONS_CHOICES, required=True),
36
+ )
37
+ message = field(
38
+ db.StringField(),
39
+ )
40
+
41
+ reported_at = field(
42
+ db.DateTimeField(default=datetime.utcnow, required=True),
43
+ readonly=True,
44
+ )
45
+
46
+ @classmethod
47
+ def mark_as_deleted_soft_delete(cls, sender, document, **kwargs):
48
+ if document.deleted:
49
+ Report.objects(object_type=sender.__name__, object_id=document.id, object_deleted_at=None).update(object_deleted_at=datetime.utcnow)
50
+
51
+ def mark_as_deleted_hard_delete(cls, document, **kwargs):
52
+ Report.objects(object_type=document.__class__.__name__, object_id=document.id, object_deleted_at=None).update(object_deleted_at=datetime.utcnow)
53
+
54
+
55
+ for model in REPORTABLE_MODELS:
56
+ signals.post_save.connect(Report.mark_as_deleted_soft_delete, sender=model)
57
+ signals.post_delete.connect(Report.mark_as_deleted_hard_delete, sender=model)
udata/models/__init__.py CHANGED
@@ -21,6 +21,7 @@ from udata.core.post.models import * # noqa
21
21
  from udata.core.jobs.models import * # noqa
22
22
  from udata.core.tags.models import * # noqa
23
23
  from udata.core.spam.models import * # noqa
24
+ from udata.core.reports.models import * # noqa
24
25
 
25
26
  from udata.features.transfer.models import * # noqa
26
27
  from udata.features.territories.models import * # noqa
@@ -0,0 +1,88 @@
1
+ from typing import List
2
+ from flask import url_for
3
+
4
+ from udata.core.dataset.factories import (DatasetFactory)
5
+ from udata.core.dataset.models import Dataset
6
+ from udata.core.reports.models import Report
7
+ from udata.core.reports.constants import REASON_ILLEGAL_CONTENT, REASON_SPAM, reports_reasons_translations
8
+ from udata.i18n import gettext as _
9
+
10
+ from . import APITestCase
11
+
12
+
13
+ class ReportsReasonsAPITest(APITestCase):
14
+ modules = []
15
+
16
+ def test_reports_reasons_api(self):
17
+ response = self.get(url_for('api.reports_reasons'))
18
+ self.assert200(response)
19
+ self.assertEqual(response.json, reports_reasons_translations())
20
+
21
+ class ReportsAPITest(APITestCase):
22
+ modules = []
23
+
24
+ def test_reports_api_create(self):
25
+ user = self.login()
26
+ illegal_dataset = DatasetFactory.create(owner=user)
27
+ spam_dataset = DatasetFactory.create(owner=user)
28
+
29
+ response = self.post(url_for('api.reports'), {
30
+ 'object_type': 'Dataset',
31
+ 'object_id': illegal_dataset.id,
32
+ 'message': 'This is not appropriate',
33
+ 'reason': REASON_ILLEGAL_CONTENT,
34
+ })
35
+ self.assert201(response)
36
+ self.assertEqual(Report.objects.count(), 1)
37
+
38
+ response = self.post(url_for('api.reports'), {
39
+ 'object_type': 'Dataset',
40
+ 'object_id': spam_dataset.id,
41
+ 'message': 'This is spammy',
42
+ 'reason': REASON_SPAM,
43
+ })
44
+ self.assert201(response)
45
+ self.assertEqual(Report.objects.count(), 2)
46
+
47
+ reports: List[Report] = list(Report.objects())
48
+ self.assertEqual(Dataset.__name__, reports[0].object_type)
49
+ self.assertEqual(illegal_dataset.id, reports[0].object_id)
50
+ self.assertEqual('This is not appropriate', reports[0].message)
51
+ self.assertEqual(REASON_ILLEGAL_CONTENT, reports[0].reason)
52
+ self.assertEqual(user.id, reports[0].by.id)
53
+
54
+ self.assertEqual(Dataset.__name__, reports[1].object_type)
55
+ self.assertEqual(spam_dataset.id, reports[1].object_id)
56
+ self.assertEqual('This is spammy', reports[1].message)
57
+ self.assertEqual(REASON_SPAM, reports[1].reason)
58
+ self.assertEqual(user.id, reports[1].by.id)
59
+
60
+ response = self.delete(url_for('api.dataset', dataset=illegal_dataset))
61
+ self.assert204(response)
62
+
63
+ reports[0].reload()
64
+ self.assertEqual(Dataset.__name__, reports[0].object_type)
65
+ self.assertEqual(illegal_dataset.id, reports[0].object_id)
66
+ self.assertEqual('This is not appropriate', reports[0].message)
67
+ self.assertEqual(REASON_ILLEGAL_CONTENT, reports[0].reason)
68
+ self.assertEqual(user.id, reports[0].by.id)
69
+ self.assertIsNotNone(reports[0].object_deleted_at)
70
+
71
+ reports[1].reload()
72
+ self.assertEqual(Dataset.__name__, reports[1].object_type)
73
+ self.assertEqual(spam_dataset.id, reports[1].object_id)
74
+ self.assertEqual('This is spammy', reports[1].message)
75
+ self.assertEqual(REASON_SPAM, reports[1].reason)
76
+ self.assertEqual(user.id, reports[1].by.id)
77
+ self.assertIsNone(reports[1].object_deleted_at)
78
+
79
+ # We should take action on manual delete in the database too
80
+ spam_dataset.delete()
81
+
82
+ reports[1].reload()
83
+ self.assertIsNotNone(reports[1].object_deleted_at)
84
+
85
+ response = self.get(url_for('api.reports'))
86
+ self.assert200(response)
87
+ self.assertEqual(len(response.json['data']), 2)
88
+
@@ -8,8 +8,8 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: udata 9.0.1.dev0\n"
10
10
  "Report-Msgid-Bugs-To: i18n@opendata.team\n"
11
- "POT-Creation-Date: 2024-06-14 11:31+0200\n"
12
- "PO-Revision-Date: 2024-06-14 11:31+0200\n"
11
+ "POT-Creation-Date: 2024-07-01 13:45+0200\n"
12
+ "PO-Revision-Date: 2024-07-01 13:45+0200\n"
13
13
  "Last-Translator: Open Data Team <i18n@opendata.team>\n"
14
14
  "Language: en\n"
15
15
  "Language-Team: Open Data Team <i18n@opendata.team>\n"
@@ -43,7 +43,7 @@ msgstr ""
43
43
  msgid "Password reset instructions"
44
44
  msgstr ""
45
45
 
46
- #: udata/settings.py:433
46
+ #: udata/settings.py:435
47
47
  msgid "This dataset has been archived"
48
48
  msgstr ""
49
49
 
@@ -51,7 +51,7 @@ msgstr ""
51
51
  msgid "Invalid URL \"{url}\": {reason}"
52
52
  msgstr ""
53
53
 
54
- #: udata/uris.py:54
54
+ #: udata/tests/test_model.py:370 udata/uris.py:54
55
55
  msgid "Invalid URL \"{url}\""
56
56
  msgstr ""
57
57
 
@@ -556,7 +556,7 @@ msgid ""
556
556
  "Allowed versions: {values}"
557
557
  msgstr ""
558
558
 
559
- #: udata/core/dataset/models.py:401 udata/core/dataset/rdf.py:430
559
+ #: udata/core/dataset/models.py:401 udata/core/dataset/rdf.py:436
560
560
  #: udata/tests/dataset/test_dataset_rdf.py:517
561
561
  #: udata/tests/dataset/test_dataset_rdf.py:530
562
562
  msgid "Nameless resource"
@@ -574,7 +574,7 @@ msgstr ""
574
574
  msgid "dataset"
575
575
  msgstr ""
576
576
 
577
- #: udata/core/dataset/rdf.py:428 udata/tests/dataset/test_dataset_rdf.py:504
577
+ #: udata/core/dataset/rdf.py:434 udata/tests/dataset/test_dataset_rdf.py:504
578
578
  msgid "{format} resource"
579
579
  msgstr ""
580
580
 
@@ -806,6 +806,30 @@ msgstr ""
806
806
  msgid "post"
807
807
  msgstr ""
808
808
 
809
+ #: udata/core/reports/constants.py:19
810
+ msgid "Spam"
811
+ msgstr ""
812
+
813
+ #: udata/core/reports/constants.py:20
814
+ msgid "Personal data"
815
+ msgstr ""
816
+
817
+ #: udata/core/reports/constants.py:21
818
+ msgid "Explicit content"
819
+ msgstr ""
820
+
821
+ #: udata/core/reports/constants.py:22
822
+ msgid "Illegal content"
823
+ msgstr ""
824
+
825
+ #: udata/core/reports/constants.py:23
826
+ msgid "Security"
827
+ msgstr ""
828
+
829
+ #: udata/core/reports/constants.py:24 udata/core/reuse/constants.py:29
830
+ msgid "Others"
831
+ msgstr ""
832
+
809
833
  #: udata/core/reuse/activities.py:22
810
834
  msgid "created a reuse"
811
835
  msgstr ""
@@ -902,10 +926,6 @@ msgstr ""
902
926
  msgid "Open data tools"
903
927
  msgstr ""
904
928
 
905
- #: udata/core/reuse/constants.py:29
906
- msgid "Others"
907
- msgstr ""
908
-
909
929
  #: udata/core/reuse/forms.py:14
910
930
  msgid "This URL is already registered"
911
931
  msgstr ""
@@ -1596,7 +1616,7 @@ msgid "Here"
1596
1616
  msgstr ""
1597
1617
 
1598
1618
  #: udata/tests/api/test_datasets_api.py:764
1599
- msgid "Invalid URL"
1619
+ msgid "Invalid URL \"test\""
1600
1620
  msgstr ""
1601
1621
 
1602
1622
  #: udata/tests/forms/test_model_field.py:155
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: udata
3
- Version: 9.0.1.dev29716
3
+ Version: 9.0.1.dev29728
4
4
  Summary: Open data portal
5
5
  Home-page: https://github.com/opendatateam/udata
6
6
  Author: Opendata Team
@@ -137,6 +137,7 @@ It is collectively taken care of by members of the
137
137
 
138
138
  ## Current (in progress)
139
139
 
140
+ - Add reports backend [#3069](https://github.com/opendatateam/udata/pull/3069)
140
141
  - Improve `udata db check-integrity` (perfs, Sentry notifications…) [#3026](https://github.com/opendatateam/udata/pull/3026)
141
142
  - Harvest dataservices [#3029](https://github.com/opendatateam/udata/pull/3029)
142
143
  - Refactor catalog exports [#3052](https://github.com/opendatateam/udata/pull/3052)
@@ -1,7 +1,7 @@
1
1
  tasks/__init__.py,sha256=CnVhb_TV-6nMhxVR6itnBmvuU2OSCs02AfNB4irVBTE,8132
2
2
  tasks/helpers.py,sha256=k_HiuiEJNgQLvWdeHqczPOAcrYpFjEepBeKo7EQzY8M,994
3
3
  udata/__init__.py,sha256=SslifvQytly1ztvHCc4bdlP3Va1F5UV60hSJC6a_guk,101
4
- udata/api_fields.py,sha256=_CArcH447noYPKk-NDFDJBbXexRmChH90pKsFlXa4Lg,13324
4
+ udata/api_fields.py,sha256=zx-BPYajUyRKOOIFIebn-MFSrRIyi4O-sf97V7KjoOI,13461
5
5
  udata/app.py,sha256=6upwrImLaWrSYtsXPW1zH84_oRxp3B6XFuocMe2D6NU,7329
6
6
  udata/assets.py,sha256=aMa-MnAEXVSTavptSn2V8sUE6jL_N0MrYCQ6_QpsuHs,645
7
7
  udata/entrypoints.py,sha256=8bZUvZ8ICO3kZxa8xDUaen6YS_OAGNKHFvaD7d8BOk0,2687
@@ -24,7 +24,7 @@ udata/worker.py,sha256=K-Wafye5-uXP4kQlffRKws2J9YbJ6m6n2QjcVsY8Nsg,118
24
24
  udata/wsgi.py,sha256=P7AJvZ5JqY4uRSBOzaFiBniChWIU9RVQ-Y0PN4vCCMY,77
25
25
  udata/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  udata/admin/views.py,sha256=wMlpnC1aINW-6JDk6-kQXhcTYBZH-5wajEuWzVDcIKA,331
27
- udata/api/__init__.py,sha256=RhgO7r6ROJzhEybCDmtHRwIiUpE-LjEIUr7dRmNdSeg,11429
27
+ udata/api/__init__.py,sha256=L-A5OsJbsfxyM67NnekSPHvsHHTuPaAXqtzHJrD3W2s,11471
28
28
  udata/api/commands.py,sha256=oK2p1VdUvULDdYuvYYpYvY_bdkPJy-KfROfoX71oOuA,3277
29
29
  udata/api/errors.py,sha256=Sy_f3WVrNTUPZjCOogIVgocDdUjnKz149KDi4mMA_Lg,240
30
30
  udata/api/fields.py,sha256=l-Fa27-easR86qgof2bk130jq1N1pNUgGmQzok1UI3Q,3094
@@ -161,6 +161,10 @@ udata/core/post/models.py,sha256=KYx3PiheMYk8z-LqkfJ80hewrYK7lDpGMa1uUP00Ats,190
161
161
  udata/core/post/permissions.py,sha256=uofU0TehhOGYyUoRXf3wuy816_D3xwMmaJbDvV336sw,83
162
162
  udata/core/post/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
163
  udata/core/post/tests/test_api.py,sha256=LSJQQDUtUVdpwZNpnJaX3yNQYyjkjTTyO7mHnR5DR_I,3948
164
+ udata/core/reports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
+ udata/core/reports/api.py,sha256=36ec56wkoGRk-3tWBNQl1OcyZF5yaIU5HfgUAz0cyAk,1355
166
+ udata/core/reports/constants.py,sha256=9cuwHv2dS_OONRB6MTO3y8lCwJmg1fELAqWJheBdqb8,843
167
+ udata/core/reports/models.py,sha256=itVsmi90wcVWgSJm-U96QfMiju2ZDoMkDtNxKQqw3r0,1881
164
168
  udata/core/reuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
169
  udata/core/reuse/activities.py,sha256=rdl_CR3RJPBMonsHYmPnPVDvCYozNdXN2H1ILrTItNQ,1417
166
170
  udata/core/reuse/api.py,sha256=72oJxmwziqvuFhxcuXy-5ckhQo39t65hp4eqvSqbQ6o,10237
@@ -343,7 +347,7 @@ udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py,sha256=17
343
347
  udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py,sha256=n5-udd1IdaD-e3q5cZx9nC4uMUSyJyT1TkI6I4gFgh0,400
344
348
  udata/migrations/2024-06-11-fix-reuse-datasets-references.py,sha256=qeJMoOpW46AA80Eoh5Br5a-E6ol6O-eqgwFWJt5XqF0,786
345
349
  udata/migrations/__init__.py,sha256=3YP8ppVG5Jx_u6fWJ099X6m3JDYsbd_wetpu4arl6_U,10882
346
- udata/models/__init__.py,sha256=RNv3-9A3ARWgKYYBJv-lJNvakPR3JLiuZ6722-CO6Kc,1288
350
+ udata/models/__init__.py,sha256=WTCK3-IW3HUTXRamHm35eiMnsSVA0AEgFT2K68QmfIo,1336
347
351
  udata/mongo/__init__.py,sha256=_ubmmhJuo6_fpuoMPEAat9Pv-5K_8rDbUuXmhe62zvU,1419
348
352
  udata/mongo/badges_field.py,sha256=NnHA6OT40UysQzO3qnxiDNLR-SCj19KlqD1c-p1DP2Q,984
349
353
  udata/mongo/datetime_fields.py,sha256=f3Q4LYa9pKRdB2W2yZVFIuIqd7_ELim1djr6xjPhEZg,1932
@@ -601,6 +605,7 @@ udata/tests/api/test_fields.py,sha256=OW85Z5MES5HeWOpapeem8OvR1cIcrqW-xMWpdZO4LZ
601
605
  udata/tests/api/test_follow_api.py,sha256=0h54P_Dfbo07u6tg0Rbai1WWgWb19ZLN2HGv4oLCWfg,3383
602
606
  udata/tests/api/test_me_api.py,sha256=8OthqVYQKZrFoGuJ7zAvoLJx1IclPNzPdD5Tnzmh3rM,14163
603
607
  udata/tests/api/test_organizations_api.py,sha256=buE1KdGIoJxfsj-ofz5CkMtgnARi38bXWR1jgPRkMzo,35732
608
+ udata/tests/api/test_reports_api.py,sha256=rqwgbOn8hpQEo-KFdSDxxjmLjtFFkleyvmLjBVTYsDc,3463
604
609
  udata/tests/api/test_reuses_api.py,sha256=GSoZ6SBRdMg6o0xKSQWtCSfvzqz_vZxERU567Rq1_GY,17009
605
610
  udata/tests/api/test_swagger.py,sha256=tLg452rCD5Q0AgFXOVZKMM6jGiWwC5diX5PxbdYni0o,760
606
611
  udata/tests/api/test_tags_api.py,sha256=xSIx_x5gqKz6BJCXKEsTqA_CR4BF1nG5NxEyfp9dy0o,2579
@@ -676,7 +681,7 @@ udata/tests/workers/test_jobs_commands.py,sha256=0Ru2N-uxgWAgYoADxFjLgyburD5OdBL
676
681
  udata/tests/workers/test_tasks_routing.py,sha256=4cBChvphj23rbLIu4dCTYYiM5OfTEUZKlXuNnyBCnWA,2260
677
682
  udata/tests/workers/test_workers_api.py,sha256=8MtfAjrPL1TUfEBfVjrYfm_4rft4DfBlcamiVvMCJAo,8863
678
683
  udata/tests/workers/test_workers_helpers.py,sha256=EVTHL-KhwbwD8snfN8HdK5o-wMXYkl2oXgtl990fQxE,647
679
- udata/translations/udata.pot,sha256=COdV87EQCmi-xh4woWqErwbnzlW9a5a2xo-6UVkPDZs,36594
684
+ udata/translations/udata.pot,sha256=fNgOTbNruk8ED7vj5hIuy--LunZsIq8bTm4roTJ41sw,37014
680
685
  udata/translations/ar/LC_MESSAGES/udata.mo,sha256=5WY0IWxqGknbC1o3BBRwTRg81ATBT9wLJHb3dK3piek,12665
681
686
  udata/translations/ar/LC_MESSAGES/udata.po,sha256=VjT4qh3z_xO1Sh0EjVW6Ffms17flM52btum1CXGXVIU,42012
682
687
  udata/translations/de/LC_MESSAGES/udata.mo,sha256=XPDFo3LbWnAUxIhtN0Y__aUMfKX6rKM4IQSlasUUhTE,16061
@@ -691,9 +696,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=iAUNwbI8ESi8MHkE3ZCYCSIXfFC27z
691
696
  udata/translations/pt/LC_MESSAGES/udata.po,sha256=uTmbHfzyFWrVXUkKSuNFzbGpX7EkUuBdD8fE04d3v5g,44572
692
697
  udata/translations/sr/LC_MESSAGES/udata.mo,sha256=1MbQHvKKNUwzMBWLNsH1qqBehO3aILhQiMhi5u1bY8E,28553
693
698
  udata/translations/sr/LC_MESSAGES/udata.po,sha256=AAryt27Gbkhk7FntCCU8_e7HSXATfsAQhwFOFC8CAj0,51152
694
- udata-9.0.1.dev29716.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
695
- udata-9.0.1.dev29716.dist-info/METADATA,sha256=9Bs1mMbhlRIg3NBi1p4VNbynxnEY41EiJV53pjoOEIo,125447
696
- udata-9.0.1.dev29716.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
697
- udata-9.0.1.dev29716.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
698
- udata-9.0.1.dev29716.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
699
- udata-9.0.1.dev29716.dist-info/RECORD,,
699
+ udata-9.0.1.dev29728.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
700
+ udata-9.0.1.dev29728.dist-info/METADATA,sha256=qFFB1G5TPGoXZxnLj_6xSut413YtnxQaC8nTCj9OJDQ,125526
701
+ udata-9.0.1.dev29728.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
702
+ udata-9.0.1.dev29728.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
703
+ udata-9.0.1.dev29728.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
704
+ udata-9.0.1.dev29728.dist-info/RECORD,,