Blame progit/lib/model.py

Pierre-Yves Chibon 33b534
# -*- coding: utf-8 -*-
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
"""
Pierre-Yves Chibon 8a5345
 (c) 2014-2015 - Copyright Red Hat Inc
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
 Authors:
Pierre-Yves Chibon ae4136
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
"""
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ee9b42
__requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4']
Pierre-Yves Chibon ae4136
import pkg_resources
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
import datetime
Pierre-Yves Chibon ae4136
import logging
Pierre-Yves Chibon 7379e4
import json
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
import sqlalchemy as sa
Pierre-Yves Chibon ae4136
from sqlalchemy import create_engine
Pierre-Yves Chibon caf4e8
from sqlalchemy.exc import SQLAlchemyError
Pierre-Yves Chibon ae4136
from sqlalchemy.ext.declarative import declarative_base
Pierre-Yves Chibon e28f89
from sqlalchemy.orm import backref
Pierre-Yves Chibon ae4136
from sqlalchemy.orm import sessionmaker
Pierre-Yves Chibon ae4136
from sqlalchemy.orm import scoped_session
Pierre-Yves Chibon ae4136
from sqlalchemy.orm import relation
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
BASE = declarative_base()
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
ERROR_LOG = logging.getLogger('progit.model')
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
def create_tables(db_url, alembic_ini=None, debug=False):
Pierre-Yves Chibon ae4136
    """ Create the tables in the database using the information from the
Pierre-Yves Chibon ae4136
    url obtained.
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    :arg db_url, URL used to connect to the database. The URL contains
Pierre-Yves Chibon ae4136
        information with regards to the database engine, the host to
Pierre-Yves Chibon ae4136
        connect to, the user and password and the database name.
Pierre-Yves Chibon ae4136
          ie: <engine>://<user>:<password>@<host>/<dbname></dbname></host></password></user></engine>
Pierre-Yves Chibon ae4136
    :kwarg alembic_ini, path to the alembic ini file. This is necessary
Pierre-Yves Chibon ae4136
        to be able to use alembic correctly, but not for the unit-tests.
Pierre-Yves Chibon ae4136
    :kwarg debug, a boolean specifying wether we should have the verbose
Pierre-Yves Chibon ae4136
        output of sqlalchemy or not.
Pierre-Yves Chibon ae4136
    :return a session that can be used to query the database.
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    """
Pierre-Yves Chibon ae4136
    engine = create_engine(db_url, echo=debug)
Pierre-Yves Chibon 05d0d7
    from progit.ui.plugins import get_plugin_tables
Pierre-Yves Chibon c46cda
    get_plugin_tables()
Pierre-Yves Chibon ae4136
    BASE.metadata.create_all(engine)
Pierre-Yves Chibon faa36d
    # engine.execute(collection_package_create_view(driver=engine.driver))
Pierre-Yves Chibon ae4136
    if db_url.startswith('sqlite:'):
Pierre-Yves Chibon faa36d
        # Ignore the warning about con_record
Pierre-Yves Chibon ae4136
        # pylint: disable=W0613
Pierre-Yves Chibon ff80f5
        def _fk_pragma_on_connect(dbapi_con, con_record):  # pragma: no cover
Pierre-Yves Chibon ae4136
            ''' Tries to enforce referential constraints on sqlite. '''
Pierre-Yves Chibon ae4136
            dbapi_con.execute('pragma foreign_keys=ON')
Pierre-Yves Chibon ae4136
        sa.event.listen(engine, 'connect', _fk_pragma_on_connect)
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    if alembic_ini is not None:  # pragma: no cover
Pierre-Yves Chibon ae4136
        # then, load the Alembic configuration and generate the
Pierre-Yves Chibon ae4136
        # version table, "stamping" it with the most recent rev:
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon faa36d
        # Ignore the warning missing alembic
Pierre-Yves Chibon ae4136
        # pylint: disable=F0401
Pierre-Yves Chibon ae4136
        from alembic.config import Config
Pierre-Yves Chibon ae4136
        from alembic import command
Pierre-Yves Chibon ae4136
        alembic_cfg = Config(alembic_ini)
Pierre-Yves Chibon ae4136
        command.stamp(alembic_cfg, "head")
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    scopedsession = scoped_session(sessionmaker(bind=engine))
Pierre-Yves Chibon caf4e8
    # Insert the default data into the db
Pierre-Yves Chibon c46cda
    try:
Pierre-Yves Chibon c46cda
        create_default_status(scopedsession)
Pierre-Yves Chibon ff80f5
    except SQLAlchemyError:  # pragma: no cover
Pierre-Yves Chibon c46cda
        pass
