seshat 26.7__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.
Files changed (263) hide show
  1. seshat/__init__.py +0 -0
  2. seshat/config.py +9 -0
  3. seshat/ui.py +133 -0
  4. seshat/utils/constants.py +63 -0
  5. seshat/utils/convenience.py +364 -0
  6. seshat/utils/rdf.py +273 -0
  7. seshat/web/cache.py +123 -0
  8. seshat/web/config.py +118 -0
  9. seshat/web/database.py +3835 -0
  10. seshat/web/email_handler.py +84 -0
  11. seshat/web/formatter.py +783 -0
  12. seshat/web/locks.py +70 -0
  13. seshat/web/resources/html_templates/400.html +5 -0
  14. seshat/web/resources/html_templates/403.html +5 -0
  15. seshat/web/resources/html_templates/404.html +5 -0
  16. seshat/web/resources/html_templates/410.html +5 -0
  17. seshat/web/resources/html_templates/500.html +6 -0
  18. seshat/web/resources/html_templates/502.html +6 -0
  19. seshat/web/resources/html_templates/503.html +7 -0
  20. seshat/web/resources/html_templates/activate_2fa_session.html +15 -0
  21. seshat/web/resources/html_templates/admin/dashboard.html +29 -0
  22. seshat/web/resources/html_templates/admin/exploratory.html +67 -0
  23. seshat/web/resources/html_templates/admin/quota_requests.html +56 -0
  24. seshat/web/resources/html_templates/admin/reports/dashboard.html +55 -0
  25. seshat/web/resources/html_templates/admin/reports/embargoed_datasets.html +69 -0
  26. seshat/web/resources/html_templates/admin/reports/restricted_datasets.html +64 -0
  27. seshat/web/resources/html_templates/admin/sparql.html +37 -0
  28. seshat/web/resources/html_templates/admin/users.html +72 -0
  29. seshat/web/resources/html_templates/author.html +116 -0
  30. seshat/web/resources/html_templates/badge.svg +46 -0
  31. seshat/web/resources/html_templates/categories.html +76 -0
  32. seshat/web/resources/html_templates/category.html +21 -0
  33. seshat/web/resources/html_templates/collection.html +26 -0
  34. seshat/web/resources/html_templates/colors.css +57 -0
  35. seshat/web/resources/html_templates/dataset.html +127 -0
  36. seshat/web/resources/html_templates/dataset_access_request.html +21 -0
  37. seshat/web/resources/html_templates/depositor/dashboard.html +116 -0
  38. seshat/web/resources/html_templates/depositor/edit-collection.html +230 -0
  39. seshat/web/resources/html_templates/depositor/edit-dataset.html +560 -0
  40. seshat/web/resources/html_templates/depositor/edit-session.html +81 -0
  41. seshat/web/resources/html_templates/depositor/language-selector.html +92 -0
  42. seshat/web/resources/html_templates/depositor/my-collections.html +89 -0
  43. seshat/web/resources/html_templates/depositor/my-data.html +113 -0
  44. seshat/web/resources/html_templates/depositor/new_private_link.html +52 -0
  45. seshat/web/resources/html_templates/depositor/private-links.html +48 -0
  46. seshat/web/resources/html_templates/depositor/profile.html +135 -0
  47. seshat/web/resources/html_templates/depositor/published-collection.html +15 -0
  48. seshat/web/resources/html_templates/depositor/submitted-for-review.html +15 -0
  49. seshat/web/resources/html_templates/email/2fa_token.html +8 -0
  50. seshat/web/resources/html_templates/email/2fa_token.txt +7 -0
  51. seshat/web/resources/html_templates/email/data_access_request.html +13 -0
  52. seshat/web/resources/html_templates/email/data_access_request.txt +14 -0
  53. seshat/web/resources/html_templates/email/dataset_approved.html +18 -0
  54. seshat/web/resources/html_templates/email/dataset_approved.txt +22 -0
  55. seshat/web/resources/html_templates/email/dataset_declined.html +8 -0
  56. seshat/web/resources/html_templates/email/dataset_declined.txt +8 -0
  57. seshat/web/resources/html_templates/email/dataset_submitted.html +8 -0
  58. seshat/web/resources/html_templates/email/dataset_submitted.txt +7 -0
  59. seshat/web/resources/html_templates/email/declined_dataset_notification.html +10 -0
  60. seshat/web/resources/html_templates/email/declined_dataset_notification.txt +6 -0
  61. seshat/web/resources/html_templates/email/feedback.html +10 -0
  62. seshat/web/resources/html_templates/email/feedback.txt +10 -0
  63. seshat/web/resources/html_templates/email/layout.html +12 -0
  64. seshat/web/resources/html_templates/email/published_dataset_notification.html +10 -0
  65. seshat/web/resources/html_templates/email/published_dataset_notification.txt +6 -0
  66. seshat/web/resources/html_templates/email/quota_approved.html +8 -0
  67. seshat/web/resources/html_templates/email/quota_approved.txt +9 -0
  68. seshat/web/resources/html_templates/email/quota_request.html +10 -0
  69. seshat/web/resources/html_templates/email/quota_request.txt +9 -0
  70. seshat/web/resources/html_templates/email/submitted_for_review_notification.html +8 -0
  71. seshat/web/resources/html_templates/email/submitted_for_review_notification.txt +10 -0
  72. seshat/web/resources/html_templates/feedback.html +53 -0
  73. seshat/web/resources/html_templates/git_instructions.html +7 -0
  74. seshat/web/resources/html_templates/institutions.html +58 -0
  75. seshat/web/resources/html_templates/jsonld.html +73 -0
  76. seshat/web/resources/html_templates/layout.html +123 -0
  77. seshat/web/resources/html_templates/loader.svg +51 -0
  78. seshat/web/resources/html_templates/maintenance.html +5 -0
  79. seshat/web/resources/html_templates/portal.html +62 -0
  80. seshat/web/resources/html_templates/private_link_is_expired.html +5 -0
  81. seshat/web/resources/html_templates/public_metadata.html +310 -0
  82. seshat/web/resources/html_templates/review/overview.html +102 -0
  83. seshat/web/resources/html_templates/review/published.html +14 -0
  84. seshat/web/resources/html_templates/search.html +322 -0
  85. seshat/web/resources/html_templates/sitemap_template.xml +11 -0
  86. seshat/web/resources/languages.json +91 -0
  87. seshat/web/resources/licenses.json +156 -0
  88. seshat/web/resources/metadata_templates/bibtex.bib +10 -0
  89. seshat/web/resources/metadata_templates/citation.cff +21 -0
  90. seshat/web/resources/metadata_templates/endnote.enw +18 -0
  91. seshat/web/resources/metadata_templates/refman.ris +19 -0
  92. seshat/web/resources/root_categories.json +296 -0
  93. seshat/web/resources/sparql_templates/account_by_email.sparql +35 -0
  94. seshat/web/resources/sparql_templates/account_by_session_token.sparql +44 -0
  95. seshat/web/resources/sparql_templates/account_categories.sparql +28 -0
  96. seshat/web/resources/sparql_templates/account_sessions.sparql +26 -0
  97. seshat/web/resources/sparql_templates/account_storage_used.sparql +23 -0
  98. seshat/web/resources/sparql_templates/account_uuid_by_orcid.sparql +14 -0
  99. seshat/web/resources/sparql_templates/accounts.sparql +67 -0
  100. seshat/web/resources/sparql_templates/append_to_list.sparql +19 -0
  101. seshat/web/resources/sparql_templates/associated_authors.sparql +16 -0
  102. seshat/web/resources/sparql_templates/author_profile.sparql +34 -0
  103. seshat/web/resources/sparql_templates/author_public_items.sparql +34 -0
  104. seshat/web/resources/sparql_templates/authors.sparql +84 -0
  105. seshat/web/resources/sparql_templates/categories.sparql +62 -0
  106. seshat/web/resources/sparql_templates/categories_tree.sparql +17 -0
  107. seshat/web/resources/sparql_templates/category_by_id.sparql +21 -0
  108. seshat/web/resources/sparql_templates/collaborators.sparql +75 -0
  109. seshat/web/resources/sparql_templates/collection_dataset_containers.sparql +8 -0
  110. seshat/web/resources/sparql_templates/collection_datasets.sparql +19 -0
  111. seshat/web/resources/sparql_templates/collection_datasets_count.sparql +11 -0
  112. seshat/web/resources/sparql_templates/collection_versions.sparql +20 -0
  113. seshat/web/resources/sparql_templates/collections.sparql +107 -0
  114. seshat/web/resources/sparql_templates/collections_by_account.sparql +18 -0
  115. seshat/web/resources/sparql_templates/collections_from_dataset.sparql +18 -0
  116. seshat/web/resources/sparql_templates/contact_info_from_container.sparql +14 -0
  117. seshat/web/resources/sparql_templates/container.sparql +28 -0
  118. seshat/web/resources/sparql_templates/container_items.sparql +58 -0
  119. seshat/web/resources/sparql_templates/container_uuid_by_id.sparql +13 -0
  120. seshat/web/resources/sparql_templates/custom_fields.sparql +33 -0
  121. seshat/web/resources/sparql_templates/dataset_files.sparql +90 -0
  122. seshat/web/resources/sparql_templates/dataset_is_under_review.sparql +11 -0
  123. seshat/web/resources/sparql_templates/dataset_statistics.sparql +26 -0
  124. seshat/web/resources/sparql_templates/dataset_statistics_timeline.sparql +21 -0
  125. seshat/web/resources/sparql_templates/dataset_storage_used.sparql +12 -0
  126. seshat/web/resources/sparql_templates/dataset_versions.sparql +23 -0
  127. seshat/web/resources/sparql_templates/datasets.sparql +238 -0
  128. seshat/web/resources/sparql_templates/decline_draft_dataset.sparql +28 -0
  129. seshat/web/resources/sparql_templates/delete_account_groups_associations.sparql +15 -0
  130. seshat/web/resources/sparql_templates/delete_account_property.sparql +15 -0
  131. seshat/web/resources/sparql_templates/delete_associations.sparql +40 -0
  132. seshat/web/resources/sparql_templates/delete_collaborator.sparql +29 -0
  133. seshat/web/resources/sparql_templates/delete_collection_draft.sparql +29 -0
  134. seshat/web/resources/sparql_templates/delete_dataset_draft.sparql +39 -0
  135. seshat/web/resources/sparql_templates/delete_dataset_embargo.sparql +23 -0
  136. seshat/web/resources/sparql_templates/delete_group_members.sparql +21 -0
  137. seshat/web/resources/sparql_templates/delete_inactive_session_by_uuid.sparql +17 -0
  138. seshat/web/resources/sparql_templates/delete_inferred_groups.sparql +21 -0
  139. seshat/web/resources/sparql_templates/delete_item_from_list.sparql +30 -0
  140. seshat/web/resources/sparql_templates/delete_items_all_from_list.sparql +24 -0
  141. seshat/web/resources/sparql_templates/delete_private_links.sparql +25 -0
  142. seshat/web/resources/sparql_templates/delete_session.sparql +17 -0
  143. seshat/web/resources/sparql_templates/delete_session_by_uuid.sparql +18 -0
  144. seshat/web/resources/sparql_templates/delete_sessions.sparql +14 -0
  145. seshat/web/resources/sparql_templates/delete_sessions_older_than.sparql +17 -0
  146. seshat/web/resources/sparql_templates/derived_from.sparql +10 -0
  147. seshat/web/resources/sparql_templates/explorer_properties.sparql +11 -0
  148. seshat/web/resources/sparql_templates/explorer_property_types.sparql +12 -0
  149. seshat/web/resources/sparql_templates/explorer_types.sparql +10 -0
  150. seshat/web/resources/sparql_templates/export_listable_type.sparql +22 -0
  151. seshat/web/resources/sparql_templates/export_root_type.sparql +19 -0
  152. seshat/web/resources/sparql_templates/funding.sparql +33 -0
  153. seshat/web/resources/sparql_templates/group.sparql +23 -0
  154. seshat/web/resources/sparql_templates/group_by_name.sparql +16 -0
  155. seshat/web/resources/sparql_templates/item_collaborative_permissions.sparql +40 -0
  156. seshat/web/resources/sparql_templates/latest_datasets_portal.sparql +17 -0
  157. seshat/web/resources/sparql_templates/licenses.sparql +16 -0
  158. seshat/web/resources/sparql_templates/members.sparql +32 -0
  159. seshat/web/resources/sparql_templates/missing_checksummed_files_for_container.sparql +16 -0
  160. seshat/web/resources/sparql_templates/missing_dois.sparql +16 -0
  161. seshat/web/resources/sparql_templates/prefixes.sparql +5 -0
  162. seshat/web/resources/sparql_templates/private_links.sparql +34 -0
  163. seshat/web/resources/sparql_templates/projects.sparql +28 -0
  164. seshat/web/resources/sparql_templates/publish_draft_collection.sparql +59 -0
  165. seshat/web/resources/sparql_templates/publish_draft_dataset.sparql +66 -0
  166. seshat/web/resources/sparql_templates/quota_requests.sparql +28 -0
  167. seshat/web/resources/sparql_templates/record_uri.sparql +10 -0
  168. seshat/web/resources/sparql_templates/references.sparql +43 -0
  169. seshat/web/resources/sparql_templates/reviews.sparql +78 -0
  170. seshat/web/resources/sparql_templates/root_categories.sparql +17 -0
  171. seshat/web/resources/sparql_templates/search_tags.sparql +14 -0
  172. seshat/web/resources/sparql_templates/statistics_authors.sparql +16 -0
  173. seshat/web/resources/sparql_templates/statistics_collections.sparql +13 -0
  174. seshat/web/resources/sparql_templates/statistics_datalink.sparql +13 -0
  175. seshat/web/resources/sparql_templates/statistics_datasets.sparql +13 -0
  176. seshat/web/resources/sparql_templates/statistics_files.sparql +32 -0
  177. seshat/web/resources/sparql_templates/subcategories_by_category.sparql +19 -0
  178. seshat/web/resources/sparql_templates/tags.sparql +47 -0
  179. seshat/web/resources/sparql_templates/transfer_account_resources.sparql +19 -0
  180. seshat/web/resources/sparql_templates/update_account.sparql +117 -0
  181. seshat/web/resources/sparql_templates/update_author.sparql +49 -0
  182. seshat/web/resources/sparql_templates/update_authors_order.sparql +34 -0
  183. seshat/web/resources/sparql_templates/update_collaborators.sparql +52 -0
  184. seshat/web/resources/sparql_templates/update_collection.sparql +110 -0
  185. seshat/web/resources/sparql_templates/update_dataset.sparql +229 -0
  186. seshat/web/resources/sparql_templates/update_dataset_thumb.sparql +36 -0
  187. seshat/web/resources/sparql_templates/update_doi_after_publishing.sparql +20 -0
  188. seshat/web/resources/sparql_templates/update_file.sparql +105 -0
  189. seshat/web/resources/sparql_templates/update_git_uuid.sparql +21 -0
  190. seshat/web/resources/sparql_templates/update_orcid_for_account.sparql +28 -0
  191. seshat/web/resources/sparql_templates/update_private_link.sparql +47 -0
  192. seshat/web/resources/sparql_templates/update_quota_request.sparql +46 -0
  193. seshat/web/resources/sparql_templates/update_review.sparql +36 -0
  194. seshat/web/resources/sparql_templates/update_seen_by_reviewer.sparql +20 -0
  195. seshat/web/resources/sparql_templates/update_session.sparql +35 -0
  196. seshat/web/resources/sparql_templates/update_view_and_download_counts.sparql +50 -0
  197. seshat/web/resources/static/css/ace.css +1 -0
  198. seshat/web/resources/static/css/dropzone.min.css +1 -0
  199. seshat/web/resources/static/css/form.css +191 -0
  200. seshat/web/resources/static/css/jquery.dataTables.min.css +1 -0
  201. seshat/web/resources/static/css/main.css +626 -0
  202. seshat/web/resources/static/css/pub.css +63 -0
  203. seshat/web/resources/static/css/quill.snow.css +10 -0
  204. seshat/web/resources/static/fonts/FiraCode-Regular.woff2 +0 -0
  205. seshat/web/resources/static/fonts/SourceSansPro-Bold.ttf +0 -0
  206. seshat/web/resources/static/fonts/SourceSansPro-Italic.ttf +0 -0
  207. seshat/web/resources/static/fonts/SourceSansPro-Regular.ttf +0 -0
  208. seshat/web/resources/static/fonts/fa-brands-400.eot +0 -0
  209. seshat/web/resources/static/fonts/fa-brands-400.ttf +0 -0
  210. seshat/web/resources/static/fonts/fa-brands-400.woff +0 -0
  211. seshat/web/resources/static/fonts/fa-brands-400.woff2 +0 -0
  212. seshat/web/resources/static/fonts/fa-solid-900.eot +0 -0
  213. seshat/web/resources/static/fonts/fa-solid-900.ttf +0 -0
  214. seshat/web/resources/static/fonts/fa-solid-900.woff +0 -0
  215. seshat/web/resources/static/fonts/fa-solid-900.woff2 +0 -0
  216. seshat/web/resources/static/images/codeworks-badge.svg +2 -0
  217. seshat/web/resources/static/images/collection-thumb.svg +33 -0
  218. seshat/web/resources/static/images/dataset-thumb.svg +86 -0
  219. seshat/web/resources/static/images/datatables/sort_asc.png +0 -0
  220. seshat/web/resources/static/images/datatables/sort_asc_disabled.png +0 -0
  221. seshat/web/resources/static/images/datatables/sort_both.png +0 -0
  222. seshat/web/resources/static/images/datatables/sort_desc.png +0 -0
  223. seshat/web/resources/static/images/datatables/sort_desc_disabled.png +0 -0
  224. seshat/web/resources/static/images/ext-link.png +0 -0
  225. seshat/web/resources/static/images/handle-logo.png +0 -0
  226. seshat/web/resources/static/images/logo.png +0 -0
  227. seshat/web/resources/static/images/logo.svg +2 -0
  228. seshat/web/resources/static/images/orcid.svg +17 -0
  229. seshat/web/resources/static/js/ace/ace.js +17 -0
  230. seshat/web/resources/static/js/ace/ext-language_tools.js +8 -0
  231. seshat/web/resources/static/js/ace/ext-searchbox.js +8 -0
  232. seshat/web/resources/static/js/ace/mode-sparql.js +8 -0
  233. seshat/web/resources/static/js/ace/theme-crimson_editor.js +8 -0
  234. seshat/web/resources/static/js/admin-dashboard.js +40 -0
  235. seshat/web/resources/static/js/d3.v7.min.js +2 -0
  236. seshat/web/resources/static/js/depositor-dashboard.js +61 -0
  237. seshat/web/resources/static/js/dropzone.min.js +1 -0
  238. seshat/web/resources/static/js/edit-collection.js +640 -0
  239. seshat/web/resources/static/js/edit-dataset.js +1673 -0
  240. seshat/web/resources/static/js/edit-profile.js +100 -0
  241. seshat/web/resources/static/js/exploratory.js +185 -0
  242. seshat/web/resources/static/js/jquery-3.6.0.min.js +2 -0
  243. seshat/web/resources/static/js/jquery.dataTables.min.js +187 -0
  244. seshat/web/resources/static/js/landing_page.js +133 -0
  245. seshat/web/resources/static/js/my-data.js +27 -0
  246. seshat/web/resources/static/js/query-editor.js +98 -0
  247. seshat/web/resources/static/js/quill.min.js +3 -0
  248. seshat/web/resources/static/js/ranking.js +84 -0
  249. seshat/web/resources/static/js/review-overview.js +211 -0
  250. seshat/web/resources/static/js/search.js +924 -0
  251. seshat/web/resources/static/js/utils.js +568 -0
  252. seshat/web/s3.py +151 -0
  253. seshat/web/ui.py +1436 -0
  254. seshat/web/validator.py +525 -0
  255. seshat/web/wsgi.py +10267 -0
  256. seshat/web/xml_formatter.py +360 -0
  257. seshat/web/zipfly.py +184 -0
  258. seshat-26.7.dist-info/METADATA +185 -0
  259. seshat-26.7.dist-info/RECORD +263 -0
  260. seshat-26.7.dist-info/WHEEL +5 -0
  261. seshat-26.7.dist-info/entry_points.txt +2 -0
  262. seshat-26.7.dist-info/licenses/LICENSE +16 -0
  263. seshat-26.7.dist-info/top_level.txt +1 -0
