Skip to content
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

Allow read/write of project settings in C# #857

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion c-sharp/JsonUtils/VerseRefConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ out string errorMessage
catch (Exception e)
{
verseRef = new VerseRef();
Console.Error.Write(e.ToString());
errorMessage = $"Invalid VerseRef ({jsonString}): {e.Message}";
return false;
}
Expand Down
62 changes: 56 additions & 6 deletions c-sharp/Projects/ParatextProjectDataProvider.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Text.Json.Nodes;
using Newtonsoft.Json.Linq;
using Paranext.DataProvider.MessageHandlers;
using Paranext.DataProvider.MessageTransports;

Expand All @@ -23,13 +25,33 @@ ProjectDetails projectDetails

Getters.Add("getChapterUSX", GetChapterUSX);
Setters.Add("setChapterUSX", SetChapterUSX);

Getters.Add("getSetting", GetProjectSetting);
Setters.Add("setSetting", SetProjectSetting);
}

protected override Task StartDataProvider()
{
return Task.CompletedTask;
}

protected override ResponseToRequest HandleRequest(string functionName, JsonArray args)
{
try
{
return functionName switch
{
"resetSetting" => ResetProjectSetting(args[0]!.ToJsonString()),
_ => base.HandleRequest(functionName, args)
};
}
catch (Exception e)
{
Console.Error.WriteLine(e.ToString());
return ResponseToRequest.Failed(e.ToString());
}
}

