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)
4849from 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