Skip to content

Commit 693dec1

Browse files
authored
Merge pull request #513 from djarecka/ga_rel_generate
updates to the release GA
2 parents 01f10db + ccba7f9 commit 693dec1

File tree

3 files changed

+133
-15
lines changed

3 files changed

+133
-15
lines changed

.github/workflows/validate_and_release.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,32 @@ jobs:
4646
uses: actions/setup-python@v5
4747
with:
4848
python-version: 3.12
49-
49+
- name: Install dependencies
50+
run: |
51+
python -m pip install --upgrade pip setuptools
52+
pip install linkml astor pre-commit
53+
pip install git+https://github.com/ReproNim/reproschema-py.git
54+
- name: Generate pydantic using linml and fixing it with reproschema specific script
55+
run: |
56+
gen-pydantic --pydantic-version 2 linkml-schema/reproschema.yaml > reproschema_model_autogen.py
57+
python scripts/fix_pydantic.py reproschema_model_autogen.py reproschema_model.py
58+
pre-commit run --files reproschema_model.py || true
59+
- name: Generate jsonld format using linkml
60+
run: |
61+
gen-jsonld --context contexts/reproschema linkml-schema/reproschema.yaml > reproschema.jsonld
62+
- name: Generate n-triples and turtle formats using reproschema
63+
run: |
64+
reproschema convert --format n-triples reproschema.jsonld > reproschema.nt
65+
reproschema convert --format turtle reproschema.jsonld > reproschema.ttl
5066
- name: Make a release
5167
run: |
5268
echo "Making a release ${{ inputs.version }}"
5369
mkdir releases/${{ inputs.version }}
54-
cp contexts/reproschema releases/${{ inputs.version }}/base
70+
cp contexts/reproschema releases/${{ inputs.version }}/reproschema
71+
cp reproschema_model.py releases/${{ inputs.version }}/reproschema_model.py
72+
cp reproschema.jsonld releases/${{ inputs.version }}/reproschema.jsonld
73+
cp reproschema.nt releases/${{ inputs.version }}/reproschema.nt
74+
cp reproschema.ttl releases/${{ inputs.version }}/reproschema.ttl
5575
# python scripts/makeRelease.py ${{ inputs.version }}
5676

5777
- name: Open pull requests to add files

linkml-schema/reproschema.yaml

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ slots:
5555
slot_uri: schema:associatedMedia
5656
audio:
5757
title: audio
58-
description: TODO
58+
description: An audio object.
5959
slot_uri: schema:audio
6060
any_of:
6161
- range: uri
@@ -101,12 +101,8 @@ slots:
101101
contentUrl:
102102
slot_uri: schema:contentUrl
103103
range: uriorcurie
104-
creator:
105-
slot_uri: schema:creator
106-
range: Person
107104
cronTable:
108105
title: cronTable
109-
description: TODO not described in reproschema
110106
slot_uri: reproschema:cronTable
111107
datumType:
112108
title: datumType
@@ -264,7 +260,7 @@ slots:
264260
range: OverrideProperty
265261
preamble:
266262
title: Preamble
267-
description: The preamble for an assessment
263+
description: The preamble for an assessment.
268264
slot_uri: reproschema:preamble
269265
multivalued: true
270266
range: langString
@@ -326,7 +322,7 @@ slots:
326322
range: datetime
327323
slot_uri: prov:startedAtTime
328324
subject_id:
329-
slot_uri: nidm:subject_id #TODO check this @type:rdf:Property
325+
slot_uri: nidm:subject_id
330326
range: string
331327
ui:
332328
title: UI
@@ -502,7 +498,7 @@ classes:
502498
class_uri: rdf:langString
503499
MediaObject:
504500
title: Media Object
505-
description: Add description #TODO
501+
description: A media object, such as an image, video, audio, or text object embedded in a web page or a downloadable dataset.
506502
is_a: Thing
507503
class_uri: schema:MediaObject
508504
slots:
@@ -542,7 +538,7 @@ classes:
542538
- id
543539
- subject_id
544540
class_uri: reproschema:Participant
545-
Protocol: # TODO multiple types
541+
Protocol:
546542
title: Protocol
547543
description: A representation of a study which comprises one or more assessments.
548544
is_a: Thing
@@ -555,6 +551,7 @@ classes:
555551
- description
556552
- landingPage
557553
- messages
554+
- preamble
558555
- prefLabel
559556
- schemaVersion
560557
- ui
@@ -596,7 +593,7 @@ classes:
596593
- unitOptions
597594
- valueType
598595
class_uri: reproschema:ResponseOption
599-
SoftwareAgent: # TODO multiple types
596+
SoftwareAgent:
600597
title: Software Agent
601598
description:
602599
Captures information about some action that took place. It also links to information
@@ -616,9 +613,8 @@ classes:
616613
- category
617614
class_uri: schema:Thing
618615
UI:
619-
title: todo
620-
description:
621-
- todo
616+
title: UI properties
617+
description: A group of properties related to UI.
622618
slots:
623619
- order
624620
- addProperties

