def extract_montage(self, m, elements): """Extract the montage corresponding to annotation list """ ann = elements[0] if len(elements) == 1: basename = ann.id + "-" + helper.title2id( self.controller.get_title(ann)) + ".webm" else: atypes = set(a.type for a in elements) if len(atypes) == 1: # Single type - use its title for basename basename = helper.title2id(ann.type) + '.webm' else: basename = 'montage.webm' self.controller.gui.render_montage_dialog(elements, basename=basename) return True
def render(self, *p): """Render the current montage. """ self.controller.gui.render_montage_dialog([ w.annotation for w in self.contents ], basename = helper.title2id(self.view_name) + ".ogv", title = _("Extracting %s") % self.view_name, label = _("Exporting montage %(title)s\nto %%(filename)s") % { 'title': self.view_name }) return True
def render(self, *p): """Render the current montage. """ self.controller.gui.render_montage_dialog( [w.annotation for w in self.contents], basename=helper.title2id(self.view_name) + ".ogv", title=_("Extracting %s") % self.view_name, label=_("Exporting montage %(title)s\nto %%(filename)s") % {'title': self.view_name}) return True
def extract_fragment(self, m, ann): """Extract the fragment corresponding to an annotation. """ title = self.controller.get_title(ann) begin = helper.format_time(ann.fragment.begin) end = helper.format_time(ann.fragment.end) self.controller.gui.render_montage_dialog([ ann ], basename = ann.id + "-" + helper.title2id(title) + ".ogv", title = _("Extracting %s") % title, label = _("Exporting annotation %(title)s\nfrom %(begin)s to %(end)s\nto %%(filename)s") % locals()) return True
def new_from_title(self, title): """Generate a new (title, identifier) from a given title. """ root = helper.title2id(title) index = 1 i = "%s%d" % (root, index) while i in self.existing: index += 1 i = "%s%d" % (root, index) if index != 1: title = "%s%d" % (title, index) return title, i
def new_from_title(self, title): """Generate a new (title, identifier) from a given title. """ root=helper.title2id(title) index=1 i="%s%d" % (root, index) while i in self.existing: index += 1 i="%s%d" % (root, index) if index != 1: title="%s%d" % (title, index) return title, i
def save_query(self, *p): """Saves the query in the package. """ l = self.eq.invalid_items() if l: self.log( _("Invalid query.\nThe following fields have an invalid value:\n%s" ) % ", ".join(l)) return True # Update the query self.eq.update_value() if hasattr(self.eq, 'container'): default_id = self.eq.container.id default_title = self.eq.container.title else: default_id = helper.title2id(self._label) default_title = self._label t, i = dialog.get_title_id( title=_("Saving the query..."), text=_("Give a title and identifier for saving the query"), element_title=default_title, element_id=default_id) if i is None: return True q = helper.get_id(self.controller.package.queries, i) # Overwriting an existing query if q: create = False self.controller.notify('EditSessionStart', element=q, immediate=True) else: create = True # Create the query q = self.controller.package.createQuery(ident=i) q.author = config.data.userid q.date = helper.get_timestamp() self.controller.package.queries.append(q) q.title = t q.content.mimetype = 'application/x-advene-simplequery' # Store the query itself in the _interactive query q.content.data = self.eq.model.xml_repr() if create: self.controller.notify('QueryCreate', query=q) else: self.controller.notify('QueryEditEnd', query=q) self.controller.notify('EditSessionEnd', element=q) return q
def save_query(self, *p): """Saves the query in the package. """ l=self.eq.invalid_items() if l: self.log(_("Invalid query.\nThe following fields have an invalid value:\n%s") % ", ".join(l)) return True # Update the query self.eq.update_value() if hasattr(self.eq, 'container'): default_id=self.eq.container.id default_title=self.eq.container.title else: default_id=helper.title2id(self._label) default_title=self._label t, i = dialog.get_title_id(title=_("Saving the query..."), text=_("Give a title and identifier for saving the query"), element_title=default_title, element_id=default_id) if i is None: return True q=helper.get_id(self.controller.package.queries, i) # Overwriting an existing query if q: create=False self.controller.notify('EditSessionStart', element=q, immediate=True) else: create=True # Create the query q=self.controller.package.createQuery(ident=i) q.author=config.data.userid q.date=helper.get_timestamp() self.controller.package.queries.append(q) q.title=t q.content.mimetype='application/x-advene-simplequery' # Store the query itself in the _interactive query q.content.data = self.eq.model.xml_repr() if create: self.controller.notify('QueryCreate', query=q) else: self.controller.notify('QueryEditEnd', query=q) self.controller.notify('EditSessionEnd', element=q) return q
def save_query(self, *p): """Saves the query in the package. """ if hasattr(self.query, 'container'): default_id = self.query.container.id default_title = self.query.container.title else: default_id = helper.title2id(self._label) default_title = self._label t, i = dialog.get_title_id( title=_("Saving the query..."), text=_("Give a title and identifier for saving the query"), element_title=default_title, element_id=default_id) if i is None: return True q = helper.get_id(self.controller.package.queries, i) # Overwriting an existing query if q: create = False else: create = True # Create the query q = self.controller.package.createQuery(ident=i) q.author = config.data.userid q.date = helper.get_timestamp() self.controller.package.queries.append(q) q.title = t if isinstance(self.query, SimpleQuery): q.content.mimetype = 'application/x-advene-simplequery' elif isinstance(self.query, Quicksearch): q.content.mimetype = 'application/x-advene-quicksearch' q.content.data = self.query.xml_repr() if create: self.controller.notify('QueryCreate', query=q) else: self.controller.notify('QueryEditEnd', query=q) return q
def save_query(self, *p): """Saves the query in the package. """ if hasattr(self.query, 'container'): default_id=self.query.container.id default_title=self.query.container.title else: default_id=helper.title2id(self._label) default_title=self._label t, i = dialog.get_title_id(title=_("Saving the query..."), text=_("Give a title and identifier for saving the query"), element_title=default_title, element_id=default_id) if i is None: return True q=helper.get_id(self.controller.package.queries, i) # Overwriting an existing query if q: create=False else: create=True # Create the query q=self.controller.package.createQuery(ident=i) q.author=config.data.userid q.date=helper.get_timestamp() self.controller.package.queries.append(q) q.title=t if isinstance(self.query, SimpleQuery): q.content.mimetype='application/x-advene-simplequery' elif isinstance(self.query, Quicksearch): q.content.mimetype='application/x-advene-quicksearch' q.content.data = self.query.xml_repr() if create: self.controller.notify('QueryCreate', query=q) else: self.controller.notify('QueryEditEnd', query=q) return q
def iterator(self, rows): progress=0.02 incr = 1.0 / self.row_count # Column cache: store (in_time, content) for each column column_cache = {} # Row cache: for coalesced types, store # (in_time, content) indexed by DCP type row_cache = {} for row in rows: row_cache.clear() self.progress(progress, _("Converting #%(num)d / %(count)d") % { 'num': rows.line_num, 'count': self.row_count}) progress += incr t = self.str2time(row[1]) for (label, tc, value) in zip(self.labels[2::2], row[2::2], row[3::2]): label = str(label, 'mac_roman') if tc == 'IN': # Store into column_cache column_cache[label]=( t, str(value, 'mac_roman') ) elif tc == 'OUT': (begin, content) = column_cache.get(label, (0, 'OUT without IN')) if label in type_mapping: # Coalesced type row_cache[label] = (begin, content) continue at = self.label2type.get(label) if at is None: at = self.label2type[label] = self.create_annotation_type(self.schema, helper.title2id(label), title=label) yield { 'begin': begin, 'end': t, 'content': content, 'type': at, } # Process row_cache output = {} for dcp_type, data in row_cache.items(): label, attr = type_mapping[dcp_type] begin, content = data at = self.label2type.get(label) if at is None: at = self.label2type[label] = self.create_annotation_type(self.schema, helper.title2id(label), title=label, mimetype='application/x-advene-structured') at.setMetaData(config.data.namespace, 'representation', 'here/content/parsed/num') info = output.setdefault(at, { 'begin': begin, 'content': [] }) #if info[begin] != begin: # # FIXME: consistency check on begin time. What to do here??? info['content'].append('%s=%s' % (attr, content.replace('\n', ' -- '))) for at, info in output.items(): yield { 'begin': info['begin'], 'end': t, 'content': "\n".join(info['content']), 'type': at, } self.progress(1.0)
def update_id(entry): id_entry.set_text(helper.title2id(unicode(entry.get_text()))) return True
def convert_transcription_cb(self, button=None): if not self.controller.gui: self.message(_("Cannot convert the data: no associated package")) return True d = Gtk.Dialog(title=_("Converting transcription"), parent=self.controller.gui.gui.win, flags=Gtk.DialogFlags.DESTROY_WITH_PARENT, buttons=( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK, )) l=Gtk.Label(label=_("Choose the annotation-type where to create annotations.\n")) l.set_line_wrap(True) l.show() d.vbox.pack_start(l, False, True, 0) # Anticipated declaration of some widgets, which need to be # updated in the handle_new_type_selection callback. new_type_dialog=Gtk.VBox() delete_existing_toggle=Gtk.CheckButton(_("Delete existing annotations in this type")) delete_existing_toggle.set_active(False) ats=list(self.controller.package.annotationTypes) newat=helper.TitledElement(value=None, title=_("Create a new annotation type")) ats.append(newat) def handle_new_type_selection(combo): el=combo.get_current_element() if el == newat: new_type_dialog.show() delete_existing_toggle.set_sensitive(False) else: new_type_dialog.hide() delete_existing_toggle.set_sensitive(True) return True type_selection=dialog.list_selector_widget(members=[ (a, self.controller.get_title(a), self.controller.get_element_color(a)) for a in ats], callback=handle_new_type_selection, preselect=self.controller.package.get_element_by_id(self.options['annotation-type-id'])) hb=Gtk.HBox() hb.pack_start(Gtk.Label(_("Select type") + " "), False, False, 0) hb.pack_start(type_selection, False, True, 0) d.vbox.pack_start(hb, False, True, 0) l=Gtk.Label(label=_("You want to create a new type. Please specify its schema and title.")) l.set_line_wrap(True) l.show() new_type_dialog.pack_start(l, False, True, 0) hb=Gtk.HBox() hb.pack_start(Gtk.Label(_("Title") + " "), False, False, 0) new_title=Gtk.Entry() hb.pack_start(new_title, True, True, 0) new_type_dialog.pack_start(hb, False, True, 0) hb=Gtk.HBox() hb.pack_start(Gtk.Label(_("Containing schema") + " "), False, False, 0) schemas=list(self.controller.package.schemas) schema_selection=dialog.list_selector_widget(members=[ (s, self.controller.get_title(s)) for s in schemas]) hb.pack_start(schema_selection, False, True, 0) new_type_dialog.pack_start(hb, False, True, 0) new_type_dialog.show_all() new_type_dialog.set_no_show_all(True) new_type_dialog.hide() d.vbox.pack_start(new_type_dialog, True, True, 0) l=Gtk.Label() l.set_markup("<b>" + _("Export options") + "</b>") d.vbox.pack_start(l, False, True, 0) d.vbox.pack_start(delete_existing_toggle, False, True, 0) empty_contents_toggle=Gtk.CheckButton(_("Generate annotations for empty contents")) empty_contents_toggle.set_active(self.options['empty-annotations']) d.vbox.pack_start(empty_contents_toggle, False, True, 0) d.connect('key-press-event', dialog.dialog_keypressed_cb) d.show_all() dialog.center_on_mouse(d) finished=None while not finished: res=d.run() if res == Gtk.ResponseType.OK: at=type_selection.get_current_element() if at == newat: new_type_title=new_title.get_text() if new_type_title == '': # Empty title. Generate one. id_=self.controller.package._idgenerator.get_id(AnnotationType) new_type_title=id_ else: id_=helper.title2id(new_type_title) # Check that the id is available if self.controller.package._idgenerator.exists(id_): dialog.message_dialog( _("The %s identifier already exists. Choose another one.") % id_, icon=Gtk.MessageType.WARNING) at=None continue # Creating a new type s=schema_selection.get_current_element() at=s.createAnnotationType(ident=id_) at.author=config.data.userid at.date=helper.get_timestamp() at.title=new_type_title at.mimetype='text/plain' at.setMetaData(config.data.namespace, 'color', next(s.rootPackage._color_palette)) at.setMetaData(config.data.namespace, 'item_color', 'here/tag_color') s.annotationTypes.append(at) self.controller.notify('AnnotationTypeCreate', annotationtype=at) if delete_existing_toggle.get_active(): # Remove all annotations of at type batch_id=object() for a in at.annotations: self.controller.delete_element(a, batch=batch_id) self.options['empty-annotations']=empty_contents_toggle.get_active() finished=True else: at=None finished=True d.destroy() if at is not None: self.options['annotation-type-id'] = at.id ti=TranscriptionImporter(package=self.controller.package, controller=self.controller, defaulttype=at, transcription_edit=self) ti.process_file('transcription') self.controller.package._modified=True self.controller.notify("PackageActivate", package=ti.package) self.message(_('Notes converted')) self.log(ti.statistics_formatted()) # Feedback dialog.message_dialog( _("Conversion completed.\n%s annotations generated.") % ti.statistics['annotation']) return True
def iterator(self): """I iterate over the created annotations. """ logger.warn("Importing using %s model", self.model) self.source_type = self.controller.package.get_element_by_id(self.source_type_id) minconf = self.confidence self.progress(.1, "Sending request to server") if self.split_types: # Dict indexed by entity type name new_atypes = {} else: new_atype = self.ensure_new_type( "concept_%s" % self.source_type_id, title = _("Concepts for %s" % (self.source_type_id))) new_atype.mimetype = 'application/json' new_atype.setMetaData(config.data.namespace, "representation", 'here/content/parsed/label') if self.create_relations: schema = self.create_schema('s_concept') rtype_id = 'concept_relation' rtype = self.package.get_element_by_id(rtype_id) if not rtype: # Create a relation type if it does not exist. rtype = schema.createRelationType(ident=rtype_id) rtype.author = config.data.get_userid() rtype.date = self.timestamp rtype.title = "Related concept" rtype.mimetype='text/plain' rtype.setHackedMemberTypes( ('*', '*') ) schema.relationTypes.append(rtype) self.update_statistics('relation-type') if not hasattr(rtype, 'getHackedMemberTypes'): logger.error("%s is not a valid relation type" % rtype_id) image_scale = self.available_models.get(self.model, {}).get('image_size') if image_scale: logger.warn("Scaling images to (%d, %d) as requested by %s", image_scale, image_scale, self.model) def get_scaled_image(t): """Return the image at the appropriate scale for the selected model. """ original = bytes(self.controller.package.imagecache.get(t)) if image_scale: im = Image.open(BytesIO(original)) im = im.resize((image_scale, image_scale)) buf = BytesIO() im.save(buf, 'PNG') scaled = buf.getvalue() buf.close() else: scaled = original return scaled # Use a requests.session to use a KeepAlive connection to the server session = requests.session() headers = {"Content-Type": "application/json", "Accept": "application/json"} response = session.post(self.url, headers=headers, json={ "model": self.model, 'media_uri': self.package.uri, 'media_filename': self.controller.get_default_media(), 'minimum_confidence': minconf, 'annotations': [ { 'annotationid': a.id, 'begin': a.fragment.begin, 'end': a.fragment.end, 'frames': [ { 'screenshot': base64.encodebytes(get_scaled_image(t)).decode('ascii'), 'timecode': t } for t in (a.fragment.begin, int((a.fragment.begin + a.fragment.end) / 2), a.fragment.end) ] } for a in self.source_type.annotations ] }) output = response.json() if output.get('status') != 200: # Not OK result. Display error message. msg = _("Server error: %s") % output.get('message', _("Server transmission error.")) logger.error(msg) self.output_message = msg return # FIXME: maybe check consistency with media_filename/media_uri? concepts = output.get('data', {}).get('concepts', []) progress = .2 step = .8 / (len(concepts) or 1) self.progress(.2, _("Parsing %d results") % len(concepts)) logger.warn(_("Parsing %d results (level %f)") % (len(concepts), self.confidence)) for item in concepts: # Should not happen, since we pass the parameter to the server if item["confidence"] < minconf: continue a = self.package.get_element_by_id(item['annotationid']) if self.detected_position: begin = item['timecode'] else: begin = a.fragment.begin end = a.fragment.end label = item.get('label') label_id = helper.title2id(label) if label and self.split_types: new_atype = new_atypes.get(label_id) if new_atype is None: # Not defined yet. Create a new one. new_atype = self.ensure_new_type(label_id, title = _("%s concept" % label)) new_atype.mimetype = 'application/json' new_atype.setMetaData(config.data.namespace, "representation", 'here/content/parsed/label') new_atypes[label_id] = new_atype an = yield { 'type': new_atype, 'begin': begin, 'end': end, 'content': json.dumps(item), } if an is not None and self.create_relations: r = self.package.createRelation( ident='_'.join( ('r', a.id, an.id) ), type=rtype, author=config.data.get_userid(), date=self.timestamp, members=(a, an)) r.title = "Relation between %s and %s" % (a.id, an.id) self.package.relations.append(r) self.update_statistics('relation') self.progress(progress) progress += step
def update_id(entry): id_entry.set_text(helper.title2id(entry.get_text())) return True
def iterator(self): """I iterate over the created annotations. """ logger.warning("Importing using %s model", self.model) self.source_type = self.controller.package.get_element_by_id(self.source_type_id) minconf = self.confidence self.progress(.1, "Sending request to server") if self.split_types: # Dict indexed by entity type name new_atypes = {} else: new_atype = self.ensure_new_type("concept_%s" % self.source_type_id, title = _("Concepts for %s" % (self.source_type_id)), mimetype = 'application/json') new_atype.setMetaData(config.data.namespace, "representation", 'here/content/parsed/label') if self.create_relations: schema = self.create_schema('s_concept') rtype_id = 'concept_relation' rtype = self.package.get_element_by_id(rtype_id) if not rtype: # Create a relation type if it does not exist. rtype = schema.createRelationType(ident=rtype_id) rtype.author = config.data.get_userid() rtype.date = self.timestamp rtype.title = "Related concept" rtype.mimetype='text/plain' rtype.setHackedMemberTypes( ('*', '*') ) schema.relationTypes.append(rtype) self.update_statistics('relation-type') if not hasattr(rtype, 'getHackedMemberTypes'): logger.error("%s is not a valid relation type", rtype_id) image_scale = self.available_models.get(self.model, {}).get('image_size') if image_scale: logger.warning("Scaling images to (%d, %d) as requested by %s", image_scale, image_scale, self.model) def get_scaled_image(t): """Return the image at the appropriate scale for the selected model. """ original = bytes(self.controller.package.imagecache.get(t)) if image_scale: im = Image.open(BytesIO(original)) im = im.resize((image_scale, image_scale)) buf = BytesIO() im.save(buf, 'PNG') scaled = buf.getvalue() buf.close() else: scaled = original return scaled # Use a requests.session to use a KeepAlive connection to the server session = requests.session() headers = {"Content-Type": "application/json", "Accept": "application/json"} response = session.post(self.url, headers=headers, json={ "model": self.model, 'media_uri': self.package.uri, 'media_filename': self.controller.get_default_media(), 'minimum_confidence': minconf, 'annotations': [ { 'annotationid': a.id, 'begin': a.fragment.begin, 'end': a.fragment.end, 'frames': [ { 'screenshot': base64.encodebytes(get_scaled_image(t)).decode('ascii'), 'timecode': t } for t in (a.fragment.begin, int((a.fragment.begin + a.fragment.end) / 2), a.fragment.end) ] } for a in self.source_type.annotations ] }) output = response.json() if output.get('status') != 200: # Not OK result. Display error message. msg = _("Server error: %s") % output.get('message', _("Server transmission error.")) logger.error(msg) self.output_message = msg return # FIXME: maybe check consistency with media_filename/media_uri? concepts = output.get('data', {}).get('concepts', []) progress = .2 step = .8 / (len(concepts) or 1) self.progress(.2, _("Parsing %d results") % len(concepts)) logger.warning(_("Parsing %(count)d results (level %(confidence)f)") % { "count": len(concepts), "confidence": self.confidence }) for item in concepts: # Should not happen, since we pass the parameter to the server if item["confidence"] < minconf: continue a = self.package.get_element_by_id(item['annotationid']) if self.detected_position: begin = item['timecode'] else: begin = a.fragment.begin end = a.fragment.end label = item.get('label') label_id = helper.title2id(label) if label and self.split_types: new_atype = new_atypes.get(label_id) if new_atype is None: # Not defined yet. Create a new one. new_atype = self.ensure_new_type(label_id, title = _("%s concept" % label), mimetype = 'application/json') new_atype.setMetaData(config.data.namespace, "representation", 'here/content/parsed/label') new_atypes[label_id] = new_atype an = yield { 'type': new_atype, 'begin': begin, 'end': end, 'content': json.dumps(item), } if an is not None and self.create_relations: r = self.package.createRelation( ident='_'.join( ('r', a.id, an.id) ), type=rtype, author=config.data.get_userid(), date=self.timestamp, members=(a, an)) r.title = "Relation between %s and %s" % (a.id, an.id) self.package.relations.append(r) self.update_statistics('relation') self.progress(progress) progress += step