From 4006181517c0f52c0e9779239522adaf25c6a81b Mon Sep 17 00:00:00 2001 From: Arild Matsson Date: Thu, 12 Dec 2024 16:37:30 +0100 Subject: [PATCH] Replace lexicons service with plain async functions --- CHANGELOG.md | 1 + app/scripts/backend/backend.ts | 2 +- app/scripts/backend/common.ts | 5 +- app/scripts/backend/lexicons.ts | 84 ++++++++-------------- app/scripts/backend/types/index.ts | 5 ++ app/scripts/backend/types/lemgram-count.ts | 13 ++++ app/scripts/components/autoc.ts | 74 +++++++++---------- app/scripts/components/simple-search.ts | 8 +-- 8 files changed, 88 insertions(+), 104 deletions(-) create mode 100644 app/scripts/backend/types/lemgram-count.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e261298..501edec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - The `corpus_config_url` setting is replaced by `get_corpus_ids`, see [doc/frontend_devel.md](./doc/frontend_devel.md) - The `structService` service is replaced by non-AngularJS async functions in `@/backend/attr-values` +- The `lexicons` service is replaced by non-AngularJS async functions in `@/backend/lexicons` - The `httpConfAddMethod*` util functions were refactored: - The `$.ajax` case of `httpConfAddMethod` was extracted into `ajaxConfAddMethod` - `httpConfAddMethodFetch` was renamed to `fetchConfAddMethod` diff --git a/app/scripts/backend/backend.ts b/app/scripts/backend/backend.ts index e8068ec8..f1d575d8 100644 --- a/app/scripts/backend/backend.ts +++ b/app/scripts/backend/backend.ts @@ -5,7 +5,7 @@ import settings from "@/settings" import { normalizeStatsData } from "@/backend/stats-proxy" import { MapResult, parseMapData } from "@/map_services" import { korpRequest } from "./common" -import { Response, WithinParameters } from "./types" +import { WithinParameters } from "./types" import { QueryResponse } from "./types/query" import { CountParams } from "./types/count" diff --git a/app/scripts/backend/common.ts b/app/scripts/backend/common.ts index 20ee11c3..15554e3d 100644 --- a/app/scripts/backend/common.ts +++ b/app/scripts/backend/common.ts @@ -2,7 +2,7 @@ import { axiosConfAddMethod } from "@/util" import { getAuthorizationHeader } from "@/components/auth/auth" import settings from "@/settings" -import { API, Response, ResponseBase } from "./types" +import { API, ErrorMessage, Response, ResponseBase } from "./types" import axios from "axios" export async function korpRequest( @@ -18,7 +18,8 @@ export async function korpRequest( const data = response.data if ("ERROR" in data) { - throw new KorpBackendError(data.ERROR.type, data.ERROR.value) + const { type, value } = data.ERROR as ErrorMessage + throw new KorpBackendError(type, value) } return data diff --git a/app/scripts/backend/lexicons.ts b/app/scripts/backend/lexicons.ts index b011e894..0efe9a8a 100644 --- a/app/scripts/backend/lexicons.ts +++ b/app/scripts/backend/lexicons.ts @@ -1,70 +1,46 @@ /** @format */ import _ from "lodash" -import angular, { IHttpService, IPromise } from "angular" -import settings from "@/settings" -import { getAuthorizationHeader } from "@/components/auth/auth" -import { httpConfAddMethod } from "@/util" -import { getLemgrams, getSenseId, getSenses, getSwefnFrame } from "@/karp" +import * as karp from "@/karp" +import { korpRequest } from "./common" -export type LexiconsService = { - relatedWordSearch: (lemgram: string) => IPromise - getLemgrams: (wf: string, resources: string[], corporaIDs: string[]) => IPromise - getSenses: (wf: string) => IPromise -} -export type LexiconsRelatedWordsResponse = LexiconsRelatedWordsItem[] -export type LexiconsRelatedWordsItem = { +export type LexiconsRelatedWords = { label: string words: string[] } -/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Statistics/paths/~1lemgram_count/get */ -type KorpLemgramCountResponse = { - time: number - [lemgram: string]: number -} - export type LemgramCount = { lemgram: string; count: number } + export type SenseResult = { sense: string; desc: string } -angular.module("korpApp").factory("lexicons", [ - "$http", - function ($http: IHttpService): LexiconsService { - return { - async getLemgrams(wf: string, resources: string[], corporaIDs: string[]) { - const lemgrams = await getLemgrams(wf, resources).then((data) => data.hits) - if (lemgrams.length == 0) return [] +/** Look up lemgrams matching a given wordform and count them in selected corpora. */ +export async function getLemgrams(wf: string, resources: string[], corporaIDs: string[]): Promise { + const lemgrams = (await karp.getLemgrams(wf, resources)).hits + if (lemgrams.length == 0) return [] - const counts = await $http( - httpConfAddMethod({ - url: settings["korp_backend_url"] + "/lemgram_count", - method: "GET", - params: { - lemgram: lemgrams.join(","), - count: "lemgram", - corpus: corporaIDs.join(","), - }, - headers: getAuthorizationHeader(), - }) - ).then(({ data }) => _.omit(data, "time")) + const data = await korpRequest("lemgram_count", { + lemgram: lemgrams.join(","), + count: "lemgram", + corpus: corporaIDs.join(","), + }) + const counts = _.omit(data, "time") - return lemgrams.map((lemgram) => ({ lemgram, count: counts[lemgram] || 0 })) - }, + return lemgrams.map((lemgram) => ({ lemgram, count: counts[lemgram] || 0 })) +} - async getSenses(wf: string) { - const lemgrams = await getLemgrams(wf, ["saldom"]).then((data) => data.hits) - if (lemgrams.length == 0) return [] +/** Look up SALDO senses of lemgrams of a given wordform. */ +export async function getSenses(wf: string): Promise { + const lemgrams = (await karp.getLemgrams(wf, ["saldom"])).hits + if (lemgrams.length == 0) return [] - const senses = await getSenses(lemgrams).then((data) => data.hits) - return senses.map(({ senseID, primary }) => ({ sense: senseID, desc: primary })) - }, + const senses = (await karp.getSenses(lemgrams)).hits + return senses.map(({ senseID, primary }) => ({ sense: senseID, desc: primary })) +} - async relatedWordSearch(lemgram: string) { - const senses = await getSenseId(lemgram).then((data) => data.hits) - if (senses.length == 0) return [] +/** Look up SweFN frames matching a given lemgram and get their other lexical units (LUs) */ +export async function relatedWordSearch(lemgram: string): Promise { + const senses = (await karp.getSenseId(lemgram)).hits + if (senses.length == 0) return [] - const frames = await getSwefnFrame(senses).then((data) => data.hits) - return frames.map((entry) => ({ label: entry.swefnID, words: entry.LUs })) - }, - } - }, -]) + const frames = (await karp.getSwefnFrame(senses)).hits + return frames.map((entry) => ({ label: entry.swefnID, words: entry.LUs })) +} diff --git a/app/scripts/backend/types/index.ts b/app/scripts/backend/types/index.ts index 3b791d2d..554116d0 100644 --- a/app/scripts/backend/types/index.ts +++ b/app/scripts/backend/types/index.ts @@ -4,6 +4,7 @@ import { AttrValuesParams, AttrValuesResponseDeep, AttrValuesResponseFlat } from import { CorpusConfigParams, CorpusConfigResponse } from "./corpus-config" import { CorpusInfoParams, CorpusInfoResponse } from "./corpus-info" import { CountParams, CountResponse } from "./count" +import { LemgramCountParams, LemgramCountResponse } from "./lemgram-count" import { LoglikeParams, LoglikeResponse } from "./loglike" import { QueryParams, QueryResponse } from "./query" @@ -28,6 +29,10 @@ export type API = { params: CountParams response: CountResponse } + lemgram_count: { + params: LemgramCountParams + response: LemgramCountResponse + } loglike: { params: LoglikeParams response: LoglikeResponse diff --git a/app/scripts/backend/types/lemgram-count.ts b/app/scripts/backend/types/lemgram-count.ts new file mode 100644 index 00000000..3d0d5a20 --- /dev/null +++ b/app/scripts/backend/types/lemgram-count.ts @@ -0,0 +1,13 @@ +/** @format */ +/** @see https://ws.spraakbanken.gu.se/docs/korp#tag/Statistics/paths/~1lemgram_count/get */ + +export type LemgramCountParams = { + lemgram: string + count: "lemgram" + corpus: string +} + +export type LemgramCountResponse = { + // The key type is given as more specific than `string` only to make TS distinguish it from "ERROR". + [lemgram: string]: number +} diff --git a/app/scripts/components/autoc.ts b/app/scripts/components/autoc.ts index 34e944cc..891cc9d1 100644 --- a/app/scripts/components/autoc.ts +++ b/app/scripts/components/autoc.ts @@ -1,10 +1,10 @@ /** @format */ import _ from "lodash" -import angular, { IController, IPromise, IQService } from "angular" +import angular, { IController, IPromise } from "angular" import settings from "@/settings" import { html, lemgramToString, saldoToString } from "@/util" import { loc } from "@/i18n" -import { LemgramCount, LexiconsService } from "@/backend/lexicons" +import { getLemgrams, getSenses, LemgramCount } from "@/backend/lexicons" import "@/directives/typeahead-click-open" type AutocController = IController & { @@ -97,9 +97,7 @@ angular.module("korpApp").component("autoc", { onChange: "&", }, controller: [ - "$q", - "lexicons", - function ($q: IQService, lexicons: LexiconsService) { + function () { const ctrl = this as AutocController ctrl.isError = false @@ -196,47 +194,39 @@ angular.module("korpApp").component("autoc", { } } - ctrl.getLemgrams = function (input: string, morphologies: string[], corpora: string[]) { - const deferred = $q.defer() - const http = lexicons.getLemgrams(input, morphologies, corpora) - http.then(function (data) { - const output: LemgramOut[] = data.map((item) => { - if (ctrl.variant === "affix") item.count = -1 - return { - ...item, - parts: ctrl.lemgramify(item.lemgram), - variant: ctrl.variant, - } - }) - output.sort((a, b) => b.count - a.count) - return deferred.resolve(output) + ctrl.getLemgrams = async (input: string, morphologies: string[], corpora: string[]) => { + const data = await getLemgrams(input, morphologies, corpora) + const output: LemgramOut[] = data.map((item) => { + if (ctrl.variant === "affix") item.count = -1 + return { + ...item, + parts: ctrl.lemgramify(item.lemgram), + variant: ctrl.variant, + } }) - return deferred.promise + output.sort((a, b) => b.count - a.count) + return output } - ctrl.getSenses = function (input: string) { - const deferred = $q.defer() - const http = lexicons.getSenses(input) - http.then(function (data) { - const output: Sense[] = data.map((item) => { - const out = { - sense: item.sense, - parts: ctrl.sensify(item.sense), - desc: item.desc ? ctrl.sensify(item.desc) : undefined, - variant: ctrl.variant, - } - return out - }) - output.sort(function (a, b) { - if (a.parts.main === b.parts.main) { - return b.parts.index.localeCompare(a.parts.index) - } else { - return a.sense.length - b.sense.length - } - }) - return deferred.resolve(output) + ctrl.getSenses = async (input: string) => { + const data = await getSenses(input) + const output: Sense[] = data.map((item) => { + const out = { + sense: item.sense, + parts: ctrl.sensify(item.sense), + desc: item.desc ? ctrl.sensify(item.desc) : undefined, + variant: ctrl.variant, + } + return out + }) + output.sort(function (a, b) { + if (a.parts.main === b.parts.main) { + return b.parts.index.localeCompare(a.parts.index) + } else { + return a.sense.length - b.sense.length + } }) - return deferred.promise + return output } }, ], diff --git a/app/scripts/components/simple-search.ts b/app/scripts/components/simple-search.ts index ec5d4e1f..94c59483 100644 --- a/app/scripts/components/simple-search.ts +++ b/app/scripts/components/simple-search.ts @@ -15,7 +15,7 @@ import "@/global-filter/global-filters" import { LocationService } from "@/urlparams" import { RootScope } from "@/root-scope.types" import { CompareSearches } from "@/services/compare-searches" -import { LexiconsRelatedWordsResponse, LexiconsService } from "@/backend/lexicons" +import { LexiconsRelatedWords, relatedWordSearch } from "@/backend/lexicons" import { SearchesService } from "@/services/searches" import { CqpSearchEvent } from "@/statemachine/types" @@ -31,7 +31,7 @@ type SimpleSearchController = IController & { isCaseInsensitive: boolean currentText?: string lemgram?: string - relatedObj?: { data: LexiconsRelatedWordsResponse; attribute: string } + relatedObj?: { data: LexiconsRelatedWords[]; attribute: string } relatedDefault: number updateSearch: () => void getCQP: () => string @@ -118,7 +118,6 @@ angular.module("korpApp").component("simpleSearch", { "$timeout", "$uibModal", "compareSearches", - "lexicons", "searches", function ( $location: LocationService, @@ -127,7 +126,6 @@ angular.module("korpApp").component("simpleSearch", { $timeout: ITimeoutService, $uibModal: ui.bootstrap.IModalService, compareSearches: CompareSearches, - lexicons: LexiconsService, searches: SearchesService ) { const ctrl = this as SimpleSearchController @@ -348,7 +346,7 @@ angular.module("korpApp").component("simpleSearch", { const saldo = attrExists("saldo") if (sense || saldo) { - lexicons.relatedWordSearch(unregescape(search.val)).then((data) => { + relatedWordSearch(unregescape(search.val)).then((data) => { // Lower some nasty words if (data.length >= 2 && data[0].label == "Excreting") { // Swap the first two elements