udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30454__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.

Files changed (413) hide show
  1. tasks/__init__.py +109 -107
  2. tasks/helpers.py +18 -18
  3. udata/__init__.py +4 -4
  4. udata/admin/views.py +5 -5
  5. udata/api/__init__.py +111 -134
  6. udata/api/commands.py +45 -37
  7. udata/api/errors.py +5 -4
  8. udata/api/fields.py +23 -21
  9. udata/api/oauth2.py +55 -74
  10. udata/api/parsers.py +15 -15
  11. udata/api/signals.py +1 -1
  12. udata/api_fields.py +137 -89
  13. udata/app.py +58 -55
  14. udata/assets.py +5 -5
  15. udata/auth/__init__.py +37 -26
  16. udata/auth/forms.py +23 -15
  17. udata/auth/helpers.py +1 -1
  18. udata/auth/mails.py +3 -3
  19. udata/auth/password_validation.py +19 -15
  20. udata/auth/views.py +94 -68
  21. udata/commands/__init__.py +71 -69
  22. udata/commands/cache.py +7 -7
  23. udata/commands/db.py +201 -140
  24. udata/commands/dcat.py +36 -30
  25. udata/commands/fixtures.py +100 -84
  26. udata/commands/images.py +21 -20
  27. udata/commands/info.py +17 -20
  28. udata/commands/init.py +10 -10
  29. udata/commands/purge.py +12 -13
  30. udata/commands/serve.py +41 -29
  31. udata/commands/static.py +16 -18
  32. udata/commands/test.py +20 -20
  33. udata/commands/tests/fixtures.py +26 -24
  34. udata/commands/worker.py +31 -33
  35. udata/core/__init__.py +12 -12
  36. udata/core/activity/__init__.py +0 -1
  37. udata/core/activity/api.py +59 -49
  38. udata/core/activity/models.py +28 -26
  39. udata/core/activity/signals.py +1 -1
  40. udata/core/activity/tasks.py +16 -10
  41. udata/core/badges/api.py +6 -6
  42. udata/core/badges/commands.py +14 -13
  43. udata/core/badges/fields.py +8 -5
  44. udata/core/badges/forms.py +7 -4
  45. udata/core/badges/models.py +16 -31
  46. udata/core/badges/permissions.py +1 -3
  47. udata/core/badges/signals.py +2 -2
  48. udata/core/badges/tasks.py +3 -2
  49. udata/core/badges/tests/test_commands.py +10 -10
  50. udata/core/badges/tests/test_model.py +24 -31
  51. udata/core/contact_point/api.py +19 -18
  52. udata/core/contact_point/api_fields.py +21 -14
  53. udata/core/contact_point/factories.py +2 -2
  54. udata/core/contact_point/forms.py +7 -6
  55. udata/core/contact_point/models.py +3 -5
  56. udata/core/dataservices/api.py +26 -21
  57. udata/core/dataservices/factories.py +13 -11
  58. udata/core/dataservices/models.py +35 -40
  59. udata/core/dataservices/permissions.py +4 -4
  60. udata/core/dataservices/rdf.py +40 -17
  61. udata/core/dataservices/tasks.py +4 -3
  62. udata/core/dataset/actions.py +10 -10
  63. udata/core/dataset/activities.py +21 -23
  64. udata/core/dataset/api.py +321 -298
  65. udata/core/dataset/api_fields.py +443 -271
  66. udata/core/dataset/apiv2.py +305 -229
  67. udata/core/dataset/commands.py +38 -36
  68. udata/core/dataset/constants.py +61 -54
  69. udata/core/dataset/csv.py +70 -74
  70. udata/core/dataset/events.py +39 -32
  71. udata/core/dataset/exceptions.py +8 -4
  72. udata/core/dataset/factories.py +57 -65
  73. udata/core/dataset/forms.py +87 -63
  74. udata/core/dataset/models.py +336 -280
  75. udata/core/dataset/permissions.py +9 -6
  76. udata/core/dataset/preview.py +15 -17
  77. udata/core/dataset/rdf.py +156 -122
  78. udata/core/dataset/search.py +92 -77
  79. udata/core/dataset/signals.py +1 -1
  80. udata/core/dataset/tasks.py +63 -54
  81. udata/core/discussions/actions.py +5 -5
  82. udata/core/discussions/api.py +124 -120
  83. udata/core/discussions/factories.py +2 -2
  84. udata/core/discussions/forms.py +9 -7
  85. udata/core/discussions/metrics.py +1 -3
  86. udata/core/discussions/models.py +25 -24
  87. udata/core/discussions/notifications.py +18 -14
  88. udata/core/discussions/permissions.py +3 -3
  89. udata/core/discussions/signals.py +4 -4
  90. udata/core/discussions/tasks.py +24 -28
  91. udata/core/followers/api.py +32 -33
  92. udata/core/followers/models.py +9 -9
  93. udata/core/followers/signals.py +3 -3
  94. udata/core/jobs/actions.py +7 -7
  95. udata/core/jobs/api.py +99 -92
  96. udata/core/jobs/commands.py +48 -49
  97. udata/core/jobs/forms.py +11 -11
  98. udata/core/jobs/models.py +6 -6
  99. udata/core/metrics/__init__.py +2 -2
  100. udata/core/metrics/commands.py +34 -30
  101. udata/core/metrics/models.py +2 -4
  102. udata/core/metrics/signals.py +1 -1
  103. udata/core/metrics/tasks.py +3 -3
  104. udata/core/organization/activities.py +12 -15
  105. udata/core/organization/api.py +167 -174
  106. udata/core/organization/api_fields.py +183 -124
  107. udata/core/organization/apiv2.py +32 -32
  108. udata/core/organization/commands.py +20 -22
  109. udata/core/organization/constants.py +11 -11
  110. udata/core/organization/csv.py +17 -15
  111. udata/core/organization/factories.py +8 -11
  112. udata/core/organization/forms.py +32 -26
  113. udata/core/organization/metrics.py +2 -1
  114. udata/core/organization/models.py +87 -67
  115. udata/core/organization/notifications.py +18 -14
  116. udata/core/organization/permissions.py +10 -11
  117. udata/core/organization/rdf.py +14 -14
  118. udata/core/organization/search.py +30 -28
  119. udata/core/organization/signals.py +7 -7
  120. udata/core/organization/tasks.py +42 -61
  121. udata/core/owned.py +38 -27
  122. udata/core/post/api.py +82 -81
  123. udata/core/post/constants.py +8 -5
  124. udata/core/post/factories.py +4 -4
  125. udata/core/post/forms.py +13 -14
  126. udata/core/post/models.py +20 -22
  127. udata/core/post/tests/test_api.py +30 -32
  128. udata/core/reports/api.py +8 -7
  129. udata/core/reports/constants.py +1 -3
  130. udata/core/reports/models.py +10 -10
  131. udata/core/reuse/activities.py +15 -19
  132. udata/core/reuse/api.py +123 -126
  133. udata/core/reuse/api_fields.py +120 -85
  134. udata/core/reuse/apiv2.py +11 -10
  135. udata/core/reuse/constants.py +23 -23
  136. udata/core/reuse/csv.py +18 -18
  137. udata/core/reuse/factories.py +5 -9
  138. udata/core/reuse/forms.py +24 -21
  139. udata/core/reuse/models.py +55 -51
  140. udata/core/reuse/permissions.py +2 -2
  141. udata/core/reuse/search.py +49 -46
  142. udata/core/reuse/signals.py +1 -1
  143. udata/core/reuse/tasks.py +4 -5
  144. udata/core/site/api.py +47 -50
  145. udata/core/site/factories.py +2 -2
  146. udata/core/site/forms.py +4 -5
  147. udata/core/site/models.py +94 -63
  148. udata/core/site/rdf.py +14 -14
  149. udata/core/spam/api.py +16 -9
  150. udata/core/spam/constants.py +4 -4
  151. udata/core/spam/fields.py +13 -7
  152. udata/core/spam/models.py +27 -20
  153. udata/core/spam/signals.py +1 -1
  154. udata/core/spam/tests/test_spam.py +6 -5
  155. udata/core/spatial/api.py +72 -80
  156. udata/core/spatial/api_fields.py +73 -58
  157. udata/core/spatial/commands.py +67 -64
  158. udata/core/spatial/constants.py +3 -3
  159. udata/core/spatial/factories.py +37 -54
  160. udata/core/spatial/forms.py +27 -26
  161. udata/core/spatial/geoids.py +17 -17
  162. udata/core/spatial/models.py +43 -47
  163. udata/core/spatial/tasks.py +2 -1
  164. udata/core/spatial/tests/test_api.py +115 -130
  165. udata/core/spatial/tests/test_fields.py +74 -77
  166. udata/core/spatial/tests/test_geoid.py +22 -22
  167. udata/core/spatial/tests/test_models.py +5 -7
  168. udata/core/spatial/translations.py +16 -16
  169. udata/core/storages/__init__.py +16 -18
  170. udata/core/storages/api.py +66 -64
  171. udata/core/storages/tasks.py +7 -7
  172. udata/core/storages/utils.py +15 -15
  173. udata/core/storages/views.py +5 -6
  174. udata/core/tags/api.py +17 -14
  175. udata/core/tags/csv.py +4 -4
  176. udata/core/tags/models.py +8 -5
  177. udata/core/tags/tasks.py +11 -13
  178. udata/core/tags/views.py +4 -4
  179. udata/core/topic/api.py +84 -73
  180. udata/core/topic/apiv2.py +157 -127
  181. udata/core/topic/factories.py +3 -4
  182. udata/core/topic/forms.py +12 -14
  183. udata/core/topic/models.py +14 -19
  184. udata/core/topic/parsers.py +26 -26
  185. udata/core/user/activities.py +30 -29
  186. udata/core/user/api.py +151 -152
  187. udata/core/user/api_fields.py +132 -100
  188. udata/core/user/apiv2.py +7 -7
  189. udata/core/user/commands.py +38 -38
  190. udata/core/user/factories.py +8 -9
  191. udata/core/user/forms.py +14 -11
  192. udata/core/user/metrics.py +2 -2
  193. udata/core/user/models.py +68 -69
  194. udata/core/user/permissions.py +4 -5
  195. udata/core/user/rdf.py +7 -8
  196. udata/core/user/tasks.py +2 -2
  197. udata/core/user/tests/test_user_model.py +24 -16
  198. udata/cors.py +99 -0
  199. udata/db/tasks.py +2 -1
  200. udata/entrypoints.py +35 -31
  201. udata/errors.py +2 -1
  202. udata/event/values.py +6 -6
  203. udata/factories.py +2 -2
  204. udata/features/identicon/api.py +5 -6
  205. udata/features/identicon/backends.py +48 -55
  206. udata/features/identicon/tests/test_backends.py +4 -5
  207. udata/features/notifications/__init__.py +0 -1
  208. udata/features/notifications/actions.py +9 -9
  209. udata/features/notifications/api.py +17 -13
  210. udata/features/territories/__init__.py +12 -10
  211. udata/features/territories/api.py +14 -15
  212. udata/features/territories/models.py +23 -28
  213. udata/features/transfer/actions.py +8 -11
  214. udata/features/transfer/api.py +84 -77
  215. udata/features/transfer/factories.py +2 -1
  216. udata/features/transfer/models.py +11 -12
  217. udata/features/transfer/notifications.py +19 -15
  218. udata/features/transfer/permissions.py +5 -5
  219. udata/forms/__init__.py +5 -2
  220. udata/forms/fields.py +164 -172
  221. udata/forms/validators.py +19 -22
  222. udata/forms/widgets.py +9 -13
  223. udata/frontend/__init__.py +31 -26
  224. udata/frontend/csv.py +68 -58
  225. udata/frontend/markdown.py +40 -44
  226. udata/harvest/actions.py +89 -77
  227. udata/harvest/api.py +294 -238
  228. udata/harvest/backends/__init__.py +4 -4
  229. udata/harvest/backends/base.py +128 -111
  230. udata/harvest/backends/dcat.py +80 -66
  231. udata/harvest/commands.py +56 -60
  232. udata/harvest/csv.py +8 -8
  233. udata/harvest/exceptions.py +6 -3
  234. udata/harvest/filters.py +24 -23
  235. udata/harvest/forms.py +27 -28
  236. udata/harvest/models.py +88 -80
  237. udata/harvest/notifications.py +15 -10
  238. udata/harvest/signals.py +13 -13
  239. udata/harvest/tasks.py +11 -10
  240. udata/harvest/tests/factories.py +23 -24
  241. udata/harvest/tests/test_actions.py +136 -166
  242. udata/harvest/tests/test_api.py +220 -214
  243. udata/harvest/tests/test_base_backend.py +117 -112
  244. udata/harvest/tests/test_dcat_backend.py +380 -308
  245. udata/harvest/tests/test_filters.py +33 -22
  246. udata/harvest/tests/test_models.py +11 -14
  247. udata/harvest/tests/test_notifications.py +6 -7
  248. udata/harvest/tests/test_tasks.py +7 -6
  249. udata/i18n.py +237 -78
  250. udata/linkchecker/backends.py +5 -11
  251. udata/linkchecker/checker.py +23 -22
  252. udata/linkchecker/commands.py +4 -6
  253. udata/linkchecker/models.py +6 -6
  254. udata/linkchecker/tasks.py +18 -20
  255. udata/mail.py +21 -21
  256. udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
  257. udata/migrations/2020-08-24-add-fs-filename.py +9 -8
  258. udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
  259. udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
  260. udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
  261. udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
  262. udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
  263. udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
  264. udata/migrations/2021-08-17-follow-integrity.py +5 -4
  265. udata/migrations/2021-08-17-harvest-integrity.py +13 -12
  266. udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
  267. udata/migrations/2021-08-17-transfer-integrity.py +5 -4
  268. udata/migrations/2021-08-17-users-integrity.py +9 -8
  269. udata/migrations/2021-12-14-reuse-topics.py +7 -6
  270. udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
  271. udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
  272. udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
  273. udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
  274. udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
  275. udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
  276. udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
  277. udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
  278. udata/migrations/__init__.py +123 -105
  279. udata/models/__init__.py +4 -4
  280. udata/mongo/__init__.py +13 -11
  281. udata/mongo/badges_field.py +3 -2
  282. udata/mongo/datetime_fields.py +13 -12
  283. udata/mongo/document.py +17 -16
  284. udata/mongo/engine.py +15 -16
  285. udata/mongo/errors.py +2 -1
  286. udata/mongo/extras_fields.py +30 -20
  287. udata/mongo/queryset.py +12 -12
  288. udata/mongo/slug_fields.py +38 -28
  289. udata/mongo/taglist_field.py +1 -2
  290. udata/mongo/url_field.py +5 -5
  291. udata/mongo/uuid_fields.py +4 -3
  292. udata/notifications/__init__.py +1 -1
  293. udata/notifications/mattermost.py +10 -9
  294. udata/rdf.py +167 -188
  295. udata/routing.py +40 -45
  296. udata/search/__init__.py +18 -19
  297. udata/search/adapter.py +17 -16
  298. udata/search/commands.py +44 -51
  299. udata/search/fields.py +13 -20
  300. udata/search/query.py +23 -18
  301. udata/search/result.py +9 -10
  302. udata/sentry.py +21 -19
  303. udata/settings.py +262 -198
  304. udata/sitemap.py +8 -6
  305. udata/storage/s3.py +20 -13
  306. udata/tags.py +4 -5
  307. udata/tasks.py +43 -42
  308. udata/tests/__init__.py +9 -6
  309. udata/tests/api/__init__.py +8 -6
  310. udata/tests/api/test_auth_api.py +395 -321
  311. udata/tests/api/test_base_api.py +33 -35
  312. udata/tests/api/test_contact_points.py +7 -9
  313. udata/tests/api/test_dataservices_api.py +211 -158
  314. udata/tests/api/test_datasets_api.py +823 -812
  315. udata/tests/api/test_follow_api.py +13 -15
  316. udata/tests/api/test_me_api.py +95 -112
  317. udata/tests/api/test_organizations_api.py +301 -339
  318. udata/tests/api/test_reports_api.py +35 -25
  319. udata/tests/api/test_reuses_api.py +134 -139
  320. udata/tests/api/test_swagger.py +5 -5
  321. udata/tests/api/test_tags_api.py +18 -25
  322. udata/tests/api/test_topics_api.py +94 -94
  323. udata/tests/api/test_transfer_api.py +53 -48
  324. udata/tests/api/test_user_api.py +128 -141
  325. udata/tests/apiv2/test_datasets.py +290 -198
  326. udata/tests/apiv2/test_me_api.py +10 -11
  327. udata/tests/apiv2/test_organizations.py +56 -74
  328. udata/tests/apiv2/test_swagger.py +5 -5
  329. udata/tests/apiv2/test_topics.py +69 -87
  330. udata/tests/cli/test_cli_base.py +8 -8
  331. udata/tests/cli/test_db_cli.py +21 -19
  332. udata/tests/dataservice/test_dataservice_tasks.py +8 -12
  333. udata/tests/dataset/test_csv_adapter.py +44 -35
  334. udata/tests/dataset/test_dataset_actions.py +2 -3
  335. udata/tests/dataset/test_dataset_commands.py +7 -8
  336. udata/tests/dataset/test_dataset_events.py +36 -29
  337. udata/tests/dataset/test_dataset_model.py +224 -217
  338. udata/tests/dataset/test_dataset_rdf.py +142 -131
  339. udata/tests/dataset/test_dataset_tasks.py +15 -15
  340. udata/tests/dataset/test_resource_preview.py +10 -13
  341. udata/tests/features/territories/__init__.py +9 -13
  342. udata/tests/features/territories/test_territories_api.py +71 -91
  343. udata/tests/forms/test_basic_fields.py +7 -7
  344. udata/tests/forms/test_current_user_field.py +39 -66
  345. udata/tests/forms/test_daterange_field.py +31 -39
  346. udata/tests/forms/test_dict_field.py +28 -26
  347. udata/tests/forms/test_extras_fields.py +102 -76
  348. udata/tests/forms/test_form_field.py +8 -8
  349. udata/tests/forms/test_image_field.py +33 -26
  350. udata/tests/forms/test_model_field.py +134 -123
  351. udata/tests/forms/test_model_list_field.py +7 -7
  352. udata/tests/forms/test_nested_model_list_field.py +117 -79
  353. udata/tests/forms/test_publish_as_field.py +36 -65
  354. udata/tests/forms/test_reference_field.py +34 -53
  355. udata/tests/forms/test_user_forms.py +23 -21
  356. udata/tests/forms/test_uuid_field.py +6 -10
  357. udata/tests/frontend/__init__.py +9 -6
  358. udata/tests/frontend/test_auth.py +7 -6
  359. udata/tests/frontend/test_csv.py +81 -96
  360. udata/tests/frontend/test_hooks.py +43 -43
  361. udata/tests/frontend/test_markdown.py +211 -191
  362. udata/tests/helpers.py +32 -37
  363. udata/tests/models.py +2 -2
  364. udata/tests/organization/test_csv_adapter.py +21 -16
  365. udata/tests/organization/test_notifications.py +11 -18
  366. udata/tests/organization/test_organization_model.py +13 -13
  367. udata/tests/organization/test_organization_rdf.py +29 -22
  368. udata/tests/organization/test_organization_tasks.py +16 -17
  369. udata/tests/plugin.py +79 -73
  370. udata/tests/reuse/test_reuse_model.py +21 -21
  371. udata/tests/reuse/test_reuse_task.py +11 -13
  372. udata/tests/search/__init__.py +11 -12
  373. udata/tests/search/test_adapter.py +60 -70
  374. udata/tests/search/test_query.py +16 -16
  375. udata/tests/search/test_results.py +10 -7
  376. udata/tests/site/test_site_api.py +11 -16
  377. udata/tests/site/test_site_metrics.py +20 -30
  378. udata/tests/site/test_site_model.py +4 -5
  379. udata/tests/site/test_site_rdf.py +94 -78
  380. udata/tests/test_activity.py +17 -17
  381. udata/tests/test_cors.py +62 -0
  382. udata/tests/test_discussions.py +292 -299
  383. udata/tests/test_i18n.py +37 -40
  384. udata/tests/test_linkchecker.py +91 -85
  385. udata/tests/test_mail.py +13 -17
  386. udata/tests/test_migrations.py +219 -180
  387. udata/tests/test_model.py +164 -157
  388. udata/tests/test_notifications.py +17 -17
  389. udata/tests/test_owned.py +14 -14
  390. udata/tests/test_rdf.py +25 -23
  391. udata/tests/test_routing.py +89 -93
  392. udata/tests/test_storages.py +137 -128
  393. udata/tests/test_tags.py +44 -46
  394. udata/tests/test_topics.py +7 -7
  395. udata/tests/test_transfer.py +42 -49
  396. udata/tests/test_uris.py +160 -161
  397. udata/tests/test_utils.py +79 -71
  398. udata/tests/user/test_user_rdf.py +5 -9
  399. udata/tests/workers/test_jobs_commands.py +57 -58
  400. udata/tests/workers/test_tasks_routing.py +23 -29
  401. udata/tests/workers/test_workers_api.py +125 -131
  402. udata/tests/workers/test_workers_helpers.py +6 -6
  403. udata/tracking.py +4 -6
  404. udata/uris.py +45 -46
  405. udata/utils.py +68 -66
  406. udata/wsgi.py +1 -1
  407. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/METADATA +7 -3
  408. udata-9.1.2.dev30454.dist-info/RECORD +706 -0
  409. udata-9.1.2.dev30355.dist-info/RECORD +0 -704
  410. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/LICENSE +0 -0
  411. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/WHEEL +0 -0
  412. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/entry_points.txt +0 -0
  413. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/top_level.txt +0 -0
