Skip to content

Commit ed61eaf

Browse files
Dev 10 25 bis (#17)
- Fixed EPC stream class : - Reading and HDF5 issues. - Fixed object version handling. - Fixed handling of prefixed classes and object generation. - Added proper handling for empty EPC files. - Added support for forcing an HDF5 path. - Ensured parent directories are created when generating an EPC file.
1 parent 97428bb commit ed61eaf

21 files changed

Lines changed: 1825 additions & 279 deletions

energyml-utils/.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[flake8]
22
# Ignore specific error codes (comma-separated list)
3-
ignore = E501, E722 #, W503, F403
3+
ignore = E501, E722, W503, F403, E203, E202
44

55
# Max line length (default is 79, can be changed)
66
max-line-length = 120
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
"""
2+
Example: Managing .rels files in EPC files using EpcStreamReader
3+
4+
This example demonstrates the new .rels management capabilities:
5+
1. Removing objects without breaking .rels files
6+
2. Cleaning orphaned relationships
7+
3. Rebuilding all .rels files from scratch
8+
"""
9+
10+
import sys
11+
from pathlib import Path
12+
13+
# Add src directory to path
14+
src_path = Path(__file__).parent.parent / "src"
15+
sys.path.insert(0, str(src_path))
16+
17+
from energyml.utils.epc_stream import EpcStreamReader
18+
19+
20+
def example_workflow(epc_path: str):
21+
"""
22+
Complete workflow example for .rels management.
23+
"""
24+
print(f"Opening EPC file: {epc_path}")
25+
reader = EpcStreamReader(epc_path)
26+
print(f"Loaded {len(reader)} objects\n")
27+
28+
# ============================================================
29+
# Scenario 1: Remove objects without breaking .rels
30+
# ============================================================
31+
print("=" * 70)
32+
print("SCENARIO 1: Remove objects (keeps .rels intact)")
33+
print("=" * 70)
34+
35+
# Get some objects to remove
36+
objects_to_remove = list(reader._metadata.keys())[-3:]
37+
print(f"\nRemoving {len(objects_to_remove)} objects:")
38+
39+
for obj_id in objects_to_remove:
40+
print(f" - {obj_id}")
41+
reader.remove_object(obj_id)
42+
43+
print(f"\nRemaining objects: {len(reader)}")
44+
print("Note: .rels files still reference removed objects (orphaned relationships)")
45+
46+
# ============================================================
47+
# Scenario 2: Clean orphaned relationships
48+
# ============================================================
49+
print("\n" + "=" * 70)
50+
print("SCENARIO 2: Clean orphaned relationships")
51+
print("=" * 70)
52+
53+
print("\nCalling clean_rels()...")
54+
clean_stats = reader.clean_rels()
55+
56+
print("\nCleaning statistics:")
57+
print(f" • .rels files scanned: {clean_stats['rels_files_scanned']}")
58+
print(f" • Orphaned relationships removed: {clean_stats['relationships_removed']}")
59+
print(f" • Empty .rels files deleted: {clean_stats['rels_files_removed']}")
60+
61+
print("\n✓ Orphaned relationships cleaned!")
62+
63+
# ============================================================
64+
# Scenario 3: Rebuild all .rels from scratch
65+
# ============================================================
66+
print("\n" + "=" * 70)
67+
print("SCENARIO 3: Rebuild all .rels from scratch")
68+
print("=" * 70)
69+
70+
print("\nCalling rebuild_all_rels()...")
71+
rebuild_stats = reader.rebuild_all_rels(clean_first=True)
72+
73+
print("\nRebuild statistics:")
74+
print(f" • Objects processed: {rebuild_stats['objects_processed']}")
75+
print(f" • .rels files created: {rebuild_stats['rels_files_created']}")
76+
print(f" • SOURCE relationships: {rebuild_stats['source_relationships']}")
77+
print(f" • DESTINATION relationships: {rebuild_stats['destination_relationships']}")
78+
print(
79+
f" • Total relationships: {rebuild_stats['source_relationships'] + rebuild_stats['destination_relationships']}"
80+
)
81+
82+
print("\n✓ All .rels files rebuilt!")
83+
84+
# ============================================================
85+
# Best Practices
86+
# ============================================================
87+
print("\n" + "=" * 70)
88+
print("BEST PRACTICES")
89+
print("=" * 70)
90+
91+
print(
92+
"""
93+
1. After removing multiple objects:
94+
→ Call clean_rels() to remove orphaned relationships
95+
96+
2. After modifying many objects or complex operations:
97+
→ Call rebuild_all_rels() to ensure consistency
98+
99+
3. Regular maintenance:
100+
→ Periodically call clean_rels() to keep .rels files tidy
101+
102+
4. When in doubt:
103+
→ Use rebuild_all_rels() to guarantee correct relationships
104+
"""
105+
)
106+
107+
108+
def quick_clean_example(epc_path: str):
109+
"""
110+
Quick example: Just clean the .rels files.
111+
"""
112+
print("\n" + "=" * 70)
113+
print("QUICK EXAMPLE: Clean .rels in one line")
114+
print("=" * 70)
115+
116+
reader = EpcStreamReader(epc_path)
117+
stats = reader.clean_rels()
118+
119+
print(f"\n✓ Cleaned! Removed {stats['relationships_removed']} orphaned relationships")
120+
121+
122+
def quick_rebuild_example(epc_path: str):
123+
"""
124+
Quick example: Rebuild all .rels files.
125+
"""
126+
print("\n" + "=" * 70)
127+
print("QUICK EXAMPLE: Rebuild all .rels in one line")
128+
print("=" * 70)
129+
130+
reader = EpcStreamReader(epc_path)
131+
stats = reader.rebuild_all_rels()
132+
133+
print(
134+
f"\n✓ Rebuilt! Created {stats['rels_files_created']} .rels files with {stats['source_relationships'] + stats['destination_relationships']} relationships"
135+
)
136+
137+
138+
if __name__ == "__main__":
139+
# Use the test EPC file
140+
test_epc = "wip/BRGM_AVRE_all_march_25.epc"
141+
142+
if not Path(test_epc).exists():
143+
print(f"EPC file not found: {test_epc}")
144+
print("Please provide a valid EPC file path")
145+
sys.exit(1)
146+
147+
# Make a temporary copy for the example
148+
import tempfile
149+
import shutil
150+
151+
with tempfile.NamedTemporaryFile(delete=False, suffix=".epc") as tmp:
152+
tmp_path = tmp.name
153+
154+
try:
155+
shutil.copy(test_epc, tmp_path)
156+
157+
# Run the complete workflow
158+
example_workflow(tmp_path)
159+
160+
# Show quick examples
161+
shutil.copy(test_epc, tmp_path)
162+
quick_clean_example(tmp_path)
163+
164+
shutil.copy(test_epc, tmp_path)
165+
quick_rebuild_example(tmp_path)
166+
167+
print("\n" + "=" * 70)
168+
print("Examples completed successfully!")
169+
print("=" * 70)
170+
171+
finally:
172+
# Cleanup
173+
if Path(tmp_path).exists():
174+
Path(tmp_path).unlink()

energyml-utils/example/main.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# Copyright (c) 2023-2024 Geosiris.
22
# SPDX-License-Identifier: Apache-2.0
3-
import json
3+
import sys
4+
from pathlib import Path
45
import re
56
from dataclasses import fields
67

8+
src_path = Path(__file__).parent.parent / "src"
9+
sys.path.insert(0, str(src_path))
10+
711
from energyml.eml.v2_3.commonv2 import *
812
from energyml.eml.v2_3.commonv2 import AbstractObject
913
from energyml.resqml.v2_0_1.resqmlv2 import DoubleHdf5Array
@@ -17,19 +21,19 @@
1721
)
1822

