girder-jsonforms 2.1.1__tar.gz → 2.1.2__tar.gz

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 (70) hide show
  1. {girder_jsonforms-2.1.1/girder_jsonforms.egg-info → girder_jsonforms-2.1.2}/PKG-INFO +1 -1
  2. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/jq.py +68 -0
  3. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/models/project.py +117 -7
  4. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/rest/aimdl.py +3 -2
  5. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/web_client/dist/girder-plugin-jsonforms.umd.cjs +3 -3
  6. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2/girder_jsonforms.egg-info}/PKG-INFO +1 -1
  7. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/setup.py +1 -1
  8. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/.github/workflows/release.yml +0 -0
  9. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/.gitignore +0 -0
  10. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/LICENSE +0 -0
  11. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/README.md +0 -0
  12. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/doc/file-uploads.md +0 -0
  13. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/doc/preload.md +0 -0
  14. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/__init__.py +0 -0
  15. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/asgi.py +0 -0
  16. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/__init__.py +0 -0
  17. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/announcement.py +0 -0
  18. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/events.py +0 -0
  19. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/google_drive.py +0 -0
  20. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/igsn_client.py +0 -0
  21. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/igsn_vocab.py +0 -0
  22. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/locks.py +0 -0
  23. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/mail.py +0 -0
  24. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/metadata_dates.py +0 -0
  25. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/orcid.py +0 -0
  26. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/project_helpers.py +0 -0
  27. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/project_tasks.py +0 -0
  28. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/response_cache.py +0 -0
  29. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/lib/timing.py +0 -0
  30. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_assets/caimee-shield-80.png +0 -0
  31. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_templates/_projectsFooter.mako +0 -0
  32. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_templates/_projectsFooter.txt.mako +0 -0
  33. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_templates/_projectsHeader.mako +0 -0
  34. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_templates/projectDecision.mako +0 -0
  35. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_templates/projectDecision.txt.mako +0 -0
  36. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_templates/projectSubmitted.mako +0 -0
  37. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/mail_templates/projectSubmitted.txt.mako +0 -0
  38. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/models/__init__.py +0 -0
  39. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/models/deposition.py +0 -0
  40. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/models/entry.py +0 -0
  41. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/models/form.py +0 -0
  42. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/rest/__init__.py +0 -0
  43. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/rest/deposition.py +0 -0
  44. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/rest/entry.py +0 -0
  45. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/rest/form.py +0 -0
  46. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/rest/project.py +0 -0
  47. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/schemas/__init__.py +0 -0
  48. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/schemas/datacite-v4.5.json +0 -0
  49. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/scripts/__init__.py +0 -0
  50. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/scripts/migrate_metadata_dates.py +0 -0
  51. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/scripts/preview_mail.py +0 -0
  52. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/settings.py +0 -0
  53. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/web_client/dist/style.css +0 -0
  54. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/__init__.py +0 -0
  55. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/amdee.py +0 -0
  56. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/folder_ops.py +0 -0
  57. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/handle_enumsource.py +0 -0
  58. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/htmax.py +0 -0
  59. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/igsn_registry.py +0 -0
  60. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/orcid.py +0 -0
  61. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/projects.py +0 -0
  62. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/pull_related_ids.py +0 -0
  63. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms/worker_plugin/relate_entry_to_igsn.py +0 -0
  64. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms.egg-info/SOURCES.txt +0 -0
  65. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms.egg-info/dependency_links.txt +0 -0
  66. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms.egg-info/entry_points.txt +0 -0
  67. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms.egg-info/not-zip-safe +0 -0
  68. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms.egg-info/requires.txt +0 -0
  69. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/girder_jsonforms.egg-info/top_level.txt +0 -0
  70. {girder_jsonforms-2.1.1 → girder_jsonforms-2.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: girder-jsonforms
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Girder plugin adding forms based on JSON-editor
5
5
  License: BSD-3-Clause
6
6
  Classifier: Development Status :: 4 - Beta
@@ -1,3 +1,4 @@
1
+ from collections.abc import Iterator
1
2
  from typing import Any
2
3
 
3
4
 
@@ -43,6 +44,73 @@ def get_value(json_data: dict, key: str) -> dict:
43
44
  return json_data
44
45
 
45
46
 
47
+ class JqPathError(ValueError):
48
+ """A path that does not fit the shape of the data it was applied to."""
49
+
50
+
51
+ def iter_values(json_data: Any, key: str) -> Iterator[tuple[str, Any]]:
52
+ """Resolve a path that may contain ``[]`` ("for each element").
53
+
54
+ Yields ``(path, value)`` pairs, where ``path`` is ``key`` with every ``[]``
55
+ replaced by the index it matched, in the same spelling ``find_key_paths``
56
+ emits (``a.[0].b``), so an emitted path can be fed straight back into
57
+ ``get_value``. A path with no ``[]`` yields at most one pair, which makes
58
+ this a generalization of ``get_value`` rather than a separate notion.
59
+
60
+ Nested iteration is supported: ``a.[].b.[].c`` iterates the outer array and
61
+ then the inner one for each outer element.
62
+
63
+ Missing data is skipped: an element that lacks the trailing key, or an
64
+ explicit index past the end of a list, yields nothing while its siblings
65
+ still yield, with their original indices intact. A path that disagrees with
66
+ the shape of the data -- ``[]`` or ``[3]`` applied to anything that is not a
67
+ list -- raises ``JqPathError`` instead, because no amount of data fixes it.
68
+
69
+ Zero matches is therefore not an error here, and a typo'd key is
70
+ indistinguishable from an empty array at this layer. Callers that care
71
+ should report how many of the elements they expected actually matched.
72
+ """
73
+ yield from _iter_values(json_data, key.split("."), "")
74
+
75
+
76
+ def _join(prefix: str, segment: str) -> str:
77
+ return f"{prefix}.{segment}" if prefix else segment
78
+
79
+
80
+ def _iter_values(
81
+ node: Any, segments: list[str], prefix: str
82
+ ) -> Iterator[tuple[str, Any]]:
83
+ if not segments:
84
+ yield prefix, node
85
+ return
86
+
87
+ head, rest = segments[0], segments[1:]
88
+
89
+ if head == "[]":
90
+ if not isinstance(node, list):
91
+ raise JqPathError(
92
+ f"{prefix or '<root>'} is a {type(node).__name__}, not a list, "
93
+ "so [] cannot iterate it"
94
+ )
95
+ for index, item in enumerate(node):
96
+ yield from _iter_values(item, rest, _join(prefix, f"[{index}]"))
97
+ return
98
+
99
+ if head.startswith("[") and head.endswith("]"):
100
+ if not isinstance(node, list):
101
+ raise JqPathError(
102
+ f"{prefix or '<root>'} is a {type(node).__name__}, not a list, "
103
+ f"so {head} cannot index it"
104
+ )
105
+ index = int(head[1:-1])
106
+ if index < len(node):
107
+ yield from _iter_values(node[index], rest, _join(prefix, head))
108
+ return
109
+
110
+ if isinstance(node, dict) and head in node:
111
+ yield from _iter_values(node[head], rest, _join(prefix, head))
112
+
113
+
46
114
  def set_value(json_data: dict, key: str, value: Any) -> None:
47
115
  for path in key.split(".")[:-1]:
48
116
  if path.startswith("["):
@@ -63,6 +63,28 @@ project_schema = {
63
63
  "lastName": {"type": "string"},
64
64
  "orcidId": {"type": "string"},
65
65
  "email": {"type": "string", "format": "email"},
66
+ # Day-to-day contact. Defaults to the PI in the UI but can be
67
+ # moved to anyone, so it is its own flag rather than derived
68
+ # from `role`.
69
+ "isPointOfContact": {"type": "boolean"},
70
+ # Whether this person is physically coming to AIMD-L; some
71
+ # members only take part remotely.
72
+ "onSite": {"type": "boolean"},
73
+ # Career stage. Orthogonal to `role`, which is the access
74
+ # level (see _role_to_access_level in lib/events.py).
75
+ "status": {
76
+ "type": "string",
77
+ "enum": [
78
+ "faculty",
79
+ "staff",
80
+ "postdoc",
81
+ "grad",
82
+ "undergrad",
83
+ "other",
84
+ ],
85
+ },
86
+ # Only set when it differs from the project's organization.
87
+ "institution": {"type": "string"},
66
88
  },
67
89
  "required": ["email", "role"],
68
90
  "additionalProperties": False,
@@ -93,8 +115,93 @@ project_schema = {
93
115
  "enum": ["integrated", "singleInstrument", "development"],
94
116
  "default": "integrated",
95
117
  },
96
- "priority": {
97
- "type": "number",
118
+ # Affiliation of the applicant. Unranked: the numbering of the `priority`
119
+ # list it replaces implied an ordering the lab never allocated on.
120
+ "accessCategory": {
121
+ "type": "string",
122
+ "enum": [
123
+ "jhu",
124
+ "external-academic",
125
+ "external-corporate",
126
+ "external-government",
127
+ "external-foreign",
128
+ ],
129
+ },
130
+ # Home institution/company. Asked of external applicants only.
131
+ "organization": {"type": "string"},
132
+ # How the data this project generates has to be handled -- it describes
133
+ # the material coming into the lab, not the proposal documents. Advisory:
134
+ # access still defaults to the project's own members (lib/events.py).
135
+ "dataClassification": {
136
+ "type": "string",
137
+ "enum": [
138
+ "open",
139
+ "confidential-proprietary",
140
+ "confidential-controlled",
141
+ "opt-out",
142
+ ],
143
+ },
144
+ "assistanceRequired": {"type": "boolean"},
145
+ # Free text on purpose: "3 days", "2 half-days", "~1 week".
146
+ "daysRequested": {"type": "string"},
147
+ # The single-instrument proposal, written inline instead of uploaded.
148
+ "experimentPlan": {"type": "string"},
149
+ "safety": {
150
+ "type": "object",
151
+ "properties": {
152
+ "sampleHazards": {
153
+ "type": "array",
154
+ "items": {
155
+ "type": "string",
156
+ "enum": [
157
+ "none",
158
+ "toxic",
159
+ "flammable",
160
+ "energetic",
161
+ "biosafety",
162
+ "radioactive",
163
+ "other",
164
+ ],
165
+ },
166
+ "default": [],
167
+ },
168
+ "otherHazards": {
169
+ "type": "array",
170
+ "items": {
171
+ "type": "string",
172
+ "enum": [
173
+ "none",
174
+ "laser",
175
+ "high-temperature",
176
+ "high-voltage",
177
+ "user-equipment",
178
+ ],
179
+ },
180
+ "default": [],
181
+ },
182
+ "description": {"type": "string"},
183
+ },
184
+ "additionalProperties": False,
185
+ },
186
+ "funding": {
187
+ "type": "object",
188
+ "properties": {
189
+ "grants": {
190
+ "type": "array",
191
+ "items": {
192
+ "type": "object",
193
+ "properties": {
194
+ "agency": {"type": "string"},
195
+ "grantNumber": {"type": "string"},
196
+ },
197
+ "additionalProperties": False,
198
+ },
199
+ "default": [],
200
+ },
201
+ # JHU internal budget/IO number.
202
+ "internalBudgetNumber": {"type": "string"},
203
+ },
204
+ "additionalProperties": False,
98
205
  },
99
206
  "status": {
100
207
  "type": "string",
@@ -181,20 +288,27 @@ class Project(AccessControlledModel):
181
288
  level=AccessType.READ,
182
289
  fields=(
183
290
  "_id",
291
+ "accessCategory",
292
+ "assistanceRequired",
184
293
  "created",
185
294
  "creatorId",
295
+ "dataClassification",
296
+ "daysRequested",
186
297
  "description",
298
+ "experimentPlan",
187
299
  "files",
300
+ "funding",
188
301
  "instruments",
189
302
  "name",
190
303
  "metadata",
191
304
  "members",
192
305
  "orcidResourceUrl",
193
- "priority",
306
+ "organization",
194
307
  "projectId",
195
308
  "projectType",
196
309
  "public",
197
310
  "publicFlags",
311
+ "safety",
198
312
  "samples",
199
313
  "submissionFolderId",
200
314
  "status",
@@ -231,10 +345,6 @@ class Project(AccessControlledModel):
231
345
  doc["instruments"] = []
232
346
  if "projectType" not in doc:
233
347
  doc["projectType"] = "integrated"
234
- try:
235
- doc["priority"] = int(doc.get("priority", 0))
236
- except (ValueError, TypeError):
237
- raise ValidationException("Priority must be an integer")
238
348
  try:
239
349
  self.validator(project_schema).validate(doc)
240
350
  except jsonschema.ValidationError as ve:
@@ -544,7 +544,7 @@ class BaseLabResource(Resource):
544
544
  @filtermodel(model=Item)
545
545
  @autoDescribeRoute(
546
546
  Description("Get a list of items with a specific data type.")
547
- .param("dataType", "The data type to filter items by.", required=True)
547
+ .param("dataType", "The data type to filter items by.", required=False)
548
548
  .param(
549
549
  "baseParentId",
550
550
  "The ID of the parent collection to count items in.",
@@ -596,8 +596,9 @@ class BaseLabResource(Resource):
596
596
  base_parent = self._get_base_parent(baseParentType, baseParentId, user)
597
597
  q = {
598
598
  "meta.igsn": {"$exists": True},
599
- "meta.data_type": dataType,
600
599
  }
600
+ if dataType:
601
+ q["meta.data_type"] = dataType
601
602
  filters = filters or {}
602
603
  try:
603
604
  filters = sanitize_query(filters)