udata/core/site/models.py CHANGED
@@ -1,13 +1,12 @@
1
- from flask import g, current_app
1
+ from flask import current_app, g
2
2
  from werkzeug.local import LocalProxy
3
3
 
4
- from udata.models import db, WithMetrics
5
- from udata.core.organization.models import Organization
6
4
  from udata.core.dataset.models import Dataset
5
+ from udata.core.organization.models import Organization
7
6
  from udata.core.reuse.models import Reuse
7
+ from udata.models import WithMetrics, db
8
8
 
9
-
10
- __all__ = ('Site', 'SiteSettings')
9
+ __all__ = ("Site", "SiteSettings")
11
10
 
12
11
 
13
12
  DEFAULT_FEED_SIZE = 20
@@ -28,126 +27,158 @@ class Site(WithMetrics, db.Document):
28
27
  settings = db.EmbeddedDocumentField(SiteSettings, default=SiteSettings)
29
28
 
30
29
  __metrics_keys__ = [
31
- 'max_dataset_followers',
32
- 'max_dataset_reuses',
33
- 'max_reuse_datasets',
34
- 'max_reuse_followers',
35
- 'max_org_followers',
36
- 'max_org_reuses',
37
- 'max_org_datasets',
38
- 'datasets',
39
- 'discussions',
40
- 'followers',
41
- 'organizations',
42
- 'public-service',
43
- 'resources',
44
- 'reuses',
45
- 'users',
46
- 'harvesters'
30
+ "max_dataset_followers",
31
+ "max_dataset_reuses",
32
+ "max_reuse_datasets",
33
+ "max_reuse_followers",
34
+ "max_org_followers",
35
+ "max_org_reuses",
36
+ "max_org_datasets",
37
+ "datasets",
38
+ "discussions",
39
+ "followers",
40
+ "organizations",
41
+ "public-service",
42
+ "resources",
43
+ "reuses",
44
+ "users",
45
+ "harvesters",
47
46
  ]
