def _set_attributes(ann_obj, ann, attributes, mods, undo_resp={}): # Find existing attributes (if any) existing_attr_anns = set((a for a in ann_obj.get_attributes() if a.target == ann.id)) #log_info('ATTR: %s' %(existing_attr_anns, )) # Note the existing annotations for undo undo_resp['attributes'] = json_dumps(dict([(e.type, e.value) for e in existing_attr_anns])) for existing_attr_ann in existing_attr_anns: if existing_attr_ann.type not in attributes: # Delete attributes that were un-set existed previously ann_obj.del_annotation(existing_attr_ann) mods.deletion(existing_attr_ann) else: # If the value of the attribute is different, alter it new_value = attributes[existing_attr_ann.type] #log_info('ATTR: "%s" "%s"' % (new_value, existing_attr_ann.value)) if existing_attr_ann.value != new_value: before = str(existing_attr_ann) existing_attr_ann.value = new_value mods.change(before, existing_attr_ann) # The remaining annotations are new and should be created for attr_type, attr_val in attributes.items(): if attr_type not in set((a.type for a in existing_attr_anns)): new_attr = AttributeAnnotation(ann.id, ann_obj.get_new_id('A'), attr_type, '', attr_val) ann_obj.add_annotation(new_attr) mods.addition(new_attr)
def _set_attributes(ann_obj, ann, attributes, mods, undo_resp={}): # Find existing attributes (if any) existing_attr_anns = set((a for a in ann_obj.get_attributes() if a.target == ann.id)) #log_info('ATTR: %s' %(existing_attr_anns, )) # Note the existing annotations for undo undo_resp['attributes'] = json_dumps(dict([(e.type, e.value) for e in existing_attr_anns])) for existing_attr_ann in existing_attr_anns: if existing_attr_ann.type not in attributes: # Delete attributes that were un-set existed previously ann_obj.del_annotation(existing_attr_ann) mods.deletion(existing_attr_ann) else: # If the value of the attribute is different, alter it new_value = attributes[existing_attr_ann.type] #log_info('ATTR: "%s" "%s"' % (new_value, existing_attr_ann.value)) if existing_attr_ann.value != new_value: before = unicode(existing_attr_ann) existing_attr_ann.value = new_value mods.change(before, existing_attr_ann) # The remaining annotations are new and should be created for attr_type, attr_val in attributes.iteritems(): if attr_type not in set((a.type for a in existing_attr_anns)): new_attr = AttributeAnnotation(ann.id, ann_obj.get_new_id('A'), attr_type, '', attr_val) ann_obj.add_annotation(new_attr) mods.addition(new_attr)
def create_comment(collection, document, id, comment=None): directory = collection undo_resp = {} real_dir = real_directory(directory) document = path_join(real_dir, document) projectconf = ProjectConfiguration(real_dir) txt_file_path = document + '.' + TEXT_FILE_SUFFIX # XXX what is this doing here? # path_split(document)[0] with TextAnnotations(document) as ann_obj: # bail as quick as possible if read-only if ann_obj._read_only: raise AnnotationsIsReadOnlyError(ann_obj.get_document()) mods = ModificationTracker() _set_special_comments(ann_obj, id, comment, mods, undo_resp=undo_resp) mods_json = mods.json_response() if undo_resp: mods_json['undo'] = json_dumps(undo_resp) mods_json['annotations'] = _json_from_ann(ann_obj) return mods_json
def _set_normalizations(ann_obj, ann, normalizations, mods, undo_resp={}): # Find existing normalizations (if any) existing_norm_anns = set( (a for a in ann_obj.get_normalizations() if a.target == ann.id)) # Note the existing annotations for undo undo_resp['normalizations'] = json_dumps([(n.refdb, n.refid, n.reftext) for n in existing_norm_anns]) # Organize into dictionaries for easier access old_norms = dict([((n.refdb, n.refid), n) for n in existing_norm_anns]) new_norms = dict([((n[0], n[1]), n[2]) for n in normalizations]) #Messager.info("Old norms: "+str(old_norms)) #Messager.info("New norms: "+str(new_norms)) # sanity check for refdb, refid, refstr in normalizations: # TODO: less aggressive failure assert refdb is not None and refdb.strip( ) != '', "Error: client sent empty norm DB" assert refid is not None and refid.strip( ) != '', "Error: client sent empty norm ID" # (the reference string is allwed to be empty) # Process deletions and updates of existing normalizations for old_norm_id, old_norm in list(old_norms.items()): if old_norm_id not in new_norms: # Delete IDs that were referenced previously but not anymore ann_obj.del_annotation(old_norm) mods.deletion(old_norm) else: # If the text value of the normalizations is different, update # (this shouldn't happen on a stable norm DB, but anyway) new_reftext = new_norms[old_norm_id] if old_norm.reftext != new_reftext: old = str(old_norm) old_norm.reftext = new_reftext mods.change(old, old_norm) # Process new normalizations for new_norm_id, new_reftext in list(new_norms.items()): if new_norm_id not in old_norms: new_id = ann_obj.get_new_id('N') # TODO: avoid magic string value norm_type = 'Reference' new_norm = NormalizationAnnotation(new_id, norm_type, ann.id, new_norm_id[0], new_norm_id[1], '\t' + new_reftext) ann_obj.add_annotation(new_norm) mods.addition(new_norm)
def _set_normalizations(ann_obj, ann, normalizations, mods, undo_resp={}): # Find existing normalizations (if any) existing_norm_anns = set((a for a in ann_obj.get_normalizations() if a.target == ann.id)) # Note the existing annotations for undo undo_resp['normalizations'] = json_dumps([(n.refdb, n.refid, n.reftext) for n in existing_norm_anns]) # Organize into dictionaries for easier access old_norms = dict([((n.refdb,n.refid),n) for n in existing_norm_anns]) new_norms = dict([((n[0],n[1]), n[2]) for n in normalizations]) #Messager.info("Old norms: "+str(old_norms)) #Messager.info("New norms: "+str(new_norms)) # sanity check for refdb, refid, refstr in normalizations: # TODO: less aggressive failure assert refdb is not None and refdb.strip() != '', "Error: client sent empty norm DB" assert refid is not None and refid.strip() != '', "Error: client sent empty norm ID" # (the reference string is allwed to be empty) # Process deletions and updates of existing normalizations for old_norm_id, old_norm in old_norms.items(): if old_norm_id not in new_norms: # Delete IDs that were referenced previously but not anymore ann_obj.del_annotation(old_norm) mods.deletion(old_norm) else: # If the text value of the normalizations is different, update # (this shouldn't happen on a stable norm DB, but anyway) new_reftext = new_norms[old_norm_id] if old_norm.reftext != new_reftext: old = unicode(old_norm) old_norm.reftext = new_reftext mods.change(old, old_norm) # Process new normalizations for new_norm_id, new_reftext in new_norms.items(): if new_norm_id not in old_norms: new_id = ann_obj.get_new_id('N') # TODO: avoid magic string value norm_type = u'Reference' new_norm = NormalizationAnnotation(new_id, norm_type, ann.id, new_norm_id[0], new_norm_id[1], u'\t'+new_reftext) ann_obj.add_annotation(new_norm) mods.addition(new_norm)
def _create_span(collection, document, offsets, _type, attributes=None, normalizations=None, _id=None, comment=None): if _offset_overlaps(offsets): raise SpanOffsetOverlapError(offsets) directory = collection undo_resp = {} _attributes = _parse_attributes(attributes) _normalizations = _parse_span_normalizations(normalizations) #log_info('ATTR: %s' %(_attributes, )) real_dir = real_directory(directory) document = path_join(real_dir, document) projectconf = ProjectConfiguration(real_dir) txt_file_path = document + '.' + TEXT_FILE_SUFFIX working_directory = path_split(document)[0] with TextAnnotations(document) as ann_obj: # bail as quick as possible if read-only if ann_obj._read_only: raise AnnotationsIsReadOnlyError(ann_obj.get_document()) mods = ModificationTracker() if _id is not None: # We are to edit an existing annotation tb_ann, e_ann = _edit_span(ann_obj, mods, _id, offsets, projectconf, _attributes, _type, undo_resp=undo_resp) else: # We are to create a new annotation #Messager.info('offsets ' + '_'.join(str(s) for s in offsets[0]) + ' _ann_by_offset ' + str(ann_obj._ann_by_offset.keys())) #Messager.info('Creating span collection(' + str(collection) + ') document(' + str(document) + ') offsets(' + str(offsets) + ') _type(' + str(_type) + ') exists(' + str(ann_obj.ann_exists_in_offset('_'.join(str(s) for s in offsets[0]))) + ')' ) if ann_obj.ann_exists_in_offset('_'.join(str(s) for s in offsets[0])): raise SpanOffsetOverlapError([str(ann_obj.get_ann_by_offset('_'.join(str(s) for s in offsets[0]))).split()[-1]]) if ann_obj.other_ann_exists_in_offset(offsets[0]): raise SpanOffsetOverlapError([], True) #Messager.info('offsets(' + str(offsets) + ')') ##Messager.info('offsets(' + str(offsets) + ') alloffsets ' + str(ann_obj._ann_offsets)) tb_ann, e_ann = __create_span(ann_obj, mods, _type, offsets, txt_file_path, projectconf, _attributes) undo_resp['action'] = 'add_tb' if e_ann is not None: undo_resp['id'] = e_ann.id else: undo_resp['id'] = tb_ann.id # Determine which annotation attributes, normalizations, # comments etc. should be attached to. If there's an event, # attach to that; otherwise attach to the textbound. if e_ann is not None: # Assign to the event, not the trigger target_ann = e_ann else: target_ann = tb_ann # Set attributes _set_attributes(ann_obj, target_ann, _attributes, mods, undo_resp=undo_resp) # Set normalizations _set_normalizations(ann_obj, target_ann, _normalizations, mods, undo_resp=undo_resp) # Set comments if tb_ann is not None: _set_comments(ann_obj, target_ann, comment, mods, undo_resp=undo_resp) if tb_ann is not None: mods_json = mods.json_response() else: # Hack, probably we had a new-line in the span mods_json = {} Messager.error('Text span contained new-line, rejected', duration=3) if undo_resp: mods_json['undo'] = json_dumps(undo_resp) mods_json['annotations'] = _json_from_ann(ann_obj) return mods_json
_set_normalizations(ann_obj, target_ann, _normalizations, mods, undo_resp=undo_resp) # Set comments if tb_ann is not None: _set_comments(ann_obj, target_ann, comment, mods, undo_resp=undo_resp) if tb_ann is not None: mods_json = mods.json_response() else: # Hack, probably we had a new-line in the span mods_json = {} Messager.error('Text span contained new-line, rejected', duration=3) if undo_resp: mods_json['undo'] = json_dumps(undo_resp) mods_json['annotations'] = _json_from_ann(ann_obj) return mods_json from annotation import BinaryRelationAnnotation def _create_equiv(ann_obj, projectconf, mods, origin, target, type, attributes, old_type, old_target): # due to legacy representation choices for Equivs (i.e. no # unique ID), support for attributes for Equivs would need # some extra work. Getting the easy non-Equiv case first. if attributes is not None: Messager.warning('_create_equiv: attributes for Equiv annotation not supported yet, please tell the devs if you need this feature (mention "issue #799").') attributes = None
def _create_span(collection, document, offsets, _type, attributes=None, normalizations=None, _id=None, comment=None): if _offset_overlaps(offsets): raise SpanOffsetOverlapError(offsets) directory = collection undo_resp = {} _attributes = _parse_attributes(attributes) _normalizations = _parse_span_normalizations(normalizations) #log_info('ATTR: %s' %(_attributes, )) real_dir = real_directory(directory) document = path_join(real_dir, document) projectconf = ProjectConfiguration(real_dir) txt_file_path = document + '.' + TEXT_FILE_SUFFIX path_split(document)[0] with TextAnnotations(document) as ann_obj: # bail as quick as possible if read-only if ann_obj._read_only: raise AnnotationsIsReadOnlyError(ann_obj.get_document()) mods = ModificationTracker() if _id is not None: # We are to edit an existing annotation tb_ann, e_ann = _edit_span(ann_obj, mods, _id, offsets, projectconf, _attributes, _type, undo_resp=undo_resp) else: # We are to create a new annotation tb_ann, e_ann = __create_span( ann_obj, mods, _type, offsets, txt_file_path, projectconf, _attributes) undo_resp['action'] = 'add_tb' if e_ann is not None: undo_resp['id'] = e_ann.id else: undo_resp['id'] = tb_ann.id # Determine which annotation attributes, normalizations, # comments etc. should be attached to. If there's an event, # attach to that; otherwise attach to the textbound. if e_ann is not None: # Assign to the event, not the trigger target_ann = e_ann else: target_ann = tb_ann # Set attributes _set_attributes(ann_obj, target_ann, _attributes, mods, undo_resp=undo_resp) # Set normalizations _set_normalizations(ann_obj, target_ann, _normalizations, mods, undo_resp=undo_resp) # Set comments if tb_ann is not None: _set_comments(ann_obj, target_ann, comment, mods, undo_resp=undo_resp) if tb_ann is not None: mods_json = mods.json_response() else: # Hack, probably we had a new-line in the span mods_json = {} Messager.error( 'Text span contained new-line, rejected', duration=3) if undo_resp: mods_json['undo'] = json_dumps(undo_resp) mods_json['annotations'] = _json_from_ann(ann_obj) return mods_json
def _create_span(collection, document, start, end, _type, attributes=None, normalizations=None, _id=None, comment=None): directory = collection undo_resp = {} _attributes = _parse_attributes(attributes) _normalizations = _parse_span_normalizations(normalizations) #log_info('ATTR: %s' %(_attributes, )) real_dir = real_directory(directory) document = path_join(real_dir, document) projectconf = ProjectConfiguration(real_dir) txt_file_path = document + '.' + TEXT_FILE_SUFFIX working_directory = path_split(document)[0] with TextAnnotations(document) as ann_obj: # bail as quick as possible if read-only if ann_obj._read_only: raise AnnotationsIsReadOnlyError(ann_obj.get_document()) mods = ModificationTracker() if _id is not None: # We are to edit an existing annotation tb_ann, e_ann = _edit_span(ann_obj, mods, _id, start, end, projectconf, _attributes, _type, undo_resp=undo_resp) else: # We are to create a new annotation tb_ann, e_ann = __create_span(ann_obj, mods, _type, start, end, txt_file_path, projectconf, _attributes) undo_resp['action'] = 'add_tb' if e_ann is not None: undo_resp['id'] = e_ann.id else: undo_resp['id'] = tb_ann.id # Determine which annotation attributes, normalizations, # comments etc. should be attached to. If there's an event, # attach to that; otherwise attach to the textbound. if e_ann is not None: target_ann = e_ann else: target_ann = tb_ann # Set annotation attributes _set_attributes(ann_obj, target_ann, _attributes, mods, undo_resp=undo_resp) # Set normalizations _set_normalizations(ann_obj, target_ann, _normalizations, mods, undo_resp=undo_resp) # Handle annotation comments if tb_ann is not None: _set_comments(ann_obj, target_ann, comment, mods, undo_resp=undo_resp) if tb_ann is not None: mods_json = mods.json_response() else: # Hack, probably we had a new-line in the span mods_json = {} Messager.error('Text span contained new-line, rejected', duration=3) if undo_resp: mods_json['undo'] = json_dumps(undo_resp) mods_json['annotations'] = _json_from_ann(ann_obj) return mods_json
def _create_span(collection, document, start, end, _type, attributes=None, normalizations=None, _id=None, comment=None): directory = collection undo_resp = {} _attributes = _parse_span_attributes(attributes) _normalizations = _parse_span_normalizations(normalizations) #log_info('ATTR: %s' %(_attributes, )) real_dir = real_directory(directory) document = path_join(real_dir, document) projectconf = ProjectConfiguration(real_dir) txt_file_path = document + '.' + TEXT_FILE_SUFFIX working_directory = path_split(document)[0] with TextAnnotations(document) as ann_obj: # bail as quick as possible if read-only if ann_obj._read_only: raise AnnotationsIsReadOnlyError(ann_obj.get_document()) mods = ModificationTracker() if _id is not None: # We are to edit an existing annotation tb_ann, e_ann = _edit_span(ann_obj, mods, _id, start, end, projectconf, _attributes, _type, undo_resp=undo_resp) else: # We are to create a new annotation tb_ann, e_ann = __create_span(ann_obj, mods, _type, start, end, txt_file_path, projectconf, _attributes) undo_resp['action'] = 'add_tb' if e_ann is not None: undo_resp['id'] = e_ann.id else: undo_resp['id'] = tb_ann.id # Determine which annotation attributes, normalizations, # comments etc. should be attached to. If there's an event, # attach to that; otherwise attach to the textbound. if e_ann is not None: target_ann = e_ann else: target_ann = tb_ann # Set annotation attributes _set_attributes(ann_obj, target_ann, _attributes, mods, undo_resp=undo_resp) # Set normalizations _set_normalizations(ann_obj, target_ann, _normalizations, mods, undo_resp=undo_resp) # Handle annotation comments if tb_ann is not None: # We are only interested in id;ed comments try: target_ann.id has_id = True except AttributeError: has_id = False if has_id: # Check if there is already an annotation comment for com_ann in ann_obj.get_oneline_comments(): if (com_ann.type == 'AnnotatorNotes' and com_ann.target == target_ann.id): found = com_ann # Note the comment in the undo undo_resp['comment'] = found.tail[1:] break else: found = None if comment: if found is not None: # Change the comment # XXX: Note the ugly tab, it is for parsing the tail before = unicode(found) found.tail = u'\t' + comment mods.change(before, found) else: # Create a new comment new_comment = OnelineCommentAnnotation( target_ann.id, ann_obj.get_new_id('#'), # XXX: Note the ugly tab u'AnnotatorNotes', u'\t' + comment) ann_obj.add_annotation(new_comment) mods.addition(new_comment) else: # We are to erase the annotation if found is not None: ann_obj.del_annotation(found) mods.deletion(found) if tb_ann is not None: mods_json = mods.json_response() else: # Hack, we had a new-line in the span mods_json = {} Messager.error('Text span contained new-line, rejected', duration=3) if undo_resp: mods_json['undo'] = json_dumps(undo_resp) mods_json['annotations'] = _json_from_ann(ann_obj) return mods_json
def _create_span(collection, document, start, end, _type, attributes=None, _id=None, comment=None): directory = collection undo_resp = {} if attributes is None: _attributes = {} else: try: _attributes = json_loads(attributes) except ValueError: # Failed to parse attributes, warn the client Messager.warning((u'Unable to parse attributes string "%s" for ' u'"createSpan", ignoring attributes for request and ' u'assuming no attributes set') % (attributes, )) _attributes = {} ### XXX: Hack since the client is sending back False and True as values... # These are __not__ to be sent, they violate the protocol for _del in [k for k, v in _attributes.items() if v == False]: del _attributes[_del] # These are to be old-style modifiers without values for _revalue in [k for k, v in _attributes.items() if v == True]: _attributes[_revalue] = True ### #log_info('ATTR: %s' %(_attributes, )) real_dir = real_directory(directory) document = path_join(real_dir, document) projectconf = ProjectConfiguration(real_dir) txt_file_path = document + '.' + TEXT_FILE_SUFFIX working_directory = path_split(document)[0] with TextAnnotations(document) as ann_obj: # bail as quick as possible if read-only if ann_obj._read_only: raise AnnotationsIsReadOnlyError(ann_obj.get_document()) mods = ModificationTracker() if _id is not None: # We are to edit an existing annotation tb_ann, e_ann = _edit_span(ann_obj, mods, _id, start, end, projectconf, _attributes, _type, undo_resp=undo_resp) else: # We are to create a new annotation tb_ann, e_ann = __create_span(ann_obj, mods, _type, start, end, txt_file_path, projectconf, _attributes) undo_resp['action'] = 'add_tb' if e_ann is not None: undo_resp['id'] = e_ann.id else: undo_resp['id'] = tb_ann.id # Set annotation attributes if e_ann is not None: # Assign attributes to the event, not the trigger _set_attributes(ann_obj, e_ann, _attributes, mods, undo_resp=undo_resp) else: _set_attributes(ann_obj, tb_ann, _attributes, mods, undo_resp=undo_resp) # Handle annotation comments if tb_ann is not None: # If this is an event, we want to attach the comment to it if e_ann is not None: comment_on = e_ann else: comment_on = tb_ann # We are only interested in id;ed comments try: comment_on.id has_id = True except AttributeError: has_id = False if has_id: # Check if there is already an annotation comment for com_ann in ann_obj.get_oneline_comments(): if (com_ann.type == 'AnnotatorNotes' and com_ann.target == comment_on.id): found = com_ann # Note the comment in the undo undo_resp['comment'] = found.tail[1:] break else: found = None if comment: if found is not None: # Change the comment # XXX: Note the ugly tab, it is for parsing the tail before = unicode(found) found.tail = u'\t' + comment mods.change(before, found) else: # Create a new comment new_comment = OnelineCommentAnnotation( comment_on.id, ann_obj.get_new_id('#'), # XXX: Note the ugly tab u'AnnotatorNotes', u'\t' + comment) ann_obj.add_annotation(new_comment) mods.addition(new_comment) else: # We are to erase the annotation if found is not None: ann_obj.del_annotation(found) mods.deletion(found) if tb_ann is not None: mods_json = mods.json_response() else: # Hack, we had a new-line in the span mods_json = {} Messager.error('Text span contained new-line, rejected', duration=3) if undo_resp: mods_json['undo'] = json_dumps(undo_resp) mods_json['annotations'] = _json_from_ann(ann_obj) return mods_json