Pierre-Yves Chibon ae4136
    return scopedsession
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon caf4e8
def create_default_status(session):
Pierre-Yves Chibon caf4e8
    """ Insert the defaults status in the status tables.
Pierre-Yves Chibon caf4e8
    """
Pierre-Yves Chibon caf4e8
Pierre-Yves Chibon caf4e8
    for status in ['Open', 'Invalid', 'Insufficient data', 'Fixed']:
Pierre-Yves Chibon caf4e8
        ticket_stat = StatusIssue(status=status)
Pierre-Yves Chibon caf4e8
        session.add(ticket_stat)
Pierre-Yves Chibon caf4e8
        try:
Pierre-Yves Chibon caf4e8
            session.flush()
Pierre-Yves Chibon faa36d
        except SQLAlchemyError, err:  # pragma: no cover
Pierre-Yves Chibon caf4e8
            ERROR_LOG.debug('Status %s could not be added', ticket_stat)
Pierre-Yves Chibon caf4e8
Pierre-Yves Chibon caf4e8
    session.commit()
Pierre-Yves Chibon caf4e8
Pierre-Yves Chibon caf4e8
Pierre-Yves Chibon cc324e
class StatusIssue(BASE):
Pierre-Yves Chibon cc324e
    """ Stores the status a ticket can have.
Pierre-Yves Chibon cc324e
Pierre-Yves Chibon cc324e
    Table -- status_issue
Pierre-Yves Chibon cc324e
    """
Pierre-Yves Chibon cc324e
    __tablename__ = 'status_issue'
Pierre-Yves Chibon cc324e
Pierre-Yves Chibon cc324e
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon cc324e
    status = sa.Column(sa.Text, nullable=False, unique=True)
Pierre-Yves Chibon cc324e
Pierre-Yves Chibon cc324e
Pierre-Yves Chibon acc99e
class User(BASE):
Pierre-Yves Chibon acc99e
    """ Stores information about users.
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
    Table -- users
Pierre-Yves Chibon acc99e
    """
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
    __tablename__ = 'users'
Pierre-Yves Chibon acc99e
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon acc99e
    user = sa.Column(sa.String(32), nullable=False, unique=True, index=True)
Pierre-Yves Chibon acc99e
    fullname = sa.Column(sa.Text, nullable=False, index=True)
Pierre-Yves Chibon baeff8
    public_ssh_key = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon 13afd6
    password = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 13afd6
    token = sa.Column(sa.String(50), nullable=True)
Pierre-Yves Chibon 13afd6
    created = sa.Column(
Pierre-Yves Chibon 13afd6
        sa.DateTime,
Pierre-Yves Chibon 13afd6
        nullable=False,
Pierre-Yves Chibon 13afd6
        default=sa.func.now())
Pierre-Yves Chibon 13afd6
    updated_on = sa.Column(
Pierre-Yves Chibon 13afd6
        sa.DateTime,
Pierre-Yves Chibon 13afd6
        nullable=False,
Pierre-Yves Chibon 13afd6
        default=sa.func.now(),
Pierre-Yves Chibon 13afd6
        onupdate=sa.func.now())
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon 13e142
    # Relations
Pierre-Yves Chibon 13e142
    group_objs = relation(
Pierre-Yves Chibon 13e142
        "ProgitGroup",
Pierre-Yves Chibon 13e142
        secondary="progit_user_group",
Pierre-Yves Chibon 13e142
        primaryjoin="users.c.id==progit_user_group.c.user_id",
Pierre-Yves Chibon 13e142
        secondaryjoin="progit_group.c.id==progit_user_group.c.group_id",
Pierre-Yves Chibon 13e142
        backref="users",
Pierre-Yves Chibon 13e142
    )
Pierre-Yves Chibon 13e142
    session = relation("ProgitUserVisit", backref="user")
Pierre-Yves Chibon 13e142
Pierre-Yves Chibon 13afd6
    @property
Pierre-Yves Chibon 13afd6
    def username(self):
Pierre-Yves Chibon 13afd6
        ''' Return the username. '''
Pierre-Yves Chibon 13afd6
        return self.user
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon 13afd6
    @property
Pierre-Yves Chibon 13afd6
    def groups(self):
Pierre-Yves Chibon 13afd6
        ''' Return the list of Group.group_name in which the user is. '''
Pierre-Yves Chibon 13afd6
        return [group.group_name for group in self.group_objs]
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon 13afd6
    def __repr__(self):
Pierre-Yves Chibon 13afd6
        ''' Return a string representation of this object. '''
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon 13afd6
        return 'User: %s - name %s' % (self.id, self.user)
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
class UserEmail(BASE):
Pierre-Yves Chibon acc99e
    """ Stores email information about the users.
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
    Table -- user_emails
Pierre-Yves Chibon acc99e
    """
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
    __tablename__ = 'user_emails'
Pierre-Yves Chibon acc99e
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon acc99e
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon acc99e
        index=True)
Pierre-Yves Chibon acc99e
    email = sa.Column(sa.Text, nullable=False, unique=True)
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
    user = relation('User', foreign_keys=[user_id],
Pierre-Yves Chibon acc99e
                    remote_side=[User.id], backref='emails')
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon ae4136
class Project(BASE):
Pierre-Yves Chibon ae4136
    """ Stores the projects.
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    Table -- projects
Pierre-Yves Chibon ae4136
    """
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    __tablename__ = 'projects'
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon acc99e
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon acc99e
        index=True)
Pierre-Yves Chibon ae4136
    name = sa.Column(sa.String(32), nullable=False, index=True)
Pierre-Yves Chibon d688cf
    description = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon ae4136
    parent_id = sa.Column(
Pierre-Yves Chibon ae4136
        sa.Integer,
Pierre-Yves Chibon ae4136
        sa.ForeignKey('projects.id', onupdate='CASCADE'),
Pierre-Yves Chibon ae4136
        nullable=True)
Pierre-Yves Chibon 20b945
    issue_tracker = sa.Column(sa.Boolean, nullable=False, default=True)
Pierre-Yves Chibon 4d5b45
    project_docs = sa.Column(sa.Boolean, nullable=False, default=True)
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    date_created = sa.Column(sa.DateTime, nullable=False,
Pierre-Yves Chibon ae4136
                             default=datetime.datetime.utcnow)
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon f3f2fe
    parent = relation('Project', remote_side=[id], backref='forks')
Pierre-Yves Chibon acc99e
    user = relation('User', foreign_keys=[user_id],
Pierre-Yves Chibon acc99e
                    remote_side=[User.id], backref='projects')
Pierre-Yves Chibon 334ada
Pierre-Yves Chibon faa36d
    users = relation(
Pierre-Yves Chibon faa36d
        'User',
Pierre-Yves Chibon 9ac467
        secondary="user_projects",
Pierre-Yves Chibon 9ac467
        primaryjoin="projects.c.id==user_projects.c.project_id",
Pierre-Yves Chibon 9ac467
        secondaryjoin="users.c.id==user_projects.c.user_id",
Pierre-Yves Chibon faa36d
        backref='co_projects'
Pierre-Yves Chibon faa36d
    )
Pierre-Yves Chibon 9ac467
Mathieu Bridon b0b97e
    @property
Mathieu Bridon b0b97e
    def path(self):
Pierre-Yves Chibon 0c862c
        ''' Return the name of the git repo on the filesystem. '''
Pierre-Yves Chibon ebf29e
        if self.parent_id:
Pierre-Yves Chibon 51f6a6
            path = '%s/%s.git' % (self.user.user, self.name)
Pierre-Yves Chibon ebf29e
        else:
Pierre-Yves Chibon ebf29e
            path = '%s.git' % (self.name)
Pierre-Yves Chibon ebf29e
        return path
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon b393e8
    @property
Pierre-Yves Chibon e2c16f
    def is_fork(self):
Pierre-Yves Chibon 49feff
        ''' Return a boolean specifying if the project is a fork or not '''
Pierre-Yves Chibon 3a95ee
        return self.parent_id is not None
Pierre-Yves Chibon 49feff
Pierre-Yves Chibon 49feff
    @property
Pierre-Yves Chibon b393e8
    def fullname(self):
Pierre-Yves Chibon b393e8
        ''' Return the name of the git repo as user/project if it is a
Pierre-Yves Chibon b393e8
        project forked, otherwise it returns the project name.
Pierre-Yves Chibon b393e8
        '''
Pierre-Yves Chibon b393e8
        str_name = self.name
Pierre-Yves Chibon 3a95ee
        if self.parent_id:
Pierre-Yves Chibon 51f6a6
            str_name = "%s/%s" % (self.user.user, str_name)
Pierre-Yves Chibon b393e8
        return str_name
Pierre-Yves Chibon b393e8
Pierre-Yves Chibon 2f8732
Pierre-Yves Chibon 96fa1e
class ProjectUser(BASE):
Pierre-Yves Chibon 96fa1e
    """ Stores the user of a projects.
Pierre-Yves Chibon 96fa1e
Pierre-Yves Chibon 96fa1e
    Table -- user_projects
Pierre-Yves Chibon 96fa1e
    """