48
47
 
49
48
  def __str__(self):
50
- return self.title or ''
49
+ return self.title or ""
51
50
 
52
51
  def count_users(self):
53
52
  from udata.models import User
54
- self.metrics['users'] = User.objects(confirmed_at__ne=None, deleted=None).count()
53
+
54
+ self.metrics["users"] = User.objects(confirmed_at__ne=None, deleted=None).count()
55
55
  self.save()
56
56
 
57
57
  def count_org(self):
58
58
  from udata.models import Organization
59
- self.metrics['organizations'] = Organization.objects.visible().count()
59
+
60
+ self.metrics["organizations"] = Organization.objects.visible().count()
60
61
  self.save()
61
62
 
62
63
  def count_org_for_badge(self, badge_kind):
63
64
  from udata.models import Organization
65
+
64
66
  self.metrics[badge_kind] = Organization.objects(badges__kind=badge_kind).count()
65
67
  self.save()
66
68
 
67
69
  def count_datasets(self):
68
70
  from udata.models import Dataset
69
- self.metrics['datasets'] = Dataset.objects.visible().count()
71
+
72
+ self.metrics["datasets"] = Dataset.objects.visible().count()
70
73
  self.save()
71
74
 
72
75
  def count_resources(self):
73
- self.metrics['resources'] = next(Dataset.objects.visible().aggregate(
74
- {'$project': {'resources': 1}},
75
- {'$unwind': '$resources'},
76
- {'$group': {'_id': 'result', 'count': {'$sum': 1}}}
77
- ), {}).get('count', 0)
76
+ self.metrics["resources"] = next(
77
+ Dataset.objects.visible().aggregate(
78
+ {"$project": {"resources": 1}},
79
+ {"$unwind": "$resources"},
80
+ {"$group": {"_id": "result", "count": {"$sum": 1}}},
81
+ ),
82
+ {},
83
+ ).get("count", 0)
78
84
  self.save()
