valuesets 0.3.1__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 valuesets might be problematic. Click here for more details.
- valuesets/__init__.py +7 -0
- valuesets/_version.py +8 -0
- valuesets/datamodel/valuesets.py +13796 -0
- valuesets/datamodel/valuesets_dataclass.py +24503 -0
- valuesets/datamodel/valuesets_pydantic.py +13796 -0
- valuesets/enums/__init__.py +590 -0
- valuesets/enums/academic/__init__.py +1 -0
- valuesets/enums/academic/research.py +559 -0
- valuesets/enums/analytical_chemistry/__init__.py +1 -0
- valuesets/enums/analytical_chemistry/mass_spectrometry.py +198 -0
- valuesets/enums/bio/__init__.py +1 -0
- valuesets/enums/bio/biological_colors.py +238 -0
- valuesets/enums/bio/cell_cycle.py +180 -0
- valuesets/enums/bio/currency_chemicals.py +52 -0
- valuesets/enums/bio/developmental_stages.py +103 -0
- valuesets/enums/bio/genome_features.py +182 -0
- valuesets/enums/bio/genomics.py +91 -0
- valuesets/enums/bio/go_aspect.py +32 -0
- valuesets/enums/bio/go_causality.py +58 -0
- valuesets/enums/bio/go_evidence.py +129 -0
- valuesets/enums/bio/human_developmental_stages.py +62 -0
- valuesets/enums/bio/insdc_geographic_locations.py +591 -0
- valuesets/enums/bio/insdc_missing_values.py +49 -0
- valuesets/enums/bio/lipid_categories.py +67 -0
- valuesets/enums/bio/mouse_developmental_stages.py +62 -0
- valuesets/enums/bio/plant_biology.py +86 -0
- valuesets/enums/bio/plant_developmental_stages.py +54 -0
- valuesets/enums/bio/plant_sex.py +81 -0
- valuesets/enums/bio/protein_evidence.py +61 -0
- valuesets/enums/bio/proteomics_standards.py +123 -0
- valuesets/enums/bio/psi_mi.py +306 -0
- valuesets/enums/bio/relationship_to_oxygen.py +37 -0
- valuesets/enums/bio/sequence_alphabets.py +449 -0
- valuesets/enums/bio/sequence_chemistry.py +357 -0
- valuesets/enums/bio/sequencing_platforms.py +302 -0
- valuesets/enums/bio/structural_biology.py +320 -0
- valuesets/enums/bio/taxonomy.py +238 -0
- valuesets/enums/bio/trophic_levels.py +85 -0
- valuesets/enums/bio/uniprot_species.py +344 -0
- valuesets/enums/bio/viral_genome_types.py +47 -0
- valuesets/enums/bioprocessing/__init__.py +1 -0
- valuesets/enums/bioprocessing/scale_up.py +249 -0
- valuesets/enums/business/__init__.py +1 -0
- valuesets/enums/business/human_resources.py +275 -0
- valuesets/enums/business/industry_classifications.py +181 -0
- valuesets/enums/business/management_operations.py +228 -0
- valuesets/enums/business/organizational_structures.py +236 -0
- valuesets/enums/business/quality_management.py +181 -0
- valuesets/enums/business/supply_chain.py +232 -0
- valuesets/enums/chemistry/__init__.py +1 -0
- valuesets/enums/chemistry/chemical_entities.py +315 -0
- valuesets/enums/chemistry/reaction_directionality.py +65 -0
- valuesets/enums/chemistry/reactions.py +256 -0
- valuesets/enums/clinical/__init__.py +1 -0
- valuesets/enums/clinical/nih_demographics.py +177 -0
- valuesets/enums/clinical/phenopackets.py +254 -0
- valuesets/enums/common_value_sets.py +8791 -0
- valuesets/enums/computing/__init__.py +1 -0
- valuesets/enums/computing/file_formats.py +294 -0
- valuesets/enums/computing/maturity_levels.py +196 -0
- valuesets/enums/computing/mime_types.py +227 -0
- valuesets/enums/confidence_levels.py +168 -0
- valuesets/enums/contributor.py +30 -0
- valuesets/enums/core.py +42 -0
- valuesets/enums/data/__init__.py +1 -0
- valuesets/enums/data/data_absent_reason.py +53 -0
- valuesets/enums/data_science/__init__.py +1 -0
- valuesets/enums/data_science/binary_classification.py +87 -0
- valuesets/enums/data_science/emotion_classification.py +66 -0
- valuesets/enums/data_science/priority_severity.py +73 -0
- valuesets/enums/data_science/quality_control.py +46 -0
- valuesets/enums/data_science/sentiment_analysis.py +50 -0
- valuesets/enums/data_science/text_classification.py +97 -0
- valuesets/enums/demographics.py +206 -0
- valuesets/enums/ecological_interactions.py +151 -0
- valuesets/enums/energy/__init__.py +1 -0
- valuesets/enums/energy/energy.py +343 -0
- valuesets/enums/energy/fossil_fuels.py +29 -0
- valuesets/enums/energy/nuclear/__init__.py +1 -0
- valuesets/enums/energy/nuclear/nuclear_facilities.py +195 -0
- valuesets/enums/energy/nuclear/nuclear_fuel_cycle.py +96 -0
- valuesets/enums/energy/nuclear/nuclear_fuels.py +175 -0
- valuesets/enums/energy/nuclear/nuclear_operations.py +191 -0
- valuesets/enums/energy/nuclear/nuclear_regulatory.py +188 -0
- valuesets/enums/energy/nuclear/nuclear_safety.py +164 -0
- valuesets/enums/energy/nuclear/nuclear_waste.py +158 -0
- valuesets/enums/energy/nuclear/reactor_types.py +163 -0
- valuesets/enums/environmental_health/__init__.py +1 -0
- valuesets/enums/environmental_health/exposures.py +265 -0
- valuesets/enums/geography/__init__.py +1 -0
- valuesets/enums/geography/geographic_codes.py +741 -0
- valuesets/enums/health/__init__.py +12 -0
- valuesets/enums/health/vaccination.py +98 -0
- valuesets/enums/health.py +36 -0
- valuesets/enums/health_base.py +36 -0
- valuesets/enums/healthcare.py +45 -0
- valuesets/enums/industry/__init__.py +1 -0
- valuesets/enums/industry/extractive_industry.py +94 -0
- valuesets/enums/industry/mining.py +388 -0
- valuesets/enums/industry/safety_colors.py +201 -0
- valuesets/enums/investigation.py +27 -0
- valuesets/enums/materials_science/__init__.py +1 -0
- valuesets/enums/materials_science/characterization_methods.py +112 -0
- valuesets/enums/materials_science/crystal_structures.py +76 -0
- valuesets/enums/materials_science/material_properties.py +119 -0
- valuesets/enums/materials_science/material_types.py +104 -0
- valuesets/enums/materials_science/pigments_dyes.py +198 -0
- valuesets/enums/materials_science/synthesis_methods.py +109 -0
- valuesets/enums/medical/__init__.py +1 -0
- valuesets/enums/medical/clinical.py +277 -0
- valuesets/enums/medical/neuroimaging.py +119 -0
- valuesets/enums/mining_processing.py +302 -0
- valuesets/enums/physics/__init__.py +1 -0
- valuesets/enums/physics/states_of_matter.py +46 -0
- valuesets/enums/social/__init__.py +1 -0
- valuesets/enums/social/person_status.py +29 -0
- valuesets/enums/spatial/__init__.py +1 -0
- valuesets/enums/spatial/spatial_qualifiers.py +246 -0
- valuesets/enums/statistics/__init__.py +5 -0
- valuesets/enums/statistics/prediction_outcomes.py +31 -0
- valuesets/enums/statistics.py +31 -0
- valuesets/enums/time/__init__.py +1 -0
- valuesets/enums/time/temporal.py +254 -0
- valuesets/enums/units/__init__.py +1 -0
- valuesets/enums/units/measurements.py +310 -0
- valuesets/enums/visual/__init__.py +1 -0
- valuesets/enums/visual/colors.py +376 -0
- valuesets/generators/__init__.py +19 -0
- valuesets/generators/auto_slot_injector.py +280 -0
- valuesets/generators/enhanced_pydantic_generator.py +100 -0
- valuesets/generators/enum_slot_generator.py +201 -0
- valuesets/generators/modular_rich_generator.py +353 -0
- valuesets/generators/prefix_standardizer.py +198 -0
- valuesets/generators/rich_enum.py +127 -0
- valuesets/generators/rich_pydantic_generator.py +310 -0
- valuesets/generators/smart_slot_syncer.py +428 -0
- valuesets/generators/sssom_generator.py +394 -0
- valuesets/merged/merged_hierarchy.yaml +21649 -0
- valuesets/schema/README.md +3 -0
- valuesets/schema/academic/research.yaml +911 -0
- valuesets/schema/analytical_chemistry/mass_spectrometry.yaml +206 -0
- valuesets/schema/bio/bio_entities.yaml +364 -0
- valuesets/schema/bio/biological_colors.yaml +434 -0
- valuesets/schema/bio/cell_cycle.yaml +309 -0
- valuesets/schema/bio/currency_chemicals.yaml +70 -0
- valuesets/schema/bio/developmental_stages.yaml +226 -0
- valuesets/schema/bio/genome_features.yaml +342 -0
- valuesets/schema/bio/genomics.yaml +101 -0
- valuesets/schema/bio/go_aspect.yaml +39 -0
- valuesets/schema/bio/go_causality.yaml +119 -0
- valuesets/schema/bio/go_evidence.yaml +215 -0
- valuesets/schema/bio/insdc_geographic_locations.yaml +911 -0
- valuesets/schema/bio/insdc_missing_values.yaml +85 -0
- valuesets/schema/bio/lipid_categories.yaml +72 -0
- valuesets/schema/bio/plant_biology.yaml +125 -0
- valuesets/schema/bio/plant_developmental_stages.yaml +77 -0
- valuesets/schema/bio/plant_sex.yaml +108 -0
- valuesets/schema/bio/protein_evidence.yaml +63 -0
- valuesets/schema/bio/proteomics_standards.yaml +116 -0
- valuesets/schema/bio/psi_mi.yaml +400 -0
- valuesets/schema/bio/relationship_to_oxygen.yaml +46 -0
- valuesets/schema/bio/sequence_alphabets.yaml +1168 -0
- valuesets/schema/bio/sequence_chemistry.yaml +477 -0
- valuesets/schema/bio/sequencing_platforms.yaml +515 -0
- valuesets/schema/bio/structural_biology.yaml +428 -0
- valuesets/schema/bio/taxonomy.yaml +453 -0
- valuesets/schema/bio/trophic_levels.yaml +118 -0
- valuesets/schema/bio/uniprot_species.yaml +1209 -0
- valuesets/schema/bio/viral_genome_types.yaml +99 -0
- valuesets/schema/bioprocessing/scale_up.yaml +458 -0
- valuesets/schema/business/human_resources.yaml +752 -0
- valuesets/schema/business/industry_classifications.yaml +448 -0
- valuesets/schema/business/management_operations.yaml +602 -0
- valuesets/schema/business/organizational_structures.yaml +645 -0
- valuesets/schema/business/quality_management.yaml +502 -0
- valuesets/schema/business/supply_chain.yaml +688 -0
- valuesets/schema/chemistry/chemical_entities.yaml +639 -0
- valuesets/schema/chemistry/reaction_directionality.yaml +60 -0
- valuesets/schema/chemistry/reactions.yaml +442 -0
- valuesets/schema/clinical/nih_demographics.yaml +285 -0
- valuesets/schema/clinical/phenopackets.yaml +429 -0
- valuesets/schema/computing/file_formats.yaml +631 -0
- valuesets/schema/computing/maturity_levels.yaml +229 -0
- valuesets/schema/computing/mime_types.yaml +266 -0
- valuesets/schema/confidence_levels.yaml +206 -0
- valuesets/schema/contributor.yaml +30 -0
- valuesets/schema/core.yaml +55 -0
- valuesets/schema/data/data_absent_reason.yaml +82 -0
- valuesets/schema/data_science/binary_classification.yaml +125 -0
- valuesets/schema/data_science/emotion_classification.yaml +109 -0
- valuesets/schema/data_science/priority_severity.yaml +122 -0
- valuesets/schema/data_science/quality_control.yaml +68 -0
- valuesets/schema/data_science/sentiment_analysis.yaml +81 -0
- valuesets/schema/data_science/text_classification.yaml +135 -0
- valuesets/schema/demographics.yaml +238 -0
- valuesets/schema/ecological_interactions.yaml +298 -0
- valuesets/schema/energy/energy.yaml +595 -0
- valuesets/schema/energy/fossil_fuels.yaml +28 -0
- valuesets/schema/energy/nuclear/nuclear_facilities.yaml +463 -0
- valuesets/schema/energy/nuclear/nuclear_fuel_cycle.yaml +82 -0
- valuesets/schema/energy/nuclear/nuclear_fuels.yaml +421 -0
- valuesets/schema/energy/nuclear/nuclear_operations.yaml +480 -0
- valuesets/schema/energy/nuclear/nuclear_regulatory.yaml +200 -0
- valuesets/schema/energy/nuclear/nuclear_safety.yaml +352 -0
- valuesets/schema/energy/nuclear/nuclear_waste.yaml +332 -0
- valuesets/schema/energy/nuclear/reactor_types.yaml +394 -0
- valuesets/schema/environmental_health/exposures.yaml +355 -0
- valuesets/schema/generated_slots.yaml +1828 -0
- valuesets/schema/geography/geographic_codes.yaml +1018 -0
- valuesets/schema/health/vaccination.yaml +102 -0
- valuesets/schema/health.yaml +38 -0
- valuesets/schema/healthcare.yaml +53 -0
- valuesets/schema/industry/extractive_industry.yaml +89 -0
- valuesets/schema/industry/mining.yaml +888 -0
- valuesets/schema/industry/safety_colors.yaml +375 -0
- valuesets/schema/investigation.yaml +64 -0
- valuesets/schema/materials_science/characterization_methods.yaml +193 -0
- valuesets/schema/materials_science/crystal_structures.yaml +138 -0
- valuesets/schema/materials_science/material_properties.yaml +135 -0
- valuesets/schema/materials_science/material_types.yaml +151 -0
- valuesets/schema/materials_science/pigments_dyes.yaml +465 -0
- valuesets/schema/materials_science/synthesis_methods.yaml +186 -0
- valuesets/schema/medical/clinical.yaml +610 -0
- valuesets/schema/medical/neuroimaging.yaml +325 -0
- valuesets/schema/mining_processing.yaml +295 -0
- valuesets/schema/physics/states_of_matter.yaml +46 -0
- valuesets/schema/slot_mixins.yaml +143 -0
- valuesets/schema/social/person_status.yaml +28 -0
- valuesets/schema/spatial/spatial_qualifiers.yaml +466 -0
- valuesets/schema/statistics/prediction_outcomes.yaml +26 -0
- valuesets/schema/statistics.yaml +34 -0
- valuesets/schema/time/temporal.yaml +435 -0
- valuesets/schema/types.yaml +15 -0
- valuesets/schema/units/measurements.yaml +675 -0
- valuesets/schema/valuesets.yaml +100 -0
- valuesets/schema/visual/colors.yaml +778 -0
- valuesets/utils/__init__.py +6 -0
- valuesets/utils/comparison.py +102 -0
- valuesets/utils/expand_dynamic_enums.py +414 -0
- valuesets/utils/mapping_utils.py +236 -0
- valuesets/validators/__init__.py +11 -0
- valuesets/validators/enum_evaluator.py +669 -0
- valuesets/validators/oak_config.yaml +70 -0
- valuesets/validators/validate_with_ols.py +241 -0
- valuesets-0.3.1.dist-info/METADATA +395 -0
- valuesets-0.3.1.dist-info/RECORD +248 -0
- valuesets-0.3.1.dist-info/WHEEL +4 -0
- valuesets-0.3.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rich Pydantic Generator for LinkML Schemas
|
|
3
|
+
|
|
4
|
+
This generator creates Pydantic-compatible Python code from LinkML schemas,
|
|
5
|
+
with enhanced enum support that includes metadata (descriptions, meanings, annotations).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Dict, Any, List, Optional, TextIO
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from linkml_runtime.utils.schemaview import SchemaView
|
|
14
|
+
from linkml_runtime.linkml_model.meta import (
|
|
15
|
+
SchemaDefinition, EnumDefinition, PermissibleValue, ClassDefinition
|
|
16
|
+
)
|
|
17
|
+
from linkml.generators.pydanticgen import PydanticGenerator
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RichPydanticGenerator(PydanticGenerator):
|
|
21
|
+
"""
|
|
22
|
+
Enhanced Pydantic generator that creates enums with metadata support.
|
|
23
|
+
|
|
24
|
+
This generator extends the base PydanticGenerator to create enums that:
|
|
25
|
+
1. Are fully compatible with standard Python enums
|
|
26
|
+
2. Include all metadata from LinkML schemas (descriptions, meanings, annotations)
|
|
27
|
+
3. Provide convenient methods to access metadata
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
generatorname = "rich-pydantic"
|
|
31
|
+
generatorversion = "0.1.0"
|
|
32
|
+
|
|
33
|
+
def __init__(self, schema: str, **kwargs):
|
|
34
|
+
super().__init__(schema, **kwargs)
|
|
35
|
+
self.schema_view = SchemaView(schema)
|
|
36
|
+
|
|
37
|
+
def serialize(self, **kwargs) -> str:
|
|
38
|
+
"""Generate the complete Python module."""
|
|
39
|
+
output = []
|
|
40
|
+
|
|
41
|
+
# Header and imports
|
|
42
|
+
output.extend(self._generate_header())
|
|
43
|
+
output.extend(self._generate_imports())
|
|
44
|
+
|
|
45
|
+
# Base classes and utilities
|
|
46
|
+
output.extend(self._generate_base_classes())
|
|
47
|
+
|
|
48
|
+
# Generate all enums with rich metadata
|
|
49
|
+
output.extend(self._generate_rich_enums())
|
|
50
|
+
|
|
51
|
+
# Generate classes (reuse base generator logic)
|
|
52
|
+
output.extend(self._generate_classes())
|
|
53
|
+
|
|
54
|
+
return "\n".join(output)
|
|
55
|
+
|
|
56
|
+
def _generate_header(self) -> List[str]:
|
|
57
|
+
"""Generate file header."""
|
|
58
|
+
return [
|
|
59
|
+
'"""',
|
|
60
|
+
'Generated Pydantic models with rich enum support.',
|
|
61
|
+
'',
|
|
62
|
+
'This module was automatically generated from a LinkML schema.',
|
|
63
|
+
'It includes enums with full metadata support (descriptions, meanings, annotations).',
|
|
64
|
+
'"""',
|
|
65
|
+
'',
|
|
66
|
+
'from __future__ import annotations',
|
|
67
|
+
''
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
def _generate_imports(self) -> List[str]:
|
|
71
|
+
"""Generate import statements."""
|
|
72
|
+
return [
|
|
73
|
+
'import re',
|
|
74
|
+
'import sys',
|
|
75
|
+
'from datetime import date, datetime, time',
|
|
76
|
+
'from decimal import Decimal',
|
|
77
|
+
'from enum import Enum',
|
|
78
|
+
'from typing import Any, ClassVar, List, Literal, Dict, Optional, Union',
|
|
79
|
+
'',
|
|
80
|
+
'from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator',
|
|
81
|
+
'',
|
|
82
|
+
'# Import our rich enum support',
|
|
83
|
+
'from valuesets.generators.rich_enum import RichEnum',
|
|
84
|
+
'',
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
def _generate_base_classes(self) -> List[str]:
|
|
88
|
+
"""Generate base configuration classes."""
|
|
89
|
+
return [
|
|
90
|
+
'metamodel_version = "None"',
|
|
91
|
+
'version = "None"',
|
|
92
|
+
'',
|
|
93
|
+
'',
|
|
94
|
+
'class ConfiguredBaseModel(BaseModel):',
|
|
95
|
+
' model_config = ConfigDict(',
|
|
96
|
+
' validate_assignment=True,',
|
|
97
|
+
' validate_default=True,',
|
|
98
|
+
' extra="forbid",',
|
|
99
|
+
' arbitrary_types_allowed=True,',
|
|
100
|
+
' use_enum_values=True,',
|
|
101
|
+
' strict=False,',
|
|
102
|
+
' )',
|
|
103
|
+
' pass',
|
|
104
|
+
'',
|
|
105
|
+
'',
|
|
106
|
+
'class LinkMLMeta(RootModel):',
|
|
107
|
+
' root: Dict[str, Any] = {}',
|
|
108
|
+
' model_config = ConfigDict(frozen=True)',
|
|
109
|
+
'',
|
|
110
|
+
' def __getattr__(self, key: str):',
|
|
111
|
+
' return getattr(self.root, key)',
|
|
112
|
+
'',
|
|
113
|
+
' def __getitem__(self, key: str):',
|
|
114
|
+
' return self.root[key]',
|
|
115
|
+
'',
|
|
116
|
+
' def __setitem__(self, key: str, value):',
|
|
117
|
+
' self.root[key] = value',
|
|
118
|
+
'',
|
|
119
|
+
' def __contains__(self, key: str) -> bool:',
|
|
120
|
+
' return key in self.root',
|
|
121
|
+
'',
|
|
122
|
+
'',
|
|
123
|
+
f'linkml_meta = LinkMLMeta({self._generate_linkml_meta()})',
|
|
124
|
+
'',
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
def _generate_linkml_meta(self) -> str:
|
|
128
|
+
"""Generate the linkml_meta dictionary."""
|
|
129
|
+
schema = self.schema_view.schema
|
|
130
|
+
meta_dict = {
|
|
131
|
+
'default_prefix': schema.default_prefix,
|
|
132
|
+
'description': schema.description,
|
|
133
|
+
'id': schema.id,
|
|
134
|
+
'name': schema.name,
|
|
135
|
+
'title': schema.title,
|
|
136
|
+
}
|
|
137
|
+
return repr(meta_dict)
|
|
138
|
+
|
|
139
|
+
def _generate_rich_enums(self) -> List[str]:
|
|
140
|
+
"""Generate all enums with metadata support."""
|
|
141
|
+
output = []
|
|
142
|
+
|
|
143
|
+
for enum_name in self.schema_view.all_enums():
|
|
144
|
+
enum_def = self.schema_view.get_enum(enum_name)
|
|
145
|
+
if enum_def and enum_def.permissible_values:
|
|
146
|
+
output.extend(self._generate_single_rich_enum(enum_name, enum_def))
|
|
147
|
+
output.append('') # Add spacing between enums
|
|
148
|
+
|
|
149
|
+
return output
|
|
150
|
+
|
|
151
|
+
def _generate_single_rich_enum(self, enum_name: str, enum_def: EnumDefinition) -> List[str]:
|
|
152
|
+
"""Generate a single rich enum."""
|
|
153
|
+
output = []
|
|
154
|
+
|
|
155
|
+
# Class definition
|
|
156
|
+
class_name = self._get_class_name(enum_name)
|
|
157
|
+
output.append(f'class {class_name}(RichEnum):')
|
|
158
|
+
|
|
159
|
+
# Class docstring
|
|
160
|
+
if enum_def.description:
|
|
161
|
+
output.append(' """')
|
|
162
|
+
output.append(f' {enum_def.description}')
|
|
163
|
+
output.append(' """')
|
|
164
|
+
|
|
165
|
+
# Enum members
|
|
166
|
+
output.append(' # Enum members')
|
|
167
|
+
for pv_name, pv in enum_def.permissible_values.items():
|
|
168
|
+
member_name = self._get_enum_member_name(pv_name)
|
|
169
|
+
member_value = pv.text if pv.text is not None else pv_name
|
|
170
|
+
output.append(f' {member_name} = "{member_value}"')
|
|
171
|
+
|
|
172
|
+
output.append('')
|
|
173
|
+
|
|
174
|
+
# Set metadata after class creation to avoid it becoming an enum member
|
|
175
|
+
output.append(f'# Set metadata after class creation to avoid it becoming an enum member')
|
|
176
|
+
output.append(f'{class_name}._metadata = {{')
|
|
177
|
+
|
|
178
|
+
for pv_name, pv in enum_def.permissible_values.items():
|
|
179
|
+
member_name = self._get_enum_member_name(pv_name)
|
|
180
|
+
metadata = self._build_metadata_dict(pv)
|
|
181
|
+
|
|
182
|
+
if metadata: # Only add if there's actual metadata
|
|
183
|
+
output.append(f' "{member_name}": {repr(metadata)},')
|
|
184
|
+
|
|
185
|
+
output.append('}')
|
|
186
|
+
|
|
187
|
+
return output
|
|
188
|
+
|
|
189
|
+
def _build_metadata_dict(self, pv: PermissibleValue) -> Dict[str, Any]:
|
|
190
|
+
"""Build metadata dictionary for a permissible value."""
|
|
191
|
+
metadata = {}
|
|
192
|
+
|
|
193
|
+
if pv.description:
|
|
194
|
+
metadata['description'] = pv.description
|
|
195
|
+
|
|
196
|
+
if pv.meaning:
|
|
197
|
+
metadata['meaning'] = pv.meaning
|
|
198
|
+
|
|
199
|
+
if pv.annotations:
|
|
200
|
+
# Convert annotation objects to simple dictionaries
|
|
201
|
+
annotations_dict = {}
|
|
202
|
+
for key, annotation in pv.annotations.items():
|
|
203
|
+
if hasattr(annotation, 'value'):
|
|
204
|
+
annotations_dict[key] = annotation.value
|
|
205
|
+
else:
|
|
206
|
+
annotations_dict[key] = str(annotation)
|
|
207
|
+
metadata['annotations'] = annotations_dict
|
|
208
|
+
|
|
209
|
+
# Add other fields if they exist
|
|
210
|
+
if hasattr(pv, 'aliases') and pv.aliases:
|
|
211
|
+
metadata['aliases'] = list(pv.aliases)
|
|
212
|
+
|
|
213
|
+
if hasattr(pv, 'deprecated') and pv.deprecated:
|
|
214
|
+
metadata['deprecated'] = pv.deprecated
|
|
215
|
+
|
|
216
|
+
return metadata
|
|
217
|
+
|
|
218
|
+
def _generate_classes(self) -> List[str]:
|
|
219
|
+
"""Generate Pydantic model classes."""
|
|
220
|
+
# For now, we'll use a simplified approach
|
|
221
|
+
# In a full implementation, we'd generate full Pydantic models
|
|
222
|
+
output = []
|
|
223
|
+
|
|
224
|
+
for class_name in self.schema_view.all_classes():
|
|
225
|
+
class_def = self.schema_view.get_class(class_name)
|
|
226
|
+
if class_def:
|
|
227
|
+
output.extend(self._generate_single_class(class_name, class_def))
|
|
228
|
+
output.append('')
|
|
229
|
+
|
|
230
|
+
return output
|
|
231
|
+
|
|
232
|
+
def _generate_single_class(self, class_name: str, class_def: ClassDefinition) -> List[str]:
|
|
233
|
+
"""Generate a single Pydantic model class."""
|
|
234
|
+
output = []
|
|
235
|
+
|
|
236
|
+
# For now, just create empty classes with docstrings
|
|
237
|
+
pydantic_class_name = self._get_class_name(class_name)
|
|
238
|
+
output.append(f'class {pydantic_class_name}(ConfiguredBaseModel):')
|
|
239
|
+
|
|
240
|
+
if class_def.description:
|
|
241
|
+
output.append(' """')
|
|
242
|
+
output.append(f' {class_def.description}')
|
|
243
|
+
output.append(' """')
|
|
244
|
+
|
|
245
|
+
output.append(' pass # TODO: Implement class slots')
|
|
246
|
+
|
|
247
|
+
return output
|
|
248
|
+
|
|
249
|
+
def _get_class_name(self, name: str) -> str:
|
|
250
|
+
"""Convert a LinkML name to a Python class name with proper CamelCase."""
|
|
251
|
+
# Handle already CamelCase names
|
|
252
|
+
if not any(c in name for c in ['_', '-', ' ']):
|
|
253
|
+
# If it's already in some form of CamelCase, preserve it
|
|
254
|
+
# Just ensure first letter is capitalized
|
|
255
|
+
return name[0].upper() + name[1:] if name else ''
|
|
256
|
+
|
|
257
|
+
# Convert snake_case, kebab-case, or space-separated to CamelCase
|
|
258
|
+
words = re.split(r'[_\s-]+', name)
|
|
259
|
+
|
|
260
|
+
# Properly capitalize each word, preserving existing caps when appropriate
|
|
261
|
+
result = []
|
|
262
|
+
for word in words:
|
|
263
|
+
if word:
|
|
264
|
+
if word.isupper():
|
|
265
|
+
# If the word is all caps (like "ISO"), keep it that way
|
|
266
|
+
result.append(word)
|
|
267
|
+
elif word[0].isupper() and len(word) > 1:
|
|
268
|
+
# If already starts with capital, preserve the casing
|
|
269
|
+
result.append(word)
|
|
270
|
+
else:
|
|
271
|
+
# Otherwise, capitalize first letter
|
|
272
|
+
result.append(word[0].upper() + word[1:].lower())
|
|
273
|
+
|
|
274
|
+
return ''.join(result)
|
|
275
|
+
|
|
276
|
+
def _get_enum_member_name(self, name: str) -> str:
|
|
277
|
+
"""Convert a permissible value name to a Python enum member name."""
|
|
278
|
+
# Convert to valid Python identifier in UPPER_CASE
|
|
279
|
+
# Replace invalid characters with underscores
|
|
280
|
+
member_name = re.sub(r'[^a-zA-Z0-9_]', '_', name).upper()
|
|
281
|
+
|
|
282
|
+
# Ensure it doesn't start with a digit
|
|
283
|
+
if member_name and member_name[0].isdigit():
|
|
284
|
+
member_name = f'_{member_name}'
|
|
285
|
+
|
|
286
|
+
return member_name
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def cli():
|
|
290
|
+
"""Command line interface for the rich pydantic generator."""
|
|
291
|
+
import argparse
|
|
292
|
+
|
|
293
|
+
parser = argparse.ArgumentParser(description='Generate rich Pydantic models from LinkML schema')
|
|
294
|
+
parser.add_argument('schema', help='LinkML schema file')
|
|
295
|
+
parser.add_argument('-o', '--output', help='Output file (default: stdout)')
|
|
296
|
+
|
|
297
|
+
args = parser.parse_args()
|
|
298
|
+
|
|
299
|
+
generator = RichPydanticGenerator(args.schema)
|
|
300
|
+
output = generator.serialize()
|
|
301
|
+
|
|
302
|
+
if args.output:
|
|
303
|
+
with open(args.output, 'w') as f:
|
|
304
|
+
f.write(output)
|
|
305
|
+
else:
|
|
306
|
+
print(output)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
if __name__ == '__main__':
|
|
310
|
+
cli()
|