class IdxINDEXIdxINDEX(db.Model): """Represent an IdxINDEXIdxINDEX record.""" __tablename__ = 'idxINDEX_idxINDEX' id_virtual = db.Column(db.MediumInteger(9, unsigned=True), db.ForeignKey(IdxINDEX.id), nullable=False, server_default='0', primary_key=True) id_normal = db.Column(db.MediumInteger(9, unsigned=True), db.ForeignKey(IdxINDEX.id), nullable=False, server_default='0', primary_key=True) virtual = db.relationship( IdxINDEX, backref=db.backref('normal'), primaryjoin="and_(IdxINDEXIdxINDEX.id_virtual==IdxINDEX.id)" ) normal = db.relationship( IdxINDEX, backref=db.backref('virtual'), primaryjoin="and_(IdxINDEXIdxINDEX.id_normal==IdxINDEX.id)" ) @staticmethod def is_virtual(id_virtual): """Check if index is virtual.""" return db.session.query( IdxINDEXIdxINDEX.query.filter_by( id_virtual=id_virtual).exists()).scalar()
class KnwKBDDEF(db.Model): """Represent a KnwKBDDEF record.""" __tablename__ = 'knwKBDDEF' id_knwKB = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(KnwKB.id), nullable=False, primary_key=True) id_collection = db.Column(db.MediumInteger(unsigned=True), db.ForeignKey(Collection.id), nullable=True) output_tag = db.Column(db.Text, nullable=True) search_expression = db.Column(db.Text, nullable=True) kb = db.relationship(KnwKB, backref=db.backref('kbdefs', uselist=False, cascade="all, delete-orphan"), single_parent=True) collection = db.relationship(Collection, backref=db.backref('kbdefs')) def to_dict(self): """Return a dict representation of KnwKBDDEF.""" return { 'field': self.output_tag, 'expression': self.search_expression, 'coll_id': self.id_collection, 'collection': self.collection.name if self.collection else None }
class PageList(db.Model): """Represent association between page and list.""" __tablename__ = 'pagesLIST' id = db.Column(db.Integer(15, unsigned=True), nullable=False, primary_key=True, autoincrement=True) """PageList identifier.""" list_id = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(Page.id), nullable=False) """Id of a list.""" page_id = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(Page.id), nullable=False) """Id of a page.""" order = db.Column(db.Integer(15, unsigned=True), nullable=False) list = db.relationship(Page, backref=db.backref("pages", cascade="all, delete-orphan"), foreign_keys=[list_id]) """Relation to the list.""" page = db.relationship(Page, backref=db.backref("lists", cascade="all, delete-orphan"), foreign_keys=[page_id]) """Relation to the page."""
class WtgTAGRecord(db.Model, Serializable): """Connection between Tag and Record.""" __tablename__ = 'wtgTAG_bibrec' __public__ = set(['id_tag', 'id_bibrec', 'date_added']) # tagTAG.id id_tag = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(WtgTAG.id), nullable=False, primary_key=True) # Bibrec.id id_bibrec = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(Bibrec.id), nullable=False, primary_key=True) # Annotation annotation = db.Column( db.Text(convert_unicode=True), default='') # Creation date date_added = db.Column(db.DateTime, default=datetime.now) # Relationships tag = db.relationship(WtgTAG, backref=db.backref('records_association', cascade='all')) tag_query = db.relationship(WtgTAG, backref=db.backref('records_association_query', cascade='all', lazy='dynamic')) bibrec = db.relationship(Bibrec, backref=db.backref('tags_association', cascade='all')) bibrec_query = db.relationship(Bibrec, backref=db.backref('tags_association_query', cascade='all', lazy='dynamic')) def __init__(self, bibrec=None, **kwargs): """TODO.""" super(WtgTAGRecord, self).__init__(**kwargs) if bibrec is not None: self.bibrec = bibrec
class SbmCOLLECTIONSbmDOCTYPE(db.Model): """Represents a SbmCOLLECTIONSbmDOCTYPE record.""" __tablename__ = 'sbmCOLLECTION_sbmDOCTYPE' id = db.Column(db.Integer(11), nullable=False, autoincrement=True, primary_key=True) _id_father = db.Column(db.Integer(11), db.ForeignKey(SbmCOLLECTION.id), nullable=True, name="id_father") id_son = db.Column(db.Char(10), db.ForeignKey(SbmDOCTYPE.sdocname), nullable=False) catalogue_order = db.Column(db.Integer(11), nullable=False, server_default='0') father = db.relationship( SbmCOLLECTION, backref=db.backref('sonDoctype', uselist=False), ) @db.hybrid_property def id_father(self): """Get id_father.""" return self._id_father @id_father.setter def id_father(self, value): """Set id_father.""" self._id_father = value or None
class BsrMETHODDATABUCKET(db.Model): """Represent a BsrMETHODDATABUCKET record.""" __tablename__ = 'bsrMETHODDATABUCKET' id_bsrMETHOD = db.Column(db.MediumInteger(9, unsigned=True), db.ForeignKey(BsrMETHOD.id), autoincrement=False, primary_key=True, nullable=False) bucket_no = db.Column(db.TinyInteger(2), primary_key=True, nullable=False, autoincrement=False) bucket_data = db.Column(db.LargeBinary) bucket_last_value = db.Column(db.String(255)) last_updated = db.Column(db.DateTime) method = db.relationship(BsrMETHOD, backref=db.backref( "buckets", collection_class=attribute_mapped_collection("bucket_no"), cascade="all, delete-orphan" ) ) @property def data(self): """Return bucket data as intbitset.""" return intbitset(self.bucket_data)
class SbmCOLLECTIONSbmCOLLECTION(db.Model): """Represents a SbmCOLLECTIONSbmCOLLECTION record.""" __tablename__ = 'sbmCOLLECTION_sbmCOLLECTION' id = db.Column(db.Integer(11), nullable=False, autoincrement=True, primary_key=True) _id_father = db.Column(db.Integer(11), db.ForeignKey(SbmCOLLECTION.id), nullable=True, name='id_father') id_son = db.Column(db.Integer(11), db.ForeignKey(SbmCOLLECTION.id), nullable=False) catalogue_order = db.Column(db.Integer(11), nullable=False, server_default='0') son = db.relationship( SbmCOLLECTION, backref=db.backref('father', uselist=False), single_parent=True, primaryjoin="and_(SbmCOLLECTIONSbmCOLLECTION.id_son==SbmCOLLECTION.id) " ) father = db.relationship( SbmCOLLECTION, backref=db.backref('son', uselist=False), single_parent=True, primaryjoin= "and_(SbmCOLLECTIONSbmCOLLECTION.id_father==SbmCOLLECTION.id) ") @db.hybrid_property def id_father(self): """Get id_father.""" return self._id_father @id_father.setter def id_father(self, value): """Set id_father.""" self._id_father = value or None
class RnkMETHODNAME(db.Model): """Represent a RnkMETHODNAME record.""" __tablename__ = 'rnkMETHODNAME' id_rnkMETHOD = db.Column(db.MediumInteger(9, unsigned=True), db.ForeignKey(RnkMETHOD.id), primary_key=True) ln = db.Column(db.Char(5), primary_key=True, server_default='') type = db.Column(db.Char(3), primary_key=True, server_default='sn') value = db.Column(db.String(255), nullable=False) method = db.relationship(RnkMETHOD, backref=db.backref('names', lazy='dynamic'))
class UserUsergroup(db.Model): """Represent a UserUsergroup record.""" USER_STATUS = { 'ADMIN': 'A', 'MEMBER': 'M', 'PENDING': 'P', } def __str__(self): """Return string representation.""" return "%s:%s" % (self.user.nickname, self.usergroup.name) __tablename__ = 'user_usergroup' id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), nullable=False, server_default='0', primary_key=True) id_usergroup = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(Usergroup.id), nullable=False, server_default='0', primary_key=True) user_status = db.Column(db.CHAR(1), nullable=False, server_default='') user_status_date = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now) user = db.relationship(User, backref=db.backref('usergroups')) usergroup = db.relationship(Usergroup, backref=db.backref( 'users', cascade="all, delete-orphan")) def is_admin(self): """Return True if user is a admin.""" return self.user_status == self.USER_STATUS['ADMIN']
class BsrMETHODNAME(db.Model): """Represent a BsrMETHODNAME record.""" __tablename__ = 'bsrMETHODNAME' id_bsrMETHOD = db.Column(db.MediumInteger(9, unsigned=True), db.ForeignKey(BsrMETHOD.id), primary_key=True, nullable=False, autoincrement=False) ln = db.Column(db.String(5), primary_key=True, nullable=False) type = db.Column(db.String(3), primary_key=True, nullable=False) value = db.Column(db.String(255), nullable=False) method = db.relationship(BsrMETHOD, backref=db.backref('names', lazy='dynamic'))
class CollectionFormat(db.Model): """Represent a CollectionFormat record.""" __tablename__ = 'collection_format' id_collection = db.Column(db.MediumInteger(9, unsigned=True), db.ForeignKey(Collection.id), primary_key=True) format_code = db.Column('format', db.String(10), primary_key=True) score = db.Column(db.TinyInteger(4, unsigned=True), nullable=False, server_default='0') collection = db.relationship( Collection, backref=db.backref( 'formats', order_by=db.desc(score) ), order_by=db.desc(score)) @property def format(self): """Return output format definition.""" return output_formats[self.format_code]
class Instrument(db.Model): __tablename__ = 'instrument' """ Fields """ id = db.Column(db.Integer(255, unsigned=True), nullable=False, primary_key=True, ) user_id = db.Column(db.Integer(255, unsigned=True), db.ForeignKey(User.id), nullable=False, ) name = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("Name"), description=_(''), ) ) access_right = db.Column(db.String(length=12), nullable=True, default='', info=dict( label=_("Access right"), description=_(''), ) ) embargo_date = db.Column(db.DateTime, nullable=True, default='', info=dict( label=_("Embargo date"), description=_(''), ) ) access_conditions = db.Column(db.String(length=4000), nullable=True, default='', info=dict( label=_("Conditions"), description=_(''), ) ) license = db.Column(db.String(length=50), nullable=True, default='', info=dict( label=_("License"), description=_(''), ) ) """ Relationships """ user = db.relationship( User, backref=db.backref("instruments", uselist=False, cascade="all, delete-orphan")) form_class = InstrumentForm flags = {} values = {} @classmethod def from_json(self, dct): return Instrument(id = dct['idInstrument'], user_id=int(dct['owner']['idUser']), name = dct['name'], access_right = dct['accessRight'], embargo_date = dct['embargoDate'], access_conditions=dct['conditions'], license = dct['license']) def get_owner(self): owner = User.query.filter_by(id=self.user_id).first() current_app.logger.debug(owner) current_app.logger.debug(owner.nickname) return owner.nickname # # Collection management # def get_collection_name(self): return '%s-%s' % (cfg['INSTRUMENTS_COLLECTION_PREFIX'], self.id) def get_collection_dbquery(self): return '%s:%s' % ("980__a", self.get_collection_name()) def get_instrument_records(self, record_types=[], public=None, curated=None): """ Return all records of this instruments""" from invenio.legacy.search_engine import search_pattern_parenthesised from invenio.modules.records.models import Record q = ['980__:%s' % self.get_collection_name()] if record_types: qtypes = ['980__:%s' % t for t in record_types] if len(qtypes) > 1: q.append('(%s)' % ' OR '.join(qtypes)) else: q.extend(qtypes) if public is not None: q.append('983__b:%s' % public) if curated is not None: q.append('983__a:%s' % curated) p = (' AND '.join(q)) recids = search_pattern_parenthesised(p=p) records = Record.query.filter(Record.id.in_(recids)) return records def save_collectionname(self, collection, name): if collection.id: c_name = Collectionname.query.filter_by( id_collection=collection.id, ln=CFG_SITE_LANG, type='ln' ).first() if c_name: update_changed_fields(c_name, dict(value=name)) return c_name c_name = Collectionname( collection=collection, ln=CFG_SITE_LANG, type='ln', value=name, ) db.session.add(c_name) return c_name def save_collectioncollection(self, collection): """Create or update CollectionCollection object.""" dad = Collection.query.filter_by( name=cfg['INSTRUMENTS_PARENT_NAME']).first() if collection.id: c_tree = CollectionCollection.query.filter_by( id_dad=dad.id, id_son=collection.id ).first() if c_tree: update_changed_fields(c_tree, dict( type=cfg['INSTRUMENTS_COLLECTION_TYPE'], score=cfg['INSTRUMENTS_COLLECTION_SCORE'])) return c_tree c_tree = CollectionCollection( dad=dad, son=collection, type=cfg['INSTRUMENTS_COLLECTION_TYPE'], score=cfg['INSTRUMENTS_COLLECTION_SCORE'], ) db.session.add(c_tree) return c_tree def save_collectionformat(self, collection): """Create or update CollectionFormat object.""" fmt = Format.query.filter_by(code=cfg['INSTRUMENTS_OUTPUTFORMAT']).first() if collection.id: c_fmt = CollectionFormat.query.filter_by( id_collection=collection.id ).first() if c_fmt: update_changed_fields(c_fmt, dict(id_format=fmt.id, score=1)) return c_fmt c_fmt = CollectionFormat( collection=collection, id_format=fmt.id, ) db.session.add(c_fmt) return c_fmt def save_acl(self, c): # Role - use Community id, because role name is limited to 32 chars. role_name = 'instrument_role_%s' % self.id role = AccROLE.query.filter_by(name=role_name).first() if not role: rule = 'allow group "%s"\ndeny any' % self.get_group_name() role = AccROLE( name=role_name, description='Owner of instruments %s' % self.name, firerole_def_ser=serialize(compile_role_definition(rule)), firerole_def_src=rule) db.session.add(role) # Argument fields = dict(keyword='collection', value=c.name) arg = AccARGUMENT.query.filter_by(**fields).first() if not arg: arg = AccARGUMENT(**fields) db.session.add(arg) # Action action = AccACTION.query.filter_by(name='viewrestrcoll').first() # User role alluserroles = UserAccROLE.query.filter_by(role=role).all() userrole = None if alluserroles: # Remove any user which is not the owner for ur in alluserroles: if ur.id_user == self.user_id: db.session.delete(ur) else: userrole = ur if not userrole: userrole = UserAccROLE(id_user=self.user_id, role=role) db.session.add(userrole) # Authorization auth = AccAuthorization.query.filter_by(role=role, action=action, argument=arg).first() if not auth: auth = AccAuthorization(role=role, action=action, argument=arg, argumentlistid=1) def save_collection(self): collection_name = self.get_collection_name() c = Collection.query.filter_by(name=collection_name).first() fields = dict( name=collection_name, dbquery=self.get_collection_dbquery() ) if c: update_changed_fields(c, fields) else: c = Collection(**fields) db.session.add(c) db.session.commit() self.collection = c self.save_collectionname(c, self.name) self.save_collectioncollection(c) self.save_collectionformat(c) self.save_acl(c) db.session.commit() def delete_collection(self): if self.collection: CollectionFormat.query.filter_by( id_collection=self.collection.id).delete() Collectionname.query.filter_by( id_collection=self.collection.id).delete() CollectionCollection.query.filter_by( id_son=self.collection.id).delete() db.session.delete(self.collection) db.session.commit() def get_group_name(self): return 'instruments-group-%d' % self.id def save_group(self): g = self.group if not g: g = Group.create(self.get_group_name(), description='Group for instruments %s' % self.id, privacy_policy=PrivacyPolicy.MEMBERS, subscription_policy=SubscriptionPolicy.APPROVAL, is_managed=False, admins=[self.owner]) g.add_member(self.owner) self.group = g db.session.commit() def is_user_allowed(self, user=None): if not user: from flask_login import current_user user = current_user uid = user.get_id() groups = user.get('group', []) return self.user_id == uid or self.group.name in groups def is_empty(self): if self.eresable: # Ensure instruments has not records. from invenio.legacy.search_engine import search_pattern q = '980__:%s' % self.get_collection_name() recids = search_pattern(p=q) if len(recids) != 0: self.eresable = False db.session.commit() return False else: return True return False @classmethod def get_instrument(cls, id): try: return cls.query.get(int(id)) except ValueError: return None @classmethod def get_or_create(cls): instance = cls.get() if instance: return instance else: return cls.create() @classmethod def create(cls): try: obj = cls( user_id=current_user.get_id(), ) db.session.add(obj) db.session.commit() # PROFILE CREATE SIGNAL return obj except IntegrityError as e: db.session.rollback() raise e except Exception as e: raise e @classmethod def get(cls): try: return cls.query.filter_by(user_id=current_user.get_id()).one() except NoResultFound: return None @classmethod def filter_instruments(cls, p, so): """Search for instruments. Helper function which takes from database only those instruments which match search criteria. Uses parameter 'so' to set instruments in the correct order. Parameter 'page' is introduced to restrict results and return only slice of them for the current page. If page == 0 function will return all instruments that match the pattern. """ #current_app.logger.debug(cls) #current_app.logger.debug(p) #current_app.logger.debug(so) query = cls.query if p: query = query.filter(db.or_( cls.id.like("%" + p + "%"), cls.name.like("%" + p + "%"), )) if so in cfg['INSTRUMENTS_SORTING_OPTIONS']: order = so == 'name' and db.asc or db.desc query = query.order_by(order(getattr(cls, so))) return query def get_access_right(self): for tuple in ACCESS_RIGHTS_CHOICES: if tuple[0] == self.access_right: return tuple[1] def get_license(self): licenses = _kb_license_choices(True, True, True) for tuple in licenses: if tuple[0] == self.license: return tuple[1] def get_groups(self): import json groups = findGroupByInstrumentId(self.id) groups_text = "" for group in json.loads(groups): groups_text += group['name'] + " | " return groups_text[:-3] def complete(self): """ Set state of draft to completed. """ self.completed = True def get_form(self, formdata=None, load_draft=True, validate_draft=False): """ Create form instance with draft data and form data if provided. :param formdata: Incoming form data. :param files: Files to ingest into form :param load_draft: True to initialize form with draft data. :param validate_draft: Set to true to validate draft data, when no form data is provided. """ if not self.has_form(): raise FormDoesNotExists(self.id) draft_data = unicodifier(self.values) if load_draft else {} formdata = MultiDict(formdata or {}) form = self.form_class( formdata=formdata, **draft_data ) if formdata: form.reset_field_data(exclude=formdata.keys()) # Set field flags if load_draft and self.flags: form.set_flags(self.flags) if validate_draft and draft_data and formdata is None: form.validate() return form def has_form(self): return self.form_class is not None def process(self, data, complete_form=False): """ Process, validate and store incoming form data and return response. """ # The form is initialized with form and draft data. The original # draft_data is accessible in Field.object_data, Field.raw_data is the # new form data and Field.data is the processed form data or the # original draft data. # # Behind the scences, Form.process() is called, which in turns call # Field.process_data(), Field.process_formdata() and any filters # defined. # # Field.object_data contains the value of process_data(), while # Field.data contains the value of process_formdata() and any filters # applied. # Run form validation which will call Field.pre_valiate(), # Field.validators, Form.validate_<field>() and Field.post_validate(). # Afterwards Field.data has been validated and any errors will be # present in Field.errors. # validated = form.validate() form = self.get_form(formdata=data) validated = form.validate() # Call Form.run_processors() which in turn will call # Field.run_processors() that allow fields to set flags (hide/show) # and values of other fields after the entire formdata has been # processed and validated. validated_flags, validated_data, validated_msgs = ( form.get_flags(), form.data, form.messages ) form.post_process(formfields=[] if complete_form else data.keys()) post_processed_flags, post_processed_data, post_processed_msgs = ( form.get_flags(), form.data, form.messages ) # Save form values self.update(form) # Build result dictionary process_field_names = None if complete_form else data.keys() # Determine if some fields where changed during post-processing. changed_values = dict( (name, value) for name, value in post_processed_data.items() if validated_data[name] != value ) # Determine changed flags changed_flags = dict( (name, flags) for name, flags in post_processed_flags.items() if validated_flags.get(name, []) != flags ) # Determine changed messages changed_msgs = dict( (name, messages) for name, messages in post_processed_msgs.items() if validated_msgs.get(name, []) != messages or process_field_names is None or name in process_field_names ) result = {} if changed_msgs: result['messages'] = changed_msgs if changed_values: result['values'] = changed_values if 'access_right' in data: if post_processed_data['access_right'] == 'open': result['hidden_off'] = ['name', 'license', 'access_right'] result['hidden_on'] = ['embargo_date', 'access_groups', 'access_conditions'] result['disabled_off'] = ['name', 'license', 'access_right'] result['disabled_on'] = ['embargo_date', 'access_groups', 'access_conditions'] if post_processed_data['access_right'] == 'embargoed': result['hidden_off'] = ['name', 'license', 'access_right', 'embargo_date'] result['hidden_on'] = ['access_groups', 'access_conditions'] result['disabled_off'] = ['name', 'license', 'access_right', 'embargo_date'] result['disabled_on'] = ['access_groups', 'access_conditions'] if post_processed_data['access_right'] == 'restricted': result['hidden_off'] = ['name', 'access_right', 'access_groups', 'access_conditions'] result['hidden_on'] = ['license', 'embargo_date'] result['disabled_off'] = ['name', 'access_right', 'access_groups', 'access_conditions'] result['disabled_on'] = ['license', 'embargo_date'] if post_processed_data['access_right'] == 'closed': result['hidden_off'] = ['name', 'access_right'] result['hidden_on'] = ['access_groups', 'access_conditions', 'license', 'embargo_date'] result['disabled_off'] = ['name', 'access_right'] result['disabled_on'] = ['access_groups', 'access_conditions', 'license', 'embargo_date'] return form, validated, result def update(self, form): """ Update draft values and flags with data from form. """ data = dict((key, value) for key, value in form.data.items() if value is not None) self.values = data self.flags = form.get_flags() def __str__(self): instrumentStr = self.name + ',' + self.access_right + ',' + \ self.access_conditions + ',' + self.license + ',' + str(self.embargo_date) return instrumentStr
class CmtRECORDCOMMENT(db.Model): """Represents a CmtRECORDCOMMENT record.""" __tablename__ = 'cmtRECORDCOMMENT' id = db.Column(db.Integer(15, unsigned=True), nullable=False, primary_key=True, autoincrement=True) id_bibrec = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(Bibrec.id), nullable=False, server_default='0') id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), nullable=False, server_default='0') title = db.Column(db.String(255), nullable=False, server_default='') body = db.Column(db.Text, nullable=False) date_creation = db.Column(db.DateTime, nullable=False, server_default='1900-01-01 00:00:00') star_score = db.Column(db.TinyInteger(5, unsigned=True), nullable=False, server_default='0') nb_votes_yes = db.Column(db.Integer(10), nullable=False, server_default='0') nb_votes_total = db.Column(db.Integer(10, unsigned=True), nullable=False, server_default='0') nb_abuse_reports = db.Column(db.Integer(10), nullable=False, server_default='0') status = db.Column(db.Char(2), nullable=False, index=True, server_default='ok') round_name = db.Column(db.String(255), nullable=False, server_default='') restriction = db.Column(db.String(50), nullable=False, server_default='') in_reply_to_id_cmtRECORDCOMMENT = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(id), nullable=False, server_default='0') reply_order_cached_data = db.Column(db.Binary, nullable=True) bibrec = db.relationship(Bibrec, backref='recordcomments') user = db.relationship(User, backref='recordcomments') replies = db.relationship('CmtRECORDCOMMENT', backref=db.backref('parent', remote_side=[id], order_by=date_creation)) @property def is_deleted(self): """Check if is deleted.""" return self.status != 'ok' def is_collapsed(self, id_user): """Return true if the comment is collapsed by user.""" return CmtCOLLAPSED.query.filter( db.and_(CmtCOLLAPSED.id_bibrec == self.id_bibrec, CmtCOLLAPSED.id_cmtRECORDCOMMENT == self.id, CmtCOLLAPSED.id_user == id_user)).count() > 0 @session_manager def collapse(self, id_user): """Collapse comment beloging to user.""" c = CmtCOLLAPSED(id_bibrec=self.id_bibrec, id_cmtRECORDCOMMENT=self.id, id_user=id_user) db.session.add(c) db.session.commit() def expand(self, id_user): """Expand comment beloging to user.""" CmtCOLLAPSED.query.filter( db.and_(CmtCOLLAPSED.id_bibrec == self.id_bibrec, CmtCOLLAPSED.id_cmtRECORDCOMMENT == self.id, CmtCOLLAPSED.id_user == id_user)).delete( synchronize_session=False) __table_args__ = (db.Index('cmtRECORDCOMMENT_reply_order_cached_data', reply_order_cached_data, mysql_length=40), db.Model.__table_args__) @classmethod def count(cls, *criteria, **filters): """Count how many comments.""" return cls.query.filter(*criteria).filter_by(**filters).count()
class GroupAdmin(db.Model): """Represent an administrator of a group.""" __tablename__ = 'groupADMIN' __table_args__ = (db.UniqueConstraint('group_id', 'admin_type', 'admin_id'), db.Model.__table_args__) id = db.Column(db.Integer(15, unsigned=True), nullable=False, primary_key=True, autoincrement=True) """GroupAdmin identifier.""" group_id = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(Group.id), nullable=False, primary_key=True) """Group for membership.""" admin_type = db.Column(db.Unicode(255)) """Generic relationship to an object.""" admin_id = db.Column(db.Integer) """Generic relationship to an object.""" # # Relations # group = db.relationship(Group, backref=db.backref('admins', cascade="all, delete-orphan")) """Group relationship.""" admin = generic_relationship(admin_type, admin_id) """Generic relationship to administrator of group.""" @classmethod def create(cls, group, admin): """Create a new group admin. :param group: Group object. :param admin: Admin object. :returns: Newly created GroupAdmin object. :raises: IntegrityError """ try: obj = cls( group=group, admin=admin, ) db.session.add(obj) db.session.commit() return obj except IntegrityError: db.session.rollback() raise @classmethod def get(cls, group, admin): """Get specific GroupAdmin object.""" try: ga = cls.query.filter_by( group=group, admin_id=admin.get_id(), admin_type=resolve_admin_type(admin)).one() return ga except Exception: return None @classmethod def delete(cls, group, admin): """Delete admin from group. :param group: Group object. :param admin: Admin object. """ try: obj = cls.query.filter(cls.admin == admin, cls.group == group).one() db.session.delete(obj) db.session.commit() except Exception: db.session.rollback() raise @classmethod def query_by_group(cls, group): """Get all admins for a specific group.""" return cls.query.filter_by(group=group) @classmethod def query_by_admin(cls, admin): """Get all groups for for a specific admin.""" return cls.query.filter_by(admin_type=resolve_admin_type(admin), admin_id=admin.get_id()) @classmethod def query_admins_by_group_ids(cls, groups_ids=None): """Get count of admins per group.""" assert groups_ids is None or isinstance(groups_ids, list) query = db.session.query(Group.id, func.count( GroupAdmin.id)).join(GroupAdmin).group_by(Group.id) if groups_ids: query = query.filter(Group.id.in_(groups_ids)) return query
class Membership(db.Model): """Represent a users membership of a group.""" MEMBERSHIP_STATE = { MembershipState.PENDING_ADMIN: _("Pending admin approval"), MembershipState.PENDING_USER: _("Pending member approval"), MembershipState.ACTIVE: _("Active"), } """Membership state choices.""" __tablename__ = 'groupMEMBER' id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), nullable=False, primary_key=True) """User for membership.""" id_group = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(Group.id), nullable=False, primary_key=True) """Group for membership.""" state = db.Column(ChoiceType(MEMBERSHIP_STATE, impl=db.String(1)), nullable=False) """State of membership.""" created = db.Column(db.DateTime, nullable=False, default=datetime.now) """Creation timestamp.""" modified = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now) """Modification timestamp.""" # # Relations # user = db.relationship(User, backref=db.backref('groups')) """User relaionship.""" group = db.relationship(Group, backref=db.backref('members', cascade="all, delete-orphan")) """Group relationship.""" @classmethod def get(cls, group, user): """Get membership for given user and group. :param group: Group object. :param user: User object. :returns: Membership or None. """ try: m = cls.query.filter_by(id_user=user.get_id(), group=group).one() return m except Exception: return None @classmethod def _filter(cls, query, state=MembershipState.ACTIVE, eager=None): """Filter a query result.""" query = query.filter_by(state=state) eager = eager or [] for field in eager: query = query.options(joinedload(field)) return query @classmethod def query_by_user(cls, user, **kwargs): """Get a user's memberships.""" return cls._filter(cls.query.filter_by(id_user=user.get_id()), **kwargs) @classmethod def query_invitations(cls, user, eager=False): """Get all invitations for given user.""" if eager: eager = [Membership.group] return cls.query_by_user(user, state=MembershipState.PENDING_USER, eager=eager) @classmethod def query_requests(cls, admin, eager=False): """Get all pending group requests.""" # Get direct pending request # if hasattr(admin, 'is_admin') and admin.is_superadmin: # q1 = GroupAdmin.query.with_entities( # GroupAdmin.group_id) # else: q1 = GroupAdmin.query_by_admin(admin).with_entities( GroupAdmin.group_id) q2 = Membership.query.filter( Membership.state == MembershipState.PENDING_ADMIN, Membership.id_group.in_(q1), ) # Get request from admin groups your are member of q3 = Membership.query_by_user( user=admin, state=MembershipState.ACTIVE).with_entities(Membership.id_group) q4 = GroupAdmin.query.filter( GroupAdmin.admin_type == 'Group', GroupAdmin.admin_id.in_(q3)).with_entities(GroupAdmin.group_id) q5 = Membership.query.filter( Membership.state == MembershipState.PENDING_ADMIN, Membership.id_group.in_(q4)) query = q2.union(q5) return query @classmethod def query_by_group(cls, group_or_id, with_invitations=False, **kwargs): """Get a group's members.""" if isinstance(group_or_id, Group): id_group = group_or_id.id else: id_group = group_or_id if not with_invitations: return cls._filter(cls.query.filter_by(id_group=id_group), **kwargs) else: return cls.query.filter( Membership.id_group == id_group, db.or_(Membership.state == MembershipState.PENDING_USER, Membership.state == MembershipState.ACTIVE)) @classmethod def search(cls, query, q): """Modify query as so include only specific members. :param query: Query object. :param str q: Search string. :returs: Query object. """ query = query.join(User).filter( db.or_(User.nickname.like("%" + q + "%"), User.email.like("%" + q + "%"))) return query @classmethod def order(cls, query, field, s): """Modify query as so to order the results. :param query: Query object. :param str s: Orderinig: ``asc`` or ``desc``. :returs: Query object. """ if s == "asc": query = query.order_by(asc(field)) elif s == "desc": query = query.order_by(desc(field)) return query @classmethod def create(cls, group, user, state=MembershipState.ACTIVE): """Create a new membership.""" try: membership = cls( id_user=user.get_id(), id_group=group.id, state=state, ) db.session.add(membership) db.session.commit() return membership except IntegrityError: db.session.rollback() raise @classmethod def delete(cls, group, user): """Delete membership.""" try: cls.query.filter_by(group=group, id_user=user.get_id()).delete() db.session.commit() except Exception: db.session.rollback() raise def accept(self): """Activate membership.""" self.state = MembershipState.ACTIVE db.session.commit() def reject(self): """Remove membership.""" try: db.session.delete(self) db.session.commit() except Exception: db.session.rollback() raise def is_active(self): """Check if membership is in an active state.""" return self.state == MembershipState.ACTIVE
class Token(db.Model): """A bearer token is the final token that can be used by the client.""" __tablename__ = 'oauth2TOKEN' id = db.Column(db.Integer, primary_key=True, autoincrement=True) """Object ID.""" client_id = db.Column( db.String(255), db.ForeignKey('oauth2CLIENT.client_id'), nullable=False, ) """Foreign key to client application.""" client = db.relationship( 'Client', backref=db.backref( 'oauth2tokens', cascade="all, delete-orphan" )) """SQLAlchemy relationship to client application.""" user_id = db.Column( db.Integer(15, unsigned=True), db.ForeignKey('user.id'), nullable=True ) """Foreign key to user.""" user = db.relationship( User, backref=db.backref( "oauth2tokens", cascade="all, delete-orphan", ) ) """SQLAlchemy relationship to user.""" token_type = db.Column(db.String(255), default='bearer') """Token type - only bearer is supported at the moment.""" access_token = db.Column(String255EncryptedType( type_in=db.String(255), key=secret_key), unique=True ) refresh_token = db.Column(String255EncryptedType( type_in=db.String(255), key=secret_key, engine=NoneAesEngine), unique=True, nullable=True ) expires = db.Column(db.DateTime, nullable=True) _scopes = db.Column(db.Text) is_personal = db.Column(db.Boolean, default=False) """Personal accesss token.""" is_internal = db.Column(db.Boolean, default=False) """Determines if token is an internally generated token.""" @property def scopes(self): """Return all scopes.""" if self._scopes: return self._scopes.split() return [] @scopes.setter def scopes(self, scopes): """Set scopes.""" validate_scopes(scopes) self._scopes = " ".join(set(scopes)) if scopes else "" def get_visible_scopes(self): """Get list of non-internal scopes for token.""" from .registry import scopes as scopes_registry return [k for k, s in scopes_registry.choices() if k in self.scopes] @classmethod def create_personal(cls, name, user_id, scopes=None, is_internal=False): """Create a personal access token. A token that is bound to a specific user and which doesn't expire, i.e. similar to the concept of an API key. """ scopes = " ".join(scopes) if scopes else "" c = Client( name=name, user_id=user_id, is_internal=True, is_confidential=False, _default_scopes=scopes ) c.gen_salt() t = Token( client_id=c.client_id, user_id=user_id, access_token=gen_salt( current_app.config.get('OAUTH2_TOKEN_PERSONAL_SALT_LEN') ), expires=None, _scopes=scopes, is_personal=True, is_internal=is_internal, ) db.session.add(c) db.session.add(t) db.session.commit() return t
class UserProfile(db.Model): __tablename__ = 'UserProfile' """ Fields """ user_id = db.Column(db.Integer(255, unsigned=True), db.ForeignKey(User.id), nullable=False, primary_key=True, ) name = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("Name"), description=_(''), ) ) institution = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("Institution"), description=_(''), ) ) email = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("Contact e-mail"), description=_(''), ) ) social_profiles = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("Social networks"), description=_(''), ) ) ei_user = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("User name"), description=_(''), ) ) ei_pass = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("Password"), description=_(''), ) ) user_proxy = db.Column(db.Text(length=10000), nullable=True, default='',) csr_priv_key = db.Column(db.Text(length=10000), nullable=True, default='') ssh_public_key = db.Column(db.Text(length=2000), nullable=True, default='', info=dict( label=_("SSH public key"), description=_('Paste your SSH public key, ' 'it\'s important to copy the ' 'key exactly without adding ' 'newlines or whitespace.'), ) ) user_db = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("User DB"), description=_(''), ) ) pass_db = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("Password DB"), description=_(''), ) ) token_onedata = db.Column(db.String(length=255), nullable=True, default='', info=dict( label=_("OneData token"), description=_(''), ) ) created = db.Column(db.DateTime, nullable=False, default=datetime.now) modified = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now) """ Relationships """ user = db.relationship( User, backref=db.backref("UserProfile", uselist=False, cascade="all, delete-orphan")) @classmethod def create(cls): try: obj = cls( user_id=current_user.get_id(), ) db.session.add(obj) db.session.commit() # PROFILE CREATE SIGNAL return obj except IntegrityError as e: db.session.rollback() raise e except Exception as e: raise e @classmethod def get(cls): try: return cls.query.filter_by(user_id=current_user.get_id()).one() except NoResultFound: return None @classmethod def get_or_create(cls): instance = cls.get() if instance: return instance else: return cls.create() def update(self, **data): for value in data: setattr(self, value, data[value]) try: db.session.commit() return self except Exception as e: raise e
class KnwKBRVAL(db.Model): """Represent a KnwKBRVAL record.""" __tablename__ = 'knwKBRVAL' m_key = db.Column(db.String(255), nullable=False, primary_key=True, index=True) m_value = db.Column(db.Text().with_variant(mysql.TEXT(30), 'mysql'), nullable=False) id_knwKB = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(KnwKB.id), nullable=False, server_default='0', primary_key=True) kb = db.relationship( KnwKB, backref=db.backref( 'kbrvals', cascade="all, delete-orphan", collection_class=attribute_mapped_collection("m_key"))) @staticmethod def query_kb_mappings(kbid, sortby="to", key="", value="", match_type="s"): """Return a list of all mappings from the given kb, ordered by key. If key given, give only those with left side (mapFrom) = key. If value given, give only those with right side (mapTo) = value. :param kb_name: knowledge base name. if "", return all :param sortby: the sorting criteria ('from' or 'to') :param key: return only entries where key matches this :param value: return only entries where value matches this :param match_type: s=substring, e=exact, sw=startswith """ # query query = KnwKBRVAL.query.filter(KnwKBRVAL.id_knwKB == kbid) # filter if len(key) > 0: if match_type == "s": key = "%" + key + "%" elif match_type == "sw": key = key + "%" else: key = '%' if len(value) > 0: if match_type == "s": value = "%" + value + "%" elif match_type == "sw": value = value + "%" else: value = '%' query = query.filter(KnwKBRVAL.m_key.like(key), KnwKBRVAL.m_value.like(value)) # order by if sortby == "from": query = query.order_by(KnwKBRVAL.m_key) else: query = query.order_by(KnwKBRVAL.m_value) return query def to_dict(self): """Return a dict representation of KnwKBRVAL.""" # FIXME remove 'id' dependency from invenio modules return { 'id': self.m_key + "_" + str(self.id_knwKB), 'key': self.m_key, 'value': self.m_value, 'kbid': self.kb.id if self.kb else None, 'kbname': self.kb.name if self.kb else None }
class BibWorkflowObject(db.Model): """Data model for wrapping data being run in the workflows. Main object being passed around in the workflows module when using the workflows API. It can be instantiated like this: .. code-block:: python obj = BibWorkflowObject() obj.save() Or, like this: .. code-block:: python obj = BibWorkflowObject.create_object() BibWorkflowObject provides some handy functions such as: .. code-block:: python obj.set_data("<xml ..... />") obj.get_data() == "<xml ..... />" obj.set_extra_data({"param": value}) obj.get_extra_data() == {"param": value} obj.add_task_result("myresult", {"result": 1}) Then to finally save the object .. code-block:: python obj.save() Now you can for example run it in a workflow: .. code-block:: python obj.start_workflow("sample_workflow") """ __tablename__ = "bwlOBJECT" id = db.Column(db.Integer, primary_key=True) # Our internal data column. Default is encoded dict. _data = db.Column(db.LargeBinary, nullable=False, default=get_default_data()) _extra_data = db.Column(db.LargeBinary, nullable=False, default=get_default_extra_data()) _id_workflow = db.Column(db.String(36), db.ForeignKey('bwlWORKFLOW.uuid'), nullable=True, name="id_workflow") version = db.Column(db.Integer(3), default=ObjectVersion.INITIAL, nullable=False, index=True) id_parent = db.Column(db.Integer, db.ForeignKey("bwlOBJECT.id"), default=None) child_objects = db.relationship("BibWorkflowObject", remote_side=[id_parent]) created = db.Column(db.DateTime, default=datetime.now, nullable=False) modified = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, nullable=False) status = db.Column(db.String(255), default="", nullable=False) data_type = db.Column(db.String(150), default="", nullable=True, index=True) uri = db.Column(db.String(500), default="") id_user = db.Column(db.Integer, default=0, nullable=False) child_logs = db.relationship("BibWorkflowObjectLog", backref=db.backref('bibworkflowobject'), cascade="all, delete, delete-orphan") workflow = db.relationship(Workflow, backref=db.backref( 'objects', cascade="all, delete-orphan")) _log = None @db.hybrid_property def id_workflow(self): """Get id_workflow.""" return self._id_workflow @id_workflow.setter def id_workflow(self, value): """Set id_workflow.""" self._id_workflow = str(value) if value else None @property def log(self): """Access logger object for this instance.""" if not self._log: db_handler_obj = BibWorkflowLogHandler(BibWorkflowObjectLog, "id") self._log = get_logger(logger_name="object.%s" % (self.id, ), db_handler_obj=db_handler_obj, loglevel=logging.DEBUG, obj=self) return self._log def get_data(self): """Get data saved in the object.""" return cPickle.loads(base64.b64decode(self._data)) def set_data(self, value): """Save data to the object.""" self._data = base64.b64encode(cPickle.dumps(value)) def get_extra_data(self): """Get extra data saved to the object.""" return cPickle.loads(base64.b64decode(self._extra_data)) def set_extra_data(self, value): """Save extra data to the object. :param value: what you want to replace extra_data with. :type value: dict """ self._extra_data = base64.b64encode(cPickle.dumps(value)) def get_workflow_name(self): """Return the workflow name for this object.""" try: if self.id_workflow: return Workflow.query.get(self.id_workflow).name except AttributeError: # Workflow non-existent pass return def get_formatted_data(self, of=None): """Get the formatted representation for this object.""" from .registry import workflows if of is None: of = cfg.get("WORKFLOWS_HOLDING_PEN_DEFAULT_OUTPUT_FORMAT") try: name = self.get_workflow_name() if not name: return "" workflow_definition = workflows[name] formatted_data = workflow_definition.formatter(self, of=of) except (KeyError, AttributeError): # Somehow the workflow or formatter does not exist from invenio.ext.logging import register_exception register_exception(alert_admin=True) formatted_data = "" return formatted_data def __repr__(self): """Represent a BibWorkflowObject.""" return "<BibWorkflowObject(id = %s, data = %s, id_workflow = %s, " \ "version = %s, id_parent = %s, created = %s, extra_data = %s)" \ % (str(self.id), str(self.get_data()), str(self.id_workflow), str(self.version), str(self.id_parent), str(self.created), str(self.get_extra_data())) def __eq__(self, other): """Enable equal operators on BibWorkflowObjects.""" if isinstance(other, BibWorkflowObject): if self._data == other._data and \ self._extra_data == other._extra_data and \ self.id_workflow == other.id_workflow and \ self.version == other.version and \ self.id_parent == other.id_parent and \ isinstance(self.created, datetime) and \ isinstance(self.modified, datetime): return True else: return False return NotImplemented def __ne__(self, other): """Enable equal operators on BibWorkflowObjects.""" return not self.__eq__(other) def add_task_result(self, name, result, template="workflows/results/default.html"): """Add a new task result defined by name. The name is the dictionary key used to group similar types of results as well as a possible label for the result. The result is a dictionary given as context to the template when rendered. The result given here is added to a list of results for this name. .. code-block:: python obj = BibWorkflowObject() # or BibWorkflowObject.query.get(id) obj.add_task_result("foo", my_result, "path/to/template") :param name: The name of the task in human friendly way. It is used as a key and label for the result rendering. :type name: string :param result: The result to store - passed to render_template(). :type result: dict :param template: The location of the template to render the result. :type template: string """ extra_data = getattr(self, "extra_data", self.get_extra_data()) task_result = {"name": name, "result": result, "template": template} if name in extra_data["_tasks_results"]: extra_data["_tasks_results"][name].append(task_result) else: extra_data["_tasks_results"][name] = [task_result] self.set_extra_data(extra_data) def update_task_results(self, name, results): """Update tasks results by name. The name is the dictionary key used to group similar types of results as well as a possible label for the result. This functions allows you to update (replace) the list of results associated with a name where each result is structured like this: .. code-block:: python task_result = { "name": "foo", "result": result, "template": template } obj = BibWorkflowObject() # or BibWorkflowObject.query.get(id) obj.update_task_results("foo", [task_result]) :param name: The name of the task in human friendly way. It is used as a key and label for the result rendering. :type name: string :param results: List of results to store - passed to render_template(). :type results: list :param template: The location of the template to render the result. :type template: string """ extra_data = getattr(self, "extra_data", self.get_extra_data()) extra_data["_tasks_results"][name] = results self.set_extra_data(extra_data) def get_tasks_results(self): """Return the complete set of tasks results. The result is given as a dictionary where each result is structured like: .. code-block:: python task_result = { "name": name, "result": result, "template": template } :return: dictionary of results as {name: [result, ..], ..} """ return self.get_extra_data()["_tasks_results"] def set_action(self, action, message): """Set the action to be taken for this object. Assign an special "action" to this object to be taken in consideration in Holding Pen. The widget is referred to by a string with the filename minus extension. A message is also needed to tell the user the action required in a textual way. :param action: name of the action to add (i.e. "approval") :type action: string :param message: message to show to the user :type message: string """ extra_data = self.get_extra_data() extra_data["_action"] = action extra_data["_message"] = message self.set_extra_data(extra_data) def get_action(self): """Retrieve the currently assigned action, if any. :return: name of action assigned as string, or None """ return self.get_extra_data().get("_action") def get_action_message(self): """Retrieve the currently assigned widget, if any.""" try: return unicodifier(self.get_extra_data()["_message"]) except KeyError: # No widget return "" def set_error_message(self, msg): """Set an error message.""" extra_data = self.get_extra_data() extra_data["_error_msg"] = msg self.set_extra_data(extra_data) def reset_error_message(self): """Reset the error message.""" extra_data = self.get_extra_data() if "_error_msg" in extra_data: del extra_data["_error_msg"] self.set_extra_data(extra_data) def get_error_message(self): """Retrieve the error message, if any.""" if "error_msg" in self.get_extra_data(): # Backwards compatibility extra_data = self.get_extra_data() msg = extra_data["error_msg"] del extra_data["error_msg"] self.set_extra_data(extra_data) self.set_error_message(msg) try: return self.get_extra_data()["_error_msg"] except KeyError: # No message return "" def remove_action(self): """Remove the currently assigned action.""" extra_data = self.get_extra_data() extra_data["_action"] = None extra_data["_message"] = "" if "_widget" in extra_data: del extra_data["_widget"] self.set_extra_data(extra_data) def start_workflow(self, workflow_name, delayed=False, **kwargs): """Run the workflow specified on the object. Will start workflows execution for the object using :py:func:`.api.start` (or :py:func:`.api.start_delayed` if `delayed=True`). :param workflow_name: name of workflow to run :type workflow_name: str :param delayed: should the workflow run asynchronously? :type delayed: bool :return: BibWorkflowEngine (or AsynchronousResultWrapper). """ if delayed: from .api import start_delayed as start_func else: from .api import start as start_func self.save() return start_func(workflow_name, data=[self], **kwargs) def continue_workflow(self, start_point="continue_next", delayed=False, **kwargs): """Continue the workflow for this object. Will continue a previous execution for the object using :py:func:`.api.continue_oid` (or :py:func:`.api.continue_oid_delayed` if `delayed=True`). The parameter `start_point` allows you to specify the point of where the workflow shall continue: * restart_prev: will restart from the previous task * continue_next: will continue to the next task * restart_task: will restart the current task :param start_point: where should the workflow start from? :type start_point: str :param delayed: should the workflow run asynchronously? :type delayed: bool :return: BibWorkflowEngine (or AsynchronousResultWrapper). """ from .errors import WorkflowAPIError self.save() if not self.id_workflow: raise WorkflowAPIError("No workflow associated with object: %r" % (repr(self), )) if delayed: from .api import continue_oid_delayed as continue_func else: from .api import continue_oid as continue_func return continue_func(self.id, start_point, **kwargs) def change_status(self, message): """Change the status.""" self.status = message def get_current_task(self): """Return the current task from the workflow engine for this object.""" extra_data = self.get_extra_data() try: return extra_data["_task_counter"] except KeyError: # Assume old version "task_counter" return extra_data["task_counter"] def get_current_task_info(self): """Return dictionary of current task function info for this object.""" from .utils import get_workflow_definition, get_func_info task_pointer = self.get_current_task() name = self.get_workflow_name() if not name: return "" current_task = get_workflow_definition(name) for step in task_pointer: current_task = current_task[step] if callable(current_task): return get_func_info(current_task) def save_to_file(self, directory=None, prefix="workflow_object_data_", suffix=".obj"): """Save the contents of self.data['data'] to file. Returns path to saved file. Warning: Currently assumes non-binary content. """ if directory is None: directory = cfg['CFG_TMPSHAREDDIR'] tmp_fd, filename = tempfile.mkstemp(dir=directory, prefix=prefix, suffix=suffix) os.write(tmp_fd, self.get_data()) os.close(tmp_fd) return filename def get_log(self, *criteria, **filters): """Return a list of log entries from BibWorkflowObjectLog. You can specify additional filters following the SQLAlchemy syntax. Get all the logs for the object: .. code-block:: python b = BibWorkflowObject.query.get(1) b.get_log() Get all the logs for the object labeled as ERROR. .. code-block:: python b = BibWorkflowObject.query.get(1) b.get_log(BibWorkflowObjectLog.log_type == logging.ERROR) :return: list of BibWorkflowObjectLog """ criterions = [BibWorkflowObjectLog.id_object == self.id] + \ list(criteria) res = BibWorkflowObjectLog.query.filter(*criterions).filter_by( **filters) return res.all() def __getstate__(self): """Return internal dict.""" return self.__dict__ def __setstate__(self, state): """Update interal dict with given state.""" self.__dict__ = state def copy(self, other): """Copy data and metadata except id and id_workflow.""" self._data = other._data self._extra_data = other._extra_data self.version = other.version self.id_parent = other.id_parent self.created = other.created self.modified = other.modified self.status = other.status self.data_type = other.data_type self.uri = other.uri @session_manager def save(self, version=None, task_counter=None, id_workflow=None): """Save object to persistent storage.""" if task_counter is not None: if isinstance(task_counter, list): self.log.debug("Saving task counter: %s" % (task_counter, )) extra_data = self.get_extra_data() extra_data["_task_counter"] = task_counter self.set_extra_data(extra_data) else: raise ValueError("Task counter must be a list!") if version is not None: if version != self.version: self.modified = datetime.now() self.version = version if id_workflow is not None: self.id_workflow = id_workflow db.session.add(self) if self.id is not None: self.log.debug("Saving object: %s" % (self.id or "new", )) workflow_object_saved.send(self) @classmethod def get(cls, *criteria, **filters): """Wrapper of SQLAlchemy to get a BibWorkflowObject. A wrapper for the filter and filter_by functions of SQLAlchemy. Define a dict with which columns should be filtered by which values. .. code-block:: python Workflow.get(uuid=uuid) Workflow.get(Workflow.uuid != uuid) The function supports also "hybrid" arguments. .. code-block:: python Workflow.get(Workflow.module_name != 'i_hate_this_module', user_id=user_id) See also SQLAlchemy BaseQuery's filter and filter_by documentation. """ return cls.query.filter(*criteria).filter_by(**filters) @classmethod @session_manager def delete(cls, oid): """Delete a BibWorkflowObject.""" if isinstance(oid, BibWorkflowObject): db.session.delete(oid) else: db.session.delete( BibWorkflowObject.get(BibWorkflowObject.id == oid).first()) @classmethod @session_manager def create_object(cls, **kwargs): """Create a new Workflow Object with given content.""" obj = BibWorkflowObject(**kwargs) db.session.add(obj) return obj @classmethod @session_manager def create_object_revision(cls, old_obj, version, **kwargs): """Create a Workflow Object copy with customized values.""" # Create new object and copy it obj = BibWorkflowObject(**kwargs) obj.copy(old_obj) # Overwrite some changes obj.version = version obj.created = datetime.now() obj.modified = datetime.now() for key, value in iteritems(kwargs): setattr(obj, key, value) db.session.add(obj) return obj
class WtgTAG(db.Model, Serializable): """A Tag.""" __tablename__ = 'wtgTAG' __public__ = set(['id', 'name', 'id_owner']) # # Access Rights # ACCESS_NAMES = { 0: 'Nothing', 10: 'View', 20: 'Add', 30: 'Add and remove', 40: 'Manage', } ACCESS_LEVELS = \ dict((v, k) for (k, v) in iteritems(ACCESS_NAMES)) ACCESS_RIGHTS = { 0: [], 10: ['view'], 20: ['view', 'add'], 30: ['view', 'add', 'remove'], 40: ['view', 'add', 'remove', 'edit'], } ACCESS_OWNER_DEFAULT = ACCESS_LEVELS['Manage'] ACCESS_GROUP_DEFAULT = ACCESS_LEVELS['View'] # Primary key id = db.Column(db.Integer(15, unsigned=True), primary_key=True, nullable=False, autoincrement=True) # Name name = db.Column(db.String(255), nullable=False, server_default='', index=True) # Owner id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id), server_default='0') # Access rights of owner user_access_rights = db.Column(db.Integer(2, unsigned=True), nullable=False, default=ACCESS_OWNER_DEFAULT) # Group # equal to 0 for private tags id_usergroup = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(Usergroup.id), server_default='0') # Group access rights group_access_rights = db.Column(db.Integer(2, unsigned=True), nullable=False, default=ACCESS_GROUP_DEFAULT) # Access rights of everyone public_access_rights = db.Column(db.Integer(2, unsigned=True), nullable=False, default=ACCESS_LEVELS['Nothing']) # Visibility in document description show_in_description = db.Column(db.Boolean, nullable=False, default=True) # Relationships user = db.relationship(User, backref=db.backref('tags', cascade='all')) user_query = db.relationship(User, backref=db.backref('tags_query', cascade='all', lazy='dynamic')) usergroup = db.relationship(Usergroup, backref=db.backref('tags', cascade='all')) # association proxy of "user_keywords" collection # to "keyword" attribute records = association_proxy('records_association', 'bibrec') # Calculated fields @db.hybrid_property def record_count(self): """TODO.""" return self.records_association_query.count() @record_count.expression def record_count(cls): """TODO.""" return db.select([db.func.count(WtgTAGRecord.id_bibrec)]) \ .where(WtgTAGRecord.id_tag == cls.id) \ .label('record_count') @db.validates('user_access_rights') @db.validates('group_access_rights') @db.validates('public_access_rights') def validate_user_access_rights(self, key, value): """Check if the value is among defined levels.""" assert value in WtgTAG.ACCESS_NAMES return value
class Client(db.Model): """A client is the app which want to use the resource of a user. It is suggested that the client is registered by a user on your site, but it is not required. The client should contain at least these information: client_id: A random string client_secret: A random string client_type: A string represents if it is confidential redirect_uris: A list of redirect uris default_redirect_uri: One of the redirect uris default_scopes: Default scopes of the client But it could be better, if you implemented: allowed_grant_types: A list of grant types allowed_response_types: A list of response types validate_scopes: A function to validate scopes """ __tablename__ = 'oauth2CLIENT' name = db.Column( db.String(40), info=dict( label=_('Name'), description=_('Name of application (displayed to users).'), validators=[validators.DataRequired()] ) ) """Human readable name of the application.""" description = db.Column( db.Text(), default=u'', info=dict( label=_('Description'), description=_('Optional. Description of the application' ' (displayed to users).'), ) ) """Human readable description.""" website = db.Column( URLType(), info=dict( label=_('Website URL'), description=_('URL of your application (displayed to users).'), ), default=u'', ) user_id = db.Column(db.ForeignKey('user.id'), nullable=True) """Creator of the client application.""" client_id = db.Column(db.String(255), primary_key=True) """Client application ID.""" client_secret = db.Column( db.String(255), unique=True, index=True, nullable=False ) """Client application secret.""" is_confidential = db.Column(db.Boolean, default=True) """Determine if client application is public or not.""" is_internal = db.Column(db.Boolean, default=False) """Determins if client application is an internal application.""" _redirect_uris = db.Column(db.Text) """A newline-separated list of redirect URIs. First is the default URI.""" _default_scopes = db.Column(db.Text) """A space-separated list of default scopes of the client. The value of the scope parameter is expressed as a list of space-delimited, case-sensitive strings. """ user = db.relationship( User, backref=db.backref( "oauth2clients", cascade="all, delete-orphan", ) ) """Relationship to user.""" @property def allowed_grant_types(self): """Return allowed grant types.""" return current_app.config['OAUTH2_ALLOWED_GRANT_TYPES'] @property def allowed_response_types(self): """Return allowed response types.""" return current_app.config['OAUTH2_ALLOWED_RESPONSE_TYPES'] # def validate_scopes(self, scopes): # return self._validate_scopes @property def client_type(self): """Return client type.""" if self.is_confidential: return 'confidential' return 'public' @property def redirect_uris(self): """Return redirect uris.""" if self._redirect_uris: return self._redirect_uris.splitlines() return [] @redirect_uris.setter def redirect_uris(self, value): """Validate and store redirect URIs for client.""" if isinstance(value, six.text_type): value = value.split("\n") value = [v.strip() for v in value] for v in value: validate_redirect_uri(v) self._redirect_uris = "\n".join(value) or "" @property def default_redirect_uri(self): """Return default redirect uri.""" try: return self.redirect_uris[0] except IndexError: pass @property def default_scopes(self): """List of default scopes for client.""" if self._default_scopes: return self._default_scopes.split(" ") return [] @default_scopes.setter def default_scopes(self, scopes): """Set default scopes for client.""" validate_scopes(scopes) self._default_scopes = " ".join(set(scopes)) if scopes else "" def validate_scopes(self, scopes): """Validate if client is allowed to access scopes.""" try: validate_scopes(scopes) return True except ScopeDoesNotExists: return False def gen_salt(self): """Generate salt.""" self.reset_client_id() self.reset_client_secret() def reset_client_id(self): """Reset client id.""" self.client_id = gen_salt( current_app.config.get('OAUTH2_CLIENT_ID_SALT_LEN') ) def reset_client_secret(self): """Reset client secret.""" self.client_secret = gen_salt( current_app.config.get('OAUTH2_CLIENT_SECRET_SALT_LEN') )
class Author(db.Model): """Model representing an author entity. Each author may be associated to an Invenio author bibrec (record), an Invenio user and may have publications. Moreover, an author may have fellow coauthors and citations (based on his publications). """ __tablename__ = 'authors_author' # When deleting a number of signatures, it should not be expected # by the mapper that the same number of authors should be deleted, # as many signatures can be associated with a publication. This # disables the default behaviour of the mapper to show a warning. __mapper_args__ = {'confirm_deleted_rows': False} id = db.Column(db.Integer(15, unsigned=True), primary_key=True, autoincrement=True, nullable=False) """Id of the author (required).""" id_bibrec = db.Column(db.MediumInteger(8, unsigned=True), db.ForeignKey(Record.id)) """Id of the associated Invenio author bibrec.""" id_user = db.Column(db.Integer(15, unsigned=True), db.ForeignKey(User.id)) """Id of the associated Invenio user.""" user = db.relationship('User', backref=db.backref("author", uselist=False)) """Invenio user associated with this author.""" publications = db.relationship('Publication', secondary='authors_signature', secondaryjoin="""and_( Signature.id_publication == Publication.id, Signature.attribution.in_( ('unknown', 'verified')))""") """Publications of the author.""" @property def record(self): """Get the Invenio author record in JSON format. :return: an instance of an author record in JSON format """ return get_record(self.id_bibrec) @property def coauthors(self): """Get the the authors co-writing this author's publications. :return: Author objects """ return (coauthor for publication in self.publications for coauthor in publication.authors if coauthor != self) @property def citations(self): """Get the citations of this author's publications. :return: Publication objects """ return (citation for publication in self.publications for citation in publication.citations) def __repr__(self): """Return a printable representation of an Author.""" return 'Author(id=%s)' % self.id @utils.session_manager def delete(self): """Delete an author and modify the associated signatures. When deleting an author, the author field in the associated signatures should be set to None. Note: This is the mandatory way to delete a publication when using database engines that do not support 'ON DELETE' clauses (e.g. MyISAM of MySQL). """ for s in Signature.query.filter(Signature.author == self).all(): s.author = None db.session.add(s) db.session.commit() db.session.delete(self)