Exemplo n.º 1
0
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',
                      collection_class=ordering_list('index'))
    board = ManyToOne('DataBoard', colname='board_id')

    def update(self, other):
        self.title = other.title
        self.index = other.index
        self.nb_max_cards = other.nb_max_cards
        session.flush()

    @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

    def create_card(self, title, user):
        card = DataCard(title=title, creation_date=datetime.now())
        self.cards.append(card)
        session.flush()
        return card

    def remove_card(self, card):
        if card in self.cards:
            self.cards.remove(card)

    def insert_card(self, index, card):
        self.cards.insert(index, card)

    def delete_card(self, card):
        self.remove_card(card)
        card.delete()

    def purge_cards(self):
        for card in self.cards:
            card.delete()

    def append_card(self, card):
        card.index = None
        self.cards.append(card)

    @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 get_cards_count(self):
        q = DataCard.query.filter(DataCard.column_id == self.id)
        return q.count()
Exemplo n.º 2
0
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')
    history = OneToMany('DataHistory')

    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

    @property
    def id(self):
        return self.username

    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]
Exemplo n.º 3
0
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()
Exemplo n.º 4
0
class DataCard(Entity):

    """Card mapper
    """
    using_options(tablename='card')
    title = Field(UnicodeText)
    description = Field(UnicodeText, default=u'')
    votes = OneToMany('DataVote')
    index = Field(Integer)
    column = ManyToOne('DataColumn')
    labels = ManyToMany('DataLabel')
    comments = OneToMany('DataComment', order_by="-creation_date")
    assets = OneToMany('DataAsset', order_by="-creation_date")
    checklists = OneToMany('DataChecklist', order_by="index")
    members = ManyToMany('DataUser')
    cover = OneToOne('DataAsset', inverse="cover")
    author = ManyToOne('DataUser', inverse="my_cards")
    creation_date = Field(DateTime)
    due_date = Field(Date)
    history = OneToMany('DataHistory')
    weight = Field(Unicode(255), default=u'')

    def delete_history(self):
        for event in self.history:
            session.delete(event)
        session.flush()

    @classmethod
    def create_card(cls, column, title, user):
        """Create new column

        In:
            - ``column`` -- DataColumn, father of the column
            - ``title`` -- title of the card
            - ``user`` -- DataUser, author of the card
        Return:
            - created DataCard instance
        """
        new_card = cls(title=title, author=user,
                       creation_date=datetime.datetime.utcnow())
        column.cards.append(new_card)
        return new_card

    @classmethod
    def delete_card(cls, card):
        """Delete card

        Delete a given card and re-index other cards

        In:
            - ``card`` -- DataCard instance to delete
        """
        index = card.index
        column = card.column
        card.delete()
        session.flush()
        # legacy databases may be broken…
        if index is None:
            return
        q = cls.query
        q = q.filter(cls.index >= index)
        q = q.filter(cls.column == column)
        q.update({'index': cls.index - 1})

    @classmethod
    def get_all(cls):
        query = cls.query.options(subqueryload('labels'), subqueryload('comments'))
        return query

    def make_cover(self, asset):
        """
        """
        DataCard.get(self.id).cover = asset.data

    def remove_cover(self):
        """
        """
        DataCard.get(self.id).cover = None

    def remove_board_member(self, member):
        """Remove member from board"""
        if member.get_user_data() in self.members:
            self.members.remove(member.get_user_data())
            session.flush()
Exemplo n.º 5
0
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()
Exemplo n.º 6
0
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
Exemplo n.º 7
0
class Artigo(Entity):
    using_options(tablename='artigo_lei')
    lei = ManyToOne('Lei')
    numero = Field(Unicode(32))
    paragrafos = OneToMany('Paragrafo')
    incisos = OneToMany('Inciso')
