Skip to content
Snippets Groups Projects
Commit 329f22fb authored by Malte Christian Kögel's avatar Malte Christian Kögel
Browse files

Store "WasStarted" properties per condition in LastParticipant.txt and recover...

Store "WasStarted" properties per condition in LastParticipant.txt and recover it when choosing "Continue Participant". That way, restarting a condition with and without closing the game in between is handled in the same way. Create backups when restarting condition. closes #94
parent 64df67dc
No related branches found
No related tags found
No related merge requests found
...@@ -26,7 +26,7 @@ void USFCondition::Generate(const FString& InPhaseName, const TArray<int>& Condi ...@@ -26,7 +26,7 @@ void USFCondition::Generate(const FString& InPhaseName, const TArray<int>& Condi
if (Factor->IsA(USFMapFactor::StaticClass())) if (Factor->IsA(USFMapFactor::StaticClass()))
{ {
Map = FactorLevel; Map = FactorLevel;
//for better readybility strip path! //for better readability strip path!
FactorLevel = FPaths::GetBaseFilename(FactorLevel); FactorLevel = FPaths::GetBaseFilename(FactorLevel);
} }
FactorLevels.Add(Factor->FactorName, FactorLevel); FactorLevels.Add(Factor->FactorName, FactorLevel);
...@@ -232,6 +232,11 @@ bool USFCondition::HasRequiredVariables() const ...@@ -232,6 +232,11 @@ bool USFCondition::HasRequiredVariables() const
return false; return false;
} }
void USFCondition::SetbStarted(bool WasStarted)
{
bStarted = WasStarted;
}
bool USFCondition::WasStarted() const bool USFCondition::WasStarted() const
{ {
return bStarted; return bStarted;
... ...
......
...@@ -529,6 +529,8 @@ void USFGameInstance::HandleGoToConditionSynced(FString ConditionName, bool bFor ...@@ -529,6 +529,8 @@ void USFGameInstance::HandleGoToConditionSynced(FString ConditionName, bool bFor
{ {
LogComment("Restarting Condition " + NextCondition->UniqueName + ", so we delete all gathered data for that condition."); LogComment("Restarting Condition " + NextCondition->UniqueName + ", so we delete all gathered data for that condition.");
FString BackUpFolderName = "Restart_" + NextCondition->UniqueName + "_BackUp-" + FDateTime::Now().ToString();
Participant->SetCurrentBackUpFolderName(BackUpFolderName);
//so remove already stored data! //so remove already stored data!
Participant->DeleteStoredDataForConditionFromLongTable(NextCondition); Participant->DeleteStoredDataForConditionFromLongTable(NextCondition);
for (USFDependentVariable* DV : NextCondition->DependentVariables) for (USFDependentVariable* DV : NextCondition->DependentVariables)
... ...
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "Help/SFUtils.h" #include "Help/SFUtils.h"
#include "Logging/SFLoggingBPLibrary.h" #include "Logging/SFLoggingBPLibrary.h"
#include "Logging/SFLoggingUtils.h" #include "Logging/SFLoggingUtils.h"
#include "JsonUtilities.h"
USFParticipant::USFParticipant() USFParticipant::USFParticipant()
{ {
...@@ -335,6 +336,7 @@ void USFParticipant::RemoveLinesOfConditionAndWriteToFile(USFCondition* Conditio ...@@ -335,6 +336,7 @@ void USFParticipant::RemoveLinesOfConditionAndWriteToFile(USFCondition* Conditio
} }
CleanedLines.Add(Lines[0]); CleanedLines.Add(Lines[0]);
bool bHasRemovedLines = false;
for (int i = 1; i < Lines.Num(); ++i) for (int i = 1; i < Lines.Num(); ++i)
{ {
TArray<FString> Entries; TArray<FString> Entries;
...@@ -356,6 +358,11 @@ void USFParticipant::RemoveLinesOfConditionAndWriteToFile(USFCondition* Conditio ...@@ -356,6 +358,11 @@ void USFParticipant::RemoveLinesOfConditionAndWriteToFile(USFCondition* Conditio
{ {
CleanedLines.Add(Lines[i]); CleanedLines.Add(Lines[i]);
} }
//At least this line will be removed
else if(!bHasRemovedLines)
{
bHasRemovedLines = true;
}
} }
else else
{ {
...@@ -363,6 +370,12 @@ void USFParticipant::RemoveLinesOfConditionAndWriteToFile(USFCondition* Conditio ...@@ -363,6 +370,12 @@ void USFParticipant::RemoveLinesOfConditionAndWriteToFile(USFCondition* Conditio
CleanedLines.Add(Lines[i]); CleanedLines.Add(Lines[i]);
} }
} }
if(bHasRemovedLines)
{
CreateLongTableBackUp(Filename);
}
FFileHelper::SaveStringArrayToFile(CleanedLines, *Filename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM, FFileHelper::SaveStringArrayToFile(CleanedLines, *Filename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM,
&IFileManager::Get(), EFileWrite::FILEWRITE_None); &IFileManager::Get(), EFileWrite::FILEWRITE_None);
} }
...@@ -451,6 +464,15 @@ TArray<USFCondition*> USFParticipant::GetLastParticipantsConditions() ...@@ -451,6 +464,15 @@ TArray<USFCondition*> USFParticipant::GetLastParticipantsConditions()
TArray<USFCondition*> LoadedConditions; TArray<USFCondition*> LoadedConditions;
TMap<USFIndependentVariable*, FString> LoadedIndependentVariablesValues; TMap<USFIndependentVariable*, FString> LoadedIndependentVariablesValues;
ReadExecutionJsonFile(GetLastParticipantID(), LoadedConditions, LoadedIndependentVariablesValues); ReadExecutionJsonFile(GetLastParticipantID(), LoadedConditions, LoadedIndependentVariablesValues);
//Load WasStarted-Values from JSON, to ensure data is not overwritten without backups, if conditions are restarted
TMap<FString, bool> LastParticipantHasStartedConditionValues = GetLastParticipantHasStartedConditionValues();
for(int i = 0; i<LoadedConditions.Num(); i++)
{
if(LastParticipantHasStartedConditionValues.Contains(LoadedConditions[i]->CreateIdentifiableName()))
{
LoadedConditions[i]->SetbStarted(LastParticipantHasStartedConditionValues[LoadedConditions[i]->CreateIdentifiableName()]);
}
}
return LoadedConditions; return LoadedConditions;
} }
...@@ -487,6 +509,27 @@ int USFParticipant::GetLastParticipantLastConditionStarted() ...@@ -487,6 +509,27 @@ int USFParticipant::GetLastParticipantLastConditionStarted()
return ParticipantJson->GetNumberField("CurrentConditionIdx"); return ParticipantJson->GetNumberField("CurrentConditionIdx");
} }
TMap<FString, bool> USFParticipant::GetLastParticipantHasStartedConditionValues()
{
TMap<FString, bool> ConditionsStarted;
TSharedPtr<FJsonObject> ParticipantJson = FSFUtils::ReadJsonFromFile("StudyRuns/LastParticipant.txt");
if (ParticipantJson != nullptr)
{
TArray<TSharedPtr<FJsonValue>> StartedConditionsArray = ParticipantJson->GetArrayField("StartedConditions");
for(auto& JsonValue : StartedConditionsArray)
{
TSharedPtr<FJsonObject> Entry = JsonValue->AsObject();
if(Entry.IsValid())
{
ConditionsStarted.Add(Entry->GetStringField("Condition"), Entry->GetBoolField("WasStarted"));
}
}
}
return ConditionsStarted;
}
bool USFParticipant::GetLastParticipantFinished() bool USFParticipant::GetLastParticipantFinished()
{ {
TSharedPtr<FJsonObject> ParticipantJson = FSFUtils::ReadJsonFromFile("StudyRuns/LastParticipant.txt"); TSharedPtr<FJsonObject> ParticipantJson = FSFUtils::ReadJsonFromFile("StudyRuns/LastParticipant.txt");
...@@ -649,6 +692,16 @@ void USFParticipant::RecoverStudyResultsOfFinishedConditions() ...@@ -649,6 +692,16 @@ void USFParticipant::RecoverStudyResultsOfFinishedConditions()
} }
} }
void USFParticipant::CreateLongTableBackUp(const FString PathToSrcFile) const
{
IFileManager& FileManager = IFileManager::Get();
const FString ReplaceWith = "StudyFramework/StudyLogs/RecyclingBin/" + CurrentBackUpFolderName + "/";
const FString PathToDestFile = PathToSrcFile.Replace(TEXT("StudyFramework/StudyLogs/"), *ReplaceWith);
FileManager.Copy( *PathToDestFile, *PathToSrcFile);
FSFLoggingUtils::Log("Created Backup: " + PathToDestFile);
}
void USFParticipant::ClearPhaseLongtables(ASFStudySetup* StudySetup) void USFParticipant::ClearPhaseLongtables(ASFStudySetup* StudySetup)
{ {
const FString LongTableFolder = FPaths::ProjectDir() + "StudyFramework/StudyLogs/"; const FString LongTableFolder = FPaths::ProjectDir() + "StudyFramework/StudyLogs/";
...@@ -667,6 +720,12 @@ void USFParticipant::ClearPhaseLongtables(ASFStudySetup* StudySetup) ...@@ -667,6 +720,12 @@ void USFParticipant::ClearPhaseLongtables(ASFStudySetup* StudySetup)
FSFLoggingUtils::Log("Moved .csv files: " + NewParentFolderPath); FSFLoggingUtils::Log("Moved .csv files: " + NewParentFolderPath);
} }
void USFParticipant::SetCurrentBackUpFolderName(FString BackUpFolderName)
{
CurrentBackUpFolderName = BackUpFolderName;
}
bool USFParticipant::SetCondition(const USFCondition* NextCondition) bool USFParticipant::SetCondition(const USFCondition* NextCondition)
{ {
if (!NextCondition) if (!NextCondition)
...@@ -707,10 +766,16 @@ void USFParticipant::LogCurrentParticipant() const ...@@ -707,10 +766,16 @@ void USFParticipant::LogCurrentParticipant() const
Json->SetNumberField("ParticipantSequenceNumber", ParticipantSequenceNumber); Json->SetNumberField("ParticipantSequenceNumber", ParticipantSequenceNumber);
Json->SetStringField("ParticipantID", ParticipantID); Json->SetStringField("ParticipantID", ParticipantID);
bool bFinished = true; bool bFinished = true;
TArray<TSharedPtr<FJsonValue>> StartedConditions; //Cannot use TMap because we want to save to JSON
for (USFCondition* Condition : Conditions) for (USFCondition* Condition : Conditions)
{ {
bFinished = bFinished && (Condition->IsFinished() || !Condition->HasRequiredVariables()); bFinished = bFinished && (Condition->IsFinished() || !Condition->HasRequiredVariables());
TSharedPtr<FJsonObject> JsonObject = MakeShared<FJsonObject>();
JsonObject->SetStringField("Condition", Condition->CreateIdentifiableName());
JsonObject->SetBoolField("WasStarted", Condition->WasStarted());
StartedConditions.Add(MakeShared<FJsonValueObject>(JsonObject));
} }
Json->SetArrayField("StartedConditions", StartedConditions);
Json->SetBoolField("Finished", bFinished); Json->SetBoolField("Finished", bFinished);
Json->SetNumberField("CurrentConditionIdx", CurrentConditionIdx); Json->SetNumberField("CurrentConditionIdx", CurrentConditionIdx);
if (USFGameInstance::Get() && USFGameInstance::Get()->GetStudySetup()) if (USFGameInstance::Get() && USFGameInstance::Get()->GetStudySetup())
... ...
......
...@@ -37,6 +37,7 @@ public: ...@@ -37,6 +37,7 @@ public:
bool IsFinished() const; bool IsFinished() const;
bool HasRequiredVariables() const; bool HasRequiredVariables() const;
bool WasStarted() const; bool WasStarted() const;
void SetbStarted(bool WasStarted);
//this is used to recover study results from the phase long table if a participant's run is continued //this is used to recover study results from the phase long table if a participant's run is continued
//return false if this entry does not match this condition //return false if this entry does not match this condition
... ...
......
...@@ -50,6 +50,7 @@ public: ...@@ -50,6 +50,7 @@ public:
static int GetLastParticipantSequenceNumber(); static int GetLastParticipantSequenceNumber();
static FString GetLastParticipantID(); static FString GetLastParticipantID();
static int GetLastParticipantLastConditionStarted(); static int GetLastParticipantLastConditionStarted();
static TMap<FString, bool> GetLastParticipantHasStartedConditionValues();
static bool GetLastParticipantFinished(); static bool GetLastParticipantFinished();
static ASFStudySetup* GetLastParticipantSetup(); static ASFStudySetup* GetLastParticipantSetup();
void LoadLastParticipantsIndependentVariables(); void LoadLastParticipantsIndependentVariables();
...@@ -69,15 +70,21 @@ public: ...@@ -69,15 +70,21 @@ public:
void RecoverStudyResultsOfFinishedConditions(); void RecoverStudyResultsOfFinishedConditions();
// the results of all participants are stored in a file per phase (called longtable) // The results of all participants are stored in a file per phase (called longtable)
// for the data to be ready to use in statistics software, this methods clears all // for the data to be ready to use in statistics software. This methods clears all
// of that data (e.g. if study is entirely restarted) // of that data (e.g. if study is entirely restarted).
// So: USE WITH CARE! // The data can be recovered from /StudyLogs/RecyclingBin
static void ClearPhaseLongtables(ASFStudySetup* StudySetup); static void ClearPhaseLongtables(ASFStudySetup* StudySetup);
// Whenever we delete data in a file (e.g. with by restarting a condition),
// we want to create a backup, to enable data recovery
// EditOperation reflects the reason (e.g. restart condition), which appears in
// recycling bin file name
void CreateLongTableBackUp(const FString PathToSrcFile) const;
void StoreTrialInTrialDVLongTable(USFMultipleTrialDependentVariable* DependentVariable, TArray<FString> Values) const; void StoreTrialInTrialDVLongTable(USFMultipleTrialDependentVariable* DependentVariable, TArray<FString> Values) const;
void DeleteStoredDataForConditionFromLongTable(USFCondition* Condition); void DeleteStoredDataForConditionFromLongTable(USFCondition* Condition);
void DeleteStoredTrialDataForCondition(USFCondition* Condition, USFMultipleTrialDependentVariable* DependentVariable); void DeleteStoredTrialDataForCondition(USFCondition* Condition, USFMultipleTrialDependentVariable* DependentVariable);
void SetCurrentBackUpFolderName(FString BackUpFolderName);
protected: protected:
bool SetCondition(const USFCondition* NextCondition); bool SetCondition(const USFCondition* NextCondition);
...@@ -93,7 +100,13 @@ protected: ...@@ -93,7 +100,13 @@ protected:
void StoreInIndependentVarLongTable() const; void StoreInIndependentVarLongTable() const;
void RemoveLinesOfConditionAndWriteToFile(USFCondition* Condition, FString Filename); void RemoveLinesOfConditionAndWriteToFile(USFCondition* Condition, FString Filename);
// This is the parent folder within RecyclingBin, where backups
// of the current operation will be stored.
// E.g. "RestartConditionBackUp-TIMESTAMP"
// Whenever we delete lines within a .csv file we should update this.
// We want to store all files related to one operation in the same
// backup folder, this is why we need this variable.
FString CurrentBackUpFolderName;
FString ParticipantID; FString ParticipantID;
//sequence number is used for randomization etc. it is also unique per participant and starts at 0 //sequence number is used for randomization etc. it is also unique per participant and starts at 0
int ParticipantSequenceNumber; int ParticipantSequenceNumber;
... ...
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment