mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-26 21:27:15 -05:00
#14470 Do not truncate folder names containing a dot when removing file extension
The ensemble file set removed the file extension by searching for the last dot in the complete path. When the path pattern was already without extension, the last dot was found in a folder name, truncating the path pattern and causing the search for SMSPEC files to fail. Add RiaFilePathTools::removeFileExtension based on std::filesystem::path::replace_extension, and use it both in RimEnsembleFileSet and in the grid and summary ensemble import, replacing a duplicated lambda doing the same operation.
This commit is contained in:
@@ -193,6 +193,18 @@ QString rootSearchPathFromSearchFilter( const QString& searchFilter )
|
||||
return pathPartList.join( separator() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Remove the file extension, if any. A folder name can contain a dot, and only the extension of the file
|
||||
/// name is removed. Paths without a file extension are returned unmodified.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString removeFileExtension( const QString& filePath )
|
||||
{
|
||||
// Use UTF-16 in both directions to avoid lossy conversion of non-ASCII paths
|
||||
std::filesystem::path path( filePath.toStdU16String() );
|
||||
|
||||
return QString::fromStdU16String( path.replace_extension().u16string() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -40,6 +40,7 @@ QString canonicalPath( const QString& path );
|
||||
std::pair<QString, QString> toFolderAndFileName( const QString& absFileName );
|
||||
QString removeDuplicatePathSeparators( const QString& path );
|
||||
QString rootSearchPathFromSearchFilter( const QString& searchFilter );
|
||||
QString removeFileExtension( const QString& filePath );
|
||||
QString commonRootOfFileNames( const QStringList& filePaths );
|
||||
std::string makeSuitableAsFileName( const std::string candidateName );
|
||||
std::string normalizePath( std::string path );
|
||||
|
||||
@@ -104,18 +104,12 @@ static void doImport( const RicImportGridAndSummaryEnsembleDialogResult& result,
|
||||
// Build the union of base paths (extension-stripped) from both lists so every realization
|
||||
// appears exactly once. findPathPattern requires each realization number to be unique across
|
||||
// the input; duplicating paths with different extensions breaks the pattern detection.
|
||||
auto stripExtension = []( const QString& path ) -> QString
|
||||
{
|
||||
int dot = path.lastIndexOf( '.' );
|
||||
return dot != -1 ? path.left( dot ) : path;
|
||||
};
|
||||
|
||||
QStringList strippedPaths;
|
||||
|
||||
for ( const auto& f : result.gridFiles )
|
||||
strippedPaths << stripExtension( f );
|
||||
strippedPaths << RiaFilePathTools::removeFileExtension( f );
|
||||
for ( const auto& f : result.summaryFiles )
|
||||
strippedPaths << stripExtension( f );
|
||||
strippedPaths << RiaFilePathTools::removeFileExtension( f );
|
||||
|
||||
strippedPaths.removeDuplicates();
|
||||
|
||||
|
||||
@@ -148,13 +148,9 @@ void RimEnsembleFileSet::findAndSetPathPatternAndRangeString( const QStringList&
|
||||
|
||||
const auto& [pattern, rangeString] = RiaEnsembleImportTools::findPathPattern( normalizedPaths, internal::placeholderString() );
|
||||
|
||||
// find the pattern without extension by finding . and remove rest of string
|
||||
auto noExtension = pattern;
|
||||
auto dotIndex = noExtension.lastIndexOf( '.' );
|
||||
if ( dotIndex != -1 )
|
||||
{
|
||||
noExtension = noExtension.left( dotIndex );
|
||||
}
|
||||
// The file paths can be given with or without a file extension. Remove the extension if present, without
|
||||
// truncating a folder name containing a dot.
|
||||
auto noExtension = RiaFilePathTools::removeFileExtension( pattern );
|
||||
|
||||
m_pathPattern = noExtension;
|
||||
m_realizationNumbersReadFromFiles = rangeString;
|
||||
|
||||
@@ -2,12 +2,24 @@
|
||||
|
||||
#include "Tools/Ensemble/RiaEnsembleImportTools.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
namespace internal
|
||||
{
|
||||
QString placeholderText()
|
||||
{
|
||||
return "$(INDEX)";
|
||||
}
|
||||
|
||||
void createEmptyFile( const QString& filePath )
|
||||
{
|
||||
QDir().mkpath( QFileInfo( filePath ).absolutePath() );
|
||||
|
||||
QFile file( filePath );
|
||||
if ( file.open( QIODevice::WriteOnly ) ) file.close();
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
TEST( RimPathPatternFileSetTest, OneVaryingNumberRealizations )
|
||||
@@ -32,6 +44,46 @@ TEST( RimPathPatternFileSetTest, OneVaryingNumberRealizations )
|
||||
}
|
||||
}
|
||||
|
||||
TEST( RimPathPatternFileSetTest, FolderNameContainingDot )
|
||||
{
|
||||
QStringList filePaths = { "/scratch/fmu/user/drogon.2024/realization-0/iter-0/eclipse/model/DROGON-0.ESMRY",
|
||||
"/scratch/fmu/user/drogon.2024/realization-1/iter-0/eclipse/model/DROGON-1.ESMRY",
|
||||
"/scratch/fmu/user/drogon.2024/realization-2/iter-0/eclipse/model/DROGON-2.ESMRY" };
|
||||
|
||||
const auto& [pattern, numberRange] = RiaEnsembleImportTools::findPathPattern( filePaths, internal::placeholderText() );
|
||||
EXPECT_STREQ( pattern.toStdString().data(),
|
||||
"/scratch/fmu/user/drogon.2024/realization-$(INDEX)/iter-0/eclipse/model/DROGON-$(INDEX).ESMRY" );
|
||||
EXPECT_STREQ( numberRange.toStdString().data(), "0-2" );
|
||||
|
||||
const auto paths = RiaEnsembleImportTools::createPathsFromPattern( pattern, numberRange, internal::placeholderText() );
|
||||
EXPECT_EQ( paths.size(), filePaths.size() );
|
||||
if ( paths.size() != filePaths.size() ) return;
|
||||
for ( auto i = 0; i < paths.size(); i++ )
|
||||
{
|
||||
EXPECT_STREQ( paths[i].toStdString().data(), filePaths[i].toStdString().data() );
|
||||
}
|
||||
}
|
||||
|
||||
TEST( RimPathPatternFileSetTest, SearchFileSystemFolderNameContainingDot )
|
||||
{
|
||||
QTemporaryDir tempDir;
|
||||
ASSERT_TRUE( tempDir.isValid() );
|
||||
|
||||
const QString rootFolder = QDir::fromNativeSeparators( tempDir.path() ) + "/drogon.2024";
|
||||
|
||||
for ( int realizationNumber = 0; realizationNumber < 3; realizationNumber++ )
|
||||
{
|
||||
const QString filePath =
|
||||
QString( "%1/realization-%2/iter-0/eclipse/model/DROGON-%2.SMSPEC" ).arg( rootFolder ).arg( realizationNumber );
|
||||
internal::createEmptyFile( filePath );
|
||||
}
|
||||
|
||||
const QString pathPattern = rootFolder + "/realization-*/iter-0/eclipse/model/DROGON-*";
|
||||
|
||||
const auto paths = RiaEnsembleImportTools::createPathsBySearchingFileSystem( pathPattern, ".SMSPEC", "*" );
|
||||
EXPECT_EQ( paths.size(), 3 );
|
||||
}
|
||||
|
||||
TEST( RimPathPatternFileSetTest, OneVaryingNumberMultipleLocations )
|
||||
{
|
||||
QStringList filePaths = { "file_1/path-02/real-1.txt", "file_2/path-02/real-2.txt", "file_3/path-02/real-3.txt", "file_13/path-02/real-13.txt" };
|
||||
|
||||
@@ -168,3 +168,28 @@ TEST( RiaFilePathTools, keyPathComponentsForEachFilePath )
|
||||
EXPECT_EQ( QString( "realization-1" ), test1.front() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RiaFilePathTools, removeFileExtension )
|
||||
{
|
||||
// A folder name can contain a dot. Only a file extension is removed, the folder name is left untouched.
|
||||
const QString pathWithExtension = "/scratch/fmu/user/drogon.2024/realization-$(INDEX)/iter-0/eclipse/model/DROGON-$(INDEX).ESMRY";
|
||||
EXPECT_EQ( QString( "/scratch/fmu/user/drogon.2024/realization-$(INDEX)/iter-0/eclipse/model/DROGON-$(INDEX)" ),
|
||||
RiaFilePathTools::removeFileExtension( pathWithExtension ) );
|
||||
|
||||
// A path without a file extension must be returned unmodified, see https://github.com/OPM/ResInsight/issues/14470
|
||||
const QString pathWithoutExtension = "/scratch/fmu/user/drogon.2024/realization-$(INDEX)/iter-0/eclipse/model/DROGON-$(INDEX)";
|
||||
EXPECT_EQ( pathWithoutExtension, RiaFilePathTools::removeFileExtension( pathWithoutExtension ) );
|
||||
|
||||
#ifdef WIN32
|
||||
// A backslash is only recognized as a path separator on Windows
|
||||
const QString windowsPath = "d:\\scratch\\drogon.2024\\realization-0\\DROGON-0";
|
||||
EXPECT_EQ( windowsPath, RiaFilePathTools::removeFileExtension( windowsPath ) );
|
||||
#endif
|
||||
|
||||
// No folder part
|
||||
EXPECT_EQ( QString( "DROGON-0" ), RiaFilePathTools::removeFileExtension( "DROGON-0.SMSPEC" ) );
|
||||
|
||||
// Empty path
|
||||
EXPECT_EQ( QString( "" ), RiaFilePathTools::removeFileExtension( "" ) );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user