Browse Source

Merge pull request #1679 from doccano/enhancement/progress

[Enhancement] Show progress
pull/1681/head
Hiroki Nakayama 2 years ago
committed by GitHub
parent
commit
8be56b3ed3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 141 additions and 16 deletions
  1. 5
      backend/metrics/views.py
  2. 46
      frontend/components/tasks/sidebar/AnnotationProgress.vue
  3. 9
      frontend/composables/useExampleItem.ts
  4. 6
      frontend/domain/models/metrics/metrics.ts
  5. 3
      frontend/domain/models/metrics/metricsRepository.ts
  6. 14
      frontend/pages/projects/_id/image-classification/index.vue
  7. 12
      frontend/pages/projects/_id/intent-detection-and-slot-filling/index.vue
  8. 12
      frontend/pages/projects/_id/sequence-labeling/index.vue
  9. 14
      frontend/pages/projects/_id/sequence-to-sequence/index.vue
  10. 14
      frontend/pages/projects/_id/speech-to-text/index.vue
  11. 8
      frontend/pages/projects/_id/text-classification/index.vue
  12. 8
      frontend/repositories/metrics/apiMetricsRepository.ts
  13. 6
      frontend/services/application/metrics/metricsApplicationService.ts

5
backend/metrics/views.py

@ -18,8 +18,9 @@ class ProgressAPI(APIView):
def get(self, request, *args, **kwargs):
examples = Example.objects.filter(project=self.kwargs["project_id"]).values("id")
total = examples.count()
done = ExampleState.objects.count_done(examples, user=self.request.user)
return {"total": total, "remaining": total - done}
complete = ExampleState.objects.count_done(examples, user=self.request.user)
data = {"total": total, "remaining": total - complete, "complete": complete}
return Response(data=data, status=status.HTTP_200_OK)
class MemberProgressAPI(APIView):

46
frontend/components/tasks/sidebar/AnnotationProgress.vue

@ -0,0 +1,46 @@
<template>
<v-card>
<v-card-title>Progress</v-card-title>
<v-card-text>
<v-list class="pt-0" dense>
<v-list-item class="pa-0">
<v-list-item-title>Total</v-list-item-title>
<v-list-item-subtitle class="text-right" v-text="progress.total" />
</v-list-item>
<v-list-item class="pa-0">
<v-list-item-title>Complete</v-list-item-title>
<v-list-item-subtitle class="text-right" v-text="progress.complete" />
</v-list-item>
</v-list>
<v-progress-linear
:value="percentage"
color="success"
height="25"
>
<template #default="{ value }">
<strong>{{ value }}%</strong>
</template>
</v-progress-linear>
</v-card-text>
</v-card>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue'
import { MyProgress } from '@/domain/models/metrics/metrics'
export default Vue.extend({
props: {
progress: {
type: Object as PropType<MyProgress>,
required: true
},
},
computed: {
percentage(): number {
return Math.ceil(this.progress.complete / this.progress.total * 100)
}
}
})
</script>

9
frontend/composables/useExampleItem.ts

@ -5,7 +5,8 @@ import { ExampleDTO } from '@/services/application/example/exampleData'
export const useExampleItem = () => {
const state = reactive({
example: {} as ExampleDTO,
totalExample: 0
totalExample: 0,
progress: {}
})
const { app } = useContext()
@ -28,16 +29,22 @@ export const useExampleItem = () => {
state.example = await exampleService.findById(projectId, state.example.id)
}
const updateProgress = async(projectId: string) => {
state.progress = await app.$services.metrics.fetchMyProgress(projectId)
}
const confirm = async(
projectId: string,
) => {
await exampleService.confirm(projectId, state.example.id)
await getExampleById(projectId)
updateProgress(projectId)
}
return {
state,
confirm,
getExample,
updateProgress
}
}

6
frontend/domain/models/metrics/metrics.ts

@ -6,3 +6,9 @@ export interface Progress {
total: number
progress: {user: string, done: number}[]
}
export interface MyProgress {
total: number
complete: number
remaining: number
}

3
frontend/domain/models/metrics/metricsRepository.ts

@ -1,7 +1,8 @@
import { Distribution, Progress } from '~/domain/models/metrics/metrics'
import { Distribution, Progress, MyProgress } from '~/domain/models/metrics/metrics'
export interface MetricsRepository {
fetchCategoryDistribution(projectId: string): Promise<Distribution>
fetchSpanDistribution(projectId: string): Promise<Distribution>
fetchMemberProgress(projectId: string): Promise<Progress>
fetchMyProgress(projectId: string): Promise<MyProgress>
}

14
frontend/pages/projects/_id/image-classification/index.vue

