mirror of https://github.com/doccano/doccano.git
6 changed files with 49 additions and 206 deletions
Split View
Diff Options
-
87frontend/domain/models/statistics/statistics.ts
-
7frontend/domain/models/statistics/statisticsRepository.ts
-
104frontend/pages/projects/_id/statistics/index.vue
-
20frontend/repositories/statistics/apiStatisticsRepository.ts
-
17frontend/services/application/statistics/statisticsApplicationService.ts
-
20frontend/services/application/statistics/statisticsData.ts
@ -1,87 +1,8 @@ |
|||
export type Label = {[key: string]: number} |
|||
|
|||
export type User = {[key: string]: number} |
|||
|
|||
export type ConfirmedCount = {[key: string]: number} |
|||
|
|||
export class Statistics { |
|||
constructor( |
|||
public label: Label, |
|||
public userLabel: User, |
|||
public total: number, |
|||
public remaining: number, |
|||
public user: User, |
|||
public confirmedCount: ConfirmedCount, |
|||
) {} |
|||
|
|||
static valueOf( |
|||
{ label, user_label, total, remaining, user, confirmed_count }: |
|||
{ |
|||
label: Label, |
|||
user_label: User, |
|||
total: number, |
|||
remaining: number, |
|||
user: User, |
|||
confirmed_count: ConfirmedCount, |
|||
} |
|||
): Statistics { |
|||
return new Statistics(label, user_label, total, remaining, user, confirmed_count) |
|||
} |
|||
|
|||
private makeData(object: Label | User, label: string) { |
|||
const labels = object ? Object.keys(object) : [] |
|||
const counts = object ? Object.values(object) : [] |
|||
return { |
|||
labels, |
|||
datasets: [{ |
|||
label, |
|||
backgroundColor: '#00d1b2', |
|||
data: counts |
|||
}] |
|||
} |
|||
} |
|||
|
|||
public labelStats(label: string) { |
|||
return this.makeData(this.label, label) |
|||
} |
|||
|
|||
public userStats(label: string) { |
|||
return this.makeData(this.user, label) |
|||
} |
|||
|
|||
public progress(labels: string[]) { |
|||
const complete = this.total - this.remaining |
|||
const incomplete = this.remaining |
|||
return { |
|||
datasets: [{ |
|||
data: [complete, incomplete], |
|||
backgroundColor: ['#00d1b2', '#ffdd57'] |
|||
}], |
|||
labels |
|||
} |
|||
} |
|||
|
|||
private makeProgressData(roleName: string, labels: string[]) { |
|||
const confirmed = this.confirmedCount[roleName] |
|||
const unconfirmed = this.total - confirmed |
|||
return { |
|||
datasets: [{ |
|||
data: [confirmed, unconfirmed], |
|||
backgroundColor: ['#00d1b2', '#ffdd57'] |
|||
}], |
|||
labels |
|||
} |
|||
} |
|||
|
|||
public annotatorProgress(labels: string[]) { |
|||
return this.makeProgressData('annotator', labels) |
|||
} |
|||
|
|||
public approverProgress(labels: string[]) { |
|||
return this.makeProgressData('annotation_approver', labels) |
|||
} |
|||
|
|||
public adminProgress(labels: string[]) { |
|||
return this.makeProgressData('project_admin', labels) |
|||
} |
|||
export type Distribution = {[user: string]: {[label: string]: number}} |
|||
export interface Progress { |
|||
total: number |
|||
progress: {user: string, done: number}[] |
|||
} |
@ -1,6 +1,7 @@ |
|||
import { Statistics } from '~/domain/models/statistics/statistics' |
|||
import { Distribution, Progress } from '~/domain/models/statistics/statistics' |
|||
|
|||
export interface StatisticsRepository { |
|||
|
|||
fetch(projectId: string): Promise<Statistics> |
|||
fetchCategoryDistribution(projectId: string): Promise<Distribution> |
|||
fetchSpanDistribution(projectId: string): Promise<Distribution> |
|||
fetchMemberProgress(projectId: string): Promise<Progress> |
|||
} |
@ -1,109 +1,33 @@ |
|||
<template> |
|||
<v-row v-if="!isEmpty"> |
|||
<v-col |
|||
cols="12" |
|||
lg="4" |
|||
> |
|||
<v-card> |
|||
<v-card-title>{{ $t('members.roles.annotator') }}</v-card-title> |
|||
<v-card-text> |
|||
<doughnut-chart |
|||
:chart-data="stats.annotatorProgress" |
|||
/> |
|||
</v-card-text> |
|||
</v-card> |
|||
<v-row> |
|||
<v-col cols="12"> |
|||
<member-progress /> |
|||
</v-col> |
|||
<v-col |
|||
cols="12" |
|||
lg="4" |
|||
> |
|||
<v-card> |
|||
<v-card-title>{{ $t('members.roles.annotationApprover') }}</v-card-title> |
|||
<v-card-text> |
|||
<doughnut-chart |
|||
:chart-data="stats.approverProgress" |
|||
/> |
|||
</v-card-text> |
|||
</v-card> |
|||
<v-col cols="12"> |
|||
<category-distribution /> |
|||
</v-col> |
|||
<v-col |
|||
cols="12" |
|||
lg="4" |
|||
> |
|||
<v-card> |
|||
<v-card-title>{{ $t('members.roles.projectAdmin') }}</v-card-title> |
|||
<v-card-text> |
|||
<doughnut-chart |
|||
:chart-data="stats.adminProgress" |
|||
/> |
|||
</v-card-text> |
|||
</v-card> |
|||
</v-col> |
|||
<v-col |
|||
cols="12" |
|||
lg="4" |
|||
> |
|||
<v-card> |
|||
<v-card-title>Label Stats</v-card-title> |
|||
<v-card-text> |
|||
<bar-chart |
|||
:chart-data="stats.label" |
|||
/> |
|||
</v-card-text> |
|||
</v-card> |
|||
</v-col> |
|||
<v-col |
|||
cols="12" |
|||
lg="4" |
|||
> |
|||
<v-card> |
|||
<v-card-title>User Stats</v-card-title> |
|||
<v-card-text> |
|||
<bar-chart |
|||
:chart-data="stats.user" |
|||
/> |
|||
</v-card-text> |
|||
</v-card> |
|||
<v-col cols="12"> |
|||
<span-distribution /> |
|||
</v-col> |
|||
</v-row> |
|||
</template> |
|||
|
|||
<script> |
|||
import _ from 'lodash' |
|||
import DoughnutChart from '@/components/statistics/ChartDoughnut' |
|||
import BarChart from '@/components/statistics/ChartBar' |
|||
import CategoryDistribution from '~/components/statistics/CategoryDistribution' |
|||
import SpanDistribution from '~/components/statistics/SpanDistribution' |
|||
import MemberProgress from '~/components/statistics/MemberProgress' |
|||
|
|||
export default { |
|||
|
|||
components: { |
|||
DoughnutChart, |
|||
BarChart |
|||
CategoryDistribution, |
|||
SpanDistribution, |
|||
MemberProgress |
|||
}, |
|||
|
|||
layout: 'project', |
|||
|
|||
validate({ params }) { |
|||
return /^\d+$/.test(params.id) |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
stats: {} |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
isEmpty() { |
|||
return _.isEmpty(this.stats) |
|||
} |
|||
}, |
|||
|
|||
async created() { |
|||
this.stats = await this.$services.statistics.fetchStatistics( |
|||
this.$route.params.id, |
|||
this.$t('statistics.labelStats'), |
|||
this.$t('statistics.userStats'), |
|||
this.$t('statistics.progress') |
|||
) |
|||
} |
|||
} |
|||
</script> |
@ -1,15 +1,27 @@ |
|||
import ApiService from '@/services/api.service' |
|||
import { StatisticsRepository } from '@/domain/models/statistics/statisticsRepository' |
|||
import { Statistics } from '~/domain/models/statistics/statistics' |
|||
import { Distribution, Progress } from '~/domain/models/statistics/statistics' |
|||
|
|||
export class APIStatisticsRepository implements StatisticsRepository { |
|||
constructor( |
|||
private readonly request = ApiService |
|||
) {} |
|||
|
|||
async fetch(projectId: string): Promise<Statistics> { |
|||
const url = `/projects/${projectId}/statistics` |
|||
async fetchCategoryDistribution(projectId: string): Promise<Distribution> { |
|||
const url = `/projects/${projectId}/category-distribution` |
|||
const response = await this.request.get(url) |
|||
return Statistics.valueOf(response.data) |
|||
return response.data |
|||
} |
|||
|
|||
async fetchSpanDistribution(projectId: string): Promise<Distribution> { |
|||
const url = `/projects/${projectId}/span-distribution` |
|||
const response = await this.request.get(url) |
|||
return response.data |
|||
} |
|||
|
|||
async fetchMemberProgress(projectId: string): Promise<Progress> { |
|||
const url = `/projects/${projectId}/member-progress` |
|||
const response = await this.request.get(url) |
|||
return response.data |
|||
} |
|||
} |
@ -1,15 +1,20 @@ |
|||
import { StatisticsDTO } from './statisticsData' |
|||
import { StatisticsRepository } from '~/domain/models/statistics/statisticsRepository' |
|||
import { Progress, Distribution } from '~/domain/models/statistics/statistics' |
|||
|
|||
export class StatisticsApplicationService { |
|||
constructor( |
|||
private readonly repository: StatisticsRepository |
|||
) {} |
|||
|
|||
public async fetchStatistics( |
|||
projectId: string, labelText: string, userText: string, progressLabels: string[] |
|||
): Promise<StatisticsDTO> { |
|||
const item = await this.repository.fetch(projectId) |
|||
return new StatisticsDTO(item, labelText, userText, progressLabels) |
|||
public async fetchMemberProgress(projectId: string): Promise<Progress> { |
|||
return await this.repository.fetchMemberProgress(projectId) |
|||
} |
|||
|
|||
public async fetchCategoryDistribution(projectId: string): Promise<Distribution> { |
|||
return await this.repository.fetchCategoryDistribution(projectId) |
|||
} |
|||
|
|||
public async fetchSpanDistribution(projectId: string): Promise<Distribution> { |
|||
return await this.repository.fetchSpanDistribution(projectId) |
|||
} |
|||
} |
@ -1,20 +0,0 @@ |
|||
import { Statistics } from '~/domain/models/statistics/statistics' |
|||
|
|||
|
|||
export class StatisticsDTO { |
|||
label: object; |
|||
user: object; |
|||
progress: object; |
|||
annotatorProgress: object; |
|||
approverProgress: object; |
|||
adminProgress: object; |
|||
|
|||
constructor(item: Statistics, labelText: string, userText: string, progressLabels: string[]) { |
|||
this.label = item.labelStats(labelText); |
|||
this.user = item.userStats(userText); |
|||
this.progress = item.progress(progressLabels); |
|||
this.annotatorProgress = item.annotatorProgress(progressLabels); |
|||
this.approverProgress = item.approverProgress(progressLabels); |
|||
this.adminProgress = item.adminProgress(progressLabels); |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save