Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
15 changes: 6 additions & 9 deletions docs/design/designOverview.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Triggered by a new commit to the `master` branch, [a GitHub workflow](../../.git

For each NVDA API version and channel, the add-on metadata with the highest version number is written.

This transformed data is then committed by the GitHub Action to the data repository main branch.
This transformed data is then committed by the GitHub Action to the [addonstore-views](https://github.com/nvaccess/addonstore-views) main branch.

### Data views

Expand All @@ -79,11 +79,10 @@ The generated data is stored in two top-level folders:
- `views`: compatibility and latest projections as relative symlinks into `addons`.

The following projected views are available in the `views` folder.
Required transformations of the data:

- `/views/<lang>/NVDA API Version/addon-1-ID/stable.json`
- `/views/<lang>/NVDA API Version/addon-1-ID/beta.json`
- `/views/<lang>/NVDA API Version/addon-2-ID/stable.json`
The views folder is expected to have the following structure:

`/views/<lang>/<NvdaAPIVersion>/<addonId>/<channel>.json`

Notes:

Expand Down Expand Up @@ -124,16 +123,14 @@ Channel can be: all, dev, stable or beta.
- Example: <https://addonStore.nvaccess.org/en/all/latest.json>

### `GET` cacheHash

Returns a hash used for cache breaking.
This hash will change whenever new add-on data is available.
The hash should match the latest commit hash of the transformed data repository branch.
For testing purposes, the hash should match the latest commit hash of the transformed [addonstore-views](https://github.com/nvaccess/addonstore-views) branch.

- <https://addonStore.nvaccess.org/cacheHash.json>
- Example return value: `"5fcf12f"`

### Legacy
This endpoint mirrors the legacy [get.php, from the addonFiles repository](https://github.com/nvaccess/addonFiles/blob/master/get.php).

#### `addonslist` end-point

The `addonslist` parameter generates a list of list of add-ons using the same logic as the [latest end-point](#get-latest) for all channels.
Expand Down
2 changes: 1 addition & 1 deletion transform/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Transforming data to views

This repository transforms add-on metadata into an output data layout with add-on files and views.
This module transforms add-on metadata into an output data layout with add-on files and views.
The output is designed to be published from a single branch.

For each NVDA version that needs to be supported by the add-on store, an entry must be added to [`nvdaAPIVersions.json`](./nvdaAPIVersions.json).
Expand Down
22 changes: 14 additions & 8 deletions transform/docs/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,30 @@ Callers are responsible for deleting any previous output directory before runnin

The following subdirectories and files are created:

- `/addons/addon-1-ID/addonVersion/en.json`
- `/addons/addon-1-ID/addonVersion/ar.json` (when a translation exists)
- `/views/en/NVDA API Version/addon-1-ID/stable.json`
- `/views/en/NVDA API Version/addon-1-ID/beta.json`
- `/views/en/NVDA API Version/addon-2-ID/stable.json`
- `/views/ar/NVDA API Version/addon-1-ID/stable.json`
Translated add-on files:

- `/addons/addon-1-ID/<addonVersion>/en.json`
- `/addons/addon-1-ID/<addonVersion>/ar.json` (when a translation exists)
- `/addons/addon-2-ID/<addonVersion>/en.json`

Symlink end views which point to translated files:

- `/views/en/<NvdaAPIVersion>/addon-1-ID/stable.json`
- `/views/en/<NvdaAPIVersion>/addon-1-ID/beta.json`
- `/views/en/<NvdaAPIVersion>/addon-2-ID/stable.json`
- `/views/ar/<NvdaAPIVersion>addon-1-ID/stable.json`
Comment thread
SaschaCowley marked this conversation as resolved.

Examples:

- `/addons/nvdaOCR/2020.3.0/en.json`
- `/addons/nvdaOCR/1.3.5/en.json`
- `/views/en/2020.3.0/nvdaOCR/stable.json`

Where `NVDA API Version` may be:

- `2022.1.0`: A major release.
- `2022.1.3`: A patch release.

The system differentiates patch releases from major releases to cater to the (very unlikely) event of requireing a breaking change or introduction to the NVDA add-on API.
The system differentiates patch releases from major releases to cater to the (very unlikely) event of requiring a breaking change or introduction to the NVDA add-on API.

## Output file data

Expand Down
48 changes: 28 additions & 20 deletions transform/src/transform/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,14 @@ def _getTranslatedAddonData(
) -> dict[str, object]:
translatedAddonData: dict[str, object] = baseAddonData.copy()
langWithoutLocale = lang.split("_")[0]
if lang in addonTranslations:
translation = addonTranslations[lang]
if "displayName" in translation:
translatedAddonData["displayName"] = translation["displayName"]
if "description" in translation:
translatedAddonData["description"] = translation["description"]
elif langWithoutLocale in addonTranslations:
translation = addonTranslations[langWithoutLocale]
if "displayName" in translation:
translatedAddonData["displayName"] = translation["displayName"]
if "description" in translation:
translatedAddonData["description"] = translation["description"]
translation = addonTranslations.get(lang)
if translation is None:
translation = addonTranslations.get(langWithoutLocale)
if translation is not None:
if (translatedDisplayName := translation.get("displayName")) is not None:
translatedAddonData["displayName"] = translatedDisplayName
if (translatedDescription := translation.get("description")) is not None:
translatedAddonData["description"] = translatedDescription
return translatedAddonData

def _createRelativeFileSymlink(*, targetPath: str, symlinkPath: str) -> None:
Expand All @@ -144,7 +140,7 @@ def _createRelativeFileSymlink(*, targetPath: str, symlinkPath: str) -> None:
del addonData["translations"]

addonVersion = str(addon.addonVersion)
translatedAddonDirPath = f"{addonDir}/addons/{addonName}/{addonVersion}"
translatedAddonDirPath = os.path.join(addonDir, "addons", addonName, addonVersion)
if translatedAddonDirPath not in writtenTranslatedAddonPath:
writtenTranslatedAddonPath.add(translatedAddonDirPath)
addonTranslations: dict[str, dict[str, str]] = {
Expand All @@ -154,7 +150,7 @@ def _createRelativeFileSymlink(*, targetPath: str, symlinkPath: str) -> None:
for lang in translatedLanguages:
translatedAddonData = _getTranslatedAddonData(addonData, addonTranslations, lang)
Path(translatedAddonDirPath).mkdir(parents=True, exist_ok=True)
with open(f"{translatedAddonDirPath}/{lang}.json", "w") as newAddonFile:
with open(os.path.join(translatedAddonDirPath, f"{lang}.json"), "w") as newAddonFile:
validateJson(translatedAddonData, JSONSchemaPaths.ADDON_DATA)
json.dump(translatedAddonData, newAddonFile)

Expand All @@ -168,9 +164,14 @@ def _createRelativeFileSymlink(*, targetPath: str, symlinkPath: str) -> None:
else:
targetLanguage = "en"

translatedAddonPath = f"{translatedAddonDirPath}/{targetLanguage}.json"
versionedViewPath = (
f"{addonDir}/views/{lang}/{str(nvdaAPIVersion)}/{addonName}/{channel}.json"
translatedAddonPath = os.path.join(translatedAddonDirPath, f"{targetLanguage}.json")
versionedViewPath = os.path.join(
addonDir,
"views",
lang,
str(nvdaAPIVersion),
addonName,
f"{channel}.json",
)
_createRelativeFileSymlink(targetPath=translatedAddonPath, symlinkPath=versionedViewPath)

Expand All @@ -192,8 +193,15 @@ def _createRelativeFileSymlink(*, targetPath: str, symlinkPath: str) -> None:
else:
targetLanguage = "en"

translatedAddonPath = f"{translatedAddonDirPath}/{targetLanguage}.json"
latestViewPath = f"{addonDir}/views/{lang}/latest/{addonName}/{channel}.json"
translatedAddonPath = os.path.join(translatedAddonDirPath, f"{targetLanguage}.json")
latestViewPath = os.path.join(
addonDir,
"views",
lang,
"latest",
addonName,
f"{channel}.json",
)
_createRelativeFileSymlink(targetPath=translatedAddonPath, symlinkPath=latestViewPath)


Expand All @@ -203,7 +211,7 @@ def readAddons(addonDir: str) -> Iterable[Addon]:
Works as a generator to minimize memory usage, as such, each use of iteration should call readAddons.
Skips addons and logs errors if the naming schema or json schema do not match what is expected.
"""
for fileName in glob.glob(f"{addonDir}/**/*.json"):
for fileName in glob.glob(os.path.join(addonDir, "**", "*.json")):
with open(fileName, "r", encoding="utf-8") as addonFile:
addonData = json.load(addonFile)
try:
Expand Down
Loading