class User(Model, UserMixin): """Users who upload DOIs.""" __tablename__ = 'user' id = Column(Integer, primary_key=True) username = Column(String(100), unique=True, nullable=True) email = Column(String(255), unique=True, nullable=False) password = Column(String(255)) first_name = Column(String(255), nullable=False) last_name = Column(String(255), nullable=False) institution = Column(String(255), nullable=False) active = Column(Boolean()) approved = Column(Boolean(), default=False) confirmed_at = Column(DateTime()) roles = relationship('Role', secondary=roles_users, backref=backref('users', lazy='dynamic')) uris = relationship('Uri', secondary=uris_users, backref=backref('users', lazy='dynamic')) # Maybe create a model mixin that has this functionality for all models? date_created = Column(DateTime, default=datetime.utcnow) last_updated = Column(DateTime, onupdate=datetime.now) @validates('email') def valid_uri(self, key, email): assert email_validator(email) return email @property def full_name(self): return f'{self.first_name} {self.last_name}' @property def is_approved(self): return self.approved def __repr__(self): return f'<User {self.id}: {self.username}>' def prepare_full_token_details(self, token): return { 'surname': self.last_name, 'name': self.first_name, 'authority': 'admin' if self.has_role('admin') else 'user', 'token': token, 'email': self.email, }
class Event(Model): """ Hold data related to the events with unique subject ids. Columns: -------- origin: The service where the event originated (e.g. Twitter). subject_id: identifier of the event, e.g. url of a tweet, doi of citing article. created_at: When this event first occurred on the origin service (specified by the provider). """ __tablename__ = 'event' id = Column(Integer, primary_key=True) uri_id = Column(Integer, ForeignKey('uri.id'), nullable=False) last_updated = Column(DateTime, default=datetime.utcnow) subject_id = Column(String, nullable=False) origin = Column(Integer, nullable=False) created_at = Column(DateTime, nullable=False) is_deleted = Column(Boolean, default=False) raw_events = relationship('RawEvent', backref='event') __table_args__ = (UniqueConstraint('uri_id', 'subject_id'), ) def __str__(self): return f'<Event: {self.id} - {self.uri}>'
class Scrape(Model): """Keep track of when DOIs were checked for new events.""" __tablename__ = 'scrape' id = Column(Integer, primary_key=True) start_date = Column(DateTime, default=datetime.utcnow) end_date = Column(DateTime, nullable=True) # Child Relationships errors = relationship('Error', backref='scrape') raw_events = relationship('RawEvent', backref='scrape') def __str__(self): return f'<Scrape on: {self.start_date}>'
class UriPrefix(Model): """Common prefix amount various URIs (only DOI for now). Columns ------- value: String representation of the doi/uri prefix. """ __tablename__ = 'uri_prefix' id = Column(Integer, primary_key=True) value = Column(String, unique=True, nullable=False) last_checked = Column(DateTime) # # TODO: replace last_checked with another table - one day. uris = relationship('Uri', backref='uri_prefix') # Child Relationships errors = relationship('Error', backref='uri_prefix')
class Uri(Model): """DOI to be scraped. Columns ------- raw: String representation of doi/uri. last_checked: datetime when scraped was triggered for this uri. """ __tablename__ = 'uri' id = Column(Integer, primary_key=True) raw = Column(String, unique=True, nullable=False) last_checked = Column(DateTime) errors = relationship('Error', backref='uri') # Child Relationships events = relationship('Event', backref='uri') urls = relationship('Url', backref='uri') metrics = relationship('Metric', uselist=False, backref='uri') prefix = Column(String, ForeignKey('uri_prefix.value', name='uri_prefix_fkey')) # TODO: Add validator DOIs. @validates('raw') def valid_uri(self, key, uri): pattern = re.compile(r'10\.\d{4,9}/[-._;()/:A-Z0-9]+', re.I) assert pattern.match(uri) return uri # FIXME: this only validates DOIs. def __str__(self): return self.raw @property def owners(self): return self.users.all()