Skip to content

Commit

Permalink
Download DBL resources data provider (#1282)
Browse files Browse the repository at this point in the history
  • Loading branch information
rolfheij-sil authored Nov 13, 2024
2 parents 1a164e5 + 26fe157 commit e78e5f7
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 14 deletions.
8 changes: 8 additions & 0 deletions assets/localization/en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{
"%about_versionLabel_format%": "Version: {version}",
"%about_licenseLabel_format%": "License: {license}",
"%downloadResources_errorRegistrationInvalid%": "User registration is not valid. Cannot retrieve resources from DBL.",
"%downloadResources_errorInstallResource_resourceNotFound%": "Resource not available from DBL",
"%downloadResources_errorInstallResource_resourceAlreadyInstalled%": "Resource is already installed and up to date. Installation skipped.",
"%downloadResources_errorInstallResource_installationFailed%": "Resource cannot be found after attempted installation. Installation failed.",
"%downloadResources_errorUninstallResource_resourceNotFound%": "Resource not found on list of DBL resources.",
"%downloadResources_errorUninstallResource_resourceNotInstalled%": "Resource is not currently installed, so it can't be removed.",
"%downloadResources_errorUninstallResource_localResourceNotFound%": "Resource cannot be located, so it can't be removed.",
"%downloadResources_errorUninstallResource_localResourceStillPresent%": "Resource is still present. Removing failed.",
"%downloadUpdateProjectTab_aria_downloadable%": "downloadable projects",
"%downloadUpdateProjectTab_aria_downloaded%": "downloaded projects",
"%downloadUpdateProjectTab_button_delete%": "Delete",
Expand Down
3 changes: 3 additions & 0 deletions c-sharp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Paranext.DataProvider.Checks;
using Paranext.DataProvider.NetworkObjects;
using Paranext.DataProvider.Projects;
using Paranext.DataProvider.Projects.DigitalBibleLibrary;
using Paranext.DataProvider.Services;
using Paranext.DataProvider.Users;
using Paratext.Data;
Expand Down Expand Up @@ -32,10 +33,12 @@ public static async Task Main()

var paratextFactory = new ParatextProjectDataProviderFactory(papi, paratextProjects);
var checkRunner = new CheckRunner(papi);
var dblResources = new DblResourcesDataProvider(papi);
var paratextRegistrationService = new ParatextRegistrationService(papi);
await Task.WhenAll(
paratextFactory.InitializeAsync(),
checkRunner.RegisterDataProviderAsync(),
dblResources.RegisterDataProviderAsync(),
paratextRegistrationService.InitializeAsync()
);

Expand Down
228 changes: 228 additions & 0 deletions c-sharp/Projects/DigitalBibleLibrary/DblDownloadableDataProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
using System.Text.Json;
using Paranext.DataProvider.Services;
using Paratext.Data;
using Paratext.Data.Archiving;
using Paratext.Data.Users;

namespace Paranext.DataProvider.Projects.DigitalBibleLibrary;

/// <summary>
/// Data provider that can install, update and uninstall DBL (Digital Bible Library) resources
/// </summary>
internal class DblResourcesDataProvider(PapiClient papiClient)
: NetworkObjects.DataProvider("paratextBibleDownloadResources.dblResourcesProvider", papiClient)
{
#region Internal classes

private class DblResourceData(
string DblEntryUid,
string DisplayName,
string FullName,
string BestLanguageName,
long Size,
bool Installed,
bool UpdateAvailable
)
{
public string DblEntryUid { get; set; } = DblEntryUid;
public string DisplayName { get; set; } = DisplayName;
public string FullName { get; set; } = FullName;
public string BestLanguageName { get; set; } = BestLanguageName;
public long Size { get; set; } = Size;
public bool Installed { get; set; } = Installed;
public bool UpdateAvailable { get; set; } = UpdateAvailable;
}

#endregion

#region Consts and member variables

public const string DBL_RESOURCES = "DblResources";

private List<InstallableResource> _resources = [];

#endregion

#region DataProvider methods

protected override List<(string functionName, Delegate function)> GetFunctions()
{
return
[
("getDblResources", GetDblResources),
("installDblResource", InstallDblResource),
("uninstallDblResource", UninstallDblResource),
];
}

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

#endregion

#region Private properties and methods

/// <summary>
/// Fetch list DBL resources
/// </summary>
/// <returns>
/// A list of all available resources on the DBL, along with information about their
/// installation status on the local machine
/// </returns>
private void FetchAvailableDBLResources()
{
_resources = InstallableDBLResource.GetInstallableDBLResources(
RegistrationInfo.DefaultUser,
new DBLRESTClientFactory(),
new DblProjectDeleter(),
new DblMigrationOperations(),
new DblResourcePasswordProvider()
);
}

/// <summary>
/// Check user registration and, if registration is valid, return a list of information about
/// available DBL resources
/// </summary>
/// <returns>
/// A list with some information about all available resources on the DBL, for the purpose of
/// presenting the resources and their installation status on the front-end
/// </returns>
private List<DblResourceData> GetDblResources(JsonElement _ignore)
{
if (!RegistrationInfo.DefaultUser.IsValid)
{
throw new Exception(
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorRegistrationInvalid%",
$"User registration is not valid. Cannot retrieve resources from DBL."
)
);
}

FetchAvailableDBLResources();

return _resources
.Select(resource => new DblResourceData(
resource.DBLEntryUid.Id,
resource.DisplayName,
resource.FullName,
resource.BestLanguageName,
resource.Size,
resource.Installed,
resource.IsNewerThanCurrentlyInstalled()
))
.ToList();
}

private void FindResource(
string dblEntryUid,
string messageToThrowIfNotFound,
out InstallableResource resource
)
{
resource =
_resources?.FirstOrDefault(r => r.DBLEntryUid.Id == dblEntryUid)
?? throw new Exception(messageToThrowIfNotFound);
}

/// <summary>
/// Try to install DBL resource with specified DBL id
/// </summary>
private void InstallDblResource(string DBLEntryUid)
{
FindResource(
DBLEntryUid,
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorInstallResource_resourceNotFound%",
$"Resource not available from DBL."
),
out var installableResource
);

if (installableResource.Installed && !installableResource.IsNewerThanCurrentlyInstalled())
throw new Exception(
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorInstallResource_resourceAlreadyInstalled%",
$"Resource is already installed and up to date. Installation skipped."
)
);

// Note that we don't get any info telling if the installation succeeded or failed
installableResource.Install();

ScrTextCollection.RefreshScrTexts();

if (!ScrTextCollection.IsPresent(installableResource.ExistingScrText))
throw new Exception(
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorInstallResource_installationFailed%",
$"Resource cannot be found after attempted installation. Installation failed."
)
);

