datacontract-cli 0.10.20__py3-none-any.whl → 0.10.22__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 datacontract-cli might be problematic. Click here for more details.

Files changed (38) hide show
  1. datacontract/{web.py → api.py} +55 -3
  2. datacontract/breaking/breaking.py +1 -1
  3. datacontract/breaking/breaking_rules.py +1 -1
  4. datacontract/cli.py +32 -10
  5. datacontract/data_contract.py +14 -100
  6. datacontract/engines/data_contract_checks.py +735 -0
  7. datacontract/engines/data_contract_test.py +51 -0
  8. datacontract/engines/soda/check_soda_execute.py +36 -30
  9. datacontract/engines/soda/connections/kafka.py +8 -3
  10. datacontract/export/avro_converter.py +2 -0
  11. datacontract/export/custom_converter.py +40 -0
  12. datacontract/export/exporter.py +1 -2
  13. datacontract/export/exporter_factory.py +4 -12
  14. datacontract/export/sodacl_converter.py +22 -294
  15. datacontract/export/sql_type_converter.py +7 -2
  16. datacontract/imports/odcs_importer.py +6 -3
  17. datacontract/imports/odcs_v3_importer.py +2 -0
  18. datacontract/imports/sql_importer.py +229 -29
  19. datacontract/lint/urls.py +4 -4
  20. datacontract/model/data_contract_specification.py +130 -129
  21. datacontract/model/exceptions.py +4 -1
  22. datacontract/model/run.py +25 -18
  23. datacontract/templates/datacontract.html +16 -2
  24. datacontract/templates/partials/definition.html +3 -95
  25. datacontract/templates/partials/model_field.html +13 -0
  26. datacontract/templates/partials/quality.html +49 -0
  27. datacontract/templates/style/output.css +151 -152
  28. {datacontract_cli-0.10.20.dist-info → datacontract_cli-0.10.22.dist-info}/METADATA +238 -184
  29. {datacontract_cli-0.10.20.dist-info → datacontract_cli-0.10.22.dist-info}/RECORD +34 -34
  30. datacontract/engines/soda/connections/dask.py +0 -28
  31. datacontract/export/odcs_v2_exporter.py +0 -124
  32. datacontract/imports/odcs_v2_importer.py +0 -177
  33. datacontract/lint/linters/example_model_linter.py +0 -91
  34. /datacontract/{model → breaking}/breaking_change.py +0 -0
  35. {datacontract_cli-0.10.20.dist-info → datacontract_cli-0.10.22.dist-info}/LICENSE +0 -0
  36. {datacontract_cli-0.10.20.dist-info → datacontract_cli-0.10.22.dist-info}/WHEEL +0 -0
  37. {datacontract_cli-0.10.20.dist-info → datacontract_cli-0.10.22.dist-info}/entry_points.txt +0 -0
  38. {datacontract_cli-0.10.20.dist-info → datacontract_cli-0.10.22.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List
3
3
 
4
4
  import pydantic as pyd
5
5
  import yaml
@@ -32,9 +32,9 @@ DATACONTRACT_TYPES = [
32
32
 
33
33
 
34
34
  class Contact(pyd.BaseModel):
35
- name: str = None
36
- url: str = None
37
- email: str = None
35
+ name: str | None = None
36
+ url: str | None = None
37
+ email: str | None = None
38
38
 
39
39
  model_config = pyd.ConfigDict(
40
40
  extra="allow",
@@ -42,37 +42,37 @@ class Contact(pyd.BaseModel):
42
42
 
43
43
 
44
44
  class ServerRole(pyd.BaseModel):
45
- name: str = None
46
- description: str = None
45
+ name: str | None = None
46
+ description: str | None = None
47
47
  model_config = pyd.ConfigDict(
48
48
  extra="allow",
49
49
  )
50
50
 
51
51
 
52
52
  class Server(pyd.BaseModel):
53
- type: str = None
54
- description: str = None
55
- environment: str = None
56
- format: str = None
57
- project: str = None
58
- dataset: str = None
59
- path: str = None
60
- delimiter: str = None
61
- endpointUrl: str = None
62
- location: str = None
63
- account: str = None
64
- database: str = None
65
- schema_: str = pyd.Field(default=None, alias="schema")
66
- host: str = None
67
- port: int = None
68
- catalog: str = None
69
- topic: str = None
70
- http_path: str = None # Use ENV variable
71
- token: str = None # Use ENV variable
72
- dataProductId: str = None
73
- outputPortId: str = None
74
- driver: str = None
75
- storageAccount: str = None
53
+ type: str | None = None
54
+ description: str | None = None
55
+ environment: str | None = None
56
+ format: str | None = None
57
+ project: str | None = None
58
+ dataset: str | None = None
59
+ path: str | None = None
60
+ delimiter: str | None = None
61
+ endpointUrl: str | None = None
62
+ location: str | None = None
63
+ account: str | None = None
64
+ database: str | None = None
65
+ schema_: str | None = pyd.Field(default=None, alias="schema")
66
+ host: str | None = None
67
+ port: int | None = None
68
+ catalog: str | None = None
69
+ topic: str | None = None
70
+ http_path: str | None = None # Use ENV variable
71
+ token: str | None = None # Use ENV variable
72
+ dataProductId: str | None = None
73
+ outputPortId: str | None = None
74
+ driver: str | None = None
75
+ storageAccount: str | None = None
76
76
  roles: List[ServerRole] = None
77
77
 
78
78
  model_config = pyd.ConfigDict(
@@ -81,11 +81,11 @@ class Server(pyd.BaseModel):
81
81
 
82
82
 
83
83
  class Terms(pyd.BaseModel):
84
- usage: str = None
85
- limitations: str = None
86
- billing: str = None
87
- noticePeriod: str = None
88
- description: str = None
84
+ usage: str | None = None
85
+ limitations: str | None = None
86
+ billing: str | None = None
87
+ noticePeriod: str | None = None
88
+ description: str | None = None
89
89
 
90
90
  model_config = pyd.ConfigDict(
91
91
  extra="allow",
@@ -93,26 +93,27 @@ class Terms(pyd.BaseModel):
93
93
 
94
94
 
95
95
  class Definition(pyd.BaseModel):
96
- domain: str = None
97
- name: str = None
98
- title: str = None
99
- description: str = None
100
- type: str = None
96
+ domain: str | None = None
97
+ name: str | None = None
98
+ title: str | None = None
99
+ description: str | None = None
100
+ type: str | None = None
101
101
  enum: List[str] = []
102
- format: str = None
103
- minLength: int = None
104
- maxLength: int = None
105
- pattern: str = None
106
- minimum: int = None
107
- exclusiveMinimum: int = None
108
- maximum: int = None
109
- exclusiveMaximum: int = None
110
- pii: bool = None
111
- classification: str = None
102
+ format: str | None = None
103
+ minLength: int | None = None
104
+ maxLength: int | None = None
105
+ pattern: str | None = None
106
+ minimum: int | None = None
107
+ exclusiveMinimum: int | None = None
108
+ maximum: int | None = None
109
+ exclusiveMaximum: int | None = None
110
+ pii: bool | None = None
111
+ classification: str | None = None
112
112
  fields: Dict[str, "Field"] = {}
113
+ items: "Field" = None
113
114
  tags: List[str] = []
114
115
  links: Dict[str, str] = {}
115
- example: str = None
116
+ example: str | None = None
116
117
  examples: List[Any] | None = None
117
118
 
118
119
  model_config = pyd.ConfigDict(
@@ -121,20 +122,20 @@ class Definition(pyd.BaseModel):
121
122
 
122
123
 
123
124
  class Quality(pyd.BaseModel):
124
- type: str = None
125
- description: str = None
126
- query: str = None
127
- dialect: str = None
128
- mustBe: int = None
129
- mustNotBe: int = None
130
- mustBeGreaterThan: int = None
131
- mustBeGreaterThanOrEqualTo: int = None
132
- mustBeLessThan: int = None
133
- mustBeLessThanOrEqualTo: int = None
125
+ type: str | None = None
126
+ description: str | None = None
127
+ query: str | None = None
128
+ dialect: str | None = None
129
+ mustBe: int | None = None
130
+ mustNotBe: int | None = None
131
+ mustBeGreaterThan: int | None = None
132
+ mustBeGreaterThanOrEqualTo: int | None = None
133
+ mustBeLessThan: int | None = None
134
+ mustBeLessThanOrEqualTo: int | None = None
134
135
  mustBeBetween: List[int] = None
135
136
  mustNotBeBetween: List[int] = None
136
- engine: str = None
137
- implementation: str | Dict[str, Any] = None
137
+ engine: str | None = None
138
+ implementation: str | Dict[str, Any] | None = None
138
139
 
139
140
  model_config = pyd.ConfigDict(
140
141
  extra="allow",
@@ -144,26 +145,26 @@ class Quality(pyd.BaseModel):
144
145
  class Field(pyd.BaseModel):
145
146
  ref: str = pyd.Field(default=None, alias="$ref")
146
147
  title: str | None = None
147
- type: str = None
148
- format: str = None
149
- required: bool = None
148
+ type: str | None = None
149
+ format: str | None = None
150
+ required: bool | None = None
150
151
  primary: bool = pyd.Field(
151
152
  default=None,
152
153
  deprecated="Removed in Data Contract Specification v1.1.0. Use primaryKey instead.",
153
154
  )
154
155
  primaryKey: bool | None = None
155
156
  unique: bool | None = None
156
- references: str = None
157
+ references: str | None = None
157
158
  description: str | None = None
158
159
  pii: bool | None = None
159
160
  classification: str | None = None
160
- pattern: str = None
161
- minLength: int = None
162
- maxLength: int = None
163
- minimum: int = None
164
- exclusiveMinimum: int = None
165
- maximum: int = None
166
- exclusiveMaximum: int = None
161
+ pattern: str | None = None
162
+ minLength: int | None = None
163
+ maxLength: int | None = None
164
+ minimum: int | None = None
165
+ exclusiveMinimum: int | None = None
166
+ maximum: int | None = None
167
+ exclusiveMaximum: int | None = None
167
168
  enum: List[str] | None = []
168
169
  tags: List[str] | None = []
169
170
  links: Dict[str, str] = {}
@@ -171,11 +172,11 @@ class Field(pyd.BaseModel):
171
172
  items: "Field" = None
172
173
  keys: "Field" = None
173
174
  values: "Field" = None
174
- precision: int = None
175
- scale: int = None
176
- example: str = pyd.Field(
175
+ precision: int | None = None
176
+ scale: int | None = None
177
+ example: Any | None = pyd.Field(
177
178
  default=None,
178
- deprecated="Removed in Data Contract Specification v1.1.0. Use " "examples instead.",
179
+ deprecated="Removed in Data Contract Specification v1.1.0. Use examples instead.",
179
180
  )
180
181
  examples: List[Any] | None = None
181
182
  quality: List[Quality] | None = []
@@ -187,10 +188,10 @@ class Field(pyd.BaseModel):
187
188
 
188
189
 
189
190
  class Model(pyd.BaseModel):
190
- description: Optional[str] = None
191
- type: Optional[str] = None
192
- namespace: Optional[str] = None
193
- title: Optional[str] = None
191
+ description: str | None = None
192
+ type: str | None = None
193
+ namespace: str | None = None
194
+ title: str | None = None
194
195
  fields: Dict[str, Field] = {}
195
196
  quality: List[Quality] | None = []
196
197
  primaryKey: List[str] | None = []
@@ -204,12 +205,12 @@ class Model(pyd.BaseModel):
204
205
 
205
206
 
206
207
  class Info(pyd.BaseModel):
207
- title: str = None
208
- version: str = None
209
- status: str = None
210
- description: str = None
211
- owner: str = None
212
- contact: Contact = None
208
+ title: str | None = None
209
+ version: str | None = None
210
+ status: str | None = None
211
+ description: str | None = None
212
+ owner: str | None = None
213
+ contact: Contact | None = None
213
214
 
214
215
  model_config = pyd.ConfigDict(
215
216
  extra="allow",
@@ -217,91 +218,91 @@ class Info(pyd.BaseModel):
217
218
 
218
219
 
219
220
  class Example(pyd.BaseModel):
220
- type: str = None
221
- description: str = None
222
- model: str = None
221
+ type: str | None = None
222
+ description: str | None = None
223
+ model: str | None = None
223
224
  data: str | object = None
224
225
 
225
226
 
226
227
  # Deprecated Quality class
227
228
  class DeprecatedQuality(pyd.BaseModel):
228
- type: str = None
229
+ type: str | None = None
229
230
  specification: str | object = None
230
231
 
231
232
 
232
233
  class Availability(pyd.BaseModel):
233
- description: Optional[str] = None
234
- percentage: Optional[str] = None
234
+ description: str | None = None
235
+ percentage: str | None = None
235
236
 
236
237
 
237
238
  class Retention(pyd.BaseModel):
238
- description: Optional[str] = None
239
- period: Optional[str] = None
240
- unlimited: Optional[bool] = None
241
- timestampField: Optional[str] = None
239
+ description: str | None = None
240
+ period: str | None = None
241
+ unlimited: bool | None = None
242
+ timestampField: str | None = None
242
243
 
243
244
 
244
245
  class Latency(pyd.BaseModel):
245
- description: Optional[str] = None
246
- threshold: Optional[str] = None
247
- sourceTimestampField: Optional[str] = None
248
- processedTimestampField: Optional[str] = None
246
+ description: str | None = None
247
+ threshold: str | None = None
248
+ sourceTimestampField: str | None = None
249
+ processedTimestampField: str | None = None
249
250
 
250
251
 
251
252
  class Freshness(pyd.BaseModel):
252
- description: Optional[str] = None
253
- threshold: Optional[str] = None
254
- timestampField: Optional[str] = None
253
+ description: str | None = None
254
+ threshold: str | None = None
255
+ timestampField: str | None = None
255
256
 
256
257
 
257
258
  class Frequency(pyd.BaseModel):
258
- description: Optional[str] = None
259
- type: Optional[str] = None
260
- interval: Optional[str] = None
261
- cron: Optional[str] = None
259
+ description: str | None = None
260
+ type: str | None = None
261
+ interval: str | None = None
262
+ cron: str | None = None
262
263
 
263
264
 
264
265
  class Support(pyd.BaseModel):
265
- description: Optional[str] = None
266
- time: Optional[str] = None
267
- responseTime: Optional[str] = None
266
+ description: str | None = None
267
+ time: str | None = None
268
+ responseTime: str | None = None
268
269
 
269
270
 
270
271
  class Backup(pyd.BaseModel):
271
- description: Optional[str] = None
272
- interval: Optional[str] = None
273
- cron: Optional[str] = None
274
- recoveryTime: Optional[str] = None
275
- recoveryPoint: Optional[str] = None
272
+ description: str | None = None
273
+ interval: str | None = None
274
+ cron: str | None = None
275
+ recoveryTime: str | None = None
276
+ recoveryPoint: str | None = None
276
277
 
277
278
 
278
279
  class ServiceLevel(pyd.BaseModel):
279
- availability: Optional[Availability] = None
280
- retention: Optional[Retention] = None
281
- latency: Optional[Latency] = None
282
- freshness: Optional[Freshness] = None
283
- frequency: Optional[Frequency] = None
284
- support: Optional[Support] = None
285
- backup: Optional[Backup] = None
280
+ availability: Availability | None = None
281
+ retention: Retention | None = None
282
+ latency: Latency | None = None
283
+ freshness: Freshness | None = None
284
+ frequency: Frequency | None = None
285
+ support: Support | None = None
286
+ backup: Backup | None = None
286
287
 
287
288
 
288
289
  class DataContractSpecification(pyd.BaseModel):
289
- dataContractSpecification: str = None
290
- id: str = None
291
- info: Info = None
290
+ dataContractSpecification: str | None = None
291
+ id: str | None = None
292
+ info: Info | None = None
292
293
  servers: Dict[str, Server] = {}
293
- terms: Terms = None
294
+ terms: Terms | None = None
294
295
  models: Dict[str, Model] = {}
295
296
  definitions: Dict[str, Definition] = {}
296
297
  examples: List[Example] = pyd.Field(
297
298
  default_factory=list,
298
299
  deprecated="Removed in Data Contract Specification " "v1.1.0. Use models.examples instead.",
299
300
  )
300
- quality: DeprecatedQuality = pyd.Field(
301
+ quality: DeprecatedQuality | None = pyd.Field(
301
302
  default=None,
302
303
  deprecated="Removed in Data Contract Specification v1.1.0. Use " "model-level and field-level quality instead.",
303
304
  )
304
- servicelevels: Optional[ServiceLevel] = None
305
+ servicelevels: ServiceLevel | None = None
305
306
  links: Dict[str, str] = {}
306
307
  tags: List[str] = []
307
308
 
@@ -1,3 +1,6 @@
1
+ from datacontract.model.run import ResultEnum
2
+
3
+
1
4
  class DataContractException(Exception):
2
5
  """Exception raised for errors in the execution of a run.
3
6
 
@@ -19,7 +22,7 @@ class DataContractException(Exception):
19
22
  engine="datacontract",
20
23
  model=None,
21
24
  original_exception=None,
22
- result: str = "failed",
25
+ result: ResultEnum = ResultEnum.failed,
23
26
  message="Run operation failed",
24
27
  ):
25
28
  self.type = type
datacontract/model/run.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from datetime import datetime, timezone
3
3
  from enum import Enum
4
- from typing import List, Optional
4
+ from typing import List
5
5
  from uuid import UUID, uuid4
6
6
 
7
7
  from pydantic import BaseModel
@@ -17,15 +17,22 @@ class ResultEnum(str, Enum):
17
17
 
18
18
 
19
19
  class Check(BaseModel):
20
+ id: str | None = None
21
+ key: str | None = None
22
+ category: str | None = None
20
23
  type: str
21
- name: Optional[str]
22
- result: ResultEnum
23
- engine: str
24
- reason: Optional[str] = None
25
- model: Optional[str] = None
26
- field: Optional[str] = None
27
- details: Optional[str] = None
28
- diagnostics: Optional[dict] = None
24
+ name: str | None = None
25
+ model: str | None = None
26
+ field: str | None = None
27
+
28
+ engine: str | None = None
29
+ language: str | None = None
30
+ implementation: str | None = None
31
+
32
+ result: ResultEnum | None = None
33
+ reason: str | None = None
34
+ details: str | None = None
35
+ diagnostics: dict | None = None
29
36
 
30
37
 
31
38
  class Log(BaseModel):
@@ -36,16 +43,16 @@ class Log(BaseModel):
36
43
 
37
44
  class Run(BaseModel):
38
45
  runId: UUID
39
- dataContractId: Optional[str] = None
40
- dataContractVersion: Optional[str] = None
41
- dataProductId: Optional[str] = None
42
- outputPortId: Optional[str] = None
43
- server: Optional[str] = None
44
- timestampStart: datetime
45
- timestampEnd: datetime
46
+ dataContractId: str | None = None
47
+ dataContractVersion: str | None = None
48
+ dataProductId: str | None = None
49
+ outputPortId: str | None = None
50
+ server: str | None = None
51
+ timestampStart: datetime | None
52
+ timestampEnd: datetime | None
46
53
  result: ResultEnum = ResultEnum.unknown
47
- checks: List[Check]
48
- logs: List[Log]
54
+ checks: List[Check] | None
55
+ logs: List[Log] | None
49
56
 
50
57
  def has_passed(self):
51
58
  self.calculate_result()
@@ -146,14 +146,28 @@
146
146
  {{ render_partial('partials/model_field.html', nested = False, field_name=field_name, field = field, level = 0) }}
147
147
  {% endfor %}
148
148
  </tbody>
149
- {% if model.primaryKey %}
150
- <tfoot class="bg-gray-50">
149
+ {% if model.primaryKey or model.quality %}
150
+ <tfoot class="divide-y divide-gray-200 bg-white">
151
+ {% if model.quality %}
152
+ {% for quality in model.quality %}
151
153
  <tr>
154
+ <th scope="colgroup" colspan="4"
155
+ class="py-2 pl-4 pr-3 text-left text-sm font-normal sm:pl-6 bg-white">
156
+ {{ render_partial('partials/quality.html', quality = quality)}}
157
+ </th>
158
+ </tr>
159
+ {% endfor %}
160
+ {% endif %}
161
+
162
+ {% if model.primaryKey %}
163
+ <tr class="bg-gray-50">
152
164
  <th scope="colgroup" colspan="4"
153
165
  class="py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
154
166
  <span>Primary Key: {{ model.primaryKey }}</span>
155
167
  </th>
156
168
  </tr>
169
+ {% endif %}
170
+
157
171
  </tfoot>
158
172
  {% endif %}
159
173
  </table>
@@ -3,112 +3,20 @@
3
3
  <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
4
4
  <div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
5
5
 
6
- <table class="min-w-full divide-y divide-gray-300">
6
+ <table class="min-w-full divide-y divide-gray-300" id="/definitions/{{ definition_name }}">
7
7
  <thead class="bg-gray-50">
8
8
  <tr>
9
9
  <th scope="colgroup" colspan="3" class="py-2 pl-4 pr-3 text-left font-semibold text-gray-900 sm:pl-6">
10
10
  <span>{{ definition_name }}</span>
11
- <span class="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10">{{ definition.domain }}</span>
12
11
  <div class="text-sm font-medium text-gray-500">{{ definition.description }}</div>
13
12
  </th>
14
13
  </tr>
15
14
  </thead>
16
15
  <tbody class="divide-y divide-gray-200 bg-white">
17
- <tr id="/definitions/{{ definition_name }}">
18
- <td class="whitespace-nowrap py-2 pl-4 pr-2 text-sm font-medium text-gray-900 sm:pl-6 w-2/12">
19
- <div class="py-2 text-sm">
20
- {% if definition.title %}
21
- <div>{{ definition.title }}</div>
22
- {% endif %}
23
- <div class="font-mono">{{ definition_name }}</div>
24
- </div>
25
- </td>
26
- <td class="whitespace-nowrap px-1 py-2 text-sm text-gray-500 w-1/12">
27
- <div class="py-2 text-sm">
28
- {{ definition.type }}
29
- {% if definition.format %}
30
- <span class="inline-flex items-center rounded-md bg-blue-50 px-1 py-1 text-xs font-medium text-blue-600 ring-1 ring-inset ring-blue-500/10 mr-1 mt-1">{{ definition.format }}</span>
31
- {% endif %}
32
- </div>
33
- </td>
34
- <td class="px-3 py-2 text-sm text-gray-500 w-9/12">
35
- {% if definition.example %}
36
- <div class="mt-1 italic">
37
- <span class="text-gray-600">Example:</span> <span class="font-mono">{{ definition.example }}</span>
38
- </div>
39
- {% endif %}
40
16
 
41
- {% if definition.examples %}
42
- <div class="mt-1 italic">
43
- <span class="text-gray-600">Examples:</span>
44
- {% for example in definition.examples %}
45
- <span class="font-mono">{{ example }}</span>{% if not loop.last %}, {% endif %}
46
- {% endfor %}
47
- </div>
48
- {% endif %}
17
+ {{ render_partial('partials/model_field.html', nested = False, field_name=definition_name,
18
+ field = definition, level = 0) }}
49
19
 
50
- {% if definition.tags %}
51
- <div>
52
- <span class="text-gray-600 italic">Tags:</span>
53
- {% for tag in definition.tags %}
54
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">{{ tag }}</span>
55
- {% endfor %}
56
- </div>
57
- {% endif %}
58
- {% if definition.enum %}
59
- <div class="py-2 text-sm">
60
- <span class="text-gray-600 italic">Enum:</span>
61
- {% for value in definition.enum %}
62
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">{{ value }}</span>
63
- {% endfor %}
64
- </div>
65
- {% endif %}
66
- <div>
67
- {% if definition.minLength %}
68
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">minLength:{{ definition.minLength }}</span>
69
- {% endif %}
70
- {% if definition.maxLength %}
71
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">maxLength:{{ definition.maxLength }}</span>
72
- {% endif %}
73
- {% if definition.pattern %}
74
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">pattern:{{ definition.pattern }}</span>
75
- {% endif %}
76
- {% if definition.precision %}
77
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">precision:{{ definition.precision }}</span>
78
- {% endif %}
79
- {% if definition.scale %}
80
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">scale:{{ definition.scale }}</span>
81
- {% endif %}
82
- {% if definition.minimum %}
83
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">minimum:{{ definition.minimum }}</span>
84
- {% endif %}
85
- {% if definition.exclusiveMinimum %}
86
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">exclusiveMinimum:{{ definition.exclusiveMinimum }}</span>
87
- {% endif %}
88
- {% if definition.maximum %}
89
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">maximum:{{ definition.maximum }}</span>
90
- {% endif %}
91
- {% if definition.exclusiveMaximum %}
92
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">exclusiveMaximum:{{ definition.exclusiveMaximum }}</span>
93
- {% endif %}
94
- {% if definition.classification %}
95
- <span class="inline-flex items-center rounded-md bg-blue-50 px-1 py-1 text-xs font-medium text-blue-600 ring-1 ring-inset ring-blue-500/10 mr-1 mt-1">{{ definition.classification }}</span>
96
- {% endif %}
97
- {% if definition.pii %}
98
- <span class="inline-flex items-center rounded-md bg-yellow-50 px-1 py-1 text-xs font-medium text-yellow-600 ring-1 ring-inset ring-yellow-500/10 mr-1 mt-1">PII</span>
99
- {% endif %}
100
- {% for key, value in definition.model_extra.items() %}
101
- <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">{{ key }}:{{ value }}</span>
102
- {% endfor %}
103
- {% if definition.links %}
104
- {% for name,href in definition.links.items() %}
105
- <a href="{{ href }}" class="inline-flex items-center px-1 py-1 mr-1 mt-1 text-sky-500 hover:text-gray-700 text-xs font-semibold">{{ name }}</a>
106
- {% endfor %}
107
- {% endif %}
108
- </div>
109
- </td>
110
-
111
- </tr>
112
20
  </tbody>
113
21
  </table>
114
22
  </div>
@@ -48,6 +48,14 @@
48
48
  </div>
49
49
  {% endif %}
50
50
 
51
+ {% if field.enum %}
52
+ <div class="mt-1 italic">
53
+ Enum:
54
+ {% for enum_value in field.enum %}
55
+ <span class="font-mono">{{ enum_value }}</span>{% if not loop.last %}, {% endif %}
56
+ {% endfor %}
57
+ </div>
58
+ {% endif %}
51
59
  <div>
52
60
  {% if field.primaryKey or field.primary %}
53
61
  <span class="inline-flex items-center rounded-md bg-gray-50 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 mr-1 mt-1">primary</span>
@@ -103,6 +111,11 @@
103
111
  {% endfor %}
104
112
  {% endif %}
105
113
  </div>
114
+ {% if field.quality %}
115
+ {% for quality in field.quality %}
116
+ {{ render_partial('partials/quality.html', quality = quality) }}
117
+ {% endfor %}
118
+ {% endif %}
106
119
  </td>
107
120
  </tr>
108
121