class Actor(Entity): name = Field(Unicode(60)) movies = ManyToMany('Movie', inverse='actors', tablename='movie_casting') using_options(tablename='actors')
class B(Entity): using_options(resolve_root='tests.db1') cs = ManyToMany('.c.C') a1s = ManyToMany('a.A1')
class DataBoard(Entity): """Board mapper - ``title`` -- board title - ``is_template`` -- is this a real board or a template? - ``columns`` -- list of board columns - ``labels`` -- list of labels for cards - ``comments_allowed`` -- who can comment ? (0 nobody, 1 board members only , 2 all application users) - ``votes_allowed`` -- who can vote ? (0 nobody, 1 board members only , 2 all application users) - ``description`` -- board description - ``visibility`` -- board visibility [0 Private, 1 Public (anyone with the URL can view), 2 Shared (anyone can view it from her home page)] - ``uri`` -- board URI (Universally Unique IDentifier) - ``last_users`` -- list of last users - ``pending`` -- invitations pending for new members (use token) - ``archive`` -- display archive column ? (0 false, 1 true) - ``archived`` -- is board archived ? """ using_options(tablename='board') title = Field(Unicode(255)) is_template = Field(Boolean, default=False) columns = OneToMany('DataColumn', order_by="index", cascade='delete', lazy='subquery') # provisional labels = OneToMany('DataLabel', order_by='index') comments_allowed = Field(Integer, default=1) votes_allowed = Field(Integer, default=1) description = Field(UnicodeText, default=u'') visibility = Field(Integer, default=0) version = Field(Integer, default=0, server_default='0') # provisional board_members = OneToMany('DataMembership', lazy='subquery', order_by=('manager')) uri = Field(Unicode(255), index=True, unique=True) last_users = ManyToOne('DataUser', order_by=('fullname', 'email')) pending = OneToMany('DataToken', order_by='username') history = OneToMany('DataHistory') background_image = Field(Unicode(255)) background_position = Field(Unicode(255)) title_color = Field(Unicode(255)) show_archive = Field(Integer, default=0) archived = Field(Boolean, default=False) # provisional weight_config = OneToOne('DataBoardWeightConfig') def __init__(self, *args, **kwargs): """Initialization. Create board and uri of the board """ super(DataBoard, self).__init__(*args, **kwargs) self.uri = unicode(uuid.uuid4()) @property def template_title(self): manager = self.get_first_manager() if not manager or self.visibility == 0: return self.title return u'{0} ({1})'.format(self.title, manager.fullname) def get_first_manager(self): if not self.board_members: return None potential_manager = self.board_members[-1] return potential_manager.user if potential_manager.manager else None def copy(self): new_data = DataBoard(title=self.title, description=self.description, background_position=self.background_position, title_color=self.title_color, comments_allowed=self.comments_allowed, votes_allowed=self.votes_allowed) # TODO: move to board extension new_data.weight_config = DataBoardWeightConfig( weighting_cards=self.weighting_cards, weights=self.weights) session.add(new_data) session.flush() # TODO: move to board extension for label in self.labels: new_data.labels.append(label.copy()) session.flush() return new_data def get_label_by_title(self, title): return (l for l in self.labels if l.title == title).next() def delete_history(self): for event in self.history: session.delete(event) session.flush() def increase_version(self): self.version += 1 if self.version > 2147483600: self.version = 1 @property def url(self): return "%s/%s" % (urllib.quote_plus( self.title.encode('ascii', 'ignore').replace('/', '_')), self.uri) @classmethod def get_by_id(cls, id): return cls.get(id) @classmethod def get_by_uri(cls, uri): return cls.get_by(uri=uri) def set_background_image(self, image): self.background_image = image or u'' @classmethod def get_all_boards(cls, user): """Return all boards the user is member of.""" query = session.query(cls).join(DataMembership) query = query.filter(cls.is_template == False, DataMembership.user == user) return query.order_by(cls.title) @classmethod def get_shared_boards(cls): query = session.query(cls).filter(cls.visibility == BOARD_SHARED) return query.order_by(cls.title) @classmethod def get_templates_for(cls, user, public_value): q = cls.query q = q.filter(cls.archived == False) q = q.filter(cls.is_template == True) q = q.order_by(cls.title) q1 = q.filter(cls.visibility == public_value) q2 = q.join(DataMembership) q2 = q2.filter(DataMembership.user == user) q2 = q2.filter(cls.visibility != public_value) return q1, q2 def create_column(self, index, title, nb_cards=None, archive=False): return DataColumn.create_column(self, index, title, nb_cards, archive) def delete_column(self, column): if column in self.columns: self.columns.remove(column) def create_label(self, title, color): label = DataLabel(title=title, color=color) self.labels.append(label) session.flush() return label ############# Membership management; those functions belong to a board extension def delete_members(self): DataMembership.delete_members(self) def has_member(self, user): """Return True if user is member of the board In: - ``user`` -- user to test (DataUser instance) Return: - True if user is member of the board """ return DataMembership.has_member(self, user) def has_manager(self, user): """Return True if user is manager of the board In: - ``user`` -- user to test (DataUser instance) Return: - True if user is manager of the board """ return DataMembership.has_member(self, user, manager=True) def remove_member(self, user): DataMembership.remove_member(board=self, user=user) def change_role(self, user, new_role): DataMembership.change_role(self, user, new_role == 'manager') def add_member(self, user, role='member'): """ Add new member to the board In: - ``new_member`` -- user to add (DataUser instance) - ``role`` -- role's member (manager or member) """ DataMembership.add_member(self, user, role == 'manager') ############# Weight configuration, those functions belong to an extension @property def weights(self): return self.weight_config.weights @weights.setter def weights(self, value): self.weight_config.weights = value @property def weighting_cards(self): return self.weight_config.weighting_cards @weighting_cards.setter def weighting_cards(self, value): self.weight_config.weighting_cards = value def reset_card_weights(self): self.weight_config.reset_card_weights() def total_weight(self): return self.weight_config.total_weight()
class Industry(Entity): using_options(tablename='industry') id = Field(Integer, primary_key=True) name = Field(String(255), default='')
class User(Entity): """Reasonably basic User definition. Probably would want additional attributes. """ using_options(tablename="user", auto_primarykey="user_id") user_name = Field(Unicode(16), required=True, unique=True) _password = Field(Unicode(40), colname="password", required=True) groups = ManyToMany( "Group", inverse="users", tablename="user_group", local_colname="group_id", remote_colname="user_id", ) def _set_password(self, password): """encrypts password on the fly""" self._password = self.__encrypt_password(password) def _get_password(self): """returns password""" return self._password password = descriptor=property(_get_password, _set_password) def __encrypt_password(self, password): """Hash the given password with SHA1. Edit this method to implement your own algorithm. """ hashed_password = password if isinstance(password, unicode): password_8bit = password.encode('UTF-8') else: password_8bit = password hashed_password = sha.new(password_8bit).hexdigest() # make sure the hased password is an UTF-8 object at the end of the # process because SQLAlchemy _wants_ a unicode object for Unicode columns if not isinstance(hashed_password, unicode): hashed_password = hashed_password.decode('UTF-8') return hashed_password def validate_password(self, password): """Check the password against existing credentials. this method _MUST_ return a boolean. @param password: the password that was provided by the user to try and authenticate. This is the clear text version that we will need to match against the (possibly) encrypted one in the database. @type password: unicode object """ return self.password == self.__encrypt_password(password)
class Degree(Entity): using_options(tablename='degree') id = Field(Integer, primary_key=True) name = Field(String(255), default='')
class Univs(Entity): using_options(tablename='univs') id = Field(Integer, primary_key=True) name = Field(String(255), default='') provinces = ManyToOne('Provinces')
class Ticket(Entity): using_options(tablename=tname) using_table_options(mysql_engine='InnoDB', mysql_charset='utf8') # Athena username requestor = Field(Unicode(255), index=True) # Locker name locker = Field(Unicode(255), index=True) # Hostname involved hostname = Field(Unicode(255), index=True) # path path = Field(Unicode(255)) # "open" or "moira" or "dns" or "resolved" state = Field(Unicode(32)) rtid = Field(Integer) # Purpose purpose = Field(UnicodeText()) events = OneToMany('Event', order_by='timestamp') @staticmethod def create(locker, hostname, path, requestor=None, purpose=""): if requestor is None: requestor = auth.current_user() t = Ticket(requestor=requestor, hostname=hostname, locker=locker, path=path, state="open", purpose=purpose) session.flush() t.addEvent(type='request', state="open", target='us') return t def addEvent(self, type, state, by=None, target=None, subject=None, body=None): if by is None: by = auth.current_user() Event(ticket=self, type=type, target=target, subject=subject, body=body, by=by) if state != self.state: self.state = state pat = "%s's %s changed the ticket re: %s to %s" else: pat = "%s's %s left the ticket re: %s as %s" try: url = "%s%s" % (tg.request.host_url, tg.url('/queue')) except: # Default to something sane if we're not in the context of a request url = "https://pony.scripts.mit.edu:444/queue" log.zwrite(pat % (by, type, self.hostname, state), instance=self.id, zsig=url) @staticmethod def all(): return Ticket.query.all()
class DataUser(Entity): """Label mapper """ using_options(tablename='user') # VARCHAR(binary=True) here is a hack to make MySQL case sensitive # like the other DBMS. # No consequences on regular databases. username = Field(VARCHAR(255, binary=True), unique=True, primary_key=True, nullable=False) source = Field(Unicode(255), nullable=False, primary_key=True) fullname = Field(Unicode(255), nullable=False) email = Field(Unicode(255), nullable=True) picture = Field(Unicode(255), nullable=True) language = Field(Unicode(255), default=u"en", nullable=True) email_to_confirm = Field(Unicode(255)) _salt = Field(Unicode(255), colname='salt', nullable=False) _password = Field(Unicode(255), colname='password', nullable=True) registration_date = Field(DateTime, nullable=False) last_login = Field(DateTime, nullable=True) display_week_numbers = Field(Boolean, default=False) board_members = OneToMany('DataBoardMember') boards = AssociationProxy( 'board_members', 'board', creator=lambda board: DataBoardMember(board=board)) board_managers = OneToMany('DataBoardManager') managed_boards = AssociationProxy( 'board_managers', 'board', creator=lambda board: DataBoardManager(board=board)) last_board = OneToOne('DataBoard', inverse='last_users') cards = ManyToMany('DataCard', inverse='members', lazy='dynamic') my_cards = OneToMany('DataCard', inverse='author') history = OneToMany('DataHistory') votes = OneToMany('DataVote') def __init__(self, username, password, fullname, email, source=u'application', picture=None, **kw): """Create a new user with an unconfirmed email""" super(DataUser, self).__init__(username=username, fullname=fullname, email=None, email_to_confirm=email, source=source, picture=picture, registration_date=datetime.datetime.utcnow(), **kw) # Create password if source is local if source == "application": self.change_password(password) else: # External authentication self.change_password('passwd') self.email_to_confirm = None def update(self, fullname, email, picture=None): self.fullname = fullname if email: self.email = email self.picture = picture def check_password(self, clear_password): """Check the user password. Return True if the password is valid for this user""" encrypted_password = self._encrypt_password(self._salt, clear_password) return encrypted_password == self._password def change_password(self, clear_password): """Change the user password""" self._salt = self._create_random_salt() self._password = self._encrypt_password(self._salt, clear_password) def set_email_to_confirm(self, email_to_confirm): if email_to_confirm: self.email_to_confirm = email_to_confirm def is_validated(self): return self.email_to_confirm is None def confirm_email(self): """Called when a user confirms his email address""" # already confirmed if self.email_to_confirm is None: return self.email = self.email_to_confirm self.email_to_confirm = None def add_board(self, board, role="member"): """Add board to user's board lists In: - ``board`` -- DataBoard instance to add - ``role`` -- user is member or manager """ boards = set(dbm.board for dbm in self.board_members) if board not in boards: self.board_members.append(DataBoardMember(board=board)) if role == "manager" and board not in self.managed_boards: self.managed_boards.append(board) def get_picture(self): return self.picture @classmethod def get_confirmed_users(cls): return cls.query.filter(cls.email is not None) @staticmethod def _create_random_salt(length=32): allowed_chars = string.ascii_letters + string.digits return u''.join(random.choice(allowed_chars) for _ in range(length)) @staticmethod def _encrypt_password(salt, password): secret = "NzlSszmvDNY2e2lVMwiKJwgWjNGFCP1a" secret_salt = hashlib.sha512(secret + salt).hexdigest() utf8_password = password.encode('utf-8') return unicode(hashlib.sha512(secret_salt + utf8_password).hexdigest()) @classmethod def get_unconfirmed_users(cls, before_date=None): q = cls.query.filter(cls.email is None) if before_date: q = q.filter(cls.registration_date < before_date) return q @classmethod def get_by_username(cls, username): return cls.get_by(username=username) @classmethod def get_by_email(cls, email): return cls.get_by(email=email) @classmethod def search(cls, value): return cls.query.filter( cls.fullname.ilike('%' + value + '%') | cls.email.ilike('%' + value + '%')) def best_friends(self, exclude_list=(), size=None): from kansha.board.models import DataBoard cls = self.__class__ bm2 = aliased(DataBoardMember) cnt = func.count(DataBoardMember.board_id) query = database.session.query(cls, cnt) query = query.join( (DataBoardMember, and_(DataBoardMember.user_source == cls.source, DataBoardMember.user_username == cls.username))) query = query.join( (DataBoard, DataBoard.id == DataBoardMember.board_id)) query = query.join((bm2, bm2.board_id == DataBoard.id)) query = query.filter(bm2.member == self) if exclude_list: query = query.filter(~cls.email.in_(exclude_list)) query = query.group_by(cls) query = query.order_by(cnt.desc(), cls.fullname) if size: query = query.limit(size) return [res[0] for res in query]
class RecipeAddition(Entity, DeepCopyMixin): USES = ('MASH', 'FIRST WORT', 'BOIL', 'POST-BOIL', 'FLAME OUT', 'PRIMARY', 'SECONDARY', 'TERTIARY') using_options(inheritance='multi', polymorphic=True) amount = Field(Float) # # At the database level, only certain units are actually stored. # We do this for the sake of uniformity (to make calculations easier). # unit = Field(Enum( *['POUND', 'OUNCE', 'TEASPOON', 'TABLESPOON', 'GALLON', 'LITER'], native_enum=False), nullable=True) use = Field(Enum(*USES, native_enum=False)) duration = Field(Interval) recipe = ManyToOne('Recipe', inverse='additions') fermentable = ManyToOne('Fermentable', inverse='additions') hop = ManyToOne('Hop', inverse='additions') yeast = ManyToOne('Yeast', inverse='additions') extra = ManyToOne('Extra', inverse='extra') @property def printable_amount(self): if getattr(self.recipe, 'unit_system', None) == 'METRIC': return UnitConvert.to_str(*to_metric(self.amount, self.unit)) return UnitConvert.to_str(self.amount, self.unit) @property def ingredient(self): for ingredient in ('fermentable', 'hop', 'yeast', 'extra'): match = getattr(self, ingredient, None) if match is not None: return match @property def pounds(self): if self.unit == 'POUND': return self.amount if self.unit == 'OUNCE': return self.amount / 16.0 raise InvalidUnitException('Could not convert `%s` to pounds.' % self.unit) @property def minutes(self): if self.duration is None: return 0 return self.duration.seconds / 60 @property def sortable_minutes(self): if self.use == 'FIRST WORT': return maxint if self.use in ('POST BOIL', 'FLAME-OUT'): return -1 return self.minutes @property def step(self): return ({ 'MASH': 'mash', 'FIRST WORT': 'boil', 'BOIL': 'boil', 'POST-BOIL': 'boil', 'FLAME OUT': 'boil', 'PRIMARY': 'fermentation', 'SECONDARY': 'fermentation', 'TERTIARY': 'fermentation' })[self.use] @property def percentage(self): additions = getattr(self.recipe, self.step) return self.recipe._percent(additions).get(self, 0) def to_xml(self): from draughtcraft.lib.beerxml import export if self.hop: kw = { 'name': self.hop.name, 'alpha': self.hop.alpha_acid, 'amount': to_kg(self.amount, self.unit), 'time': self.minutes, 'notes': self.hop.description, 'form': self.form.capitalize(), 'origin': self.hop.printed_origin } kw['use'] = { 'MASH': 'Mash', 'FIRST WORT': 'First Wort', 'BOIL': 'Boil', 'POST-BOIL': 'Aroma', 'FLAME OUT': 'Aroma', 'PRIMARY': 'Dry Hop', 'SECONDARY': 'Dry Hop', 'TERTIARY': 'Dry Hop' }.get(self.use) return export.Hop(**kw) if self.fermentable: kw = { 'name': self.fermentable.name, 'amount': to_kg(self.amount, self.unit), 'yield': self.fermentable.percent_yield, 'color': self.fermentable.lovibond, 'add_after_boil': self.step == 'fermentation', 'origin': self.fermentable.printed_origin, 'notes': self.fermentable.description } kw['type'] = { 'MALT': 'Grain', 'GRAIN': 'Grain', 'ADJUNCT': 'Adjunct', 'EXTRACT': 'Extract', 'SUGAR': 'Sugar' }.get(self.fermentable.type) if self.fermentable.type == 'EXTRACT' and \ 'DME' in self.fermentable.name: kw['type'] = 'Dry Extract' return export.Fermentable(**kw) if self.yeast: kw = { 'name': self.yeast.name, 'form': self.yeast.form.capitalize(), 'attenuation': self.yeast.attenuation * 100.00, 'notes': self.yeast.description } # Map types as appropriately as possible to BeerXML <TYPE>'s. kw['type'] = { 'ALE': 'Ale', 'LAGER': 'Lager', 'WILD': 'Ale', 'MEAD': 'Wine', 'CIDER': 'Wine', 'WINE': 'Wine' }.get(self.yeast.type) if self.yeast.form == 'LIQUID': # # If the yeast is liquid, it's probably a activator/vial. For # simplicity, we'll assume Wyeast's volume, 125ml. # kw['amount'] = 0.125 else: # # If the yeast is dry, it's probably a small packet. For # simplicity, we'll assume a standard weight of 11.5g. # kw['amount'] = 0.0115 kw['amount_is_weight'] = True if self.use in ('SECONDARY', 'TERTIARY'): kw['add_to_secondary'] = True return export.Yeast(**kw) if self.extra: kw = { 'name': self.extra.name, 'type': string.capwords(self.extra.type), 'time': self.minutes, 'notes': self.extra.description } kw['use'] = { 'MASH': 'Mash', 'FIRST WORT': 'Boil', 'BOIL': 'Boil', 'POST-BOIL': 'Boil', 'FLAME OUT': 'Boil', 'PRIMARY': 'Primary', 'SECONDARY': 'Secondary', 'TERTIARY': 'Secondary' }.get(self.use) if self.unit is None: # # If there's no unit (meaning it's just one "unit"), assume # a weight of 15 grams. # kw['amount'] = 0.015 kw['amount_is_weight'] = True elif self.extra.liquid: kw['amount'] = to_l(self.amount, self.unit) else: kw['amount'] = to_kg(self.amount, self.unit) kw['amount_is_weight'] = True return export.Misc(**kw) def __json__(self): return { 'amount': 1 if self.yeast else self.amount, 'unit': self.unit, 'use': self.use, 'minutes': self.minutes, 'ingredient': self.ingredient }
class sys_users(Entity): uid = Field(Integer, primary_key=True) username = Field(String(50)) realname = Field(String(32)) shell = Field(String(20), default='/bin/bash') password = Field(String(40)) homedir = Field(String(32), default='/home/data-exchange-home') lastchange = Field(String(50)) min = Field(Integer) max = Field(Integer) warn = Field(Integer) inact = Field(Integer) _expire = Field(Integer, colname='expire', synonym='expire') confirmed = Field(Boolean, default=False) created = Field(DateTime, default=datetime.now) email = Field(String(100)) status = Field(String(1), default='N') organisation = Field(String(100)) group = ManyToOne('sys_groups', colname='gid') known_partners = ManyToOne('known_partners') groups = ManyToMany('sys_groups', tablename='sys_groupmembers') using_options(tablename='sys_users') using_table_options(mysql_engine='InnoDB') ADDITIONAL_HOURS = 48 ADMIN_MAIL = '*****@*****.**' def validate_password(self, password): """ Check the password against existing credentials. :param password: the password that was provided by the user to try and authenticate. This is the clear text version that we will need to match against the hashed one in the database. :type password: unicode object. :return: Whether the password is valid. :rtype: bool """ salt = self.password[:2] hashed_pass = crypt.crypt(password, salt) return self.password == hashed_pass def _set_expire(self, expire): self._expire = time.mktime(expire.timetuple()) def _get_expire(self): return datetime.fromtimestamp(self._expire) expire = property(_get_expire, _set_expire) def _set_password_plain(self, password): self._password_plain = password chars = string.letters + string.digits salt = random.choice(chars) + random.choice(chars) self.password = crypt.crypt(password, salt) def _get_password_plain(self): return self._password_plain password_plain = property(_get_password_plain, _set_password_plain) @classmethod def generatePassword(cls): return ''.join(random.sample(string.letters + string.digits, 12)) @classmethod def getExpireDate(cls): return datetime.today() + timedelta(hours=cls.ADDITIONAL_HOURS) def _get_is_notify(self): if (self.status == 'A' and self.isExpired == True and self.confirmed == True) or (self.known_partners is not None): return True else: return False isNotify = property(_get_is_notify) def _get_expire_in(self): if not self.expire: return 'N/A' timediff = self.expire - datetime.today() return timediff expire_in = property(_get_expire_in) def _get_is_expired(self): if self.expire: if self.expire < datetime.today(): return True return False isExpired = property(_get_is_expired) @before_insert def _auto_confirm(self): if self.known_partners: self.confirmed = True self.status = 'A' def notify(self): if self.isNotify == True: self.sendAccountMail() else: self.notify_admin() def notify_admin(self): from email.mime.text import MIMEText mailtext = """%(name)s of %(organisation)s has requested a temporary FTP-Account. Please confirm the request here: %(link)s""" % dict( name=self.realname, organisation=self.organisation, link= 'http://arabidopsis.gmi.oeaw.ac.at:5000/management/RequestFTPAccess/confirmlist' ) msg = MIMEText(mailtext) me = '*****@*****.**' msg['Subject'] = 'New Temporary FTP-Account-Request' msg['From'] = me msg['To'] = self.ADMIN_MAIL #return True # Send the message via our own SMTP server, but don't include the # envelope header. s = smtplib.SMTP() s.connect() s.sendmail(me, [self.email], msg.as_string()) s.quit() def sendAccountMail(self): from email.mime.text import MIMEText mailtext = """Dear Mr/Mrs %(name)s! You requested a Temporary sftp-Account for the Arabidopsis-Server. You can connect to the Arabidopsis sftp-Server using following account information: Login: %(username)s Password: %(password)s Server: %(server)s | %(direct_link)s The account is active until %(expire)s You can use FileZilla or any other sftp capable application to transfer data from and to the server. Regards Norborg-Group""" % dict( name=self.realname, username=self.username, password=self.password_plain, server='arabidopsis.gmi.oeaw.ac.at:22', direct_link='sftp://%s:%[email protected]:22' % (self.username, self.password_plain), expire=self.expire) msg = MIMEText(mailtext) me = '*****@*****.**' msg['Subject'] = 'Arabidopsis Temporary sFTP-Account' msg['From'] = me msg['To'] = self.email #return True # Send the message via our own SMTP server, but don't include the # envelope header. s = smtplib.SMTP() s.connect() s.sendmail(me, [self.email], msg.as_string()) s.quit() def confirm(self): if not self.known_partners: self.known_partners = known_partners.get_by(email=self.email) self.confirmed = True if not self.known_partners: self.known_partners = known_partners(email=self.email) self.expires = self.getExpireDate() self.status = 'A' self.password_plain = self.generatePassword() __session__.flush()
class Movie(Entity): using_options(tablename='movies') title = Field(Unicode(60), required=True) short_description = Field(Unicode(512)) releasedate = Field(Date) # # All relation types are covered with their own editor # director = ManyToOne('Person') cast = OneToMany('Cast') visitor_reports = OneToMany('VisitorReport') tags = ManyToMany('Tag') genre = Field(Unicode(15)) rating = Field(camelot.types.Rating()) # end short movie definition # # Camelot includes custom sqlalchemy types, like Image, which stores an # image on disk and keeps the reference to it in the database. # cover = Field(camelot.types.Image(upload_to='covers')) # # Or File, which stores a file in the upload_to directory and stores a # reference to it in the database # script = Field(camelot.types.File(upload_to='script')) description = Field(camelot.types.RichText) # # Using a ColumnProperty, an sql query can be assigned to a field # @ColumnProperty def total_visitors(self): return sql.select([sql.func.sum(VisitorReport.visitors)], VisitorReport.movie_id==self.id) # # Normal python properties can be used as well, but then the # delegate needs be specified # @property def visitors_chart(self): # # Container classes are used to transport chunks of data between # the model thread and the gui thread, in this case a chart # from camelot.container.chartcontainer import BarContainer return BarContainer(range(len(self.visitor_reports)), [vr.visitors for vr in self.visitor_reports]) # # Each Entity subclass can have a subclass of EntityAdmin as # its inner class. The EntityAdmin class defines how the Entity # class will be displayed in the GUI. Its behavior can be steered # by specifying some class attributes # # To fully customize the way the entity is visualized, the EntityAdmin # subclass should overrule some of the EntityAdmin's methods # class Admin(EntityAdmin): # the list_display attribute specifies which entity attributes should # be visible in the table view list_display = ['cover', 'title', 'releasedate', 'rating',] lines_per_row = 5 # define filters to be available in the table view list_filter = ['genre', ComboBoxFilter('director.full_name')] # if the search function needs to look in related object attributes, # those should be specified within list_search list_search = ['director.full_name'] # the form_display attribute specifies which entity attributes should be # visible in the form view form_display = TabForm([ ('Movie', Form([ HBoxForm([WidgetOnlyForm('cover'), ['title', 'rating']]), 'short_description', 'releasedate', 'director', 'script', 'genre', 'description',], columns = 2)), ('Cast', WidgetOnlyForm('cast')), ('Visitors', WidgetOnlyForm('visitors_chart')), ('Tags', WidgetOnlyForm('tags')) ]) # create a list of actions available for the user on the form view # those actions will be executed within the model thread # form_actions = [('Burn DVD', burn_to_disk)] # # additional attributes for a field can be specified in the # field_attributes dictionary # field_attributes = dict(cast=dict(create_inline=True), genre=dict(choices=genre_choices, editable=lambda o:bool(o.title and len(o.title))), releasedate=dict(background_color=lambda o:ColorScheme.orange_1 if o.releasedate and o.releasedate < datetime.date(1920,1,1) else None), visitors_chart=dict(delegate=delegates.ChartDelegate), rating=dict(tooltip='''<table> <tr><td>1 star</td><td>Not that good</td></tr> <tr><td>2 stars</td><td>Almost good</td></tr> <tr><td>3 stars</td><td>Good</td></tr> <tr><td>4 stars</td><td>Very good</td></tr> <tr><td>5 stars</td><td>Awesome !</td></tr> </table>'''), smiley=dict(delegate=delegates.SmileyDelegate), script=dict(remove_original=True)) def __unicode__(self): return self.title or ''
class Permission(Entity): permission_id = Field(Integer, primary_key=True) permission_name = Field(Unicode(16), unique=True) description = Field(Unicode(255)) groups = ManyToMany('Group', inverse='permissions') using_options(tablename='permission')
class VisitIdentity(Entity): visit_key = Field(String(40), primary_key=True) user = ManyToOne('User', colname='user_id', use_alter=True) using_options(tablename='visit_identity')
class Director(Person): movies = e.OneToMany('Movie') e.using_options(inheritance='multi')
class Collection(Entity): name = Field(UnicodeText) using_options(tablename='collections') using_table_options(schema='inventory')
class DataColumn(Entity): """Column mapper """ using_options(tablename='column') title = Field(Unicode(200)) index = Field(Integer) nb_max_cards = Field(Integer) archive = Field(Boolean, default=False) cards = OneToMany('DataCard', order_by='index', cascade='delete') board = ManyToOne('DataBoard', colname='board_id') @classmethod def create_column(cls, board, index, title, nb_cards=None, archive=False): """Create new column In: - ``board`` -- DataBoard, father of the column - ``index`` -- position in the board - ``title`` -- title of the column Return: - created DataColumn instance """ q = cls.query q = q.filter(cls.index >= index) q = q.filter(cls.board == board) q.update({'index': cls.index + 1}) col = cls(title=title, index=index, board=board, nb_max_cards=nb_cards, archive=archive) session.add(col) session.flush() return col @classmethod def delete_column(cls, column): """Delete column Delete a given column, re-index other columns and delete all cards of the column In: - ``column`` -- DataColumn instance to delete """ index = column.index board = column.board column.delete() session.flush() q = cls.query q = q.filter(cls.index >= index) q = q.filter(cls.board == board) q.update({'index': cls.index - 1}) def reorder(self): for i, card in enumerate(self.cards): card.index = i def get_cards_count(self): q = DataCard.query.filter(DataCard.column_id == self.id) return q.count()
class DataChecklist(Entity): using_options(tablename='checklists') title = Field(Unicode(255)) items = OneToMany('DataChecklistItem', order_by='index', inverse='checklist', collection_class=ordering_list('index')) card = ManyToOne('DataCard') author = ManyToOne('DataUser') index = Field(Integer) @classmethod def get_by_card(cls, card): q = cls.query q = q.filter_by(card=card) q = q.order_by(cls.index) return q.all() def update(self, other): self.title = other.title self.index = other.index for item in other.items: self.items.append( DataChecklistItem(title=item.title, index=item.index, done=False)) database.session.flush() def __unicode__(self): titles = [item.title for item in self.items if item.title] if self.title: titles.insert(0, self.title) return u'\n'.join(titles) def to_indexable(self): return unicode(self) def add_item_from_str(self, text): item = DataChecklistItem.new_from_str(text) return self.add_item(item) def add_item(self, item): self.items.append(item) return item def insert_item(self, index, item): self.items.insert(index, item) def remove_item(self, item): self.items.remove(item) def delete_item(self, item): self.remove_item(item) item.delete() def purge(self): for item in self.items: item.delete() @staticmethod def total_items(card): return DataChecklistItem.total_items(card) @staticmethod def total_items_done(card): return DataChecklistItem.total_items_done(card)
class Provinces(Entity): using_options(tablename='provinces') id = Field(Integer, primary_key=True) name = Field(String(255))
class DataHistory(Entity): using_options(tablename='history', order_by='-when') when = Field(DateTime) action = Field(Unicode(255)) data = Field(JSONType) board = ManyToOne('DataBoard', ondelete='cascade') card = ManyToOne('DataCard', ondelete='cascade', required=True) user = ManyToOne('DataUser', ondelete='cascade', required=True) def to_string(self): data = self.data.copy() data['author'] = self.user.fullname or self.user.username return render_event(self.action, data) @classmethod def add_history(cls, board, card, user, action, data): data.update(action=action) when = datetime.utcnow() data = cls(when=when, action=action, board=board, card=card, user=user, data=data) database.session.flush() @classmethod def get_events(cls, board, hours=None): '''board to None means "everything".''' since = datetime.utcnow() - timedelta(hours=hours) q = cls.query if board: q = q.filter_by(board=board) q = q.filter(cls.when >= since) q = q.order_by(cls.board_id, cls.action, cls.when) return q.all() @classmethod def get_history(cls, board, cardid=None, username=None): q = cls.query q = q.filter_by(board=board) if cardid: q = q.filter(cls.card.has(id=cardid)) if username: q = q.filter(cls.user.has(username=username)) return q @classmethod def get_last_activity(cls, board): q = database.session.query(cls.when) q = q.filter(cls.board == board) q = q.order_by(cls.when.desc()) q = q.limit(1) return q.scalar() @classmethod def purge(cls, card): q = database.session.query(cls).filter(cls.card == card) for log in q: log.delete()
class CompanyPartner(Entity): using_options(tablename='company_partner') id = Field(Integer, primary_key=True) name = Field(String(255), default='')
class Artigo(Entity): using_options(tablename='artigo_lei') lei = ManyToOne('Lei') numero = Field(Unicode(32)) paragrafos = OneToMany('Paragrafo') incisos = OneToMany('Inciso')
class Schools(Entity): using_options(tablename='schools') id = Field(Integer, primary_key=True) name = Field(String(255), default='')
class Paragrafo(Entity): using_options(tablename='paragrafo_artigo') artigo = ManyToOne('Artigo') numero = Field(Unicode(128))
class CategoryEntity(elixir.Entity): elixir.using_options(tablename=str(categories)) # save_on_init IS VERY IMPORTANT elixir.using_mapper_options(save_on_init=False)
class Inciso(Entity): using_options(tablename='inciso_artigo') artigo = ManyToOne('Artigo') numero = Field(Unicode(32)) letras = OneToMany('Letra')
class DataBoard(Entity): """Board mapper - ``title`` -- board title - ``is_template`` -- is this a real board or a template? - ``columns`` -- list of board columns - ``labels`` -- list of labels for cards - ``comments_allowed`` -- who can comment ? (0 nobody, 1 board members only , 2 all application users) - ``votes_allowed`` -- who can vote ? (0 nobody, 1 board members only , 2 all application users) - ``description`` -- board description - ``visibility`` -- board visibility (0 Private, 1 Public) - ``members`` -- list of members (simple members and manager) - ``managers`` -- list of managers - ``uri`` -- board URI (Universally Unique IDentifier) - ``last_users`` -- list of last users - ``pending`` -- invitations pending for new members (use token) - ``archive`` -- display archive column ? (0 false, 1 true) - ``archived`` -- is board archived ? """ using_options(tablename='board') title = Field(Unicode(255)) is_template = Field(Boolean, default=False) columns = OneToMany('DataColumn', order_by="index", cascade='delete') labels = OneToMany('DataLabel', order_by='index') comments_allowed = Field(Integer, default=1) votes_allowed = Field(Integer, default=1) description = Field(UnicodeText, default=u'') visibility = Field(Integer, default=0) version = Field(Integer, default=0, server_default='0') board_members = OneToMany('DataBoardMember', cascade='delete') board_managers = OneToMany('DataBoardManager', cascade='delete') members = AssociationProxy('board_members', 'member', creator=lambda member: DataBoardMember(member=member)) managers = AssociationProxy('board_managers', 'member', creator=lambda member: DataBoardManager(member=member)) uri = Field(Unicode(255), index=True, unique=True) last_users = ManyToOne('DataUser', order_by=('fullname', 'email')) pending = OneToMany('DataToken', order_by='username') history = OneToMany('DataHistory') background_image = Field(Unicode(255)) background_position = Field(Unicode(255)) title_color = Field(Unicode(255)) show_archive = Field(Integer, default=0) archived = Field(Boolean, default=False) weighting_cards = Field(Integer, default=0) weights = Field(Unicode(255), default=u'') @property def template_title(self): if not self.managers or self.visibility == 0: return self.title return u'{0} ({1})'.format(self.title, self.managers[0].fullname) def copy(self): new_data = DataBoard(title=self.title, description=self.description, background_position=self.background_position, title_color=self.title_color, comments_allowed=self.comments_allowed, votes_allowed=self.votes_allowed, weighting_cards=self.weighting_cards, weights=self.weights) session.flush() # TODO: move to board extension for label in self.labels: new_data.labels.append(label.copy()) session.flush() return new_data def get_label_by_title(self, title): return (l for l in self.labels if l.title == title).next() def delete_members(self): for member in self.board_members: session.delete(member) session.flush() def delete_history(self): for event in self.history: session.delete(event) session.flush() def increase_version(self): self.version += 1 if self.version > 2147483600: self.version = 1 @property def url(self): return urllib.quote_plus( "%s/%s" % (self.title.encode('ascii', 'ignore'), self.uri), '/' ) def __init__(self, *args, **kwargs): """Initialization. Create board and uri of the board """ super(DataBoard, self).__init__(*args, **kwargs) self.uri = unicode(uuid.uuid4()) @classmethod def get_by_id(cls, id): return cls.get(id) @classmethod def get_by_uri(cls, uri): return cls.get_by(uri=uri) def has_member(self, user): """Return True if user is member of the board In: - ``user`` -- user to test (User instance) Return: - True if user is member of the board """ return user.data in self.members def remove_member(self, board_member): board_member.delete() def has_manager(self, user): """Return True if user is manager of the board In: - ``user`` -- user to test (User instance) Return: - True if user is manager of the board """ return user.data in self.managers def remove_manager(self, board_member): obj = DataBoardManager.get_by(board=self, member=board_member.get_user_data()) if obj is not None: obj.delete() self.remove_member(board_member) def change_role(self, board_member, new_role): obj = DataBoardManager.get_by(board=self, member=board_member.get_user_data()) if new_role == 'manager': if obj is None: obj = DataBoardManager(board=self, member=board_member.get_user_data()) session.add(obj) else: if obj is not None: obj.delete() def last_manager(self, member): """Return True if member is the last manager of the board""" return member.role == 'manager' and len(self.managers) == 1 def add_member(self, new_member, role='member'): """ Add new member to the board In: - ``new_member`` -- user to add (DataUser instance) - ``role`` -- role's member (manager or member) """ self.board_members.append(DataBoardMember(member=new_member.data)) if role == 'manager': self.managers.append(new_member.data) session.flush() def get_pending_users(self): emails = [token.username for token in self.pending] return DataUser.query.filter(DataUser.email.in_(emails)) def set_background_image(self, image): self.background_image = image or u'' @classmethod def get_all_board_ids(cls): return session.query(cls.id).filter_by(is_template=False).order_by(cls.title) @classmethod def get_templates_for(cls, user_username, user_source, public_value): q = cls.query q = q.filter(cls.archived == False) q = q.filter(cls.is_template == True) q = q.order_by(cls.title) q1 = q.filter(cls.visibility == public_value) q2 = q.join(DataBoardManager) q2 = q2.filter(DataBoardManager.user_username == user_username) q2 = q2.filter(DataBoardManager.user_source == user_source) q2 = q2.filter(cls.visibility != public_value) return q1, q2 def create_column(self, index, title, nb_cards=None, archive=False): return DataColumn.create_column(self, index, title, nb_cards, archive) def create_label(self, title, color): label = DataLabel(title=title, color=color) self.labels.append(label) session.flush() return label
class Letra(Entity): using_options(tablename='letra_inciso') inciso = ManyToOne('Inciso') letra = Field(Unicode(16))
class User(Entity): """Reasonably basic User definition. Probably would want additional attributes. """ using_options(tablename="user", auto_primarykey="user_id") user_name = Field(Unicode(16), required=True, unique=True) _password = Field(Unicode(80), colname="password", required=True) groups = ManyToMany( "Group", inverse="users", tablename="user_group", local_colname="group_id", remote_colname="user_id", ) def _set_password(self, password): """Hash password on the fly.""" hashed_password = password if isinstance(password, unicode): password_8bit = password.encode('UTF-8') else: password_8bit = password salt = sha1() salt.update(os.urandom(60)) hash = sha1() hash.update(password_8bit + salt.hexdigest()) hashed_password = salt.hexdigest() + hash.hexdigest() # Make sure the hased password is an UTF-8 object at the end of the # process because SQLAlchemy _wants_ a unicode object for Unicode # fields if not isinstance(hashed_password, unicode): hashed_password = hashed_password.decode('UTF-8') self._password = hashed_password def _get_password(self): """Return the password hashed""" return self._password password = descriptor=property(_get_password, _set_password) def validate_password(self, password): """Check the password against existing credentials. :param password: the password that was provided by the user to try and authenticate. This is the clear text version that we will need to match against the hashed one in the database. :type password: unicode object. :return: Whether the password is valid. :rtype: bool """ hashed_pass = sha1() hashed_pass.update(password + self.password[:40]) return self.password[40:] == hashed_pass.hexdigest()
class Director(Entity): name = Field(Unicode(60)) movies = OneToMany('Movie', inverse='director') using_options(tablename='directors')