Exemplo n.º 8
0
class User(Entity, ShallowCopyMixin):

    first_name = Field(Unicode(64), index=True)
    last_name = Field(Unicode(64), index=True)

    username = Field(Unicode(64), unique=True, index=True)
    _password = Field(Unicode(64), colname='password', synonym='password')
    email = Field(Unicode(64), index=True)
    bio = Field(Unicode(512))
    signup_date = Field(DateTime, default=datetime.utcnow)

    location = Field(Unicode(256))

    recipes = OneToMany('Recipe', inverse='author', order_by='-last_updated')
    user_settings = OneToMany(
        'UserSetting',
        cascade='all, delete-orphan',
        collection_class=attribute_mapped_collection('name'))
    settings = AssociationProxy(
        'user_settings',
        'value',
        creator=lambda name, value: UserSetting(name=name, value=value))

    def __init__(self, *args, **kwargs):
        super(User, self).__init__(*args, **kwargs)
        UserSetting.init_defaults(self)

    @property
    def full_name(self):
        return "%s %s" % (self.first_name or '', self.last_name or '')

    @property
    def printed_name(self):
        if self.full_name.strip():
            return self.full_name.strip()
        return self.username

    @property
    def printed_first_name(self):
        if self.first_name and self.first_name.strip():
            return self.first_name.strip()
        return self.username

    @property
    def abbreviated_name(self):
        if self.first_name and self.last_name:
            return "%s %s." % (self.first_name, self.last_name[0])
        return self.username

    @property
    def password(self):
        return self._password

    @password.setter  # noqa
    def password(self, v):
        self._password = self.__hash_password__(v)

    @property
    def published_recipes(self):
        return filter(lambda r: r.state == "PUBLISHED", self.recipes)

    @property
    def drafts(self):
        return filter(
            lambda r: r.state == "DRAFT" and r.published_version is None,
            self.recipes)

    @property
    def gravatar(self):
        return 'https://www.gravatar.com/avatar/%s?d=https://draughtcraft.com/images/glass-square.png' % (
            md5(self.email.strip().lower()).hexdigest())

    @classmethod
    def __hash_password__(cls, plain, salt=None):
        if salt is None:
            salt = b64encode(urandom(6))
        if ':' not in salt:
            salt = '%s:' % salt
        salt = salt.split(':')[0]
        return '%s:%s' % (
            salt, b64encode(PBKDF2(plain, salt, iterations=16000).read(32)))

    @classmethod
    def validate(cls, username, password):
        # Lookup the user
        user = cls.get_by(username=username)
        if user:
            salt = user.password.split(':')[0]
            pbk = cls.__hash_password__(password, salt)

            # If PBKDF2 matches...
            match = cls.query.filter(
                and_(cls.username == username, cls.password == pbk)).first()
            if match is not None:
                return match

            # Otherwise the user might have a sha256 password
            salt = getattr(getattr(conf, 'session', None), 'password_salt',
                           'example')
            sha = sha256(password + salt).hexdigest()

            # If sha256 matches...
            match = cls.query.filter(
                and_(cls.username == username, cls.password == sha)).first()
            if match is not None:
                # Overwrite to use PBKDF2 in the future
                user.password = password
                return match