1923
# from src.energyml.utils.data.hdf import *
20-
from src.energyml.utils.data.helper import get_projected_uom, is_z_reversed
21-
from src.energyml.utils.epc import *
22-
from src.energyml.utils.introspection import *
23-
from src.energyml.utils.manager import *
24-
from src.energyml.utils.serialization import *
25-
from src.energyml.utils.validation import (
24+
from energyml.utils.data.helper import get_projected_uom, is_z_reversed
25+
from energyml.utils.epc import *
26+
from energyml.utils.introspection import *
27+
from energyml.utils.manager import *
28+
from energyml.utils.serialization import *
29+
from energyml.utils.validation import (
2630
patterns_validation,
2731
dor_validation,
2832
validate_epc,
2933
correct_dor,
3034
)
31-
from src.energyml.utils.xml import *
32-
from src.energyml.utils.data.datasets_io import HDF5FileReader, get_path_in_external_with_path
35+
from energyml.utils.xml import *
36+
from energyml.utils.data.datasets_io import HDF5FileReader, get_path_in_external_with_path
3337

3438
fi_cit = Citation(
3539
title="An interpretation",
@@ -494,5 +498,22 @@ def test_dor_conversion():
494498
)
495499
# print(get_obj_uri(tr201, "coucou"))
496500

497-
print(get_usable_class(tr))
498-
print(get_usable_class(tr201))
501+
logging.basicConfig(level=logging.DEBUG)
502+
503+
emi = create_energyml_object("resqml20.ObjEarthModelInterpretation")
504+
print(type(emi))
505+
print(serialize_xml(emi))
506+
507+
from energyml.resqml.v2_0_1 import resqmlv2
508+
509+
emi = resqmlv2.ObjEarthModelInterpretation()
510+
print(type(emi))
511+
print(serialize_xml(emi))
512+
513+
emi = read_energyml_xml_file("C:/Users/Cryptaro/Downloads/emi.xml")
514+
print(type(emi))
515+
print(serialize_xml(emi))
516+
517+
emi = create_energyml_object("resqml20.EarthModelInterpretation")
518+
print(type(emi))
519+
print(serialize_xml(emi))

energyml-utils/example/main_hdf.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright (c) 2023-2024 Geosiris.
2+
# SPDX-License-Identifier: Apache-2.0
3+
import sys
4+
from pathlib import Path
5+
6+
# Add src directory to path
7+
src_path = Path(__file__).parent.parent / "src"
8+
sys.path.insert(0, str(src_path))
9+
10+
from energyml.utils.data.datasets_io import get_path_in_external_with_path
11+
from energyml.utils.introspection import get_obj_uri
12+
13+
14+
if __name__ == "__main__":
15+
from energyml.utils.epc import Epc
16+
17+
# Create an EPC file
18+
epc = Epc.read_file("wip/BRGM_AVRE_all_march_25.epc")
19+
20+
print("\n".join(map(lambda o: str(get_obj_uri(o)), epc.energyml_objects)))
21+
22+
print(epc.get_h5_file_paths("eml:///resqml22.PolylineSetRepresentation(e75db94d-a251-4f31-8a24-23b9573fbf39)"))
23+
24+
print(
25+
get_path_in_external_with_path(
26+
epc.get_object_by_identifier(
27+
"eml:///resqml22.PolylineSetRepresentation(e75db94d-a251-4f31-8a24-23b9573fbf39)"
28+
)
29+
)
30+
)
31+
32+
print(
33+
epc.read_h5_dataset(
34+
"eml:///resqml22.PolylineSetRepresentation(e75db94d-a251-4f31-8a24-23b9573fbf39)",
35+
"/RESQML/e75db94d-a251-4f31-8a24-23b9573fbf39/points_patch0",
36+
)
37+
)

0 commit comments

Comments
 (0)