def special_quad_patterns(cls, alias_maker, discussion_id): discussion_alias = alias_maker.get_reln_alias(cls.discussion) return [ QuadMapPatternS( None, FOAF.homepage, PatternIriClass( QUADNAMES.post_external_link_iri, # TODO: Use discussion.get_base_url. # This should be computed outside the DB. get_global_base_url() + '/%s/posts/local:Content/%d', None, ('slug', Unicode, False), ('id', Integer, False)).apply( discussion_alias.slug, cls.id), name=QUADNAMES.post_external_link_map) ]
def special_quad_patterns(cls, alias_maker, discussion_id): discussion_alias = alias_maker.get_reln_alias(cls.discussion) return [ QuadMapPatternS( None, RDF.type, IriClass(VirtRDF.QNAME_ID).apply(Idea.rdf_type), name=QUADNAMES.class_Idea_class), QuadMapPatternS( None, FOAF.homepage, PatternIriClass( QUADNAMES.idea_external_link_iri, # TODO: Use discussion.get_base_url. # This should be computed outside the DB. get_global_base_url() + '/%s/idea/local:Idea/%d', None, ('slug', Unicode, False), ('id', Integer, False)).apply( discussion_alias.slug, cls.id), name=QUADNAMES.idea_external_link_map) ]
class Base(object): def __init__(self, **kw): for k, v in kw.iteritems(): setattr(self, k, v) class A(Base): __tablename__ = "test_a" id = Column(Integer, primary_key=True) name = Column( String, info={'rdf': QuadMapPattern(None, TST.name, None)}) inspect(A).local_table.info = { "rdf_iri": PatternIriClass( TST.ta_iri, 'http://example.com/test#tA/%d', None, ('id', Integer, False)), "rdf_patterns": [QuadMapPattern(None, RDF.type, TST.tA)] } class B(Base): __tablename__ = "test_b" id = Column(Integer, primary_key=True) name = Column(String, info={'rdf': QuadMapPattern(None, TST.name, None)}) type = Column(String(20)) a_id = Column(Integer, ForeignKey(A.id), info={ 'rdf': QuadMapPattern(None, TST.alink)}) a = relation(A) __mapper_args__ = { 'polymorphic_identity': 'B',
class Extract(IdeaContentPositiveLink): """ An extracted part of a Content. A quotation to be referenced by an `Idea`. """ __tablename__ = 'extract' rdf_class = CATALYST.Excerpt # Extract ID represents both the oa:Annotation and the oa:SpecificResource # TODO: This iri is not yet dereferencable. specific_resource_iri = PatternIriClass( QUADNAMES.oa_specific_resource_iri, get_global_base_url() + '/data/SpecificResource/%d', None, ('id', Integer, False)) id = Column(Integer, ForeignKey( 'idea_content_positive_link.id', ondelete='CASCADE', onupdate='CASCADE' ), primary_key=True, info= { 'rdf': QuadMapPatternS(None, ASSEMBL.db_id)}) graph_iri_class = PatternIriClass( QUADNAMES.ExcerptGraph_iri, get_global_base_url() + '/data/ExcerptGraph/%d', None, ('id', Integer, False)) # TODO: body was misused to contain the extract fragment content, # which should belong in the TextFragmentIdentifier, # whereas it was meant to be a comment on the extract # if used from the Web annotator. I'll have to migrate it. body = Column(UnicodeText, nullable=False) # info={'rdf': QuadMapPatternS(None, OA.hasBody)}) discussion_id = Column(Integer, ForeignKey( 'discussion.id', ondelete="CASCADE", onupdate="CASCADE"), nullable=False, index=True, info={'rdf': QuadMapPatternS(None, CATALYST.relevantToConversation)}) discussion = relationship( Discussion, backref=backref('extracts', cascade="all, delete-orphan"), info={'rdf': QuadMapPatternS(None, ASSEMBL.in_conversation)}) important = Column('important', Boolean, server_default='0') def extract_graph_name(self): from pyramid.threadlocal import get_current_registry reg = get_current_registry() host = reg.settings['public_hostname'] return URIRef('http://%s/data/ExcerptGraph/%d' % (host, self.id)) def extract_graph_iri(self): return getattr(QUADNAMES, 'extract_%d_iri' % self.id) @classmethod def special_quad_patterns(cls, alias_maker, discussion_id): return [ QuadMapPatternS( None, OA.hasBody, cls.graph_iri_class.apply(cls.id), name=QUADNAMES.oa_hasBody, conditions=((cls.idea_id != None), (Idea.tombstone_date == None))), QuadMapPatternS( #Content.iri_class().apply(cls.content_id), cls.specific_resource_iri.apply(cls.id), # It would be better to use CATALYST.expressesIdea, # but Virtuoso hates the redundancy. ASSEMBL.resourceExpressesIdea, Idea.iri_class().apply(cls.idea_id), name=QUADNAMES.assembl_postExtractRelatedToIdea, conditions=((cls.idea_id != None), (Idea.tombstone_date == None) # and it's a post extract... treat webpages separately. )), QuadMapPatternS( None, OA.hasTarget, cls.specific_resource_iri.apply(cls.id), name=QUADNAMES.oa_hasTarget), QuadMapPatternS( cls.specific_resource_iri.apply(cls.id), RDF.type, OA.SpecificResource, name=QUADNAMES.oa_SpecificResource_type), QuadMapPatternS( cls.specific_resource_iri.apply(cls.id), ASSEMBL.in_conversation, Discussion.iri_class().apply(cls.discussion_id), name=QUADNAMES.oa_SpecificResource_in_conversation), QuadMapPatternS( cls.specific_resource_iri.apply(cls.id), OA.hasSource, Content.iri_class().apply(cls.content_id), name=QUADNAMES.oa_hasSource), # TODO: Paths # QuadMapPatternS( # AgentProfile.iri_class().apply((cls.content_id, Post.creator_id)), # DCTERMS.contributor, # Idea.iri_class().apply(cls.idea_id), # name=QUADNAMES.assembl_idea_contributor, # conditions=(cls.idea_id != None,)), ] annotation_text = Column(UnicodeText) owner_id = Column( Integer, ForeignKey('agent_profile.id'), nullable=False, ) owner = relationship( 'AgentProfile', foreign_keys=[owner_id], backref='extracts_owned') extract_source = relationship(Content, backref="extracts") extract_ideas = relationship(Idea, backref="extracts") __mapper_args__ = { 'polymorphic_identity': 'assembl:postExtractRelatedToIdea', } @property def target(self): retval = { '@type': self.content.external_typename() } if isinstance(self.content, Post): retval['@id'] = Post.uri_generic(self.content.id) elif self.content.type == 'webpage': retval['url'] = self.content.url return retval def __repr__(self): r = super(Extract, self).__repr__() body = self.body or "" return r[:-1] + body[:20].encode("ascii", "ignore") + ">" def get_target(self): return self.content def get_post(self): if isinstance(self.content, Post): return self.content def infer_text_fragment(self): return self._infer_text_fragment_inner( self.content.get_title(), self.content.get_body(), self.get_post().id) def _infer_text_fragment_inner(self, title, body, post_id): # dead code? If not needs to be refactored with langstrings body = IMAPMailbox.sanitize_html(body, []) quote = self.body.replace("\r", "") try: # for historical reasons quote = quopri.decodestring(quote) except: pass quote = IMAPMailbox.sanitize_html(quote, []) if quote != self.body: self.body = quote quote = quote.replace("\n", "") start = body.find(quote) lookin = 'message-body' if start < 0: xpath = "//div[@id='%s']/div[class='post_title']" % (post_id) start = title.find(quote) if start < 0: return None lookin = 'message-subject' xpath = "//div[@id='message-%s']//div[@class='%s']" % ( Post.uri_generic(post_id), lookin) tfi = self.db.query(TextFragmentIdentifier).filter_by( extract=self).first() if not tfi: tfi = TextFragmentIdentifier(extract=self) tfi.xpath_start = tfi.xpath_end = xpath tfi.offset_start = start tfi.offset_end = start+len(quote) return tfi def send_to_changes(self, connection=None, operation=CrudOperation.UPDATE, discussion_id=None, view_def="changes"): """invoke the modelWatcher on creation""" super(Extract, self).send_to_changes( connection, operation, discussion_id, view_def) watcher = get_model_watcher() if operation == CrudOperation.UPDATE: watcher.processExtractModified(self.id, 0) # no versions yet. elif operation == CrudOperation.DELETE: watcher.processExtractDeleted(self.id) elif operation == CrudOperation.CREATE: watcher.processExtractCreated(self.id) def get_discussion_id(self): return self.discussion_id @classmethod def get_discussion_conditions(cls, discussion_id, alias_maker=None): return (cls.discussion_id == discussion_id,) @classmethod def base_conditions(cls, alias=None, alias_maker=None): # Allow idea-less extracts return () @classmethod def restrict_to_owners(cls, query, user_id): "filter query according to object owners" return query.filter(cls.owner_id == user_id) crud_permissions = CrudPermissions( P_ADD_EXTRACT, P_READ, P_EDIT_EXTRACT, P_EDIT_EXTRACT, P_EDIT_MY_EXTRACT, P_EDIT_MY_EXTRACT)