Exemplo n.º 9
0
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()
Exemplo n.º 10
0
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)
Exemplo n.º 11
0
class Recipe(Entity, DeepCopyMixin, ShallowCopyMixin):

    TYPES = ('MASH', 'EXTRACT', 'EXTRACTSTEEP', 'MINIMASH')

    MASH_METHODS = ('SINGLESTEP', 'TEMPERATURE', 'DECOCTION', 'MULTISTEP')

    STATES = ('DRAFT', 'PUBLISHED')

    type = Field(Enum(*TYPES, native_enum=False), default='MASH', index=True)
    name = Field(Unicode(256), index=True)
    gallons = Field(Float, default=5)
    boil_minutes = Field(Integer, default=60)
    notes = Field(UnicodeText)
    creation_date = Field(DateTime, default=datetime.utcnow)
    last_updated = Field(DateTime, default=datetime.utcnow, index=True)

    # Cached statistics
    _og = Field(Float, colname='og')
    _fg = Field(Float, colname='fg')
    _abv = Field(Float, colname='abv')
    _srm = Field(Integer, colname='srm')
    _ibu = Field(Integer, colname='ibu')

    mash_method = Field(Enum(*MASH_METHODS, native_enum=False),
                        default='SINGLESTEP')
    mash_instructions = Field(UnicodeText)

    state = Field(Enum(*STATES, native_enum=False), default='DRAFT')
    current_draft = ManyToOne('Recipe', inverse='published_version')
    published_version = OneToOne('Recipe',
                                 inverse='current_draft',
                                 order_by='creation_date')

    copied_from = ManyToOne('Recipe', inverse='copies')
    copies = OneToMany('Recipe',
                       inverse='copied_from',
                       order_by='creation_date')

    views = OneToMany('RecipeView',
                      inverse='recipe',
                      cascade='all, delete-orphan')
    additions = OneToMany('RecipeAddition',
                          inverse='recipe',
                          cascade='all, delete-orphan')
    fermentation_steps = OneToMany('FermentationStep',
                                   inverse='recipe',
                                   order_by='step',
                                   cascade='all, delete-orphan')
    slugs = OneToMany('RecipeSlug',
                      inverse='recipe',
                      order_by='id',
                      cascade='all, delete-orphan')
    style = ManyToOne('Style', inverse='recipes')
    author = ManyToOne('User', inverse='recipes')

    __ignored_properties__ = ('current_draft', 'published_version', 'copies',
                              'views', 'creation_date', 'state', '_og', '_fg',
                              '_abv', '_srm', '_ibu')

    def __init__(self, **kwargs):
        super(Recipe, self).__init__(**kwargs)
        if kwargs.get('name') and not kwargs.get('slugs'):
            self.slugs.append(entities.RecipeSlug(name=kwargs['name']))

    def duplicate(self, overrides={}):
        """
        Used to duplicate a recipe.

        An optional hash of `overrides` can be specified to override the
        default copied values, e.g.,

        dupe = user.recipes[0].duplicate({'author': otheruser})
        assert dupe.author == otheruser
        """
        # Make a deep copy of the instance
        copy = deepcopy(self)

        # For each override...
        for k, v in overrides.items():

            # If the key is already defined, and is a list (i.e., a ManyToOne)
            if isinstance(getattr(copy, k, None), list):

                #
                # Delete each existing entity, because we're about to
                # override the value.
                #
                for i in getattr(copy, k):
                    i.delete()

            # Set the new (overridden) value
            setattr(copy, k, v)

        return copy

    def draft(self):
        """
        Used to create a new, unpublished draft of a recipe.
        """
        if self.current_draft:
            self.current_draft.delete()
            self.current_draft = None

        return self.duplicate({'published_version': self})

    def publish(self):
        """
        Used to publish an orphan draft as a new recipe.
        """
        assert self.state == 'DRAFT', "Only drafts can be published."

        # If this recipe is a draft of another, merge the changes back in
        if self.published_version:
            return self.merge()

        # Otherwise, just set the state to PUBLISHED
        self.state = 'PUBLISHED'

        # Store cached values
        self._og = self.calculations.og
        self._fg = self.calculations.fg
        self._abv = self.calculations.abv
        self._srm = self.calculations.srm
        self._ibu = self.calculations.ibu

        # Generate a new slug if the existing one hasn't changed.
        existing = [slug.slug for slug in self.slugs]
        if entities.RecipeSlug.to_slug(self.name) not in existing:
            self.slugs.append(entities.RecipeSlug(name=self.name))

    def merge(self):
        """
        Used to merge a drafted recipe's changes back into its source.
        """

        # Make sure this is a draft with a source recipe
        assert self.state == 'DRAFT', "Only drafts can be merged."
        source = self.published_version
        assert source is not None, \
            "This recipe doesn't have a `published_version`."

        # Clone the draft onto the published version
        self.__copy_target__ = self.published_version
        deepcopy(self)

        # Delete the draft
        self.delete()

    @property
    def calculations(self):
        return Calculations(self)

    @property
    def efficiency(self):
        if self.author:
            return self.author.settings['brewhouse_efficiency']
        return .75

    @property
    def unit_system(self):
        if request.context['metric'] is True:
            return 'METRIC'
        return 'US'

    @property
    def metric(self):
        return self.unit_system == 'METRIC'

    @property
    def liters(self):
        liters = to_metric(*(self.gallons, "GALLON"))[0]
        return round(liters, 3)

    @liters.setter  # noqa
    def liters(self, v):
        gallons = to_us(*(v, "LITER"))[0]
        self.gallons = gallons

    def _partition(self, additions):
        """
        Partition a set of recipe additions
        by ingredient type, e.g.,:

        _partition([grain, grain2, hop])
        {'Fermentable': [grain, grain2], 'Hop': [hop]}
        """
        p = {}
        for a in additions:
            p.setdefault(a.ingredient.__class__, []).append(a)
        return p

    def _percent(self, partitions):
        """
        Calculate percentage of additions by amount
        within a set of recipe partitions.
        e.g.,

        _percent({'Fermentable': [grain, grain2], 'Hop': [hop]})
        {grain : .75, grain2 : .25, hop : 1}
        """

        percentages = {}
        for type, additions in partitions.items():
            total = sum([addition.amount for addition in additions])
            for addition in additions:
                if total:
                    percentages[addition] = float(
                        addition.amount) / float(total)
                else:
                    percentages[addition] = 0

        return percentages

    @property
    def mash(self):
        return self._partition([a for a in self.additions if a.step == 'mash'])

    @property
    def boil(self):
        return self._partition([a for a in self.additions if a.step == 'boil'])

    @property
    def fermentation(self):
        return self._partition(
            [a for a in self.additions if a.step == 'fermentation'])

    def contains(self, ingredient, step):
        if step not in ('mash', 'boil', 'fermentation'):
            return False

        additions = getattr(self, step)
        for a in sum(additions.values(), []):
            if a.ingredient == ingredient:
                return True

        return False

    @property
    def next_fermentation_step(self):
        """
        The next available fermentation step for a recipe.
        e.g., if temperature/length is already defined for "PRIMARY" a
        fermentation period, returns "SECONDARY".  If "SECONDARY" is already
        defined, returns "TERTIARY".

        Always returns one of `model.FermentationStep.STEPS`.
        """

        total = len(self.fermentation_steps)

        return {1: 'SECONDARY', 2: 'TERTIARY'}.get(total, None)

    def url(self, public=True):
        """
        The URL for a recipe.
        """
        return '/recipes/%s/%s/%s' % (
            ('%x' % self.id).lower(), self.slugs[-1].slug,
            '' if public else 'builder')

    @property
    def printable_type(self):
        return {
            'MASH': 'All Grain',
            'EXTRACT': 'Extract',
            'EXTRACTSTEEP': 'Extract w/ Steeped Grains',
            'MINIMASH': 'Mini-Mash'
        }[self.type]

    def touch(self):
        self.last_updated = datetime.utcnow()

    @property
    def og(self):
        return self._og

    @property
    def fg(self):
        return self._fg

    @property
    def abv(self):
        return self._abv

    @property
    def srm(self):
        return self._srm

    @property
    def ibu(self):
        return self._ibu

    def to_xml(self):
        from draughtcraft.lib.beerxml import export
        kw = {
            'name': self.name,
            'type': {
                'MASH': 'All Grain',
                'MINIMASH': 'Partial Mash'
            }.get(self.type, 'Extract'),
            'brewer': self.author.printed_name if self.author else 'Unknown',
            'batch_size': self.liters,
            'boil_size': self.liters * 1.25,
            'boil_time': self.boil_minutes,
            'notes': self.notes,
            'fermentation_stages': len(self.fermentation_steps),
        }

        hops = [a.to_xml() for a in self.additions if a.hop]
        fermentables = [a.to_xml() for a in self.additions if a.fermentable]
        yeast = [a.to_xml() for a in self.additions if a.yeast]
        extras = [a.to_xml() for a in self.additions if a.extra]

        kw['hops'] = hops
        kw['fermentables'] = fermentables
        kw['yeasts'] = yeast
        kw['miscs'] = extras

        kw['mash'] = []
        kw['waters'] = []

        if self.style is None:
            kw['style'] = export.Style(name='',
                                       category='No Style Chosen',
                                       type='None',
                                       category_number=0,
                                       style_letter='',
                                       og_min=0,
                                       og_max=0,
                                       ibu_min=0,
                                       ibu_max=0,
                                       color_min=0,
                                       color_max=0,
                                       fg_min=0,
                                       fg_max=0)
        else:
            kw['style'] = self.style.to_xml()

        if self.type != 'EXTRACT':
            kw['efficiency'] = self.efficiency * 100.00

        for stage in self.fermentation_steps:
            if stage.step == 'PRIMARY':
                kw['primary_age'] = stage.days
                kw['primary_temp'] = stage.celsius
            if stage.step == 'SECONDARY':
                kw['secondary_age'] = stage.days
                kw['secondary_temp'] = stage.celsius
            if stage.step == 'TERTIARY':
                kw['tertiary_age'] = stage.days
                kw['tertiary_temp'] = stage.celsius

        return export.Recipe(**kw).render()

    def __json__(self):
        from draughtcraft.templates.helpers import alphanum_key

        def inventory(cls, types=[]):
            return sorted([
                f.__json__() for f in cls.query.all()
                if not types or (types and f.type in types)
            ],
                          key=lambda f: alphanum_key(f['name']))

        #
        # Attempt to look up the preferred calculation method for the
        # recipe's author.
        #
        ibu_method = 'tinseth'
        user = self.author
        if user:
            ibu_method = user.settings.get('default_ibu_formula', 'tinseth')

        return {
            # Basic attributes
            'name':
            self.name,
            'author':
            self.author.username if self.author else '',
            'style':
            self.style.id if self.style else None,
            'gallons':
            self.gallons,

            # Ingredients
            'mash':
            filter(lambda a: a.step == 'mash', self.additions),
            'boil':
            filter(lambda a: a.step == 'boil', self.additions),
            'fermentation':
            filter(lambda a: a.step == 'fermentation', self.additions),
            'ibu_method':
            ibu_method,
            'efficiency':
            self.efficiency,

            # Inventory
            'inventory': {
                'malts':
                inventory(entities.Fermentable,
                          ('MALT', 'GRAIN', 'ADJUNCT', 'SUGAR')),
                'extracts':
                inventory(entities.Fermentable, ('EXTRACT', )),
                'hops':
                inventory(entities.Hop),
                'yeast':
                inventory(entities.Yeast),
                'extras':
                inventory(entities.Extra)
            },

            # Extras
            'mash_method':
            self.mash_method,
            'mash_instructions':
            self.mash_instructions,
            'boil_minutes':
            self.boil_minutes,
            'fermentation_steps':
            self.fermentation_steps,
            'notes':
            self.notes,
            'metric':
            self.metric
        }