scripts/fix_pydantic.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
""" Using ast transformer to fix issues with automatic pydantic generation"""
2+
3+
import ast
4+
import sys
5+
6+
import astor
7+
8+
9+
class ClassRemover(ast.NodeTransformer):
10+
def __init__(self, class_name):
11+
self.class_name = class_name
12+
13+
def visit_ClassDef(self, node):
14+
# Remove the class if its name matches the class_to_remove
15+
if node.name == self.class_name:
16+
return None
17+
return node
18+
19+
def visit_Expr(self, node):
20+
# Check if the node is a call expression
21+
if isinstance(node.value, ast.Call):
22+
# Check if the call expression is an attribute (method call)
23+
if isinstance(node.value.func, ast.Attribute):
24+
# Check if the method call matches the specified class
25+
if (
26+
isinstance(node.value.func.value, ast.Name)
27+
and node.value.func.value.id == self.class_name
28+
):
29+
return None # Remove this node
30+
return self.generic_visit(node)
31+
32+
33+
class TypeReplacer(ast.NodeTransformer):
34+
def __init__(self, old_type, new_type):
35+
self.old_type = old_type
36+
self.new_type = new_type
37+
38+
def visit_FunctionDef(self, node):
39+
# Check all arguments in the function definition
40+
for arg in node.args.args:
41+
if arg.annotation:
42+
arg.annotation = self.visit(arg.annotation)
43+
return self.generic_visit(node)
44+
45+
def visit_AsyncFunctionDef(self, node):
46+
# Handle async function definitions similarly
47+
for arg in node.args.args:
48+
if arg.annotation:
49+
arg.annotation = self.visit(arg.annotation)
50+
return self.generic_visit(node)
51+
52+
def visit_Name(self, node):
53+
# Replace the old type with the new type
54+
if node.id == self.old_type:
55+
node.id = self.new_type
56+
return node
57+
58+
def visit_Subscript(self, node):
59+
# Handle Union, Optional, and other subscripted types
60+
node.value = self.visit(node.value)
61+
node.slice = self.visit(node.slice)
62+
return node
63+
64+
def visit_Index(self, node):
65+
# Handle the index part of subscripted types
66+
node.value = self.visit(node.value)
67+
return node
68+
69+
def visit_Tuple(self, node):
70+
# Handle tuples in type annotations
71+
node.elts = [self.visit(elt) for elt in node.elts]
72+
return node
73+
74+
75+
def edit_pydantic(input_file, output_file):
76+
77+
with open(input_file, "r") as file:
78+
tree = ast.parse(file.read())
79+
80+
transformer_class = ClassRemover(class_name="LangString")
81+
tree_modclass = transformer_class.visit(tree)
82+
83+
transformer_tp = TypeReplacer(
84+
old_type="LangString", new_type="Dict[str, str]"
85+
)
86+
tree_modclass_modtype = transformer_tp.visit(tree_modclass)
87+
88+
with open(output_file, "w") as file:
89+
file.write(astor.to_source(tree_modclass_modtype))
90+
91+
92+
if __name__ == "__main__":
93+
input_file = sys.argv[1]
94+
if len(sys.argv) < 3:
95+
output_file = input_file
96+
else:
97+
output_file = sys.argv[2]
98+
print(
99+
f"Fixing automatically generated pydantic file {input_file} "
100+
f"and saving to {output_file}"
101+
)
102+
edit_pydantic(input_file, output_file)

0 commit comments

Comments
 (0)