Skip to content

Commit f18c5c0

Browse files
committed
add parser for ann
1 parent 0288d68 commit f18c5c0

File tree

9 files changed

+286
-31
lines changed

9 files changed

+286
-31
lines changed

css/style.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ header > button:hover {
6363
border-bottom:1px solid grey;
6464
}
6565

66+
67+
/* input */
68+
#brat-input {
69+
}
70+
#brat-input textarea {
71+
width:100%;
72+
height:300px;
73+
padding:5px;
74+
font-size:15px;
75+
}
76+
77+
78+
/* modal */
6679
.modal {
6780
color:#222;
6881
background:rgba(0,0,0,0.3);

data/example.ann

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
un lock able
2+
T1 BOUND 0 2 un
3+
T2 FREE 4 7 lock
4+
T3 BOUND 8 12 able
5+
R1 unlock controlled:T1 controller:T2
6+
R2 unlockable controller:R1 controlled:T3

data/example2.ann

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Induction of p21 by p53 following DNA damage inhibits both Cdk4 and Cdk2 activities
2+
T1 Gene_or_gene_product 13 16 p21
3+
T2 Gene_or_gene_product 20 23 p53
4+
T3 BioProcess 34 44 DNA damage
5+
T4 Gene_or_gene_product 59 63 Cdk4
6+
T5 Gene_or_gene_product 68 72 Cdk2
7+
T6 Positive_activation 0 9 Induction
8+
T7 Negative_regulation 45 53 inhibits
9+
E1 Positive_activation:T6 Controller:T2 Controlled:T1
10+
E2 Negative_regulation::T7 Controller:E1 Controlled:T4
11+
E3 Negative_regulation::T7 Controller:E1 Controlled:T5

index.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<body>
1111

1212
<header id="header">
13+
Examples:
1314
<select id="dataset">
1415
<option>-- select dataset --</option>
1516
<option>data1</option>
@@ -21,10 +22,28 @@
2122
<option>morpheme2</option>
2223
<option>event annotation</option>
2324
</select>
25+
<button id="custom-annotation">Custom annotation</button>
2426
<button id="taxonomy-toggle">Taxonomy</button>
2527
<button id="options-toggle">Options</button>
2628
</header>
2729

30+
<div id="brat-input" class="modal">
31+
<div>
32+
<header>
33+
<span class="tab active">Custom input in .ann format</span>
34+
</header>
35+
<div class="page active">
36+
<div>
37+
<button><label for="file-input">Load .ann file</label></button>
38+
<input type="file" id="file-input" style="display:none;">
39+
</div>
40+
<textarea></textarea>
41+
<div id="error-message"></div>
42+
</div>
43+
</div>
44+
</div>
45+
46+
2847
<div id="main">
2948
<div id="tooltip">
3049
</div>
@@ -47,14 +66,17 @@
4766
<p><input type="checkbox" data-option="tree"> Open tree view in modal</p>
4867
</div>
4968
</div>
69+
5070
</div>
5171

5272
<script src="libs/svg.js"></script>
5373
<script src="libs/svg.draggable.js"></script>
5474
<script src="libs/jscolor.min.js"></script>
5575
<script src="libs/d3.min.js"></script>
5676

77+
<script src="js/xhr.js"></script>
5778
<script src="js/ymljson.js"></script>
79+
<script src="js/ann-parser.js"></script>
5880
<script src="js/parser.js"></script>
5981
<script src="js/treelayout.js"></script>
6082
<script src="js/main.js"></script>
@@ -69,6 +91,11 @@
6991
<script src="js/components/link.js"></script>
7092

7193
<script type="text/javascript">
94+
95+
// page setup
96+
// manage input
97+
// manage visualization
98+
7299
Main.init();
73100
</script>
74101

js/ann-parser.js

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
const parseAnn = (function() {
2+
class TextBoundMention {
3+
constructor(id, label, charStart, charEnd) {
4+
this.id = id;
5+
this.label = label;
6+
this.charStart = charStart;
7+
this.charEnd = charEnd;
8+
}
9+
}
10+
11+
class EventMention {
12+
constructor(id, label, trigger, args) {
13+
this.id = id;
14+
this.label = label;
15+
this.trigger = trigger;
16+
this.arguments = args;
17+
}
18+
}
19+
20+
class RelationMention {
21+
constructor(id, label, arg1, arg2) {
22+
this.id = id;
23+
this.label = label;
24+
this.arguments = [arg1, arg2];
25+
}
26+
}
27+
28+
class Attribute {
29+
constructor(id, target, attribute, value) {
30+
this.id = id;
31+
this.target = target;
32+
this.attribute = attribute;
33+
this.value = value;
34+
}
35+
}
36+
37+
const re = /:+(?=[TER]\d+$)/;
38+
39+
function parseTextBoundMention(tokens, text) {
40+
const id = +tokens[0].slice(1),
41+
label = tokens[1],
42+
charStart = +tokens[2],
43+
charEnd = +tokens[3];
44+
45+
if (id > 0 && charStart >= 0 && charStart < charEnd && charEnd < text.length) {
46+
return new TextBoundMention('T' + id, label, charStart, charEnd);
47+
}
48+
}
49+
50+
function parseEventMention(tokens, mentions) {
51+
const id = +tokens[0].slice(1),
52+
trigger = tokens[1],
53+
arguments = tokens.slice(2);
54+
55+
if (id > 0 && trigger) {
56+
let split = trigger.split(re);
57+
if (split[0].length > 0 && mentions[split[1]]) {
58+
59+
const em = new EventMention('E' + id, split[0], split[1], []);
60+
61+
arguments.forEach(argument => {
62+
let splitArgument = argument.split(re);
63+
if (splitArgument[0].length > 0 && mentions[splitArgument[1]]) {
64+
em.arguments.push({
65+
type: splitArgument[0],
66+
id: splitArgument[1]
67+
});
68+
}
69+
});
70+
71+
return em;
72+
}
73+
}
74+
}
75+
76+
function parseRelationMention(tokens, mentions) {
77+
const id = +tokens[0].slice(1),
78+
label = tokens[1],
79+
arg1 = tokens[2],
80+
arg2 = tokens[3];
81+
82+
if (id > 0 && arg2) {
83+
const split1 = arg1.split(re),
84+
split2 = arg2.split(re);
85+
86+
if (mentions[split1[1]] && mentions[split2[1]]) {
87+
return new RelationMention('R' + id, label, {
88+
type: split1[0],
89+
id: split1[1]
90+
}, {
91+
type: split2[0],
92+
id: split2[1]
93+
});
94+
}
95+
}
96+
}
97+
98+
function parseAttributes(tokens, mentions) {
99+
const id = +tokens[0].slice(1),
100+
attr = tokens[1],
101+
target = tokens[2];
102+
103+
if (id > 0 && mentions[target]) {
104+
return new Attribute(id, target, attr, tokens.slice(3).join(' '));
105+
}
106+
}
107+
108+
function parse(input) {
109+
110+
var output = {
111+
texts: [],
112+
events: [],
113+
relations: [],
114+
attributes: [],
115+
unparsedLines: [],
116+
mentions: {}
117+
}
118+
119+
let lines = input.split('\n');
120+
121+
let text = lines[0];
122+
if (!text) {
123+
output.unparsedLines = lines;
124+
return output;
125+
}
126+
127+
let unparsedLines = [];
128+
let mentions = {};
129+
130+
for (let i = 1; i < lines.length; ++i) {
131+
const line = lines[i].trim();
132+
if (!line) { continue; }
133+
134+
let tokens = line.split(/\s+/);
135+
136+
let parseIsSuccessful = false;
137+
138+
/** The following IDs are currently supported:
139+
140+
T: text-bound annotation
141+
E: event
142+
R: relation
143+
A: attribute
144+
145+
Normalizations, notes, and equivalence relations are not currently supported
146+
*/
147+
148+
switch (tokens[0].charAt(0)) {
149+
case 'T':
150+
let tbm = parseTextBoundMention(tokens, text);
151+
if (tbm) {
152+
output.texts.push(tbm);
153+
mentions[tbm.id] = tbm;
154+
}
155+
break;
156+
case 'E':
157+
let em = parseEventMention(tokens, mentions);
158+
if (em) {
159+
output.events.push(em);
160+
mentions[em.id] = em;
161+
}
162+
break;
163+
case 'R':
164+
let rm = parseRelationMention(tokens, mentions);
165+
if (rm) {
166+
output.relations.push(rm);
167+
mentions[rm.id] = rm;
168+
}
169+
break;
170+
case 'A':
171+
let a = parseAttribute(tokens, mentions);
172+
if (a) {
173+
output.attributes.push(a);
174+
mentions[a.id] = a;
175+
}
176+
break;
177+
}
178+
179+
if (!parseIsSuccessful) {
180+
unparsedLines.push(line);
181+
}
182+
}
183+
184+
output.mentions = mentions;
185+
output.unparsedLines = unparsedLines;
186+
187+
return output;
188+
}
189+
190+
return parse;
191+
})();

js/main.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ const Main = (function() {
2525
/**
2626
* init: set up singleton classes and create initial drawing
2727
*/
28+
let _initialized = false;
2829
function init() {
2930
// setup
31+
if (_initialized) { return; }
32+
_initialized = true;
3033
body = document.body.getBoundingClientRect();
3134
svg = SVG('main')
3235
.size(body.width, window.innerHeight - body.top - 10);
@@ -38,8 +41,13 @@ const Main = (function() {
3841
tm = new Taxonomy('taxonomy');
3942
tree = new TreeLayout('#tree', svg);
4043

44+
load('data/example2.ann').then(text => {
45+
console.log(text);
46+
console.log(parseAnn(text));
47+
});
48+
return;
4149
// load and render initial dataset by default
42-
changeDataset(6);
50+
// changeDataset(6);
4351

4452
// svg event listeners
4553
svg.on('row-resize', function(e) {
@@ -198,6 +206,9 @@ const Main = (function() {
198206
}
199207
});
200208

209+
document.getElementById('custom-annotation').onclick = function() {
210+
document.getElementById('brat-input').classList.add('open');
211+
}
201212
document.getElementById('options-toggle').onclick = function() {
202213
setActiveTab('options');
203214
}

js/parser.js

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -103,28 +103,12 @@ const Parser = (function() {
103103
/* load a json from a path */
104104
readJson(path, callback) {
105105

106-
var json = path || './data/data1.json';
107-
108-
function loadJSON(path) {
109-
var httpRequest = new XMLHttpRequest();
110-
httpRequest.onreadystatechange = function() {
111-
if (httpRequest.readyState === 4) {
112-
if (httpRequest.status === 200) {
113-
var data = JSON.parse(httpRequest.responseText);
114-
115-
parseData(data);
116-
if (callback) {
117-
callback();
118-
}
119-
}
120-
}
121-
};
122-
httpRequest.open('GET', path);
123-
httpRequest.send();
124-
}
125-
126-
/* parse json file*/
127-
loadJSON(json);
106+
load(path).then((data) => {
107+
parseData(data);
108+
if (callback) {
109+
callback();
110+
}
111+
})
128112
}
129113

130114
get tokens() { return _tokens; }

js/xhr.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function load(url) {
2+
var xhr = new XMLHttpRequest();
3+
xhr.open("GET", url);
4+
xhr.send();
5+
6+
return new Promise((res, err) => {
7+
xhr.onreadystatechange = function() {
8+
if (xhr.readyState === 4) {
9+
if (xhr.status === 200) {
10+
res(xhr.responseText);
11+
}
12+
else {
13+
err(xhr);
14+
}
15+
}
16+
}
17+
});
18+
}

0 commit comments

Comments
 (0)