Blame pagure/lib/model.py

Pierre-Yves Chibon 33b534
# -*- coding: utf-8 -*-
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
"""
Pierre-Yves Chibon 4b8077
 (c) 2014-2018 - 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 67d1cc
from __future__ import unicode_literals, absolute_import
Aurélien Bompard 831553
Adam Williamson c74215
import arrow
Pierre-Yves Chibon ae4136
import datetime
Pierre-Yves Chibon 7d9e04
import collections
Pierre-Yves Chibon ae4136
import logging
Pierre-Yves Chibon 7379e4
import json
Lubomír Sedlář f8b879
import operator
Mark Reynolds 784dde
import re
Patrick Uiterwijk 3f97f6
import pygit2
Patrick Uiterwijk 3f97f6
import os
Pierre-Yves Chibon ae4136
Aurélien Bompard 619e2a
import six
Pierre-Yves Chibon ae4136
import sqlalchemy as sa
Pierre-Yves Chibon 5d25e7
Patrick Uiterwijk 4012dc
from sqlalchemy import create_engine
Pierre-Yves Chibon caf4e8
from sqlalchemy.exc import SQLAlchemyError
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
Patrick Uiterwijk 9b237b
from sqlalchemy.orm import validates
Pierre-Yves Chibon ae4136
Matt Prahl cbadc1
import pagure.exceptions
Patrick Uiterwijk 3f97f6
from pagure.config import config as pagure_config
Patrick Uiterwijk 4012dc
from pagure.lib.model_base import BASE
Patrick Uiterwijk 4012dc
from pagure.lib.plugins import get_plugin_tables
Aurélien Bompard 619e2a
from pagure.utils import is_true
Matt Prahl cbadc1
Pierre-Yves Chibon e3755f
Pierre-Yves Chibon 4635b5
_log = logging.getLogger(__name__)
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon 22a554
# hit w/ all the id field we use
Pierre-Yves Chibon 22a554
# pylint: disable=invalid-name
Pierre-Yves Chibon 22a554
# pylint: disable=too-few-public-methods
Pierre-Yves Chibon 22a554
# pylint: disable=no-init
Pierre-Yves Chibon 22a554
# pylint: disable=too-many-lines
Pierre-Yves Chibon b8c7ae
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon 1f6bd5
def create_tables(db_url, alembic_ini=None, acls=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.
Abhijeet Kasurde a6221b
    :kwarg debug, a boolean specifying whether 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 9c2953
    if db_url.startswith("postgres"):  # pragma: no cover
Pierre-Yves Chibon 9c2953
        engine = create_engine(db_url, echo=debug, client_encoding="utf8")
Pierre-Yves Chibon 223c88
    else:  # pragma: no cover
Pierre-Yves Chibon a68ee8
        engine = create_engine(db_url, echo=debug)
Ryan Lerch cd0b04
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 9c2953
    if db_url.startswith("sqlite:"):
Pierre-Yves Chibon faa36d
        # Ignore the warning about con_record
Pierre-Yves Chibon 22a554
        # pylint: disable=unused-argument
Pierre-Yves Chibon 0074a5
        def _fk_pragma_on_connect(dbapi_con, _):  # pragma: no cover
Pierre-Yves Chibon 9c2953
            """ Tries to enforce referential constraints on sqlite. """
Pierre-Yves Chibon 9c2953
            dbapi_con.execute("pragma foreign_keys=ON")
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
        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 22a554
        # pylint: disable=import-error
Pierre-Yves Chibon ae4136
        from alembic.config import Config
Pierre-Yves Chibon ae4136
        from alembic import command
Pierre-Yves Chibon 9c2953
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 980f59
    BASE.metadata.bind = scopedsession
Pierre-Yves Chibon caf4e8
    # Insert the default data into the db
Pierre-Yves Chibon 1f6bd5
    create_default_status(scopedsession, acls=acls)
Pierre-Yves Chibon ae4136
    return scopedsession
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon 1f6bd5
def create_default_status(session, acls=None):
Pierre-Yves Chibon caf4e8
    """ Insert the defaults status in the status tables.
Pierre-Yves Chibon caf4e8
    """
Pierre-Yves Chibon caf4e8
Pierre-Yves Chibon 9c2953
    statuses = ["Open", "Closed"]
Pierre-Yves Chibon f610bf
    for status in statuses:
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 ad9df5
            session.commit()
Pierre-Yves Chibon b8c7ae
        except SQLAlchemyError:  # pragma: no cover
Pierre-Yves Chibon ad9df5
            session.rollback()
Pierre-Yves Chibon 9c2953
            _log.debug("Status %s could not be added", ticket_stat)
Pierre-Yves Chibon caf4e8
Pierre-Yves Chibon 9c2953
    for status in ["Open", "Closed", "Merged"]:
Pierre-Yves Chibon 28e510
        pr_stat = StatusPullRequest(status=status)
Pierre-Yves Chibon 28e510
        session.add(pr_stat)
Pierre-Yves Chibon 28e510
        try:
Pierre-Yves Chibon 28e510
            session.commit()
Pierre-Yves Chibon 28e510
        except SQLAlchemyError:  # pragma: no cover
Pierre-Yves Chibon 28e510
            session.rollback()
Pierre-Yves Chibon 9c2953
            _log.debug("Status %s could not be added", pr_stat)
Pierre-Yves Chibon 28e510
Pierre-Yves Chibon 9c2953
    for grptype in ["user", "admin"]:
Pierre-Yves Chibon c2a75b
        grp_type = PagureGroupType(group_type=grptype)
Pierre-Yves Chibon ad9df5
        session.add(grp_type)
Pierre-Yves Chibon ad9df5
        try:
Pierre-Yves Chibon ad9df5
            session.commit()
Pierre-Yves Chibon ad9df5
        except SQLAlchemyError:  # pragma: no cover
Pierre-Yves Chibon ad9df5
            session.rollback()
Pierre-Yves Chibon 9c2953
            _log.debug("Type %s could not be added", grptype)
Pierre-Yves Chibon caf4e8
Aurélien Bompard 831553
    acls = acls or {}
Aurélien Bompard 831553
    keys = sorted(list(acls.keys()))
Aurélien Bompard 831553
    for acl in keys:
Pierre-Yves Chibon 9c2953
        item = ACL(name=acl, description=acls[acl])
Pierre-Yves Chibon 1f6bd5
        session.add(item)
Pierre-Yves Chibon 1f6bd5
        try:
Pierre-Yves Chibon 1f6bd5
            session.commit()
Pierre-Yves Chibon 1f6bd5
        except SQLAlchemyError:  # pragma: no cover
Pierre-Yves Chibon 1f6bd5
            session.rollback()
Pierre-Yves Chibon 9c2953
            _log.debug("ACL %s could not be added", acl)
Pierre-Yves Chibon 1f6bd5
Pierre-Yves Chibon 9c2953
    for access in ["ticket", "commit", "admin"]:
Vivek Anand 967335
        access_obj = AccessLevels(access=access)
Vivek Anand 967335
        session.add(access_obj)
Vivek Anand 967335
        try:
Vivek Anand 967335
            session.commit()
Vivek Anand 967335
        except SQLAlchemyError:
Vivek Anand 967335
            session.rollback()
Pierre-Yves Chibon 9c2953
            _log.debug("Access level %s could not be added", access)
Vivek Anand 967335
Vivek Anand 967335
Aurélien Bompard 619e2a
def arrow_ts(value):
Aurélien Bompard 619e2a
    return "%s" % arrow.get(value).timestamp
Aurélien Bompard 619e2a
Aurélien Bompard 619e2a
Vivek Anand 967335
class AccessLevels(BASE):
Pierre-Yves Chibon 9c2953
    """ Different access levels a user/group can have for a project """
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    __tablename__ = "access_levels"
Vivek Anand 967335
Vivek Anand 967335
    access = sa.Column(sa.String(255), primary_key=True)
Vivek Anand 967335
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 9c2953
Pierre-Yves Chibon 9c2953
    __tablename__ = "status_issue"
Pierre-Yves Chibon cc324e
Pierre-Yves Chibon cc324e
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 5fd1da
    status = sa.Column(sa.String(255), nullable=False, unique=True)
Pierre-Yves Chibon cc324e
Pierre-Yves Chibon cc324e
Pierre-Yves Chibon 28e510
class StatusPullRequest(BASE):
Pierre-Yves Chibon 28e510
    """ Stores the status a pull-request can have.
Pierre-Yves Chibon 28e510
Pierre-Yves Chibon 28e510
    Table -- status_issue
Pierre-Yves Chibon 28e510
    """
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    __tablename__ = "status_pull_requests"
Pierre-Yves Chibon 28e510
Pierre-Yves Chibon 28e510
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 5fd1da
    status = sa.Column(sa.String(255), nullable=False, unique=True)
Pierre-Yves Chibon 28e510
Pierre-Yves Chibon 28e510
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 9c2953
    __tablename__ = "users"
Pierre-Yves Chibon acc99e
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 5fd1da
    user = sa.Column(sa.String(255), nullable=False, unique=True, index=True)
Pierre-Yves Chibon 5fd1da
    fullname = sa.Column(sa.String(255), nullable=False, index=True)
Pierre-Yves Chibon cd311b
    default_email = sa.Column(sa.Text, nullable=False)
Pierre-Yves Chibon cbfdca
    _settings = 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 9c2953
    created = sa.Column(sa.DateTime, nullable=False, 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 9c2953
        onupdate=sa.func.now(),
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 13afd6
Patrick Uiterwijk ce6d6d
    refuse_sessions_before = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=True, default=None
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk ce6d6d
Pierre-Yves Chibon 13e142
    # Relations
Pierre-Yves Chibon 13e142
    group_objs = relation(
Pierre-Yves Chibon fe5017
        "PagureGroup",
Pierre-Yves Chibon fe5017
        secondary="pagure_user_group",
Pierre-Yves Chibon fe5017
        primaryjoin="users.c.id==pagure_user_group.c.user_id",
Pierre-Yves Chibon fe5017
        secondaryjoin="pagure_group.c.id==pagure_user_group.c.group_id",
Pierre-Yves Chibon 13e142
        backref="users",
Pierre-Yves Chibon 13e142
    )
Pierre-Yves Chibon fe5017
    session = relation("PagureUserVisit", backref="user")
Pierre-Yves Chibon 13e142
Pierre-Yves Chibon 13afd6
    @property
Pierre-Yves Chibon 13afd6
    def username(self):
Pierre-Yves Chibon 9c2953
        """ Return the username. """
Pierre-Yves Chibon 13afd6
        return self.user
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon 13afd6
    @property
Pierre-Yves Chibon da402f
    def html_title(self):
Pierre-Yves Chibon 9c2953
        """ Return the ``fullname (username)`` or simply ``username`` to be
Pierre-Yves Chibon da402f
        used in the html templates.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon da402f
        if self.fullname:
Pierre-Yves Chibon da402f
            return "%s (%s)" % (self.fullname, self.user)
Pierre-Yves Chibon da402f
        else:
Pierre-Yves Chibon da402f
            return self.user
Pierre-Yves Chibon da402f
Pierre-Yves Chibon da402f
    @property
Pierre-Yves Chibon 13afd6
    def groups(self):
Pierre-Yves Chibon 9c2953
        """ 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 cbfdca
    @property
Pierre-Yves Chibon cbfdca
    def settings(self):
Pierre-Yves Chibon cbfdca
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon cbfdca
        dict object.
Pierre-Yves Chibon cbfdca
        """
Pierre-Yves Chibon 9c2953
        default = {"cc_me_to_my_actions": False}
Pierre-Yves Chibon cbfdca
Pierre-Yves Chibon cbfdca
        if self._settings:
Pierre-Yves Chibon cbfdca
            current = json.loads(self._settings)
Pierre-Yves Chibon cbfdca
            # Update the current dict with the new keys
Pierre-Yves Chibon cbfdca
            for key in default:
Pierre-Yves Chibon cbfdca
                if key not in current:
Pierre-Yves Chibon cbfdca
                    current[key] = default[key]
Aurélien Bompard 619e2a
                elif is_true(current[key]):
Pierre-Yves Chibon cbfdca
                    current[key] = True
Pierre-Yves Chibon cbfdca
            return current
Pierre-Yves Chibon cbfdca
        else:
Pierre-Yves Chibon cbfdca
            return default
Pierre-Yves Chibon cbfdca
Pierre-Yves Chibon cbfdca
    @settings.setter
Pierre-Yves Chibon cbfdca
    def settings(self, settings):
Pierre-Yves Chibon 9c2953
        """ Ensures the settings are properly saved. """
Pierre-Yves Chibon cbfdca
        self._settings = json.dumps(settings)
Pierre-Yves Chibon cbfdca
Pierre-Yves Chibon 13afd6
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        """ Return a string representation of this object. """
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon 9c2953
        return "User: %s - name %s" % (self.id, self.user)
Pierre-Yves Chibon 13afd6
Pierre-Yves Chibon fd69cc
    def to_json(self, public=False):
Pierre-Yves Chibon 9c2953
        """ Return a representation of the User in a dictionary. """
Pierre-Yves Chibon 9c2953
        output = {"name": self.user, "fullname": self.fullname}
Pierre-Yves Chibon fd69cc
        if not public:
Pierre-Yves Chibon 9c2953
            output["default_email"] = self.default_email
Pierre-Yves Chibon 9c2953
            output["emails"] = sorted([email.email for email in self.emails])
Pierre-Yves Chibon fd69cc
Pierre-Yves Chibon ce5468
        return output
Pierre-Yves Chibon ce5468
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 9c2953
    __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 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 5fd1da
    email = sa.Column(sa.String(255), nullable=False, unique=True)
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon 6c18f5
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 6c18f5
        backref=backref(
Pierre-Yves Chibon 9c2953
            "emails", cascade="delete, delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 6c18f5
    )
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon acc99e
Pierre-Yves Chibon 77b6d4
class UserEmailPending(BASE):
Pierre-Yves Chibon 77b6d4
    """ Stores email information about the users.
Pierre-Yves Chibon 77b6d4
Pierre-Yves Chibon 77b6d4
    Table -- user_emails_pending
Pierre-Yves Chibon 77b6d4
    """
Pierre-Yves Chibon 77b6d4
Pierre-Yves Chibon 9c2953
    __tablename__ = "user_emails_pending"
Pierre-Yves Chibon 77b6d4
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 77b6d4
    user_id = sa.Column(
Pierre-Yves Chibon 77b6d4
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon 77b6d4
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 5fd1da
    email = sa.Column(sa.String(255), nullable=False, unique=True)
Pierre-Yves Chibon 77b6d4
    token = sa.Column(sa.String(50), nullable=True)
Pierre-Yves Chibon 9c2953
    created = sa.Column(sa.DateTime, nullable=False, default=sa.func.now())
Pierre-Yves Chibon 77b6d4
Pierre-Yves Chibon 77b6d4
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 77b6d4
        backref=backref(
Pierre-Yves Chibon 9c2953
            "emails_pending",
Pierre-Yves Chibon 77b6d4
            cascade="delete, delete-orphan",
Pierre-Yves Chibon 9c2953
            single_parent=True,
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 77b6d4
    )
Pierre-Yves Chibon 77b6d4
Pierre-Yves Chibon 77b6d4
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 9c2953
    __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 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon d3b817
    namespace = sa.Column(sa.String(255), nullable=True, index=True)
Richard Marko 45a244
    name = sa.Column(sa.String(255), nullable=False, index=True)
Pierre-Yves Chibon d688cf
    description = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 55e94b
    url = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 75f31c
    _settings = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon bcfb59
    # The hook_token is used to sign the notification sent via web-hook
Pierre-Yves Chibon d4e2f2
    hook_token = sa.Column(sa.String(40), nullable=False, unique=True)
Pierre-Yves Chibon 132383
    avatar_email = sa.Column(sa.Text, nullable=True)
farhaanbukhsh 20a2e1
    is_fork = sa.Column(sa.Boolean, default=False, nullable=False)
Vivek Anand ed7e6e
    read_only = sa.Column(sa.Boolean, default=True, nullable=False)
Pierre-Yves Chibon ae4136
    parent_id = sa.Column(
Pierre-Yves Chibon ae4136
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon e8b9d0
    _priorities = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon dff667
    default_priority = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 4557f8
    _milestones = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon f7a5ec
    _milestones_keys = sa.Column(sa.Text, nullable=True)
Lubomír Sedlář fc06fa
    _quick_replies = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 2fee4f
    _reports = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon c2e7b0
    _notifications = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 73001b
    _close_status = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 2de085
    _block_users = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 227f4c
    mirrored_from = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 227f4c
    mirrored_from_last_log = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon ae4136
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    date_modified = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Neha Kandpal 55bf77
    parent = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Neha Kandpal 55bf77
        remote_side=[id],
Neha Kandpal 55bf77
        backref=backref(
Pierre-Yves Chibon 9c2953
            "forks", order_by=str("(projects.c.date_created).desc()")
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref="projects",
Neha Kandpal 55bf77
    )
farhaanbukhsh 1ff188
    private = sa.Column(sa.Boolean, nullable=False, default=False)
Patrick Uiterwijk 3f97f6
    repospanner_region = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 334ada
Pierre-Yves Chibon faa36d
    users = relation(
Pierre-Yves Chibon 9c2953
        "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 9c2953
        backref="co_projects",
Pierre-Yves Chibon faa36d
    )
Pierre-Yves Chibon 9ac467
Vivek Anand 967335
    admins = relation(
Pierre-Yves Chibon 9c2953
        "User",
Vivek Anand 967335
        secondary="user_projects",
Vivek Anand 967335
        primaryjoin="projects.c.id==user_projects.c.project_id",
Vivek Anand 967335
        secondaryjoin="and_(users.c.id==user_projects.c.user_id,\
Vivek Anand 967335
                user_projects.c.access=='admin')",
Pierre-Yves Chibon 9c2953
        backref="co_projects_admins",
Pierre-Yves Chibon 9c2953
        viewonly=True,
Vivek Anand 967335
    )
Vivek Anand 967335
Vivek Anand 967335
    committers = relation(
Pierre-Yves Chibon 9c2953
        "User",
Vivek Anand 967335
        secondary="user_projects",
Vivek Anand 967335
        primaryjoin="projects.c.id==user_projects.c.project_id",
Vivek Anand 967335
        secondaryjoin="and_(users.c.id==user_projects.c.user_id,\
Vivek Anand 967335
                or_(user_projects.c.access=='commit',\
Vivek Anand 967335
                    user_projects.c.access=='admin'))",
Pierre-Yves Chibon 9c2953
        backref="co_projects_committers",
Pierre-Yves Chibon 9c2953
        viewonly=True,
Vivek Anand 967335
    )