SendDataUpdateEvent(DBL_RESOURCES, "DBL resources data updated");
}

/// <summary>
/// Try to uninstall DBL resource with specified DBL id
/// </summary>
private void UninstallDblResource(string DBLEntryUid)
{
FindResource(
DBLEntryUid,
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorUninstallResource_resourceNotFound%",
$"Resource not found on list of DBL resources."
),
out var installableResource
);

if (!installableResource.Installed)
throw new Exception(
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorUninstallResource_resourceNotInstalled%",
$"Resource is not currently installed, so it can't be removed."
)
);

var objectToBeDeleted = installableResource.ExistingScrText;

var isPresent = ScrTextCollection.IsPresent(objectToBeDeleted);
if (!isPresent)
throw new Exception(
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorUninstallResource_localResourceNotFound%",
$"Resource cannot be located, so it can't be removed."
)
);

// Note that we don't get any info telling if uninstalling succeeded or failed
ScrTextCollection.DeleteProject(objectToBeDeleted);

ScrTextCollection.RefreshScrTexts();

isPresent = ScrTextCollection.IsPresent(objectToBeDeleted);
if (isPresent)
throw new Exception(
LocalizationService.GetLocalizedString(
PapiClient,
"%downloadResources_errorUninstallResource_localResourceStillPresent%",
$"Resource is still present. Removing failed."
)
);

