Browse Source

Extract code from sequence_labeling for reuse

pull/10/head
Hironsan 7 years ago
parent
commit
1673176082
8 changed files with 153 additions and 145 deletions
  1. BIN
      app/db.sqlite3
  2. 6
      app/server/package-lock.json
  3. 2
      app/server/package.json
  4. 8
      app/server/static/js/http.js
  5. 102
      app/server/static/js/mixin.js
  6. 127
      app/server/static/js/sequence_labeling.js
  7. 17
      app/server/templates/annotation/sequence_labeling.html
  8. 36
      app/server/webpack.config.js

BIN
app/db.sqlite3

6
app/server/package-lock.json

@ -4012,9 +4012,9 @@
}
},
"vue-shortkey": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/vue-shortkey/-/vue-shortkey-3.1.5.tgz",
"integrity": "sha512-tva1QsEbEO0Qc4N62ODSophFB4LszwQZzwEn9VaB3uchIJ0wsfVX8zSYi1OkFOuY/MsodDoC/YbbHlwINl27gA==",
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/vue-shortkey/-/vue-shortkey-3.1.6.tgz",
"integrity": "sha512-Nh5cwN86rfNCKINVi16OzN/iVPLvhk1yvVZD8h8E34WlzDRtdm+dngUjfsedz30+Eebbr6c444TmLLIrKqzW9g==",
"requires": {
"vue": "2.5.16"
}

2
app/server/package.json

