Skip to content

Commit

Permalink
Replace lexicons service with plain async functions
Browse files Browse the repository at this point in the history
  • Loading branch information
arildm committed Dec 12, 2024
1 parent 72363ca commit 4006181
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 104 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
2 changes: 1 addition & 1 deletion app/scripts/backend/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
5 changes: 3 additions & 2 deletions app/scripts/backend/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<K extends keyof API>(
Expand All @@ -18,7 +18,8 @@ export async function korpRequest<K extends keyof API>(
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
Expand Down
84 changes: 30 additions & 54 deletions app/scripts/backend/lexicons.ts
Original file line number Diff line number Diff line change
@@ -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<LexiconsRelatedWordsResponse>
getLemgrams: (wf: string, resources: string[], corporaIDs: string[]) => IPromise<LemgramCount[]>
getSenses: (wf: string) => IPromise<SenseResult[]>
}
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<LemgramCount[]> {
const lemgrams = (await karp.getLemgrams(wf, resources)).hits
if (lemgrams.length == 0) return []

const counts = await $http<KorpLemgramCountResponse>(
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<SenseResult[]> {
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<LexiconsRelatedWords[]> {
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 }))
}
5 changes: 5 additions & 0 deletions app/scripts/backend/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -28,6 +29,10 @@ export type API = {
params: CountParams
response: CountResponse
}
lemgram_count: {
params: LemgramCountParams
response: LemgramCountResponse
}
loglike: {
params: LoglikeParams
response: LoglikeResponse
Expand Down
13 changes: 13 additions & 0 deletions app/scripts/backend/types/lemgram-count.ts
Original file line number Diff line number Diff line change
@@ -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
}
74 changes: 32 additions & 42 deletions app/scripts/components/autoc.ts
Original file line number Diff line number Diff line change
@@ -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 & {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -196,47 +194,39 @@ angular.module("korpApp").component("autoc", {
}
}

ctrl.getLemgrams = function (input: string, morphologies: string[], corpora: string[]) {
const deferred = $q.defer<LemgramOut[]>()
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<Sense[]>()
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
}
},
],
Expand Down
8 changes: 3 additions & 5 deletions app/scripts/components/simple-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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
Expand Down Expand Up @@ -118,7 +118,6 @@ angular.module("korpApp").component("simpleSearch", {
"$timeout",
"$uibModal",
"compareSearches",
"lexicons",
"searches",
function (
$location: LocationService,
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 4006181

Please sign in to comment.