Pierre-Yves Chibon 96fa1e
Pierre-Yves Chibon 96fa1e
    __tablename__ = 'user_projects'
Pierre-Yves Chibon 96fa1e
    __table_args__ = (
Pierre-Yves Chibon acc99e
        sa.UniqueConstraint('project_id', 'user_id'),
Pierre-Yves Chibon 96fa1e
    )
Pierre-Yves Chibon 96fa1e
Pierre-Yves Chibon 96fa1e
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 96fa1e
    project_id = sa.Column(
Pierre-Yves Chibon 96fa1e
        sa.Integer,
Pierre-Yves Chibon 96fa1e
        sa.ForeignKey('projects.id', onupdate='CASCADE'),
Pierre-Yves Chibon 96fa1e
        nullable=False)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon acc99e
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon acc99e
        index=True)
Pierre-Yves Chibon 96fa1e
Pierre-Yves Chibon 96fa1e
Pierre-Yves Chibon ae4136
class Issue(BASE):
Pierre-Yves Chibon ae4136
    """ Stores the issues reported on a project.
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    Table -- issues
Pierre-Yves Chibon ae4136
    """
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    __tablename__ = 'issues'
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 366781
    uid = sa.Column(sa.String(32), unique=True, nullable=False)
Pierre-Yves Chibon ae4136
    project_id = sa.Column(
Pierre-Yves Chibon ae4136
        sa.Integer,
Pierre-Yves Chibon ae4136
        sa.ForeignKey(
Pierre-Yves Chibon ae4136
            'projects.id', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon 70e8de
        primary_key=True)
Pierre-Yves Chibon ae4136
    title = sa.Column(
Pierre-Yves Chibon ae4136
        sa.Text,
Pierre-Yves Chibon ae4136
        nullable=False)
Pierre-Yves Chibon ae4136
    content = sa.Column(
Pierre-Yves Chibon ae4136
        sa.Text(),
Pierre-Yves Chibon ae4136
        nullable=False)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon acc99e
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon acc99e
        index=True)
Pierre-Yves Chibon fdf0f1
    assignee_id = sa.Column(
Pierre-Yves Chibon fdf0f1
        sa.Integer,
Pierre-Yves Chibon fdf0f1
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon fdf0f1
        nullable=True,
Pierre-Yves Chibon fdf0f1
        index=True)