@ -11,7 +11,7 @@
"dependencies": {
"vue": "^2.5.16",
"vue-loader": "^15.2.4",
"vue-shortkey": "^3.1.5"
"vue-shortkey": "^3.1.6"
},
"devDependencies": {
"webpack": "^4.12.0",

8
app/server/static/js/http.js

@ -0,0 +1,8 @@
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
var base_url = window.location.href.split('/').slice(3, 5).join('/');
let HTTP = axios.create({
baseURL: `/api/${base_url}/`
});
export default HTTP;

102
app/server/static/js/mixin.js

@ -0,0 +1,102 @@
import HTTP from './http.js';
var annotationMixin = {
data: function () {
return {
cur: 0,
items: [{
id: null,
text: '',
labels: []
}],
labels: [],
guideline: 'Here is the Annotation Guideline Text',
total: 0,
remaining: 0,
searchQuery: '',
url: '',
}
},
methods: {
nextPage: async function () {
this.cur += 1;
if (this.cur == this.items.length) {
if (this.next) {
this.url = this.next;
await this.search();
this.cur = 0;
} else {
this.cur = this.items.length - 1;
}
}
this.showMessage(this.cur);
},
prevPage: async function () {
this.cur -= 1;
if (this.cur == -1) {
if (this.prev) {
this.url = this.prev;
await this.search();
this.cur = this.items.length - 1;
} else {
this.cur = 0;
}
}
this.showMessage(this.cur);
},
search: async function () {
await HTTP.get(this.url).then(response => {
this.items = response.data['results'];
this.next = response.data['next'];
this.prev = response.data['previous'];
})
},
showMessage: function (index) {
this.cur = index;
},
submit: async function () {
this.url = `docs/?q=${this.searchQuery}`;
await this.search();
this.cur = 0;
},
updateProgress: function () {
HTTP.get('progress').then(response => {
this.total = response.data['total'];
this.remaining = response.data['remaining'];
})
},
deleteLabel: async function (index) {
var doc_id = this.items[this.cur].id;
var annotation_id = this.items[this.cur]['labels'][index].id;
HTTP.delete(`docs/${doc_id}/annotations/${annotation_id}`).then(response => {
this.items[this.cur]['labels'].splice(index, 1)
});
this.updateProgress();
}
},
created: function () {
HTTP.get('labels').then(response => {
this.labels = response.data
});
this.updateProgress();
this.submit();
},
computed: {
achievement: function () {
var done = this.total - this.remaining;
var percentage = Math.round(done / this.total * 100);
return this.total > 0 ? percentage : 0;
},
progressColor: function () {
if (this.achievement < 30) {
return 'is-danger'
} else if (this.achievement < 70) {
return 'is-warning'
} else {
return 'is-primary'
}
}
}
};
export default annotationMixin;

127
app/server/static/js/sequence_labeling.js

@ -1,14 +1,9 @@
//import Vue from 'vue';
//Vue.use(require('vue-shortkey'));
import Vue from 'vue';
Vue.use(require('vue-shortkey'));
import annotationMixin from './mixin.js';
import HTTP from './http.js';
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
var base_url = window.location.href.split('/').slice(3, 5).join('/');
const HTTP = axios.create({
baseURL: `/api/${base_url}/`
})
const Annotator = {
Vue.component('annotator', {
template: '<div @click="setSelectedRange">\
<span v-for="r in chunks" v-bind:class="{tag: r.color}" v-bind:style="{ color: r.color, backgroundColor: r.background }">{{ r.word }}<button v-if="r.color" class="delete is-small" @click="deleteLabel(r.index)"></button></span>\
</div>',
@ -75,14 +70,14 @@ const Annotator = {
this.entityPositions.splice(index, 1)
},
getBackgroundColor: function (label_id) {
for (item of this.labels) {
for (var item of this.labels) {
if (item.id == label_id) {
return item.background_color
}
}
},
getTextColor: function (label_id) {
for (item of this.labels) {
for (var item of this.labels) {
if (item.id == label_id) {
return item.text_color
}
@ -101,7 +96,9 @@ const Annotator = {
chunks: function () {
var res = [];
var left = 0;
for (let [i, e] of this.sortedEntityPositions.entries()) {
var i = 0;
for (let i in this.sortedEntityPositions) {
var e = this.sortedEntityPositions[i];
var text = this.text.slice(left, e['start_offset']);
res.push({
'word': text,
@ -130,113 +127,11 @@ const Annotator = {
return res
}
}
}
var annotationMixin = {
data: function () {
return {
cur: 0,
items: [{
id: null,
text: '',
labels: []
}],
labels: [],
guideline: 'Here is the Annotation Guideline Text',
total: 0,
remaining: 0,
searchQuery: '',
url: '',
}
},
methods: {
nextPage: async function () {
this.cur += 1;
if (this.cur == this.items.length) {
if (this.next) {
this.url = this.next;
await this.search();
this.cur = 0;
} else {
this.cur = this.items.length - 1;
}
}
this.showMessage(this.cur);
},
prevPage: async function () {
this.cur -= 1;
if (this.cur == -1) {
if (this.prev) {
this.url = this.prev;
await this.search();
this.cur = this.items.length - 1;
} else {
this.cur = 0;
}
}
this.showMessage(this.cur);
},
search: async function () {
await HTTP.get(this.url).then(response => {
this.items = response.data['results'];
this.next = response.data['next'];
this.prev = response.data['previous'];
})
},
showMessage: function (index) {
this.cur = index;
},
submit: async function () {
this.url = `docs/?q=${this.searchQuery}`;
await this.search();
this.cur = 0;
},
updateProgress: function () {
HTTP.get('progress').then(response => {
this.total = response.data['total'];
this.remaining = response.data['remaining'];
})
},
deleteLabel: async function (index) {
var doc_id = this.items[this.cur].id;
var annotation_id = this.items[this.cur]['labels'][index].id;
HTTP.delete(`docs/${doc_id}/annotations/${annotation_id}`).then(response => {
this.items[this.cur]['labels'].splice(index, 1)
});
this.updateProgress();
}
},
created: function () {
HTTP.get('labels').then(response => {
this.labels = response.data
});
this.updateProgress();
this.submit();
},
computed: {
achievement: function () {
var done = this.total - this.remaining;
var percentage = Math.round(done / this.total * 100);
return this.total > 0 ? percentage : 0;
},
progressColor: function () {
if (this.achievement < 30) {
return 'is-danger'
} else if (this.achievement < 70) {
return 'is-warning'
} else {
return 'is-primary'
}
}
}
}
})
var vm = new Vue({
el: '#mail-app',
delimiters: ['[[', ']]'],
components: {
'annotator': Annotator,
},
mixins: [annotationMixin],
methods: {
annotate: function (label_id) {

17
app/server/templates/annotation/sequence_labeling.html

@ -1,13 +1,6 @@
{% extends "annotation/annotation_base.html" %}
{% load static %}
<div class="control">
<div class="tags has-addons">
<span class="tag is-dark">npm</span>
<span class="tag is-info">0.5.0</span>
</div>
</div>
{% block annotation-area %}
<div class="card">
<header class="card-header">
@ -17,7 +10,7 @@
<div class="tags has-addons">
<a class="tag is-medium"
v-bind:style="{ color: label.text_color, backgroundColor: label.background_color }"
v-on:click="annotate(label.id)">
v-on:click="annotate(label.id)" v-shortkey.once="['ctrl', [[ label.shortcut ]] ]" @shortkey="annotate(label.id)">
[[ label.text ]]
</a>
<span class="tag is-medium">[[ label.shortcut ]]</span>
@ -28,12 +21,16 @@
</header>
<div class="card-content">
<div class="content">
<annotator ref="annotator" v-bind:labels="labels" v-bind:entity-positions="items[cur].labels.slice()" v-bind:text="items[cur].text" @delete-label="deleteLabel"></annotator>
<annotator ref="annotator"
v-bind:labels="labels"
v-bind:entity-positions="items[cur].labels.slice()"
v-bind:text="items[cur].text"
@delete-label="deleteLabel"></annotator>
</div>
</div>
</div>
{% endblock %}
{% block footer %}
<script type="text/javascript" src="{% static 'js/sequence_labeling.js' %}"></script>
<script type="text/javascript" src="{% static 'dist/sequence_labeling.js' %}"></script>
{% endblock %}

36
app/server/webpack.config.js

@ -1,23 +1,29 @@
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module.exports = {
mode: 'development',
entry: {
'sequence_labeling': './static/js/sequence_labeling.js'
},
output: {
path: __dirname + '/static/dist',
filename: '[name].js'
},
module: {
rules: [
// ... other rules
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// make sure to include the plugin!
new VueLoaderPlugin()
],
new VueLoaderPlugin()
],
resolve: {
extensions: ['.js', '.vue'],
alias: {
vue$: 'vue/dist/vue.esm.js', //webpack使う場合はこっちを指定する https://jp.vuejs.org/v2/guide/installation.html#%E7%94%A8%E8%AA%9E
},
extensions: ['.js', '.vue'],
alias: {
vue$: 'vue/dist/vue.esm.js',
},
},
}
Loading…
Cancel
Save