Skip to content
Snippets Groups Projects
Commit c0a63a42 authored by Valentin Rigal's avatar Valentin Rigal Committed by Erwan Rouchet
Browse files

Add elements from selection in a dataset

parent 7871fce2
No related branches found
No related tags found
1 merge request!1545Add elements from selection in a dataset
......@@ -24,7 +24,8 @@ export * from './process'
export * from './repository'
export * from './rights'
export * from './search'
export * from './selection'
export * from './selection.js'
export * from './selection.ts'
export * from './transcription'
export * from './transkribus'
export * from './user'
......
import axios from 'axios'
import { unique } from '.'
import { UUID } from '@/types'
interface CreateDatasetElementsSelectionParameters {
/**
* ID of the dataset that will receive selected elements
*/
dataset_id: UUID
/**
* Name of the set elements will be added to
*/
set: string
}
/**
* Add selected elements to a corpus' dataset
*/
export const createDatasetElementsSelection = unique(async (corpusId: UUID, params: CreateDatasetElementsSelectionParameters) => (await axios.post(`/corpus/${corpusId}/datasets/selection/`, params)))
......@@ -39,11 +39,20 @@
:disabled="!canCreate || null"
class="dropdown-item"
v-on:click="createParentModal = canCreate"
:title="canCreate ? 'Link selected elements to another parent folder.' : executeDisabledTitle"
:title="canCreate ? 'Link selected elements to another parent folder.' : createDisabledTitle"
>
<i class="icon-direction"></i>
Link to another parent
</a>
<a
:disabled="!canCreate || null"
class="dropdown-item"
v-on:click="datasetSelectionModal = canCreate"
:title="canCreate ? 'Add those elements to a dataset.' : createDisabledTitle"
>
<i class="icon-bookmark"></i>
Add to a dataset
</a>
<a
:disabled="!canCreate || null"
class="dropdown-item"
......@@ -176,6 +185,8 @@
<DeleteResultsModal v-model="deleteResultsModal" :corpus-id="corpusId" selection />
<DatasetFromSelectionModal v-model="datasetSelectionModal" :corpus-id="corpusId" />
<ElementList
:elements="elements"
max-size
......@@ -192,6 +203,7 @@ import { createProcessRedirect } from '@/helpers'
import MLClassSelect from '@/components/MLClassSelect.vue'
import Modal from '@/components/Modal.vue'
import DeleteResultsModal from '@/components/Process/Workers/DeleteResultsModal.vue'
import DatasetFromSelectionModal from '@/components/Navigation/DatasetFromSelectionModal.vue'
import ElementList from './ElementList'
import FolderPicker from '@/components/Navigation/FolderPicker'
......@@ -204,7 +216,8 @@ export default {
DeleteResultsModal,
MLClassSelect,
Modal,
FolderPicker
FolderPicker,
DatasetFromSelectionModal
},
props: {
corpusId: {
......@@ -225,7 +238,8 @@ export default {
moveLoading: false,
createParentModal: false,
createParentLoading: false,
pickedFolder: null
pickedFolder: null,
datasetSelectionModal: false
}),
mounted () {
if (this.hasMlClasses[this.corpusId] === undefined) {
......
<template>
<Modal
:model-value="modelValue"
v-on:update:model-value="updateModelValue"
:title="truncateLong(title)"
>
<div v-if="availableDatasets === null" class="loader mx-auto is-size-1"></div>
<div v-else-if="availableDatasets?.length === 0" class="notification is-warning">
<p>No dataset available.</p>
<p>
You can create one from the
<router-link :to="{ name: 'corpus-update', params: { corpusId } }" class="has-text-weight-semibold">
corpus details page
</router-link>
.
</p>
</div>
<form v-else v-on:submit.prevent="addToSelection">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Dataset</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<span class="select is-fullwidth">
<select
v-model="selectedDataset"
:disabled="loading ?? null"
>
<option :value="null" selected disabled></option>
<template v-if="availableDatasets">
<option
v-for="dataset in availableDatasets"
:value="dataset"
:key="dataset.id"
:title="dataset.description"
>
{{ truncateLong(dataset.name) }}
</option>
</template>
</select>
</span>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Set</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<span class="select is-fullwidth">
<select
v-model="set"
:disabled="(!selectedDataset || loading) ?? null"
:title="selectedDataset ? `Set elements will be added to on dataset ${selectedDataset.name}` : 'Please select a dataset first' "
>
<option :value="null" selected disabled></option>
<template v-if="selectedDataset?.sets">
<option
v-for="value in selectedDataset.sets"
:value="value"
:key="value"
>
{{ value }}
</option>
</template>
</select>
</span>
</div>
</div>
</div>
</div>
</form>
<template v-slot:footer="{ close }">
<button
class="button is-info"
:class="{ 'is-loading': loading }"
type="submit"
:disabled="!canAdd"
:title="canAdd ? 'Add selected elements to the dataset' : 'Please select a dataset and related set'"
v-on:click="addToSelection"
>
Add elements
</button>
<button class="button ml-auto" v-on:click="close">Cancel</button>
</template>
</Modal>
</template>
<script lang="ts">
import {
mapState as mapVuexState,
mapActions as mapVuexActions
} from 'vuex'
import { corporaMixin, truncateMixin } from '@/mixins'
import { UUID_REGEX } from '@/config'
import { Dataset, UUID } from '@/types'
import Modal from '@/components/Modal.vue'
import { defineComponent, PropType } from 'vue'
export default defineComponent({
mixins: [
corporaMixin,
truncateMixin
],
components: {
Modal
},
emits: {
'update:modelValue': (value: boolean) => typeof value === 'boolean'
},
props: {
modelValue: {
type: Boolean,
default: false
},
/**
* Id of the corpus to delete results on
*/
corpusId: {
type: String as PropType<UUID>,
validator: value => typeof value === 'string' && UUID_REGEX.test(value),
required: true
}
},
data: () => ({
loading: false,
/**
* ID of the dataset that will receive selected elements
*/
selectedDataset: null as Dataset | null,
/**
* Name of the set elements will be added to
*/
set: null as string | null
}),
computed: {
...mapVuexState('corpora', ['corpusDatasets']),
title (): string {
return `Add ${this.corpus?.name} selection to a dataset`
},
canAdd (): boolean {
return this.selectedDataset !== null && this.set !== null
},
availableDatasets (): Dataset[] | null {
return this.corpusDatasets?.[this.corpusId] ?? null
}
},
methods: {
...mapVuexActions('corpora', ['listCorpusDatasets', 'addDatasetElementsSelection']),
updateModelValue (value: boolean) { this.$emit('update:modelValue', value) },
async addToSelection () {
if (!this.canAdd) return
this.loading = true
try {
await this.addDatasetElementsSelection({
corpusId: this.corpusId,
params: {
dataset_id: this.selectedDataset?.id,
set: this.set
}
})
this.selectedDataset = null
this.set = null
this.$emit('update:modelValue', false)
} finally {
this.loading = false
}
}
},
watch: {
modelValue (newValue: boolean) {
if (newValue && this.availableDatasets === null) {
this.listCorpusDatasets({ corpusId: this.corpusId })
}
}
}
})
</script>
......@@ -419,6 +419,17 @@ export const actions = {
}
},
// Add selected elements to a dataset of the same corpus
async addDatasetElementsSelection ({ commit }, { corpusId, params }) {
try {
await api.createDatasetElementsSelection(corpusId, params)
commit('notifications/notify', { type: 'success', text: 'Elements have been added to the dataset.' }, { root: true })
} catch (err) {
commit('notifications/notify', { type: 'error', text: errorParser(err) }, { root: true })
throw err
}
},
async listExports ({ commit }, { corpusId, ...payload }) {
try {
commit('setExports', await api.listExports(corpusId, removeEmptyStrings(payload)))
......
......@@ -2229,6 +2229,34 @@ describe('corpora', () => {
})
})
describe('addDatasetElementsSelection', () => {
it('adds elements from a selection to a dataset', async () => {
mock.onPost('/corpus/corpusid/datasets/selection/', { dataset_id: 'datasetid', set: 'test' }).reply(204)
const params = { dataset_id: 'datasetid', set: 'test' }
await store.dispatch('corpora/addDatasetElementsSelection', { corpusId: 'corpusid', params })
assert.deepStrictEqual(store.history, [
{ action: 'corpora/addDatasetElementsSelection', payload: { corpusId: 'corpusid', params } },
{ mutation: 'notifications/notify', payload: { type: 'success', text: 'Elements have been added to the dataset.' } }
])
})
it('throw errors', async () => {
mock.onPost('/corpus/corpusid/datasets/selection/', { dataset_id: 'datasetid', set: 'test' }).reply(400)
const params = { dataset_id: 'datasetid', set: 'test' }
await assertRejects(
async () => await store.dispatch('corpora/addDatasetElementsSelection', { corpusId: 'corpusid', params })
)
assert.deepStrictEqual(store.history, [
{ action: 'corpora/addDatasetElementsSelection', payload: { corpusId: 'corpusid', params } },
{ mutation: 'notifications/notify', payload: { type: 'error', text: 'Request failed with status code 400' } }
])
})
})
describe('listExports', () => {
it('lists exports in a corpus', async () => {
mock.onGet('/corpus/corpusid/export/').reply(200, { count: 1, results: [exportSample] })
......
......@@ -5,6 +5,8 @@
"strict": true,
"jsx": "preserve",
"allowJs": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment