Commit 9484ca64fa3019727bd28d2a4beddc764913f2e4

Authored by Bartłomiej Nitoń
1 parent 29efb79e

Added facet attribute and popups for binary relations.

client/src/visualizer.js
@@ -126,7 +126,7 @@ var Visualizer = (function($, window, undefined) { @@ -126,7 +126,7 @@ var Visualizer = (function($, window, undefined) {
126 return span; 126 return span;
127 }; 127 };
128 128
129 - var EventDesc = function(id, triggerId, roles, klass) { 129 + var EventDesc = function(id, triggerId, roles, klass, facet="none") {
130 this.id = id; 130 this.id = id;
131 this.triggerId = triggerId; 131 this.triggerId = triggerId;
132 var roleList = this.roles = []; 132 var roleList = this.roles = [];
@@ -137,6 +137,7 @@ var Visualizer = (function($, window, undefined) { @@ -137,6 +137,7 @@ var Visualizer = (function($, window, undefined) {
137 this.equiv = true; 137 this.equiv = true;
138 } else if (klass == "relation") { 138 } else if (klass == "relation") {
139 this.relation = true; 139 this.relation = true;
  140 + this.facet = facet;
140 } 141 }
141 // this.leftSpans = undefined; 142 // this.leftSpans = undefined;
142 // this.rightSpans = undefined; 143 // this.rightSpans = undefined;
@@ -176,6 +177,7 @@ var Visualizer = (function($, window, undefined) { @@ -176,6 +177,7 @@ var Visualizer = (function($, window, undefined) {
176 } else if (eventDesc.relation) { 177 } else if (eventDesc.relation) {
177 this.relation = true; 178 this.relation = true;
178 this.eventDescId = eventNo; 179 this.eventDescId = eventNo;
  180 + this.facet = eventDesc.facet
179 } 181 }
180 // this.marked = undefined; 182 // this.marked = undefined;
181 }; 183 };
@@ -647,8 +649,8 @@ var Visualizer = (function($, window, undefined) { @@ -647,8 +649,8 @@ var Visualizer = (function($, window, undefined) {
647 t2 = rel[2][1][1]; 649 t2 = rel[2][1][1];
648 } 650 }
649 data.eventDescs[rel[0]] = 651 data.eventDescs[rel[0]] =
650 - // (id, triggerId, roles, klass)  
651 - new EventDesc(t1, t1, [[rel[1], t2]], 'relation'); 652 + // (id, triggerId, roles, klass, facet)
  653 + new EventDesc(t1, t1, [[rel[1], t2]], 'relation', rel[3]);
652 }); 654 });
653 655
654 // attributes 656 // attributes
@@ -2929,21 +2931,15 @@ Util.profileStart('before render'); @@ -2929,21 +2931,15 @@ Util.profileStart('before render');
2929 recGetRelatedEquivSpans(related, spans, data); 2931 recGetRelatedEquivSpans(related, spans, data);
2930 }); 2932 });
2931 } 2933 }
2932 - var getRelatedQuasiSpans = function(id, spans, data) {  
2933 - spans[id] = true; 2934 + var getRelatedBinarySpans = function(id, spans, data) {
2934 var span = data.spans[id]; 2935 var span = data.spans[id];
2935 - $.each(span.incoming, function(arcNo, arc) {  
2936 - var related = arc.origin;  
2937 - if (related in spans || arc.type != "Quasi")  
2938 - return true;  
2939 - spans[related] = true;  
2940 - });  
2941 $.each(span.outgoing, function(arcNo, arc) { 2936 $.each(span.outgoing, function(arcNo, arc) {
2942 var related = arc.target; 2937 var related = arc.target;
2943 - if (related in spans || arc.type != "Quasi") 2938 + if (related in spans || !arc.relation)
2944 return true; 2939 return true;
2945 - spans[related] = true;  
2946 - }); 2940 + spans[related] = arc;
  2941 +
  2942 + });
2947 } 2943 }
2948 2944
2949 var highlightSpan = function(span, highlight, highlightGroup, bgColor, highlightRounding) { 2945 var highlightSpan = function(span, highlight, highlightGroup, bgColor, highlightRounding) {
@@ -2976,7 +2972,6 @@ Util.profileStart('before render'); @@ -2976,7 +2972,6 @@ Util.profileStart('before render');
2976 // (spanTypes.SPAN_DEFAULT && spanTypes.SPAN_DEFAULT.bgColor) || 2972 // (spanTypes.SPAN_DEFAULT && spanTypes.SPAN_DEFAULT.bgColor) ||
2977 // '#ffffff'); 2973 // '#ffffff');
2978 var bgColor = "#99FF66"; 2974 var bgColor = "#99FF66";
2979 - var bgColor2 = '#FF66FF';  
2980 2975
2981 highlight = []; 2976 highlight = [];
2982 highlightSpan(span, highlight, highlightGroup, bgColor, highlightRounding); 2977 highlightSpan(span, highlight, highlightGroup, bgColor, highlightRounding);
@@ -2990,31 +2985,24 @@ Util.profileStart('before render'); @@ -2990,31 +2985,24 @@ Util.profileStart('before render');
2990 2985
2991 var related = {}; 2986 var related = {};
2992 recGetRelatedEquivSpans(id, related, data); 2987 recGetRelatedEquivSpans(id, related, data);
2993 -  
2994 - var spanIds = []; 2988 +
2995 $.each(related, function(spanId, dummy) { 2989 $.each(related, function(spanId, dummy) {
2996 - spanIds.push('rect[data-span-id="' + spanId + '"]');  
2997 var relatedSpan = data.spans[spanId]; 2990 var relatedSpan = data.spans[spanId];
2998 highlightSpan(relatedSpan, highlight, highlightGroup, bgColor, highlightRounding); 2991 highlightSpan(relatedSpan, highlight, highlightGroup, bgColor, highlightRounding);
2999 }); 2992 });
3000 -  
3001 - //hack to higlight quasi identity  
3002 - var quasi = {};  
3003 - getRelatedQuasiSpans(id, quasi, data);  
3004 - var count = 0;  
3005 - for(var prop in quasi) {  
3006 - if(quasi.hasOwnProperty(prop))  
3007 - ++count;  
3008 - }  
3009 - if (count > 1) {  
3010 - $.each(quasi, function(spanId, dummy) {  
3011 - spanIds.push('rect[data-span-id="' + spanId + '"]');  
3012 - var relatedSpan = data.spans[spanId];  
3013 - highlightSpan(relatedSpan, highlight, highlightGroup, bgColor2, highlightRounding);  
3014 - });  
3015 - }  
3016 -  
3017 - highlightSpans = $svg.find(spanIds.join(', ')).parent().addClass('highlight'); 2993 +
  2994 + // hack to highlight relations
  2995 + var binary = {};
  2996 + getRelatedBinarySpans(id, binary, data);
  2997 + $.each(binary, function(spanId, arc) {
  2998 + var relatedSpan = data.spans[spanId];
  2999 + var color = relationTypesHash['SPAN_DEFAULT'].color;
  3000 + if (relationTypesHash[arc.type] != null) {
  3001 + color = relationTypesHash[arc.type].color;
  3002 + }
  3003 + highlightSpan(relatedSpan, highlight, highlightGroup, color, highlightRounding);
  3004 + });
  3005 + dispatcher.post('displayRelationsComments', [id, data, relationTypesHash]);
3018 } 3006 }
3019 forceRedraw(); 3007 forceRedraw();
3020 } else if (!that.arcDragOrigin && (id = target.attr('data-arc-role'))) { 3008 } else if (!that.arcDragOrigin && (id = target.attr('data-arc-role'))) {
client/src/visualizer_ui.js
@@ -259,12 +259,45 @@ var VisualizerUI = (function($, window, undefined) { @@ -259,12 +259,45 @@ var VisualizerUI = (function($, window, undefined) {
259 element.css({ top: y, left: x }); 259 element.css({ top: y, left: x });
260 }; 260 };
261 261
  262 + var adjustToSpan = function(spanId, element, offset, top, right) {
  263 + var span = $(spanId);
  264 + var spanX = span.position().left;
  265 + var spanY = span.position().top;
  266 + // get the real width, without wrapping
  267 + element.css({ left: 0, top: 0 });
  268 + var screenHeight = $(window).height();
  269 + var screenWidth = $(window).width();
  270 + // FIXME why the hell is this 22 necessary?!?
  271 + var elementHeight = element.height() + 22;
  272 + var elementWidth = element.width() + 22;
  273 + var x, y;
  274 + offset = offset || 0;
  275 + if (top) {
  276 + y = spanY - elementHeight - offset;
  277 + if (y < 0) top = false;
  278 + }
  279 + if (!top) {
  280 + y = spanY + offset;
  281 + }
  282 + if (right) {
  283 + x = spanX + offset;
  284 + if (x >= screenWidth - elementWidth) right = false;
  285 + }
  286 + if (!right) {
  287 + x = x = spanX - elementWidth - offset;
  288 + }
  289 + if (y < 0) y = 0;
  290 + if (x < 0) x = 0;
  291 + element.css({ top: y, left: x });
  292 + };
  293 +
262 var commentPopup = $('#commentpopup'); 294 var commentPopup = $('#commentpopup');
263 var commentDisplayed = false; 295 var commentDisplayed = false;
264 296
265 var displayCommentTimer = null; 297 var displayCommentTimer = null;
266 var displayComment = function(evt, target, comment, commentText, commentType, immediately) { 298 var displayComment = function(evt, target, comment, commentText, commentType, immediately) {
267 var idtype; 299 var idtype;
  300 +
268 if (commentType) { 301 if (commentType) {
269 // label comment by type, with special case for default note type 302 // label comment by type, with special case for default note type
270 var commentLabel; 303 var commentLabel;
@@ -468,8 +501,58 @@ var VisualizerUI = (function($, window, undefined) { @@ -468,8 +501,58 @@ var VisualizerUI = (function($, window, undefined) {
468 }); 501 });
469 }; 502 };
470 503
  504 + var displayRelationCommentTimers = [];
  505 + var relationsPopupsContainer = $('#relationspopups');
  506 + var displayRelationsComments = function(sourceId, data, relationTypesHash) {
  507 +
  508 + for (timer in displayRelationCommentTimers) {
  509 + clearTimeout(timer);
  510 + }
  511 + displayRelationCommentTimers = [];
  512 +
  513 + var span = data.spans[sourceId];
  514 + $.each(span.outgoing, function(arcNo, arc) {
  515 + var related = arc.target;
  516 + if (!arc.relation)
  517 + return true;
  518 +
  519 + var relType = "Unknown Relation";
  520 + if (relationTypesHash[arc.type] != null) {
  521 + relType = relationTypesHash[arc.type].labels[1];
  522 + }
  523 + var targetId = arc.target;
  524 + var comment = ('<div class="comment_text">' +
  525 + Util.escapeHTML(relType) +
  526 + '</div>');
  527 + var targetFullId = 'rect[data-span-id="' + targetId + '"]';
  528 +
  529 + // display initial comment HTML
  530 + commentLabel = '<b>Facet:</b> ';
  531 + comment += commentLabel + Util.escapeHTMLwithNewlines(arc.facet);
  532 +
  533 + relationsPopupsContainer.append("<div id='relationpopup' class='relTo"+targetId+"'></div>")
  534 + var popup = relationsPopupsContainer.children().last();
  535 +
  536 + var idtype = 'comment_relation';
  537 + popup[0].className = idtype;
  538 + popup.html(comment);
  539 + adjustToSpan(targetFullId, popup, 10, true, true);
  540 +
  541 + /* slight "tooltip" delay to allow highlights to be seen
  542 + before the popup obstructs them. */
  543 + var displayRelationCommentTimer = setTimeout(function() {
  544 +
  545 + popup.stop(true, true).fadeIn();
  546 +
  547 + }, 500);
  548 + displayRelationCommentTimers.push(displayRelationCommentTimer);
  549 +
  550 + });
  551 + };
  552 +
471 var onDocChanged = function() { 553 var onDocChanged = function() {
472 commentPopup.hide(); 554 commentPopup.hide();
  555 + relationsPopupsContainer.empty();
473 commentDisplayed = false; 556 commentDisplayed = false;
474 }; 557 };
475 558
@@ -518,6 +601,7 @@ var VisualizerUI = (function($, window, undefined) { @@ -518,6 +601,7 @@ var VisualizerUI = (function($, window, undefined) {
518 601
519 var hideComment = function() { 602 var hideComment = function() {
520 clearTimeout(displayCommentTimer); 603 clearTimeout(displayCommentTimer);
  604 + relationsPopupsContainer.empty();
521 if (commentDisplayed) { 605 if (commentDisplayed) {
522 commentPopup.stop(true, true).fadeOut(function() { commentDisplayed = false; }); 606 commentPopup.stop(true, true).fadeOut(function() { commentDisplayed = false; });
523 } 607 }
@@ -2284,6 +2368,7 @@ var VisualizerUI = (function($, window, undefined) { @@ -2284,6 +2368,7 @@ var VisualizerUI = (function($, window, undefined) {
2284 on('annotationIsAvailable', annotationIsAvailable). 2368 on('annotationIsAvailable', annotationIsAvailable).
2285 on('messages', displayMessages). 2369 on('messages', displayMessages).
2286 on('displaySpanComment', displaySpanComment). 2370 on('displaySpanComment', displaySpanComment).
  2371 + on('displayRelationsComments', displayRelationsComments).
2287 on('displayArcComment', displayArcComment). 2372 on('displayArcComment', displayArcComment).
2288 on('displaySentComment', displaySentComment). 2373 on('displaySentComment', displaySentComment).
2289 on('docChanged', onDocChanged). 2374 on('docChanged', onDocChanged).
diff.xhtml
@@ -143,6 +143,7 @@ @@ -143,6 +143,7 @@
143 <div id="messages" class="messages"/> 143 <div id="messages" class="messages"/>
144 <div id="pulluptrigger"/> 144 <div id="pulluptrigger"/>
145 <div id="commentpopup"/> 145 <div id="commentpopup"/>
  146 + <div id="relationspopups"/>
146 <div id="header" class="ui-widget"> 147 <div id="header" class="ui-widget">
147 <div id="mainHeader" class="ui-widget-header"> 148 <div id="mainHeader" class="ui-widget-header">
148 <div id="mainlogo" class="logo unselectable">brat</div> 149 <div id="mainlogo" class="logo unselectable">brat</div>
index.xhtml
@@ -105,6 +105,7 @@ @@ -105,6 +105,7 @@
105 <div id="messages" class="messages"/> 105 <div id="messages" class="messages"/>
106 <div id="pulluptrigger"/> 106 <div id="pulluptrigger"/>
107 <div id="commentpopup"/> 107 <div id="commentpopup"/>
  108 + <div id="relationspopups"/>
108 <div id="header" class="ui-widget"> 109 <div id="header" class="ui-widget">
109 <div id="mainHeader" class="ui-widget-header"> 110 <div id="mainHeader" class="ui-widget-header">
110 <div id="mainlogo" class="logo unselectable">brat</div> 111 <div id="mainlogo" class="logo unselectable">brat</div>
offline.xhtml
@@ -48,6 +48,7 @@ @@ -48,6 +48,7 @@
48 <img id="spinner" src="static/img/spinner.gif"/> 48 <img id="spinner" src="static/img/spinner.gif"/>
49 <div id="messages"/> 49 <div id="messages"/>
50 <div id="commentpopup"/> 50 <div id="commentpopup"/>
  51 + <div id="relationspopups"/>
51 <div id="header" class="ui-widget"> 52 <div id="header" class="ui-widget">
52 <div id="mainHeader" class="ui-widget-header"> 53 <div id="mainHeader" class="ui-widget-header">
53 <div id="mainlogo" class="logo unselectable">brat</div> 54 <div id="mainlogo" class="logo unselectable">brat</div>
server/src/annotation.py
@@ -756,18 +756,22 @@ class Annotations(object): @@ -756,18 +756,22 @@ class Annotations(object):
756 except ValueError: 756 except ValueError:
757 raise IdedAnnotationLineSyntaxError(id, self.ann_line, self.ann_line_num+1, input_file_path) 757 raise IdedAnnotationLineSyntaxError(id, self.ann_line, self.ann_line_num+1, input_file_path)
758 758
759 - if len(args) != 2:  
760 - Messager.error('Error parsing relation: must have exactly two arguments') 759 + if len(args) < 2:
  760 + Messager.error('Error parsing relation: must be Arg1 Arg2 (optional)Facet')
761 raise IdedAnnotationLineSyntaxError(id, self.ann_line, self.ann_line_num+1, input_file_path) 761 raise IdedAnnotationLineSyntaxError(id, self.ann_line, self.ann_line_num+1, input_file_path)
762 762
  763 + facet = "none"
  764 + if len(args) == 3:
  765 + facet = args[2][0]
  766 +
763 if args[0][0] == args[1][0]: 767 if args[0][0] == args[1][0]:
764 - Messager.error('Error parsing relation: arguments must not be identical') 768 + Messager.error('Error parsing relation: related entities must not be identical')
765 raise IdedAnnotationLineSyntaxError(id, self.ann_line, self.ann_line_num+1, input_file_path) 769 raise IdedAnnotationLineSyntaxError(id, self.ann_line, self.ann_line_num+1, input_file_path)
766 770
767 return BinaryRelationAnnotation(id, type, 771 return BinaryRelationAnnotation(id, type,
768 args[0][0], args[0][1], 772 args[0][0], args[0][1],
769 args[1][0], args[1][1], 773 args[1][0], args[1][1],
770 - data_tail, source_id=input_file_path) 774 + data_tail, facet, source_id=input_file_path)
771 775
772 def _parse_equiv_annotation(self, dummy, data, data_tail, input_file_path): 776 def _parse_equiv_annotation(self, dummy, data, data_tail, input_file_path):
773 # NOTE: first dummy argument to have a uniform signature with other 777 # NOTE: first dummy argument to have a uniform signature with other
@@ -1541,25 +1545,27 @@ class BinaryRelationAnnotation(IdedAnnotation): @@ -1541,25 +1545,27 @@ class BinaryRelationAnnotation(IdedAnnotation):
1541 1545
1542 Represented in standoff as 1546 Represented in standoff as
1543 1547
1544 - ID\tTYPE ARG1:ID1 ARG2:ID2 1548 + ID\tTYPE ARG1:ID1 ARG2:ID2 Facet(optional)
1545 1549
1546 Where ARG1 and ARG2 are arbitrary (but not identical) labels. 1550 Where ARG1 and ARG2 are arbitrary (but not identical) labels.
1547 """ 1551 """
1548 - def __init__(self, id, type, arg1l, arg1, arg2l, arg2, tail, source_id=None): 1552 + def __init__(self, id, type, arg1l, arg1, arg2l, arg2, tail, facet="none", source_id=None):
1549 IdedAnnotation.__init__(self, id, type, tail, source_id=source_id) 1553 IdedAnnotation.__init__(self, id, type, tail, source_id=source_id)
1550 self.arg1l = arg1l 1554 self.arg1l = arg1l
1551 self.arg1 = arg1 1555 self.arg1 = arg1
1552 self.arg2l = arg2l 1556 self.arg2l = arg2l
1553 self.arg2 = arg2 1557 self.arg2 = arg2
  1558 + self.facet = facet
1554 1559
1555 def __str__(self): 1560 def __str__(self):
1556 - return u'%s\t%s %s:%s %s:%s%s' % ( 1561 + return u'%s\t%s %s:%s %s:%s %s%s' % (
1557 self.id, 1562 self.id,
1558 self.type, 1563 self.type,
1559 self.arg1l, 1564 self.arg1l,
1560 self.arg1, 1565 self.arg1,
1561 self.arg2l, 1566 self.arg2l,
1562 self.arg2, 1567 self.arg2,
  1568 + self.facet,
1563 self.tail 1569 self.tail
1564 ) 1570 )
1565 1571
server/src/document.py
@@ -684,7 +684,7 @@ def _enrich_json_with_data(j_dic, ann_obj): @@ -684,7 +684,7 @@ def _enrich_json_with_data(j_dic, ann_obj):
684 j_dic['relations'].append( 684 j_dic['relations'].append(
685 [unicode(rel_ann.id), unicode(rel_ann.type), 685 [unicode(rel_ann.id), unicode(rel_ann.type),
686 [(rel_ann.arg1l, rel_ann.arg1), 686 [(rel_ann.arg1l, rel_ann.arg1),
687 - (rel_ann.arg2l, rel_ann.arg2)]] 687 + (rel_ann.arg2l, rel_ann.arg2)], rel_ann.facet]
688 ) 688 )
689 689
690 for tb_ann in ann_obj.get_textbounds(): 690 for tb_ann in ann_obj.get_textbounds():
style-vis.css
@@ -393,6 +393,28 @@ fieldset legend { @@ -393,6 +393,28 @@ fieldset legend {
393 border-radius: 3px; 393 border-radius: 3px;
394 max-width: 80%; 394 max-width: 80%;
395 } 395 }
  396 +#relationpopup {
  397 + font-family: 'Liberation Sans', Verdana, Arial, Helvetica, sans-serif;
  398 + position: fixed;
  399 + top: 0;
  400 + left: 0;
  401 + opacity: 0.95;
  402 + padding: 10px;
  403 + display: none;
  404 + border: 1px outset #000000;
  405 + background-color: #f5f5f9;
  406 + /* background-color: #d7e7ee; */
  407 + /* background-color: #eeeeee; */
  408 + color: #000000;
  409 + z-index: 20;
  410 + -moz-box-shadow: 5px 5px 5px #aaaaaa;
  411 + -webkit-box-shadow: 5px 5px 5px #aaaaaa;
  412 + box-shadow: 5px 5px 5px #aaaaaa;
  413 + -moz-border-radius: 3px;
  414 + -webkit-border-radius: 3px;
  415 + border-radius: 3px;
  416 + max-width: 80%;
  417 +}
396 #more_info_readme { 418 #more_info_readme {
397 height: 350px; 419 height: 350px;
398 } 420 }