Add tool used to extract project file revisions from project file database

* Add tool used to extract project file versions from database
* Delete old records from backup to limit number of records to 50
This commit is contained in:
Magne Sjaastad 2024-05-04 13:21:31 +02:00 committed by GitHub
parent a55b53c725
commit 96481d81a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 279 additions and 5 deletions

View File

@ -66,11 +66,40 @@ bool insertContent( const QString& content )
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool deleteOldRecords( int maximumRecordCount )
{
QSqlQuery countQuery( "SELECT COUNT(*) FROM file_versions" );
if ( !countQuery.exec() || !countQuery.next() )
{
RiaLogging::error( "Error counting records: " + countQuery.lastError().text() );
return false;
}
int count = countQuery.value( 0 ).toInt();
int recordsToDelete = count - maximumRecordCount;
if ( recordsToDelete <= 0 ) return true;
QSqlQuery query;
query.prepare( "DELETE FROM file_versions WHERE id IN (SELECT id FROM file_versions ORDER BY timestamp ASC LIMIT :limit)" );
query.bindValue( ":limit", recordsToDelete );
if ( !query.exec() )
{
QString txt = "Error deleting old records:" + query.lastError().text();
RiaLogging::error( txt );
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool appendTextToDatabase( const QString& databaseFilePath, const QString& content )
bool appendTextToDatabase( const QString& databaseFilePath, int maximumRecordCount, const QString& content )
{
const QString databaseType = "QSQLITE";
@ -101,6 +130,8 @@ bool appendTextToDatabase( const QString& databaseFilePath, const QString& conte
db.setDatabaseName( databaseFilePath );
if ( !createTableIfNeeded() ) return false;
if ( !deleteOldRecords( maximumRecordCount ) ) return false;
if ( !insertContent( content ) ) return false;
return true;

View File

@ -25,5 +25,5 @@
//==================================================================================================
namespace RiaProjectBackupTools
{
bool appendTextToDatabase( const QString& databaseFilePath, const QString& content );
}
bool appendTextToDatabase( const QString& databaseFilePath, int maximumRecordCount, const QString& content );
} // namespace RiaProjectBackupTools

View File

@ -416,8 +416,9 @@ bool RimProject::writeProjectFile()
if ( RiaPreferences::current()->storeBackupOfProjectFiles() )
{
QString backupFilename = fileName + "db";
RiaProjectBackupTools::appendTextToDatabase( backupFilename, content );
QString backupFilename = fileName + "db";
const int maximumRecordCount = 50;
RiaProjectBackupTools::appendTextToDatabase( backupFilename, maximumRecordCount, content );
}
distributePathsFromGlobalPathList();

View File

@ -989,6 +989,11 @@ else(OCTAVE_MKOCTFILE)
)
endif(OCTAVE_MKOCTFILE)
add_subdirectory(ThirdParty/extract-projectfile-versions)
install(TARGETS extract-projectfile-versions
DESTINATION ${RESINSIGHT_INSTALL_FOLDER}
)
# ##############################################################################
# Visual Studio : Create the ruleset file to be used by Static Code Analysis
# https://stackoverflow.com/questions/75031903/how-to-enable-static-analysis-with-custom-ruleset-in-msvc-via-cmakelists-txt

View File

@ -0,0 +1,78 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: true
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 140
CommentPragmas: "^ IWYU pragma:"
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH]
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|isl|json)/)'
Priority: 3
- Regex: ".*"
Priority: 1
IncludeIsMainRegex: "$"
IndentCaseLabels: true
IndentWidth: 4
IndentWrappedFunctionNames: true
JavaScriptQuotes: Leave
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PenaltyBreakAssignment: 13
PenaltyBreakBeforeFirstCallParameter: 10000
PenaltyBreakComment: 20
PenaltyBreakFirstLessLess: 12
PenaltyBreakString: 100
PenaltyExcessCharacter: 5
PenaltyReturnTypeOnItsOwnLine: 30
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: true
SpacesInSquareBrackets: false
Standard: c++20
TabWidth: 4
UseTab: Never

View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.5)
project(extract-projectfile-versions)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5 COMPONENTS Core Sql REQUIRED)
add_executable(extract-projectfile-versions main.cpp)
target_link_libraries(extract-projectfile-versions Qt5::Core Qt5::Sql)
if(MSVC)
add_custom_command(
TARGET extract-projectfile-versions
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:extract-projectfile-versions>
$<TARGET_FILE_DIR:extract-projectfile-versions>
COMMAND_EXPAND_LISTS
)
endif(MSVC)

View File

@ -0,0 +1,135 @@
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<QDateTime, QString>> getAllContent()
{
std::vector<std::pair<QDateTime, QString>> content;
QSqlQuery query;
query.prepare( "SELECT timestamp, content FROM file_versions" );
if ( !query.exec() )
{
qDebug() << "Error retrieving content:" << query.lastError().text();
return content;
}
while ( query.next() )
{
QDateTime timestamp = query.value( 0 ).toDateTime();
QString projectContent = query.value( 1 ).toString();
content.push_back( std::make_pair( timestamp, projectContent ) );
}
return content;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void extractVersionsToFolder( const QString& destinationFolder )
{
auto allContent = getAllContent();
for ( const auto& [timestamp, content] : allContent )
{
const QString dateFormat = "yyyy-MM-dd_hh-mm-ss";
QString filename = destinationFolder + "/" + timestamp.toString( dateFormat ) + ".rsp";
QFile file( filename );
if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
qCritical() << "Error opening file for writing:" << filename;
return;
}
QTextStream out( &file );
out << content;
file.close();
}
}
int main( int argc, char* argv[] )
{
QCoreApplication app( argc, argv );
QCoreApplication::setApplicationName( "extract-projectfile-versions" );
QCoreApplication::setApplicationVersion( "1.0" );
QCommandLineParser parser;
parser.setApplicationDescription(
"extract-projectfile-versions is used to restore previous revisions of a ResInsight project from a project file database." );
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument( "file", "ResInsight project file database (*.rspdb)" );
parser.addPositionalArgument( "outputfolder", "Output folder all project files" );
parser.process( app );
const QStringList args = parser.positionalArguments();
if ( args.size() != 2 )
{
qCritical() << "Failed to detect two input arguments.";
parser.showHelp( 1 );
return 1;
}
QString databaseFilePath = args.front();
if ( !QFile::exists( databaseFilePath ) )
{
qCritical() << "Database file does not exist:" << databaseFilePath;
return 1;
}
QString destinationDir = args[1];
QDir dir;
if ( !dir.mkpath( destinationDir ) )
{
qCritical() << "Not able to create destination folder : " << destinationDir;
return 1;
}
const QString databaseType = "QSQLITE";
if ( !QSqlDatabase::isDriverAvailable( databaseType ) )
{
qInfo() << "sqlite database is not available.";
return 1;
}
// Try to open the SQLITE database
QSqlDatabase db = QSqlDatabase::database();
if ( !db.isValid() || !db.open() )
{
qInfo() << "Adding database";
// Add the SQLITE database, and it it required to do this once per session. The database will be available during the lifetime
// of the application, and can be accessed using QSqlDatabase::database()
db = QSqlDatabase::addDatabase( databaseType );
}
QFileInfo fileInfo( databaseFilePath );
auto dbPath = fileInfo.absoluteFilePath();
db.setDatabaseName( dbPath );
if ( !db.open() )
{
QString txt = "Error opening database:" + db.lastError().text();
qCritical() << txt;
return 1;
}
extractVersionsToFolder( destinationDir );
return 1;
}