udata 13.0.1.dev12__py3-none-any.whl → 14.4.1.dev7__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.

Files changed (177) hide show
  1. udata/api/__init__.py +2 -8
  2. udata/api_fields.py +35 -4
  3. udata/app.py +30 -50
  4. udata/auth/__init__.py +29 -6
  5. udata/auth/forms.py +8 -6
  6. udata/auth/views.py +6 -3
  7. udata/commands/__init__.py +2 -14
  8. udata/commands/db.py +13 -25
  9. udata/commands/info.py +0 -16
  10. udata/commands/serve.py +3 -11
  11. udata/commands/tests/test_fixtures.py +9 -9
  12. udata/core/access_type/api.py +1 -1
  13. udata/core/access_type/constants.py +12 -8
  14. udata/core/activity/api.py +5 -6
  15. udata/core/avatars/api.py +43 -0
  16. udata/core/avatars/test_avatar_api.py +30 -0
  17. udata/core/badges/tests/test_commands.py +6 -6
  18. udata/core/csv.py +5 -0
  19. udata/core/dataservices/models.py +15 -3
  20. udata/core/dataservices/tasks.py +7 -0
  21. udata/core/dataset/api.py +2 -0
  22. udata/core/dataset/models.py +2 -2
  23. udata/core/dataset/permissions.py +31 -0
  24. udata/core/dataset/tasks.py +50 -10
  25. udata/core/discussions/models.py +1 -0
  26. udata/core/metrics/__init__.py +0 -6
  27. udata/core/organization/api.py +8 -5
  28. udata/core/organization/mails.py +1 -1
  29. udata/core/organization/models.py +9 -1
  30. udata/core/organization/notifications.py +84 -0
  31. udata/core/organization/permissions.py +1 -1
  32. udata/core/organization/tasks.py +3 -0
  33. udata/core/pages/tests/test_api.py +32 -0
  34. udata/core/post/api.py +24 -69
  35. udata/core/post/models.py +84 -16
  36. udata/core/post/tests/test_api.py +24 -1
  37. udata/core/reports/api.py +18 -0
  38. udata/core/reports/models.py +42 -2
  39. udata/core/reuse/models.py +1 -1
  40. udata/core/reuse/tasks.py +7 -0
  41. udata/core/site/models.py +2 -6
  42. udata/core/spatial/commands.py +2 -4
  43. udata/core/spatial/forms.py +2 -2
  44. udata/core/spatial/models.py +0 -10
  45. udata/core/spatial/tests/test_api.py +1 -36
  46. udata/core/user/models.py +15 -2
  47. udata/cors.py +2 -5
  48. udata/db/migrations.py +279 -0
  49. udata/features/notifications/api.py +7 -18
  50. udata/features/notifications/models.py +56 -0
  51. udata/features/notifications/tasks.py +25 -0
  52. udata/flask_mongoengine/engine.py +0 -4
  53. udata/frontend/__init__.py +3 -122
  54. udata/frontend/markdown.py +2 -1
  55. udata/harvest/actions.py +24 -9
  56. udata/harvest/api.py +30 -22
  57. udata/harvest/backends/__init__.py +21 -9
  58. udata/harvest/backends/base.py +29 -3
  59. udata/harvest/backends/ckan/harvesters.py +13 -2
  60. udata/harvest/backends/dcat.py +3 -0
  61. udata/harvest/backends/maaf.py +1 -0
  62. udata/harvest/commands.py +39 -4
  63. udata/harvest/filters.py +17 -6
  64. udata/harvest/forms.py +9 -6
  65. udata/harvest/models.py +16 -0
  66. udata/harvest/permissions.py +27 -0
  67. udata/harvest/tasks.py +3 -5
  68. udata/harvest/tests/ckan/test_ckan_backend.py +35 -2
  69. udata/harvest/tests/ckan/test_ckan_backend_errors.py +1 -1
  70. udata/harvest/tests/ckan/test_ckan_backend_filters.py +1 -1
  71. udata/harvest/tests/ckan/test_dkan_backend.py +1 -1
  72. udata/harvest/tests/dcat/udata.xml +6 -6
  73. udata/harvest/tests/factories.py +1 -1
  74. udata/harvest/tests/test_actions.py +63 -8
  75. udata/harvest/tests/test_api.py +278 -123
  76. udata/harvest/tests/test_base_backend.py +88 -1
  77. udata/harvest/tests/test_dcat_backend.py +60 -13
  78. udata/harvest/tests/test_filters.py +6 -0
  79. udata/i18n.py +11 -273
  80. udata/mail.py +5 -1
  81. udata/migrations/2025-10-31-create-membership-request-notifications.py +55 -0
  82. udata/migrations/2025-11-13-delete-user-email-index.py +25 -0
  83. udata/migrations/2025-12-04-add-uuid-to-discussion-messages.py +28 -0
  84. udata/models/__init__.py +0 -8
  85. udata/mongo/slug_fields.py +1 -1
  86. udata/rdf.py +45 -6
  87. udata/routing.py +2 -10
  88. udata/sentry.py +4 -10
  89. udata/settings.py +23 -17
  90. udata/tasks.py +4 -3
  91. udata/templates/mail/message.html +5 -31
  92. udata/tests/__init__.py +28 -12
  93. udata/tests/api/__init__.py +108 -21
  94. udata/tests/api/test_activities_api.py +36 -0
  95. udata/tests/api/test_auth_api.py +121 -95
  96. udata/tests/api/test_base_api.py +7 -4
  97. udata/tests/api/test_dataservices_api.py +29 -1
  98. udata/tests/api/test_datasets_api.py +45 -21
  99. udata/tests/api/test_organizations_api.py +192 -197
  100. udata/tests/api/test_reports_api.py +157 -0
  101. udata/tests/api/test_reuses_api.py +147 -147
  102. udata/tests/api/test_security_api.py +12 -12
  103. udata/tests/api/test_swagger.py +4 -4
  104. udata/tests/api/test_tags_api.py +8 -8
  105. udata/tests/api/test_user_api.py +13 -1
  106. udata/tests/apiv2/test_swagger.py +4 -4
  107. udata/tests/apiv2/test_topics.py +1 -1
  108. udata/tests/cli/test_cli_base.py +8 -9
  109. udata/tests/dataset/test_dataset_commands.py +4 -4
  110. udata/tests/dataset/test_dataset_model.py +66 -26
  111. udata/tests/dataset/test_dataset_rdf.py +99 -5
  112. udata/tests/dataset/test_resource_preview.py +0 -1
  113. udata/tests/frontend/test_auth.py +24 -1
  114. udata/tests/frontend/test_csv.py +0 -3
  115. udata/tests/helpers.py +37 -27
  116. udata/tests/organization/test_notifications.py +67 -2
  117. udata/tests/plugin.py +6 -261
  118. udata/tests/site/test_site_csv_exports.py +22 -10
  119. udata/tests/test_activity.py +9 -9
  120. udata/tests/test_cors.py +1 -1
  121. udata/tests/test_dcat_commands.py +2 -2
  122. udata/tests/test_discussions.py +5 -5
  123. udata/tests/test_migrations.py +181 -481
  124. udata/tests/test_notifications.py +15 -57
  125. udata/tests/test_notifications_task.py +43 -0
  126. udata/tests/test_owned.py +81 -1
  127. udata/tests/test_storages.py +25 -19
  128. udata/tests/test_topics.py +77 -61
  129. udata/tests/test_uris.py +33 -0
  130. udata/tests/workers/test_jobs_commands.py +23 -23
  131. udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
  132. udata/translations/ar/LC_MESSAGES/udata.po +187 -108
  133. udata/translations/de/LC_MESSAGES/udata.mo +0 -0
  134. udata/translations/de/LC_MESSAGES/udata.po +187 -108
  135. udata/translations/es/LC_MESSAGES/udata.mo +0 -0
  136. udata/translations/es/LC_MESSAGES/udata.po +187 -108
  137. udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
  138. udata/translations/fr/LC_MESSAGES/udata.po +188 -109
  139. udata/translations/it/LC_MESSAGES/udata.mo +0 -0
  140. udata/translations/it/LC_MESSAGES/udata.po +187 -108
  141. udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
  142. udata/translations/pt/LC_MESSAGES/udata.po +187 -108
  143. udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
  144. udata/translations/sr/LC_MESSAGES/udata.po +187 -108
  145. udata/translations/udata.pot +215 -106
  146. udata/uris.py +0 -2
  147. udata/utils.py +5 -0
  148. udata-14.4.1.dev7.dist-info/METADATA +109 -0
  149. {udata-13.0.1.dev12.dist-info → udata-14.4.1.dev7.dist-info}/RECORD +153 -166
  150. {udata-13.0.1.dev12.dist-info → udata-14.4.1.dev7.dist-info}/entry_points.txt +3 -5
  151. udata/core/followers/views.py +0 -15
  152. udata/core/post/forms.py +0 -30
  153. udata/entrypoints.py +0 -93
  154. udata/features/identicon/__init__.py +0 -0
  155. udata/features/identicon/api.py +0 -13
  156. udata/features/identicon/backends.py +0 -131
  157. udata/features/identicon/tests/__init__.py +0 -0
  158. udata/features/identicon/tests/test_backends.py +0 -18
  159. udata/features/territories/__init__.py +0 -49
  160. udata/features/territories/api.py +0 -25
  161. udata/features/territories/models.py +0 -51
  162. udata/flask_mongoengine/json.py +0 -38
  163. udata/migrations/__init__.py +0 -367
  164. udata/templates/mail/base.html +0 -105
  165. udata/templates/mail/base.txt +0 -6
  166. udata/templates/mail/button.html +0 -3
  167. udata/templates/mail/layouts/1-column.html +0 -19
  168. udata/templates/mail/layouts/2-columns.html +0 -20
  169. udata/templates/mail/layouts/center-panel.html +0 -16
  170. udata/tests/cli/test_db_cli.py +0 -68
  171. udata/tests/features/territories/__init__.py +0 -20
  172. udata/tests/features/territories/test_territories_api.py +0 -185
  173. udata/tests/frontend/test_hooks.py +0 -149
  174. udata-13.0.1.dev12.dist-info/METADATA +0 -133
  175. {udata-13.0.1.dev12.dist-info → udata-14.4.1.dev7.dist-info}/WHEEL +0 -0
  176. {udata-13.0.1.dev12.dist-info → udata-14.4.1.dev7.dist-info}/licenses/LICENSE +0 -0
  177. {udata-13.0.1.dev12.dist-info → udata-14.4.1.dev7.dist-info}/top_level.txt +0 -0