Vivek Anand 967335
Pierre-Yves Chibon faaaa3
    groups = relation(
Pierre-Yves Chibon faaaa3
        "PagureGroup",
Pierre-Yves Chibon faaaa3
        secondary="projects_groups",
Pierre-Yves Chibon faaaa3
        primaryjoin="projects.c.id==projects_groups.c.project_id",
Pierre-Yves Chibon faaaa3
        secondaryjoin="pagure_group.c.id==projects_groups.c.group_id",
Pierre-Yves Chibon 830728
        backref=backref(
Pierre-Yves Chibon 830728
            "projects",
Pierre-Yves Chibon 9c2953
            order_by=str(
Pierre-Yves Chibon 9c2953
                "func.lower(projects.c.namespace).desc(), "
Pierre-Yves Chibon 9c2953
                "func.lower(projects.c.name)"
Pierre-Yves Chibon 9c2953
            ),
Pierre-Yves Chibon b4a226
        ),
Pierre-Yves Chibon 4fa20c
        order_by="PagureGroup.group_name.asc()",
Pierre-Yves Chibon faaaa3
    )
Pierre-Yves Chibon faaaa3
Vivek Anand 967335
    admin_groups = relation(
Vivek Anand 967335
        "PagureGroup",
Vivek Anand 967335
        secondary="projects_groups",
Vivek Anand 967335
        primaryjoin="projects.c.id==projects_groups.c.project_id",
Vivek Anand 967335
        secondaryjoin="and_(pagure_group.c.id==projects_groups.c.group_id,\
Vivek Anand 967335
                projects_groups.c.access=='admin')",
Vivek Anand 967335
        backref="projects_admin_groups",
Pierre-Yves Chibon 4fa20c
        order_by="PagureGroup.group_name.asc()",
Pierre-Yves Chibon 9c2953
        viewonly=True,
Vivek Anand 967335
    )
Vivek Anand 967335
Vivek Anand 967335
    committer_groups = relation(
Vivek Anand 967335
        "PagureGroup",
Vivek Anand 967335
        secondary="projects_groups",
Vivek Anand 967335
        primaryjoin="projects.c.id==projects_groups.c.project_id",
Vivek Anand 967335
        secondaryjoin="and_(pagure_group.c.id==projects_groups.c.group_id,\
Vivek Anand 967335
                or_(projects_groups.c.access=='admin',\
Vivek Anand 967335
                    projects_groups.c.access=='commit'))",
Vivek Anand 967335
        backref="projects_committer_groups",
Pierre-Yves Chibon 4fa20c
        order_by="PagureGroup.group_name.asc()",
Pierre-Yves Chibon 9c2953
        viewonly=True,
Vivek Anand 967335
    )
Vivek Anand 967335
Lenka Segura 98b37a
    def __repr__(self):
Pierre-Yves Chibon 0c2351
        return (
Pierre-Yves Chibon cc7cf3
            "Project(%s, name:%s, namespace:%s, url:%s, is_fork:%s, "
Pierre-Yves Chibon cc7cf3
            "parent_id:%s)"
Pierre-Yves Chibon 0c2351
            % (
Pierre-Yves Chibon 0c2351
                self.id,
Pierre-Yves Chibon 0c2351
                self.name,
Pierre-Yves Chibon 0c2351
                self.namespace,
Pierre-Yves Chibon 0c2351
                self.url,
Pierre-Yves Chibon 0c2351
                self.is_fork,
Pierre-Yves Chibon 0c2351
                self.parent_id,
Pierre-Yves Chibon 0c2351
            )
Lenka Segura 98b37a
        )
Lenka Segura 98b37a
Mathieu Bridon b0b97e
    @property
Pierre-Yves Chibon a0b640
    def isa(self):
Pierre-Yves Chibon 9c2953
        """ A string to allow finding out that this is a project. """
Pierre-Yves Chibon 9c2953
        return "project"
Pierre-Yves Chibon a0b640
Pierre-Yves Chibon a0b640
    @property
Pierre-Yves Chibon 327b88
    def mail_id(self):
Pierre-Yves Chibon 9c2953
        """ Return a unique representation of the project as string that
Pierre-Yves Chibon 327b88
        can be used when sending emails.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return "%s-project-%s" % (self.fullname, self.id)
Pierre-Yves Chibon 327b88
Pierre-Yves Chibon 327b88
    @property
Patrick Uiterwijk 3f97f6
    def is_on_repospanner(self):
Patrick Uiterwijk 3f97f6
        """ Returns whether this repo is on repoSpanner. """
Patrick Uiterwijk 3f97f6
        return self.repospanner_region is not None
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    @property
Mathieu Bridon b0b97e
    def path(self):
Pierre-Yves Chibon 9c2953
        """ Return the name of the git repo on the filesystem. """
Pierre-Yves Chibon 9c2953
        return "%s.git" % self.fullname
Pierre-Yves Chibon ae4136
Patrick Uiterwijk 3f97f6
    def repospanner_repo_info(self, repotype, region=None):
Patrick Uiterwijk 3f97f6
        """ Returns info for getting a repoSpanner repo for a project.
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        Args:
Patrick Uiterwijk 3f97f6
            repotype (string): Type of repository
Patrick Uiterwijk 3f97f6
            region (string): If repo is not on repoSpanner, return url as if
Patrick Uiterwijk 3f97f6
                it was in this region. Used for migrating to repoSpanner.
Patrick Uiterwijk 3f97f6
        Return type: (url, dict): First is the clone url, then a dict with
Patrick Uiterwijk 3f97f6
            the regioninfo.
Patrick Uiterwijk 3f97f6
        """
Patrick Uiterwijk 3f97f6
        if not self.is_on_repospanner and region is None:
Patrick Uiterwijk 3f97f6
            raise ValueError("Repo %s is not on repoSpanner" % self.fullname)
Patrick Uiterwijk 3f97f6
        if self.is_on_repospanner and region is not None:
Patrick Uiterwijk 3f97f6
            raise ValueError(
Patrick Uiterwijk 3f97f6
                "Repo %s is already on repoSpanner" % self.fullname
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk 3f97f6
        if region is None:
Patrick Uiterwijk 3f97f6
            region = self.repospanner_region
Patrick Uiterwijk 3f97f6
        regioninfo = pagure_config["REPOSPANNER_REGIONS"].get(region)
Patrick Uiterwijk 3f97f6
        if not regioninfo:
Patrick Uiterwijk 3f97f6
            raise ValueError(
Patrick Uiterwijk 3f97f6
                "Invalid repoSpanner region %s looked up" % region
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        url = "%s/repo/%s.git" % (
Patrick Uiterwijk 3f97f6
            regioninfo["url"],
Patrick Uiterwijk 3f97f6
            self._repospanner_repo_name(repotype, region),
Patrick Uiterwijk 3f97f6
        )
Patrick Uiterwijk 3f97f6
        return url, regioninfo
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 8174a4
    def _repospanner_repo_name(self, repotype, region=None):
Patrick Uiterwijk 3f97f6
        """ Returns the name of a repo as named in repoSpanner.
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        Args:
Patrick Uiterwijk 3f97f6
            repotype (string): Type of repository
Patrick Uiterwijk 3f97f6
            region (string): repoSpanner region name
Patrick Uiterwijk 3f97f6
        Return type: (string)
Patrick Uiterwijk 3f97f6
        """
Patrick Uiterwijk 8174a4
        if region is None:
Patrick Uiterwijk 8174a4
            region = self.repospanner_region
Patrick Uiterwijk 3f97f6
        return os.path.join(
Patrick Uiterwijk 3f97f6
            pagure_config["REPOSPANNER_REGIONS"][region].get(
Patrick Uiterwijk 3f97f6
                "repo_prefix", ""
Patrick Uiterwijk 3f97f6
            ),
Patrick Uiterwijk 3f97f6
            repotype,
Patrick Uiterwijk 3f97f6
            self.fullname,
Patrick Uiterwijk 3f97f6
        )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    def repopath(self, repotype):
Patrick Uiterwijk 3f97f6
        """ Return the full repository path of the git repo on the filesystem.
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        If the repository is on repoSpanner, this will be a pseudo repository,
Patrick Uiterwijk 3f97f6
        which is "git repo enough" to be considered a valid repo, but any
Patrick Uiterwijk 3f97f6
        access should go through a repoSpanner enlightened libgit2.
Patrick Uiterwijk 3f97f6
        """
Patrick Uiterwijk 3f97f6
        if self.is_on_repospanner:
Patrick Uiterwijk 3f97f6
            pseudopath = os.path.join(
Patrick Uiterwijk 3f97f6
                pagure_config["REPOSPANNER_PSEUDO_FOLDER"], repotype, self.path
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk 3f97f6
            if not os.path.exists(pseudopath):
Patrick Uiterwijk 3f97f6
                repourl, regioninfo = self.repospanner_repo_info(repotype)
Patrick Uiterwijk 3f97f6
                fake = pygit2.init_repository(pseudopath, bare=True)
Patrick Uiterwijk 3f97f6
                fake.config["repospanner.url"] = repourl
Patrick Uiterwijk 3f97f6
                fake.config["repospanner.cert"] = regioninfo["push_cert"][
Patrick Uiterwijk 3f97f6
                    "cert"
Patrick Uiterwijk 3f97f6
                ]
Patrick Uiterwijk 3f97f6
                fake.config["repospanner.key"] = regioninfo["push_cert"]["key"]
Patrick Uiterwijk 3f97f6
                fake.config["repospanner.cacert"] = regioninfo["ca"]
Patrick Uiterwijk 3f97f6
                fake.config["repospanner.enabled"] = True
Patrick Uiterwijk 3f97f6
                del fake
Patrick Uiterwijk 3f97f6
            return pseudopath
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        maindir = None
Patrick Uiterwijk 3f97f6
        if repotype == "main":
Patrick Uiterwijk 3f97f6
            maindir = pagure_config["GIT_FOLDER"]
Patrick Uiterwijk 3f97f6
        elif repotype == "docs":
Patrick Uiterwijk 3f97f6
            maindir = pagure_config["DOCS_FOLDER"]
Patrick Uiterwijk 3f97f6
        elif repotype == "tickets":
Patrick Uiterwijk 3f97f6
            maindir = pagure_config["TICKETS_FOLDER"]
Patrick Uiterwijk 3f97f6
        elif repotype == "requests":
Patrick Uiterwijk 3f97f6
            maindir = pagure_config["REQUESTS_FOLDER"]
Patrick Uiterwijk 3f97f6
        else:
Patrick Uiterwijk 3f97f6
            return ValueError("Repotype %s is invalid" % repotype)
Patrick Uiterwijk 3f97f6
        if maindir is None:
Patrick Uiterwijk 3f97f6
            if repotype == "main":
Patrick Uiterwijk 3f97f6
                raise Exception("No maindir for main repos?")
Patrick Uiterwijk 3f97f6
            return None
Patrick Uiterwijk 3f97f6
        return os.path.join(maindir, self.path)
Patrick Uiterwijk 3f97f6
Pierre-Yves Chibon b393e8
    @property
Pierre-Yves Chibon b393e8
    def fullname(self):
Pierre-Yves Chibon 9c2953
        """ 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 9c2953
        """
Pierre-Yves Chibon b393e8
        str_name = self.name
Pierre-Yves Chibon 166f3b
        if self.namespace:
Pierre-Yves Chibon 9c2953
            str_name = "%s/%s" % (self.namespace, str_name)
farhaanbukhsh 20a2e1
        if self.is_fork:
Pierre-Yves Chibon 4fcac8
            str_name = "forks/%s/%s" % (self.user.user, str_name)
Pierre-Yves Chibon b393e8
        return str_name
Pierre-Yves Chibon b393e8
Pierre-Yves Chibon 75f31c
    @property
Pierre-Yves Chibon 469e24
    def url_path(self):
Pierre-Yves Chibon 9c2953
        """ Return the path at which this project can be accessed in the
Pierre-Yves Chibon 469e24
        web UI.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 469e24
        path = self.name
Pierre-Yves Chibon 469e24
        if self.namespace:
Pierre-Yves Chibon 9c2953
            path = "%s/%s" % (self.namespace, path)
Pierre-Yves Chibon 469e24
        if self.is_fork:
Pierre-Yves Chibon 469e24
            path = "fork/%s/%s" % (self.user.user, path)
Pierre-Yves Chibon 469e24
        return path
Pierre-Yves Chibon 469e24
Pierre-Yves Chibon 469e24
    @property
Pierre-Yves Chibon 9e650b
    def tags_text(self):
Pierre-Yves Chibon 9c2953
        """ Return the list of tags in a simple text form. """
Pierre-Yves Chibon 9e650b
        return [tag.tag for tag in self.tags]
Pierre-Yves Chibon 9e650b
Pierre-Yves Chibon 9e650b
    @property
Pierre-Yves Chibon 75f31c
    def settings(self):
Pierre-Yves Chibon 75f31c
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon 75f31c
        dict object.
Pierre-Yves Chibon 75f31c
        """
Pierre-Yves Chibon fdb2df
        default = {
Pierre-Yves Chibon 9c2953
            "issue_tracker": True,
Pierre-Yves Chibon 9c2953
            "project_documentation": False,
Pierre-Yves Chibon 9c2953
            "pull_requests": True,
Pierre-Yves Chibon 9c2953
            "Only_assignee_can_merge_pull-request": False,
Pierre-Yves Chibon 9c2953
            "Minimum_score_to_merge_pull-request": -1,
Pierre-Yves Chibon 9c2953
            "Web-hooks": None,
Pierre-Yves Chibon 9c2953
            "Enforce_signed-off_commits_in_pull-request": False,
Pierre-Yves Chibon 9c2953
            "always_merge": False,
Pierre-Yves Chibon 9c2953
            "issues_default_to_private": False,
Pierre-Yves Chibon 9c2953
            "fedmsg_notifications": True,
Pierre-Yves Chibon 9c2953
            "stomp_notifications": True,
jingjing a949ff
            "mqtt_notifications": True,
Pierre-Yves Chibon 9c2953
            "pull_request_access_only": False,
Pierre-Yves Chibon 9c2953
            "notify_on_pull-request_flag": False,
Pierre-Yves Chibon 9c2953
            "notify_on_commit_flag": False,
Pierre-Yves Chibon 9c2953
            "issue_tracker_read_only": False,
Slavek Kabrda cdb053
            "disable_non_fast-forward_merges": False,
Pierre-Yves Chibon 3821e6
            "open_metadata_access_to_all": False,
Pierre-Yves Chibon fdb2df
        }
Pierre-Yves Chibon fdb2df
Pierre-Yves Chibon 75f31c
        if self._settings:
Pierre-Yves Chibon fdb2df
            current = json.loads(self._settings)
Pierre-Yves Chibon fdb2df
            # Update the current dict with the new keys
Pierre-Yves Chibon fdb2df
            for key in default:
Pierre-Yves Chibon fdb2df
                if key not in current:
Pierre-Yves Chibon fdb2df
                    current[key] = default[key]
Pierre-Yves Chibon 9c2953
                elif key == "Minimum_score_to_merge_pull-request":
Ricky Elrod b50203
                    current[key] = int(current[key])
Aurélien Bompard 619e2a
                elif is_true(current[key]):
Ricky Elrod b50203
                    current[key] = True
Pierre-Yves Chibon 4d55f3
            # Update the current dict, removing the old keys
Pierre-Yves Chibon 4d55f3
            for key in sorted(current):
Pierre-Yves Chibon 4d55f3
                if key not in default:
Pierre-Yves Chibon 4d55f3
                    del current[key]
Pierre-Yves Chibon fdb2df
            return current
Pierre-Yves Chibon 75f31c
        else:
Pierre-Yves Chibon fdb2df
            return default
Pierre-Yves Chibon 75f31c
Pierre-Yves Chibon e1cc67
    @settings.setter
Pierre-Yves Chibon e1cc67
    def settings(self, settings):
Pierre-Yves Chibon 9c2953
        """ Ensures the settings are properly saved. """
Pierre-Yves Chibon 75f31c
        self._settings = json.dumps(settings)
Pierre-Yves Chibon 75f31c
Pierre-Yves Chibon 1dbdfe
    @property
Pierre-Yves Chibon 4557f8
    def milestones(self):
Pierre-Yves Chibon 4557f8
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon 4557f8
        dict object.
Pierre-Yves Chibon 4557f8
        """
Pierre-Yves Chibon 4557f8
        milestones = {}
Pierre-Yves Chibon 4557f8
Pierre-Yves Chibon 4557f8
        if self._milestones:
Pierre-Yves Chibon 9c2953
Aurélien Bompard f61bb3
            def _convert_to_dict(value):
Aurélien Bompard f61bb3
                if isinstance(value, dict):
Aurélien Bompard f61bb3
                    return value
Pierre-Yves Chibon 0619e5
                else:
Pierre-Yves Chibon 9c2953
                    return {"date": value, "active": True}
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
            milestones = dict(
Pierre-Yves Chibon 9c2953
                [
Pierre-Yves Chibon 9c2953
                    (k, _convert_to_dict(v))
Pierre-Yves Chibon 9c2953
                    for k, v in json.loads(self._milestones).items()
Pierre-Yves Chibon 9c2953
                ]
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 4557f8
Pierre-Yves Chibon 4557f8
        return milestones
Pierre-Yves Chibon 4557f8
Pierre-Yves Chibon 4557f8
    @milestones.setter
Pierre-Yves Chibon 4557f8
    def milestones(self, milestones):
Pierre-Yves Chibon 9c2953
        """ Ensures the milestones are properly saved. """
Pierre-Yves Chibon 4557f8
        self._milestones = json.dumps(milestones)
Pierre-Yves Chibon 4557f8
Pierre-Yves Chibon 4557f8
    @property
Pierre-Yves Chibon f7a5ec
    def milestones_keys(self):
Pierre-Yves Chibon f7a5ec
        """ Return the list of milestones so we can keep the order consistent.
Pierre-Yves Chibon f7a5ec
        """
Pierre-Yves Chibon f7a5ec
        milestones_keys = {}
Pierre-Yves Chibon f7a5ec
Pierre-Yves Chibon f7a5ec
        if self._milestones_keys:
Pierre-Yves Chibon f7a5ec
            milestones_keys = json.loads(self._milestones_keys)
Pierre-Yves Chibon f7a5ec
Pierre-Yves Chibon f7a5ec
        return milestones_keys
Pierre-Yves Chibon f7a5ec
Pierre-Yves Chibon f7a5ec
    @milestones_keys.setter
Pierre-Yves Chibon f7a5ec
    def milestones_keys(self, milestones_keys):
Pierre-Yves Chibon 9c2953
        """ Ensures the milestones keys are properly saved. """
Pierre-Yves Chibon f7a5ec
        self._milestones_keys = json.dumps(milestones_keys)
Pierre-Yves Chibon f7a5ec
Pierre-Yves Chibon f7a5ec
    @property
Pierre-Yves Chibon e8b9d0
    def priorities(self):
Pierre-Yves Chibon e8b9d0
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon e8b9d0
        dict object.
Pierre-Yves Chibon e8b9d0
        """
Pierre-Yves Chibon e8b9d0
        priorities = {}
Pierre-Yves Chibon e8b9d0
Pierre-Yves Chibon e8b9d0
        if self._priorities:
Pierre-Yves Chibon e8b9d0
            priorities = json.loads(self._priorities)
Pierre-Yves Chibon e8b9d0
Pierre-Yves Chibon e8b9d0
        return priorities
Pierre-Yves Chibon e8b9d0
Pierre-Yves Chibon e8b9d0
    @priorities.setter
Pierre-Yves Chibon e8b9d0
    def priorities(self, priorities):
Pierre-Yves Chibon 9c2953
        """ Ensures the priorities are properly saved. """
Pierre-Yves Chibon e8b9d0
        self._priorities = json.dumps(priorities)
Pierre-Yves Chibon e8b9d0
Pierre-Yves Chibon e8b9d0
    @property
Pierre-Yves Chibon 2de085
    def block_users(self):
Pierre-Yves Chibon 2de085
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon 2de085
        dict object.
Pierre-Yves Chibon 2de085
        """
Pierre-Yves Chibon 2de085
        block_users = []
Pierre-Yves Chibon 2de085
Pierre-Yves Chibon 2de085
        if self._block_users:
Pierre-Yves Chibon 2de085
            block_users = json.loads(self._block_users)
Pierre-Yves Chibon 2de085
Pierre-Yves Chibon 2de085
        return block_users
Pierre-Yves Chibon 2de085
Pierre-Yves Chibon 2de085
    @block_users.setter
Pierre-Yves Chibon 2de085
    def block_users(self, block_users):
Pierre-Yves Chibon 2de085
        """ Ensures the block_users are properly saved. """
Pierre-Yves Chibon 2de085
        self._block_users = json.dumps(block_users)
Pierre-Yves Chibon 2de085
Pierre-Yves Chibon 2de085
    @property
Lubomír Sedlář fc06fa
    def quick_replies(self):
Lubomír Sedlář fc06fa
        """ Return a list of quick replies available for pull requests and
Lubomír Sedlář fc06fa
        issues.
Lubomír Sedlář fc06fa
        """
Lubomír Sedlář fc06fa
        quick_replies = []
Lubomír Sedlář fc06fa
Lubomír Sedlář fc06fa
        if self._quick_replies:
Lubomír Sedlář fc06fa
            quick_replies = json.loads(self._quick_replies)
Lubomír Sedlář fc06fa
Lubomír Sedlář fc06fa
        return quick_replies
Lubomír Sedlář fc06fa
Lubomír Sedlář fc06fa
    @quick_replies.setter
Lubomír Sedlář fc06fa
    def quick_replies(self, quick_replies):
Lubomír Sedlář fc06fa
        """ Ensures the quick replies are properly saved. """
Lubomír Sedlář fc06fa
        self._quick_replies = json.dumps(quick_replies)
Lubomír Sedlář fc06fa
Lubomír Sedlář fc06fa
    @property
Pierre-Yves Chibon c2e7b0
    def notifications(self):
Pierre-Yves Chibon c2e7b0
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon c2e7b0
        dict object.
Pierre-Yves Chibon c2e7b0
        """
Pierre-Yves Chibon c2e7b0
        notifications = {}
Pierre-Yves Chibon c2e7b0
Pierre-Yves Chibon c2e7b0
        if self._notifications:
Pierre-Yves Chibon c2e7b0
            notifications = json.loads(self._notifications)
Pierre-Yves Chibon c2e7b0
Pierre-Yves Chibon c2e7b0
        return notifications
Pierre-Yves Chibon c2e7b0
Pierre-Yves Chibon c2e7b0
    @notifications.setter
Pierre-Yves Chibon c2e7b0
    def notifications(self, notifications):
Pierre-Yves Chibon 9c2953
        """ Ensures the notifications are properly saved. """
Pierre-Yves Chibon c2e7b0
        self._notifications = json.dumps(notifications)
Pierre-Yves Chibon c2e7b0
Pierre-Yves Chibon c2e7b0
    @property
Pierre-Yves Chibon 2fee4f
    def reports(self):
Pierre-Yves Chibon 2fee4f
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon 2fee4f
        dict object.
Pierre-Yves Chibon 2fee4f
        """
Pierre-Yves Chibon 2fee4f
        reports = {}
Pierre-Yves Chibon 2fee4f
Pierre-Yves Chibon 2fee4f
        if self._reports:
Pierre-Yves Chibon 2fee4f
            reports = json.loads(self._reports)
Pierre-Yves Chibon 2fee4f
Pierre-Yves Chibon 2fee4f
        return reports
Pierre-Yves Chibon 2fee4f
Pierre-Yves Chibon 2fee4f
    @reports.setter
Pierre-Yves Chibon 2fee4f
    def reports(self, reports):
Pierre-Yves Chibon 9c2953
        """ Ensures the reports are properly saved. """
Pierre-Yves Chibon 2fee4f
        self._reports = json.dumps(reports)
Pierre-Yves Chibon 2fee4f
Pierre-Yves Chibon 2fee4f
    @property
Pierre-Yves Chibon 73001b
    def close_status(self):
Pierre-Yves Chibon 73001b
        """ Return the dict stored as string in the database as an actual
Pierre-Yves Chibon 73001b
        dict object.
Pierre-Yves Chibon 73001b
        """
Pierre-Yves Chibon 73001b
        close_status = []
Pierre-Yves Chibon 73001b
Pierre-Yves Chibon 73001b
        if self._close_status:
Pierre-Yves Chibon 73001b
            close_status = json.loads(self._close_status)
Pierre-Yves Chibon 73001b
Pierre-Yves Chibon 73001b
        return close_status
Pierre-Yves Chibon 73001b
Pierre-Yves Chibon 73001b
    @close_status.setter
Pierre-Yves Chibon 73001b
    def close_status(self, close_status):
Pierre-Yves Chibon 9c2953
        """ Ensures the different close status are properly saved. """
Pierre-Yves Chibon 73001b
        self._close_status = json.dumps(close_status)
Pierre-Yves Chibon 73001b
Pierre-Yves Chibon 73001b
    @property
Pierre-Yves Chibon 1dbdfe
    def open_requests(self):
Pierre-Yves Chibon 9c2953
        """ Returns the number of open pull-requests for this project. """
Pierre-Yves Chibon 9c2953
        return (
Pierre-Yves Chibon 9c2953
            BASE.metadata.bind.query(PullRequest)
Pierre-Yves Chibon 9c2953
            .filter(self.id == PullRequest.project_id)
Pierre-Yves Chibon 9c2953
            .filter(PullRequest.status == "Open")
Pierre-Yves Chibon 9c2953
            .count()
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 1dbdfe
Pierre-Yves Chibon 1dbdfe
    @property
Pierre-Yves Chibon 1dbdfe
    def open_tickets(self):
Pierre-Yves Chibon 9c2953
        """ Returns the number of open tickets for this project. """
Pierre-Yves Chibon 9c2953
        return (
Pierre-Yves Chibon 9c2953
            BASE.metadata.bind.query(Issue)
Pierre-Yves Chibon 9c2953
            .filter(self.id == Issue.project_id)
Pierre-Yves Chibon 9c2953
            .filter(Issue.status == "Open")
Pierre-Yves Chibon 9c2953
            .count()
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 1dbdfe
Pierre-Yves Chibon 1dbdfe
    @property
Pierre-Yves Chibon 1dbdfe
    def open_tickets_public(self):
Pierre-Yves Chibon 9c2953
        """ Returns the number of open tickets for this project. """
Pierre-Yves Chibon 9c2953
        return (
Pierre-Yves Chibon 9c2953
            BASE.metadata.bind.query(Issue)
Pierre-Yves Chibon 9c2953
            .filter(self.id == Issue.project_id)
Pierre-Yves Chibon 9c2953
            .filter(Issue.status == "Open")
Pierre-Yves Chibon 9c2953
            .filter(Issue.private == False)  # noqa: E712
Pierre-Yves Chibon 9c2953
            .count()
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 1dbdfe
Pierre-Yves Chibon 7d9e04
    @property
Pierre-Yves Chibon 7d9e04
    def contributors(self):
Pierre-Yves Chibon 7d9e04
        """ Return the dict presenting the different contributors of the
Pierre-Yves Chibon 7d9e04
        project based on their access level.
Pierre-Yves Chibon 7d9e04
        """
Pierre-Yves Chibon 7d9e04
        contributors = collections.defaultdict(list)
Pierre-Yves Chibon 7d9e04
Pierre-Yves Chibon 7d9e04
        for user in self.user_projects:
Pierre-Yves Chibon 7d9e04
            contributors[user.access].append(user.user)
Pierre-Yves Chibon 7d9e04
Pierre-Yves Chibon 7d9e04
        return contributors
Pierre-Yves Chibon 7d9e04
Vivek Anand 2847f5
    @property
Vivek Anand 2847f5
    def contributor_groups(self):
Vivek Anand 2847f5
        """ Return the dict presenting the different contributors of the
Vivek Anand 2847f5
        project based on their access level.
Vivek Anand 2847f5
        """
Vivek Anand 2847f5
        contributors = collections.defaultdict(list)
Vivek Anand 2847f5
Vivek Anand 2847f5
        for group in self.projects_groups:
Vivek Anand 2847f5
            contributors[group.access].append(group.group)
Vivek Anand 2847f5
Vivek Anand 2847f5
        return contributors
Vivek Anand 2847f5
Matt Prahl cbadc1
    def get_project_users(self, access, combine=True):
Pierre-Yves Chibon 9c2953
        """ Returns the list of users/groups of the project according
Matt Prahl cbadc1
        to the given access.
Matt Prahl cbadc1
Matt Prahl cbadc1
        :arg access: the access level to query for, can be: 'admin',
Matt Prahl cbadc1
            'commit' or 'ticket'.
Matt Prahl cbadc1
        :type access: string
Matt Prahl cbadc1
        :arg combine: The access levels have some hierarchy -
Matt Prahl cbadc1
            like: all the users having commit access also has
Matt Prahl cbadc1
            ticket access and the admins have all the access
Matt Prahl cbadc1
            that commit and ticket access users have. If combine
Matt Prahl cbadc1
            is set to False, this function will only return those
Matt Prahl cbadc1
            users which have the given access and no other access.
Matt Prahl cbadc1
            ex: if access is 'ticket' and combine is True, it will
Matt Prahl cbadc1
            return all the users with ticket access which includes
Matt Prahl cbadc1
            all the committers and admins. If combine were False,
Matt Prahl cbadc1
            it would have returned only the users with ticket access
Matt Prahl cbadc1
            and would not have included committers and admins.
Matt Prahl cbadc1
        :type combine: boolean
Pierre-Yves Chibon 9c2953
        """
Matt Prahl cbadc1
Pierre-Yves Chibon 9c2953
        if access not in ["admin", "commit", "ticket"]:
Matt Prahl cbadc1
            raise pagure.exceptions.AccessLevelNotFound(
Pierre-Yves Chibon 9c2953
                "The access level does not exist"
Pierre-Yves Chibon 9c2953
            )
Matt Prahl cbadc1
Matt Prahl cbadc1
        if combine:
Pierre-Yves Chibon 9c2953
            if access == "admin":
Matt Prahl cbadc1
                return self.admins
Pierre-Yves Chibon 9c2953
            elif access == "commit":
Matt Prahl cbadc1
                return self.committers
Pierre-Yves Chibon 9c2953
            elif access == "ticket":
Matt Prahl cbadc1
                return self.users
Matt Prahl cbadc1
        else:
Pierre-Yves Chibon 9c2953
            if access == "admin":
Matt Prahl cbadc1
                return self.admins
Pierre-Yves Chibon 9c2953
            elif access == "commit":
Matt Prahl cbadc1
                committers = set(self.committers)
Matt Prahl cbadc1
                admins = set(self.admins)
Matt Prahl cbadc1
                return list(committers - admins)
Pierre-Yves Chibon 9c2953
            elif access == "ticket":
Matt Prahl cbadc1
                committers = set(self.committers)
Matt Prahl cbadc1
                admins = set(self.admins)
Matt Prahl cbadc1
                users = set(self.users)
Matt Prahl cbadc1
                return list(users - committers - admins)
Matt Prahl cbadc1
Matt Prahl cbadc1
    def get_project_groups(self, access, combine=True):
Pierre-Yves Chibon 9c2953
        """ Returns the list of groups of the project according
Matt Prahl cbadc1
        to the given access.
Matt Prahl cbadc1
Matt Prahl cbadc1
        :arg access: the access level to query for, can be: 'admin',
Matt Prahl cbadc1
            'commit' or 'ticket'.
Matt Prahl cbadc1
        :type access: string
Matt Prahl cbadc1
        :arg combine: The access levels have some hierarchy -
Matt Prahl cbadc1
            like: all the groups having commit access also has
Matt Prahl cbadc1
            ticket access and the admin_groups have all the access
Matt Prahl cbadc1
            that committer_groups and ticket access groups have.
Matt Prahl cbadc1
            If combine is set to False, this function will only return
Matt Prahl cbadc1
            those groups which have the given access and no other access.
Matt Prahl cbadc1
            ex: if access is 'ticket' and combine is True, it will
Matt Prahl cbadc1
            return all the groups with ticket access which includes
Matt Prahl cbadc1
            all the committer_groups and admin_groups. If combine were False,
Matt Prahl cbadc1
            it would have returned only the groups with ticket access
Matt Prahl cbadc1
            and would not have included committer_groups and admin_groups.
Matt Prahl cbadc1
        :type combine: boolean
Pierre-Yves Chibon 9c2953
        """
Matt Prahl cbadc1
Pierre-Yves Chibon 9c2953
        if access not in ["admin", "commit", "ticket"]:
Matt Prahl cbadc1
            raise pagure.exceptions.AccessLevelNotFound(
Pierre-Yves Chibon 9c2953
                "The access level does not exist"
Pierre-Yves Chibon 9c2953
            )
Matt Prahl cbadc1
Matt Prahl cbadc1
        if combine:
Pierre-Yves Chibon 9c2953
            if access == "admin":
Matt Prahl cbadc1
                return self.admin_groups
Pierre-Yves Chibon 9c2953
            elif access == "commit":
Matt Prahl cbadc1
                return self.committer_groups
Pierre-Yves Chibon 9c2953
            elif access == "ticket":
Matt Prahl cbadc1
                return self.groups
Matt Prahl cbadc1
        else:
Pierre-Yves Chibon 9c2953
            if access == "admin":
Matt Prahl cbadc1
                return self.admin_groups
Pierre-Yves Chibon 9c2953
            elif access == "commit":
Matt Prahl cbadc1
                committers = set(self.committer_groups)
Matt Prahl cbadc1
                admins = set(self.admin_groups)
Matt Prahl cbadc1
                return list(committers - admins)
Pierre-Yves Chibon 9c2953
            elif access == "ticket":
Matt Prahl cbadc1
                committers = set(self.committer_groups)
Matt Prahl cbadc1
                admins = set(self.admin_groups)
Matt Prahl cbadc1
                groups = set(self.groups)
Matt Prahl cbadc1
                return list(groups - committers - admins)
Matt Prahl cbadc1
Matt Prahl cbadc1
    @property
Matt Prahl cbadc1
    def access_users(self):
Pierre-Yves Chibon 9c2953
        """ Return a dictionary with all user access
Pierre-Yves Chibon 9c2953
        """
Matt Prahl cbadc1
        return {
Pierre-Yves Chibon 9c2953
            "admin": self.get_project_users(access="admin", combine=False),
Pierre-Yves Chibon 9c2953
            "commit": self.get_project_users(access="commit", combine=False),
Pierre-Yves Chibon 9c2953
            "ticket": self.get_project_users(access="ticket", combine=False),
Matt Prahl cbadc1
        }
Matt Prahl cbadc1
Matt Prahl cbadc1
    @property
Matt Prahl 6bf79a
    def access_users_json(self):
Pierre-Yves Chibon 9c2953
        json_access_users = {"owner": [self.user.username]}
Matt Prahl 6bf79a
        for access, users in self.access_users.items():
Matt Prahl 6bf79a
            json_access_users[access] = []
Matt Prahl 6bf79a
            for user in users:
Matt Prahl 6bf79a
                json_access_users[access].append(user.user)
Matt Prahl 6bf79a
Matt Prahl 6bf79a
        return json_access_users
Matt Prahl 6bf79a
Matt Prahl 6bf79a
    @property
Matt Prahl 6bf79a
    def access_groups_json(self):
Matt Prahl 6bf79a
        json_access_groups = {}
Matt Prahl 6bf79a
        for access, groups in self.access_groups.items():
Matt Prahl 6bf79a
            json_access_groups[access] = []
Matt Prahl 6bf79a
            for group in groups:
Matt Prahl 6bf79a
                json_access_groups[access].append(group.group_name)
Matt Prahl 6bf79a
Matt Prahl 6bf79a
        return json_access_groups
Matt Prahl 6bf79a
Matt Prahl 6bf79a
    @property
Matt Prahl cbadc1
    def access_groups(self):
Pierre-Yves Chibon 9c2953
        """ Return a dictionary with all group access
Pierre-Yves Chibon 9c2953
        """
Matt Prahl cbadc1
        return {
Pierre-Yves Chibon 9c2953
            "admin": self.get_project_groups(access="admin", combine=False),
Pierre-Yves Chibon 9c2953
            "commit": self.get_project_groups(access="commit", combine=False),
Pierre-Yves Chibon 9c2953
            "ticket": self.get_project_groups(access="ticket", combine=False),
Matt Prahl cbadc1
        }
Matt Prahl cbadc1
Patrick Uiterwijk 7b9080
    def lock(self, ltype):
Patrick Uiterwijk 7b9080
        """ Get a SQL lock of type ltype for the current project.
Patrick Uiterwijk 7b9080
        """
Patrick Uiterwijk 7b9080
        return ProjectLocker(self, ltype)
Patrick Uiterwijk 7b9080
Pierre-Yves Chibon 68dfc1
    def to_json(self, public=False, api=False):
Pierre-Yves Chibon 9c2953
        """ Return a representation of the project as JSON.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        custom_keys = [[key.name, key.key_type] for key in self.issue_keys]
Pierre-Yves Chibon 0b7590
Pierre-Yves Chibon 0b7590
        output = {
Pierre-Yves Chibon 9c2953
            "id": self.id,
Pierre-Yves Chibon 9c2953
            "name": self.name,
Pierre-Yves Chibon 9c2953
            "fullname": self.fullname,
Pierre-Yves Chibon 9c2953
            "url_path": self.url_path,
Pierre-Yves Chibon 9c2953
            "description": self.description,
Pierre-Yves Chibon 9c2953
            "namespace": self.namespace,
Pierre-Yves Chibon 9c2953
            "parent": self.parent.to_json(public=public, api=api)
Pierre-Yves Chibon 9c2953
            if self.parent
Pierre-Yves Chibon 9c2953
            else None,
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 9c2953
            "date_modified": arrow_ts(self.date_modified),
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 9c2953
            "access_users": self.access_users_json,
Pierre-Yves Chibon 9c2953
            "access_groups": self.access_groups_json,
Pierre-Yves Chibon 9c2953
            "tags": self.tags_text,
Pierre-Yves Chibon 9c2953
            "priorities": self.priorities,
Pierre-Yves Chibon 9c2953
            "custom_keys": custom_keys,
Pierre-Yves Chibon 9c2953
            "close_status": self.close_status,
Pierre-Yves Chibon 9c2953
            "milestones": self.milestones,
Pierre-Yves Chibon 0b7590
        }
Pierre-Yves Chibon 421c1d
        if not api and not public:
Pierre-Yves Chibon 9c2953
            output["settings"] = self.settings
Pierre-Yves Chibon 0b7590
Pierre-Yves Chibon 0b7590
        return output
Pierre-Yves Chibon 0b7590
Pierre-Yves Chibon 2f8732
Patrick Uiterwijk 7b9080
class ProjectLock(BASE):
Patrick Uiterwijk 7b9080
    """ Table used to define project-specific locks.
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
    Table -- project_locks
Patrick Uiterwijk 7b9080
    """
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    __tablename__ = "project_locks"
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
    project_id = sa.Column(
Patrick Uiterwijk 7b9080
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
Patrick Uiterwijk 7b9080
        nullable=False,
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk 7b9080
    lock_type = sa.Column(
Patrick Uiterwijk 7b9080
        sa.Enum(
Pierre-Yves Chibon 9c2953
            "WORKER", "WORKER_TICKET", "WORKER_REQUEST", name="lock_type_enum"
Patrick Uiterwijk 7b9080
        ),
Patrick Uiterwijk 7b9080
        nullable=False,
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
class ProjectLocker(object):
Patrick Uiterwijk 7b9080
    """ This is used as a context manager to lock a project.
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
    This is used as a context manager to make it very explicit when we unlock
Patrick Uiterwijk 7b9080
    the project, and so that we unlock even if an exception occurs.
Patrick Uiterwijk 7b9080
    """
Pierre-Yves Chibon 9c2953
Patrick Uiterwijk 7b9080
    def __init__(self, project, ltype):
Patrick Uiterwijk 7b9080
        self.session = None
Patrick Uiterwijk 7b9080
        self.lock = None
Patrick Uiterwijk 7b9080
        self.project_id = project.id
Patrick Uiterwijk 7b9080
        self.ltype = ltype
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
    def __enter__(self):
Pierre-Yves Chibon cf98be
        from pagure.lib.model_base import create_session
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
        self.session = create_session()
Patrick Uiterwijk 7b9080
Pierre-Yves Chibon 9c2953
        _log.info("Grabbing lock for %d", self.project_id)
Pierre-Yves Chibon 9c2953
        query = (
Pierre-Yves Chibon 9c2953
            self.session.query(ProjectLock)
Pierre-Yves Chibon 9c2953
            .filter(ProjectLock.project_id == self.project_id)
Pierre-Yves Chibon 9c2953
            .filter(ProjectLock.lock_type == self.ltype)
Pierre-Yves Chibon 9c2953
            .with_for_update(nowait=False, read=False)
Pierre-Yves Chibon 9c2953
        )
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
        try:
Patrick Uiterwijk 7b9080
            self.lock = query.one()
Pierre-Yves Chibon 1fc6d8
        except Exception:
Pierre-Yves Chibon 9c2953
            pl = ProjectLock(project_id=self.project_id, lock_type=self.ltype)
Patrick Uiterwijk 7b9080
            self.session.add(pl)
Patrick Uiterwijk 7b9080
            self.session.commit()
Patrick Uiterwijk 7b9080
            self.lock = query.one()
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
        assert self.lock is not None
Pierre-Yves Chibon 9c2953
        _log.info("Got lock for %d: %s", self.project_id, self.lock)
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
    def __exit__(self, *exargs):
Pierre-Yves Chibon 9c2953
        _log.info("Releasing lock for %d", self.project_id)
Patrick Uiterwijk 7b9080
        self.session.remove()
Pierre-Yves Chibon 9c2953
        _log.info("Released lock for %d", self.project_id)
Patrick Uiterwijk 7b9080
Patrick Uiterwijk 7b9080
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 9c2953
    __tablename__ = "user_projects"
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("project_id", "user_id", "access"),)
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 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Vivek Anand 967335
    access = sa.Column(
Vivek Anand 967335
        sa.String(255),
Vivek Anand 967335
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "access_levels.access", onupdate="CASCADE", ondelete="CASCADE"
Vivek Anand 967335
        ),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 96fa1e
Pierre-Yves Chibon 7d9e04
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon b4a226
        backref=backref(
Pierre-Yves Chibon 9c2953
            "user_projects", cascade="delete,delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon b4a226
    )
Pierre-Yves Chibon 7d9e04
Pierre-Yves Chibon 9c2953
    user = relation("User", backref="user_projects")
Pierre-Yves Chibon 7d9e04
Pierre-Yves Chibon 96fa1e
Patrick Uiterwijk 9b237b
class SSHKey(BASE):
Patrick Uiterwijk 9b237b
    """ Stores information about SSH keys.
Patrick Uiterwijk 9a7915
Patrick Uiterwijk 9b237b
    Every instance needs to either have user_id set (SSH key for a specific
Patrick Uiterwijk 9b237b
    user) or project_id ("deploy key" for a specific project).
Patrick Uiterwijk 9b237b
Patrick Uiterwijk 9b237b
    Table -- sshkeys
Patrick Uiterwijk 9a7915
    """
Patrick Uiterwijk 9a7915
Patrick Uiterwijk 9b237b
    __tablename__ = "sshkeys"
Patrick Uiterwijk 9a7915
    id = sa.Column(sa.Integer, primary_key=True)
Patrick Uiterwijk 9a7915
    project_id = sa.Column(
Patrick Uiterwijk 9a7915
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
Patrick Uiterwijk 9b237b
        nullable=True,
Patrick Uiterwijk 9b237b
        index=True,
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk 9a7915
    pushaccess = sa.Column(sa.Boolean, nullable=False, default=False)
Patrick Uiterwijk 9b237b
    user_id = sa.Column(
Patrick Uiterwijk 9b237b
        sa.Integer,
Patrick Uiterwijk 9b237b
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Patrick Uiterwijk 9b237b
        nullable=True,
Patrick Uiterwijk 9b237b
        index=True,
Patrick Uiterwijk 9b237b
    )
Patrick Uiterwijk 9a7915
    public_ssh_key = sa.Column(sa.Text, nullable=False)
Patrick Uiterwijk 9a7915
    ssh_short_key = sa.Column(sa.Text, nullable=False)
Patrick Uiterwijk 9b237b
    ssh_search_key = sa.Column(
Patrick Uiterwijk 702cfe
        sa.String(length=60), nullable=False, index=True, unique=True
Patrick Uiterwijk 9b237b
    )
Patrick Uiterwijk 9a7915
    creator_user_id = sa.Column(
Patrick Uiterwijk 9a7915
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Patrick Uiterwijk 9a7915
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk 9a7915
Patrick Uiterwijk 9b237b
    # Validations
Patrick Uiterwijk 9b237b
    # These two validators are intended to make sure an SSHKey is either
Patrick Uiterwijk 9b237b
    # assigned to a Project or a User, but not both.
Patrick Uiterwijk 9b237b
    @validates("project_id")
Patrick Uiterwijk 9b237b
    def validate_project_id(self, key, value):
Patrick Uiterwijk 9b237b
        """ Validates that user_id is not set. """
Patrick Uiterwijk 9b237b
        if self.user_id is not None:
Patrick Uiterwijk 9b237b
            raise ValueError("SSHKey can't have both project and user")
Patrick Uiterwijk 9b237b
        return value
Patrick Uiterwijk 9b237b
Patrick Uiterwijk 9b237b
    @validates("user_id")
Patrick Uiterwijk 9b237b
    def validate_user_id(self, key, value):
Patrick Uiterwijk 9b237b
        """ Validates that project_id is not set. """
Patrick Uiterwijk 9b237b
        if self.project_id is not None:
Patrick Uiterwijk 9b237b
            raise ValueError("SSHKey can't have both user and project")
Patrick Uiterwijk 9b237b
        return value
Patrick Uiterwijk 9b237b
Patrick Uiterwijk 9a7915
    # Relations
Patrick Uiterwijk 9a7915
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Patrick Uiterwijk 9a7915
        backref=backref(
Pierre-Yves Chibon 9c2953
            "deploykeys", cascade="delete, delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 12805b
    )
Patrick Uiterwijk 9a7915
Patrick Uiterwijk 9b237b
    user = relation(
Patrick Uiterwijk 9b237b
        "User",
Patrick Uiterwijk 9b237b
        foreign_keys=[user_id],
Patrick Uiterwijk 9b237b
        remote_side=[User.id],
Patrick Uiterwijk 9b237b
        backref=backref(
Patrick Uiterwijk 9b237b
            "sshkeys", cascade="delete, delete-orphan", single_parent=True
Patrick Uiterwijk 9b237b
        ),
Patrick Uiterwijk 9b237b
    )
Patrick Uiterwijk 9b237b
Pierre-Yves Chibon 9606b4
    creator_user = relation(
Pierre-Yves Chibon 9c2953
        "User", foreign_keys=[creator_user_id], remote_side=[User.id]
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk 9a7915
Patrick Uiterwijk 9a7915
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 9c2953
    __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 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    title = sa.Column(sa.Text, nullable=False)
Pierre-Yves Chibon 9c2953
    content = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon fdf0f1
    assignee_id = sa.Column(
Pierre-Yves Chibon fdf0f1
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon fdf0f1
        nullable=True,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon caf4e8
    status = sa.Column(
Pierre-Yves Chibon 5fd1da
        sa.String(255),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("status_issue.status", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        default="Open",
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon cc3505
    private = sa.Column(sa.Boolean, nullable=False, default=False)
Pierre-Yves Chibon e8b9d0
    priority = sa.Column(sa.Integer, nullable=True, default=None)
Pierre-Yves Chibon 3cea10
    milestone = sa.Column(sa.String(255), nullable=True, default=None)
Pierre-Yves Chibon 73001b
    close_status = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    last_updated = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Vivek Anand 0f4fed
    closed_at = sa.Column(sa.DateTime, nullable=True)
Clement Verna 81c130
    closed_by_id = sa.Column(
Clement Verna 81c130
        sa.Integer,
Clement Verna 81c130
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Clement Verna 81c130
        nullable=True,
Clement Verna 81c130
    )
Vivek Anand 0f4fed
Pierre-Yves Chibon 0ef501
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 9c2953
        backref=backref("issues", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 9c2953
        single_parent=True,
Pierre-Yves Chibon b4a226
    )
Pierre-Yves Chibon 701b83
Pierre-Yves Chibon 9c2953
    user = relation(
Pierre-Yves Chibon 9c2953
        "User", foreign_keys=[user_id], remote_side=[User.id], backref="issues"
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    assignee = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[assignee_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref="assigned_issues",
Pierre-Yves Chibon 9c2953
    )
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 16035c
    tags = relation(
Pierre-Yves Chibon 16035c
        "TagColored",
Pierre-Yves Chibon 16035c
        secondary="tags_issues_colored",
Pierre-Yves Chibon 16035c
        primaryjoin="issues.c.uid==tags_issues_colored.c.issue_uid",
Pierre-Yves Chibon 16035c
        secondaryjoin="tags_issues_colored.c.tag_id==tags_colored.c.id",
Pierre-Yves Chibon 9c2953
        viewonly=True,
Pierre-Yves Chibon 16035c
    )
Pierre-Yves Chibon 16035c
Clement Verna 81c130
    closed_by = relation(
Clement Verna 81c130
        "User",
Clement Verna 81c130
        foreign_keys=[closed_by_id],
Clement Verna 81c130
        remote_side=[User.id],
Clement Verna 81c130
        backref="closed_issues",
Clement Verna 81c130
    )
Clement Verna 81c130
Pierre-Yves Chibon 2a0134
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        return "Issue(%s, project:%s, user:%s, title:%s)" % (
Pierre-Yves Chibon 9c2953
            self.id,
Pierre-Yves Chibon 9c2953
            self.project.name,
Pierre-Yves Chibon 9c2953
            self.user.user,
Pierre-Yves Chibon 9c2953
            self.title,
Pierre-Yves Chibon 2a0134
        )
Pierre-Yves Chibon 2a0134
Pierre-Yves Chibon 11c5e9
    @property
Mark Reynolds 784dde
    def attachments(self):
Pierre-Yves Chibon 9c2953
        """ Return a list of attachment tuples: (LINK, FILENAME, DISPLAY_NAME,
Pierre-Yves Chibon 9c2953
        DATE) """
Mark Reynolds 8b826a
Mark Reynolds 8b826a
        def extract_info(text):
Pierre-Yves Chibon 9c2953
            """ Return a tuple containing the link, file name, and the
Pierre-Yves Chibon 9c2953
            "display" file name from the markdown attachment link """
Karsten Hopp b310af
            pattern_md = re.compile(r"^\[\!(.*)\]")
Karsten Hopp b310af
            pattern_link = re.compile(r"\(([^)]+)\)")
Karsten Hopp b310af
            pattern_file = re.compile(r"\[([^]]+)\]")
Mark Reynolds 784dde
Mark Reynolds 784dde
            try:
Mark Reynolds 8b826a
                md_link = pattern_md.search(text).group(1)
Mark Reynolds 8b826a
                link = pattern_link.search(md_link).group(1)
Mark Reynolds 8b826a
                filename = pattern_file.search(md_link).group(1)
Mark Reynolds 8b826a
                if md_link is None or link is None or filename is None:
Mark Reynolds 784dde
                    # No match, return the original string
Mark Reynolds 8b826a
                    return (text, text, text)
Mark Reynolds 8b826a
                if len(filename) > 50:
Mark Reynolds 8b826a
                    # File name is too long to display, truncate it.
Mark Reynolds 8b826a
                    display_name = filename[:50] + "..."
Mark Reynolds 8b826a
                else:
Mark Reynolds 8b826a
                    display_name = filename
Abhijeet Kasurde f4bf50
            except AttributeError:
Mark Reynolds 784dde
                # Search failed, return the original string
Mark Reynolds 8b826a
                return (text, text, text)
Mark Reynolds 8b826a
            return (link, filename, display_name)
Mark Reynolds 784dde
Mark Reynolds 784dde
        attachments = []
Mark Reynolds 784dde
        if self.content:
Mark Reynolds 784dde
            # Check the initial issue description for attachments
Pierre-Yves Chibon 9c2953
            lines = self.content.split("\n")
Mark Reynolds 784dde
            for line in lines:
Mark Reynolds 784dde
                if line and line != "" and line.startswith("[!["):
Mark Reynolds 8b826a
                    link, filename, display_name = extract_info(line)
Mark Reynolds 784dde
                    attachments.append(
Pierre-Yves Chibon 9c2953
                        (
Pierre-Yves Chibon 9c2953
                            link,
Pierre-Yves Chibon 9c2953
                            filename,
Pierre-Yves Chibon 9c2953
                            display_name,
Pierre-Yves Chibon 9c2953
                            self.date_created.strftime("%Y-%m-%d %H:%M:%S"),
Pierre-Yves Chibon 9c2953
                            None,
Pierre-Yves Chibon 9c2953
                        )
Pierre-Yves Chibon 9606b4
                    )
Mark Reynolds 784dde
        if self.comments:
Mark Reynolds 784dde
            # Check the comments for attachments
Mark Reynolds 784dde
            for comment in self.comments:
Mark Reynolds 784dde
                if comment.id == 0:
Mark Reynolds 784dde
                    comment_text = comment.content
Mark Reynolds 784dde
                else:
Mark Reynolds 784dde
                    comment_text = comment.comment
Pierre-Yves Chibon 9c2953
                lines = comment_text.split("\n")
Mark Reynolds 784dde
                for line in lines:
Mark Reynolds 784dde
                    if line and line != "" and line.startswith("[!["):
Mark Reynolds 8b826a
                        link, filename, display_name = extract_info(line)
Mark Reynolds 784dde
                        attachments.append(
Pierre-Yves Chibon 9c2953
                            (
Pierre-Yves Chibon 9c2953
                                link,
Pierre-Yves Chibon 9c2953
                                filename,
Pierre-Yves Chibon 9c2953
                                display_name,
Pierre-Yves Chibon 9c2953
                                comment.date_created.strftime(
Pierre-Yves Chibon 9c2953
                                    "%Y-%m-%d %H:%M:%S"
Pierre-Yves Chibon 9c2953
                                ),
Pierre-Yves Chibon 9c2953
                                "%s" % comment.id,
Pierre-Yves Chibon 9c2953
                            )
Pierre-Yves Chibon 12805b
                        )
Mark Reynolds 784dde
        return attachments
Mark Reynolds 784dde
Mark Reynolds 784dde
    @property
Pierre-Yves Chibon a53135
    def isa(self):
Pierre-Yves Chibon 9c2953
        """ A string to allow finding out that this is an issue. """
Pierre-Yves Chibon 9c2953
        return "issue"
Pierre-Yves Chibon a53135
Pierre-Yves Chibon a53135
    @property
Patrick Uiterwijk 3f97f6
    def repotype(self):
Patrick Uiterwijk 3f97f6
        """ A string returning the repotype for repopath() calls. """
Patrick Uiterwijk 3f97f6
        return "tickets"
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    @property
Pierre-Yves Chibon 11c5e9
    def mail_id(self):
Pierre-Yves Chibon 9c2953
        """ Return a unique reprensetation of the issue as string that
Pierre-Yves Chibon 11c5e9
        can be used when sending emails.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return "%s-ticket-%s" % (self.project.name, self.uid)
Pierre-Yves Chibon 11c5e9
Pierre-Yves Chibon 0e6770
    @property
Pierre-Yves Chibon 0e6770
    def tags_text(self):
Pierre-Yves Chibon 9c2953
        """ Return the list of tags in a simple text form. """
Pierre-Yves Chibon 0e6770
        return [tag.tag for tag in self.tags]
Pierre-Yves Chibon 0e6770
Pierre-Yves Chibon 48b5f8
    @property
Pierre-Yves Chibon 215f54
    def depending_text(self):
Pierre-Yves Chibon 9c2953
        """ Return the list of issue this issue depends on in simple text. """
Pierre-Yves Chibon a8a5f4
        return [issue.id for issue in self.parents]
Pierre-Yves Chibon 48b5f8
Pierre-Yves Chibon 48b5f8
    @property
Pierre-Yves Chibon 215f54
    def blocking_text(self):
Pierre-Yves Chibon 9c2953
        """ Return the list of issue this issue blocks on in simple text. """
Pierre-Yves Chibon a8a5f4
        return [issue.id for issue in self.children]
Pierre-Yves Chibon 48b5f8
Pierre-Yves Chibon a15c90
    @property
Pierre-Yves Chibon a15c90
    def user_comments(self):
Pierre-Yves Chibon 9c2953
        """ Return user comments only, filter it from notifications
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon f610bf
        return [
Pierre-Yves Chibon 9c2953
            comment for comment in self.comments if not comment.notification
Pierre-Yves Chibon 9c2953
        ]
Pierre-Yves Chibon a15c90
Aurélien Bompard 831553
    @property
Aurélien Bompard 831553
    def sortable_priority(self):
Pierre-Yves Chibon 9c2953
        """ Return an empty string if no priority is set allowing issues to
Pierre-Yves Chibon 9c2953
        be sorted using this attribute. """
Pierre-Yves Chibon 9c2953
        return self.priority if self.priority else ""
Aurélien Bompard 831553
Pierre-Yves Chibon f8e0d1
    def to_json(self, public=False, with_comments=True, with_project=False):
Pierre-Yves Chibon 9c2953
        """ Returns a dictionary representation of the issue.
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 8001d4
        custom_fields = [
Pierre-Yves Chibon 8001d4
            dict(
Pierre-Yves Chibon 8001d4
                name=field.key.name,
Pierre-Yves Chibon 8001d4
                key_type=field.key.key_type,
Pierre-Yves Chibon 8001d4
                value=field.value,
Pierre-Yves Chibon 9c2953
                key_data=field.key.key_data,
Pierre-Yves Chibon 8001d4
            )
Pierre-Yves Chibon 8001d4
            for field in self.other_fields
Pierre-Yves Chibon 8001d4
        ]
Pierre-Yves Chibon 8001d4
Pierre-Yves Chibon 7379e4
        output = {
Pierre-Yves Chibon 9c2953
            "id": self.id,
Pierre-Yves Chibon 9c2953
            "title": self.title,
Pierre-Yves Chibon 9c2953
            "content": self.content,
Pierre-Yves Chibon 9c2953
            "status": self.status,
Pierre-Yves Chibon 9c2953
            "close_status": self.close_status,
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 9c2953
            "last_updated": arrow_ts(self.last_updated),
Pierre-Yves Chibon 9c2953
            "closed_at": arrow_ts(self.closed_at) if self.closed_at else None,
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 9c2953
            "private": self.private,
Pierre-Yves Chibon 9c2953
            "tags": self.tags_text,
Pierre-Yves Chibon 9c2953
            "depends": ["%s" % item for item in self.depending_text],
Pierre-Yves Chibon 9c2953
            "blocks": ["%s" % item for item in self.blocking_text],
Pierre-Yves Chibon 9c2953
            "assignee": self.assignee.to_json(public=public)
Pierre-Yves Chibon 9c2953
            if self.assignee
Pierre-Yves Chibon 9c2953
            else None,
Pierre-Yves Chibon 9c2953
            "priority": self.priority,
Pierre-Yves Chibon 9c2953
            "milestone": self.milestone,
Pierre-Yves Chibon 9c2953
            "custom_fields": custom_fields,
Clement Verna 81c130
            "closed_by": self.closed_by.to_json(public=public)
Clement Verna 81c130
            if self.closed_by
Clement Verna 81c130
            else None,
Pierre-Yves Chibon 7379e4
        }
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 7379e4
        comments = []
Pierre-Yves Chibon 8d838d
        if with_comments:
Pierre-Yves Chibon 8d838d
            for comment in self.comments:
Pierre-Yves Chibon 8d838d
                comments.append(comment.to_json(public=public))
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon 9c2953
        output["comments"] = comments
Pierre-Yves Chibon 7379e4
Pierre-Yves Chibon f8e0d1
        if with_project:
Pierre-Yves Chibon 9c2953
            output["project"] = self.project.to_json(public=public, api=True)
Pierre-Yves Chibon f8e0d1
Pierre-Yves Chibon ca11ed
        return 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 9c2953
    __tablename__ = "issue_to_issue"
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon a1d4cc
    parent_issue_id = sa.Column(
Pierre-Yves Chibon fa4c05
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon a1d4cc
    child_issue_id = sa.Column(
Pierre-Yves Chibon fa4c05
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon a1d4cc
Pierre-Yves Chibon 11d019
class PrToIssue(BASE):
Pierre-Yves Chibon 11d019
    """ Stores the associations between issues and pull-requests.
Pierre-Yves Chibon 11d019
Pierre-Yves Chibon 11d019
    Table -- pr_to_issue
Pierre-Yves Chibon 11d019
    """
Pierre-Yves Chibon 11d019
Pierre-Yves Chibon 9c2953
    __tablename__ = "pr_to_issue"
Pierre-Yves Chibon 11d019
Pierre-Yves Chibon 11d019
    pull_request_uid = sa.Column(
Pierre-Yves Chibon 11d019
        sa.String(32),
Pierre-Yves Chibon 11d019
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "pull_requests.uid", ondelete="CASCADE", onupdate="CASCADE"
Pierre-Yves Chibon 11d019
        ),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 11d019
    issue_uid = sa.Column(
Pierre-Yves Chibon 11d019
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2479ca
    origin = sa.Column(sa.String(32), index=True)
Pierre-Yves Chibon 11d019
Pierre-Yves Chibon 11d019
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 9c2953
    __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 fa4c05
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    comment = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon bb9f71
    parent_id = sa.Column(
Pierre-Yves Chibon bb9f71
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issue_comments.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2fba89
Pierre-Yves Chibon 2fba89
    notification = sa.Column(sa.Boolean, default=False, nullable=False)
Pierre-Yves Chibon a9bac6
    edited_on = sa.Column(sa.DateTime, nullable=True)
Pierre-Yves Chibon a9bac6
    editor_id = sa.Column(
Pierre-Yves Chibon a9bac6
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon bb9f71
    issue = relation(
Pierre-Yves Chibon 9c2953
        "Issue",
Pierre-Yves Chibon 9c2953
        foreign_keys=[issue_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[Issue.uid],
Pierre-Yves Chibon 424eb3
        backref=backref(
Pierre-Yves Chibon 9c2953
            "comments",
Pierre-Yves Chibon 9c2953
            cascade="delete, delete-orphan",
Pierre-Yves Chibon 9c2953
            order_by=str("IssueComment.date_created"),
Pierre-Yves Chibon 424eb3
        ),
Pierre-Yves Chibon e28f89
    )
Pierre-Yves Chibon 6123e5
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 6123e5
        foreign_keys=[user_id],
Pierre-Yves Chibon 6123e5
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref="comment_issues",
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    editor = relation("User", foreign_keys=[editor_id], remote_side=[User.id])
Pierre-Yves Chibon bb9f71
Lubomír Sedlář 59889e
    _reactions = sa.Column(sa.Text, nullable=True)
Lubomír Sedlář 59889e
Pierre-Yves Chibon d01873
    @property
Pierre-Yves Chibon d01873
    def mail_id(self):
Pierre-Yves Chibon 9c2953
        """ Return a unique reprensetation of the issue as string that
Pierre-Yves Chibon d01873
        can be used when sending emails.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return "%s-ticket-%s-%s" % (
Pierre-Yves Chibon 9c2953
            self.issue.project.name,
Pierre-Yves Chibon 9c2953
            self.issue.uid,
Pierre-Yves Chibon 9c2953
            self.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon d01873
Pierre-Yves Chibon e14912
    @property
Pierre-Yves Chibon e14912
    def parent(self):
Pierre-Yves Chibon 9c2953
        """ Return the parent, in this case the issue object. """
Pierre-Yves Chibon e14912
        return self.issue
Pierre-Yves Chibon e14912
Lubomír Sedlář 59889e
    @property
Lubomír Sedlář 59889e
    def reactions(self):
Pierre-Yves Chibon 9c2953
        """ Return the reactions stored as a string in the database parsed as
Lubomír Sedlář 59889e
        an actual dict object.
Pierre-Yves Chibon 9c2953
        """
Lubomír Sedlář 59889e
        if self._reactions:
Lubomír Sedlář 59889e
            return json.loads(self._reactions)
Lubomír Sedlář 59889e
        return {}
Lubomír Sedlář 59889e
Lubomír Sedlář 59889e
    @reactions.setter
Lubomír Sedlář 59889e
    def reactions(self, reactions):
Pierre-Yves Chibon 9c2953
        """ Ensures that reactions are properly saved. """
Lubomír Sedlář 59889e
        self._reactions = json.dumps(reactions)
Lubomír Sedlář 59889e
Pierre-Yves Chibon 7dbc9d
    def to_json(self, public=False):
Pierre-Yves Chibon 9c2953
        """ Returns a dictionary representation of the issue.
Pierre-Yves Chibon 7dbc9d
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 7dbc9d
        output = {
Pierre-Yves Chibon 9c2953
            "id": self.id,
Pierre-Yves Chibon 9c2953
            "comment": self.comment,
Pierre-Yves Chibon 9c2953
            "parent": self.parent_id,
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 9c2953
            "edited_on": arrow_ts(self.edited_on) if self.edited_on else None,
Pierre-Yves Chibon 9c2953
            "editor": self.editor.to_json(public=public)
Pierre-Yves Chibon 9c2953
            if self.editor_id
Pierre-Yves Chibon 9c2953
            else None,
Pierre-Yves Chibon 9c2953
            "notification": self.notification,
Pierre-Yves Chibon 9c2953
            "reactions": self.reactions,
Pierre-Yves Chibon 164a57
        }
Pierre-Yves Chibon 7dbc9d
        return output
Pierre-Yves Chibon 7dbc9d
Pierre-Yves Chibon bb9f71
Pierre-Yves Chibon 99ab6e
class IssueKeys(BASE):
Pierre-Yves Chibon 99ab6e
    """ Stores the custom keys a project can use on issues.
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
    Table -- issue_keys
Pierre-Yves Chibon 99ab6e
    """
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 9c2953
    __tablename__ = "issue_keys"
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 99ab6e
    project_id = sa.Column(
Pierre-Yves Chibon 99ab6e
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b6b5f6
    name = sa.Column(sa.String(255), nullable=False)
Pierre-Yves Chibon 147377
    key_type = sa.Column(sa.String(255), nullable=False)
Mark Reynolds e18f6c
    key_data = sa.Column(sa.Text())
Mark Reynolds a8f1ac
    key_notify = sa.Column(sa.Boolean, default=False, nullable=False)
Pierre-Yves Chibon 147377
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("project_id", "name"),)
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 99ab6e
        backref=backref(
Pierre-Yves Chibon 9c2953
            "issue_keys", cascade="delete, delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 12805b
    )
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 0d9dae
    def __lt__(self, other):
Pierre-Yves Chibon 9c2953
        if hasattr(other, "name"):
Pierre-Yves Chibon 0d9dae
            return self.name.__lt__(other.name)
Pierre-Yves Chibon 0d9dae
Mark Reynolds b47c03
    @property
Mark Reynolds b47c03
    def data(self):
Pierre-Yves Chibon 9c2953
        """ Return the list of items """
Mark Reynolds b47c03
        if self.key_data:
Mark Reynolds 75e6d9
            return json.loads(self.key_data)
Mark Reynolds b47c03
        else:
Mark Reynolds b47c03
            return None
Mark Reynolds b47c03
Mark Reynolds b47c03
    @data.setter
Mark Reynolds 6fac0c
    def data(self, data_obj):
Pierre-Yves Chibon 9c2953
        """ Store the list data in JSON. """
Mark Reynolds 6fac0c
        if data_obj is None:
Mark Reynolds b47c03
            self.key_data = None
Mark Reynolds b47c03
        else:
Mark Reynolds 6fac0c
            self.key_data = json.dumps(data_obj)
Mark Reynolds b47c03
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
class IssueValues(BASE):
Pierre-Yves Chibon 99ab6e
    """ Stores the values of the custom keys set by project on issues.
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
    Table -- issue_values
Pierre-Yves Chibon 99ab6e
    """
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 9c2953
    __tablename__ = "issue_values"
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
    key_id = sa.Column(
Pierre-Yves Chibon 99ab6e
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issue_keys.id", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 99ab6e
    issue_uid = sa.Column(
Pierre-Yves Chibon 99ab6e
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 99ab6e
    value = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
    issue = relation(
Pierre-Yves Chibon 9c2953
        "Issue",
Pierre-Yves Chibon 9c2953
        foreign_keys=[issue_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[Issue.uid],
Pierre-Yves Chibon 99ab6e
        backref=backref(
Pierre-Yves Chibon 9c2953
            "other_fields", cascade="delete, delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 12805b
    )
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
    key = relation(
Pierre-Yves Chibon 9c2953
        "IssueKeys",
Pierre-Yves Chibon 9c2953
        foreign_keys=[key_id],
Pierre-Yves Chibon 9c2953
        remote_side=[IssueKeys.id],
Pierre-Yves Chibon 9c2953
        backref=backref("values", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 9606b4
    )
Pierre-Yves Chibon 99ab6e
Pierre-Yves Chibon 99ab6e
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 9c2953
    __tablename__ = "tags"
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon 16035c
    tag = sa.Column(sa.String(255), primary_key=True)
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
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 9c2953
    __tablename__ = "tags_issues"
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon 16035c
    tag = sa.Column(
Pierre-Yves Chibon 16035c
        sa.String(255),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("tags.tag", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 56c81e
    issue_uid = sa.Column(
Pierre-Yves Chibon fa4c05
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon 82116a
    issue = relation(
Pierre-Yves Chibon 9c2953
        "Issue",
Pierre-Yves Chibon 9c2953
        foreign_keys=[issue_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[Issue.uid],
Pierre-Yves Chibon 1b4126
        backref=backref(
Pierre-Yves Chibon 9c2953
            "old_tags", cascade="delete, delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 12805b
    )
Pierre-Yves Chibon 82116a
Pierre-Yves Chibon 8cc35c
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        return "TagIssue(issue:%s, tag:%s)" % (self.issue.id, self.tag)
Pierre-Yves Chibon 8cc35c
Pierre-Yves Chibon acf64d
Pierre-Yves Chibon 16035c
class TagColored(BASE):
Pierre-Yves Chibon 16035c
    """ Stores the colored tags.
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    Table -- tags_colored
Pierre-Yves Chibon 16035c
    """
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 9c2953
    __tablename__ = "tags_colored"
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 16035c
    tag = sa.Column(sa.String(255), nullable=False)
Pradeep CE (cep) dc15c9
    tag_description = sa.Column(sa.String(255), default="")
Pierre-Yves Chibon 16035c
    project_id = sa.Column(
Pierre-Yves Chibon 16035c
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 16035c
        nullable=False,
Pierre-Yves Chibon 16035c
    )
Pierre-Yves Chibon 16035c
    tag_color = sa.Column(sa.String(25), default="DeepSkyBlue")
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("project_id", "tag"),)
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon b4a226
        backref=backref(
Pierre-Yves Chibon 9c2953
            "tags_colored", cascade="delete,delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 16035c
    )
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        return "TagColored(id: %s, tag:%s, tag_description:%s, color:%s)" % (
Pierre-Yves Chibon 9c2953
            self.id,
Pierre-Yves Chibon 9c2953
            self.tag,
Pierre-Yves Chibon 9c2953
            self.tag_description,
Pierre-Yves Chibon 9c2953
            self.tag_color,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
class TagIssueColored(BASE):
Pierre-Yves Chibon 16035c
    """ Stores the colored tag associated with an issue.
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    Table -- tags_issues_colored
Pierre-Yves Chibon 16035c
    """
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 9c2953
    __tablename__ = "tags_issues_colored"
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    tag_id = sa.Column(
Pierre-Yves Chibon 16035c
        sa.Integer,
Pierre-Yves Chibon 16035c
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "tags_colored.id", ondelete="CASCADE", onupdate="CASCADE"
Pierre-Yves Chibon 16035c
        ),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 16035c
    issue_uid = sa.Column(
Pierre-Yves Chibon 16035c
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    issue = relation(
Pierre-Yves Chibon 9c2953
        "Issue",
Pierre-Yves Chibon 9c2953
        foreign_keys=[issue_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[Issue.uid],
Pierre-Yves Chibon 179785
        backref=backref(
Pierre-Yves Chibon 9c2953
            "tags_issues_colored", cascade="delete, delete-orphan"
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 16035c
    )
Pierre-Yves Chibon 16035c
    tag = relation(
Pierre-Yves Chibon 9c2953
        "TagColored", foreign_keys=[tag_id], remote_side=[TagColored.id]
Pierre-Yves Chibon 16035c
    )
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        return "TagIssueColored(issue:%s, tag:%s, project:%s)" % (
Pierre-Yves Chibon 9c2953
            self.issue.id,
Pierre-Yves Chibon 9c2953
            self.tag.tag,
Pierre-Yves Chibon 9c2953
            self.tag.project.fullname,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon 16035c
Pierre-Yves Chibon b96994
class TagProject(BASE):
Pierre-Yves Chibon b96994
    """ Stores the tag associated with a project.
Pierre-Yves Chibon b96994
Pierre-Yves Chibon b96994
    Table -- tags_projects
Pierre-Yves Chibon b96994
    """
Pierre-Yves Chibon b96994
Pierre-Yves Chibon 9c2953
    __tablename__ = "tags_projects"
Pierre-Yves Chibon b96994
Pierre-Yves Chibon b96994
    tag = sa.Column(
Pierre-Yves Chibon 5fd1da
        sa.String(255),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("tags.tag", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b96994
    project_id = sa.Column(
Pierre-Yves Chibon b96994
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b96994
Pierre-Yves Chibon b96994
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon b96994
        backref=backref(
Pierre-Yves Chibon 9c2953
            "tags", cascade="delete, delete-orphan", single_parent=True
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 12805b
    )
Pierre-Yves Chibon b96994
Pierre-Yves Chibon b96994
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        return "TagProject(project:%s, tag:%s)" % (
Pierre-Yves Chibon 9c2953
            self.project.fullname,
Pierre-Yves Chibon 9c2953
            self.tag,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon b96994
Pierre-Yves Chibon b96994
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 9c2953
    __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 9c2953
    title = sa.Column(sa.Text, nullable=False)
Pierre-Yves Chibon 7b8c4b
    project_id = sa.Column(
Pierre-Yves Chibon 7b8c4b
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    branch = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 85ebac
    project_id_from = sa.Column(
Pierre-Yves Chibon 85ebac
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", ondelete="SET NULL", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    remote_git = sa.Column(sa.Text(), nullable=True)
Pierre-Yves Chibon 9c2953
    branch_from = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 9c2953
    commit_start = sa.Column(sa.Text(), nullable=True)
Pierre-Yves Chibon 9c2953
    commit_stop = sa.Column(sa.Text(), nullable=True)
Pierre-Yves Chibon 9c2953
    initial_comment = sa.Column(sa.Text(), nullable=True)
Pierre-Yves Chibon 1a9d17
    user_id = sa.Column(
Pierre-Yves Chibon 1a9d17
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon acc99e
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon fe4f48
    assignee_id = sa.Column(
Pierre-Yves Chibon fe4f48
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon fe4f48
        nullable=True,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 23e9a1
    merge_status = sa.Column(
Pierre-Yves Chibon 23e9a1
        sa.Enum(
Pierre-Yves Chibon 9c2953
            "NO_CHANGE",
Pierre-Yves Chibon 9c2953
            "FFORWARD",
Pierre-Yves Chibon 9c2953
            "CONFLICTS",
Pierre-Yves Chibon 9c2953
            "MERGE",
Pierre-Yves Chibon 9c2953
            name="merge_status_enum",
Pierre-Yves Chibon e3755f
        ),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon fe4f48
Pierre-Yves Chibon e180e7
    allow_rebase = sa.Column(sa.Boolean, default=False, nullable=False)
Pierre-Yves Chibon e180e7
Pierre-Yves Chibon 1ab575
    # While present this column isn't used anywhere yet
Pierre-Yves Chibon 1ab575
    private = sa.Column(sa.Boolean, nullable=False, default=False)
Pierre-Yves Chibon 1ab575
Pierre-Yves Chibon 28e510
    status = sa.Column(
Pierre-Yves Chibon 5fd1da
        sa.String(255),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("status_pull_requests.status", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        default="Open",
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 896422
    closed_by_id = sa.Column(
Pierre-Yves Chibon 896422
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    closed_at = sa.Column(sa.DateTime, nullable=True)
Pierre-Yves Chibon 7b8c4b
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon bcd466
    updated_on = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    last_updated = sa.Column(
Pierre-Yves Chibon bcd466
        sa.DateTime,
Pierre-Yves Chibon bcd466
        nullable=False,
Pierre-Yves Chibon 9c2953
        default=datetime.datetime.utcnow,
Pierre-Yves Chibon 9c2953
        onupdate=datetime.datetime.utcnow,
Pierre-Yves Chibon 9c2953
    )
Mark Reynolds 23b4d2
Pierre-Yves Chibon a482cb
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 9c2953
        backref=backref("requests", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 9c2953
        single_parent=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon a482cb
    project_from = relation(
Pierre-Yves Chibon 9c2953
        "Project", foreign_keys=[project_id_from], remote_side=[Project.id]
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 701b83
Pierre-Yves Chibon 9c2953
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref="pull_requests",
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    assignee = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[assignee_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref="assigned_requests",
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    closed_by = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[closed_by_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref="closed_requests",
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2a35bc
Pierre-Yves Chibon 413a97
    tags = relation(
Pierre-Yves Chibon 413a97
        "TagColored",
Pierre-Yves Chibon 413a97
        secondary="tags_pull_requests",
Pierre-Yves Chibon 413a97
        primaryjoin="pull_requests.c.uid==tags_pull_requests.c.request_uid",
Pierre-Yves Chibon 413a97
        secondaryjoin="tags_pull_requests.c.tag_id==tags_colored.c.id",
Pierre-Yves Chibon 9c2953
        viewonly=True,
Pierre-Yves Chibon 413a97
    )
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 11d019
    related_issues = relation(
Pierre-Yves Chibon 11d019
        "Issue",
Pierre-Yves Chibon 11d019
        secondary="pr_to_issue",
Pierre-Yves Chibon 11d019
        primaryjoin="pull_requests.c.uid==pr_to_issue.c.pull_request_uid",
Pierre-Yves Chibon 11d019
        secondaryjoin="pr_to_issue.c.issue_uid==issues.c.uid",
Pierre-Yves Chibon 666c31
        backref=backref(
Pierre-Yves Chibon 9c2953
            "related_prs", order_by=str("pull_requests.c.id.desc()")
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 11d019
    )
Pierre-Yves Chibon 11d019
Pierre-Yves Chibon 2a35bc
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        return "PullRequest(%s, project:%s, user:%s, title:%s)" % (
Pierre-Yves Chibon 9c2953
            self.id,
Pierre-Yves Chibon 9c2953
            self.project.name,
Pierre-Yves Chibon 9c2953
            self.user.user,
Pierre-Yves Chibon 9c2953
            self.title,
Pierre-Yves Chibon 2a35bc
        )
Pierre-Yves Chibon 4e1ed8
Pierre-Yves Chibon ff841c
    @property
Pierre-Yves Chibon a53135
    def isa(self):
Pierre-Yves Chibon 9c2953
        """ A string to allow finding out that this is an pull-request. """
Pierre-Yves Chibon 9c2953
        return "pull-request"
Pierre-Yves Chibon a53135
Pierre-Yves Chibon a53135
    @property
Patrick Uiterwijk 3f97f6
    def repotype(self):
Patrick Uiterwijk 3f97f6
        """ A string returning the repotype for repopath() calls. """
Patrick Uiterwijk 3f97f6
        return "requests"
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    @property
Pierre-Yves Chibon ff841c
    def mail_id(self):
Pierre-Yves Chibon 9c2953
        """ Return a unique reprensetation of the issue as string that
Pierre-Yves Chibon ff841c
        can be used when sending emails.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return "%s-pull-request-%s" % (self.project.name, self.uid)
Pierre-Yves Chibon ff841c
Pierre-Yves Chibon 4c2be0
    @property
Pierre-Yves Chibon 413a97
    def tags_text(self):
Pierre-Yves Chibon 9c2953
        """ Return the list of tags in a simple text form. """
Pierre-Yves Chibon 413a97
        return [tag.tag for tag in self.tags]
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 413a97
    @property
Pierre-Yves Chibon 4c2be0
    def discussion(self):
Pierre-Yves Chibon 9c2953
        """ Return the list of comments related to the pull-request itself,
Pierre-Yves Chibon 4c2be0
        ie: not related to a specific commit.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return [comment for comment in self.comments if not comment.commit_id]
Pierre-Yves Chibon 4c2be0
Pierre-Yves Chibon 369b16
    @property
Michael Watters 803d72
    def flags_stats(self):
Michael Watters 803d72
        """ Return some stats about the flags associated with this PR.
Michael Watters 803d72
        """
Michael Watters 803d72
        flags = self.flags
Michael Watters 803d72
        flags.reverse()
Michael Watters 803d72
Michael Watters 803d72
        # Only keep the last flag from each service
Michael Watters 803d72
        tmp = {}
Michael Watters 803d72
        for flag in flags:
Michael Watters 803d72
            tmp[flag.username] = flag
Michael Watters 803d72
Michael Watters 803d72
        output = collections.defaultdict(list)
Michael Watters 803d72
        for flag in tmp.values():
Michael Watters 803d72
            output[flag.status].append(flag)
Michael Watters 803d72
Michael Watters 803d72
        return output
Michael Watters 803d72
Michael Watters 803d72
    @property
Pierre-Yves Chibon 369b16
    def score(self):
Pierre-Yves Chibon 9c2953
        """ Return the review score of the pull-request by checking the
Pierre-Yves Chibon 369b16
        number of +1, -1, :thumbup: and :thumbdown: in the comment of the
Pierre-Yves Chibon 369b16
        pull-request.
Pierre-Yves Chibon 369b16
        This includes only the main comments not the inline ones.
Pierre-Yves Chibon 369b16
Pierre-Yves Chibon 369b16
        An user can only give one +1 and one -1.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon aaa2d1
        votes = {}
Pierre-Yves Chibon 369b16
        for comment in self.discussion:
Pierre-Yves Chibon 9c2953
            for word in ["+1", ":thumbsup:"]:
Pierre-Yves Chibon 369b16
                if word in comment.comment:
Pierre-Yves Chibon aaa2d1
                    votes[comment.user_id] = 1
Pierre-Yves Chibon 369b16
                    break
Pierre-Yves Chibon 9c2953
            for word in ["-1", ":thumbsdown:"]:
Pierre-Yves Chibon 369b16
                if word in comment.comment:
Pierre-Yves Chibon aaa2d1
                    votes[comment.user_id] = -1
Pierre-Yves Chibon 369b16
                    break
Pierre-Yves Chibon 369b16
Pierre-Yves Chibon aaa2d1
        return sum(votes.values())
Pierre-Yves Chibon 369b16
Pierre-Yves Chibon 5d25e7
    @property
Pierre-Yves Chibon 2233c1
    def threshold_reached(self):
Pierre-Yves Chibon 2233c1
        """ Return whether the pull-request has reached the threshold above
Pierre-Yves Chibon 2233c1
        which it is allowed to be merged, if the project requests a minimal
Pierre-Yves Chibon 2233c1
        score on pull-request, otherwise returns None.
Pierre-Yves Chibon 2233c1
Pierre-Yves Chibon 2233c1
        """
Pierre-Yves Chibon 2233c1
        threshold = self.project.settings.get(
Pierre-Yves Chibon 2233c1
            "Minimum_score_to_merge_pull-request", -1
Pierre-Yves Chibon 2233c1
        )
Pierre-Yves Chibon 2233c1
        if threshold is None or threshold < 0:
Pierre-Yves Chibon 2233c1
            return None
Pierre-Yves Chibon 2233c1
        else:
Pierre-Yves Chibon 2233c1
            return int(self.score) >= int(threshold)
Pierre-Yves Chibon 2233c1
Pierre-Yves Chibon 2233c1
    @property
Pierre-Yves Chibon 5d25e7
    def remote(self):
Pierre-Yves Chibon 9c2953
        """ Return whether the current PullRequest is a remote pull-request
Pierre-Yves Chibon 5d25e7
        or not.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 1c278a
        return self.remote_git is not None
Pierre-Yves Chibon 5d25e7
farhaanbukhsh 98d820
    @property
farhaanbukhsh 98d820
    def user_comments(self):
Pierre-Yves Chibon 9c2953
        """ Return user comments only, filter it from notifications
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon f610bf
        return [
Pierre-Yves Chibon 9c2953
            comment for comment in self.comments if not comment.notification
Pierre-Yves Chibon 9c2953
        ]
farhaanbukhsh 98d820
Pierre-Yves Chibon 418e59
    def to_json(self, public=False, api=False, with_comments=True):
Pierre-Yves Chibon 9c2953
        """ Returns a dictionary representation of the pull-request.
Pierre-Yves Chibon 201d94
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 201d94
        output = {
Pierre-Yves Chibon 9c2953
            "id": self.id,
Pierre-Yves Chibon 9c2953
            "uid": self.uid,
Pierre-Yves Chibon 9c2953
            "title": self.title,
Pierre-Yves Chibon 9c2953
            "branch": self.branch,
Pierre-Yves Chibon 9c2953
            "project": self.project.to_json(public=public, api=api),
Pierre-Yves Chibon 9c2953
            "branch_from": self.branch_from,
Pierre-Yves Chibon 9c2953
            "repo_from": self.project_from.to_json(public=public, api=api)
Pierre-Yves Chibon 9c2953
            if self.project_from
Pierre-Yves Chibon 9c2953
            else None,
Pierre-Yves Chibon 9c2953
            "remote_git": self.remote_git,
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 9c2953
            "updated_on": arrow_ts(self.updated_on),
Pierre-Yves Chibon 9c2953
            "last_updated": arrow_ts(self.last_updated),
Pierre-Yves Chibon 9c2953
            "closed_at": arrow_ts(self.closed_at) if self.closed_at else None,
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 9c2953
            "assignee": self.assignee.to_json(public=public)
Pierre-Yves Chibon 9c2953
            if self.assignee
Pierre-Yves Chibon 9c2953
            else None,
Pierre-Yves Chibon 9c2953
            "status": self.status,
Pierre-Yves Chibon 9c2953
            "commit_start": self.commit_start,
Pierre-Yves Chibon 9c2953
            "commit_stop": self.commit_stop,
Pierre-Yves Chibon 9c2953
            "closed_by": self.closed_by.to_json(public=public)
Pierre-Yves Chibon 9c2953
            if self.closed_by
Pierre-Yves Chibon 9c2953
            else None,
Pierre-Yves Chibon 9c2953
            "initial_comment": self.initial_comment,
Pierre-Yves Chibon 9c2953
            "cached_merge_status": self.merge_status or "unknown",
Pierre-Yves Chibon 2233c1
            "threshold_reached": self.threshold_reached,
Pierre-Yves Chibon 31a638
            "tags": self.tags_text,
Pierre-Yves Chibon 201d94
        }
Pierre-Yves Chibon 201d94
Pierre-Yves Chibon 201d94
        comments = []
Pierre-Yves Chibon 418e59
        if with_comments:
Pierre-Yves Chibon 418e59
            for comment in self.comments:
Pierre-Yves Chibon 418e59
                comments.append(comment.to_json(public=public))
Pierre-Yves Chibon 201d94
Pierre-Yves Chibon 9c2953
        output["comments"] = comments
Pierre-Yves Chibon 201d94
Pierre-Yves Chibon ca11ed
        return output
Pierre-Yves Chibon 201d94
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 9c2953
    __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 fa4c05
        sa.String(32),
Pierre-Yves Chibon 26f048
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "pull_requests.uid", ondelete="CASCADE", onupdate="CASCADE"
Pierre-Yves Chibon e3755f
        ),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    commit_id = sa.Column(sa.String(40), nullable=True, index=True)
Pierre-Yves Chibon 26f048
    user_id = sa.Column(
Pierre-Yves Chibon 26f048
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon 26f048
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    filename = sa.Column(sa.Text, nullable=True)
Pierre-Yves Chibon 9c2953
    line = sa.Column(sa.Integer, nullable=True)
Pierre-Yves Chibon 9c2953
    tree_id = sa.Column(sa.String(40), nullable=True)
Pierre-Yves Chibon 9c2953
    comment = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 26f048
    parent_id = sa.Column(
Pierre-Yves Chibon 26f048
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("pull_request_comments.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 142774
    notification = sa.Column(sa.Boolean, default=False, nullable=False)
Pierre-Yves Chibon a9bac6
    edited_on = sa.Column(sa.DateTime, nullable=True)
farhaanbukhsh a04e02
    editor_id = sa.Column(
farhaanbukhsh a04e02
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
farhaanbukhsh a04e02
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon 9c2953
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref=backref(
Pierre-Yves Chibon 9c2953
            "pull_request_comments",
Pierre-Yves Chibon 9c2953
            order_by=str("PullRequestComment.date_created"),
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 26f048
    pull_request = relation(
Pierre-Yves Chibon 9c2953
        "PullRequest",
Pierre-Yves Chibon 424eb3
        backref=backref(
Pierre-Yves Chibon 9c2953
            "comments",
Pierre-Yves Chibon 6123e5
            cascade="delete, delete-orphan",
Pierre-Yves Chibon 9c2953
            order_by=str("PullRequestComment.date_created"),
Pierre-Yves Chibon 424eb3
        ),
Pierre-Yves Chibon 986eb3
        foreign_keys=[pull_request_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[PullRequest.uid],
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    editor = relation("User", foreign_keys=[editor_id], remote_side=[User.id])
Pierre-Yves Chibon 26f048
Lubomír Sedlář 59889e
    _reactions = sa.Column(sa.Text, nullable=True)
Lubomír Sedlář 59889e
Pierre-Yves Chibon 0a6ba6
    @property
Pierre-Yves Chibon 0a6ba6
    def mail_id(self):
Pierre-Yves Chibon 9c2953
        """ Return a unique representation of the issue as string that
Pierre-Yves Chibon 0a6ba6
        can be used when sending emails.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return "%s-pull-request-%s-%s" % (
Pierre-Yves Chibon 9c2953
            self.pull_request.project.name,
Pierre-Yves Chibon 9c2953
            self.pull_request.uid,
Pierre-Yves Chibon 9c2953
            self.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 0a6ba6
Pierre-Yves Chibon e14912
    @property
Pierre-Yves Chibon e14912
    def parent(self):
Pierre-Yves Chibon 9c2953
        """ Return the parent, in this case the pull_request object. """
Pierre-Yves Chibon e14912
        return self.pull_request
Pierre-Yves Chibon e14912
Lubomír Sedlář 59889e
    @property
Lubomír Sedlář 59889e
    def reactions(self):
Pierre-Yves Chibon 9c2953
        """ Return the reactions stored as a string in the database parsed as
Lubomír Sedlář 59889e
        an actual dict object.
Pierre-Yves Chibon 9c2953
        """
Lubomír Sedlář 59889e
        if self._reactions:
Lubomír Sedlář 59889e
            return json.loads(self._reactions)
Lubomír Sedlář 59889e
        return {}
Lubomír Sedlář 59889e
Lubomír Sedlář 59889e
    @reactions.setter
Lubomír Sedlář 59889e
    def reactions(self, reactions):
Pierre-Yves Chibon 9c2953
        """ Ensures that reactions are properly saved. """
Lubomír Sedlář 59889e
        self._reactions = json.dumps(reactions)
Lubomír Sedlář 59889e
Pierre-Yves Chibon 418e59
    def to_json(self, public=False):
Pierre-Yves Chibon 9c2953
        """ Return a dict representation of the pull-request comment. """
Pierre-Yves Chibon 0074a5
Pierre-Yves Chibon 418e59
        return {
Pierre-Yves Chibon 9c2953
            "id": self.id,
Pierre-Yves Chibon 9c2953
            "commit": self.commit_id,
Pierre-Yves Chibon 9c2953
            "tree": self.tree_id,
Pierre-Yves Chibon 9c2953
            "filename": self.filename,
Pierre-Yves Chibon 9c2953
            "line": self.line,
Pierre-Yves Chibon 9c2953
            "comment": self.comment,
Pierre-Yves Chibon 9c2953
            "parent": self.parent_id,
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 9c2953
            "edited_on": arrow_ts(self.edited_on) if self.edited_on else None,
Pierre-Yves Chibon 9c2953
            "editor": self.editor.to_json(public=public)
Pierre-Yves Chibon 9c2953
            if self.editor_id
Pierre-Yves Chibon 9c2953
            else None,
Pierre-Yves Chibon 9c2953
            "notification": self.notification,
Pierre-Yves Chibon 9c2953
            "reactions": self.reactions,
Pierre-Yves Chibon 418e59
        }
Pierre-Yves Chibon 418e59
Pierre-Yves Chibon 26f048
Pierre-Yves Chibon c48bfd
class PullRequestFlag(BASE):
Pierre-Yves Chibon c48bfd
    """ Stores the flags attached to a pull-request.
Pierre-Yves Chibon c48bfd
Pierre-Yves Chibon 1c0618
    Table -- pull_request_flags
Pierre-Yves Chibon c48bfd
    """
Pierre-Yves Chibon c48bfd
Pierre-Yves Chibon 9c2953
    __tablename__ = "pull_request_flags"
Pierre-Yves Chibon c48bfd
Pierre-Yves Chibon c48bfd
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon dc4c19
    uid = sa.Column(sa.String(32), nullable=False)
Pierre-Yves Chibon c48bfd
    pull_request_uid = sa.Column(
Pierre-Yves Chibon fa4c05
        sa.String(32),
Pierre-Yves Chibon c48bfd
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "pull_requests.uid", ondelete="CASCADE", onupdate="CASCADE"
Pierre-Yves Chibon e3755f
        ),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon ba2b1c
    token_id = sa.Column(
Pierre-Yves Chibon 2659eb
        sa.String(64),
Pierre-Yves Chibon 2659eb
        sa.ForeignKey("tokens.id", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 4104e6
        nullable=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    status = sa.Column(sa.String(32), nullable=False)
Pierre-Yves Chibon c48bfd
    user_id = sa.Column(
Pierre-Yves Chibon c48bfd
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon c48bfd
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    username = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 9c2953
    percent = sa.Column(sa.Integer(), nullable=True)
Pierre-Yves Chibon 9c2953
    comment = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 9c2953
    url = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon c48bfd
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 669c00
    date_updated = sa.Column(
Pierre-Yves Chibon 669c00
        sa.DateTime,
Pierre-Yves Chibon 669c00
        nullable=False,
Pierre-Yves Chibon 669c00
        default=datetime.datetime.utcnow,
Pierre-Yves Chibon 669c00
        onupdate=datetime.datetime.utcnow,
Pierre-Yves Chibon 669c00
    )
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("uid", "pull_request_uid"),)
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon c48bfd
        backref=backref(
Pierre-Yves Chibon 9c2953
            "pull_request_flags", order_by=str("PullRequestFlag.date_created")
Pierre-Yves Chibon c48bfd
        ),
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    pull_request = relation(
Pierre-Yves Chibon 9c2953
        "PullRequest",
Pierre-Yves Chibon f10b6f
        backref=backref(
Pierre-Yves Chibon f10b6f
            "flags",
Pierre-Yves Chibon f10b6f
            order_by=str("(pull_request_flags.c.date_created).desc()"),
Pierre-Yves Chibon 998b35
            cascade="delete, delete-orphan",
Pierre-Yves Chibon f10b6f
        ),
Pierre-Yves Chibon c48bfd
        foreign_keys=[pull_request_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[PullRequest.uid],
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon c48bfd
Pierre-Yves Chibon 327b88
    @property
Pierre-Yves Chibon 327b88
    def mail_id(self):
Pierre-Yves Chibon 9c2953
        """ Return a unique representation of the flag as string that
Pierre-Yves Chibon 327b88
        can be used when sending emails.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return "%s-pull-request-%s-%s" % (
Pierre-Yves Chibon 9c2953
            self.pull_request.project.name,
Pierre-Yves Chibon 9c2953
            self.pull_request.uid,
Pierre-Yves Chibon 9c2953
            self.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 327b88
Pierre-Yves Chibon 7d7b37
    def to_json(self, public=False):
Pierre-Yves Chibon 9c2953
        """ Returns a dictionary representation of the pull-request.
Pierre-Yves Chibon 7d7b37
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 7d7b37
        output = {
Pierre-Yves Chibon 9c2953
            "pull_request_uid": self.pull_request_uid,
Pierre-Yves Chibon 9c2953
            "username": self.username,
Pierre-Yves Chibon 9c2953
            "percent": self.percent,
Pierre-Yves Chibon 9c2953
            "comment": self.comment,
Pierre-Yves Chibon 9c2953
            "status": self.status,
Pierre-Yves Chibon 9c2953
            "url": self.url,
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 669c00
            "date_updated": arrow_ts(self.date_updated),
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 7d7b37
        }
Pierre-Yves Chibon 7d7b37
Pierre-Yves Chibon 7d7b37
        return output
Pierre-Yves Chibon 7d7b37
Pierre-Yves Chibon c48bfd
Pierre-Yves Chibon 8715b5
class CommitFlag(BASE):
Pierre-Yves Chibon 8715b5
    """ Stores the flags attached to a commit.
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    Table -- commit_flags
Pierre-Yves Chibon 8715b5
    """
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 9c2953
    __tablename__ = "commit_flags"
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 8715b5
    commit_hash = sa.Column(sa.String(40), index=True, nullable=False)
Pierre-Yves Chibon 8715b5
    project_id = sa.Column(
Pierre-Yves Chibon 8715b5
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 8715b5
    token_id = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.String(64), sa.ForeignKey("tokens.id"), nullable=False
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 8715b5
    user_id = sa.Column(
Pierre-Yves Chibon 8715b5
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon 8715b5
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon dc4c19
    uid = sa.Column(sa.String(32), nullable=False)
Pierre-Yves Chibon 9c2953
    status = sa.Column(sa.String(32), nullable=False)
Pierre-Yves Chibon 9c2953
    username = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 9c2953
    percent = sa.Column(sa.Integer(), nullable=True)
Pierre-Yves Chibon 9c2953
    comment = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 9c2953
    url = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 669c00
    date_updated = sa.Column(
Pierre-Yves Chibon 669c00
        sa.DateTime,
Pierre-Yves Chibon 669c00
        nullable=False,
Pierre-Yves Chibon 669c00
        default=datetime.datetime.utcnow,
Pierre-Yves Chibon 669c00
        onupdate=datetime.datetime.utcnow,
Pierre-Yves Chibon 669c00
    )
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("commit_hash", "uid"),)
Pierre-Yves Chibon dc4c19
Pierre-Yves Chibon 327b88
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 9c2953
        backref=backref("commit_flags", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 9c2953
        single_parent=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 327b88
Pierre-Yves Chibon 666c31
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 666c31
        foreign_keys=[user_id],
Pierre-Yves Chibon 666c31
        remote_side=[User.id],
Pierre-Yves Chibon 666c31
        backref=backref(
Pierre-Yves Chibon 9c2953
            "commit_flags", order_by=str("CommitFlag.date_created")
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 666c31
    )
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 327b88
    @property
Pierre-Yves Chibon 327b88
    def isa(self):
Pierre-Yves Chibon 9c2953
        """ A string to allow finding out that this is a commit flag. """
Pierre-Yves Chibon 9c2953
        return "commit-flag"
Pierre-Yves Chibon 327b88
Pierre-Yves Chibon 327b88
    @property
Pierre-Yves Chibon 327b88
    def mail_id(self):
Pierre-Yves Chibon 9c2953
        """ Return a unique representation of the flag as string that
Pierre-Yves Chibon 327b88
        can be used when sending emails.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return "%s-commit-%s-%s" % (
Pierre-Yves Chibon 9c2953
            self.project.name,
Pierre-Yves Chibon 9c2953
            self.project.id,
Pierre-Yves Chibon 9c2953
            self.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 327b88
Pierre-Yves Chibon 8715b5
    def to_json(self, public=False):
Pierre-Yves Chibon 9c2953
        """ Returns a dictionary representation of the commit flag.
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 8715b5
        output = {
Pierre-Yves Chibon 9c2953
            "commit_hash": self.commit_hash,
Pierre-Yves Chibon 9c2953
            "username": self.username,
Pierre-Yves Chibon 9c2953
            "percent": self.percent,
Pierre-Yves Chibon 9c2953
            "comment": self.comment,
Pierre-Yves Chibon 9c2953
            "status": self.status,
Pierre-Yves Chibon 9c2953
            "url": self.url,
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 669c00
            "date_updated": arrow_ts(self.date_updated),
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 8715b5
        }
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
        return output
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 8715b5
Pierre-Yves Chibon 413a97
class TagPullRequest(BASE):
Pierre-Yves Chibon 413a97
    """ Stores the tag associated with an pull-request.
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 413a97
    Table -- tags_pull_requests
Pierre-Yves Chibon 413a97
    """
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 9c2953
    __tablename__ = "tags_pull_requests"
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 413a97
    tag_id = sa.Column(
Pierre-Yves Chibon 413a97
        sa.Integer,
Pierre-Yves Chibon 413a97
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "tags_colored.id", ondelete="CASCADE", onupdate="CASCADE"
Pierre-Yves Chibon 413a97
        ),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 413a97
    request_uid = sa.Column(
Pierre-Yves Chibon 413a97
        sa.String(32),
Pierre-Yves Chibon 413a97
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "pull_requests.uid", ondelete="CASCADE", onupdate="CASCADE"
Pierre-Yves Chibon 413a97
        ),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    date_created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 413a97
    pull_request = relation(
Pierre-Yves Chibon 9c2953
        "PullRequest",
Pierre-Yves Chibon 413a97
        foreign_keys=[request_uid],
Pierre-Yves Chibon 413a97
        remote_side=[PullRequest.uid],
Pierre-Yves Chibon 9c2953
        backref=backref("tags_pr_colored", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 413a97
    )
Pierre-Yves Chibon 413a97
    tag = relation(
Pierre-Yves Chibon 9c2953
        "TagColored", foreign_keys=[tag_id], remote_side=[TagColored.id]
Pierre-Yves Chibon 413a97
    )
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 413a97
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        return "TagPullRequest(PR:%s, tag:%s)" % (
Pierre-Yves Chibon 9c2953
            self.pull_request.id,
Pierre-Yves Chibon 9c2953
            self.tag,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon 413a97
Pierre-Yves Chibon ad9df5
class PagureGroupType(BASE):
Pierre-Yves Chibon ad9df5
    """
Pierre-Yves Chibon ad9df5
    A list of the type a group can have definition.
Pierre-Yves Chibon ad9df5
    """
Pierre-Yves Chibon ad9df5
Pierre-Yves Chibon ad9df5
    # names like "Group", "Order" and "User" are reserved words in SQL
Pierre-Yves Chibon ad9df5
    # so we set the name to something safe for SQL
Pierre-Yves Chibon 9c2953
    __tablename__ = "pagure_group_type"
Pierre-Yves Chibon ad9df5
Pierre-Yves Chibon c2a75b
    group_type = sa.Column(sa.String(16), primary_key=True)
Pierre-Yves Chibon ad9df5
    created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon ad9df5
Pierre-Yves Chibon ad9df5
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        """ Return a string representation of this object. """
Pierre-Yves Chibon ad9df5
Pierre-Yves Chibon 9c2953
        return "GroupType: %s" % (self.group_type)
Pierre-Yves Chibon ad9df5
Pierre-Yves Chibon ad9df5
Pierre-Yves Chibon fe5017
class PagureGroup(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 9c2953
    __tablename__ = "pagure_group"
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon c2701e
    group_name = sa.Column(sa.String(255), nullable=False, unique=True)
Pierre-Yves Chibon bb29e1
    display_name = sa.Column(sa.String(255), nullable=False, unique=True)
Pierre-Yves Chibon bb29e1
    description = sa.Column(sa.String(255), nullable=True)
Pierre-Yves Chibon c2a75b
    group_type = sa.Column(
Pierre-Yves Chibon ad9df5
        sa.String(16),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("pagure_group_type.group_type"),
Pierre-Yves Chibon 9c2953
        default="user",
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon c1542e
    user_id = sa.Column(
Pierre-Yves Chibon c1542e
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon c1542e
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2b1b47
    created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon c1542e
    creator = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon c1542e
        foreign_keys=[user_id],
Pierre-Yves Chibon c1542e
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref=backref("groups_created"),
Pierre-Yves Chibon c1542e
    )
Pierre-Yves Chibon c1542e
Pierre-Yves Chibon 2b1b47
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        """ Return a string representation of this object. """
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 9c2953
        return "Group: %s - name %s" % (self.id, self.group_name)
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 7702dd
    def to_json(self, public=False):
Pierre-Yves Chibon 9c2953
        """ Returns a dictionary representation of the pull-request.
Pierre-Yves Chibon 7702dd
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 7702dd
        output = {
Pierre-Yves Chibon 9c2953
            "name": self.group_name,
Pierre-Yves Chibon 9c2953
            "display_name": self.display_name,
Pierre-Yves Chibon 9c2953
            "description": self.description,
Pierre-Yves Chibon 9c2953
            "group_type": self.group_type,
Pierre-Yves Chibon 9c2953
            "creator": self.creator.to_json(public=public),
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.created),
Pierre-Yves Chibon 9c2953
            "members": [user.username for user in self.users],
Pierre-Yves Chibon 7702dd
        }
Pierre-Yves Chibon 7702dd
Pierre-Yves Chibon 7702dd
        return output
Pierre-Yves Chibon 7702dd
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon b08582
class ProjectGroup(BASE):
Pierre-Yves Chibon b08582
    """
Pierre-Yves Chibon b08582
    Association table linking the projects table to the pagure_group table.
Pierre-Yves Chibon b08582
    This allow linking projects to groups.
Pierre-Yves Chibon b08582
    """
Pierre-Yves Chibon b08582
Pierre-Yves Chibon 9c2953
    __tablename__ = "projects_groups"
Pierre-Yves Chibon b08582
Pierre-Yves Chibon b08582
    project_id = sa.Column(
Pierre-Yves Chibon 69599a
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 9c2953
        primary_key=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b08582
    group_id = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.Integer, sa.ForeignKey("pagure_group.id"), primary_key=True
Pierre-Yves Chibon 9c2953
    )
Vivek Anand 967335
    access = sa.Column(
Vivek Anand 967335
        sa.String(255),
Vivek Anand 967335
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "access_levels.access", onupdate="CASCADE", ondelete="CASCADE"
Vivek Anand 967335
        ),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b08582
Vivek Anand 2847f5
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon b4a226
        backref=backref(
Pierre-Yves Chibon 9c2953
            "projects_groups",
Pierre-Yves Chibon 9c2953
            cascade="delete,delete-orphan",
Pierre-Yves Chibon 9c2953
            single_parent=True,
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon b4a226
    )
Vivek Anand 2847f5
Pierre-Yves Chibon 9c2953
    group = relation("PagureGroup", backref="projects_groups")
Vivek Anand 2847f5
Pierre-Yves Chibon b08582
    # Constraints
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("project_id", "group_id"),)
Pierre-Yves Chibon f610bf
Pierre-Yves Chibon b08582
Vivek Anand f019f5
class Star(BASE):
Vivek Anand f019f5
    """ Stores users association with the all the projects which
Vivek Anand f019f5
    they have starred
Vivek Anand f019f5
Vivek Anand f019f5
    Table -- star
Vivek Anand f019f5
    """
Vivek Anand f019f5
Pierre-Yves Chibon 9c2953
    __tablename__ = "stargazers"
Vivek Anand f019f5
    __table_args__ = (
Vivek Anand ed7323
        sa.UniqueConstraint(
Pierre-Yves Chibon 9c2953
            "project_id",
Pierre-Yves Chibon 9c2953
            "user_id",
Pierre-Yves Chibon 9c2953
            name="uq_stargazers_project_id_user_id_key",
Pierre-Yves Chibon 9c2953
        ),
Vivek Anand f019f5
    )
Vivek Anand f019f5
Vivek Anand f019f5
    id = sa.Column(sa.Integer, primary_key=True)
Vivek Anand f019f5
    project_id = sa.Column(
Vivek Anand f019f5
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
Vivek Anand 0b85e6
        nullable=False,
Vivek Anand 0b85e6
        index=True,
Vivek Anand 0b85e6
    )
Vivek Anand f019f5
    user_id = sa.Column(
Vivek Anand f019f5
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
Vivek Anand f019f5
        nullable=False,
Vivek Anand f019f5
    )
Vivek Anand f019f5
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref=backref("stars", cascade="delete, delete-orphan"),
Vivek Anand f019f5
    )
Vivek Anand f019f5
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 9c2953
        backref=backref("stargazers", cascade="delete, delete-orphan"),
Vivek Anand f019f5
    )
Vivek Anand f019f5
Vivek Anand f019f5
Gaurav Kumar 00f2c8
class Watcher(BASE):
Gaurav Kumar 00f2c8
    """ Stores the user of a projects.
Gaurav Kumar 00f2c8
Gaurav Kumar 00f2c8
    Table -- watchers
Gaurav Kumar 00f2c8
    """
Gaurav Kumar 00f2c8
Pierre-Yves Chibon 9c2953
    __tablename__ = "watchers"
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("project_id", "user_id"),)
Gaurav Kumar 00f2c8
Gaurav Kumar 00f2c8
    id = sa.Column(sa.Integer, primary_key=True)
Gaurav Kumar 00f2c8
    project_id = sa.Column(
Gaurav Kumar 00f2c8
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Gaurav Kumar 00f2c8
    user_id = sa.Column(
Gaurav Kumar 00f2c8
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Gaurav Kumar 00f2c8
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Matt Prahl b49f93
    watch_issues = sa.Column(sa.Boolean, nullable=False, default=False)
Matt Prahl b49f93
    watch_commits = sa.Column(sa.Boolean, nullable=False, default=False)
Gaurav Kumar 00f2c8
Gaurav Kumar 00f2c8
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref=backref("watchers", cascade="delete, delete-orphan"),
Gaurav Kumar 00f2c8
    )
Gaurav Kumar 00f2c8
Gaurav Kumar 00f2c8
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 9c2953
        backref=backref("watchers", cascade="delete, delete-orphan"),
Gaurav Kumar 00f2c8
    )
Gaurav Kumar 00f2c8
Pierre-Yves Chibon 5b6014
Aurélien Bompard 619e2a
@six.python_2_unicode_compatible
Pierre-Yves Chibon 5b6014
class PagureLog(BASE):
Pierre-Yves Chibon 5b6014
    """
Pierre-Yves Chibon 5b6014
    Log user's actions.
Pierre-Yves Chibon 5b6014
    """
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
    __tablename__ = "pagure_logs"
Pierre-Yves Chibon 5b6014
Pierre-Yves Chibon 5b6014
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 5b6014
    user_id = sa.Column(
Pierre-Yves Chibon 5b6014
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 0d2315
        nullable=True,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    user_email = sa.Column(sa.String(255), nullable=True, index=True)
Pierre-Yves Chibon 5b6014
    project_id = sa.Column(
Pierre-Yves Chibon 5b6014
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 5b6014
        nullable=True,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 5b6014
    )
Pierre-Yves Chibon 5b6014
    issue_uid = sa.Column(
Pierre-Yves Chibon 5b6014
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", ondelete="CASCADE", onupdate="CASCADE"),
Pierre-Yves Chibon 5b6014
        nullable=True,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 5b6014
    )
Pierre-Yves Chibon 5b6014
    pull_request_uid = sa.Column(
Pierre-Yves Chibon 5b6014
        sa.String(32),
Pierre-Yves Chibon 5b6014
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "pull_requests.uid", ondelete="CASCADE", onupdate="CASCADE"
Pierre-Yves Chibon 5b6014
        ),
Pierre-Yves Chibon 5b6014
        nullable=True,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 5b6014
    )
Pierre-Yves Chibon 810c3d
    log_type = sa.Column(sa.Text, nullable=False)
Pierre-Yves Chibon 86a79b
    ref_id = sa.Column(sa.Text, nullable=False)
Pierre-Yves Chibon 5b6014
    date = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.Date, nullable=False, default=datetime.datetime.utcnow, index=True
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 5b6014
    date_created = sa.Column(
Pierre-Yves Chibon 5b6014
        sa.DateTime,
Pierre-Yves Chibon 5b6014
        nullable=False,
Pierre-Yves Chibon 5b6014
        default=datetime.datetime.utcnow,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 5b6014
Pierre-Yves Chibon 5b6014
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref=backref("logs", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 5b6014
    )
Pierre-Yves Chibon 5b6014
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 9c2953
        backref=backref("logs", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 5b6014
    )
Pierre-Yves Chibon 5b6014
    issue = relation(
Pierre-Yves Chibon 9c2953
        "Issue", foreign_keys=[issue_uid], remote_side=[Issue.uid]
Pierre-Yves Chibon 5b6014
    )
Pierre-Yves Chibon 5b6014
    pull_request = relation(
Pierre-Yves Chibon 9c2953
        "PullRequest",
Pierre-Yves Chibon 5b6014
        foreign_keys=[pull_request_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[PullRequest.uid],
Pierre-Yves Chibon 5b6014
    )
Pierre-Yves Chibon 5b6014
Pierre-Yves Chibon 79835c
    def to_json(self, public=False):
Pierre-Yves Chibon 9c2953
        """ Returns a dictionary representation of the issue.
Pierre-Yves Chibon 79835c
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 79835c
        output = {
Pierre-Yves Chibon 9c2953
            "id": self.id,
Pierre-Yves Chibon 9c2953
            "type": self.log_type,
Pierre-Yves Chibon 9c2953
            "ref_id": self.ref_id,
Pierre-Yves Chibon 9c2953
            "date": self.date.strftime("%Y-%m-%d"),
Pierre-Yves Chibon 9c2953
            "date_created": arrow_ts(self.date_created),
Pierre-Yves Chibon 9c2953
            "user": self.user.to_json(public=public),
Pierre-Yves Chibon 79835c
        }
Pierre-Yves Chibon 79835c
        return output
Pierre-Yves Chibon 79835c
Pierre-Yves Chibon 86a79b
    def __str__(self):
Pierre-Yves Chibon 9c2953
        """ A string representation of this log entry. """
Pierre-Yves Chibon 9c2953
        verb = ""
Pierre-Yves Chibon 9c2953
        desc = "%(user)s %(verb)s %(project)s#%(obj_id)s"
Pierre-Yves Chibon 86a79b
        arg = {
Pierre-Yves Chibon 9c2953
            "user": self.user.user if self.user else self.user_email,
Pierre-Yves Chibon 9c2953
            "obj_id": self.ref_id,
Pierre-Yves Chibon 9c2953
            "project": self.project.fullname,
Pierre-Yves Chibon 86a79b
        }
Pierre-Yves Chibon 86a79b
Vibhor Verma 921910
        issue_verb = {
Pierre-Yves Chibon 9c2953
            "created": "created issue",
Pierre-Yves Chibon 9c2953
            "commented": "commented on issue",
Pierre-Yves Chibon 9c2953
            "close": "closed issue",
Pierre-Yves Chibon 9c2953
            "open": "opened issue",
Vibhor Verma 8e11a7
        }
Vibhor Verma 8e11a7
Vibhor Verma 921910
        pr_verb = {
Pierre-Yves Chibon 9c2953
            "created": "created PR",
Pierre-Yves Chibon 9c2953
            "commented": "commented on PR",
Pierre-Yves Chibon 9c2953
            "closed": "closed PR",
Pierre-Yves Chibon 9c2953
            "merged": "merged PR",
Vibhor Verma 8e11a7
        }
Vibhor Verma 8e11a7
Vibhor Verma 86e781
        if self.issue and self.log_type in issue_verb:
Vibhor Verma 8e11a7
            verb = issue_verb[self.log_type]
Vibhor Verma 86e781
        elif self.pull_request and self.log_type in pr_verb:
Vibhor Verma 8e11a7
            verb = pr_verb[self.log_type]
Pierre-Yves Chibon 9c2953
        elif (
Pierre-Yves Chibon 9c2953
            not self.pull_request
Pierre-Yves Chibon 9c2953
            and not self.issue
Pierre-Yves Chibon 9c2953
            and self.log_type == "created"
Pierre-Yves Chibon 9c2953
        ):
Pierre-Yves Chibon 9c2953
            verb = "created Project"
Pierre-Yves Chibon 9c2953
            desc = "%(user)s %(verb)s %(project)s"
Pierre-Yves Chibon 9c2953
        elif self.log_type == "committed":
Pierre-Yves Chibon 9c2953
            verb = "committed on"
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
        arg["verb"] = verb
Pierre-Yves Chibon 86a79b
Pierre-Yves Chibon 86a79b
        return desc % arg
Pierre-Yves Chibon 86a79b
Pierre-Yves Chibon 9c2953
    def date_tz(self, tz="UTC"):
Pierre-Yves Chibon 9c2953
        """Returns the date (as a datetime.date()) of this log entry
Adam Williamson 8a161a
        in a specified timezone (Olson name as a string). Assumes that
Adam Williamson 8a161a
        date_created is aware, or UTC. If tz isn't a valid timezone
Adam Williamson 8a161a
        identifier for arrow, just returns the date component of
Adam Williamson 8a161a
        date_created.
Pierre-Yves Chibon 9c2953
        """
Adam Williamson 8a161a
        try:
Adam Williamson 8a161a
            return arrow.get(self.date_created).to(tz).date()
Adam Williamson 8a161a
        except arrow.parser.ParserError:
Adam Williamson 8a161a
            return self.date_created.date()
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon c0c94a
Pierre-Yves Chibon 71b551
class IssueWatcher(BASE):
Pierre-Yves Chibon 71b551
    """ Stores the users watching issues.
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    Table -- issue_watchers
Pierre-Yves Chibon 71b551
    """
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 9c2953
    __tablename__ = "issue_watchers"
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("issue_uid", "user_id"),)
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 71b551
    issue_uid = sa.Column(
Pierre-Yves Chibon cb035c
        sa.String(32),
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("issues.uid", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 71b551
    user_id = sa.Column(
Pierre-Yves Chibon 71b551
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 71b551
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    watch = sa.Column(sa.Boolean, nullable=False)
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref=backref("issue_watched", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 71b551
    )
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    issue = relation(
Pierre-Yves Chibon 9c2953
        "Issue",
Pierre-Yves Chibon 9c2953
        foreign_keys=[issue_uid],
Pierre-Yves Chibon 9c2953
        remote_side=[Issue.uid],
Pierre-Yves Chibon 9c2953
        backref=backref("watchers", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 71b551
    )
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
class PullRequestWatcher(BASE):
Pierre-Yves Chibon 71b551
    """ Stores the users watching issues.
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    Table -- pull_request_watchers
Pierre-Yves Chibon 71b551
    """
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 9c2953
    __tablename__ = "pull_request_watchers"
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("pull_request_uid", "user_id"),)
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 71b551
    pull_request_uid = sa.Column(
Pierre-Yves Chibon cb035c
        sa.String(32),
Pierre-Yves Chibon 71b551
        sa.ForeignKey(
Pierre-Yves Chibon 9c2953
            "pull_requests.uid", onupdate="CASCADE", ondelete="CASCADE"
Pierre-Yves Chibon 9c2953
        ),
Pierre-Yves Chibon 9c2953
        nullable=False,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 71b551
    user_id = sa.Column(
Pierre-Yves Chibon 71b551
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
Pierre-Yves Chibon 71b551
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    watch = sa.Column(sa.Boolean, nullable=False)
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon 9c2953
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
        backref=backref("pr_watched", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 71b551
    )
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
    pull_request = relation(
Pierre-Yves Chibon 9c2953
        "PullRequest",
Pierre-Yves Chibon 9606b4
        foreign_keys=[pull_request_uid],
Pierre-Yves Chibon 9606b4
        remote_side=[PullRequest.uid],
Pierre-Yves Chibon 9c2953
        backref=backref("watchers", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 71b551
    )
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 71b551
Pierre-Yves Chibon 09a30d
#
Pierre-Yves Chibon 09a30d
# Class and tables specific for the API/token access
Pierre-Yves Chibon 09a30d
#
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 2a7216
Pierre-Yves Chibon 09a30d
class ACL(BASE):
Pierre-Yves Chibon 09a30d
    """
Pierre-Yves Chibon 09a30d
    Table listing all the rights a token can be given
Pierre-Yves Chibon 09a30d
    """
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 9c2953
    __tablename__ = "acls"
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 09a30d
    name = sa.Column(sa.String(32), unique=True, nullable=False)
Pierre-Yves Chibon 09a30d
    description = sa.Column(sa.Text(), nullable=False)
Pierre-Yves Chibon 09a30d
    created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        """ Return a string representation of this object. """
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 9c2953
        return "ACL: %s - name %s" % (self.id, self.name)
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
class Token(BASE):
Pierre-Yves Chibon 09a30d
    """
Pierre-Yves Chibon 09a30d
    Table listing all the tokens per user and per project
Pierre-Yves Chibon 09a30d
    """
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 9c2953
    __tablename__ = "tokens"
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
    id = sa.Column(sa.String(64), primary_key=True)
Pierre-Yves Chibon 09a30d
    user_id = sa.Column(
Pierre-Yves Chibon 09a30d
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("users.id", onupdate="CASCADE"),
Pierre-Yves Chibon 09a30d
        nullable=False,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 09a30d
    project_id = sa.Column(
Pierre-Yves Chibon 09a30d
        sa.Integer,
Pierre-Yves Chibon 9c2953
        sa.ForeignKey("projects.id", onupdate="CASCADE"),
Pierre-Yves Chibon 1e5d3a
        nullable=True,
Pierre-Yves Chibon 9c2953
        index=True,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 60a786
    description = sa.Column(sa.Text(), nullable=True)
Pierre-Yves Chibon 09a30d
    expiration = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 09a30d
    created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
    acls = relation(
Pierre-Yves Chibon 09a30d
        "ACL",
Pierre-Yves Chibon 09a30d
        secondary="tokens_acls",
Pierre-Yves Chibon 09a30d
        primaryjoin="tokens.c.id==tokens_acls.c.token_id",
Pierre-Yves Chibon 09a30d
        secondaryjoin="acls.c.id==tokens_acls.c.acl_id",
Pierre-Yves Chibon 09a30d
    )
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon c6d233
    user = relation(
Pierre-Yves Chibon 9c2953
        "User",
Pierre-Yves Chibon c6d233
        backref=backref(
Pierre-Yves Chibon 9c2953
            "tokens",
Pierre-Yves Chibon 9c2953
            cascade="delete, delete-orphan",
Pierre-Yves Chibon 9c2953
            order_by=str("Token.created"),
Pierre-Yves Chibon c6d233
        ),
Pierre-Yves Chibon c6d233
        foreign_keys=[user_id],
Pierre-Yves Chibon 9c2953
        remote_side=[User.id],
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon c6d233
Pierre-Yves Chibon 1d9dd7
    project = relation(
Pierre-Yves Chibon 9c2953
        "Project",
Pierre-Yves Chibon 9c2953
        backref=backref("tokens", cascade="delete, delete-orphan"),
Pierre-Yves Chibon 1d9dd7
        foreign_keys=[project_id],
Pierre-Yves Chibon 9c2953
        remote_side=[Project.id],
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 1d9dd7
Pierre-Yves Chibon 09a30d
    def __repr__(self):
Pierre-Yves Chibon 9c2953
        """ Return a string representation of this object. """
Pierre-Yves Chibon 09a30d
Fabien Boucher b0c868
        return "Token: %s - name %s - expiration: %s" % (
Pierre-Yves Chibon 4104e6
            self.id,
Pierre-Yves Chibon 4104e6
            self.description,
Pierre-Yves Chibon 4104e6
            self.expiration,
Pierre-Yves Chibon 4104e6
        )
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon e82230
    @property
Pierre-Yves Chibon e82230
    def expired(self):
Pierre-Yves Chibon 9c2953
        """ Returns whether a token has expired or not. """
Pierre-Yves Chibon 7db430
        if datetime.datetime.utcnow().date() >= self.expiration.date():
Pierre-Yves Chibon e82230
            return True
Pierre-Yves Chibon e82230
        else:
Pierre-Yves Chibon e82230
            return False
Pierre-Yves Chibon e82230
Pierre-Yves Chibon f32aa5
    @property
Pierre-Yves Chibon f32aa5
    def acls_list(self):
Pierre-Yves Chibon 9c2953
        """ Return a list containing the name of each ACLs this token has.
Pierre-Yves Chibon 9c2953
        """
Aurélien Bompard 619e2a
        return sorted(["%s" % acl.name for acl in self.acls])
Pierre-Yves Chibon f32aa5
Lubomír Sedlář f8b879
    @property
Lubomír Sedlář f8b879
    def acls_list_pretty(self):
Pierre-Yves Chibon 9c2953
        """
Lubomír Sedlář f8b879
        Return a list containing the description of each ACLs this token has.
Pierre-Yves Chibon 9c2953
        """
Pierre-Yves Chibon 9c2953
        return [
Pierre-Yves Chibon 9c2953
            acl.description
Pierre-Yves Chibon 9c2953
            for acl in sorted(self.acls, key=operator.attrgetter("name"))
Pierre-Yves Chibon 9c2953
        ]
Lubomír Sedlář f8b879
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
class TokenAcl(BASE):
Pierre-Yves Chibon 09a30d
    """
Pierre-Yves Chibon 09a30d
    Association table linking the tokens table to the acls table.
Pierre-Yves Chibon 09a30d
    This allow linking token to acl.
Pierre-Yves Chibon 09a30d
    """
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 9c2953
    __tablename__ = "tokens_acls"
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
    token_id = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.String(64), sa.ForeignKey("tokens.id"), primary_key=True
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 9c2953
    acl_id = sa.Column(sa.Integer, sa.ForeignKey("acls.id"), primary_key=True)
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon 09a30d
    # Constraints
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("token_id", "acl_id"),)
Pierre-Yves Chibon 09a30d
Pierre-Yves Chibon b08582
Pierre-Yves Chibon b08582
# ##########################################################
Pierre-Yves Chibon b08582
# These classes are only used if you're using the `local`
Pierre-Yves Chibon b08582
#                  authentication method
Pierre-Yves Chibon b08582
# ##########################################################
Pierre-Yves Chibon b08582
Pierre-Yves Chibon b08582
Pierre-Yves Chibon b08582
class PagureUserVisit(BASE):
Pierre-Yves Chibon b08582
    """
Pierre-Yves Chibon b08582
    Table storing the visits of the user.
Pierre-Yves Chibon b08582
    """
Pierre-Yves Chibon b08582
Pierre-Yves Chibon 9c2953
    __tablename__ = "pagure_user_visit"
Pierre-Yves Chibon b08582
Pierre-Yves Chibon b08582
    id = sa.Column(sa.Integer, primary_key=True)
Pierre-Yves Chibon 9c2953
    user_id = sa.Column(sa.Integer, sa.ForeignKey("users.id"), nullable=False)
Pierre-Yves Chibon b08582
    visit_key = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.String(40), nullable=False, unique=True, index=True
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b08582
    user_ip = sa.Column(sa.String(50), nullable=False)
Pierre-Yves Chibon b08582
    created = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.DateTime, nullable=False, default=datetime.datetime.utcnow
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b08582
    expiry = sa.Column(sa.DateTime)
Pierre-Yves Chibon b08582
Pierre-Yves Chibon b08582
Pierre-Yves Chibon fe5017
class PagureUserGroup(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 9c2953
    __tablename__ = "pagure_user_group"
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    user_id = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.Integer, sa.ForeignKey("users.id"), primary_key=True
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2b1b47
    group_id = sa.Column(
Pierre-Yves Chibon 9c2953
        sa.Integer, sa.ForeignKey("pagure_group.id"), primary_key=True
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2b1b47
Pierre-Yves Chibon 2b1b47
    # Constraints
Pierre-Yves Chibon 9c2953
    __table_args__ = (sa.UniqueConstraint("user_id", "group_id"),)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
# Make sure to load the Plugin tables, so they have a chance to register
Patrick Uiterwijk 4012dc
get_plugin_tables()