79
85
 
80
86
  def count_reuses(self):
81
- self.metrics['reuses'] = Reuse.objects.visible().count()
87
+ self.metrics["reuses"] = Reuse.objects.visible().count()
82
88
  self.save()
83
89
 
84
90
  def count_followers(self):
85
91
  from udata.models import Follow
86
- self.metrics['followers'] = Follow.objects(until=None).count()
92
+
93
+ self.metrics["followers"] = Follow.objects(until=None).count()
87
94
  self.save()
88
95
 
89
96
  def count_discussions(self):
90
97
  from udata.models import Discussion
91
- self.metrics['discussions'] = Discussion.objects.count()
98
+
99
+ self.metrics["discussions"] = Discussion.objects.count()
92
100
  self.save()
93
101
 
94
102
  def count_harvesters(self):
95
103
  from udata.harvest.models import HarvestSource
96
- self.metrics['harvesters'] = HarvestSource.objects().count()
104
+
105
+ self.metrics["harvesters"] = HarvestSource.objects().count()
97
106
  self.save()
98
107
 
99
108
  def count_max_dataset_followers(self):
100
- dataset = (Dataset.objects(metrics__followers__gt=0).visible()
101
- .order_by('-metrics.followers').first())
102
- self.metrics['max_dataset_followers'] = dataset.metrics['followers'] if dataset else 0
109
+ dataset = (
110
+ Dataset.objects(metrics__followers__gt=0)
111
+ .visible()
112
+ .order_by("-metrics.followers")
113
+ .first()
114
+ )
115
+ self.metrics["max_dataset_followers"] = dataset.metrics["followers"] if dataset else 0
103
116
  self.save()
104
117
 
105
118
  def count_max_dataset_reuses(self):
106
- dataset = (Dataset.objects(metrics__reuses__gt=0).visible()
107
- .order_by('-metrics.reuses').first())
108
- self.metrics['max_dataset_reuses'] = dataset.metrics['reuses'] if dataset else 0
119
+ dataset = (
120
+ Dataset.objects(metrics__reuses__gt=0).visible().order_by("-metrics.reuses").first()
121
+ )
122
+ self.metrics["max_dataset_reuses"] = dataset.metrics["reuses"] if dataset else 0
109
123
  self.save()
110
124
 
111
125
  def count_max_reuse_datasets(self):
112
- reuse = (Reuse.objects(metrics__datasets__gt=0).visible()
113
- .order_by('-metrics.datasets').first())
114
- self.metrics['max_reuse_datasets'] = reuse.metrics['datasets'] if reuse else 0
126
+ reuse = (
127
+ Reuse.objects(metrics__datasets__gt=0).visible().order_by("-metrics.datasets").first()
128
+ )
129
+ self.metrics["max_reuse_datasets"] = reuse.metrics["datasets"] if reuse else 0
115
130
  self.save()
116
131
 
117
132
  def count_max_reuse_followers(self):
118
- reuse = (Reuse.objects(metrics__followers__gt=0).visible()
119
- .order_by('-metrics.followers').first())
120
- self.metrics['max_reuse_followers'] = reuse.metrics['followers'] if reuse else 0
133
+ reuse = (
134
+ Reuse.objects(metrics__followers__gt=0).visible().order_by("-metrics.followers").first()
135
+ )
136
+ self.metrics["max_reuse_followers"] = reuse.metrics["followers"] if reuse else 0
121
137
  self.save()