Exemplo n.º 12
0
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 ''
Exemplo n.º 13
0
class Style(Entity, ShallowCopyMixin):

    TYPES = [
        'LAGER',
        'ALE',
        'MEAD',
        'CIDER'
    ]

    uid = Field(Unicode(32), unique=True)
    name = Field(Unicode(256), index=True)
    url = Field(Unicode(256))

    # Gravities
    min_og = Field(Float)
    max_og = Field(Float)
    min_fg = Field(Float)
    max_fg = Field(Float)

    # IBU
    min_ibu = Field(Integer)
    max_ibu = Field(Integer)

    # SRM
    min_srm = Field(Integer)
    max_srm = Field(Integer)

    # ABV
    min_abv = Field(Float)
    max_abv = Field(Float)

    category = Field(Unicode(64))
    category_number = Field(Integer)
    style_letter = Field(Unicode(1))

    type = Field(Enum(*TYPES, native_enum=False))

    recipes = OneToMany('Recipe', inverse='style')

    def defined(self, statistic):
        if statistic not in (
            'og',
            'fg',
            'abv',
            'srm',
            'ibu'
        ):
            raise InvalidStatistic('Invalid statistic, %s' % statistic)

        minimum = getattr(self, 'min_%s' % statistic)
        maximum = getattr(self, 'max_%s' % statistic)

        return minimum is not None and maximum is not None

    def matches(self, recipe, statistic):
        if statistic not in (
            'og',
            'fg',
            'abv',
            'srm',
            'ibu'
        ):
            raise InvalidStatistic('Invalid statistic, %s' % statistic)

        minimum = getattr(self, 'min_%s' % statistic)
        maximum = getattr(self, 'max_%s' % statistic)

        if minimum is None or maximum is None:
            return False

        actual = getattr(recipe.calculations, statistic)

        if actual <= maximum and actual >= minimum:
            return True

        return False

    def to_xml(self):
        from draughtcraft.lib.beerxml import export
        kw = {
            'name': self.name,
            'category': self.category,
            'category_number': self.category_number,
            'style_letter': self.style_letter,
            'style_guide': 'BJCP',
            'type': self.type.capitalize(),
            'og_min': self.min_og,
            'og_max': self.max_og,
            'fg_min': self.min_fg,
            'fg_max': self.max_fg,
            'ibu_min': self.min_ibu,
            'ibu_max': self.max_ibu,
            'color_min': self.min_srm,
            'color_max': self.max_srm,
            'abv_min': self.min_abv,
            'abv_max': self.max_abv
        }
        return export.Style(**kw)