@@ -4,11 +4,6 @@ udata = udata.commands:cli
4
4
  [pytest11]
5
5
  udata = udata.tests.plugin
6
6
 
7
- [udata.avatars]
8
- adorable = udata.features.identicon.backends:adorable
9
- internal = udata.features.identicon.backends:internal
10
- robohash = udata.features.identicon.backends:robohash
11
-
12
7
  [udata.harvesters]
13
8
  ckan = udata.harvest.backends.ckan.harvesters:CkanBackend
14
9
  csw-dcat = udata.harvest.backends.dcat:CswDcatBackend
@@ -16,3 +11,6 @@ csw-iso-19139 = udata.harvest.backends.dcat:CswIso19139DcatBackend
16
11
  dcat = udata.harvest.backends.dcat:DcatBackend
17
12
  dkan = udata.harvest.backends.ckan.harvesters:DkanBackend
18
13
  maaf = udata.harvest.backends.maaf:MaafBackend
14
+
15
+ [udata.i18n]
16
+ udata = udata.translations
@@ -1,15 +0,0 @@
1
- # from udata.auth import current_user
2
-
3
- # from udata.i18n import I18nBlueprint
4
-
5
- # from .models import Follow
6
-
7
- # blueprint = I18nBlueprint('followers', __name__)
8
-
9
-
10
- # @blueprint.app_template_global()
11
- # @blueprint.app_template_filter()
12
- # def is_following(obj):
13
- # if not current_user.is_authenticated:
14
- # return False
15
- # return Follow.objects.is_following(current_user._get_current_object(), obj)
udata/core/post/forms.py DELETED
@@ -1,30 +0,0 @@
1
- from udata.forms import ModelForm, fields, validators, widgets
2
- from udata.i18n import lazy_gettext as _
3
-
4
- from .constants import IMAGE_SIZES
5
- from .models import Post
6
-
7
- __all__ = ("PostForm",)
8
-
9
-
10
- class PostForm(ModelForm):
11
- model_class = Post
12
-
13
- owner = fields.CurrentUserField()
14
-
15
- name = fields.StringField(_("Name"), [validators.DataRequired()])
16
- headline = fields.StringField(_("Headline"), widget=widgets.TextArea())
17
- content = fields.MarkdownField(_("Content"), [validators.DataRequired()])
18
-
19
- datasets = fields.DatasetListField(_("Associated datasets"))
20
- reuses = fields.ReuseListField(_("Associated reuses"))
21
-
22
- image = fields.ImageField(_("Image"), sizes=IMAGE_SIZES)
23
- credit_to = fields.StringField(_("Image credits"))
24
- credit_url = fields.URLField(_("Credit URL"))
25
-
26
- tags = fields.TagField(_("Tags"))
27
-
28
- body_type = fields.StringField(
29
- _("body type"), description=_("Specify your body type (HTML or markdown)")
30
- )
udata/entrypoints.py DELETED
@@ -1,93 +0,0 @@
1
- import pkg_resources
2
-
3
- # Here for documentation purpose
4
- ENTRYPOINTS = {
5
- "udata.avatars": "Avatar rendering backends",
6
- "udata.harvesters": "Harvest backends",
7
- "udata.metrics": "Extra metrics",
8
- "udata.models": "Models and migrations",
9
- "udata.plugins": "Generic plugin",
10
- "udata.tasks": "Tasks and jobs",
11
- "udata.views": "Extra views",
12
- }
13
-
14
-
15
- class EntrypointError(Exception):
16
- pass
17
-
18
-
19
- def iter_all(name):
20
- """Iter all entrypoints registered on a given key"""
21
- return pkg_resources.iter_entry_points(name)
22
-
23
-
24
- def get_all(entrypoint_key):
25
- """Load all entrypoints registered on a given key"""
26
- return dict(_ep_to_kv(e) for e in iter_all(entrypoint_key))
27
-
28
-
29
- def get_enabled(name, app):
30
- """
31
- Get (and load) entrypoints registered on name
32
- and enabled for the given app.
33
- """
34
- plugins = app.config["PLUGINS"]
35
- return dict(
36
- _ep_to_kv(e)
37
- for e in iter_all(name)
38
- if e.name in plugins or e.name.startswith(tuple(plugins))
39
- )
40
-
41
-
42
- def get_plugin_module(name, app, plugin):
43
- """
44
- Get the module for a given plugin
45
- """
46
- return next((m for p, m in get_enabled(name, app).items() if p == plugin), None)
47
-
48
-
49
- def _ep_to_kv(entrypoint):
50
- """
51
- Transform an entrypoint into a key-value tuple where:
52
- - key is the entrypoint name
53
- - value is the entrypoint class with the name attribute
54
- matching from entrypoint name
55
- """
56
- cls = entrypoint.load()
57
- cls.name = entrypoint.name
58
- return (entrypoint.name, cls)
59
-
60
-
61
- def known_dists():
62
- """Return a list of all Distributions exporting udata.* entrypoints"""
63
- return (
64
- dist
65
- for dist in pkg_resources.working_set
66
- if any(k in ENTRYPOINTS for k in dist.get_entry_map().keys())
67
- )
68
-
69
-
70
- def get_plugins_dists(app, name=None):
71
- """Return a list of Distributions with enabled udata plugins"""
72
- if name:
73
- plugins = set(e.name for e in iter_all(name) if e.name in app.config["PLUGINS"])
74
- else:
75
- plugins = set(app.config["PLUGINS"])
76
- return [
77
- d for d in known_dists() if any(set(v.keys()) & plugins for v in d.get_entry_map().values())
78
- ]
79
-
80
-
81
- def get_roots(app=None):
82
- """
83
- Returns the list of root packages/modules exposing endpoints.
84
-
85
- If app is provided, only returns those of enabled plugins
86
- """
87
- roots = set()
88
- plugins = app.config["PLUGINS"] if app else None
89
- for name in ENTRYPOINTS.keys():
90
- for ep in iter_all(name):
91
- if plugins is None or ep.name in plugins:
92
- roots.add(ep.module_name.split(".", 1)[0])
93
- return list(roots)
File without changes
@@ -1,13 +0,0 @@
1
- from udata.api import API, api
2
-
3
- from . import backends
4
-
5
- ns = api.namespace("avatars", "Avatars")
6
-
7
-
8
- @ns.route("/<identifier>/<int:size>/", endpoint="avatar")
9
- class IdenticonAPI(API):
10
- @api.doc("avatars")
11
- def get(self, identifier, size):
12
- """Get a deterministic avatar given an identifier at a given size"""
13
- return backends.get_identicon(identifier, size)
@@ -1,131 +0,0 @@
1
- import hashlib
2
- import io
3
-
4
- import pydenticon
5
- from flask import current_app, redirect, send_file
6
-
7
- from udata import entrypoints
8
- from udata.app import cache
9
-
10
- ADORABLE_AVATARS_URL = "https://api.adorable.io/avatars/{size}/{identifier}.png" # noqa
11
- ROBOHASH_URL = "https://robohash.org/{identifier}.png?size={size}x{size}&set={skin}&bgset={bg}" # noqa
12
-
13
-
14
- # Default values overriden by theme default and local config
15
- DEFAULTS = {
16
- "AVATAR_PROVIDER": "internal",
17
- # Internal provider
18
- "AVATAR_INTERNAL_SIZE": 7,
19
- "AVATAR_INTERNAL_FOREGROUND": [
20
- "rgb(45,79,255)",
21
- "rgb(254,180,44)",
22
- "rgb(226,121,234)",
23
- "rgb(30,179,253)",
24
- "rgb(232,77,65)",
25
- "rgb(49,203,115)",
26
- "rgb(141,69,170)",
27
- ],
28
- "AVATAR_INTERNAL_BACKGROUND": "rgb(224,224,224)",
29
- "AVATAR_INTERNAL_PADDING": 10,
30
- # robohash prodiver
31
- "AVATAR_ROBOHASH_SKIN": "set1",
32
- "AVATAR_ROBOHASH_BACKGROUND": "bg1",
33
- }
34
-
35
-
36
- def get_config(key):
37
- """
38
- Get an identicon configuration parameter.
39
-
40
- Precedance order is:
41
- - application config (`udata.cfg`)
42
- - default
43
- """
44
- key = "AVATAR_{0}".format(key.upper())
45
- local_config = current_app.config.get(key)
46
- return local_config or DEFAULTS[key]
47
-
48
-
49
- def get_internal_config(key):
50
- return get_config("internal_{0}".format(key))
51
-
52
-
53
- def get_provider():
54
- """Get the current provider from config"""
55
- name = get_config("provider")
56
- available = entrypoints.get_all("udata.avatars")
57
- if name not in available:
58
- raise ValueError("Unknown avatar provider: {0}".format(name))
59
- return available[name]
60
-
61
-
62
- def get_identicon(identifier, size):
63
- """
64
- Get an identicon for a given identifier at a given size.
65
-
66
- Automatically select the provider from `AVATAR_PROVIDER`
67
-
68
- :returns: a HTTP response, either an image or a redirect
69
- """
70
- return get_provider()(identifier, size)
71
-
72
-
73
- @cache.memoize()
74
- def generate_pydenticon(identifier, size):
75
- """
76
- Use pydenticon to generate an identicon image.
77
- All parameters are extracted from configuration.
78
- """
79
- blocks_size = get_internal_config("size")
80
- foreground = get_internal_config("foreground")
81
- background = get_internal_config("background")
82
- generator = pydenticon.Generator(
83
- blocks_size, blocks_size, digest=hashlib.sha1, foreground=foreground, background=background
84
- )
85
-
86
- # Pydenticon adds padding to the size and as a consequence
87
- # we need to compute the size without the padding
88
- padding = int(round(get_internal_config("padding") * size / 100.0))
89
- size = size - 2 * padding
90
- padding = (padding,) * 4
91
- return generator.generate(identifier, size, size, padding=padding, output_format="png")
92
-
93
-
94
- def internal(identifier, size):
95
- """
96
- Internal provider
97
-
98
- Use pydenticon to generate an identicon.
99
- """
100
- identicon = generate_pydenticon(identifier, size)
101
- response = send_file(io.BytesIO(identicon), mimetype="image/png")
102
- etag = hashlib.sha1(identicon).hexdigest()
103
- response.set_etag(etag)
104
- return response
105
-
106
-
107
- def adorable(identifier, size):
108
- """
109
- Adorable Avatars provider
110
-
111
- Simply redirect to the external API.
112
-
113
- See: http://avatars.adorable.io/
114
- """
115
- url = ADORABLE_AVATARS_URL.format(identifier=identifier, size=size)
116
- return redirect(url)
117
-
118
-
119
- def robohash(identifier, size):
120
- """
121
- Robohash provider
122
-
123
- Redirect to the Robohash API
124
- with parameters extracted from configuration.
125
-
126
- See: https://robohash.org/
127
- """
128
- skin = get_config("robohash_skin")
129
- background = get_config("robohash_background")
130
- url = ROBOHASH_URL.format(identifier=identifier, size=size, skin=skin, bg=background)
131
- return redirect(url)
File without changes
@@ -1,18 +0,0 @@
1
- from udata.features.identicon.backends import internal
2
- from udata.tests.api import PytestOnlyAPITestCase
3
- from udata.tests.helpers import assert200
4
- from udata.utils import faker
5
-
6
-
7
- class InternalBackendTest(PytestOnlyAPITestCase):
8
- def test_base_rendering(self):
9
- response = internal(faker.word(), 32)
10
- assert200(response)
11
- assert response.mimetype == "image/png"
12
- assert response.is_streamed
13
- etag, weak = response.get_etag()
14
- assert etag is not None
15
-
16
- def test_render_twice_the_same(self):
17
- identifier = faker.word()
18
- self.assertStreamEqual(internal(identifier, 32), internal(identifier, 32))
@@ -1,49 +0,0 @@
1
- from flask import current_app
2
-
3
- from udata.models import GeoZone, db
4
-
5
-
6
- def check_for_territories(query):
7
- """
8
- Return a geozone queryset of territories given the `query`.
9
-
10
- Results are sorted by population and area (biggest first).
11
- """
12
- if not query or not current_app.config.get("ACTIVATE_TERRITORIES"):
13
- return []
14
-
15
- dbqs = db.Q()
16
- query = query.lower()
17
- is_digit = query.isdigit()
18
- query_length = len(query)
19
- for level in current_app.config.get("HANDLED_LEVELS"):
20
- if level == "country":
21
- continue # Level not fully handled yet.
22
- q = db.Q(level=level)
23
- if query_length == 2 and level == "fr:departement" and (is_digit or query in ("2a", "2b")):
24
- # Counties + Corsica.
25
- q &= db.Q(code=query)
26
- elif query_length == 3 and level == "fr:departement" and is_digit:
27
- # French DROM-COM.
28
- q &= db.Q(code=query)
29
- elif (
30
- query_length == 5
31
- and level == "fr:commune"
32
- and (is_digit or query.startswith("2a") or query.startswith("2b"))
33
- ):
34
- # INSEE code then postal codes with Corsica exceptions.
35
- q &= db.Q(code=query)
36
- elif query_length >= 4:
37
- # Check names starting with query or exact match.
38
- q &= db.Q(name__istartswith=query) | db.Q(name__iexact=query)
39
- else:
40
- continue
41
-
42
- # Meta Q object, ready to be passed to a queryset.
43
- dbqs |= q
44
-
45
- if dbqs.empty:
46
- return []
47
-
48
- # Sort matching results by population and area.
49
- return GeoZone.objects(dbqs).order_by("-population", "-area")
@@ -1,25 +0,0 @@
1
- from udata.api import API, api
2
- from udata.features.territories import check_for_territories
3
-
4
- suggest_parser = api.parser()
5
- suggest_parser.add_argument(
6
- "q", type=str, help="The string to autocomplete/suggest", location="args", required=True
7
- )
8
- suggest_parser.add_argument(
9
- "size", type=int, help="The maximum result size", location="args", required=False
10
- )
11
-
12
-
13
- @api.route("/territory/suggest/", endpoint="suggest_territory")
14
- class SuggestTerritoriesAPI(API):
15
- @api.doc("suggest_territory")
16
- @api.expect(suggest_parser)
17
- def get(self):
18
- args = suggest_parser.parse_args()
19
- territories = check_for_territories(args["q"])
20
- if args["size"]:
21
- territories = territories[: args["size"]]
22
- return [
23
- {"id": territory.id, "title": territory.name, "page": territory.external_url}
24
- for territory in territories
25
- ]
@@ -1,51 +0,0 @@
1
- from udata.models import License, Organization
2
-
3
- __all__ = ("TerritoryDataset", "ResourceBasedTerritoryDataset", "TERRITORY_DATASETS")
4
-
5
-
6
- TERRITORY_DATASETS = {"commune": {}, "departement": {}, "region": {}, "country": {}}
7
-
8
-
9
- class TerritoryDataset(object):
10
- order = 0
11
- id = ""
12
- title = ""
13
- organization_id = ""
14
- url_template = ""
15
- description = ""
16
- license_id = "fr-lo"
17
-
18
- def __init__(self, territory):
19
- self.territory = territory
20
-
21
- @property
22
- def url(self):
23
- return self.url_template.format(code=self.territory.code)
24
-
25
- @property
26
- def slug(self):
27
- return "{territory_id}:{id}".format(territory_id=self.territory.id, id=self.id)
28
-
29
- @property
30
- def organization(self):
31
- return Organization.objects.get(id=self.organization_id)
32
-
33
- @property
34
- def license(self):
35
- return License.objects(id=self.license_id).first()
36
-
37
-
38
- class ResourceBasedTerritoryDataset(TerritoryDataset):
39
- dataset_id = ""
40
- resource_id = ""
41
- territory_attr = ""
42
- csv_column = ""
43
-
44
- def url_for(self, external=False):
45
- return None
46
-
47
- url = property(url_for)
48
-
49
- @property
50
- def external_url(self):
51
- return self.url_for(external=True)
@@ -1,38 +0,0 @@
1
- from bson import json_util
2
- from flask.json import JSONEncoder
3
- from mongoengine.base import BaseDocument
4
- from mongoengine.queryset import QuerySet
5
-
6
-
7
- def _make_encoder(superclass):
8
- class MongoEngineJSONEncoder(superclass):
9
- """
10
- A JSONEncoder which provides serialization of MongoEngine
11
- documents and queryset objects.
12
- """
13
-
14
- def default(self, obj):
15
- if isinstance(obj, BaseDocument):
16
- return json_util._json_convert(obj.to_mongo())
17
- elif isinstance(obj, QuerySet):
18
- return json_util._json_convert(obj.as_pymongo())
19
- return superclass.default(self, obj)
20
-
21
- return MongoEngineJSONEncoder
22
-
23
-
24
- MongoEngineJSONEncoder = _make_encoder(JSONEncoder)
25
-
26
-
27
- def override_json_encoder(app):
28
- """
29
- A function to dynamically create a new MongoEngineJSONEncoder class
30
- based upon a custom base class.
31
- This function allows us to combine MongoEngine serialization with
32
- any changes to Flask's JSONEncoder which a user may have made
33
- prior to calling init_app.
34
-
35
- NOTE: This does not cover situations where users override
36
- an instance's json_encoder after calling init_app.
37
- """
38
- app.json_encoder = _make_encoder(app.json_encoder)