protected override string GetProjectStorageInterpreterId()
{
return _paratextPsi.DataProviderName;
Expand All @@ -45,6 +67,34 @@ protected override ResponseToRequest SetExtensionData(ProjectDataScope dataScope
return _paratextPsi.SetExtensionData(dataScope, data);
}

private ResponseToRequest GetProjectSetting(string key)
{
return Get(ProjectDataType.SETTINGS, JToken.Parse(key).ToString());
}

private ResponseToRequest SetProjectSetting(string key, string value)
{
return Set(ProjectDataType.SETTINGS, JToken.Parse(key).ToString(), value);
}

// Typically for "reset" we would want to erase the setting and then call "getDefault" if a
// setting is not present when "get" is called. Since we're using PT settings as the backing
// store here, though, we want to keep all properties filled in inside of Settings.xml files
private ResponseToRequest ResetProjectSetting(string key)
{
string settingName = JToken.Parse(key).ToString();
string? defaultValue = ProjectSettingsService.GetDefault(
PapiClient,
settingName,
ProjectType.Paratext
);
if (defaultValue == null)
return ResponseToRequest.Failed($"Default value for {settingName} was null");
ResponseToRequest retVal = Set(ProjectDataType.SETTINGS, settingName, defaultValue);
SendDataUpdateEvent(retVal.Contents);
return retVal;
}

private ResponseToRequest Get(string dataType, string dataQualifier)
{
ProjectDataScope scope =
Expand Down Expand Up @@ -72,34 +122,34 @@ private ResponseToRequest Set(string dataType, string dataQualifier, string data
#region USFM handling methods
private ResponseToRequest GetBookUSFM(string jsonString)
{
return Get(ParatextProjectStorageInterpreter.BookUSFM, jsonString);
return Get(ProjectDataType.BOOK_USFM, jsonString);
}

private ResponseToRequest GetChapterUSFM(string jsonString)
{
return Get(ParatextProjectStorageInterpreter.ChapterUSFM, jsonString);
return Get(ProjectDataType.CHAPTER_USFM, jsonString);
}

private ResponseToRequest GetVerseUSFM(string jsonString)
{
return Get(ParatextProjectStorageInterpreter.VerseUSFM, jsonString);
return Get(ProjectDataType.VERSE_USFM, jsonString);
}

private ResponseToRequest SetChapterUSFM(string dataQualifier, string data)
{
return Set(ParatextProjectStorageInterpreter.ChapterUSFM, dataQualifier, data);
return Set(ProjectDataType.CHAPTER_USFM, dataQualifier, data);
}
#endregion

#region USX handling methods
private ResponseToRequest GetChapterUSX(string jsonString)
{
return Get(ParatextProjectStorageInterpreter.ChapterUSX, jsonString);
return Get(ProjectDataType.CHAPTER_USX, jsonString);
}

private ResponseToRequest SetChapterUSX(string dataQualifier, string data)
{
return Set(ParatextProjectStorageInterpreter.ChapterUSX, dataQualifier, data);
return Set(ProjectDataType.CHAPTER_USX, dataQualifier, data);
}
#endregion
}
83 changes: 63 additions & 20 deletions c-sharp/Projects/ParatextProjectStorageInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,15 @@ namespace Paranext.DataProvider.Projects;
internal class ParatextProjectStorageInterpreter : ProjectStorageInterpreter
{
#region Constants / Member variables
public const string BookUSFM = "BookUSFM";
public const string ChapterUSFM = "ChapterUSFM";
public const string VerseUSFM = "VerseUSFM";
public const string ChapterUSX = "ChapterUSX";

// All data types related to Scripture editing. Changes to any portion of Scripture should send
// out updates to all these data types
public static readonly List<string> AllScriptureDataTypes = new List<string>
{
BookUSFM,
ChapterUSFM,
VerseUSFM,
ChapterUSX
};
public static readonly List<string> AllScriptureDataTypes =
[
ProjectDataType.BOOK_USFM,
ProjectDataType.CHAPTER_USFM,
ProjectDataType.VERSE_USFM,
ProjectDataType.CHAPTER_USX
];

private readonly LocalParatextProjects _paratextProjects;
#endregion
Expand Down Expand Up @@ -140,22 +135,25 @@ public override ResponseToRequest GetProjectData(ProjectDataScope scope)

return scope.DataType switch
{
BookUSFM
ProjectDataType.BOOK_USFM
=> string.IsNullOrEmpty(error)
? ResponseToRequest.Succeeded(scrText.GetText(verseRef, false, true))
: ResponseToRequest.Failed(error),
ChapterUSFM
ProjectDataType.CHAPTER_USFM
=> string.IsNullOrEmpty(error)
? ResponseToRequest.Succeeded(scrText.GetText(verseRef, true, true))
: ResponseToRequest.Failed(error),
VerseUSFM
ProjectDataType.VERSE_USFM
=> string.IsNullOrEmpty(error)
? ResponseToRequest.Succeeded(scrText.Parser.GetVerseUsfmText(verseRef))
: ResponseToRequest.Failed(error),
ChapterUSX
ProjectDataType.CHAPTER_USX
=> string.IsNullOrEmpty(error)
? ResponseToRequest.Succeeded(GetChapterUsx(scrText, verseRef))
: ResponseToRequest.Failed(error),
ProjectDataType.SETTINGS
=> ResponseToRequest.Succeeded(scrText.Settings.ParametersDictionary[
ProjectSettings.GetParatextSettingNameFromPlatformBibleSettingName(scope.DataQualifier) ?? scope.DataQualifier]),
_ => ResponseToRequest.Failed($"Unknown data type: {scope.DataType}")
};
}
Expand Down Expand Up @@ -184,7 +182,7 @@ public override ResponseToRequest SetProjectData(ProjectDataScope scope, string

switch (scope.DataType)
{
case ChapterUSFM:
case ProjectDataType.CHAPTER_USFM:
if (!string.IsNullOrEmpty(error))
return ResponseToRequest.Failed(error);
RunWithinLock(
Expand All @@ -200,9 +198,9 @@ public override ResponseToRequest SetProjectData(ProjectDataScope scope, string
);
}
);
// The value of returned strings are case sensitive and cannot change unless data provider subscriptions change
// The value of returned strings are case-sensitive and cannot change unless data provider subscriptions change
return ResponseToRequest.Succeeded(AllScriptureDataTypes);
case ChapterUSX:
case ProjectDataType.CHAPTER_USX:
if (!string.IsNullOrEmpty(error))
return ResponseToRequest.Failed(error);
ResponseToRequest? response = null;
Expand All @@ -211,6 +209,51 @@ public override ResponseToRequest SetProjectData(ProjectDataScope scope, string
writeLock => response = SetChapterUsx(scrText, verseRef, data, writeLock)
);
return response ?? ResponseToRequest.Failed("Unknown error occurred");
case ProjectDataType.SETTINGS:
// If there is no Paratext setting for the name given, we'll create one lower down
ResponseToRequest? currentValueResponse = ResponseToRequest.Failed("");
try
{
currentValueResponse = GetProjectData(scope);
}
catch (KeyNotFoundException) {}

// Make sure the value we're planning to set is valid
var currentValueJson = currentValueResponse.Success
? JsonConvert.SerializeObject(currentValueResponse.Contents)
: "";
if (!ProjectSettingsService.IsValid(
PapiClient,
data,
currentValueJson,
scope.DataQualifier,
"",
ProjectType.Paratext))
return ResponseToRequest.Failed($"Validation failed for {scope.DataQualifier}");

// Figure out which setting name to use
var paratextSettingName =
ProjectSettings.GetParatextSettingNameFromPlatformBibleSettingName(
scope.DataQualifier) ?? scope.DataQualifier;

// Now actually write the setting
string? errorMessage = null;
RunWithinLock(
WriteScope.AllSettingsFiles(),
_ => {
try
{
scrText.Settings.SetSetting(paratextSettingName, data);
scrText.Settings.Save();
}
catch (Exception ex)
{
errorMessage = ex.Message;
}
});
return (errorMessage != null)
? ResponseToRequest.Failed(errorMessage)
: ResponseToRequest.Succeeded(ProjectDataType.SETTINGS);
default:
return ResponseToRequest.Failed($"Unknown data type: {scope.DataType}");
}
Expand Down Expand Up @@ -301,7 +344,7 @@ public override ResponseToRequest SetExtensionData(ProjectDataScope scope, strin
// ReSharper restore AccessToDisposedClosure
}
);
// The value of returned string is case sensitive and cannot change unless data provider subscriptions change
// The value of returned string is case-sensitive and cannot change unless data provider subscriptions change
return ResponseToRequest.Succeeded("ExtensionData");
}
catch (Exception e)
Expand Down
10 changes: 10 additions & 0 deletions c-sharp/Projects/ProjectDataType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Paranext.DataProvider.Projects;

