Skip to content

Commit 882b515

Browse files
rels fixed for ExternalPartRef proxy
1 parent 5eb6b40 commit 882b515

7 files changed

Lines changed: 1231 additions & 148 deletions

File tree

energyml-utils/example/main_stream_sample.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,11 +657,18 @@ def arrays_equal(arr1, arr2, name):
657657
logging.info("==> EPC file closed successfully")
658658

659659

660+
def recompute_rels(path: str):
661+
EpcStreamReader(epc_file_path=path, enable_parallel_rels=True, rels_update_mode=RelsUpdateMode.UPDATE_ON_CLOSE)
662+
663+
660664
if __name__ == "__main__":
661665
logging.basicConfig(level=logging.DEBUG)
662666

663667
# main((sys.argv[1] if len(sys.argv) > 1 else None) or "wip/80wells_surf.epc")
664668

665669
# test_create_epc("wip/test_create.epc")
666670
# test_create_epc_v2("wip/test_create.epc")
667-
test_create_epc_v3_with_different_external_files("wip/test_create_v3.epc")
671+
# test_create_epc_v3_with_different_external_files("wip/test_create_v3.epc")
672+
673+
recompute_rels(sys.argv[1] if len(sys.argv) > 1 else "wip/failingData/fix/S-PASS-1-EARTHMODEL_ONLY.epc")
674+
recompute_rels(sys.argv[1] if len(sys.argv) > 1 else "wip/failingData/fix/S-PASS-1-GEOMODEL.epc")