@ -62,7 +62,8 @@
</v-card>
</template>
<template #sidebar>
<list-metadata :metadata="image.meta" />
<annotation-progress :progress="progress" />
<list-metadata :metadata="image.meta" class="mt-4" />
</template>
</layout-text>
</template>
@ -78,10 +79,12 @@ import ListMetadata from '@/components/tasks/metadata/ListMetadata'
import ToolbarLaptop from '@/components/tasks/toolbar/ToolbarLaptop'
import ToolbarMobile from '@/components/tasks/toolbar/ToolbarMobile'
import { useLabelList } from '@/composables/useLabelList'
import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
export default {
components: {
AnnotationProgress,
LabelGroup,
LabelSelect,
LayoutText,
@ -118,7 +121,8 @@ export default {
width: 0
},
mdiText,
mdiFormatListBulleted
mdiFormatListBulleted,
progress: {}
}
},
@ -162,6 +166,7 @@ export default {
async created() {
this.getLabelList(this.projectId)
this.project = await this.$services.project.findById(this.projectId)
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
methods: {
@ -202,9 +207,14 @@ export default {
}
},
async updateProgress() {
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
async confirm() {
await this.$services.example.confirm(this.projectId, this.image.id)
await this.$fetch()
this.updateProgress()
},
setImageSize(val) {

12
frontend/pages/projects/_id/intent-detection-and-slot-filling/index.vue

@ -44,7 +44,8 @@
</v-card>
</template>
<template #sidebar>
<list-metadata :metadata="doc.meta" />
<annotation-progress :progress="progress" />
<list-metadata :metadata="doc.meta" class="mt-4" />
</template>
</layout-text>
</template>
@ -56,9 +57,11 @@ import ListMetadata from '@/components/tasks/metadata/ListMetadata'
import ToolbarLaptop from '@/components/tasks/toolbar/ToolbarLaptop'
import ToolbarMobile from '@/components/tasks/toolbar/ToolbarMobile'
import LabelGroup from '@/components/tasks/textClassification/LabelGroup'
import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
export default {
components: {
AnnotationProgress,
EntityEditor,
LayoutText,
ListMetadata,
@ -83,6 +86,7 @@ export default {
project: {},
exclusive: false,
enableAutoLabeling: false,
progress: {}
}
},
@ -123,6 +127,7 @@ export default {
this.spanTypes = await this.$services.spanType.list(this.projectId)
this.categoryTypes = await this.$services.categoryType.list(this.projectId)
this.project = await this.$services.project.findById(this.projectId)
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
methods: {
@ -165,9 +170,14 @@ export default {
await this.listSpan(this.doc.id)
},
async updateProgress() {
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
async confirm() {
await this.$services.example.confirm(this.projectId, this.doc.id)
await this.$fetch()
this.updateProgress()
}
}
}

12
frontend/pages/projects/_id/sequence-labeling/index.vue

@ -38,7 +38,8 @@
</v-card>
</template>
<template #sidebar>
<list-metadata :metadata="doc.meta" />
<annotation-progress :progress="progress" />
<list-metadata :metadata="doc.meta" class="mt-4" />
<v-card class="mt-4">
<v-card-title>Label Types</v-card-title>
<v-card-text>
@ -80,10 +81,12 @@ import ListMetadata from '@/components/tasks/metadata/ListMetadata'
import ToolbarLaptop from '@/components/tasks/toolbar/ToolbarLaptop'
import ToolbarMobile from '@/components/tasks/toolbar/ToolbarMobile'
import EntityEditor from '@/components/tasks/sequenceLabeling/EntityEditor.vue'
import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
export default {
components: {
AnnotationProgress,
EntityEditor,
LayoutText,
ListMetadata,
@ -108,6 +111,7 @@ export default {
enableAutoLabeling: false,
rtl: false,
selectedLabelIndex: null,
progress: {},
}
},
@ -167,6 +171,7 @@ export default {
this.labels = await this.$services.spanType.list(this.projectId)
this.linkTypes = await this.$services.linkTypes.list(this.projectId)
this.project = await this.$services.project.findById(this.projectId)
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
methods: {
@ -215,9 +220,14 @@ export default {
}
},
async updateProgress() {
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
async confirm() {
await this.$services.example.confirm(this.projectId, this.doc.id)
await this.$fetch()
this.updateProgress()
},
changeSelectedLabel(event) {

14
frontend/pages/projects/_id/sequence-to-sequence/index.vue

@ -29,7 +29,8 @@
/>
</template>
<template #sidebar>
<list-metadata :metadata="doc.meta" />
<annotation-progress :progress="progress" />
<list-metadata :metadata="doc.meta" class="mt-4" />
</template>
</layout-text>
</template>
@ -40,11 +41,13 @@ import LayoutText from '@/components/tasks/layout/LayoutText'
import ListMetadata from '@/components/tasks/metadata/ListMetadata'
import ToolbarLaptop from '@/components/tasks/toolbar/ToolbarLaptop'
import ToolbarMobile from '@/components/tasks/toolbar/ToolbarMobile'
import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
import Seq2seqBox from '~/components/tasks/seq2seq/Seq2seqBox'
export default {
components: {
AnnotationProgress,
LayoutText,
ListMetadata,
Seq2seqBox,
@ -62,7 +65,8 @@ export default {
annotations: [],
docs: [],
project: {},
enableAutoLabeling: false
enableAutoLabeling: false,
progress: {}
}
},
@ -104,6 +108,7 @@ export default {
async created() {
this.project = await this.$services.project.findById(this.projectId)
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
methods: {
@ -139,9 +144,14 @@ export default {
}
},
async updateProgress() {
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
async confirm() {
await this.$services.example.confirm(this.projectId, this.doc.id)
await this.$fetch()
this.updateProgress()
}
}
}

14
frontend/pages/projects/_id/speech-to-text/index.vue

@ -36,7 +36,8 @@
/>
</template>
<template #sidebar>
<list-metadata :metadata="item.meta" />
<annotation-progress :progress="progress" />
<list-metadata :metadata="item.meta" class="mt-4" />
</template>
</layout-text>
</template>
@ -47,12 +48,14 @@ import LayoutText from '@/components/tasks/layout/LayoutText'
import ListMetadata from '@/components/tasks/metadata/ListMetadata'
import ToolbarLaptop from '@/components/tasks/toolbar/ToolbarLaptop'
import ToolbarMobile from '@/components/tasks/toolbar/ToolbarMobile'
import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
import Seq2seqBox from '~/components/tasks/seq2seq/Seq2seqBox'
import AudioViewer from '~/components/tasks/audio/AudioViewer'
export default {
components: {
AnnotationProgress,
AudioViewer,
LayoutText,
ListMetadata,
@ -72,7 +75,8 @@ export default {
items: [],
project: {},
enableAutoLabeling: false,
isLoading: false
isLoading: false,
progress: {}
}
},
@ -116,6 +120,7 @@ export default {
async created() {
this.project = await this.$services.project.findById(this.projectId)
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
methods: {
@ -151,9 +156,14 @@ export default {
}
},
async updateProgress() {
this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
},
async confirm() {
await this.$services.example.confirm(this.projectId, this.item.id)
await this.$fetch()
this.updateProgress()
}
}
}

8
frontend/pages/projects/_id/text-classification/index.vue

@ -45,7 +45,8 @@
</v-card>
</template>
<template #sidebar>
<list-metadata :metadata="example.meta" />
<annotation-progress :progress="progress" />
<list-metadata :metadata="example.meta" class="mt-4" />
</template>
</layout-text>
</template>
@ -63,10 +64,12 @@ import { useExampleItem } from '@/composables/useExampleItem'
import { useLabelList } from '@/composables/useLabelList'
import { useProjectItem } from '@/composables/useProjectItem'
import { useTeacherList } from '@/composables/useTeacherList'
import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
export default {
components: {
AnnotationProgress,
ButtonLabelSwitch,
LabelGroup,
LabelSelect,
@ -85,7 +88,7 @@ export default {
const { app, params, query } = useContext()
const projectId = params.value.id
const { state: projectState, getProjectById } = useProjectItem()
const { state: exampleState, confirm, getExample } = useExampleItem()
const { state: exampleState, confirm, getExample, updateProgress } = useExampleItem()
const {
state: teacherState,
annotateLabel,
@ -101,6 +104,7 @@ export default {
getLabelList(projectId)
getProjectById(projectId)
updateProgress(projectId)
const { fetch } = useFetch(async() => {
await getExample(

8
frontend/repositories/metrics/apiMetricsRepository.ts

@ -1,6 +1,6 @@
import ApiService from '@/services/api.service'
import { MetricsRepository } from '@/domain/models/metrics/metricsRepository'
import { Distribution, Progress } from '~/domain/models/metrics/metrics'
import { Distribution, Progress, MyProgress } from '~/domain/models/metrics/metrics'
export class APIMetricsRepository implements MetricsRepository {
constructor(
@ -24,4 +24,10 @@ export class APIMetricsRepository implements MetricsRepository {
const response = await this.request.get(url)
return response.data
}
async fetchMyProgress(projectId: string): Promise<MyProgress> {
const url = `/projects/${projectId}/metrics/progress`
const response = await this.request.get(url)
return response.data
}
}

6
frontend/services/application/metrics/metricsApplicationService.ts

@ -1,5 +1,5 @@
import { MetricsRepository } from '~/domain/models/metrics/metricsRepository'
import { Progress, Distribution } from '~/domain/models/metrics/metrics'
import { Progress, Distribution, MyProgress } from '~/domain/models/metrics/metrics'
export class MetricsApplicationService {
constructor(
@ -17,4 +17,8 @@ export class MetricsApplicationService {
public async fetchSpanDistribution(projectId: string): Promise<Distribution> {
return await this.repository.fetchSpanDistribution(projectId)
}
public async fetchMyProgress(projectId: string): Promise<MyProgress> {
return await this.repository.fetchMyProgress(projectId)
}
}
Loading…
Cancel
Save