heurist-api 0.1.2__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 heurist-api might be problematic. Click here for more details.
- heurist/__init__.py +1 -0
- heurist/api/__init__.py +0 -0
- heurist/api/client.py +122 -0
- heurist/api/connection.py +71 -0
- heurist/api/constants.py +19 -0
- heurist/api/credentials.py +71 -0
- heurist/api/exceptions.py +45 -0
- heurist/api/url_builder.py +148 -0
- heurist/api/utils.py +24 -0
- heurist/cli/__init__.py +0 -0
- heurist/cli/__main__.py +227 -0
- heurist/cli/load.py +55 -0
- heurist/cli/records.py +49 -0
- heurist/cli/schema.py +94 -0
- heurist/database/__init__.py +3 -0
- heurist/database/basedb.py +125 -0
- heurist/database/database.py +96 -0
- heurist/models/__init__.py +0 -0
- heurist/models/dynamic/__init__.py +3 -0
- heurist/models/dynamic/annotation.py +143 -0
- heurist/models/dynamic/create_model.py +82 -0
- heurist/models/dynamic/date.py +61 -0
- heurist/models/dynamic/type.py +96 -0
- heurist/models/structural/DetailTypes.py +34 -0
- heurist/models/structural/RecStructure.py +27 -0
- heurist/models/structural/RecTypeGroups.py +27 -0
- heurist/models/structural/RecTypes.py +27 -0
- heurist/models/structural/Terms.py +27 -0
- heurist/models/structural/__init__.py +19 -0
- heurist/models/structural/dty.py +121 -0
- heurist/models/structural/hml_structure.py +36 -0
- heurist/models/structural/rst.py +141 -0
- heurist/models/structural/rtg.py +25 -0
- heurist/models/structural/rty.py +81 -0
- heurist/models/structural/trm.py +34 -0
- heurist/models/structural/utils.py +53 -0
- heurist/schema/__init__.py +27 -0
- heurist/schema/models.py +70 -0
- heurist/schema/rel_to_dict.py +39 -0
- heurist/sql/__init__.py +21 -0
- heurist/sql/joinRecordTypeIDNameByGroupType.sql +10 -0
- heurist/sql/joinRecordTypeMetadata.sql +17 -0
- heurist/sql/selectRecordTypeSchema.sql +51 -0
- heurist/sql/sql_safety.py +101 -0
- heurist/utils/constants.py +1 -0
- heurist/utils/rel_to_dict_array.py +8 -0
- heurist/validators/__init__.py +3 -0
- heurist/validators/detail_validator.py +142 -0
- heurist/validators/exceptions.py +34 -0
- heurist/validators/parse_heurist_date.py +71 -0
- heurist/validators/record_validator.py +156 -0
- heurist/workflows/__init__.py +3 -0
- heurist/workflows/etl.py +66 -0
- heurist_api-0.1.2.dist-info/METADATA +453 -0
- heurist_api-0.1.2.dist-info/RECORD +80 -0
- heurist_api-0.1.2.dist-info/WHEEL +4 -0
- heurist_api-0.1.2.dist-info/entry_points.txt +2 -0
- heurist_api-0.1.2.dist-info/licenses/LICENSE +427 -0
- mock_data/__init__.py +22 -0
- mock_data/blocktext/__init__.py +0 -0
- mock_data/blocktext/single.py +7 -0
- mock_data/date/__init__.py +0 -0
- mock_data/date/compound_repeated.py +44 -0
- mock_data/date/compound_single.py +30 -0
- mock_data/date/simple_single.py +16 -0
- mock_data/date/timestamp_repeated.py +30 -0
- mock_data/enum/__init__.py +0 -0
- mock_data/enum/repeated.py +29 -0
- mock_data/enum/single.py +18 -0
- mock_data/file/__init__.py +0 -0
- mock_data/file/single.py +28 -0
- mock_data/float/__init__.py +0 -0
- mock_data/float/single.py +8 -0
- mock_data/freetext/__init__.py +0 -0
- mock_data/freetext/single.py +16 -0
- mock_data/geo/__init__.py +0 -0
- mock_data/geo/single.py +22 -0
- mock_data/resource/__init__.py +0 -0
- mock_data/resource/repeated.py +35 -0
- mock_data/resource/single.py +16 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from heurist.models.structural.DetailTypes import (
|
|
2
|
+
DetailTypes as DetailTypesModel,
|
|
3
|
+
)
|
|
4
|
+
from heurist.models.structural.RecStructure import (
|
|
5
|
+
RecStructure as RecStructureModel,
|
|
6
|
+
)
|
|
7
|
+
from heurist.models.structural.RecTypeGroups import (
|
|
8
|
+
RecTypeGroups as RecTypeGroupsModel,
|
|
9
|
+
)
|
|
10
|
+
from heurist.models.structural.RecTypes import RecTypes as RecTypesModel
|
|
11
|
+
from heurist.models.structural.Terms import Terms as TermsModel
|
|
12
|
+
from pydantic_xml import BaseXmlModel, element
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HMLStructure(BaseXmlModel, tag="hml_structure", search_mode="unordered"):
|
|
16
|
+
"""Parent dataclass forr modeling the entire Heurist database structure.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
detail_types (DetailTypes): model for data nested in the DetailTypes tag.
|
|
20
|
+
record_structures (RecStructure): model for data nested in the RecStructure tag.
|
|
21
|
+
record_types (RecTypes): model for data nested in the RecTypes tag.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
>>> from mock_data import DB_STRUCTURE_XML
|
|
25
|
+
>>>
|
|
26
|
+
>>>
|
|
27
|
+
>>> # Parse structure
|
|
28
|
+
>>> xml = DB_STRUCTURE_XML
|
|
29
|
+
>>> hml = HMLStructure.from_xml(xml)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
DetailTypes: DetailTypesModel = element(tag="DetailTypes")
|
|
33
|
+
RecStructure: RecStructureModel = element(tag="RecStructure")
|
|
34
|
+
RecTypes: RecTypesModel = element(tag="RecTypes")
|
|
35
|
+
RecTypeGroups: RecTypeGroupsModel = element(tag="RecTypeGroups")
|
|
36
|
+
Terms: TermsModel = element(tag="Terms")
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Literal, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic_xml import BaseXmlModel, element
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RST(BaseXmlModel, tag="rst", search_mode="unordered"):
|
|
8
|
+
"""Dataclass to model one of the database's Record Structures. Record Structures
|
|
9
|
+
are the fields of a Record Type.
|
|
10
|
+
|
|
11
|
+
When possible, the attribute descriptions are taken from Heurist's source code.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
rst_ID (int): Primary key for the record structures table
|
|
15
|
+
rst_RecTypeID (int): The record type to which this detail is allocated, \
|
|
16
|
+
0 = all rectypes
|
|
17
|
+
rst_DetailTypeID (int): Detail type for this field or, if MSB set, \
|
|
18
|
+
FieldSet code + 32767
|
|
19
|
+
rst_DisplayName (str): Display name for this dtl type in this rectype, \
|
|
20
|
+
autofill with dty_Name
|
|
21
|
+
rst_DisplayHelpText (Optional[str]): The user help text to be displayed for \
|
|
22
|
+
this detail type for this record type
|
|
23
|
+
rst_DisplayExtendedDescription (Optional[str]): The rollover text to be \
|
|
24
|
+
displayed for this detail type for this record type
|
|
25
|
+
rst_DisplayOrder (int): A sort order for display of this detail type in the \
|
|
26
|
+
record edit form
|
|
27
|
+
rst_DisplayWidth (int): The field width displayed for this detail type in \
|
|
28
|
+
this record type
|
|
29
|
+
rst_DisplayHeight (int): The field height for this detail type in this record \
|
|
30
|
+
type, only relevant for memo fields
|
|
31
|
+
rst_DefaultValue (Optional[str]): The default value for this detail type for \
|
|
32
|
+
this record type
|
|
33
|
+
rst_RecordMatchOrder (int): Indicates order of significance in detecting \
|
|
34
|
+
duplicate records, 1 = highest
|
|
35
|
+
rst_CalcFunctionID (Optional[int]): FK to table of function specifications for \
|
|
36
|
+
calculating string values
|
|
37
|
+
rst_CalcFieldMask (Optional[str]): A mask string along the lines of the title \
|
|
38
|
+
mask allowing a composite field to be generated from other fields in the \
|
|
39
|
+
record
|
|
40
|
+
rst_RequirementType (Literal["required", "recommended", "optional", \
|
|
41
|
+
"forbidden"]):
|
|
42
|
+
rst_NonOwnerVisibility (Literal["hidden", "viewable", "public", "pending"]): \
|
|
43
|
+
Allows restriction of visibility of a particular field in a specified \
|
|
44
|
+
record type
|
|
45
|
+
rst_Status (Literal["reserved", "approved", "pending", "open"]): Reserved \
|
|
46
|
+
Heurist codes, approved/pending by ''Board'', and user additions
|
|
47
|
+
rst_MayModify (Literal["locked", "discouraged", "open"]): Extent to which \
|
|
48
|
+
detail may be modified within this record structure
|
|
49
|
+
rst_OriginatingDBID (int): Database where this record structure element \
|
|
50
|
+
originated, 0 = locally
|
|
51
|
+
rst_IDInOriginatingDB (Optional[int]): ID used in database where this record \
|
|
52
|
+
structure element originated
|
|
53
|
+
rst_MaxValues (int): Maximum number of values per record for this detail, 1 - \
|
|
54
|
+
single, >1 limited, NULL or 0 = no limit
|
|
55
|
+
rst_MinValues (int): If required, minimum number of values per record for this \
|
|
56
|
+
detail
|
|
57
|
+
rst_InitialRepeats (int): Number of repeat values to be displayed for this \
|
|
58
|
+
field when a new record is first displayed
|
|
59
|
+
rst_DisplayDetailTypeGroupID (Optional[int]): If set, places detail in \
|
|
60
|
+
specified group instead of according to dty_DetailTypeGroup
|
|
61
|
+
rst_FilteredJsonTermIDTree (Optional[str]): JSON encoded tree of allowed \
|
|
62
|
+
terms, subset of those defined in defDetailType. This field is no \
|
|
63
|
+
longer used
|
|
64
|
+
rst_PtrFilteredIDs (Optional[str]): Allowed Rectypes (CSV) within list defined \
|
|
65
|
+
by defDetailType (for pointer details) This field is no longer used
|
|
66
|
+
rst_CreateChildIfRecPtr (bool): For pointer fields, flags that new records \
|
|
67
|
+
created from this field should be marked as children of the creating record
|
|
68
|
+
rst_PointerMode (Literal["dropdown_add", "dropdown", "addorbrowse", "addonly", \
|
|
69
|
+
"browseonly"]): When adding record pointer values, default or null = show \
|
|
70
|
+
both add and browse, otherwise only allow add or only allow \
|
|
71
|
+
browse-for-existing
|
|
72
|
+
rst_PointerBrowseFilter (Optional[str]): When adding record pointer values, \
|
|
73
|
+
defines a Heurist filter to restrict the list of target records browsed
|
|
74
|
+
rst_OrderForThumbnailGeneration (Optional[str]): Priority order of fields to \
|
|
75
|
+
use in generating thumbnail, null = do not use
|
|
76
|
+
rst_TermIDTreeNonSelectableIDs (Optional[str]): Term IDs to use as \
|
|
77
|
+
non-selectable headers for this field
|
|
78
|
+
rst_ShowDetailCertainty (bool): When editing the field, allow editng of the \
|
|
79
|
+
dtl_Certainty value (off by default)
|
|
80
|
+
rst_ShowDetailAnnotation (bool): When editing the field, allow editng of the \
|
|
81
|
+
dtl_Annotation value (off by default)
|
|
82
|
+
rst_NumericLargestValueUsed (Optional[int]): For numeric fields, Null = \
|
|
83
|
+
no auto increment, 0 or more indicates largest value used so far. \
|
|
84
|
+
Set to 0 to switch on incrementing
|
|
85
|
+
rst_EntryMask (Optional[str]): Data entry mask, use to control decimals on \
|
|
86
|
+
numeric values, content of text fields etc. for this record type - future \
|
|
87
|
+
implementation Aug 2017
|
|
88
|
+
rst_Modified (datetime): Date of last modification of this record, used to get \
|
|
89
|
+
last updated date for table
|
|
90
|
+
rst_LocallyModified (int): Flags a definition element which has been modified \
|
|
91
|
+
relative to the original source
|
|
92
|
+
rst_SemanticReferenceURL (Optional[str]): The URI to a semantic definition or \
|
|
93
|
+
web page describing this field used within this record type
|
|
94
|
+
rst_TermsAsButtons (bool): If 1, term list fields are represented as buttons \
|
|
95
|
+
(if single value) or checkboxes (if repeat values)
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
rst_ID: int = element()
|
|
99
|
+
rst_RecTypeID: int = element()
|
|
100
|
+
rst_DetailTypeID: int = element()
|
|
101
|
+
rst_DisplayName: str = element()
|
|
102
|
+
rst_DisplayHelpText: Optional[str] = element(default=None)
|
|
103
|
+
rst_DisplayExtendedDescription: Optional[str] = element(default=None)
|
|
104
|
+
rst_DisplayOrder: int = element()
|
|
105
|
+
rst_DisplayWidth: int = element()
|
|
106
|
+
rst_DisplayHeight: int = element()
|
|
107
|
+
rst_DefaultValue: Optional[str] = element(default=None)
|
|
108
|
+
rst_RecordMatchOrder: int = element()
|
|
109
|
+
rst_CalcFunctionID: Optional[int] = element(default=None)
|
|
110
|
+
rst_CalcFieldMask: Optional[str] = element(default=None)
|
|
111
|
+
rst_RequirementType: Literal["required", "recommended", "optional", "forbidden"] = (
|
|
112
|
+
element()
|
|
113
|
+
)
|
|
114
|
+
rst_NonOwnerVisibility: Literal["hidden", "viewable", "public", "pending"] = (
|
|
115
|
+
element()
|
|
116
|
+
)
|
|
117
|
+
rst_Status: Literal["reserved", "approved", "pending", "open"] = element()
|
|
118
|
+
rst_MayModify: Literal["locked", "discouraged", "open"] = element()
|
|
119
|
+
rst_OriginatingDBID: int = element()
|
|
120
|
+
rst_IDInOriginatingDB: Optional[int] = element(default=None)
|
|
121
|
+
rst_MaxValues: int = element()
|
|
122
|
+
rst_MinValues: int = element()
|
|
123
|
+
rst_InitialRepeats: int = element()
|
|
124
|
+
rst_DisplayDetailTypeGroupID: Optional[int] = element(default=None)
|
|
125
|
+
rst_FilteredJsonTermIDTree: Optional[str] = element(default=None)
|
|
126
|
+
rst_PtrFilteredIDs: Optional[str] = element(default=None)
|
|
127
|
+
rst_CreateChildIfRecPtr: bool = element()
|
|
128
|
+
rst_PointerMode: Literal[
|
|
129
|
+
"dropdown_add", "dropdown", "addorbrowse", "addonly", "browseonly"
|
|
130
|
+
] = element()
|
|
131
|
+
rst_PointerBrowseFilter: Optional[str] = element(default=None)
|
|
132
|
+
rst_OrderForThumbnailGeneration: Optional[str] = element(default=None)
|
|
133
|
+
rst_TermIDTreeNonSelectableIDs: Optional[str] = element(default=None)
|
|
134
|
+
rst_ShowDetailCertainty: bool = element()
|
|
135
|
+
rst_ShowDetailAnnotation: bool = element()
|
|
136
|
+
rst_NumericLargestValueUsed: Optional[int] = element(default=None)
|
|
137
|
+
rst_EntryMask: Optional[str] = element(default=None)
|
|
138
|
+
rst_Modified: datetime = element()
|
|
139
|
+
rst_LocallyModified: int = element()
|
|
140
|
+
rst_SemanticReferenceURL: Optional[str] = element(default=None)
|
|
141
|
+
rst_TermsAsButtons: bool = element()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pydantic_xml import BaseXmlModel, element
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RTG(BaseXmlModel, tag="rtg", search_mode="unordered"):
|
|
8
|
+
"""Dataclass to model one of the database's Record Type Groups. A Record Type \
|
|
9
|
+
Group categorizes the record types in the database.
|
|
10
|
+
|
|
11
|
+
When possible, the attribute descriptions are taken from Heurist's source code.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
rtg_ID (int): __Description__.
|
|
15
|
+
rtg_Name (str): __Description__.
|
|
16
|
+
rtg_Domain (str): __Description__.
|
|
17
|
+
rtg_Description (Optional[str]): __Description__.
|
|
18
|
+
rtg_Modified (datetime): __Description__.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
rtg_ID: int = element()
|
|
22
|
+
rtg_Name: str = element()
|
|
23
|
+
rtg_Domain: str = element()
|
|
24
|
+
rtg_Description: Optional[str] = element(default=None)
|
|
25
|
+
rtg_Modified: datetime = element()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Literal, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic_xml import BaseXmlModel, element
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RTY(BaseXmlModel, tag="rty", search_mode="unordered"):
|
|
8
|
+
"""Dataclass to model one of the database's Record Types. A Record Type is the \
|
|
9
|
+
schema for an entity in the database.
|
|
10
|
+
|
|
11
|
+
When possible, the attribute descriptions are taken from Heurist's source code.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
rty_ID (int): Record type code, widely used to reference record types, primary \
|
|
15
|
+
key
|
|
16
|
+
rty_Name (str): The name which is used to describe this record (object) type
|
|
17
|
+
rty_OrderInGroup (int): Ordering within record type display groups for pulldowns
|
|
18
|
+
rty_Description (str): Description of this record type
|
|
19
|
+
rty_TitleMask (str): Mask to build a composite title by combining field values
|
|
20
|
+
rty_CanonicalTitleMask (str): Version of the mask converted to detail codes \
|
|
21
|
+
for processing
|
|
22
|
+
rty_Plural (Optional[str]): Plural form of the record type name, manually \
|
|
23
|
+
entered
|
|
24
|
+
rty_Status (Literal["reserved", "approved", "pending", "open"]): Reserved \
|
|
25
|
+
Heurist codes, approved/pending by ''Board'', and user additions
|
|
26
|
+
rty_OriginatingDBID (Optional[int]): Database where this record type \
|
|
27
|
+
originated, 0 = locally
|
|
28
|
+
rty_NameInOriginatingDB: (Optional[str]) Name used in database where this \
|
|
29
|
+
record type originated
|
|
30
|
+
rty_IDInOriginatingDB (Optional[int]): ID in database where this record type \
|
|
31
|
+
originated
|
|
32
|
+
rty_NonOwnerVisibility (Literal["hidden", "viewable", "public", "pending"]): \
|
|
33
|
+
Allows blanket restriction of visibility of a particular record type
|
|
34
|
+
rty_ShowInLists (bool): Flags if record type is to be shown in end-user \
|
|
35
|
+
interface, 1=yes
|
|
36
|
+
rty_RecTypeGroupID (int): Record type group to which this record type belongs
|
|
37
|
+
rty_RecTypeModelIDs (str): The model group(s) to which this rectype belongs, \
|
|
38
|
+
comma sep. list
|
|
39
|
+
rty_FlagAsFieldset (bool): 0 = full record type, 1 = Fieldset = set of fields \
|
|
40
|
+
to include in other rectypes
|
|
41
|
+
rty_ReferenceURL (Optional[str]): A semantic reference URI for, or a URL \
|
|
42
|
+
describing, the record type
|
|
43
|
+
rty_AlternativeRecEditor (Optional[str]): Name or URL of alternative record \
|
|
44
|
+
editor function to be used for this rectype
|
|
45
|
+
rty_Type (Literal["normal", "relationship", "dummy"]): Use to flag special \
|
|
46
|
+
record types to trigger special functions
|
|
47
|
+
rty_ShowURLOnEditForm (bool): Determines whether special URL field is shown at \
|
|
48
|
+
the top of the edit form
|
|
49
|
+
rty_ShowDescriptionOnEditForm (bool): Determines whether the record type \
|
|
50
|
+
description field is shown at the top of the edit form
|
|
51
|
+
rty_Modified (datetime): Date of last modification of this record, used to get \
|
|
52
|
+
last updated date for table
|
|
53
|
+
rty_LocallyModified (bool): Flags a definition element which has been modified \
|
|
54
|
+
relative to the original source
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
rty_ID: int = element()
|
|
58
|
+
rty_Name: str = element()
|
|
59
|
+
rty_OrderInGroup: int = element()
|
|
60
|
+
rty_Description: str = element()
|
|
61
|
+
rty_TitleMask: str = element()
|
|
62
|
+
rty_CanonicalTitleMask: Optional[str] = element(default=None)
|
|
63
|
+
rty_Plural: Optional[str] = element(default=None)
|
|
64
|
+
rty_Status: Literal["reserved", "approved", "pending", "open"] = element()
|
|
65
|
+
rty_OriginatingDBID: Optional[int] = element(default=None)
|
|
66
|
+
rty_NameInOriginatingDB: Optional[str] = element(default=None)
|
|
67
|
+
rty_IDInOriginatingDB: Optional[int] = element(default=None)
|
|
68
|
+
rty_NonOwnerVisibility: Literal["hidden", "viewable", "public", "pending"] = (
|
|
69
|
+
element()
|
|
70
|
+
)
|
|
71
|
+
rty_ShowInLists: bool = element()
|
|
72
|
+
rty_RecTypeGroupID: int = element()
|
|
73
|
+
rty_RecTypeModelIDs: str = element(default=None)
|
|
74
|
+
rty_FlagAsFieldset: bool = element(default=None)
|
|
75
|
+
rty_ReferenceURL: Optional[str] = element(default=None)
|
|
76
|
+
rty_AlternativeRecEditor: Optional[str] = element(default=None)
|
|
77
|
+
rty_Type: Literal["normal", "relationship", "dummy"] = element()
|
|
78
|
+
rty_ShowURLOnEditForm: bool = element()
|
|
79
|
+
rty_ShowDescriptionOnEditForm: bool = element()
|
|
80
|
+
rty_Modified: datetime = element()
|
|
81
|
+
rty_LocallyModified: bool = element()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Literal, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic_xml import BaseXmlModel, element
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TRM(BaseXmlModel, tag="trm", search_mode="unordered"):
|
|
8
|
+
"""Dataclass to model one of the database's vocabulary terms.
|
|
9
|
+
|
|
10
|
+
When possible, the attribute descriptions are taken from Heurist's source code.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
trm_ID: int = element()
|
|
14
|
+
trm_Label: str = element()
|
|
15
|
+
trm_InverseTermId: Optional[int] = element(default=None)
|
|
16
|
+
trm_Description: Optional[str] = element(default=None)
|
|
17
|
+
trm_Status: Literal["open", "approved", "reserved"] = element()
|
|
18
|
+
trm_OriginatingDBID: int = element()
|
|
19
|
+
trm_NameInOriginatingDB: Optional[str] = element(default=None)
|
|
20
|
+
trm_IDInOriginatingDB: Optional[int] = element(default=None)
|
|
21
|
+
trm_AddedByImport: bool = element()
|
|
22
|
+
trm_IsLocalExtension: bool = element()
|
|
23
|
+
trm_Domain: Literal["relation", "enum"] = element()
|
|
24
|
+
trm_OntID: int = element()
|
|
25
|
+
trm_ChildCount: int = element()
|
|
26
|
+
trm_ParentTermID: Optional[int] = element(default=None)
|
|
27
|
+
trm_Depth: int = element()
|
|
28
|
+
trm_Modified: datetime = element()
|
|
29
|
+
trm_LocallyModified: bool = element()
|
|
30
|
+
trm_Code: Optional[str] = element(default=None)
|
|
31
|
+
trm_SemanticReferenceURL: Optional[str] = element(default=None)
|
|
32
|
+
trm_IllustrationURL: Optional[str] = element(default=None)
|
|
33
|
+
trm_VocabularyGroupID: Optional[int] = element(default=None)
|
|
34
|
+
trm_OrderInBranch: Optional[int] = element(default=None)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Module for utilities commonly used by XML parsers in schemas."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def split_ids(input: str) -> list:
|
|
7
|
+
"""Function for converting a string representation of a list of quoted integers \
|
|
8
|
+
into a Python list object.
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
>>> s = "3001,3110,3113,3288"
|
|
12
|
+
>>> split_ids(s)
|
|
13
|
+
[3001, 3110, 3113, 3288]
|
|
14
|
+
>>> s = '[\\"3001\\",\\"3110\\",\\"3113\\",\\"3288\\"]'
|
|
15
|
+
>>> split_ids(s)
|
|
16
|
+
[3001, 3110, 3113, 3288]
|
|
17
|
+
>>> l = ['[\\"3001\\",\\"3110\\",\\"3113\\",\\"3288\\"]']
|
|
18
|
+
>>> split_ids(l)
|
|
19
|
+
[3001, 3110, 3113, 3288]
|
|
20
|
+
>>> s = '[]'
|
|
21
|
+
>>> split_ids(s)
|
|
22
|
+
[]
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
input (str|Any): List of integers.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
list: _description_
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# Handle input of potentially malformatted JSON list
|
|
32
|
+
if isinstance(input, list) or "[" in input:
|
|
33
|
+
if not isinstance(input, str):
|
|
34
|
+
s = json.dumps(input)
|
|
35
|
+
if not isinstance(s, str):
|
|
36
|
+
print(input)
|
|
37
|
+
raise TypeError("Integer list was not converted to string.")
|
|
38
|
+
else:
|
|
39
|
+
s = input
|
|
40
|
+
|
|
41
|
+
# Remove the JSON string escape characters
|
|
42
|
+
s = s.replace("\\", "")
|
|
43
|
+
s = s.replace('"', "")
|
|
44
|
+
s = s.replace("[", "")
|
|
45
|
+
s = s.replace("]", "")
|
|
46
|
+
|
|
47
|
+
input_string = s
|
|
48
|
+
|
|
49
|
+
else:
|
|
50
|
+
input_string = input
|
|
51
|
+
|
|
52
|
+
# Split and recast regular string
|
|
53
|
+
return [int(i) for i in input_string.split(",") if i != ""]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import date
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import duckdb
|
|
6
|
+
from heurist.schema.rel_to_dict import convert_rty_description
|
|
7
|
+
from heurist.sql.sql_safety import SafeSQLName
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def output_csv(dir: Path, descriptions: list[duckdb.DuckDBPyRelation]) -> None:
|
|
11
|
+
for rel in descriptions:
|
|
12
|
+
name = rel.select("rty_Name").limit(1).fetchone()[0]
|
|
13
|
+
safe_name = SafeSQLName().create_table_name(name)
|
|
14
|
+
fp = dir.joinpath(safe_name).with_suffix(".csv")
|
|
15
|
+
rel.write_csv(file_name=str(fp), header=True)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def output_json(descriptions: list[duckdb.DuckDBPyRelation], fp: Path) -> None:
|
|
19
|
+
date_string = date.today().isoformat()
|
|
20
|
+
data = {"lastModified": date_string, "items": []}
|
|
21
|
+
for desc in descriptions:
|
|
22
|
+
kv_dict = convert_rty_description(description=desc)
|
|
23
|
+
for id, metadata in kv_dict.items():
|
|
24
|
+
d = {"id": id} | metadata
|
|
25
|
+
data["items"].append(d)
|
|
26
|
+
with open(fp, "w", encoding="utf-8") as f:
|
|
27
|
+
json.dump(data, f, indent=4, ensure_ascii=False)
|
heurist/schema/models.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from pydantic.functional_validators import BeforeValidator
|
|
5
|
+
from typing_extensions import Annotated
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def convert_vocab_map_to_list(vocab_map: dict | None) -> list[dict]:
|
|
9
|
+
"""
|
|
10
|
+
Convert map generated in the SQL query to a list of vocab dictionary objects.
|
|
11
|
+
|
|
12
|
+
See the SQL query in the sql/ directory. The relevant selection is:
|
|
13
|
+
map(list(trm_Label), list(
|
|
14
|
+
{
|
|
15
|
+
"description": trm_Description,
|
|
16
|
+
"url": trm_SemanticReferenceURL,
|
|
17
|
+
"id": trm_ID
|
|
18
|
+
})
|
|
19
|
+
) AS terms
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
>>> vocab0 = {'perg': {'description': 'Parchment', 'url': None, 'id': 9782}}
|
|
23
|
+
>>> vocab1 = {'chart': {'description': 'Paper', 'url': None, 'id': 9783}}
|
|
24
|
+
>>> vocab2 = {'mixed': {'description': None, 'url': None, 'id': 9785}}
|
|
25
|
+
>>> map = vocab0 | vocab1 | vocab2
|
|
26
|
+
>>> terms = convert_vocab_map_to_list(map)
|
|
27
|
+
>>> len(terms)
|
|
28
|
+
3
|
|
29
|
+
>>> terms[0]
|
|
30
|
+
{'label': 'perg', 'description': 'Parchment', 'url': None, 'id': 9782}
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
vocab_map (dict | None): Map created in SQL query from aggregation function.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
list[dict]: List of vocabulary term metadata in dictionary objects.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
vocab_terms = []
|
|
40
|
+
if vocab_map:
|
|
41
|
+
for k, v in vocab_map.items():
|
|
42
|
+
nd = {"label": k} | v
|
|
43
|
+
vocab_terms.append(nd)
|
|
44
|
+
return vocab_terms
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
VocabTerms = Annotated[list, BeforeValidator(convert_vocab_map_to_list)]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DTY(BaseModel):
|
|
51
|
+
rst_DisplayName: str
|
|
52
|
+
rst_DisplayHelpText: str
|
|
53
|
+
dty_ID: int
|
|
54
|
+
dty_Type: str
|
|
55
|
+
dty_PtrTargetRectypeIDs: Optional[List[int]]
|
|
56
|
+
dty_SemanticReferenceURL: Optional[str]
|
|
57
|
+
trm_TreeID: Optional[int]
|
|
58
|
+
trm_Label: Optional[str]
|
|
59
|
+
trm_Description: Optional[str]
|
|
60
|
+
rst_RequirementType: str
|
|
61
|
+
rst_MaxValues: int
|
|
62
|
+
vocabTerms: Optional[VocabTerms]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class RTY(BaseModel):
|
|
66
|
+
rty_ID: int
|
|
67
|
+
rty_Name: str
|
|
68
|
+
rty_Description: str
|
|
69
|
+
rty_TitleMask: str
|
|
70
|
+
rty_ReferenceURL: Optional[str]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import duckdb
|
|
2
|
+
from heurist.schema.models import DTY, RTY
|
|
3
|
+
from heurist.utils.rel_to_dict_array import rel_to_dict_array
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def convert_rty_description(description: duckdb.DuckDBPyRelation) -> dict:
|
|
7
|
+
"""
|
|
8
|
+
Convert the SQL result of the joined record type schema description into \
|
|
9
|
+
a Python dictionary.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
description (duckdb.DuckDBPyRelation): Relation from SQL query.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
dict: Dictionary representation of a record's schema.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
rel = description.filter("dty_Type not like 'separator'").order(
|
|
19
|
+
"group_id asc, rst_DisplayOrder asc"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
sections = {}
|
|
23
|
+
|
|
24
|
+
for field in rel_to_dict_array(rel):
|
|
25
|
+
section_id = field["group_id"]
|
|
26
|
+
section_name = field["sec"]
|
|
27
|
+
if not sections.get(section_id):
|
|
28
|
+
sections.update({section_id: {"sectionName": section_name, "fields": []}})
|
|
29
|
+
field_model = DTY.model_validate(field).model_dump()
|
|
30
|
+
sections[section_id]["fields"].append(field_model)
|
|
31
|
+
|
|
32
|
+
section_list = list(sections.values())
|
|
33
|
+
|
|
34
|
+
rty_rel = rel.limit(1)
|
|
35
|
+
rty_data = rel_to_dict_array(rty_rel)[0]
|
|
36
|
+
rty = RTY.model_validate(rty_data)
|
|
37
|
+
|
|
38
|
+
output = {rty.rty_ID: {"metadata": rty.model_dump(), "sections": section_list}}
|
|
39
|
+
return output
|
heurist/sql/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
file_path_record_type_schema = Path(__file__).parent.joinpath(
|
|
4
|
+
"selectRecordTypeSchema.sql"
|
|
5
|
+
)
|
|
6
|
+
file_path_record_type_metadata_by_group_type = Path(__file__).parent.joinpath(
|
|
7
|
+
"joinRecordTypeIDNameByGroupType.sql"
|
|
8
|
+
)
|
|
9
|
+
file_path_fully_joined_record_type_metadata = Path(__file__).parent.joinpath(
|
|
10
|
+
"joinRecordTypeMetadata.sql"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
with open(file_path_record_type_schema) as f:
|
|
15
|
+
RECORD_TYPE_SCHEMA = f.read()
|
|
16
|
+
|
|
17
|
+
with open(file_path_record_type_metadata_by_group_type) as f:
|
|
18
|
+
RECORD_BY_GROUP_TYPE = f.read()
|
|
19
|
+
|
|
20
|
+
with open(file_path_fully_joined_record_type_metadata) as f:
|
|
21
|
+
RECORD_TYPE_METADATA = f.read()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Select the ID and name of record types that are in
|
|
2
|
+
targeted record type groups.
|
|
3
|
+
|
|
4
|
+
This query should be appended with 1 or more conditions
|
|
5
|
+
of "rtg.rtg_Name" = '<target group name>'. */
|
|
6
|
+
SELECT
|
|
7
|
+
rty.rty_ID,
|
|
8
|
+
rty.rty_Name
|
|
9
|
+
FROM rty
|
|
10
|
+
INNER JOIN rtg ON rty.rty_RecTypeGroupID = rtg.rtg_ID
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* Join the tables Record Structure (rst), Detail Type (dty),
|
|
2
|
+
and Record Type (rty) to get all the relevant information
|
|
3
|
+
about a record type's data fields.
|
|
4
|
+
|
|
5
|
+
This query requires a parameter: the record type ID. */
|
|
6
|
+
SELECT
|
|
7
|
+
dty_ID,
|
|
8
|
+
rst_DisplayName,
|
|
9
|
+
dty_Type,
|
|
10
|
+
rst_MaxValues
|
|
11
|
+
FROM rst
|
|
12
|
+
INNER JOIN rty ON rst.rst_RecTypeID = rty.rty_ID
|
|
13
|
+
INNER JOIN dty ON rst.rst_DetailTypeID = dty.dty_ID
|
|
14
|
+
WHERE rty.rty_ID = ?
|
|
15
|
+
AND dty.dty_Type NOT LIKE 'separator'
|
|
16
|
+
AND dty.dty_Type NOT LIKE 'relmarker'
|
|
17
|
+
ORDER BY rst.rst_DisplayOrder
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* Build a selection of data fields for a record type that
|
|
2
|
+
groups the fields by their groups in Heurist's interface,
|
|
3
|
+
as defined by the "separator" field Heurist adds between
|
|
4
|
+
fields of two different groups.
|
|
5
|
+
|
|
6
|
+
This query requires a parameter: the record type ID. */
|
|
7
|
+
SELECT
|
|
8
|
+
CASE
|
|
9
|
+
WHEN group_id != 0 THEN FIRST_VALUE(rst_DisplayName) OVER (PARTITION BY group_id)
|
|
10
|
+
ELSE NULL
|
|
11
|
+
END
|
|
12
|
+
AS sec,
|
|
13
|
+
CASE
|
|
14
|
+
WHEN group_id !=0 THEN FIRST_VALUE(rst_DisplayHelpText) OVER (PARTITION BY group_id)
|
|
15
|
+
ELSE NULL
|
|
16
|
+
END
|
|
17
|
+
AS secHelpText
|
|
18
|
+
, *
|
|
19
|
+
FROM (
|
|
20
|
+
SELECT *
|
|
21
|
+
FROM (
|
|
22
|
+
SELECT
|
|
23
|
+
COUNT(
|
|
24
|
+
CASE WHEN dty_type LIKE 'separator' THEN rst_DisplayName ELSE NULL end
|
|
25
|
+
) OVER (ORDER BY rst_DisplayOrder) AS group_id,
|
|
26
|
+
*
|
|
27
|
+
FROM rst
|
|
28
|
+
JOIN rty ON rst_RecTypeID = rty.rty_ID
|
|
29
|
+
JOIN dty ON rst_DetailTypeID = dty.dty_ID
|
|
30
|
+
WHERE rty_ID = ?
|
|
31
|
+
)
|
|
32
|
+
LEFT JOIN (
|
|
33
|
+
SELECT
|
|
34
|
+
a.vocab_id as trm_TreeID,
|
|
35
|
+
b.trm_Label,
|
|
36
|
+
b.trm_Description,
|
|
37
|
+
a.term_count as n_vocabTerms,
|
|
38
|
+
a.terms as vocabTerms
|
|
39
|
+
FROM (
|
|
40
|
+
SELECT
|
|
41
|
+
trm_ParentTermID AS vocab_id,
|
|
42
|
+
count(*) AS term_count,
|
|
43
|
+
map(list(trm_Label), list({"description": trm_Description, "url": trm_SemanticReferenceURL, "id": trm_ID})) AS terms
|
|
44
|
+
FROM trm
|
|
45
|
+
GROUP BY trm_ParentTermID
|
|
46
|
+
) a
|
|
47
|
+
LEFT JOIN trm b ON a.vocab_id = b.trm_ID
|
|
48
|
+
) c ON c.trm_TreeID = dty_JsonTermIDTree
|
|
49
|
+
ORDER BY rst_DisplayOrder
|
|
50
|
+
)
|
|
51
|
+
ORDER BY rst_DisplayOrder
|