Skip to content

Commit 341f3fa

Browse files
committed
parse operations
1 parent 9156a26 commit 341f3fa

File tree

2 files changed

+140
-50
lines changed

2 files changed

+140
-50
lines changed

atomrdf/datamodels/workflow/operations.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def from_graph(cls, graph, activity_id):
8080

8181

8282
class Rotate(Activity):
83-
rotation_matrix: Optional[DataProperty[List[List[float]]]] = None
83+
rotation_matrix: Optional[List[List[float]]] = None
8484

8585
def to_graph(self, graph):
8686
activity_id = f"rotate:{str(uuid.uuid4())}"
@@ -185,12 +185,12 @@ def from_graph(cls, graph, activity_id):
185185
id=activity_id,
186186
input_sample=input_sample,
187187
output_sample=output_sample,
188-
rotation_matrix=DataProperty(value=rotation_matrix),
188+
rotation_matrix=rotation_matrix,
189189
)
190190

191191

192192
class Translate(Activity):
193-
translation_vector: Optional[DataProperty[List[float]]] = None
193+
translation_vector: Optional[List[float]] = None
194194

195195
def to_graph(self, graph):
196196
activity_id = f"translate:{str(uuid.uuid4())}"
@@ -207,21 +207,21 @@ def to_graph(self, graph):
207207
(
208208
translation_vector,
209209
CMSO.hasComponent_x,
210-
Literal(self.translation_vector.value[0], datatype=XSD.float),
210+
Literal(self.translation_vector[0], datatype=XSD.float),
211211
)
212212
)
213213
graph.add(
214214
(
215215
translation_vector,
216216
CMSO.hasComponent_y,
217-
Literal(self.translation_vector.value[1], datatype=XSD.float),
217+
Literal(self.translation_vector[1], datatype=XSD.float),
218218
)
219219
)
220220
graph.add(
221221
(
222222
translation_vector,
223223
CMSO.hasComponent_z,
224-
Literal(self.translation_vector.value[2], datatype=XSD.float),
224+
Literal(self.translation_vector[2], datatype=XSD.float),
225225
)
226226
)
227227

@@ -241,14 +241,16 @@ def from_graph(cls, graph, activity_id):
241241
id=activity_id,
242242
input_sample=input_sample,
243243
output_sample=output_sample,
244-
translation_vector=DataProperty(value=translation_vector_lst),
244+
translation_vector=(
245+
translation_vector_lst[0] if translation_vector_lst else None
246+
),
245247
)
246248

247249

248250
class Shear(Activity):
249-
shear_vector: Optional[DataProperty[List[float]]] = None
250-
normal_vector: Optional[DataProperty[List[float]]] = None
251-
distance: Optional[DataProperty[float]] = None
251+
shear_vector: Optional[List[float]] = None
252+
normal_vector: Optional[List[float]] = None
253+
distance: Optional[float] = None
252254

253255
def to_graph(self, graph):
254256
activity_id = f"shear:{str(uuid.uuid4())}"
@@ -263,21 +265,21 @@ def to_graph(self, graph):
263265
(
264266
shear_vector,
265267
CMSO.hasComponent_x,
266-
Literal(self.shear_vector.value[0], datatype=XSD.float),
268+
Literal(self.shear_vector[0], datatype=XSD.float),
267269
)
268270
)
269271
graph.add(
270272
(
271273
shear_vector,
272274
CMSO.hasComponent_y,
273-
Literal(self.shear_vector.value[1], datatype=XSD.float),
275+
Literal(self.shear_vector[1], datatype=XSD.float),
274276
)
275277
)
276278
graph.add(
277279
(
278280
shear_vector,
279281
CMSO.hasComponent_z,
280-
Literal(self.shear_vector.value[2], datatype=XSD.float),
282+
Literal(self.shear_vector[2], datatype=XSD.float),
281283
)
282284
)
283285

@@ -292,29 +294,29 @@ def to_graph(self, graph):
292294
(
293295
normal_vector,
294296
CMSO.hasComponent_x,
295-
Literal(self.normal_vector.value[0], datatype=XSD.float),
297+
Literal(self.normal_vector[0], datatype=XSD.float),
296298
)
297299
)
298300
graph.add(
299301
(
300302
normal_vector,
301303
CMSO.hasComponent_y,
302-
Literal(self.normal_vector.value[1], datatype=XSD.float),
304+
Literal(self.normal_vector[1], datatype=XSD.float),
303305
)
304306
)
305307
graph.add(
306308
(
307309
normal_vector,
308310
CMSO.hasComponent_z,
309-
Literal(self.normal_vector.value[2], datatype=XSD.float),
311+
Literal(self.normal_vector[2], datatype=XSD.float),
310312
)
311313
)
312314
if self.distance:
313315
graph.add(
314316
(
315317
plane,
316318
CMSO.hasDistanceFromOrigin,
317-
Literal(self.distance.value, datatype=XSD.float),
319+
Literal(self.distance, datatype=XSD.float),
318320
)
319321
)
320322

@@ -349,7 +351,11 @@ def from_graph(cls, graph, activity_id):
349351
id=activity_id,
350352
input_sample=input_sample,
351353
output_sample=output_sample,
352-
shear_vector=DataProperty(value=shear_vector_lst),
353-
normal_vector=DataProperty(value=normal_vector_lst),
354-
distance=DataProperty(value=distance),
354+
shear_vector=shear_vector_lst[0] if shear_vector_lst else None,
355+
normal_vector=(
356+
normal_vector_lst[0]
357+
if normal_vector_lst and len(normal_vector_lst) > 0
358+
else None
359+
),
360+
distance=distance,
355361
)

atomrdf/io/workflow_parser.py

Lines changed: 114 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,31 @@
77

88
from atomrdf.datamodels.structure import AtomicScaleSample
99
from atomrdf.datamodels.workflow.workflow import Simulation
10+
from atomrdf.datamodels.workflow.operations import (
11+
DeleteAtom,
12+
SubstituteAtom,
13+
AddAtom,
14+
Rotate,
15+
Translate,
16+
Shear,
17+
)
1018
from atomrdf import KnowledgeGraph
1119
from rdflib import URIRef, Literal, Namespace, XSD
1220

1321
DCAT = Namespace("http://www.w3.org/ns/dcat#")
1422

23+
# Mapping of operation method names to their classes
24+
OPERATION_MAP = {
25+
"DeleteAtom": DeleteAtom,
26+
"SubstituteAtom": SubstituteAtom,
27+
"AddAtom": AddAtom,
28+
"Rotate": Rotate,
29+
"Rotation": Rotate, # Alias
30+
"Translate": Translate,
31+
"Translation": Translate, # Alias
32+
"Shear": Shear,
33+
}
34+
1535

1636
class WorkflowParser:
1737
"""
@@ -20,7 +40,8 @@ class WorkflowParser:
2040
Handles parsing of:
2141
- Computational samples (with deduplication via hashing)
2242
- Workflows/Simulations
23-
- Activities (transformations between samples)
43+
- Operations (transformations between samples: DeleteAtom, SubstituteAtom,
44+
AddAtom, Rotate, Translate, Shear)
2445
2546
Attributes
2647
----------
@@ -477,32 +498,92 @@ def parse_workflows(self, workflow_data_list: List[Dict[str, Any]]) -> List[str]
477498

478499
return workflow_uris
479500

480-
def parse_activities(self, activity_data_list: List[Dict[str, Any]]) -> List[str]:
501+
def parse_operations(self, operation_data_list: List[Dict[str, Any]]) -> List[str]:
481502
"""
482-
Parse activity data (transformations between samples).
503+
Parse operation data (transformations between samples).
504+
505+
Operations include: DeleteAtom, SubstituteAtom, AddAtom, Rotate,
506+
Translate, and Shear.
483507
484508
Parameters
485509
----------
486-
activity_data_list : list of dict
487-
List of activity dictionaries
510+
operation_data_list : list of dict
511+
List of operation dictionaries. Each must have:
512+
- 'method': The operation type (e.g., 'DeleteAtom', 'Rotate')
513+
- 'input_sample': Sample ID or list of sample IDs
514+
- 'output_sample': Sample ID or list of sample IDs
515+
- Additional method-specific parameters (e.g., rotation_matrix for Rotate)
488516
489517
Returns
490518
-------
491519
list of str
492-
List of activity URIs created
520+
List of operation URIs created
493521
494-
Notes
495-
-----
496-
This method is not yet fully implemented.
522+
Raises
523+
------
524+
ValueError
525+
If operation method is not recognized
497526
"""
498-
# TODO: Implement activity parsing
499-
# This will be similar to workflow parsing but for Activity objects
500-
activity_uris = []
501-
if self.debug:
502-
print(
503-
f"Activity parsing not yet implemented. Found {len(activity_data_list)} activities."
504-
)
505-
return activity_uris
527+
operation_uris = []
528+
529+
for i, operation_data in enumerate(operation_data_list):
530+
method = operation_data.get("method")
531+
if not method:
532+
if self.debug:
533+
print(f"Skipping operation {i+1}: no method specified")
534+
continue
535+
536+
# Get the operation class
537+
operation_class = OPERATION_MAP.get(method)
538+
if not operation_class:
539+
raise ValueError(
540+
f"Unknown operation method: {method}. "
541+
f"Available methods: {list(OPERATION_MAP.keys())}"
542+
)
543+
544+
# Resolve input sample references
545+
if "input_sample" in operation_data:
546+
input_sample = operation_data["input_sample"]
547+
if isinstance(input_sample, list):
548+
operation_data["input_sample"] = [
549+
self.sample_map.get(s, s) for s in input_sample
550+
]
551+
else:
552+
operation_data["input_sample"] = self.sample_map.get(
553+
input_sample, input_sample
554+
)
555+
556+
# Resolve output sample references
557+
if "output_sample" in operation_data:
558+
output_sample = operation_data["output_sample"]
559+
if isinstance(output_sample, list):
560+
operation_data["output_sample"] = [
561+
self.sample_map.get(s, s) for s in output_sample
562+
]
563+
else:
564+
operation_data["output_sample"] = self.sample_map.get(
565+
output_sample, output_sample
566+
)
567+
568+
# Remove 'method' from data as it's not part of the model
569+
operation_data_copy = operation_data.copy()
570+
operation_data_copy.pop("method", None)
571+
572+
# Create the operation object
573+
operation = operation_class(**operation_data_copy)
574+
575+
# Add to knowledge graph
576+
operation.to_graph(self.kg)
577+
operation_uris.append(operation.id)
578+
579+
if self.debug:
580+
print(
581+
f"Added operation {i+1} ({method}): "
582+
f"{operation_data.get('input_sample')} -> "
583+
f"{operation_data.get('output_sample')}"
584+
)
585+
586+
return operation_uris
506587

507588
def parse(self, data: Union[str, Path, Dict[str, Any]]) -> Dict[str, Any]:
508589
"""
@@ -521,7 +602,7 @@ def parse(self, data: Union[str, Path, Dict[str, Any]]) -> Dict[str, Any]:
521602
522603
- 'sample_map' : dict mapping original IDs to URIs
523604
- 'workflow_uris' : list of created workflow URIs
524-
- 'activity_uris' : list of created activity URIs
605+
- 'operation_uris' : list of created operation URIs
525606
526607
Raises
527608
------
@@ -556,19 +637,22 @@ def parse(self, data: Union[str, Path, Dict[str, Any]]) -> Dict[str, Any]:
556637
f"Unsupported data type: {type(data)}. Expected str, Path, or dict."
557638
)
558639