122
138
 
123
139
  def count_max_org_followers(self):
124
- org = (Organization.objects(metrics__followers__gt=0).visible()
125
- .order_by('-metrics.followers').first())
126
- self.metrics['max_org_followers'] = org.metrics['followers'] if org else 0
140
+ org = (
141
+ Organization.objects(metrics__followers__gt=0)
142
+ .visible()
143
+ .order_by("-metrics.followers")
144
+ .first()
145
+ )
146
+ self.metrics["max_org_followers"] = org.metrics["followers"] if org else 0
127
147
  self.save()
128
148
 
129
149
  def count_max_org_reuses(self):
130
- org = (Organization.objects(metrics__reuses__gt=0).visible()
131
- .order_by('-metrics.reuses').first())
132
- self.metrics['max_org_reuses'] = org.metrics['reuses'] if org else 0
150
+ org = (
151
+ Organization.objects(metrics__reuses__gt=0)
152
+ .visible()
153
+ .order_by("-metrics.reuses")
154
+ .first()
155
+ )
156
+ self.metrics["max_org_reuses"] = org.metrics["reuses"] if org else 0
133
157
  self.save()
134
158
 
135
159
  def count_max_org_datasets(self):
136
- org = (Organization.objects(metrics__datasets__gt=0).visible()
137
- .order_by('-metrics.datasets').first())
138
- self.metrics['max_org_datasets'] = org.metrics['datasets'] if org else 0
160
+ org = (
161
+ Organization.objects(metrics__datasets__gt=0)
162
+ .visible()
163
+ .order_by("-metrics.datasets")
164
+ .first()
165
+ )
166
+ self.metrics["max_org_datasets"] = org.metrics["datasets"] if org else 0
139
167
  self.save()
140
168
 
141
169
 
142
170
  def get_current_site():
143
- if getattr(g, 'site', None) is None:
144
- site_id = current_app.config['SITE_ID']
145
- site_title = current_app.config.get('SITE_TITLE')
146
- site_keywords = current_app.config.get('SITE_KEYWORDS', [])
147
- g.site, _ = Site.objects.get_or_create(id=site_id, defaults={
148
- 'title': site_title,
149
- 'keywords': site_keywords,
150
- })
171
+ if getattr(g, "site", None) is None:
172
+ site_id = current_app.config["SITE_ID"]
173
+ site_title = current_app.config.get("SITE_TITLE")
174
+ site_keywords = current_app.config.get("SITE_KEYWORDS", [])
175
+ g.site, _ = Site.objects.get_or_create(
176
+ id=site_id,
177
+ defaults={
178
+ "title": site_title,
179
+ "keywords": site_keywords,
180
+ },
181
+ )
151
182
  if g.site.title != site_title:
152
183
  Site.objects(id=site_id).modify(set__title=site_title)
153
184
  if g.site.keywords != site_keywords:
udata/core/site/rdf.py CHANGED
@@ -1,36 +1,36 @@
1
- '''
1
+ """
2
2
  This module centralize site helpers for RDF/DCAT serialization and parsing
3
- '''
4
- from flask import url_for, current_app
5
- from rdflib import Graph, URIRef, Literal, BNode
6
- from rdflib.namespace import RDF, FOAF
3
+ """
4
+
5
+ from flask import current_app, url_for
6
+ from rdflib import BNode, Graph, Literal, URIRef
7
+ from rdflib.namespace import FOAF, RDF
7
8
 
8
9
  from udata.core.dataservices.rdf import dataservice_to_rdf
9
10
  from udata.core.dataset.rdf import dataset_to_rdf
10
11
  from udata.core.organization.rdf import organization_to_rdf
11
12
  from udata.core.user.rdf import user_to_rdf
12
13
  from udata.rdf import DCAT, DCT, namespace_manager, paginate_catalog
13
- from udata.utils import Paginable
14
14
  from udata.uris import endpoint_for
15
+ from udata.utils import Paginable
15
16
 
16
17
 
17
- def build_catalog(site, datasets, dataservices = [], format=None):
18
- '''Build the DCAT catalog for this site'''
19
- site_url = endpoint_for('site.home_redirect', 'api.site', _external=True)
20
- catalog_url = url_for('api.site_rdf_catalog', _external=True)
18
+ def build_catalog(site, datasets, dataservices=[], format=None):
19
+ """Build the DCAT catalog for this site"""
20
+ site_url = endpoint_for("site.home_redirect", "api.site", _external=True)
21
+ catalog_url = url_for("api.site_rdf_catalog", _external=True)
21
22
  graph = Graph(namespace_manager=namespace_manager)
22
23
  catalog = graph.resource(URIRef(catalog_url))
23
24
 
24
25
  catalog.set(RDF.type, DCAT.Catalog)
25
26
  catalog.set(DCT.title, Literal(site.title))
26
27
  catalog.set(DCT.description, Literal(f"{site.title}"))
27
- catalog.set(DCT.language,
28
- Literal(current_app.config['DEFAULT_LANGUAGE']))
28
+ catalog.set(DCT.language, Literal(current_app.config["DEFAULT_LANGUAGE"]))
29
29
  catalog.set(FOAF.homepage, URIRef(site_url))
30
30
 
31
31
  publisher = graph.resource(BNode())
32
32
  publisher.set(RDF.type, FOAF.Organization)
33
- publisher.set(FOAF.name, Literal(current_app.config['SITE_AUTHOR']))
33
+ publisher.set(FOAF.name, Literal(current_app.config["SITE_AUTHOR"]))
34
34
  catalog.set(DCT.publisher, publisher)
35
35
 
36
36
  for dataset in datasets:
@@ -46,6 +46,6 @@ def build_catalog(site, datasets, dataservices = [], format=None):
46
46
  catalog.add(DCAT.DataService, rdf_dataservice)
47
47
 
48
48
  if isinstance(datasets, Paginable):
49
- paginate_catalog(catalog, graph, datasets, format, 'api.site_rdf_catalog_format')
49
+ paginate_catalog(catalog, graph, datasets, format, "api.site_rdf_catalog_format")
50
50
 
51
51
  return catalog
udata/core/spam/api.py CHANGED
@@ -1,10 +1,10 @@
1
1
  from mongoengine import Q
2
2
 
3
- from udata.api import api, API
3
+ from udata.api import API, api
4
4
  from udata.auth import admin_permission
5
5
  from udata.core.discussions.models import Discussion
6
- from udata.core.spam.fields import potential_spam_fields
7
6
  from udata.core.spam.constants import POTENTIAL_SPAM
7
+ from udata.core.spam.fields import potential_spam_fields
8
8
  from udata.utils import id_or_404
9
9
 
10
10
 
@@ -12,6 +12,7 @@ class SpamAPIMixin(API):
12
12
  """
