Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
arildm committed Apr 23, 2024
2 parents acd2658 + 72b935f commit caa2190
Show file tree
Hide file tree
Showing 99 changed files with 2,654 additions and 1,691 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
BASE=/mink/
VITE_BACKEND_URL=https://spraakbanken2.it.gu.se/ws/mink/
VITE_AUTH_URL=https://sp.spraakbanken.gu.se/auth/
VITE_LOGOUT_URL=https://sp.spraakbanken.gu.se/Shibboleth.sso/Logout
# VITE_JWT_URL=https://sp.spraakbanken.gu.se/auth/jwt
VITE_KORP_URL=https://spraakbanken.gu.se/korp/
VITE_STRIX_URL=https://spraakbanken.gu.se/strix/
VITE_MATOMO_URL=https://spraakbanken.gu.se/stats/
Expand Down
6 changes: 5 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ module.exports = {
"eslint:recommended",
"plugin:vue/vue3-recommended",
"@vue/eslint-config-typescript",
"prettier",
"prettier", // Keep Prettier last
],
plugins: ["import"],
rules: {
"import/order": ["warn"],
},
};
26 changes: 25 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@ As this project is a user-facing application, the places in the semantic version

## [Unreleased]

## [1.5.0] (2024-04-23)

### Added

- Upload custom corpus config [#99](https://github.com/spraakbanken/mink-frontend/issues/99)
- A quick, brief resource listing view for admin [#146](https://github.com/spraakbanken/mink-frontend/issues/146)
- Group export files by folder [#95](https://github.com/spraakbanken/mink-frontend/issues/95)
- JWT request now has a 2s timeout and two retries [#124](https://github.com/spraakbanken/mink-frontend/issues/124)
- Upload an entire folder of source files [#97](https://github.com/spraakbanken/mink-frontend/issues/97)

### Changed

- Removed unused `message` param from `spin()` [#157](https://github.com/spraakbanken/mink-frontend/issues/157)
- Merged the corpus Metadata and Configuration panels/forms
- Updated dependencies

### Fixed

- Use the new Språkbanken Text logo
- Unallowed file format sometimes saved to local config [#153](https://github.com/spraakbanken/mink-frontend/issues/153)
- Make language switcher more visible [#90](https://github.com/spraakbanken/mink-frontend/issues/90)
- Show hint if parsing config fails

## [1.4.0] (2024-03-11)

### Added
Expand Down Expand Up @@ -160,7 +183,8 @@ The frontend is now open to the general public! This version allows users to:

Code changes up until this point are not documented other than in the git commit log.

[unreleased]: https://github.com/spraakbanken/mink-frontend/compare/v1.4.0...HEAD
[unreleased]: https://github.com/spraakbanken/mink-frontend/compare/v1.5.0...HEAD
[1.5.0]: https://github.com/spraakbanken/mink-frontend/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/spraakbanken/mink-frontend/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/spraakbanken/mink-frontend/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/spraakbanken/mink-frontend/compare/v1.1.0...v1.2.0
Expand Down
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ It is a [Vue 3](https://v3.vuejs.org/) app using [Vite](https://vitejs.dev/).
- Visualization of API calls:
1. Wrap async calls in [`spin`](src/spin/spin.composable.js):
```js
spin(loadCorpora(), "Loading corpora", "corpora");
spin(loadCorpora(), "corpora");
```
2. For markup that might change after the call, wrap it in [`<PendingContent>`](src/spin/PendingContent.vue) to apply animation:
```html
Expand All @@ -37,21 +37,26 @@ It is a [Vue 3](https://v3.vuejs.org/) app using [Vite](https://vitejs.dev/).

## Recommended IDE setup

[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar).
[VSCode](https://code.visualstudio.com/) with the plugins
[ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint),
[Vue](https://marketplace.visualstudio.com/items?itemName=Vue.volar) and
[Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode).

VSCode settings:

```json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll": "explicit"
},
"vetur.validation.template": false,
"editor.formatOnSave": true,
"[vue]": {
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
Expand Down
2 changes: 2 additions & 0 deletions env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
interface ImportMetaEnv {
readonly VITE_BACKEND_URL: string;
readonly VITE_AUTH_URL: string;
readonly VITE_LOGOUT_URL: string;
readonly VITE_JWT_URL?: string;
readonly VITE_KORP_URL: string;
readonly VITE_STRIX_URL: string;
readonly VITE_MATOMO_URL: string;
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mink-frontend",
"version": "1.4.0",
"version": "1.5.0",
"license": "MIT",
"scripts": {
"dev": "vite",
Expand Down Expand Up @@ -28,6 +28,7 @@
"@vueuse/core": "^10.7.2",
"autoprefixer": "^10.4.7",
"axios": "^1",
"datatransfer-files-promise": "^2.0.0",
"eslint": "^8.15.0",
"filesize": "^10.0.6",
"js-yaml": "^4.1.0",
Expand All @@ -50,6 +51,7 @@
"@types/node": "^20.10.6",
"@vue/eslint-config-typescript": "^12.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-vue": "^9.0",
"happy-dom": "^12.10.3",
"prettier": "^3.2.4",
Expand Down
2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { computed } from "vue";
import { useRoute } from "vue-router";
import { useTitle } from "@vueuse/core";
import AppHeader from "@/page/AppHeader.vue";
import api from "@/api/api";
import * as util from "@/util";
import { useAuth } from "@/auth/auth.composable";
Expand All @@ -10,7 +11,6 @@ import { useResourceStore } from "@/store/resource.store";
import MessageToasts from "@/message/MessageToasts.vue";
import usePageTitle from "@/page/title.composable";
import BreadcrumbBar from "@/page/BreadcrumbBar.vue";
import AppHeader from "./page/AppHeader.vue";
const { refreshJwt } = useAuth();
useLocale();
Expand Down
7 changes: 4 additions & 3 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
AdminModeStatusData,
CreateMetadataData,
ProgressHandler,
} from "./api.types";
} from "@/api/api.types";

/** Mink backend API client */
class MinkApi {
Expand Down Expand Up @@ -115,11 +115,11 @@ class MinkApi {
/** @see https://ws.spraakbanken.gu.se/ws/mink/api-doc#tag/Manage-Sources/operation/uploadsources */
async uploadSources(
corpusId: string,
files: FileList,
files: File[],
onProgress?: ProgressHandler,
) {
const formData = new FormData();
[...files].forEach((file) => formData.append("files[]", file));
files.forEach((file) => formData.append("files[]", file));
const response = await this.axios.put<MinkResponse>(
"upload-sources",
formData,
Expand Down Expand Up @@ -191,6 +191,7 @@ class MinkApi {
params: { corpus_id: corpusId },
})
// Errors are okay.
// TODO Use the `validateStatus` config option instead
.catch((reason) => reason.response);
return response.data;
}
Expand Down
11 changes: 10 additions & 1 deletion src/api/api.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ByLang } from "@/util.types";
import type { AxiosProgressEvent } from "axios";
import type { ByLang, SweEng } from "@/util.types";

/** Properties common to most backend responses */
export type MinkResponse<T extends { [k: string]: any } = {}> = T & {
Expand Down Expand Up @@ -61,10 +61,19 @@ export type ResourceInfoOneData = ResourceInfo;

/** Data about a resource and its job status */
export type ResourceInfo = {
owner?: UserData;
resource: ResourceData;
job: CorpusStatus;
};

/** Data about a Mink user */
export type UserData = {
id: string;
name: string;
email: string;
ui_language: SweEng;
};

/** Basic data about a resource */
export type ResourceData = {
type: ResourceType;
Expand Down
93 changes: 22 additions & 71 deletions src/api/backend.composable.ts
Original file line number Diff line number Diff line change
@@ -1,150 +1,101 @@
import { useI18n } from "vue-i18n";
import api from "./api";
import api from "@/api/api";
import useSpin from "@/spin/spin.composable";
import type { ProgressHandler } from "./api.types";
import type { ProgressHandler } from "@/api/api.types";

/** Wraps API endpoints with Spin. */
export default function useMinkBackend() {
const { spin } = useSpin();
const { t } = useI18n();

const loadCorpusIds = () =>
spin(api.listCorpora(), t("corpus.list.loading"), "corpora");
const loadCorpusIds = () => spin(api.listCorpora(), "corpora");

const createCorpus = () =>
spin(api.createCorpus(), t("corpus.creating"), "create");
const createCorpus = () => spin(api.createCorpus(), "create");

const createMetadata = (publicId: string) =>
spin(api.createMetadata(publicId), null, "create");
spin(api.createMetadata(publicId), "create");

const deleteCorpus = (corpusId: string) =>
spin(
api.removeCorpus(corpusId),
t("corpus.deleting"),
`corpus/${corpusId}`,
);
spin(api.removeCorpus(corpusId), `corpus/${corpusId}`);

const deleteMetadata = (resourceId: string) =>
spin(api.removeMetadata(resourceId), null, `resource/${resourceId}`);
spin(api.removeMetadata(resourceId), `resource/${resourceId}`);

const loadConfig = (corpusId: string) =>
spin(
api.downloadConfig(corpusId),
t("config.loading"),
`corpus/${corpusId}/config`,
);
spin(api.downloadConfig(corpusId), `corpus/${corpusId}/config`);

const saveConfig = (corpusId: string, configYaml: string) =>
spin(
api.uploadConfig(corpusId, configYaml),
t("corpus.configuring"),
`corpus/${corpusId}/config`,
);
spin(api.uploadConfig(corpusId, configYaml), `corpus/${corpusId}/config`);

const downloadSource = (corpusId: string, filename: string, binary = false) =>
spin(
api.downloadSources(corpusId, filename, binary),
t("source.downloading"),
`corpus/${corpusId}/sources/${filename}`,
);

const downloadPlaintext = (corpusId: string, filename: string) =>
spin(
api.downloadSourceText(corpusId, filename),
t("source.downloading_plain"),
`corpus/${corpusId}/sources/${filename}/plain`,
);

const uploadSources = (
corpusId: string,
files: FileList,
files: File[],
onProgress?: ProgressHandler,
) =>
spin(
api.uploadSources(corpusId, files, onProgress),
t("source.uploading", files.length),
`corpus/${corpusId}/sources`,
);

const deleteSource = (corpusId: string, filename: string) =>
spin(
api.removeSource(corpusId, filename),
t("source.deleting"),
`corpus/${corpusId}/sources`,
);
spin(api.removeSource(corpusId, filename), `corpus/${corpusId}/sources`);

const uploadMetadata = (resourceId: string, yaml: string) =>
spin(
api.uploadMetadataYaml(resourceId, yaml),
null,
`resource/${resourceId}/metadata`,
);

const downloadMetadata = (resourceId: string) =>
spin(
api.downloadMetaataYaml(resourceId),
null,
`resource/${resourceId}/metadata`,
);

const resourceInfoAll = () =>
spin(api.resourceInfoAll(), t("resource.loading"), "corpora");
const resourceInfoAll = () => spin(api.resourceInfoAll(), "corpora");

const resourceInfoOne = (corpusId: string) =>
spin(
api.resourceInfoOne(corpusId),
t("resource.loading"),
`corpus/${corpusId}/job`,
);
spin(api.resourceInfoOne(corpusId), `corpus/${corpusId}/job`);

const runJob = (corpusId: string) =>
spin(api.runSparv(corpusId), t("job.starting"), `corpus/${corpusId}/job`);
spin(api.runSparv(corpusId), `corpus/${corpusId}/job`);

const installKorp = (corpusId: string) =>
spin(
api.installKorp(corpusId),
t("job.installing"),
`corpus/${corpusId}/job`,
);
spin(api.installKorp(corpusId), `corpus/${corpusId}/job`);

const installStrix = (corpusId: string) =>
spin(
api.installStrix(corpusId),
t("job.installing"),
`corpus/${corpusId}/job`,
);
spin(api.installStrix(corpusId), `corpus/${corpusId}/job`);

const abortJob = (corpusId: string) =>
spin(api.abortJob(corpusId), t("job.aborting"), `corpus/${corpusId}/job`);
spin(api.abortJob(corpusId), `corpus/${corpusId}/job`);

const loadExports = (corpusId: string) =>
spin(
api.listExports(corpusId),
t("exports.loading"),
`corpus/${corpusId}/exports`,
);
spin(api.listExports(corpusId), `corpus/${corpusId}/exports`);

const downloadExports = (corpusId: string) =>
spin(
api.downloadExports(corpusId),
t("exports.downloading"),
`corpus/${corpusId}/exports`,
);
spin(api.downloadExports(corpusId), `corpus/${corpusId}/exports`);

const downloadExportFiles = (corpusId: string, filename: string) =>
spin(
api.downloadExportFile(corpusId, filename),
t("exports.downloading"),
`corpus/${corpusId}/exports`,
);

const checkAdminMode = () => spin(api.adminModeStatus(), null, "admin-mode");
const checkAdminMode = () => spin(api.adminModeStatus(), "admin-mode");

const enableAdminMode = () =>
spin(api.adminModeOn(), "Enabling admin mode", "admin-mode");
const enableAdminMode = () => spin(api.adminModeOn(), "admin-mode");

const disableAdminMode = () =>
spin(api.adminModeOff(), "Disabling admin mode", "admin-mode");
const disableAdminMode = () => spin(api.adminModeOff(), "admin-mode");

return {
loadCorpusIds,
Expand Down
4 changes: 2 additions & 2 deletions src/api/backendInfo.composable.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { computed, readonly, ref } from "vue";
import api from "./api";
import type { InfoData } from "./api.types";
import api from "@/api/api";
import type { InfoData } from "@/api/api.types";
import { keyBy, objsToDict } from "@/util";

export type Info = {
Expand Down
2 changes: 1 addition & 1 deletion src/api/corpusConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
parseConfig,
type ConfigOptions,
validateConfig,
} from "./corpusConfig";
} from "@/api/corpusConfig";

describe("makeConfig", () => {
test("sets minimal info", async () => {
Expand Down
Loading

0 comments on commit caa2190

Please sign in to comment.