Pierre-Yves Chibon caf4e8
    status = sa.Column(
Pierre-Yves Chibon caf4e8
        sa.Text,
Pierre-Yves Chibon caf4e8
        sa.ForeignKey(
Pierre-Yves Chibon caf4e8
            'status_issue.status', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon caf4e8
        default='Open',
Pierre-Yves Chibon caf4e8
        nullable=False)
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
    date_created = sa.Column(sa.DateTime, nullable=False,
Pierre-Yves Chibon ae4136
                             default=datetime.datetime.utcnow)
Pierre-Yves Chibon 7b8c4b
Pierre-Yves Chibon 0ef501
    project = relation(
Pierre-Yves Chibon 0ef501
        'Project', foreign_keys=[project_id], remote_side=[Project.id],
Pierre-Yves Chibon 81b45a
        backref='issues', cascade="delete, delete-orphan",
Pierre-Yves Chibon 81b45a
        single_parent=True)
Pierre-Yves Chibon acc99e
    user = relation('User', foreign_keys=[user_id],
Pierre-Yves Chibon acc99e
                    remote_side=[User.id], backref='issues')
Pierre-Yves Chibon fdf0f1
    assignee = relation('User', foreign_keys=[assignee_id],
Pierre-Yves Chibon 6ceec6
                        remote_side=[User.id], backref='assigned_issues')
Pierre-Yves Chibon 0ef501
Pierre-Yves Chibon 403570
    parents = relation(
Pierre-Yves Chibon 403570
        "Issue",
Pierre-Yves Chibon 403570
        secondary="issue_to_issue",
Pierre-Yves Chibon 763cad
        primaryjoin="issues.c.uid==issue_to_issue.c.child_issue_id",
Pierre-Yves Chibon 763cad
        secondaryjoin="issue_to_issue.c.parent_issue_id==issues.c.uid",
Pierre-Yves Chibon 403570
        backref="children",
Pierre-Yves Chibon 403570
    )
Pierre-Yves Chibon 403570
Pierre-Yves Chibon 2a0134
    def __repr__(self):
Pierre-Yves Chibon 2a0134
        return 'Issue(%s, project:%s, user:%s, title:%s)' % (
Pierre-Yves Chibon 2a0134
            self.id, self.project.name, self.user.user, self.title
Pierre-Yves Chibon 2a0134
        )
Pierre-Yves Chibon 2a0134
Pierre-Yves Chibon 11c5e9
    @property
Pierre-Yves Chibon 11c5e9
    def mail_id(self):
Pierre-Yves Chibon 11c5e9
        ''' Return a unique reprensetation of the issue as string that
Pierre-Yves Chibon 11c5e9
        can be used when sending emails.
Pierre-Yves Chibon 11c5e9
        '''
Pierre-Yves Chibon 11c5e9
        return '%s-ticket-%s@progit' % (self.project.name, self.id)
Pierre-Yves Chibon 11c5e9
Pierre-Yves Chibon 7379e4
    def to_json(self):
Pierre-Yves Chibon 7379e4
        ''' Returns a JSON representation of the issue using the JSON module
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 7379e4
        '''
Pierre-Yves Chibon 7379e4
        output = {
Pierre-Yves Chibon 09a0c1
            'title': self.title,
Pierre-Yves Chibon 09a0c1
            'content': self.content,
Pierre-Yves Chibon 09a0c1
            'status': self.status,
Pierre-Yves Chibon 09a0c1
            'date_created': self.date_created.strftime('%s'),
Pierre-Yves Chibon 7379e4
            'user': {
Pierre-Yves Chibon 09a0c1
                'name': self.user.user,
Pierre-Yves Chibon 09a0c1
                'emails': [email.email for email in self.user.emails],
Pierre-Yves Chibon 7379e4
            }
Pierre-Yves Chibon 7379e4
        }
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 7379e4
        comments = []
Pierre-Yves Chibon 09a0c1
        for comment in self.comments:
Pierre-Yves Chibon 7379e4
            cmt = {
Pierre-Yves Chibon 7379e4
                'id': comment.id,
Pierre-Yves Chibon 7379e4
                'comment': comment.comment,
Pierre-Yves Chibon 7379e4
                'parent': comment.parent_id,
Pierre-Yves Chibon 7379e4
                'date_created': comment.date_created.strftime('%s'),
Pierre-Yves Chibon 7379e4
                'user': {
Pierre-Yves Chibon 7379e4
                    'name': comment.user.user,
Pierre-Yves Chibon 7379e4
                    'emails': [email.email for email in comment.user.emails],
Pierre-Yves Chibon 7379e4
                }
Pierre-Yves Chibon 7379e4
            }
Pierre-Yves Chibon 7379e4
            comments.append(cmt)
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 7379e4
        output['comments'] = comments
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 7379e4
        return json.dumps(output)
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 7b8c4b
Pierre-Yves Chibon a1d4cc
class IssueToIssue(BASE):
Pierre-Yves Chibon a1d4cc
    """ Stores the parent/child relationship between two issues.
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon a1d4cc
    Table -- issue_to_issue
Pierre-Yves Chibon a1d4cc
    """
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon a1d4cc
    __tablename__ = 'issue_to_issue'
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon a1d4cc
    parent_issue_id = sa.Column(
Pierre-Yves Chibon 763cad
        sa.Text,
Pierre-Yves Chibon a1d4cc
        sa.ForeignKey(
Pierre-Yves Chibon 763cad
            'issues.uid', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon a1d4cc
        primary_key=True)
Pierre-Yves Chibon a1d4cc
    child_issue_id = sa.Column(
Pierre-Yves Chibon 763cad
        sa.Text,
Pierre-Yves Chibon a1d4cc
        sa.ForeignKey(
Pierre-Yves Chibon 763cad
            'issues.uid', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon a1d4cc
        primary_key=True)
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon bb9f71
class IssueComment(BASE):
Pierre-Yves Chibon bb9f71
    """ Stores the comments made on a commit/file.
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon bb9f71
    Table -- issue_comments
Pierre-Yves Chibon bb9f71
    """
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon bb9f71
    __tablename__ = 'issue_comments'
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon bb9f71
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 1b1a9f
    issue_uid = sa.Column(
Pierre-Yves Chibon 1b1a9f
        sa.Text,
Pierre-Yves Chibon bb9f71
        sa.ForeignKey(
Pierre-Yves Chibon 1b1a9f
            'issues.uid', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon bb9f71
        index=True)
Pierre-Yves Chibon bb9f71
    comment = sa.Column(
Pierre-Yves Chibon bb9f71
        sa.Text(),
Pierre-Yves Chibon bb9f71
        nullable=False)
Pierre-Yves Chibon bb9f71
    parent_id = sa.Column(
Pierre-Yves Chibon bb9f71
        sa.Integer,
Pierre-Yves Chibon 36edcd
        sa.ForeignKey('issue_comments.id', onupdate='CASCADE'),
Pierre-Yves Chibon bb9f71
        nullable=True)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon acc99e
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon acc99e
        index=True)
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon bb9f71
    date_created = sa.Column(sa.DateTime, nullable=False,
Pierre-Yves Chibon bb9f71
                             default=datetime.datetime.utcnow)
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon bb9f71
    issue = relation(
Pierre-Yves Chibon 1b1a9f
        'Issue', foreign_keys=[issue_uid], remote_side=[Issue.uid],
Pierre-Yves Chibon e28f89
        backref=backref('comments', order_by="IssueComment.date_created")
Pierre-Yves Chibon e28f89
    )
Pierre-Yves Chibon acc99e
    user = relation('User', foreign_keys=[user_id],
Pierre-Yves Chibon acc99e
                    remote_side=[User.id], backref='comment_issues')
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon acf64d
class Tag(BASE):
Pierre-Yves Chibon acf64d
    """ Stores the tags.
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon acf64d
    Table -- tags
Pierre-Yves Chibon acf64d
    """
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon acf64d
    __tablename__ = 'tags'
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon acf64d
    tag = sa.Column(sa.Text(), primary_key=True)
Pierre-Yves Chibon acf64d
    date_created = sa.Column(sa.DateTime, nullable=False,
Pierre-Yves Chibon acf64d
                             default=datetime.datetime.utcnow)
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon acf64d
class TagIssue(BASE):
Pierre-Yves Chibon acf64d
    """ Stores the tag associated with an issue.
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon acf64d
    Table -- tags_issues
Pierre-Yves Chibon acf64d
    """
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon acf64d
    __tablename__ = 'tags_issues'
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon 2acb3a
    tag = sa.Column(
Pierre-Yves Chibon 2acb3a
        sa.Text(),
Pierre-Yves Chibon 2acb3a
        sa.ForeignKey(
Pierre-Yves Chibon 2acb3a
            'tags.tag', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon 2acb3a
        primary_key=True)
Pierre-Yves Chibon 56c81e
    issue_uid = sa.Column(
Pierre-Yves Chibon 56c81e
        sa.Text,
Pierre-Yves Chibon d28641
        sa.ForeignKey(
Pierre-Yves Chibon 56c81e
            'issues.uid', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon d28641
        primary_key=True)
Pierre-Yves Chibon acf64d
    date_created = sa.Column(sa.DateTime, nullable=False,
Pierre-Yves Chibon acf64d
                             default=datetime.datetime.utcnow)
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon 82116a
    issue = relation(
Pierre-Yves Chibon 56c81e
        'Issue', foreign_keys=[issue_uid], remote_side=[Issue.uid],
Pierre-Yves Chibon 1b4126
        backref=backref(
Pierre-Yves Chibon 1b4126
            'tags', cascade="delete, delete-orphan", single_parent=True)
Pierre-Yves Chibon 1b4126
        )
Pierre-Yves Chibon 82116a
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon 7b8c4b
class PullRequest(BASE):
Pierre-Yves Chibon 7b8c4b
    """ Stores the pull requests created on a project.
Pierre-Yves Chibon 7b8c4b
Pierre-Yves Chibon 7b8c4b
    Table -- pull_requests
Pierre-Yves Chibon 7b8c4b
    """
Pierre-Yves Chibon 7b8c4b
Pierre-Yves Chibon 7b8c4b
    __tablename__ = 'pull_requests'
Pierre-Yves Chibon 7b8c4b
Pierre-Yves Chibon 7b8c4b
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 366781
    uid = sa.Column(sa.String(32), unique=True, nullable=False)
Pierre-Yves Chibon 8d350d
    title = sa.Column(
Pierre-Yves Chibon 8d350d
        sa.Text,
Pierre-Yves Chibon 8d350d
        nullable=False)
Pierre-Yves Chibon 7b8c4b
    project_id = sa.Column(
Pierre-Yves Chibon 7b8c4b
        sa.Integer,
Pierre-Yves Chibon 7b8c4b
        sa.ForeignKey(
Pierre-Yves Chibon 7b8c4b
            'projects.id', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon ea7c34
        primary_key=True)
Pierre-Yves Chibon 8d350d
    branch = sa.Column(
Pierre-Yves Chibon 8d350d
        sa.Text(),
Pierre-Yves Chibon 8d350d
        nullable=False)
Pierre-Yves Chibon 85ebac
    project_id_from = sa.Column(
Pierre-Yves Chibon 85ebac
        sa.Integer,
Pierre-Yves Chibon 85ebac
        sa.ForeignKey(
Pierre-Yves Chibon 85ebac
            'projects.id', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon 85ebac
        nullable=False)
Pierre-Yves Chibon 8d350d
    branch_from = sa.Column(
Pierre-Yves Chibon 9e21cc
        sa.Text(),
Pierre-Yves Chibon 9e21cc
        nullable=False)
Pierre-Yves Chibon 8d350d
    commit_start = sa.Column(
Pierre-Yves Chibon 8d350d
        sa.Text(),
Pierre-Yves Chibon 04a4f4
        nullable=True)
Pierre-Yves Chibon 8d350d
    commit_stop = sa.Column(
Pierre-Yves Chibon 8d350d
        sa.Text(),
Pierre-Yves Chibon 665ab7
        nullable=True)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon acc99e
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon acc99e
        index=True)
Pierre-Yves Chibon 7f6e93
    status = sa.Column(sa.Boolean, nullable=False, default=True)
Pierre-Yves Chibon 7b8c4b
Pierre-Yves Chibon 7b8c4b
    date_created = sa.Column(sa.DateTime, nullable=False,
Pierre-Yves Chibon 7b8c4b
                             default=datetime.datetime.utcnow)
Pierre-Yves Chibon aa3b6b
Pierre-Yves Chibon aa3b6b
    repo = relation(
Pierre-Yves Chibon 58e9f5
        'Project', foreign_keys=[project_id], remote_side=[Project.id],
Pierre-Yves Chibon 81b45a
        backref='requests', cascade="delete, delete-orphan",
Pierre-Yves Chibon 81b45a
        single_parent=True)
Pierre-Yves Chibon aa3b6b
    repo_from = relation(
Pierre-Yves Chibon aa3b6b
        'Project', foreign_keys=[project_id_from], remote_side=[Project.id])
Pierre-Yves Chibon acc99e
    user = relation('User', foreign_keys=[user_id],
Pierre-Yves Chibon acc99e
                    remote_side=[User.id], backref='pull_requests')
Pierre-Yves Chibon 2a35bc
Pierre-Yves Chibon 2a35bc
    def __repr__(self):
Pierre-Yves Chibon 2a35bc
        return 'PullRequest(%s, project:%s, user:%s, title:%s)' % (
Pierre-Yves Chibon 2a35bc
            self.id, self.repo.name, self.user.user, self.title
Pierre-Yves Chibon 2a35bc
        )
Pierre-Yves Chibon 4e1ed8
Pierre-Yves Chibon ff841c
    @property
Pierre-Yves Chibon ff841c
    def mail_id(self):
Pierre-Yves Chibon ff841c
        ''' Return a unique reprensetation of the issue as string that
Pierre-Yves Chibon ff841c
        can be used when sending emails.
Pierre-Yves Chibon ff841c
        '''
Pierre-Yves Chibon ff841c
        return '%s-pull-request-%s@progit' % (self.repo.name, self.id)
Pierre-Yves Chibon ff841c
Pierre-Yves Chibon 4e1ed8
Pierre-Yves Chibon 26f048
class PullRequestComment(BASE):
Pierre-Yves Chibon 26f048
    """ Stores the comments made on a pull-request.
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 26f048
    Table -- pull_request_comments
Pierre-Yves Chibon 26f048
    """
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 26f048
    __tablename__ = 'pull_request_comments'
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 26f048
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 986eb3
    pull_request_uid = sa.Column(
Pierre-Yves Chibon 986eb3
        sa.Text,
Pierre-Yves Chibon 26f048
        sa.ForeignKey(
Pierre-Yves Chibon 986eb3
            'pull_requests.uid', ondelete='CASCADE', onupdate='CASCADE'),
Pierre-Yves Chibon 26f048
        nullable=False)
Pierre-Yves Chibon 26f048
    commit_id = sa.Column(
Pierre-Yves Chibon 26f048
        sa.String(40),
Pierre-Yves Chibon 26f048
        nullable=False,
Pierre-Yves Chibon 26f048
        index=True)
Pierre-Yves Chibon 26f048
    user_id = sa.Column(
Pierre-Yves Chibon 26f048
        sa.Integer,
Pierre-Yves Chibon 26f048
        sa.ForeignKey('users.id', onupdate='CASCADE'),
Pierre-Yves Chibon 26f048
        nullable=False,
Pierre-Yves Chibon 26f048
        index=True)
Pierre-Yves Chibon 5bffad
    filename = sa.Column(
Pierre-Yves Chibon 5bffad
        sa.Text,
Pierre-Yves Chibon 5bffad
        nullable=True)
Pierre-Yves Chibon 26f048
    line = sa.Column(
Pierre-Yves Chibon 26f048
        sa.Integer,
Pierre-Yves Chibon 26f048
        nullable=True)
Pierre-Yves Chibon 26f048
    comment = sa.Column(
Pierre-Yves Chibon 26f048
        sa.Text(),
Pierre-Yves Chibon 26f048
        nullable=False)
Pierre-Yves Chibon 26f048
    parent_id = sa.Column(
Pierre-Yves Chibon 26f048
        sa.Integer,
Pierre-Yves Chibon 26f048
        sa.ForeignKey('pull_request_comments.id', onupdate='CASCADE'),
Pierre-Yves Chibon 26f048
        nullable=True)
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 26f048
    date_created = sa.Column(sa.DateTime, nullable=False,
Pierre-Yves Chibon 26f048
                             default=datetime.datetime.utcnow)
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 26f048
    user = relation('User', foreign_keys=[user_id],
Pierre-Yves Chibon df1ed0
                    remote_side=[User.id],
Pierre-Yves Chibon df1ed0
                    backref=backref(
Pierre-Yves Chibon df1ed0
                        'pull_request_comments',
Pierre-Yves Chibon df1ed0
                        order_by="PullRequestComment.date_created")
Pierre-Yves Chibon df1ed0
                    )
Pierre-Yves Chibon 26f048
    pull_request = relation(
Pierre-Yves Chibon 986eb3
        'PullRequest', backref='comments',
Pierre-Yves Chibon 986eb3
        foreign_keys=[pull_request_uid],
Pierre-Yves Chibon 986eb3
        remote_side=[PullRequest.uid])
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 2b1b47
# ##########################################################
Pierre-Yves Chibon 2b1b47
# These classes are only used if you're using the `local`
Pierre-Yves Chibon 2b1b47
#                  authentication method
Pierre-Yves Chibon 2b1b47
# ##########################################################
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
class ProgitUserVisit(BASE):
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    __tablename__ = 'progit_user_visit'
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 2b1b47
    user_id = sa.Column(
Pierre-Yves Chibon 2b1b47
        sa.Integer, sa.ForeignKey('users.id'), nullable=False)
Pierre-Yves Chibon 2b1b47
    visit_key = sa.Column(
Pierre-Yves Chibon 2b1b47
        sa.String(40), nullable=False, unique=True, index=True)
Pierre-Yves Chibon 2b1b47
    user_ip = sa.Column(sa.String(50), nullable=False)
Pierre-Yves Chibon 2b1b47
    created = sa.Column(
Pierre-Yves Chibon 2b1b47
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow)
Pierre-Yves Chibon 2b1b47
    expiry = sa.Column(sa.DateTime)
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
class ProgitGroup(BASE):
Pierre-Yves Chibon 2b1b47
    """
Pierre-Yves Chibon 2b1b47
    An ultra-simple group definition.
Pierre-Yves Chibon 2b1b47
    """
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    # names like "Group", "Order" and "User" are reserved words in SQL
Pierre-Yves Chibon 2b1b47
    # so we set the name to something safe for SQL
Pierre-Yves Chibon 2b1b47
    __tablename__ = 'progit_group'
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 2b1b47
    group_name = sa.Column(sa.String(16), nullable=False, unique=True)
Pierre-Yves Chibon 2b1b47
    created = sa.Column(
Pierre-Yves Chibon 2b1b47
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow)
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    def __repr__(self):
Pierre-Yves Chibon 2b1b47
        ''' Return a string representation of this object. '''
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
        return 'Group: %s - name %s' % (self.id, self.group_name)
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
class ProgitUserGroup(BASE):
Pierre-Yves Chibon 2b1b47
    """
Pierre-Yves Chibon 2b1b47
    Association table linking the mm_user table to the mm_group table.
Pierre-Yves Chibon 2b1b47
    This allow linking users to groups.
Pierre-Yves Chibon 2b1b47
    """
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    __tablename__ = 'progit_user_group'
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    user_id = sa.Column(
Pierre-Yves Chibon 2b1b47
        sa.Integer, sa.ForeignKey('users.id'), primary_key=True)
Pierre-Yves Chibon 2b1b47
    group_id = sa.Column(
Pierre-Yves Chibon 2b1b47
        sa.Integer, sa.ForeignKey('progit_group.id'), primary_key=True)
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    # Constraints
Pierre-Yves Chibon 2b1b47
    __table_args__ = (
Pierre-Yves Chibon 2b1b47
        sa.UniqueConstraint(
Pierre-Yves Chibon 2b1b47
            'user_id', 'group_id'),
Pierre-Yves Chibon 2b1b47
    )