public sealed class ProjectDataType
{
public const string BOOK_USFM = "BookUSFM";
public const string CHAPTER_USFM = "ChapterUSFM";
public const string CHAPTER_USX = "ChapterUSX";
public const string SETTINGS = "Settings";
public const string VERSE_USFM = "VerseUSFM";
}
53 changes: 53 additions & 0 deletions c-sharp/Projects/ProjectSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace Paranext.DataProvider.Projects;

public sealed class ProjectSettings
{
public const string PB_BOOKS_PRESENT = "platformScripture.booksPresent";
public const string PT_BOOKS_PRESENT = "BooksPresent";

public const string PB_FULL_NAME = "platform.fullName";
public const string PT_FULL_NAME = "FullName";

public const string PB_LANGUAGE = "platform.language";
public const string PT_LANGUAGE = "Language";

public const string PB_VERSIFICATION = "platformScripture.versification";
public const string PT_VERSIFICATION = "Versification";

// Make sure this dictionary gets updated whenever new settings are added
private static readonly Dictionary<string, string> s_platformBibleToParatextSettingsNames =
new()
{
{ PB_BOOKS_PRESENT, PT_BOOKS_PRESENT },
{ PB_FULL_NAME, PT_FULL_NAME },
{ PB_LANGUAGE, PT_LANGUAGE },
{ PB_VERSIFICATION, PT_VERSIFICATION },
};

private static readonly Dictionary<string, string> s_paratextToPlatformBibleSettingsNames =
s_platformBibleToParatextSettingsNames.ToDictionary((i) => i.Value, (i) => i.Key);

/// <summary>
/// Convert project setting names from Platform.Bible terminology to Paratext terminology
/// </summary>
/// <param name="pbSettingName">Setting name in Platform.Bible terminology</param>
/// <returns>Setting name in Paratext terminology if a mapping exists</returns>
public static string? GetParatextSettingNameFromPlatformBibleSettingName(string pbSettingName)
{
return s_platformBibleToParatextSettingsNames.TryGetValue(pbSettingName, out string? retVal)
? retVal
: null;
}

/// <summary>
/// Convert project setting names from Paratext terminology to Platform.Bible terminology
/// </summary>
/// <param name="ptSettingName">Setting name in Paratext terminology</param>
/// <returns>Setting name in Platform.Bible terminology if a mapping exists</returns>
public static string? GetPlatformBibleSettingNameFromParatextSettingName(string ptSettingName)
{
return s_paratextToPlatformBibleSettingsNames.TryGetValue(ptSettingName, out string? retVal)
? retVal
: null;
}
}
Loading
Loading