class KVPair(db.Model): __tablename__ = 'kvpair' key = db.Column(db.String(64), primary_key=True) value = db.Column(db.Text) def __init__(self, key, value): self.key = key self.value = value
class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True) password = db.Column(db.String(256)) auth_token = db.Column(db.String(256)) def __init__(self, username, password): self.username = username self.password = hash_password(password) self.auth_token = pwd_context.encrypt('{0}{1}'.format( username, app.config['SECRET_KEY'])) def verify_password(self, password): return pwd_context.verify(password, self.password) def change_password(self, password): self.password = hash_password(password) self.auth_token = pwd_context.encrypt('{0}{1}'.format( self.username, app.config['SECRET_KEY'])) @property def is_authenticated(self): return True @property def is_active(self): return True @property def is_anonymous(self): return False @property def is_admin(self): if self.username == 'admin': return True else: return False def get_id(self): try: return unicode(self.id) except NameError: return str(self.id) def __repr__(self): return '<User %r>' % self.username def get_auth_token(self): return self.auth_token
class Log(db.Model): __tablename__ = 'log' id = db.Column(db.Integer, primary_key=True) entity = db.Column(db.String(ID_MAX)) user = db.Column(db.String(32)) timestamp = db.Column(db.DateTime) data = db.Column(db.Text) def __init__(self, entity, user, data): self.entity = entity self.data = data self.user = user self.timestamp = datetime.now() def __repr__(self): return 'Entity `%s\', user `%s\' (%s): %s' %\ (self.entity, self.user, self.timestamp, self.data)
class ImportLog(db.Model): __tablename__ = 'ImportLog' id = db.Column(db.Integer, primary_key=True) import_id = db.Column(db.String(ID_MAX)) # XXX: Do not add a foreign key constraint referencing User user = db.Column(db.String(32)) timestamp = db.Column(db.DateTime) data = db.Column(db.Text) def __init__(self, import_id, user, data): self.import_id = import_id self.data = data self.user = user self.timestamp = datetime.now() def __repr__(self): return 'Import `%s\', user `%s\' (%s): %s' %\ (self.import_id, self.user, self.timestamp, self.data)
class DocumentHit(db.Model): __tablename__ = 'documenthit' id = db.Column(db.Integer, primary_key=True) ip = db.Column(db.String(15)) document_id = db.Column( db.Integer, db.ForeignKey("document.id", onupdate='cascade', ondelete='cascade')) referrer = db.Column(db.String(128)) timestamp = db.Column(db.DateTime) def __init__(self, document, ip, referrer): self.document_id = document self.ip = ip self.referrer = clean_url(referrer) if referrer else referrer self.timestamp = datetime.now() def __repr__(self): return '<DocumentHit, time=%s, document=%s, type=%s>' %\ (self.timestamp, self.document_id, self.document.type)
class Data(Document): __tablename__ = 'data' id = db.Column(db.Integer, db.ForeignKey('document.id', onupdate='cascade', ondelete='cascade'), primary_key=True) _format = db.Column('format', db.Enum(*data_formats, name='Format')) __mapper_args__ = {'polymorphic_identity': 'data'} def __init__(self, entity, format, url=None, enabled=True, notes=None): super(Data, self).__init__(entity, url=url, enabled=enabled, notes=notes) if format in data_formats: self.format = format else: raise Exception("Incorrect data format") def __repr__(self): return '<Data(%s), entity=%s, format=%s, url=%s, enabled=%s>' %\ (self.id, self.entity_id, self.format, self.url, self.enabled) @property def data_pid(self): url = '{base_url}/collection/{entity_type}/data/{entity_id}'.format( base_url=app.config['BASE_URL'], entity_type=self.entity.type, entity_id=self.entity_id) return url @property def persistent_uri(self): uri = app.config['BASE_URL'] uri += '/collection/%s/data/%s/%s' % (self.entity.type, self.entity_id, self.format) p = urlparse.urlparse(uri, 'http') netloc = p.netloc or p.path path = p.path if p.netloc else '' p = urlparse.ParseResult('http', netloc, path, *p[3:]) uri = p.geturl() uris = [uri] if kvstore.get('titles_enabled'): uris.append(uri + '/' + self.entity.slug) if self.format == 'html': uris.append(uri[:-5]) return uris def to_dict(self): dict = super(Data, self).to_dict() dict['format'] = self.format return dict @property def format(self): return self._format @format.setter def format(self, value): if value not in data_formats: raise Exception("Incorrect data format") if self._format != value: log( self.entity_id, "Changed format from '%s' to '%s' for %s" % (self._format, value, self)) self._format = value format = db.synonym('_format', descriptor=format)
class Entity(db.Model): __tablename__ = 'entity' _id = db.Column('id', db.String(ID_MAX), primary_key=True) original_id = db.Column(db.String(ID_MAX)) _type = db.Column('type', db.Enum(*entity_types, name='EntityType')) _title = db.Column('title', db.String(TITLE_MAX)) slug = db.Column(db.String(SLUG_MAX)) _documents = db.relationship( "Document", #cascade='all,delete', cascade='all,delete-orphan', backref='entity', order_by='Document.type') def __init__(self, id, type='work', title=None): self.id = id self.type = type self.title = title def __repr__(self): return '<Entity(%s), id=%s (%s), title=%s>' %\ (self.type, self.id, self.original_id, self.title) @property def persistent_uri(self): url = app.config['BASE_URL'] + '/collection/%s' % self.id p = urlparse.urlparse(url, 'http') netloc = p.netloc or p.path path = p.path if p.netloc else '' p = urlparse.ParseResult('http', netloc, path, *p[3:]) url = p.geturl() if kvstore.get('titles_enabled'): return [url, url + '/%s' % self.slug] else: return [url] @property def work_pid(self): url = '{base_url}/collection/{entity_id}'.format( base_url=app.config['BASE_URL'], entity_id=self.id) return url @property def persistent_uris(self): uris = self.persistent_uri for doc in self.documents: uris += doc.persistent_uri return uris @property def title(self): return self._title @title.setter def title(self, value): if self._title != value: log(self.id, "Changed title from '%s' to '%s'" % (self._title, value)) self._title = value self.slug = slugify(value)[:64] if value else SLUG_DEFAULT @property def id(self): return self._id @id.setter def id(self, value): new_id = cleanID(value) ent = Entity.query.filter(Entity.id == new_id).first() # check against self (in case we're changing an existing entity's PID) if ent and not ent == self: # PID already in DB. If the unclean PID is not equal to the existing # entity's original PID, this is probably a collision. if value == ent.original_id: raise EntityPIDExistsException() else: raise EntityCollisionException(ent.original_id) if self._id != new_id: log(self.id, "Changed id from '%s' to '%s'" % (self._id, new_id)) self.original_id = value self._id = new_id @property def type(self): return self._type @type.setter def type(self, value): if value not in entity_types: raise Exception("Wrong entity type") if self._type != value: log(self.id, "Changed type from '%s' to '%s'" % (self._type, value)) self._type = value @property def documents(self): def sort_help(a, b): # Helper function for sorting the documents list if (a.type == 'data') or (b.type == 'data'): return 0 else: return -1 if a.order < b.order else 1 docs = self._documents return sorted(docs, sort_help) @property def active_documents(self): count = 0 for document in self.documents: if document.enabled and\ (document.url != '' or document.url != None): count += 1 return count id = db.synonym('_id', descriptor=id) title = db.synonym('_title', descriptor=title) type = db.synonym('_type', descriptor=type)
class Representation(Document): __tablename__ = 'representation' id = db.Column(db.Integer, db.ForeignKey('document.id', onupdate='cascade', ondelete='cascade'), primary_key=True) order = db.Column(db.Integer) _reference = db.Column('reference', db.Boolean) label = db.Column(db.String(LABEL_MAX)) __mapper_args__ = {'polymorphic_identity': 'representation'} def __init__(self, entity, order, label=None, url=None, enabled=True, notes=None, reference=False): super(Representation, self).__init__(entity, url=url, enabled=enabled, notes=notes) self.order = order self.reference = reference self.label = label def __repr__(self): if self.label: return '<Representation(%s), entity=%s, label=%s, url=%s, order=%s, ref=%s, enabled=%s>' %\ (self.id, self.entity_id, self.label, self.url, self.order, self.reference, self.enabled) else: return '<Representation(%s), entity=%s, url=%s, order=%s, ref=%s>' %\ (self.id, self.entity_id, self.url, self.order, self.reference) @property def persistent_uri(self): uri = app.config['BASE_URL'] uri += '/collection/%s/representation/%s/%s' % ( self.entity.type, self.entity_id, self.order) p = urlparse.urlparse(uri, 'http') netloc = p.netloc or p.path path = p.path if p.netloc else '' p = urlparse.ParseResult('http', netloc, path, *p[3:]) uri = p.geturl() uris = [uri] if kvstore.get('titles_enabled'): uris.append(uri + '/' + self.entity.slug) if self.reference: uri = app.config[ 'BASE_URL'] + '/collection/%s/representation/%s' % ( self.entity.type, self.entity_id) p = urlparse.urlparse(uri, 'http') netloc = p.netloc or p.path path = p.path if p.netloc else '' p = urlparse.ParseResult('http', netloc, path, *p[3:]) uri = p.geturl() uris.append(uri) return uris def to_dict(self): dict = super(Representation, self).to_dict() dict['order'] = self.order dict['reference'] = self.reference dict['label'] = self.label return dict @property def reference(self): return self._reference @reference.setter def reference(self, value): value = bool(value) if self._reference and (not value): log(self.entity_id, "Removed as reference: %s" % self) elif (not self._reference) and value: log(self.entity_id, "Set as reference: %s" % self) self._reference = value reference = db.synonym('_reference', descriptor=reference)
class Document(db.Model): __tablename__ = 'document' id = db.Column(db.Integer, primary_key=True) entity_id = db.Column(db.String(64), db.ForeignKey("entity.id", onupdate='cascade', ondelete='cascade')) _enabled = db.Column('enabled', db.Boolean) notes = db.Column(db.Text) _url = db.Column('url', db.String(512)) type = db.Column(db.String(50)) hits = db.relationship('DocumentHit', #cascade='all,delete', cascade='all, delete-orphan', backref='document') __mapper_args__ = { 'polymorphic_identity': 'document', 'polymorphic_on': type } def __init__(self, entity_id, url=None, enabled=True, notes=None): self.url = url self.entity_id = entity_id self.enabled = enabled self.notes = notes def __repr__(self): raise Exception("Implement me") def to_dict(self): return {'url':self.url if self.url else "", 'enabled':self.enabled, 'id':self.id, 'type':self.type, 'notes':self.notes, 'resolves':self.resolves, 'entity':self.entity_id} @property def persistent_uri(self): raise Exception("Implement me") @property def resolves(self): if self.url: try: r = requests.head(self.url, allow_redirects=True) if r.status_code == 200: return True else: return False except: return False else: return False @property def url(self): return self._url @url.setter def url(self, url): # TODO: URL validation here old = self._url if url: u = urlparse(url) if u.scheme: self._url = url else: self._url = urlunparse(('http',)+u[1:]) else: self._url = None if old != self._url: log(self.entity_id, "Changed URL from '%s' to '%s' for %s" % (old, self._url, self)) @property def enabled(self): return self._enabled @enabled.setter def enabled(self, value): value = bool(value) if self._enabled and (not value): log(self.entity_id, "Disabled document %s" % self) elif (not self._enabled) and value: log(self.entity_id, "Enabled document %s" % self) self._enabled = value enabled = db.synonym('_enabled', descriptor=enabled) url = db.synonym('_url', descriptor=url)