Exemplo n.º 14
0
class User(Entity):
    """Mapper for the `user' entity that is the same table used by
    wordpress"""
    using_options(tablename='wp_users')
    id = Field(Integer, primary_key=True)
    name = Field(Unicode(64), colname='user_nicename')
    nickname = Field(Unicode(64), colname='display_name')
    username = Field(Unicode(64), colname='user_login')
    password = Field(Unicode(256), colname='user_pass')
    email = Field(Unicode(64), colname='user_email')
    user_activation_key = Field(Unicode(60))

    receive_sms   = Field(Boolean)
    receive_email = Field(Boolean)

    buzzes = OneToMany('Buzz')
    contribs = OneToMany('Contrib')
    follows = OneToMany('UserFollow')
    creation_date = Field(
        DateTime, colname='user_registered',
        default=datetime.now)
    status = Field(
        Boolean, colname='user_status',
        default=True)

    @before_insert
    def set_defaults(self):
        """Sets default values to fields that depends on other field
        values to be set before inserting"""
        if not self.name:
            self.name = unicode(self.username)
        if not self.nickname:
            self.nickname = unicode(self.name)

    @before_insert
    def hash_password(self):
        """Converts the password field into a phpass hashed string"""
        print "\n\nGRAVANDO SENHA NOVA 1:",self.password
        hasher = phpass.PasswordHash(8, False)
        self.password = hasher.hash_password(self.password)
        print "GRAVANDO SENHA NOVA 2:",self.password,"\n\n"

    def set_password(self, passwd):
        """Updates the user's password

        This method does _not_ update the database itself, please call
        the '''session.commit()''' method.
        """
        self.password = passwd
        self.hash_password()

    def has_roles(self, roles):
        """Returns True if the current user has one of the given roles.

        This method uses the `OR' logic. If the user has _AT LEAST ONE_
        of the specified roles, it'll return True.
        """

        # Making sure we're handling lists or tuples
        if not isinstance(roles, (list, tuple)):
            raise TypeError('has_roles() does not handle any thing but '
                            'lists and tuples')

        # So, let's query for the usermeta object. Once we don't handle
        # wordpress registered rows, let's make sure that it will never
        # raise an unexpected exception.
        query = UserMeta.query.filter_by(
            user_id=self.id,
            meta_key='wp_capabilities')
        try:
            meta = query.one()
        except NoResultFound:
            return False

        # Now it is tome to make sure that the user has _AT LEAST ONE_
        # of the roles specified in the `roles' param.
        return True in [i.lower() in meta.meta_value for i in roles]

    def public_dict(self):
        """Returns all public items about the user in a dict format"""
        # It's easier to create a new dict than selecting each public
        # field from the `.to_dict()' return.
        return dict(
            id=self.id,
            name=self.name,
            nickname=self.nickname,
            email=self.email,
            display_name=self.display_name,
            avatar_url=self.avatar_url,
            creation_date=self.creation_date,
            facebook=self.get_meta('facebook'),
            twitter=self.get_meta('twitter')
        )

    def public_json(self):
        """Returns the same content as `public_dict()', but in JSON
        format"""
        return dumps(self.public_dict())

    @property
    def avatar_url(self):
        """Returns the avatar image of this user or the default one"""
        fname = self.get_meta('avatar')
        return fname and Upload.imageset.url(fname) or \
            url_for('static', filename='img/avatar.png')

    @property
    def display_name(self):
        """Just a shortcut to decide which value should be exposed to
        identify a user"""
        return self.nickname or self.name

    def get_meta(self, key):
        """Returns a value of a meta var of a user"""
        try:
            return UserMeta.query \
                .filter_by(user_id=self.id, meta_key=key) \
                .one() \
                .meta_value
        except NoResultFound:
            return None

    def set_meta(self, key, value):
        """Set a meta value for the user instance.

        This method does not flush to the database! So you have to call
        the `session.commit()' method after calling it."""
        meta = get_or_create(UserMeta, user_id=self.id, meta_key=key)[0]
        meta.meta_value = value

    def metadata(self):
        """Returns _all_ metadata set for an user instance.

        This method is here to clean a mess created by using the
        wordpress scheme. Because of this choice, we have user metadata
        split between the `wp_users' and `wp_user_meta' tables (and,
        sometimes, coming from a social network).

        So, to fix this situation, I've at least wrote this method to
        provide an encapsulated method to get all metadata associated
        with an user instance.

        Be careful exposing this method to the end users. It will return
        all the information about an user, including his/her (hashed)
        password string, mail address, etc.
        """
        data = self.to_dict()
        data.update(dict((i.meta_key, i.meta_value) for i in
                     UserMeta.query.filter_by(user_id=self.id).all()))
        return data
