55import json
66import os
77import re
8+ import sys
89import uuid
910from datetime import datetime
1011from pathlib import Path
@@ -304,6 +305,14 @@ def xml_upload(input_file: str, server: str, user: str, password: str, imgdir: s
304305 f"{ err .message } " )
305306 quit (0 )
306307
308+ if sys .platform .startswith ("darwin" ) or sys .platform .startswith ("linux" ):
309+ save_location = f"{ os .path .expanduser ('~' )} /.dsp-tools"
310+ elif sys .platform .startswith ("win" ):
311+ save_location = "."
312+ else :
313+ save_location = "."
314+ # TODO: use the home directory provided by Pathlib
315+
307316 # Connect to the DaSCH Service Platform API and get the project context
308317 con = Connection (server )
309318 try_network_action (failure_msg = "Unable to login to DSP server" , action = lambda : con .login (user , password ))
@@ -351,42 +360,68 @@ def xml_upload(input_file: str, server: str, user: str, password: str, imgdir: s
351360 id2iri_mapping , failed_uploads = _upload_resources (resources , imgdir , sipi_server , permissions_lookup ,
352361 resclass_name_2_type , id2iri_mapping , con , failed_uploads )
353362 except BaseException as err :
354- _handle_upload_error (err , input_file , id2iri_mapping , failed_uploads , stashed_xml_texts , stashed_resptr_props )
363+ _handle_upload_error (
364+ err = err ,
365+ id2iri_mapping = id2iri_mapping ,
366+ failed_uploads = failed_uploads ,
367+ stashed_xml_texts = stashed_xml_texts ,
368+ stashed_resptr_props = stashed_resptr_props ,
369+ proj_shortcode = shortcode ,
370+ onto_name = default_ontology ,
371+ server = server ,
372+ save_location = save_location
373+ )
355374
356375 # update the resources with the stashed XML texts
357- nonapplied_xml_texts = {}
358376 if len (stashed_xml_texts ) > 0 :
359377 try :
360378 nonapplied_xml_texts = _upload_stashed_xml_texts (verbose , id2iri_mapping , con , stashed_xml_texts )
361- nonapplied_xml_texts = _purge_stashed_xml_texts (nonapplied_xml_texts , id2iri_mapping )
379+ if len (nonapplied_xml_texts ) > 0 :
380+ raise BaseError (f"Error while trying to upload the stashed xml texts" )
362381 except BaseException as err :
363- _handle_upload_error (err , input_file , id2iri_mapping , failed_uploads , stashed_xml_texts , stashed_resptr_props )
382+ _handle_upload_error (
383+ err = err ,
384+ id2iri_mapping = id2iri_mapping ,
385+ failed_uploads = failed_uploads ,
386+ stashed_xml_texts = stashed_xml_texts ,
387+ stashed_resptr_props = stashed_resptr_props ,
388+ proj_shortcode = shortcode ,
389+ onto_name = default_ontology ,
390+ server = server ,
391+ save_location = save_location
392+ )
364393
365394 # update the resources with the stashed resptrs
366- nonapplied_resptr_props = {}
367395 if len (stashed_resptr_props ) > 0 :
368396 try :
369397 nonapplied_resptr_props = _upload_stashed_resptr_props (verbose , id2iri_mapping , con , stashed_resptr_props )
370- nonapplied_resptr_props = _purge_stashed_resptr_props (stashed_resptr_props , id2iri_mapping )
398+ if len (nonapplied_resptr_props ) > 0 :
399+ raise BaseError (f"Error while trying to upload the stashed resptr props" )
371400 except BaseException as err :
372- _handle_upload_error (err , input_file , id2iri_mapping , failed_uploads , stashed_xml_texts , stashed_resptr_props )
401+ _handle_upload_error (
402+ err = err ,
403+ id2iri_mapping = id2iri_mapping ,
404+ failed_uploads = failed_uploads ,
405+ stashed_xml_texts = stashed_xml_texts ,
406+ stashed_resptr_props = stashed_resptr_props ,
407+ proj_shortcode = shortcode ,
408+ onto_name = default_ontology ,
409+ server = server ,
410+ save_location = save_location
411+ )
373412
374413 # write log files
375414 success = True
376- timestamp_str = datetime .now ().strftime ("%Y%m%d-%H%M%S" )
377- if len (nonapplied_xml_texts ) > 0 :
378- _write_stashed_xml_texts (nonapplied_xml_texts , timestamp_str )
379- success = False
380- if len (nonapplied_resptr_props ) > 0 :
381- _write_stashed_resptr_props (nonapplied_resptr_props , timestamp_str )
382- success = False
415+ timestamp_str = datetime .now ().strftime ("%Y-%m-%d_%H%M%S" )
416+ id2iri_mapping_file = f"id2iri_{ Path (input_file ).stem } _mapping_{ timestamp_str } .json"
417+ with open (id2iri_mapping_file , "x" ) as f :
418+ json .dump (id2iri_mapping , f , ensure_ascii = False , indent = 4 )
419+ print (f"The mapping of internal IDs to IRIs was written to { id2iri_mapping_file } " )
383420 if failed_uploads :
384421 print (f"\n WARNING: Could not upload the following resources: { failed_uploads } \n " )
385422 success = False
386423 if success :
387424 print ("All resources have successfully been uploaded." )
388- if len (id2iri_mapping ) > 0 :
389- _write_id2iri_mapping (input_file , id2iri_mapping , timestamp_str )
390425
391426 return success
392427
@@ -706,11 +741,14 @@ def _purge_stashed_resptr_props(
706741
707742def _handle_upload_error (
708743 err : BaseException ,
709- input_file : str ,
710744 id2iri_mapping : dict [str , str ],
711745 failed_uploads : list [str ],
712746 stashed_xml_texts : dict [XMLResource , dict [XMLProperty , dict [str , KnoraStandoffXml ]]],
713- stashed_resptr_props : dict [XMLResource , dict [XMLProperty , list [str ]]]
747+ stashed_resptr_props : dict [XMLResource , dict [XMLProperty , list [str ]]],
748+ proj_shortcode : str ,
749+ onto_name : str ,
750+ server : str ,
751+ save_location : str
714752) -> None :
715753 """
716754 In case the xmlupload must be interrupted, e.g. because of an error that could not be handled, or due to keyboard
@@ -720,33 +758,64 @@ def _handle_upload_error(
720758
721759 Args:
722760 err: error that was the cause of the abort
723- input_file: file name of the original XML file
724761 id2iri_mapping: mapping of ids from the XML file to IRIs in DSP (only successful uploads appear here)
725762 failed_uploads: resources that caused an error when uploading to DSP
726763 stashed_xml_texts: all xml texts that have been stashed
727764 stashed_resptr_props: all resptr props that have been stashed
765+ proj_shortcode: shortcode of the project the data belongs to
766+ onto_name: name of the ontology the data references
767+ server: the server which the data is uploaded onto
768+ save_location: path to the directory where dsp-tools should save logs (OS dependent)
728769
729770 Returns:
730771 None
731772 """
732773
733774 print (f'\n =========================================='
734775 f'\n xmlupload must be aborted because of an error' )
735- timestamp_str = datetime .now ().strftime ("%Y%m%d-%H%M%S" )
776+ timestamp_str = datetime .now ().strftime ("%Y-%m-%d_%H%M%S" )
777+
778+ server_substitutions = {
779+ r"https?://" : "" ,
780+ r"^api\..+" : "" ,
781+ r":\d{4}/?$" : "" ,
782+ r"0.0.0.0" : "localhost"
783+ }
784+ for pattern , repl in server_substitutions .items ():
785+ server = re .sub (pattern , repl , server )
786+
787+ if save_location == "." :
788+ save_location_full = f"xmluploads/{ server } /{ proj_shortcode } /{ onto_name } "
789+ else :
790+ save_location_full = f"{ save_location } /xmluploads/{ server } /{ proj_shortcode } /{ onto_name } "
791+ os .makedirs (save_location_full , exist_ok = True )
736792
737- # write id2iri_mapping of the resources that are already in DSP
738- if len ( id2iri_mapping ) > 0 :
739- _write_id2iri_mapping ( input_file , id2iri_mapping , timestamp_str )
793+ # only stashed properties of resources that already exist in DSP are of interest
794+ stashed_xml_texts = _purge_stashed_xml_texts ( stashed_xml_texts , id2iri_mapping )
795+ stashed_resptr_props = _purge_stashed_resptr_props ( stashed_resptr_props , id2iri_mapping )
740796
741- # Both stashes are purged from resources that have not been uploaded yet. Only stashed properties of resources that
742- # already exist in DSP are of interest.
743- stashed_xml_texts_purged = _purge_stashed_xml_texts ( stashed_xml_texts , id2iri_mapping )
744- if len ( stashed_xml_texts_purged ) > 0 :
745- _write_stashed_xml_texts ( stashed_xml_texts_purged , timestamp_str )
797+ if id2iri_mapping :
798+ id2iri_mapping_file = f" { save_location_full } / { timestamp_str } _id2iri_mapping.json"
799+ with open ( id2iri_mapping_file , "x" ) as f :
800+ json . dump ( id2iri_mapping , f , ensure_ascii = False , indent = 4 )
801+ print ( f"The mapping of internal IDs to IRIs was written to { id2iri_mapping_file } " )
746802
747- stashed_resptr_props_purged = _purge_stashed_resptr_props (stashed_resptr_props , id2iri_mapping )
748- if len (stashed_resptr_props_purged ) > 0 :
749- _write_stashed_resptr_props (stashed_resptr_props_purged , timestamp_str )
803+ if stashed_xml_texts :
804+ stashed_xml_texts_serializable = {r .id : {p .name : xml for p , xml in rdict .items ()} for r , rdict in stashed_xml_texts .items ()}
805+ xml_filename = f"{ save_location_full } /{ timestamp_str } _stashed_text_properties.json"
806+ with open (xml_filename , "x" ) as f :
807+ json .dump (stashed_xml_texts_serializable , f , ensure_ascii = False , indent = 4 , cls = KnoraStandoffXmlEncoder )
808+ print (f"There are stashed text properties that could not be reapplied to the resources they were stripped "
809+ f"from. They were saved to { xml_filename } ." )
810+
811+ if stashed_resptr_props :
812+ stashed_resptr_props_serializable = {r .id : {p .name : plist for p , plist in rdict .items ()} for r , rdict in stashed_resptr_props .items ()}
813+ resptr_filename = f"{ save_location_full } /{ timestamp_str } _stashed_resptr_properties.json"
814+ with open (resptr_filename , "x" ) as f :
815+ json .dump (stashed_resptr_props_serializable , f , ensure_ascii = False , indent = 4 )
816+ print (
817+ f"There are stashed resptr properties that could not be reapplied to the resources they were stripped "
818+ f"from. They were saved to { resptr_filename } " )
750819
751820 # print the resources that threw an error when they were tried to be uploaded
752821 if failed_uploads :
@@ -759,101 +828,3 @@ def _handle_upload_error(
759828 print ('The error will now be raised again:\n '
760829 '==========================================\n ' )
761830 raise err
762-
763-
764- def _write_id2iri_mapping (input_file : str , id2iri_mapping : dict [str , str ], timestamp_str : str ) -> None :
765- """
766- Write the id2iri mapping into a file. The timestamp must be created by the caller, so that different log files can
767- have an identical timestamp.
768-
769- Args:
770- input_file: the file name of the original XML file
771- id2iri_mapping: mapping of ids from the XML file to IRIs in DSP
772- timestamp_str: timestamp for log file identification
773-
774- Returns:
775- None
776- """
777-
778- id2iri_mapping_file = "id2iri_" + Path (input_file ).stem + "_mapping_" + timestamp_str + ".json"
779- with open (id2iri_mapping_file , "w" ) as outfile :
780- print (f"The mapping of internal IDs to IRIs was written to { id2iri_mapping_file } " )
781- outfile .write (json .dumps (id2iri_mapping ))
782-
783-
784- def _write_stashed_xml_texts (
785- stashed_xml_texts : dict [XMLResource , dict [XMLProperty , dict [str , KnoraStandoffXml ]]],
786- timestamp_str : str
787- ) -> None :
788- """
789- Write the stashed_xml_texts into a file. The timestamp must be created by the caller, so that different log files
790- can have an identical timestamp.
791-
792- Args:
793- stashed_xml_texts: all xml texts that have been stashed
794- timestamp_str: timestamp for log file identification
795-
796- Returns:
797- None
798- """
799-
800- filename = f'stashed_text_properties_{ timestamp_str } .txt'
801- print (f'There are stashed text properties that could not be reapplied to the resources they were stripped from. '
802- f'They were saved to { filename } ' )
803- with open (filename , 'a' ) as f :
804- f .write ('Stashed text properties that could not be reapplied\n ' )
805- f .write ('***************************************************\n ' )
806- f .write ('During the xmlupload, some text properties had to be stashed away, because the salsah-links in \n '
807- 'their XML text formed a circle. The xmlupload can only be done if these circles are broken up, by \n '
808- 'stashing away some chain elements of the circle. \n '
809- 'Some resources that have been stripped from some of their text properties have been created in DSP, \n '
810- 'but the stashed text properties could not be reapplied to them, because the xmlupload was \n '
811- 'unexpectedly interrupted. \n '
812- 'This file is a list of all text properties that are now missing in DSP. The texts have been \n '
813- 'replaced by a hash number that now stands in the text field in DSP. \n '
814- '(Not listed are the stripped resources that haven\' t been created in DSP yet.) \n ' )
815- for res , props in stashed_xml_texts .items ():
816- f .write (f'\n { res .id } ' )
817- f .write ('\n ' + '=' * len (res .id ))
818- for prop , stashed_texts in props .items ():
819- if len (stashed_texts ) > 0 :
820- f .write (f'\n { prop .name } ' )
821- f .write ('\n ' + '-' * len (prop .name ))
822- for hash , standoff in stashed_texts .items ():
823- f .write (f'\n text with hash { hash } :\n { str (standoff ).strip ()} \n ' )
824-
825-
826- def _write_stashed_resptr_props (
827- stashed_resptr_props : dict [XMLResource , dict [XMLProperty , list [str ]]],
828- timestamp_str : str
829- ) -> None :
830- """
831- Write the stashed_resptr_props into a file. The timestamp must be created by the caller, so that different log files
832- can have an identical timestamp.
833-
834- Args:
835- stashed_resptr_props: all resptr props that have been stashed
836- timestamp_str: timestamp for log file identification
837-
838- Returns:
839- None
840- """
841-
842- filename = f'stashed_resptr_properties_{ timestamp_str } .txt'
843- print (f'There are stashed resptr properties that could not be reapplied to the resources they were stripped from. '
844- f'They were saved to { filename } ' )
845- with open (filename , 'a' ) as f :
846- f .write ('Stashed resptr properties that could not be reapplied\n ' )
847- f .write ('*****************************************************\n ' )
848- f .write ('During the xmlupload, some resptr-props had to be stashed away, because they formed a circle. The \n '
849- 'xmlupload can only be done if these circles are broken up, by stashing away some chain elements of \n '
850- 'the circle. \n '
851- 'Some resources that have been stripped from some of their resptr-props have been created in DSP, \n '
852- 'but the stashed resptr-props could not be reapplied to them, because the xmlupload was unexpectedly \n '
853- 'interrupted. \n '
854- 'This file is a list of all resptr-props that are now missing in DSP. (Not listed are the stripped \n '
855- 'resources that haven\' t been created in DSP yet. \n ' )
856- for res , props_ in stashed_resptr_props .items ():
857- f .write (f'\n { res .id } \n ---------\n ' )
858- for prop , stashed_props in props_ .items ():
859- f .write (f'{ prop .name } \n \t { stashed_props } \n ' )
0 commit comments