Browse Source

Change UI to create intent detection and slot filling label

pull/1619/head
Hironsan 2 years ago
parent
commit
46896c88c7
6 changed files with 111 additions and 35 deletions
  1. 6
      backend/api/urls.py
  2. 10
      frontend/components/configAutoLabeling/form/LabelMapping.vue
  3. 28
      frontend/domain/models/label/label.ts
  4. 79
      frontend/pages/projects/_id/labels/index.vue
  5. 10
      frontend/plugins/services.ts
  6. 13
      frontend/repositories/label/apiLabelRepository.ts

6
backend/api/urls.py

@ -47,12 +47,12 @@ urlpatterns_project = [
# name='label_detail'
# ),
path(
route='doc-types',
route='category-types',
view=label.DocTypeList.as_view(),
name='doc_types'
),
path(
route='doc-types/<int:label_id>',
route='category-types/<int:label_id>',
view=label.DocTypeDetail.as_view(),
name='doc_type'
),
@ -67,7 +67,7 @@ urlpatterns_project = [
name='span_type'
),
path(
route='doc-type-upload',
route='category-type-upload',
view=label.DocTypeUploadAPI.as_view(),
name='doc_type_upload'
),

10
frontend/components/configAutoLabeling/form/LabelMapping.vue

@ -155,8 +155,14 @@ export default Vue.extend({
},
async created() {
const labels = await this.$services.label.list(this.$route.params.id)
this.items = labels.map(item => item.text)
const project = await this.$services.project.findById(this.$route.params.id)
if (project.projectType.endsWith('Classification')) {
const labels = await this.$services.categoryType.list(this.$route.params.id)
this.items = labels.map(item => item.text)
} else {
const labels = await this.$services.spanType.list(this.$route.params.id)
this.items = labels.map(item => item.text)
}
},
methods: {

28
frontend/domain/models/label/label.ts

@ -47,6 +47,15 @@ export class LabelItemList {
}
}
interface LabelResponse {
id: number
text: string
prefix_key: string | null
suffix_key: string | null
background_color: string
text_color: string
}
export class LabelItem {
constructor(
public id: number,
@ -57,18 +66,22 @@ export class LabelItem {
public textColor: string = '#ffffff'
) {}
static valueOf(
{ id, text, prefix_key, suffix_key, background_color, text_color }:
{ id: number, text: string, prefix_key: string, suffix_key: string, background_color: string, text_color: string }
): LabelItem {
return new LabelItem(id, text, prefix_key, suffix_key, background_color, text_color)
static valueOf(label: LabelResponse): LabelItem {
return new LabelItem(
label.id,
label.text,
label.prefix_key,
label.suffix_key,
label.background_color,
label.text_color
)
}
get name(): string {
return this.text
}
toObject(): Object {
toObject(): LabelResponse {
return {
id: this.id,
text: this.text,
@ -79,3 +92,6 @@ export class LabelItem {
}
}
}
export class DocTypeItem extends LabelItem {}
export class SpanTypeItem extends LabelItem {}

79
frontend/pages/projects/_id/labels/index.vue

@ -1,5 +1,9 @@
<template>
<v-card>
<v-tabs v-if="hasMultiType" v-model="tab">
<v-tab class="text-capitalize">Category</v-tab>
<v-tab class="text-capitalize">Span</v-tab>
</v-tabs>
<v-card-title>
<action-menu
@create="dialogCreate=true"
@ -102,15 +106,20 @@ export default Vue.extend({
items: [] as LabelDTO[],
selected: [] as LabelDTO[],
isLoading: false,
errorMessage: ''
errorMessage: '',
tab: null,
project: {} as ProjectDTO,
}
},
async fetch() {
this.isLoading = true
this.items = await this.$services.label.list(this.projectId)
this.isLoading = false
},
// async fetch() {
// this.items = []
// this.isLoading = true
// console.log('start fetch')
// this.items = await this.service.list(this.projectId)
// console.log('end fetch')
// this.isLoading = false
// },
computed: {
canDelete(): boolean {
@ -127,16 +136,58 @@ export default Vue.extend({
const item = this.items[this.editedIndex] // to remove myself
return this.items.filter(_ => _ !== item).map(item => item.suffixKey)
.filter(item => item !==null) as string[]
},
hasMultiType(): boolean {
if ('projectType' in this.project) {
return this.project.projectType === 'IntentDetectionAndSlotFilling'
} else {
return false
}
},
service(): any {
if (!('projectType' in this.project)) {
return
}
if (this.hasMultiType) {
if (this.tab === 0) {
return this.$services.categoryType
} else {
return this.$services.spanType
}
} else if (this.project.projectType.endsWith('Classification')) {
return this.$services.categoryType
} else {
return this.$services.spanType
}
}
},
watch: {
tab() {
this.list()
}
},
async created() {
this.project = await this.$services.project.findById(this.projectId)
this.list()
},
methods: {
async list() {
this.items = []
this.isLoading = true
this.items = await this.service.list(this.projectId)
this.isLoading = false
},
async create() {
await this.$services.label.create(this.projectId, this.editedItem)
await this.service.create(this.projectId, this.editedItem)
},
async update() {
await this.$services.label.update(this.projectId, this.editedItem)
await this.service.update(this.projectId, this.editedItem)
},
save() {
@ -145,7 +196,7 @@ export default Vue.extend({
} else {
this.create()
}
this.$fetch()
this.list()
this.close()
},
@ -158,20 +209,20 @@ export default Vue.extend({
},
async remove() {
await this.$services.label.bulkDelete(this.projectId, this.selected)
this.$fetch()
await this.service.bulkDelete(this.projectId, this.selected)
this.list()
this.dialogDelete = false
this.selected = []
},
async download() {
await this.$services.label.export(this.projectId)
await this.service.export(this.projectId)
},
async upload(file: File) {
try {
await this.$services.label.upload(this.projectId, file)
this.$fetch()
await this.service.upload(this.projectId, file)
this.list()
this.closeUpload()
} catch(e) {
this.errorMessage = e.message

10
frontend/plugins/services.ts

@ -46,7 +46,8 @@ import {LinkTypesApplicationService} from "~/services/application/links/linkType
import {ApiLinkTypesRepository} from "~/repositories/links/apiLinkTypesRepository";
export interface Services {
label: LabelApplicationService,
categoryType: LabelApplicationService,
spanType: LabelApplicationService,
linkTypes: LinkTypesApplicationService,
member: MemberApplicationService,
user: UserApplicationService,
@ -77,7 +78,6 @@ declare module 'vue/types/vue' {
}
const plugin: Plugin = (context, inject) => {
const labelRepository = new APILabelRepository()
const linkTypesRepository = new ApiLinkTypesRepository()
const memberRepository = new APIMemberRepository()
const userRepository = new APIUserRepository()
@ -101,7 +101,8 @@ const plugin: Plugin = (context, inject) => {
const downloadFormatRepository = new APIDownloadFormatRepository()
const downloadRepository = new APIDownloadRepository()
const label = new LabelApplicationService(labelRepository)
const categoryType = new LabelApplicationService(new APILabelRepository('category-type'))
const spanType = new LabelApplicationService(new APILabelRepository('span-type'))
const linkTypes = new LinkTypesApplicationService(linkTypesRepository)
const member = new MemberApplicationService(memberRepository)
const user = new UserApplicationService(userRepository)
@ -125,7 +126,8 @@ const plugin: Plugin = (context, inject) => {
const download = new DownloadApplicationService(downloadRepository)
const services: Services = {
label,
categoryType,
spanType,
linkTypes,
member,
user,

13
frontend/repositories/label/apiLabelRepository.ts

@ -13,44 +13,45 @@ export interface LabelItemResponse {
export class APILabelRepository implements LabelRepository {
constructor(
private readonly baseUrl = 'label',
private readonly request = ApiService
) {}
async list(projectId: string): Promise<LabelItem[]> {
const url = `/projects/${projectId}/labels`
const url = `/projects/${projectId}/${this.baseUrl}s`
const response = await this.request.get(url)
const responseItems: LabelItemResponse[] = response.data
return responseItems.map(item => LabelItem.valueOf(item))
}
async create(projectId: string, item: LabelItem): Promise<LabelItem> {
const url = `/projects/${projectId}/labels`
const url = `/projects/${projectId}/${this.baseUrl}s`
const response = await this.request.post(url, item.toObject())
const responseItem: LabelItemResponse = response.data
return LabelItem.valueOf(responseItem)
}
async update(projectId: string, item: LabelItem): Promise<LabelItem> {
const url = `/projects/${projectId}/labels/${item.id}`
const url = `/projects/${projectId}/${this.baseUrl}s/${item.id}`
const response = await this.request.patch(url, item.toObject())
const responseItem: LabelItemResponse = response.data
return LabelItem.valueOf(responseItem)
}
async bulkDelete(projectId: string, labelIds: number[]): Promise<void> {
const url = `/projects/${projectId}/labels`
const url = `/projects/${projectId}/${this.baseUrl}s`
await this.request.delete(url, { ids: labelIds })
}
async uploadFile(projectId: string, payload: FormData): Promise<void> {
const url = `/projects/${projectId}/label-upload`
const url = `/projects/${projectId}/${this.baseUrl}-upload`
const config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
try {
await this.request.post(`/projects/${projectId}/label-upload`, payload, config)
await this.request.post(url, payload, config)
} catch(e) {
const data = e.response.data
if ('detail' in data) {

Loading…
Cancel
Save