559-
result = {"sample_map": {}, "workflow_uris": [], "activity_uris": []}
640+
result = {"sample_map": {}, "workflow_uris": [], "operation_uris": []}
560641

561-
# Parse samples first (they may be referenced by workflows/activities)
642+
# Parse samples first (they may be referenced by workflows/operations)
562643
if "computational_sample" in data:
563644
result["sample_map"] = self.parse_samples(data["computational_sample"])
564645

565646
# Parse workflows
566647
if "workflow" in data:
567648
result["workflow_uris"] = self.parse_workflows(data["workflow"])
568649

569-
# Parse activities
570-
if "activity" in data:
571-
result["activity_uris"] = self.parse_activities(data["activity"])
650+
# Parse operations (formerly activities)
651+
if "operation" in data:
652+
result["operation_uris"] = self.parse_operations(data["operation"])
653+
# Backwards compatibility: also check for 'activity' key
654+
elif "activity" in data:
655+
result["operation_uris"] = self.parse_operations(data["activity"])
572656

573657
return result
574658

@@ -705,26 +789,26 @@ def parse_workflow(
705789
return uris[0] if uris else None
706790

707791

708-
def parse_activity(
709-
activity_data: Dict[str, Any], graph: Optional[KnowledgeGraph] = None
792+
def parse_operation(
793+
operation_data: Dict[str, Any], graph: Optional[KnowledgeGraph] = None
710794
) -> str:
711795
"""
712-
Parse a single activity.
796+
Parse a single operation.
713797
714798
Parameters
715799
----------
716-
activity_data : dict
717-
Activity dictionary
800+
operation_data : dict
801+
Operation dictionary with 'method' field specifying the operation type
718802
graph : KnowledgeGraph, optional
719803
Knowledge graph instance. If None, creates a new one.
720804
721805
Returns
722806
-------
723807
str or None
724-
URI of the created activity
808+
URI of the created operation
725809
"""
726810
parser = WorkflowParser(kg=graph)
727-
uris = parser.parse_activities([activity_data])
811+
uris = parser.parse_operations([operation_data])
728812
return uris[0] if uris else None
729813

730814

0 commit comments

Comments
 (0)