Skip to content

Commit

Permalink
Add utils for ranking and finding labels (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls authored Oct 2, 2024
1 parent 3552e49 commit 37e7773
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 0 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Add utils for ranking and finding labels

### Deprecated

### Removed

### Security
3 changes: 3 additions & 0 deletions arches_vue_utils/src/arches_vue_utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const PREF_LABEL = "prefLabel";
export const ALT_LABEL = "altLabel";
export const HIDDEN_LABEL = "hiddenLabel";
16 changes: 16 additions & 0 deletions arches_vue_utils/src/arches_vue_utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,19 @@ export interface Language {
name: string;
scope: string;
}

export interface Label {
value: string;
language_id: string;
valuetype_id: string;
}

export interface WithLabels {
labels: Label[];
}

export interface WithValues {
values: Label[];
}

export type Labellable = WithLabels | WithValues;
92 changes: 92 additions & 0 deletions arches_vue_utils/src/arches_vue_utils/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
ALT_LABEL,
HIDDEN_LABEL,
PREF_LABEL,
} from "@/arches_vue_utils/constants.ts";
import { getItemLabel, rankLabel } from "@/arches_vue_utils/utils.ts";

import type { Label } from "@/arches_vue_utils/types";

// Test utils
function asLabel(valuetype_id: string, language_id: string): Label {
return {
value: "arbitrary",
valuetype_id,
language_id,
};
}

const systemLanguageCode = "en-ZA"; // arbitrary

describe("rankLabel() util", () => {
const rank = (
valuetype_id: string,
labelLanguageCode: string,
desiredLanguageCode: string,
) =>
rankLabel(
asLabel(valuetype_id, labelLanguageCode),
desiredLanguageCode,
systemLanguageCode,
);

// Test cases inspired from python module
it("Prefers explicit region", () => {
expect(rank(PREF_LABEL, "fr-CA", "fr-CA")).toBeGreaterThan(
rank(PREF_LABEL, "fr", "fr-CA"),
);
});
it("Prefers pref over alt", () => {
expect(rank(PREF_LABEL, "fr", "fr-CA")).toBeGreaterThan(
rank(ALT_LABEL, "fr", "fr-CA"),
);
});
it("Prefers alt over hidden", () => {
expect(rank(ALT_LABEL, "fr", "fr-CA")).toBeGreaterThan(
rank(HIDDEN_LABEL, "fr", "fr-CA"),
);
});
it("Prefers alt label in system language to anything else", () => {
expect(rank(ALT_LABEL, systemLanguageCode, "en")).toBeGreaterThan(
rank(PREF_LABEL, "de", "en"),
);
});
it("Prefers region-insensitive match in system language", () => {
expect(rank(PREF_LABEL, "en", "de")).toBeGreaterThan(
rank(PREF_LABEL, "fr", "de"),
);
});
});

describe("getItemLabel() util", () => {
it("Errors if no labels", () => {
expect(() =>
getItemLabel(
{ labels: [] },
systemLanguageCode,
systemLanguageCode,
),
).toThrow();
expect(() =>
getItemLabel(
{ values: [] },
systemLanguageCode,
systemLanguageCode,
),
).toThrow();
});
it("Falls back to system language", () => {
expect(
getItemLabel(
{
labels: [
asLabel(PREF_LABEL, "de"),
asLabel(PREF_LABEL, systemLanguageCode),
],
},
"fr",
systemLanguageCode,
).language_id,
).toEqual(systemLanguageCode);
});
});
64 changes: 64 additions & 0 deletions arches_vue_utils/src/arches_vue_utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ALT_LABEL, PREF_LABEL } from "@/arches_vue_utils/constants.ts";

import type {
Label,
Labellable,
WithLabels,
WithValues,
} from "@/arches_vue_utils/types";

/* Port of rank_label in arches.app.utils.i18n python module */
export const rankLabel = (
label: Label,
preferredLanguageCode: string,
systemLanguageCode: string,
): number => {
let rank = 1;
if (label.valuetype_id === PREF_LABEL) {
rank = 10;
} else if (label.valuetype_id === ALT_LABEL) {
rank = 4;
}

// Some arches deployments may not have standardized capitalizations.
const labelLanguageFull = label.language_id.toLowerCase();
const labelLanguageNoRegion = label.language_id
.split(/[-_]/)[0]
.toLowerCase();
const preferredLanguageFull = preferredLanguageCode.toLowerCase();
const preferredLanguageNoRegion = preferredLanguageCode
.split(/[-_]/)[0]
.toLowerCase();
const systemLanguageFull = systemLanguageCode.toLowerCase();
const systemLanguageNoRegion = systemLanguageCode
.split(/[-_]/)[0]
.toLowerCase();

if (labelLanguageFull === preferredLanguageFull) {
rank *= 10;
} else if (labelLanguageNoRegion === preferredLanguageNoRegion) {
rank *= 5;
} else if (labelLanguageFull === systemLanguageFull) {
rank *= 3;
} else if (labelLanguageNoRegion === systemLanguageNoRegion) {
rank *= 2;
}

return rank;
};

export const getItemLabel = (
item: Labellable,
preferredLanguageCode: string,
systemLanguageCode: string,
): Label => {
const labels = (item as WithLabels).labels ?? (item as WithValues).values;
if (!labels.length) {
throw new Error();
}
return labels.sort(
(a, b) =>
rankLabel(b, preferredLanguageCode, systemLanguageCode) -
rankLabel(a, preferredLanguageCode, systemLanguageCode),
)[0];
};

0 comments on commit 37e7773

Please sign in to comment.