seshat/__init__.py ADDED
File without changes
seshat/config.py ADDED
@@ -0,0 +1,9 @@
1
+ """
2
+ This module helps the code find stuff outside of the Python package's scope.
3
+ It is optional, and only available when built using the autotools-based build.
4
+ """
5
+ import os
6
+
7
+ PREFIX = "/usr/local"
8
+ DATA_ROOT_DIR = "${prefix}/share".replace("${prefix}", PREFIX)
9
+ DOCUMENTATION_DIRECTORY = os.path.join (DATA_ROOT_DIR, "seshat")
seshat/ui.py ADDED
@@ -0,0 +1,133 @@
1
+ """
2
+ This module contains the entry point for the program.
3
+ """
4
+
5
+ import argparse
6
+ import signal
7
+ import sys
8
+ import logging
9
+ import os
10
+ import importlib.metadata
11
+
12
+ import seshat.web.ui as web_ui
13
+
14
+ def show_version ():
15
+ """Show the program's version."""
16
+
17
+ version = importlib.metadata.version (__package__ or __name__)
18
+ print(f"This is seshat v{version}")
19
+ sys.exit(0)
20
+
21
+ def show_help ():
22
+ """Show a GNU-style help message."""
23
+
24
+ print("""This is seshat.\n
25
+ Available subcommands and options:
26
+
27
+ web:
28
+ --help -h Show a help message.
29
+ --config-file=ARG -c Load configuration from a file.
30
+ --initialize -i Populate the RDF store with default triples.
31
+ --extract-transactions-from-log=ARG
32
+ -e Extract transactions from the log file. It does
33
+ not extract delayed transactions, so it matches
34
+ what would have been applied to the database
35
+ normally. The optional argument is a datetime to
36
+ start from, in the format of YYYY-MM-DD HH:MM:SS.
37
+ --extract-delayed-transactions-from-log=ARG
38
+ -d Similar to -e, except this option only extracts
39
+ delayed transactions from the log file.
40
+ --apply-transactions=ARG
41
+ -A Apply transactions extracted by the -e option above.
42
+ The optional argument is the path to the folder that
43
+ contains the SPARQL transaction files. By default
44
+ it will look in the pre-configured directory.
45
+ --full-rdf-export -f Exports all metadata as RDF triples in N3 format.
46
+ This feature is in BETA state. Do not rely on it
47
+ for a full back-up.
48
+ --public-rdf-export -p Exports all publically available metadata as RDF
49
+ triples in N3 format. This feature is in BETA
50
+ state. Do not reply on it for an accurate
51
+ representation of publically available metadata.
52
+
53
+ Global options:
54
+ --help -h Show this message.
55
+ --version -v Show versioning information.\n""")
56
+ sys.exit(0)
57
+
58
+ def sigint_handler (sig, frame): # pylint: disable=unused-argument
59
+ """Signal handler for SIGINT and SIGTERM."""
60
+ logger = logging.getLogger(__name__)
61
+ logger.info ("Received shutdown signal. Goodbye!")
62
+ sys.exit(0)
63
+
64
+ def main_inner ():
65
+ """The main entry point of the program."""
66
+
67
+ signal.signal(signal.SIGINT, sigint_handler)
68
+ signal.signal(signal.SIGTERM, sigint_handler)
69
+
70
+ logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(name)s: %(message)s',
71
+ level=logging.INFO)
72
+
73
+ ## COMMAND-LINE ARGUMENTS
74
+ ## ------------------------------------------------------------------------
75
+ parser = argparse.ArgumentParser(
76
+ usage = '\n %(prog)s [web] ...',
77
+ prog = 'seshat',
78
+ add_help = False)
79
+
80
+ subparsers = parser.add_subparsers(dest='command', help='sub-command help')
81
+
82
+ ### WEB SUBCOMMAND
83
+ ### -----------------------------------------------------------------------
84
+ web_parser = subparsers.add_parser('web', help="Options for the 'web' subcommand.")
85
+ web_parser.add_argument('--config-file','-c', type=str, default=None)
86
+ web_parser.add_argument('--initialize', '-i', action='store_true')
87
+ web_parser.add_argument('--extract-transactions-from-log', '-e', nargs='?',
88
+ const='', default=None)
89
+ web_parser.add_argument('--extract-delayed-transactions-from-log', '-d', nargs='?',
90
+ const='', default=None)
91
+ web_parser.add_argument('--apply-transactions', '-A', nargs='?',
92
+ const='', default=None)
93
+ web_parser.add_argument('--full-rdf-export', '-f', action='store_true')
94
+ web_parser.add_argument('--public-rdf-export', '-p', action='store_true')
95
+
96
+ ### GLOBAL ARGUMENTS
97
+ ### -----------------------------------------------------------------------
98
+ parser.add_argument('--help', '-h', action='store_true')
99
+ parser.add_argument('--version', '-v', action='store_true')
100
+
101
+ # When using PyInstaller and Nuitka, argv[0] seems to get duplicated.
102
+ # In the case of Nuitka, relative paths are converted to absolute paths.
103
+ # This bit de-duplicates argv[0] in these cases.
104
+ try:
105
+ if os.path.abspath(sys.argv[0]) == os.path.abspath(sys.argv[1]):
106
+ sys.argv = sys.argv[1:]
107
+ except IndexError:
108
+ pass
109
+
110
+ args = parser.parse_args()
111
+ if args.help:
112
+ show_help()
113
+ if args.version:
114
+ show_version()
115
+
116
+ if args.command == "web":
117
+ web_ui.main (args.config_file, True, args.initialize,
118
+ args.extract_transactions_from_log,
119
+ args.extract_delayed_transactions_from_log,
120
+ args.apply_transactions, args.full_rdf_export,
121
+ args.public_rdf_export)
122
+
123
+ elif len(sys.argv) == 1:
124
+ print("Try --help for usage options.")
125
+
126
+ def main ():
127
+ """Wrapper to catch KeyboardInterrupts for main_inner."""
128
+ try:
129
+ main_inner()
130
+ except KeyboardInterrupt:
131
+ sigint_handler (None, None)
132
+
133
+ sys.exit(0)
@@ -0,0 +1,63 @@
1
+ """
2
+ This module provides constants that can be used throughout
3
+ the codebase.
4
+ """
5
+
6
+ filetypes_by_extension = {
7
+ "bash": "Shell",
8
+ "bib": "TeX",
9
+ "c": "C",
10
+ "cc": "C++",
11
+ "cpp": "C++",
12
+ "cs": "C#",
13
+ "css": "CSS",
14
+ "cxx": "C++",
15
+ "d": "D",
16
+ "f": "Fortran",
17
+ "f77": "Fortran",
18
+ "f90": "Fortran",
19
+ "for": "Fortran",
20
+ "geojson": "JSON",
21
+ "go": "Go",
22
+ "gohtml": "Go",
23
+ "h": "C",
24
+ "hpp": "C++",
25
+ "hs": "Haskell",
26
+ "html": "HTML",
27
+ "htm": "HTML",
28
+ "hxx": "C++",
29
+ "in": "Automake",
30
+ "ipynb": "Jupyter Notebook",
31
+ "java": "Java",
32
+ "jl": "Julia",
33
+ "js": "JavaScript",
34
+ "json": "JSON",
35
+ "m": "Matlab",
36
+ "mat": "Matlab",
37
+ "md": "Markdown",
38
+ "mts": "TypeScript",
39
+ "mysql": "SQL",
40
+ "php": "PHP",
41
+ "pl": "Perl",
42
+ "pm": "Perl",
43
+ "py": "Python",
44
+ "r": "R",
45
+ "rb": "Ruby",
46
+ "rs": "Rust",
47
+ "sh": "Shell",
48
+ "sparql": "SPARQL",
49
+ "sql": "SQL",
50
+ "sty": "TeX",
51
+ "tex": "TeX",
52
+ "ts": "TypeScript",
53
+ "tsx": "TypeScript",
54
+ "xml": "XML",
55
+ "xyz": "Chemical XYZ",
56
+ "yaml": "YAML",
57
+ "yml": "YAML",
58
+ }
59
+
60
+ allowed_html_tags = ["p", "strong", "em", "u", "s", "ol", "ul", "li", "code", "pre", "br", "sup", "sub", "h2", "h3"]
61
+
62
+ iiif_supported_formats = [ ".jpg", ".png", ".pdf", ".tif", ".tiff", ".gif", ".webp" ]
63
+ datetime_format = "%Y-%m-%dT%H:%M:%S" # pylint: disable=invalid-name
@@ -0,0 +1,364 @@
1
+ """
2
+ This module provides convenience functions that can be used throughout
3
+ the codebase.
4
+ """
5
+
6
+ import re
7
+ import logging
8
+ from html.parser import HTMLParser
9
+ from html import escape, unescape
10
+ from seshat.utils.constants import allowed_html_tags
11
+
12
+ RLIMIT_AVAILABLE = False # pylint: disable=invalid-name
13
+ try:
14
+ from resource import setrlimit, RLIMIT_AS, RLIM_INFINITY
15
+ RLIMIT_AVAILABLE = True # pylint: disable=invalid-name
16
+ except ImportError:
17
+ pass
18
+
19
+ class HTMLStripper (HTMLParser):
20
+ """Overriden HTMLParser to strip HTML tags inspired by Django's implementation."""
21
+
22
+ def __init__ (self):
23
+ super().__init__(convert_charrefs=False)
24
+ self.reset()
25
+ self.state = []
26
+
27
+ def handle_data (self, data):
28
+ self.state.append (data)
29
+
30
+ def handle_entityref (self, name):
31
+ self.state.append (f"&{name};")
32
+
33
+ def handle_charref (self, name):
34
+ self.state.append (f"&#{name};")
35
+
36
+ def get_data (self):
37
+ """Return stripped HTML."""
38
+ return "".join(self.state)
39
+
40
+
41
+ def html_to_plaintext (value, respect_newlines=False):
42
+ """
43
+ Outputs plain text without HTML tags.
44
+ When RESPECT_NEWLINES is set to True, it will convert line
45
+ breaks to newline characters.
46
+ """
47
+ if respect_newlines:
48
+ value = value.replace("</p>", "</p>\n\n")
49
+ value = value.replace("<br>", "\n")
50
+ value = value.replace("<br/>", "\n")
51
+ value = value.replace("<br />", "\n")
52
+
53
+ while "<" in value and ">" in value:
54
+ tag_count = value.count("<")
55
+ html = HTMLStripper()
56
+ html.feed (value)
57
+ html.close()
58
+ value = html.get_data()
59
+ if tag_count == value.count("<"):
60
+ break
61
+
62
+ return value
63
+
64
+ def contains_disallowed_html (value):
65
+ """Return True when there is a disallowed tag in VALUE, False otherwise."""
66
+
67
+ if value is None:
68
+ return False
69
+
70
+ stripped_value = unescape (value)
71
+ for tag in allowed_html_tags:
72
+ stripped_value = stripped_value.replace (f"<{tag}>", "")
73
+ stripped_value = stripped_value.replace (f"</{tag}>", "")
74
+
75
+ return html_to_plaintext (stripped_value) != stripped_value
76
+
77
+ def encode_html (value, allow_simple_tags=True, allow_some_punctuation=True):
78
+ """
79
+ Returns VALUE but with encoded HTML entities. When ALLOW_SIMPLE_TAGS is
80
+ True, it doesn't encode HTML tags in constants.allowed_html_tags.
81
+ """
82
+ encoded_value = escape (value)
83
+ if allow_simple_tags:
84
+ for tag in allowed_html_tags:
85
+ encoded_value = encoded_value.replace (f"&lt;{tag}&gt;", f"<{tag}>")
86
+ encoded_value = encoded_value.replace (f"&lt;/{tag}&gt;", f"</{tag}>")
87
+
88
+ if allow_some_punctuation:
89
+ for character in [("&", "&amp;"), ("\"", "&quot;"), (" ", "&nbsp;")]:
90
+ encoded_value = encoded_value.replace (character[1], character[0])
91
+
92
+ return encoded_value
93
+
94
+ def decode_html (value):
95
+ """Reverses the effect of 'encode_html'."""
96
+ return unescape(value)
97
+
98
+ def value_or (record, key, other):
99
+ """Return the value of KEY or OTHER."""
100
+ try:
101
+ return record[key]
102
+ except (IndexError, KeyError, TypeError):
103
+ return other
104
+
105
+ def value_or_none (record, key):
106
+ """Return the value of KEY or None."""
107
+ return value_or (record, key, None)
108
+
109
+ def pretty_print_size (num_bytes):
110
+ """Return pretty-printed file size."""
111
+
112
+ output = ""
113
+ if not isinstance(num_bytes, int):
114
+ output = "0B"
115
+ elif num_bytes < 1000:
116
+ output = f"{num_bytes}B"
117
+ elif num_bytes < 1000000:
118
+ output = f"{num_bytes/1000:.2f}KB"
119
+ elif num_bytes < 1000000000:
120
+ output = f"{num_bytes/1000000:.2f}MB"
121
+ elif num_bytes < 1000000000000:
122
+ output = f"{num_bytes/1000000000:.2f}GB"
123
+ elif num_bytes < 1000000000000000:
124
+ output = f"{num_bytes/1000000000000:.2f}TB"
125
+ else:
126
+ output = f"{num_bytes/1000000000000000:.2f}PB"
127
+
128
+ return output
129
+
130
+ def decimal_coord(raw_input, axis, digits=5):
131
+ '''
132
+ Converts txt to string with decimal coordinates or None if invalid.
133
+ Accepts input like "42.597" or "5º 38’ 18.5’’ E".
134
+ axis is 'N' or 'E'
135
+ 5 digits is accuracy (RMS) of 40 cm.
136
+ '''
137
+ pattern = r"""^(-?\d+)[º°]\s*(\d+)[’']\s*((\d+)(\.\d?)?)((’’)|('')|"|”)\s*([NESW]?)$"""
138
+ if raw_input is None:
139
+ return None
140
+
141
+ try:
142
+ deg = float(raw_input)
143
+ except ValueError:
144
+ raw_input = str(raw_input).strip()
145
+ pattern_match = re.search(pattern, raw_input)
146
+ if not pattern_match:
147
+ return None # Undecipherable gibberish
148
+
149
+ components = pattern_match.groups()
150
+ deg = int(components[0]) + int(components[1])/60 + float(components[2])/3600
151
+ direction = components[-1] #direction may be N,E,S,W, axis N,E.
152
+ if (direction == 'S' and axis == 'N') or (direction == 'W' and axis == 'E'):
153
+ deg = -deg
154
+ elif direction not in (axis, ''):
155
+ return None # Probably mixed up lat and lon
156
+
157
+ max_deg = 90 if axis == 'N' else 180
158
+ if abs(deg) <= max_deg:
159
+ return f'{deg:.{digits}f}'
160
+
161
+ return None
162
+
163
+ def decimal_coords(lat, lon, digits=5):
164
+ '''
165
+ Converts raw input lat, lon to decimal coordinats or None if invalid.
166
+ '''
167
+ lat_validated = decimal_coord(lat, 'N', digits=digits)
168
+ lon_validated = decimal_coord(lon, 'E', digits=digits)
169
+ return (lat_validated, lon_validated)
170
+
171
+ def self_or_value_or_none(record, key):
172
+ '''
173
+ Return record[key]['value'] or record[key] or none.
174
+ Use: deal with triples where table2rdf transformed a string into a number.
175
+ '''
176
+ if key in record:
177
+ return value_or (record[key], 'value', record[key])
178
+
179
+ return None
180
+
181
+ def parses_to_int (input_string):
182
+ """Return True when wrapping in int() would succeed, False otherwise."""
183
+ try:
184
+ int(input_string)
185
+ except (ValueError, TypeError):
186
+ return False
187
+
188
+ return True
189
+
190
+ def deduplicate_list (alist):
191
+ """
192
+ Return deduplicated list, retaining original ordering,
193
+ based on first occurrence of duplicates.
194
+ """
195
+ try:
196
+ return list(dict.fromkeys(alist))
197
+ except TypeError:
198
+ logging.error('Wrong type of %s in deduplicate_list', alist)
199
+ return None
200
+
201
+ def make_citation (authors, year, title, version, item_type, doi,
202
+ publisher=None, max_cited_authors=5):
203
+ """Return citation in standard Datacite format."""
204
+ try:
205
+ auths = [{key:val for key,val in author.items() if val}
206
+ for author in authors] #remove empty name parts
207
+ citation = '; '.join([
208
+ (f"{author['last_name']}, {author['first_name']}" if
209
+ not {'first_name','last_name'}-set(author)
210
+ else value_or (author, 'full_name', "Unknown")) for author in auths[:max_cited_authors]])
211
+ if authors[max_cited_authors:max_cited_authors+1]:
212
+ citation += ' et. al.'
213
+ citation += f' ({year}): {title}'
214
+ if not citation.endswith('.'):
215
+ citation += '.'
216
+
217
+ publisher = f"{publisher}. " if publisher else ""
218
+ citation += f' Version {version}. {publisher}{item_type}. https://doi.org/{doi}'
219
+ return citation
220
+ except TypeError:
221
+ logging.error('could not make citation for %s', doi)
222
+ return None
223
+
224
+ def custom_field_name (name):
225
+ """Return a predictable name for a custom field."""
226
+
227
+ name = name.lower().replace(" ", "_")
228
+
229
+ ## Exceptions to the custom field names.
230
+ if name == "licence_remarks":
231
+ name = "license_remarks"
232
+ if name == "geolocation_latitude":
233
+ name = "latitude"
234
+ if name == "geolocation_longitude":
235
+ name = "longitude"
236
+
237
+ return name
238
+
239
+ def is_opendap_url (url):
240
+ """Returns True when URL links to 4TU's OPeNDAP server, False otherwise."""
241
+ try:
242
+ return url.split("/")[2] == "opendap.4tu.nl"
243
+ except (AttributeError, IndexError, KeyError):
244
+ return False
245
+
246
+ def add_logging_level (level_name, level_number, method_name=None):
247
+ """
248
+ Comprehensively adds a new logging level to the `logging` module and the
249
+ currently configured logging class.
250
+
251
+ `level_name` becomes an attribute of the `logging` module with the value
252
+ `level_number`. `method_name` becomes a convenience method for both `logging`
253
+ itself and the class returned by `logging.getLoggerClass()` (usually just
254
+ `logging.Logger`). If `method_name` is not specified, `level_name.lower()` is
255
+ used.
256
+
257
+ To avoid accidental clobberings of existing attributes, this method will
258
+ not overwrite existing attributes.
259
+
260
+ Code copied and adapted from the following Stack Overflow post:
261
+ https://stackoverflow.com/questions/2183233/35804945#35804945
262
+ """
263
+ if not method_name:
264
+ method_name = level_name.lower()
265
+
266
+ if (hasattr(logging, level_name) or
267
+ hasattr(logging, method_name) or
268
+ hasattr(logging.getLoggerClass(), method_name)):
269
+ return
270
+
271
+ # This method was inspired by the answers to Stack Overflow post
272
+ # http://stackoverflow.com/q/2183233/2988730, especially
273
+ # http://stackoverflow.com/a/13638084/2988730
274
+ def log_for_level(self, message, *args, **kwargs):
275
+ if self.isEnabledFor(level_number):
276
+ self._log(level_number, message, args, **kwargs) # pylint: disable=protected-access
277
+
278
+ def log_to_root(message, *args, **kwargs):
279
+ logging.log(level_number, message, *args, **kwargs)
280
+
281
+ logging.addLevelName(level_number, level_name)
282
+ setattr(logging, level_name, level_number)
283
+ setattr(logging.getLoggerClass(), method_name, log_for_level)
284
+ setattr(logging, method_name, log_to_root)
285
+
286
+ def landing_page_url (item_id, version=None, item_type="dataset", base_url=""):
287
+ """Returns (a version-specific) URL to an item's landing page."""
288
+ url = f"{base_url}/{item_type}s/{item_id}"
289
+ if version:
290
+ url += f"/{version}"
291
+ return url
292
+
293
+ def split_author_name(name):
294
+ """ Procedure to split name of author into first and last name.
295
+ Works for often occurring name patterns:
296
+ * first_name (initials) last_name
297
+ * first_name I.N.I.T. last_name
298
+ * first_name last_name
299
+ * last_name """
300
+ name = re.sub(r'\s+', ' ', name)
301
+ if ')' in name:
302
+ parts = name.split(')', 1)
303
+ parts[0] += ')'
304
+ elif '.' in name:
305
+ parts = name[::-1].split('.', 1)
306
+ parts = [part[::-1] for part in parts][::-1]
307
+ parts[0] += '.'
308
+ else:
309
+ parts = name.split(' ', 1)
310
+ parts = [part.strip() for part in parts]
311
+ parts = ([''] + parts)[-2:]
312
+ return parts
313
+
314
+ def split_string (input_string, delimiter=","):
315
+ """Splits a string by a delimiter character and strips whitespace."""
316
+ if not isinstance (input_string, str) or input_string == "":
317
+ return None
318
+ if not isinstance (delimiter, str) or len(delimiter) != 1:
319
+ return None
320
+
321
+ parts = [part.strip() for part in input_string.split (delimiter)]
322
+ return [part for part in parts if part]
323
+
324
+ def strip_string (input_string):
325
+ """Removes whitespace from the beginning and end of a string."""
326
+ if isinstance (input_string, str):
327
+ return input_string.strip()
328
+ return input_string
329
+
330
+ def normalize_identifier (value, prefix_url):
331
+ """Procedure to rewrite URLs to URIs."""
332
+ # Don't process invalid entries
333
+ if not isinstance(value, str):
334
+ return None
335
+ # Don't store empty values.
336
+ value = value.strip()
337
+ if value == "":
338
+ return None
339
+ # Strip the URI prefix from identifiers.
340
+ if value.startswith (prefix_url):
341
+ return value[len(prefix_url):]
342
+
343
+ return value
344
+
345
+ def normalize_orcid (orcid):
346
+ """Procedure to make storing ORCID identifiers consistent."""
347
+ return normalize_identifier (orcid, "https://orcid.org/")
348
+
349
+ def normalize_doi (doi):
350
+ """Procedure to make storing DOIs consistent."""
351
+ return normalize_identifier (normalize_identifier (doi, "https://doi.org/"), "doi.org/")
352
+
353
+ def limit_memory_for_subprocess (limit_in_bytes = 4096000000):
354
+ """Procedure to attempt to limit the memory usage of a subprocess."""
355
+ if not RLIMIT_AVAILABLE:
356
+ logging.warning ("Because 'setrlimit' is not available, memory usage may rise unbounded.")
357
+ return None
358
+
359
+ try:
360
+ setrlimit (RLIMIT_AS, (limit_in_bytes, RLIM_INFINITY))
361
+ except ValueError:
362
+ logging.warning ("Call to 'setrlimit' failed.")
363
+
364
+ return None