Exemplo n.º 15
0
class Inciso(Entity):
    using_options(tablename='inciso_artigo')
    artigo = ManyToOne('Artigo')
    numero = Field(Unicode(32))
    letras = OneToMany('Letra')
Exemplo n.º 16
0
class DataMembership(Entity):
    using_options(tablename='membership')
    board = ManyToOne('DataBoard', ondelete='cascade', required=True)
    user = ManyToOne(DataUser, ondelete='cascade', required=True)
    card_memberships = OneToMany(DataCardMembership,
                                 cascade='delete, delete-orphan')
    # provisional, for notifications until they are refactored
    cards = association_proxy('card_memberships', 'card')
    manager = Field(Boolean, default=False, nullable=False)
    notify = Field(Integer, default=NOTIFY_MINE)
    using_table_options(
        sa.UniqueConstraint('board_id',
                            'user_username',
                            'user_source',
                            name='membership_ix'))

    @classmethod
    def get_for_card(cls, card):
        return cls.query.join(DataCardMembership).filter(
            DataCardMembership.card == card)

    @classmethod
    def search(cls, board, user):
        return cls.get_by(board=board, user=user)  # at most one

    @classmethod
    def subscribers(cls):
        return cls.query.filter(
            sa.or_(DataMembership.notify == NOTIFY_ALL,
                   DataMembership.notify == NOTIFY_MINE))

    @staticmethod
    def favorites_for(card):
        query = database.session.query(DataUser)
        query = query.join(DataMembership)
        # In the future, cards will be linked to boards directly, so demeter won't be hurt anymore
        query = query.filter(DataMembership.board == card.column.board)
        query = query.outerjoin(DataCardMembership)
        query = query.group_by(DataUser)
        query = query.order_by(func.count(DataUser.username).desc())
        return query

    @classmethod
    def add_card_members_from_emails(cls, card, emails):
        """Provisional: will take memberships instead of emails."""
        query = cls.query.join(DataUser).filter(
            sa.or_(DataUser.email.in_(emails),
                   DataUser.email_to_confirm.in_(emails)),
            DataMembership.board == card.column.board)
        memberships = query.all()
        for membership in memberships:
            membership.card_memberships.append(DataCardMembership(card=card))
        database.session.flush()
        return memberships

    @classmethod
    def remove_card_member(cls, card, username):
        """Provisional: will take a membership instead of username."""
        membership = cls.query.join(DataUser).filter(
            DataUser.username == username,
            cls.board == card.column.board).first()
        if membership:
            card_membership = DataCardMembership.get_by(card=card,
                                                        membership=membership)
            if card_membership:
                card_membership.delete()

    @classmethod
    def add_member(cls, board, user, manager=False):
        membership = cls(board=board, user=user, manager=manager)
        database.session.add(membership)
        database.session.flush()
        return membership

    @classmethod
    def remove_member(cls, board, user):
        membership = cls.get_by(board=board, user=user)
        if membership:
            membership.delete()
            database.session.flush()

    @classmethod
    def has_member(cls, board, user, manager=False):
        membership = cls.get_by(board=board, user=user)
        return (bool(membership)
                and membership.manager) if manager else bool(membership)

    @classmethod
    def delete_members(cls, board):
        cls.query.filter_by(board=board).delete(synchronize_session=False)

    @classmethod
    def change_role(cls, board, user, manager):
        ms = cls.get_by(board=board, user=user)
        if ms:
            ms.manager = manager
            database.session.flush()
Exemplo n.º 17
0
class Director(Entity):
    name = Field(Unicode(60))
    movies = OneToMany('Movie', inverse='director')
    using_options(tablename='directors')