-
Notifications
You must be signed in to change notification settings - Fork 106
feat: Copy/archive input XML files into the output directory #4030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 31 commits
20b501c
3db8b1a
8afead3
25c325b
1cf33d5
a4b35d0
c873915
6efbe14
140aa51
9ed7c03
5c30635
0c5fb2d
bf1ca66
6b35d1b
a61d8c7
8738084
93e2806
dbfa0fb
5a7c196
d745bf6
f8acdb4
c97394d
aa5a851
7041f73
135e3a9
377ebdf
d498048
6e229b8
c388276
81084b8
4eb17c7
2413b78
8ca1a6c
83e59d2
5db3b4d
02a276d
e203ced
6ac9d27
6d1a0ee
b05f6cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,252 @@ | ||||||
| /* | ||||||
| * ------------------------------------------------------------------------------------------------------------ | ||||||
| * SPDX-License-Identifier: LGPL-2.1-only | ||||||
| * | ||||||
| * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC | ||||||
| * Copyright (c) 2018-2024 TotalEnergies | ||||||
| * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University | ||||||
| * Copyright (c) 2023-2024 Chevron | ||||||
| * Copyright (c) 2019- GEOS/GEOSX Contributors | ||||||
| * All rights reserved | ||||||
| * | ||||||
| * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. | ||||||
| * ------------------------------------------------------------------------------------------------------------ | ||||||
| */ | ||||||
|
|
||||||
| /** | ||||||
| * @file ArchiveInputDeck.cpp | ||||||
| */ | ||||||
|
|
||||||
| #include "ArchiveInputDeck.hpp" | ||||||
|
|
||||||
| #include "common/GeosxConfig.hpp" | ||||||
| #include "common/Path.hpp" | ||||||
| #include "common/format/Format.hpp" | ||||||
| #include "common/logger/Logger.hpp" | ||||||
| #include "dataRepository/xmlWrapper.hpp" | ||||||
|
|
||||||
| #include <algorithm> | ||||||
| #include <chrono> | ||||||
| #include <filesystem> | ||||||
| #include <system_error> | ||||||
|
|
||||||
| namespace geos | ||||||
| { | ||||||
|
|
||||||
| using namespace dataRepository; | ||||||
|
|
||||||
| namespace archiveInputDeck | ||||||
| { | ||||||
|
|
||||||
| namespace | ||||||
| { | ||||||
|
|
||||||
| string makeTimestamp() | ||||||
| { | ||||||
| auto const now = std::chrono::system_clock::now(); | ||||||
| auto const time_t_now = std::chrono::system_clock::to_time_t( now ); | ||||||
| std::ostringstream timestampStream; | ||||||
| timestampStream << std::put_time( std::localtime( &time_t_now ), "%Y%m%d_%H%M%S" ); | ||||||
| return timestampStream.str(); | ||||||
| } | ||||||
|
|
||||||
| void stripMetadataAttributes( xmlWrapper::xmlNode node ) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure that you don't break something here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| { | ||||||
| node.remove_attribute( xmlWrapper::filePathString ); | ||||||
| node.remove_attribute( xmlWrapper::charOffsetString ); | ||||||
|
|
||||||
| for( xmlWrapper::xmlNode child : node.children() ) | ||||||
| { | ||||||
| stripMetadataAttributes( child ); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| void reorderTags( xmlWrapper::xmlNode rootNode, string_array const & tagOrder ) | ||||||
| { | ||||||
| xmlWrapper::xmlNode lastInserted; | ||||||
| for( string const & tagName : tagOrder ) | ||||||
| { | ||||||
| xmlWrapper::xmlNode tag = rootNode.child( tagName.c_str() ); | ||||||
| if( !tag ) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| lastInserted ? rootNode.insert_move_after( tag, lastInserted ) | ||||||
| : rootNode.append_move( tag ); | ||||||
|
|
||||||
| lastInserted = tag; | ||||||
| } | ||||||
|
|
||||||
| // ProblemManager's order list doesn't provide every XML tags available in GEOS | ||||||
| // so we put the missing ones below the ones it provides. | ||||||
| // And sort them alphabetically | ||||||
| stdVector< string > missingTags; | ||||||
|
|
||||||
| for( xmlWrapper::xmlNode const & tag : rootNode.children() ) | ||||||
| { | ||||||
| string const & tagName = tag.name(); | ||||||
|
|
||||||
| if( std::find( tagOrder.begin(), tagOrder.end(), tag.name() ) == tagOrder.end() ) | ||||||
| { | ||||||
| missingTags.push_back( tagName ); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| std::sort( missingTags.begin(), missingTags.end() ); | ||||||
|
|
||||||
| for( string const & tagName : missingTags ) | ||||||
| { | ||||||
| xmlWrapper::xmlNode tag = rootNode.child( tagName.c_str() ); | ||||||
|
|
||||||
| if( tag ) | ||||||
| { | ||||||
| rootNode.append_move( tag ); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| void sortAttributes( xmlWrapper::xmlNode node ) | ||||||
| { | ||||||
| stdVector< std::pair< string, string > > attributes; | ||||||
| for( xmlWrapper::xmlAttribute attr = node.first_attribute(); | ||||||
| attr; | ||||||
| attr = attr.next_attribute() ) | ||||||
| { | ||||||
| attributes.emplace_back( attr.name(), attr.value() ); | ||||||
| } | ||||||
|
|
||||||
| std::sort( attributes.begin(), | ||||||
| attributes.end(), | ||||||
| []( std::pair< string, string > const & a, | ||||||
| std::pair< string, string > const & b ) | ||||||
| { | ||||||
| // name attribute should be the first attribute, and not sorted alphabetically | ||||||
| bool const aIsName = ( a.first == "name" ); | ||||||
| bool const bIsName = ( b.first == "name" ); | ||||||
| if( aIsName != bIsName ) | ||||||
| { | ||||||
| return aIsName; | ||||||
| } | ||||||
|
|
||||||
| // other attributes are sorted alphabetically | ||||||
| return a.first < b.first; | ||||||
| } ); | ||||||
|
|
||||||
| // pugi doesn't have any move_attribute method yet, so we have to | ||||||
| // copy and remove attributes | ||||||
| while( node.remove_attribute( node.first_attribute() ) ) | ||||||
| {} | ||||||
| for( auto const & attr : attributes ) | ||||||
| { | ||||||
| node.append_attribute( attr.first.c_str() ).set_value( attr.second.c_str() ); | ||||||
| } | ||||||
|
|
||||||
| for( xmlWrapper::xmlNode child : node.children() ) | ||||||
| { | ||||||
| sortAttributes( child ); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| xmlWrapper::xmlDocument flattenXMLs( string_array const & fileNames ) | ||||||
| { | ||||||
| xmlWrapper::xmlDocument flatDoc; | ||||||
| xmlWrapper::xmlNode root = flatDoc.appendChild( "Problem" ); | ||||||
|
|
||||||
| for( string const & fileName : fileNames ) | ||||||
| { | ||||||
| xmlWrapper::xmlDocument doc; | ||||||
| xmlWrapper::xmlResult const result = doc.loadFile( fileName, true ); | ||||||
| GEOS_THROW_IF( !result, | ||||||
| GEOS_FMT( "Could not load XML file '{}': {}", fileName, result.description() ), | ||||||
| InputError ); | ||||||
| xmlWrapper::xmlNode docRoot = doc.getFirstChild(); | ||||||
|
|
||||||
| doc.addIncludedXML( docRoot ); | ||||||
|
|
||||||
| for( xmlWrapper::xmlNode & node : docRoot.children() ) | ||||||
| { | ||||||
| root.append_copy( node ); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return flatDoc; | ||||||
| } | ||||||
|
|
||||||
| void copySchemaToArchive( string const & archiveDir ) | ||||||
| { | ||||||
| std::filesystem::path const candidates[] = { | ||||||
| GEOS_SCHEMA_SOURCE_PATH, | ||||||
| GEOS_SCHEMA_INSTALL_PATH | ||||||
| }; | ||||||
|
|
||||||
| std::error_code ec; | ||||||
| for( std::filesystem::path const & source : candidates ) | ||||||
| { | ||||||
| if( source.empty() || !std::filesystem::is_regular_file( source, ec ) ) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| std::filesystem::path const destination = std::filesystem::path( archiveDir ) / "schema.xsd"; | ||||||
| std::filesystem::copy_file( source, | ||||||
| destination, | ||||||
| ec ); | ||||||
|
|
||||||
| if( ec ) | ||||||
| { | ||||||
| GEOS_WARNING( GEOS_FMT( "Failed to copy XSD schema to archive '{}': {}", | ||||||
| destination.string(), ec.message() ) ); | ||||||
| return; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you want to |
||||||
| } | ||||||
|
|
||||||
| GEOS_LOG_RANK_0( GEOS_FMT( "Archived XSD schema: {}", | ||||||
| getAbsolutePath( destination.string() ) ) ); | ||||||
|
|
||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| GEOS_WARNING( "Could not locate the XSD schema for archiving" ); | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| } | ||||||
|
|
||||||
|
|
||||||
| void archiveInputDeck( string_array const & inputFileNames, | ||||||
| string const & outputDirectory, | ||||||
| string_array const & xmlTagOrder, | ||||||
| integer const level ) | ||||||
| { | ||||||
| if( level == 0 || inputFileNames.empty() || outputDirectory.empty() ) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? |
||||||
| { | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| string const timestamp = makeTimestamp(); | ||||||
| string const archiveDir = joinPath( outputDirectory, "archive_inputFiles", timestamp ); | ||||||
| makeDirsForPath( archiveDir + "/" ); | ||||||
|
|
||||||
| xmlWrapper::xmlDocument flatDoc = flattenXMLs( inputFileNames ); | ||||||
| xmlWrapper::xmlNode root = flatDoc.getFirstChild(); | ||||||
|
|
||||||
| stripMetadataAttributes( root ); | ||||||
| reorderTags( root, xmlTagOrder ); | ||||||
| sortAttributes( root ); | ||||||
|
|
||||||
| string const inputArchiveFile = joinPath( archiveDir, "input.xml" ); | ||||||
| flatDoc.saveFile( inputArchiveFile ); | ||||||
|
|
||||||
| GEOS_LOG_RANK_0( GEOS_FMT( "Archived XML inputs: {}", | ||||||
| getAbsolutePath( inputArchiveFile ) ) ); | ||||||
|
|
||||||
| if( level >= 2 ) | ||||||
| { | ||||||
| copySchemaToArchive( archiveDir ); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| } /* namespace archiveInputDeck */ | ||||||
|
|
||||||
| } /* namespace geos */ | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /* | ||
| * ------------------------------------------------------------------------------------------------------------ | ||
| * SPDX-License-Identifier: LGPL-2.1-only | ||
| * | ||
| * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC | ||
| * Copyright (c) 2018-2024 TotalEnergies | ||
| * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University | ||
| * Copyright (c) 2023-2024 Chevron | ||
| * Copyright (c) 2019- GEOS/GEOSX Contributors | ||
| * All rights reserved | ||
| * | ||
| * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. | ||
| * ------------------------------------------------------------------------------------------------------------ | ||
| */ | ||
|
|
||
| /** | ||
| * @file ArchiveInputDeck.hpp | ||
| */ | ||
|
|
||
| #ifndef GEOS_FILEIO_OUTPUTS_ARCHIVEINPUTDECK_HPP_ | ||
| #define GEOS_FILEIO_OUTPUTS_ARCHIVEINPUTDECK_HPP_ | ||
|
|
||
| #include "common/DataTypes.hpp" | ||
|
|
||
| namespace geos | ||
| { | ||
|
|
||
| namespace archiveInputDeck | ||
| { | ||
|
|
||
| /** | ||
| * @brief Archive the XML input deck (and optionally the XSD schema) into the | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add a note that it should not be called on multiple ranks |
||
| * output directory. | ||
| * @param inputFileNames Container of XML file names to start the copy from | ||
| * @param outputDirectory The output directory to copy files into | ||
| * @param xmlTagOrder The order of the XML tags in the XML archive file | ||
| * @param level Archiving strategy level: | ||
| * - 0: no archiving (returns immediately) | ||
| * - 1: XML inputs only (flattened into a single file) | ||
| * - 2: XML inputs + the XSD schema | ||
| * | ||
| * Copy XML input files and every included files they contain (specified in | ||
| * the Included tag) into a single flat file. When @p level is at least 2, the | ||
| * XSD schema is also copied next to the flattened input. | ||
| */ | ||
| void archiveInputDeck( string_array const & inputFileNames, | ||
| string const & outputDirectory, | ||
| string_array const & xmlTagOrder, | ||
| integer level ); | ||
|
|
||
| } /* namespace archiveInputDeck */ | ||
|
|
||
| } /* namespace geos */ | ||
|
|
||
|
|
||
| #endif // GEOS_FILEIO_OUTPUTS_ARCHIVEINPUTDECK_HPP_ | ||
Uh oh!
There was an error while loading. Please reload this page.