Skip to content

Commit

Permalink
merge: dev into master
Browse files Browse the repository at this point in the history
  • Loading branch information
arildm committed Jan 20, 2025
2 parents 0124962 + 2eda9b2 commit 2cbcc4e
Show file tree
Hide file tree
Showing 87 changed files with 1,664 additions and 1,927 deletions.
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@

## [Unreleased]

## [9.8.0] - 2025-01-20

### Added

- Sidebar: Collapse and expand attribute sections [#199](https://github.com/spraakbanken/korp-frontend/issues/199)
- Error messages from backend show up in the GUI [#97](https://github.com/spraakbanken/korp-frontend/issues/97)
- Catch unhandled errors and show in dialog [#419](https://github.com/spraakbanken/korp-frontend/issues/419)
- Save searches from extended mode [#118](https://github.com/spraakbanken/korp-frontend/issues/118)

### Changed

- 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`
- Util function `httpConfAddMethodFetch` was renamed to `selectHttpMethod`
- Util functions `httpConfAddMethod` and `httpConfAddMethodAngular` were removed
- Utilities for `JQuery.ajax` usage are removed (`.progress` handler, `AjaxSettings` type)
- The members of the `ProgressReport` type returned by `calcProgress()` have been renamed from `{struct, stats, total_results}` to `{data, percent, hits}`
- The `makeRequest` methods of the `*Proxy` classes now return native `Promise`

### Fixed

- No map for advanced CQP expressions that CQPParser does not recognize [#212](https://github.com/spraakbanken/korp-frontend/issues/212)
- Missing lemgrams in autocomplete [#416](https://github.com/spraakbanken/korp-frontend/issues/416)
- The response JSON download button now handles POST and logged-in requests, and has been moved into each corresponding result tab [#417](https://github.com/spraakbanken/korp-frontend/issues/417)
- Limit search history selector width [#415](https://github.com/spraakbanken/korp-frontend/issues/415)
- Search not triggered when choosing simple lemgram search from search history [#152](https://github.com/spraakbanken/korp-frontend/issues/152)

## [9.7.2] - 2024-12-09

### Added
Expand Down Expand Up @@ -308,6 +336,7 @@
- Lots of bug fixes for the sidebar

[unreleased]: https://github.com/spraakbanken/korp-frontend/compare/master...dev
[9.8.0]: https://github.com/spraakbanken/korp-frontend/releases/tag/v9.8.0
[9.7.2]: https://github.com/spraakbanken/korp-frontend/releases/tag/v9.7.2
[9.7.1]: https://github.com/spraakbanken/korp-frontend/releases/tag/v9.7.1
[9.7.0]: https://github.com/spraakbanken/korp-frontend/releases/tag/v9.7.0
Expand Down
2 changes: 1 addition & 1 deletion app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h2>You need JavaScript to run Korp.</h2>
</noscript>
<div id="preload"></div>
<div id="main" style="opacity: 0; display: none">
<header></header>
<app-header></app-header>
<div class="px-3 pb-3" id="content">
<searchtabs></searchtabs>
<frontpage></frontpage>
Expand Down
3 changes: 0 additions & 3 deletions app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ require("rickshaw/rickshaw.css")

require("leaflet/dist/leaflet.css")
require("leaflet.markercluster/dist/MarkerCluster.css")
require("components-jqueryui/themes/smoothness/jquery-ui.min.css")
require("./styles/_bootstrap-custom.scss")

require("./styles/tailwind.scss")
Expand Down Expand Up @@ -68,6 +67,4 @@ require("./lib/jquery.tooltip.pack.js")
require("./scripts/main")
require("./scripts/app")

require("./scripts/video_controllers.js")
require("./scripts/backend/struct-service")
require("./scripts/matomo")
2 changes: 1 addition & 1 deletion app/markup/about.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="modal-header">
<h2>Korp version 9.7.2</h2>
<h2>Korp version 9.8.0</h2>
<span ng-click="clickX()" class="close-x">×</span>

</div>
Expand Down
34 changes: 31 additions & 3 deletions app/scripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { initLocales } from "@/data_init"
import { RootScope } from "@/root-scope.types"
import { Folder } from "./settings/config.types"
import { CorpusTransformed } from "./settings/config-transformed.types"
import { html } from "@/util"
import { getService, html } from "@/util"
import { loc, locObj } from "@/i18n"
import "@/components/header"
import "@/components/app-header"
import "@/components/searchtabs"
import "@/components/frontpage"
import "@/components/results"
Expand All @@ -31,6 +31,32 @@ import { LocationService } from "./urlparams"
import { LocLangMap } from "@/i18n/types"
import { getAllCorporaInFolders } from "./components/corpus-chooser/util"

// Catch unhandled exceptions within Angular, see https://docs.angularjs.org/api/ng/service/$exceptionHandler
korpApp.factory("$exceptionHandler", [
function () {
return (exception: Error) => {
// Cannot inject services normally here, because it creates circular dependencies
const $uibModal = getService("$uibModal")
const $rootScope = getService("$rootScope")

const scope = $rootScope.$new() as IScope & { message: string }
scope.message = String(exception)

const modal = $uibModal.open({
template: html`<div class="modal-body">
<korp-error message="{{message}}"></korp-error>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="$close()">{{'modal_close' | loc:$root.lang}}</button>
</div>`,
scope: scope,
})
// Dismissing the modal rejects the `result` promise. Catch it to avoid a "Possibly unhandled rejection" error.
modal.result.catch(() => {})
}
},
])

// load all custom components
let customComponents: Record<string, IComponentOptions> = {}

Expand Down Expand Up @@ -286,9 +312,11 @@ korpApp.run([
</div>`,
scope: s,
size: "md",
backdrop: "static",
// Prevent backdrop click if not resolvable
backdrop: resolvable || "static",
keyboard: false,
})
modal.result.catch(() => onClose?.())

s.translations = translations

Expand Down
47 changes: 47 additions & 0 deletions app/scripts/backend/attr-values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/** @format */
import memoize from "lodash/memoize"
import { AttrValuesResponseDeep, AttrValuesResponseFlat, RecursiveRecord } from "./types/attr-values"
import { korpRequest } from "./common"

/** Find which unique values occur and count them. */
export const countAttrValues: (
corpora: string[],
attrs: string[],
/** Attributes whose values should be split by "|" */
split?: string[]
) => Promise<RecursiveRecord<number>> = memoize(
async (corpora, attrs, split = []) => {
// Join attributes as a hierarchical path that shapes the response
const attributePath = attrs.join(">")
const data = (await korpRequest("attr_values", {
corpus: corpora.join(","),
attr: attributePath,
count: true,
per_corpus: false,
split: split.join(",") || undefined,
})) as AttrValuesResponseDeep
return data.combined[attributePath]
},
// Memoize based on the values of all arguments
(...args) => JSON.stringify(args)
)

/** Find which unique values occur for a given attribute. */
export const getAttrValues: (
corpora: string[],
attr: string,
/** Whether values should be split by "|" */
split?: boolean
) => Promise<string[]> = memoize(
async (corpora, attr, split) => {
const data = (await korpRequest("attr_values", {
corpus: corpora.join(","),
attr: attr,
per_corpus: false,
split: split ? attr : undefined,
})) as AttrValuesResponseFlat
return data.combined[attr]
},
// Memoize based on the values of all arguments
(...args) => JSON.stringify(args)
)
56 changes: 11 additions & 45 deletions app/scripts/backend/backend.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,13 @@
/** @format */
import _ from "lodash"
import { getAuthorizationHeader } from "@/components/auth/auth"
import { KorpResponse, WithinParameters } from "@/backend/types"
import { SavedSearch } from "@/local-storage"
import settings from "@/settings"
import { httpConfAddMethodFetch } from "@/util"
import { KorpStatsParams, KorpStatsResponse, normalizeStatsData } from "@/backend/stats-proxy"
import { normalizeStatsData } from "@/backend/stats-proxy"
import { MapResult, parseMapData } from "@/map_services"
import { KorpQueryResponse } from "@/backend/kwic-proxy"

type KorpLoglikeResponse = {
/** Log-likelihood average. */
average: number
/** Log-likelihood values. */
loglike: Record<string, number>
/** Absolute frequency for the values in set 1. */
set1: Record<string, number>
/** Absolute frequency for the values in set 2. */
set2: Record<string, number>
}
import { korpRequest } from "./common"
import { WithinParameters } from "./types"
import { QueryResponse } from "./types/query"
import { CountParams } from "./types/count"

export type CompareResult = [CompareTables, number, SavedSearch, SavedSearch, string[]]

Expand Down Expand Up @@ -52,16 +41,6 @@ export type MapRequestResult = {

type MapAttribute = { label: string; corpora: string[] }

async function korpRequest<T extends Record<string, any> = {}, P extends Record<string, any> = {}>(
endpoint: string,
params: P
): Promise<KorpResponse<T>> {
const { url, request } = httpConfAddMethodFetch(settings.korp_backend_url + "/" + endpoint, params)
request.headers = { ...request.headers, ...getAuthorizationHeader() }
const response = await fetch(url, request)
return (await response.json()) as KorpResponse<T>
}

/** Note: since this is using native Promise, we must use it with something like $q or $scope.$apply for AngularJS to react when they resolve. */
export async function requestCompare(
cmpObj1: SavedSearch,
Expand All @@ -86,17 +65,12 @@ export async function requestCompare(
set1_cqp: cmpObj1.cqp,
set2_corpus: corpora2.join(",").toUpperCase(),
set2_cqp: cmpObj2.cqp,
max: "50",
max: 50,
split,
top,
}

const data = await korpRequest<KorpLoglikeResponse>("loglike", params)

if ("ERROR" in data) {
// TODO Create a KorpBackendError which could be displayed nicely
throw new Error(data.ERROR.value)
}
const data = await korpRequest("loglike", params)

const objs: CompareItemRaw[] = _.map(data.loglike, (value, key) => ({
value: key,
Expand Down Expand Up @@ -134,7 +108,7 @@ export async function requestMapData(
attribute: MapAttribute,
relative?: boolean
): Promise<MapRequestResult> {
const params: KorpStatsParams = {
const params: CountParams = {
group_by_struct: attribute.label,
cqp,
corpus: attribute.corpora.join(","),
Expand All @@ -146,22 +120,14 @@ export async function requestMapData(

Object.keys(cqpExprs).map((cqp, i) => (params[`subcqp${i}`] = cqp))

const data = await korpRequest<KorpStatsResponse>("count", params)

if ("ERROR" in data) {
// TODO Create a KorpBackendError which could be displayed nicely
throw new Error(data.ERROR.value)
}
const data = await korpRequest("count", params)

const normalizedData = normalizeStatsData(data) as any // TODO Type correctly
let result = parseMapData(normalizedData, cqp, cqpExprs)
return { corpora: attribute.corpora, cqp, within, data: result, attribute }
}

export async function getDataForReadingMode(
inputCorpus: string,
textId: string
): Promise<KorpResponse<KorpQueryResponse>> {
export async function getDataForReadingMode(inputCorpus: string, textId: string): Promise<QueryResponse> {
const corpus = inputCorpus.toUpperCase()
const corpusSettings = settings.corpusListing.get(inputCorpus)

Expand All @@ -184,5 +150,5 @@ export async function getDataForReadingMode(
end: 0,
}

return korpRequest<KorpQueryResponse>("query", params)
return korpRequest("query", params)
}
Loading

0 comments on commit 2cbcc4e

Please sign in to comment.