divejson 0.2.0__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.
divejson/__init__.py ADDED
@@ -0,0 +1,64 @@
1
+ """Tools for DiveJSON, an open dive-log interchange format.
2
+
3
+ The format itself — the normative specification, the JSON Schema and the conformance
4
+ corpus — lives at <https://github.com/divejson/divejson>. This package implements it:
5
+ ``validate`` — the schema pass plus the requirements the spec lists as beyond-schema —
6
+ ``uddf``, which reads UDDF logbooks into conforming documents, and ``conform``, the
7
+ conformance runner every implementation of the format provides. All three are importable
8
+ functions; ``cli`` is a thin wrapper over them.
9
+
10
+ The schema, the fixtures and the mapping documents are **vendored** here, from the
11
+ specification commit named in the repository's ``SPEC_REF``, so that an installed package
12
+ resolves its own schema and needs no checkout of anything.
13
+ """
14
+
15
+ __version__ = "0.2.0"
16
+
17
+ SPEC_VERSION = "1.0"
18
+
19
+ # Imported below the two constants rather than at the top of the file: `validate` reads
20
+ # `SPEC_VERSION` from this module as it imports, so the names it needs have to exist by
21
+ # the time the import runs.
22
+ from .conform import compared # noqa: E402
23
+ from .uddf import ( # noqa: E402
24
+ PRODUCER_KEY,
25
+ UDDF_ID_NAMESPACE,
26
+ Conversion,
27
+ DoctypeRefusedError,
28
+ MalformedUddfError,
29
+ NonConformingOutputError,
30
+ Note,
31
+ UddfError,
32
+ convert_uddf,
33
+ convert_uddf_file,
34
+ )
35
+ from .validate import ( # noqa: E402
36
+ DuplicateMemberError,
37
+ Issue,
38
+ load_document,
39
+ load_schema,
40
+ parse_document,
41
+ validate_document,
42
+ )
43
+
44
+ __all__ = [
45
+ "SPEC_VERSION",
46
+ "PRODUCER_KEY",
47
+ "UDDF_ID_NAMESPACE",
48
+ "Conversion",
49
+ "DoctypeRefusedError",
50
+ "DuplicateMemberError",
51
+ "Issue",
52
+ "MalformedUddfError",
53
+ "NonConformingOutputError",
54
+ "Note",
55
+ "UddfError",
56
+ "__version__",
57
+ "compared",
58
+ "convert_uddf",
59
+ "convert_uddf_file",
60
+ "load_document",
61
+ "load_schema",
62
+ "parse_document",
63
+ "validate_document",
64
+ ]
@@ -0,0 +1,450 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://divejson.org/schema/1.0/divejson.schema.json",
4
+ "title": "DiveJSON 1.0",
5
+ "description": "A DiveJSON document: an open dive-log interchange format. This schema describes exactly the members version 1.0 defines; the normative specification is spec/divejson.md, and the requirements it lists as beyond-schema are checked by the reference validator, not here. Absence is the only spelling of \"not recorded\": no member accepts null.",
6
+ "type": "object",
7
+ "properties": {
8
+ "format": { "const": "divejson" },
9
+ "version": { "const": "1.0" },
10
+ "exported_at": { "$ref": "#/$defs/offset_date_time" },
11
+ "generator": { "$ref": "#/$defs/generator" },
12
+ "diver": { "$ref": "#/$defs/diver" },
13
+ "dives": { "type": "array", "items": { "$ref": "#/$defs/dive" } },
14
+ "trips": { "type": "array", "items": { "$ref": "#/$defs/trip" } },
15
+ "courses": { "type": "array", "items": { "$ref": "#/$defs/course" } },
16
+ "sites": { "type": "array", "items": { "$ref": "#/$defs/dive_site" } },
17
+ "species": { "type": "array", "items": { "$ref": "#/$defs/species" } },
18
+ "gear": { "type": "array", "items": { "$ref": "#/$defs/gear_item" } },
19
+ "gear_sets": { "type": "array", "items": { "$ref": "#/$defs/gear_set" } },
20
+ "gear_service_schedules": { "type": "array", "items": { "$ref": "#/$defs/gear_service_schedule" } },
21
+ "gear_service_records": { "type": "array", "items": { "$ref": "#/$defs/gear_service_record" } },
22
+ "certifications": { "type": "array", "items": { "$ref": "#/$defs/certification" } },
23
+ "extensions": { "$ref": "#/$defs/extensions" }
24
+ },
25
+ "required": ["format", "version", "exported_at"],
26
+ "additionalProperties": false,
27
+ "$defs": {
28
+ "uuid": {
29
+ "type": "string",
30
+ "format": "uuid",
31
+ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
32
+ },
33
+ "uuid_list": {
34
+ "type": "array",
35
+ "items": { "$ref": "#/$defs/uuid" },
36
+ "uniqueItems": true
37
+ },
38
+ "date_time": {
39
+ "type": "string",
40
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?([Zz]|[+-][0-9]{2}:[0-9]{2})?$"
41
+ },
42
+ "offset_date_time": {
43
+ "type": "string",
44
+ "format": "date-time",
45
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?([Zz]|[+-][0-9]{2}:[0-9]{2})$"
46
+ },
47
+ "date": {
48
+ "type": "string",
49
+ "format": "date",
50
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
51
+ },
52
+ "notes": {
53
+ "type": "string",
54
+ "maxLength": 10000
55
+ },
56
+ "extensions": {
57
+ "type": "object",
58
+ "propertyNames": { "pattern": "^[a-z0-9][a-z0-9._-]*$" }
59
+ },
60
+ "position": {
61
+ "type": "object",
62
+ "properties": {
63
+ "latitude": { "type": "number", "minimum": -90, "maximum": 90 },
64
+ "longitude": { "type": "number", "minimum": -180, "maximum": 180 },
65
+ "extensions": { "$ref": "#/$defs/extensions" }
66
+ },
67
+ "required": ["latitude", "longitude"],
68
+ "additionalProperties": false
69
+ },
70
+ "bounding_box": {
71
+ "type": "object",
72
+ "properties": {
73
+ "south": { "type": "number", "minimum": -90, "maximum": 90 },
74
+ "north": { "type": "number", "minimum": -90, "maximum": 90 },
75
+ "west": { "type": "number", "minimum": -180, "maximum": 180 },
76
+ "east": { "type": "number", "minimum": -180, "maximum": 180 },
77
+ "extensions": { "$ref": "#/$defs/extensions" }
78
+ },
79
+ "required": ["south", "north", "west", "east"],
80
+ "additionalProperties": false
81
+ },
82
+ "generator": {
83
+ "type": "object",
84
+ "properties": {
85
+ "name": { "type": "string", "minLength": 1 },
86
+ "version": { "type": "string" },
87
+ "extensions": { "$ref": "#/$defs/extensions" }
88
+ },
89
+ "required": ["name"],
90
+ "additionalProperties": false
91
+ },
92
+ "diver": {
93
+ "type": "object",
94
+ "properties": {
95
+ "uuid": { "$ref": "#/$defs/uuid" },
96
+ "name": { "type": "string" },
97
+ "username": { "type": "string" },
98
+ "email": { "type": "string", "format": "email" },
99
+ "created_at": { "$ref": "#/$defs/date_time" },
100
+ "extensions": { "$ref": "#/$defs/extensions" }
101
+ },
102
+ "additionalProperties": false
103
+ },
104
+ "dive": {
105
+ "type": "object",
106
+ "properties": {
107
+ "uuid": { "$ref": "#/$defs/uuid" },
108
+ "dive_number": { "type": "integer" },
109
+ "started_at": { "$ref": "#/$defs/date_time" },
110
+ "duration": { "type": "integer", "exclusiveMinimum": 0 },
111
+ "notes": { "$ref": "#/$defs/notes" },
112
+ "max_depth": { "type": "number", "exclusiveMinimum": 0 },
113
+ "avg_depth": { "type": "number", "exclusiveMinimum": 0 },
114
+ "bottom_temperature": { "type": "number" },
115
+ "visibility": { "type": "number", "minimum": 0 },
116
+ "weight": { "type": "number", "minimum": 0 },
117
+ "water_type": { "enum": ["salt", "fresh", "brackish", "en13319"] },
118
+ "altitude": { "type": "integer", "minimum": -450, "maximum": 6500 },
119
+ "cns_start": { "type": "number", "minimum": 0 },
120
+ "cns_end": { "type": "number", "minimum": 0 },
121
+ "otu_start": { "type": "number", "minimum": 0 },
122
+ "otu_end": { "type": "number", "minimum": 0 },
123
+ "surface_pressure": { "type": "number", "minimum": 0.4, "maximum": 1.2 },
124
+ "entry_position": { "$ref": "#/$defs/position" },
125
+ "exit_position": { "$ref": "#/$defs/position" },
126
+ "trip_uuid": { "$ref": "#/$defs/uuid" },
127
+ "course_uuid": { "$ref": "#/$defs/uuid" },
128
+ "site_uuids": { "$ref": "#/$defs/uuid_list" },
129
+ "gear_uuids": { "$ref": "#/$defs/uuid_list" },
130
+ "species_uuids": { "$ref": "#/$defs/uuid_list" },
131
+ "cylinders": { "type": "array", "items": { "$ref": "#/$defs/cylinder" } },
132
+ "source_file": { "$ref": "#/$defs/stored_file" },
133
+ "profile": { "$ref": "#/$defs/profile" },
134
+ "created_at": { "$ref": "#/$defs/date_time" },
135
+ "extensions": { "$ref": "#/$defs/extensions" }
136
+ },
137
+ "required": ["uuid", "started_at"],
138
+ "additionalProperties": false
139
+ },
140
+ "cylinder": {
141
+ "type": "object",
142
+ "properties": {
143
+ "volume": { "type": "number", "exclusiveMinimum": 0 },
144
+ "start_pressure": { "type": "number", "exclusiveMinimum": 0, "maximum": 350 },
145
+ "end_pressure": { "type": "number", "minimum": 0, "maximum": 350 },
146
+ "oxygen": { "type": "number", "minimum": 0, "maximum": 100 },
147
+ "helium": { "type": "number", "minimum": 0, "maximum": 100 },
148
+ "po2_limit": { "type": "number", "minimum": 0.4, "maximum": 2.0 },
149
+ "gas_number": { "type": "integer", "minimum": 0 },
150
+ "role": { "enum": ["bottom", "deco", "diluent", "oxygen"] },
151
+ "usage": { "enum": ["parallel", "staged"] },
152
+ "extensions": { "$ref": "#/$defs/extensions" }
153
+ },
154
+ "additionalProperties": false
155
+ },
156
+ "profile": {
157
+ "type": "object",
158
+ "properties": {
159
+ "duration": { "type": "integer", "minimum": 0 },
160
+ "depth": { "$ref": "#/$defs/series" },
161
+ "ceiling": { "$ref": "#/$defs/series" },
162
+ "temperature": { "$ref": "#/$defs/series" },
163
+ "pressures": { "type": "array", "items": { "$ref": "#/$defs/pressure_series" } },
164
+ "events": { "type": "array", "items": { "$ref": "#/$defs/event" } },
165
+ "extensions": { "$ref": "#/$defs/extensions" }
166
+ },
167
+ "required": ["duration"],
168
+ "additionalProperties": false
169
+ },
170
+ "series": {
171
+ "type": "object",
172
+ "properties": {
173
+ "times": { "type": "array", "items": { "type": "integer", "minimum": 0 } },
174
+ "values": { "type": "array", "items": { "type": "integer" } },
175
+ "extensions": { "$ref": "#/$defs/extensions" }
176
+ },
177
+ "required": ["times", "values"],
178
+ "additionalProperties": false
179
+ },
180
+ "pressure_series": {
181
+ "type": "object",
182
+ "properties": {
183
+ "times": { "type": "array", "items": { "type": "integer", "minimum": 0 } },
184
+ "values": { "type": "array", "items": { "type": "integer" } },
185
+ "gas_number": { "type": "integer", "minimum": 0 },
186
+ "extensions": { "$ref": "#/$defs/extensions" }
187
+ },
188
+ "required": ["times", "values", "gas_number"],
189
+ "additionalProperties": false
190
+ },
191
+ "event": {
192
+ "type": "object",
193
+ "properties": {
194
+ "time": { "type": "integer", "minimum": 0 },
195
+ "type": { "enum": ["gas_switch", "deep_stop", "safety_stop", "bookmark", "other"] },
196
+ "gas_number": { "type": "integer", "minimum": 0 },
197
+ "label": { "type": "string" },
198
+ "extensions": { "$ref": "#/$defs/extensions" }
199
+ },
200
+ "required": ["time", "type"],
201
+ "additionalProperties": false,
202
+ "if": {
203
+ "properties": { "type": { "const": "other" } },
204
+ "required": ["type"]
205
+ },
206
+ "then": {
207
+ "required": ["label"],
208
+ "properties": { "label": { "type": "string", "minLength": 1 } }
209
+ }
210
+ },
211
+ "stored_file": {
212
+ "type": "object",
213
+ "properties": {
214
+ "uuid": { "$ref": "#/$defs/uuid" },
215
+ "original_filename": { "type": "string", "minLength": 1, "maxLength": 255 },
216
+ "content_type": { "type": "string", "minLength": 1, "maxLength": 64 },
217
+ "byte_size": { "type": "integer", "minimum": 0 },
218
+ "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
219
+ "archive_path": { "type": "string", "minLength": 1 },
220
+ "extensions": { "$ref": "#/$defs/extensions" }
221
+ },
222
+ "required": ["uuid", "original_filename", "content_type", "byte_size", "sha256"],
223
+ "additionalProperties": false
224
+ },
225
+ "trip": {
226
+ "type": "object",
227
+ "properties": {
228
+ "uuid": { "$ref": "#/$defs/uuid" },
229
+ "name": { "type": "string", "minLength": 1, "maxLength": 255 },
230
+ "locations": { "type": "array", "items": { "$ref": "#/$defs/trip_location" } },
231
+ "starts_on": { "$ref": "#/$defs/date" },
232
+ "ends_on": { "$ref": "#/$defs/date" },
233
+ "notes": { "$ref": "#/$defs/notes" },
234
+ "created_at": { "$ref": "#/$defs/date_time" },
235
+ "extensions": { "$ref": "#/$defs/extensions" }
236
+ },
237
+ "required": ["uuid", "name", "starts_on"],
238
+ "additionalProperties": false
239
+ },
240
+ "trip_location": {
241
+ "type": "object",
242
+ "properties": {
243
+ "name": { "type": "string", "minLength": 1, "maxLength": 255 },
244
+ "display_name": { "type": "string", "maxLength": 512 },
245
+ "position": { "$ref": "#/$defs/position" },
246
+ "bbox": { "$ref": "#/$defs/bounding_box" },
247
+ "extensions": { "$ref": "#/$defs/extensions" }
248
+ },
249
+ "required": ["name"],
250
+ "additionalProperties": false,
251
+ "dependentRequired": { "bbox": ["position"] }
252
+ },
253
+ "course": {
254
+ "type": "object",
255
+ "properties": {
256
+ "uuid": { "$ref": "#/$defs/uuid" },
257
+ "name": { "type": "string", "minLength": 1, "maxLength": 255 },
258
+ "agency": {
259
+ "enum": [
260
+ "padi", "ssi", "naui", "sdi", "tdi", "cmas", "raid", "bsac",
261
+ "gue", "iantd", "psai", "dan", "efr", "andi", "snsi", "acuc",
262
+ "pss", "ida", "other"
263
+ ]
264
+ },
265
+ "agency_other": { "type": "string", "minLength": 1, "maxLength": 64 },
266
+ "status": {
267
+ "enum": ["planned", "in_progress", "completed", "incomplete", "provisional", "not_passed"]
268
+ },
269
+ "starts_on": { "$ref": "#/$defs/date" },
270
+ "ends_on": { "$ref": "#/$defs/date" },
271
+ "instructor_name": { "type": "string", "maxLength": 255 },
272
+ "instructor_number": { "type": "string", "maxLength": 64 },
273
+ "training_center": { "type": "string", "maxLength": 255 },
274
+ "notes": { "$ref": "#/$defs/notes" },
275
+ "created_at": { "$ref": "#/$defs/date_time" },
276
+ "extensions": { "$ref": "#/$defs/extensions" }
277
+ },
278
+ "required": ["uuid", "name", "agency"],
279
+ "additionalProperties": false,
280
+ "if": {
281
+ "properties": { "agency": { "const": "other" } },
282
+ "required": ["agency"]
283
+ },
284
+ "then": {
285
+ "required": ["agency_other"]
286
+ },
287
+ "else": {
288
+ "not": { "required": ["agency_other"] }
289
+ }
290
+ },
291
+ "dive_site": {
292
+ "type": "object",
293
+ "properties": {
294
+ "uuid": { "$ref": "#/$defs/uuid" },
295
+ "name": { "type": "string", "minLength": 1, "maxLength": 255 },
296
+ "location": { "type": "string", "maxLength": 255 },
297
+ "position": { "$ref": "#/$defs/position" },
298
+ "notes": { "$ref": "#/$defs/notes" },
299
+ "created_at": { "$ref": "#/$defs/date_time" },
300
+ "extensions": { "$ref": "#/$defs/extensions" }
301
+ },
302
+ "required": ["uuid", "name"],
303
+ "additionalProperties": false
304
+ },
305
+ "species": {
306
+ "type": "object",
307
+ "properties": {
308
+ "uuid": { "$ref": "#/$defs/uuid" },
309
+ "aphia_id": { "type": "integer", "exclusiveMinimum": 0 },
310
+ "scientific_name": { "type": "string", "minLength": 1, "maxLength": 255 },
311
+ "common_name": { "type": "string", "minLength": 1, "maxLength": 255 },
312
+ "rank": { "type": "string", "maxLength": 64 },
313
+ "wikidata_qid": { "type": "string", "minLength": 1, "maxLength": 32 },
314
+ "created_at": { "$ref": "#/$defs/date_time" },
315
+ "extensions": { "$ref": "#/$defs/extensions" }
316
+ },
317
+ "required": ["uuid"],
318
+ "additionalProperties": false,
319
+ "anyOf": [
320
+ { "required": ["aphia_id"] },
321
+ { "required": ["scientific_name"] },
322
+ { "required": ["common_name"] },
323
+ { "required": ["wikidata_qid"] }
324
+ ]
325
+ },
326
+ "gear_item": {
327
+ "type": "object",
328
+ "properties": {
329
+ "uuid": { "$ref": "#/$defs/uuid" },
330
+ "name": { "type": "string", "minLength": 1, "maxLength": 255 },
331
+ "brand": { "type": "string", "maxLength": 255 },
332
+ "type": {
333
+ "enum": [
334
+ "mask", "snorkel", "fins", "wetsuit", "drysuit", "vest", "hood",
335
+ "gloves", "boots", "bcd", "regulator", "computer", "cylinder",
336
+ "light", "smb", "mirror", "whistle", "reel", "knife", "line_cutter",
337
+ "shears", "compass", "camera", "other"
338
+ ]
339
+ },
340
+ "notes": { "$ref": "#/$defs/notes" },
341
+ "rented": { "type": "boolean" },
342
+ "archived": { "type": "boolean" },
343
+ "archived_at": { "$ref": "#/$defs/date_time" },
344
+ "dive_count": { "type": "integer", "minimum": 0 },
345
+ "created_at": { "$ref": "#/$defs/date_time" },
346
+ "extensions": { "$ref": "#/$defs/extensions" }
347
+ },
348
+ "required": ["uuid", "name"],
349
+ "additionalProperties": false
350
+ },
351
+ "gear_set": {
352
+ "type": "object",
353
+ "properties": {
354
+ "uuid": { "$ref": "#/$defs/uuid" },
355
+ "name": { "type": "string", "minLength": 1, "maxLength": 255 },
356
+ "weight": { "type": "number", "minimum": 0 },
357
+ "gear_uuids": { "$ref": "#/$defs/uuid_list" },
358
+ "created_at": { "$ref": "#/$defs/date_time" },
359
+ "extensions": { "$ref": "#/$defs/extensions" }
360
+ },
361
+ "required": ["uuid", "name"],
362
+ "additionalProperties": false
363
+ },
364
+ "service_type": {
365
+ "enum": ["service", "visual_inspection", "hydrostatic_test", "battery", "oxygen_clean", "other"]
366
+ },
367
+ "gear_service_schedule": {
368
+ "type": "object",
369
+ "properties": {
370
+ "uuid": { "$ref": "#/$defs/uuid" },
371
+ "gear_uuid": { "$ref": "#/$defs/uuid" },
372
+ "type": { "$ref": "#/$defs/service_type" },
373
+ "label": { "type": "string", "maxLength": 120 },
374
+ "starts_on": { "$ref": "#/$defs/date" },
375
+ "interval_months": { "type": "integer", "exclusiveMinimum": 0 },
376
+ "interval_dives": { "type": "integer", "exclusiveMinimum": 0 },
377
+ "dive_count_at_start": { "type": "integer", "minimum": 0 },
378
+ "active": { "type": "boolean" },
379
+ "last_service_on": { "$ref": "#/$defs/date" },
380
+ "next_due_on": { "$ref": "#/$defs/date" },
381
+ "next_due_at_dive_count": { "type": "integer", "minimum": 0 },
382
+ "created_at": { "$ref": "#/$defs/date_time" },
383
+ "extensions": { "$ref": "#/$defs/extensions" }
384
+ },
385
+ "required": ["uuid", "gear_uuid", "type", "starts_on"],
386
+ "additionalProperties": false,
387
+ "anyOf": [
388
+ { "required": ["interval_months"] },
389
+ { "required": ["interval_dives"] }
390
+ ]
391
+ },
392
+ "gear_service_record": {
393
+ "type": "object",
394
+ "properties": {
395
+ "uuid": { "$ref": "#/$defs/uuid" },
396
+ "gear_uuid": { "$ref": "#/$defs/uuid" },
397
+ "gear_service_schedule_uuid": { "$ref": "#/$defs/uuid" },
398
+ "type": { "$ref": "#/$defs/service_type" },
399
+ "serviced_on": { "$ref": "#/$defs/date" },
400
+ "dive_count_at_service": { "type": "integer", "minimum": 0 },
401
+ "label": { "type": "string", "maxLength": 120 },
402
+ "performed_by": { "type": "string", "maxLength": 255 },
403
+ "notes": { "$ref": "#/$defs/notes" },
404
+ "created_at": { "$ref": "#/$defs/date_time" },
405
+ "extensions": { "$ref": "#/$defs/extensions" }
406
+ },
407
+ "required": ["uuid", "gear_uuid", "type", "serviced_on"],
408
+ "additionalProperties": false
409
+ },
410
+ "certification": {
411
+ "type": "object",
412
+ "properties": {
413
+ "uuid": { "$ref": "#/$defs/uuid" },
414
+ "agency": {
415
+ "enum": [
416
+ "padi", "ssi", "naui", "sdi", "tdi", "cmas", "raid", "bsac",
417
+ "gue", "iantd", "psai", "dan", "efr", "andi", "snsi", "acuc",
418
+ "pss", "ida", "other"
419
+ ]
420
+ },
421
+ "agency_other": { "type": "string", "minLength": 1, "maxLength": 64 },
422
+ "name": { "type": "string", "minLength": 1, "maxLength": 255 },
423
+ "certification_number": { "type": "string", "maxLength": 64 },
424
+ "certified_on": { "$ref": "#/$defs/date" },
425
+ "expires_on": { "$ref": "#/$defs/date" },
426
+ "instructor_name": { "type": "string", "maxLength": 255 },
427
+ "instructor_number": { "type": "string", "maxLength": 64 },
428
+ "training_center": { "type": "string", "maxLength": 255 },
429
+ "course_uuid": { "$ref": "#/$defs/uuid" },
430
+ "notes": { "$ref": "#/$defs/notes" },
431
+ "front_file": { "$ref": "#/$defs/stored_file" },
432
+ "back_file": { "$ref": "#/$defs/stored_file" },
433
+ "created_at": { "$ref": "#/$defs/date_time" },
434
+ "extensions": { "$ref": "#/$defs/extensions" }
435
+ },
436
+ "required": ["uuid", "agency", "name"],
437
+ "additionalProperties": false,
438
+ "if": {
439
+ "properties": { "agency": { "const": "other" } },
440
+ "required": ["agency"]
441
+ },
442
+ "then": {
443
+ "required": ["agency_other"]
444
+ },
445
+ "else": {
446
+ "not": { "required": ["agency_other"] }
447
+ }
448
+ }
449
+ }
450
+ }