13
13
  Base Spam Model API.
14
14
  """
15
+
15
16
  model = None
16
17
 
17
18
  def get_model(self, id):
@@ -37,21 +38,27 @@ class SpamAPIMixin(API):
37
38
  return {}, 200
38
39
 
39
40
 
40
- ns = api.namespace('spam', 'Spam related operations')
41
+ ns = api.namespace("spam", "Spam related operations")
41
42
 
42
43
 
43
- @ns.route('/', endpoint='spam')
44
+ @ns.route("/", endpoint="spam")
44
45
  class SpamAPI(API):
45
46
  """
46
47
  Base class for a discussion thread.
47
48
  """
48
- @api.doc('get_potential_spams')
49
+
50
+ @api.doc("get_potential_spams")
49
51
  @api.secure(admin_permission)
50
52
  @api.marshal_with(potential_spam_fields)
51
53
  def get(self):
52
54
  """Get all potential spam objects"""
53
- discussions = Discussion.objects(Q(spam__status=POTENTIAL_SPAM) | Q(discussion__spam__status=POTENTIAL_SPAM))
55
+ discussions = Discussion.objects(
56
+ Q(spam__status=POTENTIAL_SPAM) | Q(discussion__spam__status=POTENTIAL_SPAM)
57
+ )
54
58
 
55
- return [{
56
- 'message': discussion.spam_report_message([discussion]),
57
- } for discussion in discussions]
59
+ return [
60
+ {
61
+ "message": discussion.spam_report_message([discussion]),
62
+ }
63
+ for discussion in discussions
64
+ ]
@@ -1,5 +1,5 @@
1
- NOT_CHECKED = 'not_checked'
2
- POTENTIAL_SPAM = 'potential_spam'
3
- NO_SPAM = 'no_spam'
1
+ NOT_CHECKED = "not_checked"
2
+ POTENTIAL_SPAM = "potential_spam"
3
+ NO_SPAM = "no_spam"
4
4
 
5
- SPAM_STATUS_CHOICES = [NOT_CHECKED, POTENTIAL_SPAM, NO_SPAM]
5
+ SPAM_STATUS_CHOICES = [NOT_CHECKED, POTENTIAL_SPAM, NO_SPAM]
udata/core/spam/fields.py CHANGED
@@ -1,11 +1,17 @@
1
1
  from udata.api import api, fields
2
- from .constants import SPAM_STATUS_CHOICES
3
2
 
4
- spam_fields = api.model('Spam', {
5
- 'status': fields.String(description='Status', enum=SPAM_STATUS_CHOICES, readonly=True),
6
- })
3
+ from .constants import SPAM_STATUS_CHOICES
7
4
 
8
- potential_spam_fields = api.model('PotentialSpam', {
9
- 'message': fields.String(readonly=True),
10
- })
5
+ spam_fields = api.model(
6
+ "Spam",
7
+ {
8
+ "status": fields.String(description="Status", enum=SPAM_STATUS_CHOICES, readonly=True),
9
+ },
10
+ )
11
11
 
12
+ potential_spam_fields = api.model(
13
+ "PotentialSpam",
14
+ {
15
+ "message": fields.String(readonly=True),
16
+ },
17
+ )
udata/core/spam/models.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from flask import current_app
2
2
  from langdetect import detect
3
+ from mongoengine import signals
3
4
 
4
5
  from udata.mongo import db
5
- from .signals import on_new_potential_spam
6
- from mongoengine import signals
7
6
 
8
- from .constants import NO_SPAM, POTENTIAL_SPAM, SPAM_STATUS_CHOICES, NOT_CHECKED
7
+ from .constants import NO_SPAM, NOT_CHECKED, POTENTIAL_SPAM, SPAM_STATUS_CHOICES
8
+ from .signals import on_new_potential_spam
9
9
 
10
10
 
11
11
  class SpamInfo(db.EmbeddedDocument):
@@ -28,16 +28,15 @@ class SpamMixin(object):
28
28
 
29
29
  @staticmethod
30
30
  def spam_words():
31
- return current_app.config.get('SPAM_WORDS', [])
31
+ return current_app.config.get("SPAM_WORDS", [])
32
32
 
33
33
  @staticmethod
34
34
  def allowed_langs():
35
- return current_app.config.get('SPAM_ALLOWED_LANGS', [])
36
-
35
+ return current_app.config.get("SPAM_ALLOWED_LANGS", [])
36
+
37
37
  @staticmethod
38
38
  def minimum_string_length_for_lang_check():
39
- return current_app.config.get('SPAM_MINIMUM_STRING_LENGTH_FOR_LANG_CHECK', 30)
40
-
39
+ return current_app.config.get("SPAM_MINIMUM_STRING_LENGTH_FOR_LANG_CHECK", 30)
41
40
 
42
41
  def clean(self):
43
42
  super().clean()
@@ -91,15 +90,22 @@ class SpamMixin(object):
91
90
  for word in SpamMixin.spam_words():
92
91
  if word in text.lower():
93
92
  self.spam.status = POTENTIAL_SPAM
94
- self._report(text=text, breadcrumb=breadcrumb, reason=f"contains spam words \"{word}\"")
93
+ self._report(
94
+ text=text, breadcrumb=breadcrumb, reason=f'contains spam words "{word}"'
95
+ )
95
96
  return
96
97
 
97
98
  # Language detection is not working well with texts of a few words.
98
- if SpamMixin.allowed_langs() and len(text) > SpamMixin.minimum_string_length_for_lang_check():
99
+ if (
100
+ SpamMixin.allowed_langs()
101
+ and len(text) > SpamMixin.minimum_string_length_for_lang_check()
102
+ ):
99
103
  lang = detect(text.lower())
100
104
  if lang not in SpamMixin.allowed_langs():
101
105
  self.spam.status = POTENTIAL_SPAM
102
- self._report(text=text, breadcrumb=breadcrumb, reason=f"not allowed language \"{lang}\"")
106
+ self._report(
107
+ text=text, breadcrumb=breadcrumb, reason=f'not allowed language "{lang}"'
108
+ )
103
109
  return
104
110
 
105
111
  for embed in self.embeds_to_check_for_spam():
@@ -130,19 +136,20 @@ class SpamMixin(object):
130
136
 
131
137
  for name, args in callbacks.items():
132
138
  callback = getattr(base_model, name)
133
- callback(*args['args'], **args['kwargs'])
139
+ callback(*args["args"], **args["kwargs"])
134
140
 
135
141
  def is_spam(self):
136
142
  return self.spam and self.spam.status == POTENTIAL_SPAM
137
143
 
138
144
  def texts_to_check_for_spam(self):
139
145
  raise NotImplementedError(
140
- "Please implement the `texts_to_check_for_spam` method. Should return a list of strings to check.")
146
+ "Please implement the `texts_to_check_for_spam` method. Should return a list of strings to check."
147
+ )
141
148
 
142
149
  def embeds_to_check_for_spam(self):
143
150
  return []
144
151
 
145
- def spam_is_whitelisted(self) -> bool :
152
+ def spam_is_whitelisted(self) -> bool:
146
153
  return False
147
154
 
148
155
  def spam_report_message(self):
@@ -177,7 +184,7 @@ def spam_protected(get_model_to_check=None):
177
184
  It will save the class method called with its arguments inside the `SpamInfo` object to be
178
185
  called later if needed (in case of false positive).
179
186
  The decorator accept an argument, a function to get the model to check when we are doing an operation
180
- on an embed document. The class method should always take a `self` as a first argument which is the base
187
+ on an embed document. The class method should always take a `self` as a first argument which is the base
181
188
  model to allow saving the callbacks back into Mongo (we cannot .save() an embed document).
182
189
  """