SendDataUpdateEvent(DBL_RESOURCES, "DBL resources data updated");
}

#endregion
}
36 changes: 36 additions & 0 deletions c-sharp/Projects/DigitalBibleLibrary/DblMigrationOperations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Paratext.Data;
using Paratext.Data.Archiving;
using Paratext.Data.Languages;

namespace Paranext.DataProvider.Projects.DigitalBibleLibrary;

public class DblMigrationOperations : IMigrationOperations
{
/// <summary>
/// See `Paratext.Migration.PTMigration.Migrate` for steps involved in migrating data
/// </summary>
public UnsupportedReason MigrateProjectIfNeeded(ScrText scrText)
{
return scrText.NeedsMigration
? UnsupportedReason.CannotUpgrade
: UnsupportedReason.Supported;
}

/// <summary>
/// Adapted from `Paratext.Migration.MigrateLanguage`
/// </summary>
public LanguageId DetermineBestLangIdToUseForResource(
string ldmlLanguageId,
string dblLanguageId
)
{
LanguageId ethnologueDblLanguageId = LanguageId.FromEthnologueCode(dblLanguageId);
if (string.IsNullOrEmpty(ldmlLanguageId))
return ethnologueDblLanguageId;

LanguageId ethnologueLdmlLanguageId = LanguageId.FromEthnologueCode(ldmlLanguageId);
if (ethnologueLdmlLanguageId.Code == ethnologueDblLanguageId.Code)
return ethnologueLdmlLanguageId;
return ethnologueDblLanguageId;
}
}
20 changes: 20 additions & 0 deletions c-sharp/Projects/DigitalBibleLibrary/DblProjectDeleter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Paratext.Data;
using Paratext.Data.Archiving;
using Paratext.Data.ProjectComments;
using Paratext.Data.Repository;

namespace Paranext.DataProvider.Projects.DigitalBibleLibrary;

/// <summary>
/// Adapted from `Paratext.ProjectMenu.DeleteProjectForm.DeleteProject`
/// </summary>
public class DblProjectDeleter : IProjectDeleter
{
public void DeleteProject(ScrText scrText)
{
CommentManager.RemoveCommentManager(scrText);
VersioningManager.RemoveVersionedText(scrText);
if (!scrText.Settings.IsMarbleResource)
ScrTextCollection.DeleteProject(scrText);
}
}
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"biblionexus",
"camelcase",
"consts",
"deleter",
"deuterocanon",
"dockbox",
"electronmon",
Expand Down
7 changes: 0 additions & 7 deletions src/extension-host/data/menu.data.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@
"platform.helpMisc": { "column": "platform.help", "order": 2 }
},
"items": [
{
"label": "%mainMenu_downloadInstallResources%",
"localizeNotes": "Application main menu > Project > Download/Install Resources",
"group": "platform.projectResources",
"order": 1,
"command": "platform.downloadAndInstallResources"
},
{
"label": "%mainMenu_settings%",
"localizeNotes": "Application main menu > Project > Settings",
Expand Down
7 changes: 0 additions & 7 deletions src/renderer/components/platform-bible-menu.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,6 @@ const supportAndDevelopmentMenuLayout: LocalizedMainMenu = {
'paratext.helpSubgroup': { menuItem: 'paratext.helpRoot', order: 1 },
},
items: [
{
label: 'Download/Install Resources',
localizeNotes: 'Main application menu > Paratext column > Download/Install Resources',
group: 'paratext.sendReceive',
order: 1,
command: 'platform.downloadAndInstallResources',
},
{
label: 'Open Project...',
tooltip: 'Open project or resource text(s)',
Expand Down

0 comments on commit e78e5f7

Please sign in to comment.