You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
5.2 KiB

  1. <template>
  2. <layout-text v-if="doc.id">
  3. <template #header>
  4. <toolbar-laptop
  5. :doc-id="doc.id"
  6. :enable-auto-labeling.sync="enableAutoLabeling"
  7. :guideline-text="project.guideline"
  8. :is-reviewd="doc.isConfirmed"
  9. :total="docs.count"
  10. class="d-none d-sm-block"
  11. @click:clear-label="clear"
  12. @click:review="confirm"
  13. />
  14. <toolbar-mobile
  15. :total="docs.count"
  16. class="d-flex d-sm-none"
  17. />
  18. </template>
  19. <template #content>
  20. <v-card>
  21. <v-card-title>
  22. <label-group
  23. :labels="categoryTypes"
  24. :annotations="categories"
  25. :single-label="exclusive"
  26. @add="addCategory"
  27. @remove="removeCategory"
  28. />
  29. </v-card-title>
  30. <v-divider />
  31. <div class="annotation-text pa-4">
  32. <entity-editor
  33. :dark="$vuetify.theme.dark"
  34. :rtl="isRTL"
  35. :text="doc.text"
  36. :entities="spans"
  37. :entity-labels="spanTypes"
  38. @addEntity="addSpan"
  39. @click:entity="updateSpan"
  40. @contextmenu:entity="deleteSpan"
  41. />
  42. </div>
  43. </v-card>
  44. </template>
  45. <template #sidebar>
  46. <annotation-progress :progress="progress" />
  47. <list-metadata :metadata="doc.meta" class="mt-4" />
  48. </template>
  49. </layout-text>
  50. </template>
  51. <script>
  52. import { mapGetters } from 'vuex'
  53. import EntityEditor from '@/components/tasks/sequenceLabeling/EntityEditor.vue'
  54. import LayoutText from '@/components/tasks/layout/LayoutText'
  55. import ListMetadata from '@/components/tasks/metadata/ListMetadata'
  56. import ToolbarLaptop from '@/components/tasks/toolbar/ToolbarLaptop'
  57. import ToolbarMobile from '@/components/tasks/toolbar/ToolbarMobile'
  58. import LabelGroup from '@/components/tasks/textClassification/LabelGroup'
  59. import AnnotationProgress from '@/components/tasks/sidebar/AnnotationProgress.vue'
  60. export default {
  61. components: {
  62. AnnotationProgress,
  63. EntityEditor,
  64. LayoutText,
  65. ListMetadata,
  66. LabelGroup,
  67. ToolbarLaptop,
  68. ToolbarMobile
  69. },
  70. layout: 'workspace',
  71. validate({ params, query }) {
  72. return /^\d+$/.test(params.id) && /^\d+$/.test(query.page)
  73. },
  74. data() {
  75. return {
  76. docs: [],
  77. spans: [],
  78. categories: [],
  79. spanTypes: [],
  80. categoryTypes: [],
  81. project: {},
  82. exclusive: false,
  83. enableAutoLabeling: false,
  84. progress: {}
  85. }
  86. },
  87. async fetch() {
  88. this.docs = await this.$services.example.fetchOne(
  89. this.projectId,
  90. this.$route.query.page,
  91. this.$route.query.q,
  92. this.$route.query.isChecked
  93. )
  94. const doc = this.docs.items[0]
  95. await this.listSpan(doc.id)
  96. await this.listCategory(doc.id)
  97. },
  98. computed: {
  99. ...mapGetters('auth', ['isAuthenticated', 'getUsername', 'getUserId']),
  100. ...mapGetters('config', ['isRTL']),
  101. projectId() {
  102. return this.$route.params.id
  103. },
  104. doc() {
  105. if (_.isEmpty(this.docs) || this.docs.items.length === 0) {
  106. return {}
  107. } else {
  108. return this.docs.items[0]
  109. }
  110. }
  111. },
  112. watch: {
  113. '$route.query': '$fetch'
  114. },
  115. async created() {
  116. this.spanTypes = await this.$services.spanType.list(this.projectId)
  117. this.categoryTypes = await this.$services.categoryType.list(this.projectId)
  118. this.project = await this.$services.project.findById(this.projectId)
  119. this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
  120. },
  121. methods: {
  122. async listSpan(docId) {
  123. const spans = await this.$services.sequenceLabeling.list(this.projectId, docId);
  124. this.spans = spans;
  125. },
  126. async deleteSpan(id) {
  127. await this.$services.sequenceLabeling.delete(this.projectId, this.doc.id, id)
  128. await this.listSpan(this.doc.id)
  129. },
  130. async addSpan(startOffset, endOffset, labelId) {
  131. await this.$services.sequenceLabeling.create(this.projectId, this.doc.id, labelId, startOffset, endOffset)
  132. await this.listSpan(this.doc.id)
  133. },
  134. async updateSpan(annotationId, labelId) {
  135. await this.$services.sequenceLabeling.changeLabel(this.projectId, this.doc.id, annotationId, labelId)
  136. await this.listSpan(this.doc.id)
  137. },
  138. async listCategory(id) {
  139. this.categories = await this.$services.textClassification.list(this.projectId, id)
  140. },
  141. async removeCategory(id) {
  142. await this.$services.textClassification.delete(this.projectId, this.doc.id, id)
  143. await this.listCategory(this.doc.id)
  144. },
  145. async addCategory(labelId) {
  146. await this.$services.textClassification.create(this.projectId, this.doc.id, labelId)
  147. await this.listCategory(this.doc.id)
  148. },
  149. async clear() {
  150. await this.$services.sequenceLabeling.clear(this.projectId, this.doc.id)
  151. await this.listSpan(this.doc.id)
  152. },
  153. async updateProgress() {
  154. this.progress = await this.$services.metrics.fetchMyProgress(this.projectId)
  155. },
  156. async confirm() {
  157. await this.$services.example.confirm(this.projectId, this.doc.id)
  158. await this.$fetch()
  159. this.updateProgress()
  160. }
  161. }
  162. }
  163. </script>
  164. <style scoped>
  165. .annotation-text {
  166. font-size: 1.25rem !important;
  167. font-weight: 500;
  168. line-height: 2rem;
  169. font-family: "Roboto", sans-serif !important;
  170. opacity: 0.6;
  171. }
  172. </style>