183
190
 
@@ -191,13 +198,13 @@ def spam_protected(get_model_to_check=None):
191
198
 
192
199
  if not isinstance(model_to_check, SpamMixin):
193
200
  raise ValueError(
194
- "@spam_protected should be called within a SpamMixin. " + type(model_to_check).__name__ + " given.")
201
+ "@spam_protected should be called within a SpamMixin. "
202
+ + type(model_to_check).__name__
203
+ + " given."
204
+ )
195
205
 
196
206
  if model_to_check.is_spam():
197
- model_to_check.spam.callbacks[f.__name__] = {
198
- 'args': args[1:],
199
- 'kwargs': kwargs
200
- }
207
+ model_to_check.spam.callbacks[f.__name__] = {"args": args[1:], "kwargs": kwargs}
201
208
  base_model.save_without_spam_detection()
202
209
  else:
203
210
  f(*args, **kwargs)
@@ -3,4 +3,4 @@ from blinker import Namespace
3
3
  namespace = Namespace()
4
4
 
5
5
  #: Triggered when a spam is detected
6
- on_new_potential_spam = namespace.signal('on-new-potential-spam')
6
+ on_new_potential_spam = namespace.signal("on-new-potential-spam")
@@ -1,14 +1,16 @@
1
1
  import logging
2
+
2
3
  import pytest
3
- from udata.tests import TestCase
4
+
4
5
  from udata.mongo import db
6
+ from udata.tests import TestCase
5
7
 
6
- from ..models import SpamMixin
7
8
  from ..constants import POTENTIAL_SPAM
8
-
9
+ from ..models import SpamMixin
9
10
 
10
11
  log = logging.getLogger(__name__)
11
12
 
13
+
12
14
  class TestModel(SpamMixin, db.Document):
13
15
  text = db.StringField(required=True)
14
16
  _created = True
@@ -18,9 +20,8 @@ class TestModel(SpamMixin, db.Document):
18
20
 
19
21
 
20
22
  class SpamTest(TestCase):
21
- @pytest.mark.options(SPAM_WORDS=['spam'], SPAM_ALLOWED_LANGS=['fr'])
23
+ @pytest.mark.options(SPAM_WORDS=["spam"], SPAM_ALLOWED_LANGS=["fr"])
22
24
  def test_uppercase_lang_detect(self):
23
25
  model = TestModel(text="DONNEES DE RECENSEMENT - MARCHES PUBLICS")
24
26
  model.detect_spam()
25
27
  self.assertNotEqual(model.spam.status, POTENTIAL_SPAM)
26
-