energyml-utils/src/energyml/utils/epc.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,10 @@ def compute_rels(self) -> Dict[str, Relationships]:
347347
obj_id: [
348348
Relationship(
349349
target=gen_energyml_object_path(target_obj, self.export_version),
350-
type_value=EPCRelsRelationshipType.DESTINATION_OBJECT.get_type(),
350+
type_value=get_rels_dor_type(
351+
gen_energyml_object_path(self.get_object(obj_id), self.export_version),
352+
in_dor_owner_rels_file=False,
353+
),
351354
id=f"_{obj_id}_{get_obj_type(get_obj_usable_class(target_obj))}_{get_obj_identifier(target_obj)}",
352355
)
353356
for target_obj in target_obj_list
@@ -364,7 +367,9 @@ def compute_rels(self) -> Dict[str, Relationships]:
364367
rels[obj_id].append(
365368
Relationship(
366369
target=gen_energyml_object_path(target_obj, self.export_version),
367-
type_value=EPCRelsRelationshipType.SOURCE_OBJECT.get_type(),
370+
type_value=get_rels_dor_type(
371+
gen_energyml_object_path(target_obj, self.export_version), in_dor_owner_rels_file=True
372+
),
368373
id=f"_{obj_id}_{get_obj_type(get_obj_usable_class(target_obj))}_{get_obj_identifier(target_obj)}",
369374
)
370375
)
@@ -943,6 +948,7 @@ def close(self) -> None:
943948
# Backward compatibility: re-export functions that were moved to epc_utils
944949
# This allows existing code that imports these functions from epc.py to continue working
945950
from .epc_utils import (
951+
get_rels_dor_type,
946952
update_prop_kind_dict_cache,
947953
get_property_kind_by_uuid,
948954
get_property_kind_and_parents,

energyml-utils/src/energyml/utils/epc_stream.py

Lines changed: 67 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
create_mandatory_structure_epc,
4444
extract_uuid_and_version_from_obj_path,
4545
gen_rels_path_from_obj_path,
46+
get_rels_dor_type,
4647
repair_epc_structure_if_not_valid,
4748
)
4849
from energyml.utils.storage_interface import (
@@ -535,7 +536,8 @@ def gen_rels_path_from_identifier(self, identifier: str) -> Optional[str]:
535536
return None
536537
return self.gen_rels_path_from_metadata(metadata)
537538

538-
def get_core_properties(self) -> Optional[CoreProperties]:
539+
@property
540+
def core_properties(self) -> Optional[CoreProperties]:
539541
"""Get core properties (loaded lazily)."""
540542
if self._core_props is None and self._core_props_path:
541543
try:
@@ -549,6 +551,56 @@ def get_core_properties(self) -> Optional[CoreProperties]:
549551

550552
return self._core_props
551553

554+
@core_properties.setter
555+
def core_properties(self, core_props: CoreProperties) -> None:
556+
"""Set core properties (updates immediately in the EPC zip file)."""
557+
self._core_props = core_props
558+
self._write_core_properties_to_zip(core_props)
559+
560+
def _write_core_properties_to_zip(self, core_props: CoreProperties) -> None:
561+
"""Write core properties to the EPC zip file, replacing existing ones."""
562+
core_props_path = gen_core_props_path()
563+
temp_path = None
564+
565+
try:
566+
# Create a temporary file for the new zip
567+
with tempfile.NamedTemporaryFile(delete=False, suffix=".epc") as temp_file:
568+
temp_path = temp_file.name
569+
570+
# Create new zip and copy all content except old core properties
571+
with zipfile.ZipFile(temp_path, "w", zipfile.ZIP_DEFLATED) as target_zf:
572+
with self.zip_accessor.get_zip_file() as source_zf:
573+
# Copy all files except the core properties
574+
for item in source_zf.infolist():
575+
if item.filename != core_props_path:
576+
data = source_zf.read(item.filename)
577+
target_zf.writestr(item, data)
578+
579+
# Write new core properties
580+
core_props_xml = serialize_xml(core_props)
581+
zip_info = zipfile.ZipInfo(
582+
filename=core_props_path,
583+
date_time=datetime.now().timetuple()[:6],
584+
)
585+
target_zf.writestr(zip_info, core_props_xml)
586+
587+
# Replace the original file
588+
shutil.move(temp_path, self.zip_accessor.epc_file_path)
589+
590+
# Reopen the zip file to reflect changes
591+
self.zip_accessor.reopen_persistent_zip()
592+
593+
logging.info(f"Successfully updated core properties in {self.zip_accessor.epc_file_path}")
594+
595+
except Exception as e:
596+
# Clean up temp file if it exists
597+
if temp_path and os.path.exists(temp_path):
598+
try:
599+
os.remove(temp_path)
600+
except Exception:
601+
pass
602+
raise IOError(f"Failed to write core properties to EPC: {e}")
603+
552604
def detect_epc_version(self) -> EpcExportVersion:
553605
"""Detect EPC packaging version based on file structure."""
554606
try:
@@ -580,37 +632,6 @@ def detect_epc_version(self) -> EpcExportVersion:
580632
logging.warning(f"Failed to detect EPC version, defaulting to CLASSIC: {e}")
581633
return EpcExportVersion.CLASSIC
582634

583-
# def update_content_types_xml(
584-
# self, source_zip: zipfile.ZipFile, metadata: EpcObjectMetadata, add: bool = True
585-
# ) -> str:
586-
# """Update [Content_Types].xml to add or remove object entry.
587-
588-
# Args:
589-
# source_zip: Open ZIP file to read from
590-
# metadata: Object metadata
591-
# add: If True, add entry; if False, remove entry
592-
593-
# Returns:
594-
# Updated [Content_Types].xml as string
595-
# """
596-
# # Read existing content types
597-
# content_types = self._read_content_types(source_zip)
598-
599-
# if add:
600-
# # Add new override entry
601-
# new_override = Override()
602-
# new_override.part_name = f"/{metadata.file_path}"
603-
# new_override.content_type = metadata.content_type
604-
# content_types.override.append(new_override)
605-
# else:
606-
# # Remove existing override entry
607-
# content_types.override = [
608-
# o for o in content_types.override if o.part_name and o.part_name.lstrip("/") != metadata.file_path
609-
# ]
610-
611-
# # Serialize back to XML
612-
# return serialize_xml(content_types)
613-
614635
def get_content_type(self, zf: zipfile.ZipFile) -> Types:
615636

616637
meta_dict_key_path = {
@@ -822,14 +843,14 @@ def update_rels_for_new_object(self, obj: Any, obj_identifier: str) -> None:
822843

823844
dest_rel = Relationship(
824845
target=target_path,
825-
type_value=str(EPCRelsRelationshipType.DESTINATION_OBJECT),
846+
type_value=get_rels_dor_type(dor_target=target_path, in_dor_owner_rels_file=True),
826847
id=f"_{gen_uuid()}",
827848
)
828849
dest_rels.append(dest_rel)
829850

830851
source_relationships[target_path] = Relationship(
831852
target=obj_file_path,
832-
type_value=str(EPCRelsRelationshipType.SOURCE_OBJECT),
853+
type_value=get_rels_dor_type(dor_target=target_path, in_dor_owner_rels_file=False),
833854
id=f"_{gen_uuid()}",
834855
)
835856

@@ -869,7 +890,7 @@ def update_rels_for_modified_object(self, obj: Any, obj_identifier: str) -> None
869890
# DESTINATION relationship : current is referenced by
870891
dest_rel = Relationship(
871892
target=target_path,
872-
type_value=str(EPCRelsRelationshipType.DESTINATION_OBJECT),
893+
type_value=get_rels_dor_type(dor_target=target_path, in_dor_owner_rels_file=True),
873894
id=f"_{gen_uuid()}",
874895
)
875896
current_rels_additions.append(dest_rel)
@@ -878,7 +899,7 @@ def update_rels_for_modified_object(self, obj: Any, obj_identifier: str) -> None
878899
# REVERSED SOURCE relationship : target references current, if not already existing (to avoid duplicates if DORs are not changed for this target)
879900
source_rel = Relationship(
880901
target=obj_path,
881-
type_value=str(EPCRelsRelationshipType.SOURCE_OBJECT),
902+
type_value=get_rels_dor_type(dor_target=target_path, in_dor_owner_rels_file=False),
882903
id=f"_{gen_uuid()}",
883904
)
884905
reversed_source_relationships[target_path] = source_rel
@@ -2136,9 +2157,6 @@ def _rebuild_all_rels_sequential(self, clean_first: bool = True) -> Dict[str, in
21362157
- 'source_relationships': Number of SOURCE relationships created
21372158
- 'destination_relationships': Number of DESTINATION relationships created
21382159
"""
2139-
import tempfile
2140-
import shutil
2141-
21422160
stats = {
21432161
"objects_processed": 0,
21442162
"rels_files_created": 0,
@@ -2207,10 +2225,12 @@ def _rebuild_all_rels_sequential(self, clean_first: bool = True) -> Dict[str, in
22072225
if target_identifier in self._metadata:
22082226
target_metadata = self._metadata[target_identifier]
22092227
target_type = get_obj_type(get_obj_usable_class(dor))
2210-
2228+
target_path = target_metadata.file_path(
2229+
export_version=self._metadata_mgr._export_version
2230+
)
22112231
rel = Relationship(
2212-
target=target_metadata.file_path(export_version=self._metadata_mgr._export_version),
2213-
type_value=EPCRelsRelationshipType.DESTINATION_OBJECT.get_type(),
2232+
target=target_path,
2233+
type_value=get_rels_dor_type(dor_target=target_path, in_dor_owner_rels_file=True),
22142234
id=f"_{identifier}_{target_type}_{target_identifier}",
22152235
)
22162236
relationships.append(rel)
@@ -2246,7 +2266,7 @@ def _rebuild_all_rels_sequential(self, clean_first: bool = True) -> Dict[str, in
22462266

22472267
rel = Relationship(
22482268
target=source_metadata.file_path(export_version=self._metadata_mgr._export_version),
2249-
type_value=EPCRelsRelationshipType.SOURCE_OBJECT.get_type(),
2269+
type_value=get_rels_dor_type(dor_target=target_rels_path, in_dor_owner_rels_file=False),
22502270
id=f"_{target_identifier}_{source_type}_{source_identifier}",
22512271
)
22522272

@@ -2413,11 +2433,12 @@ def _rebuild_all_rels_parallel(self, clean_first: bool = True) -> Dict[str, int]
24132433
continue
24142434

24152435
target_metadata = self._metadata[target_identifier]
2436+
target_path = target_metadata.file_path(export_version=export_version)
24162437

24172438
# Create DESTINATION relationship (this object -> target)
24182439
rel = Relationship(
2419-
target=target_metadata.file_path(export_version=export_version),
2420-
type_value=EPCRelsRelationshipType.DESTINATION_OBJECT.get_type(),
2440+
target=target_path,
2441+
type_value=get_rels_dor_type(dor_target=target_path, in_dor_owner_rels_file=True),
24212442
id=f"_{identifier}_{target_type}_{target_identifier}",
24222443
)
24232444
rels_files[obj_rels_path].relationship.append(rel)
@@ -2448,7 +2469,7 @@ def _rebuild_all_rels_parallel(self, clean_first: bool = True) -> Dict[str, int]
24482469
# Create SOURCE relationship (source object -> this target object)
24492470
rel = Relationship(
24502471
target=source_metadata.file_path(export_version=export_version),
2451-
type_value=EPCRelsRelationshipType.SOURCE_OBJECT.get_type(),
2472+
type_value=get_rels_dor_type(dor_target=target_rels_path, in_dor_owner_rels_file=False),
24522473
id=f"_{target_identifier}_{source_type}_{source_identifier}",
24532474
)
24542475

0 commit comments

Comments
 (0)