Blame progit/lib.py

Pierre-Yves Chibon 1e2e25
#-*- coding: utf-8 -*-
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
"""
Pierre-Yves Chibon 1e2e25
 (c) 2014 - Copyright Red Hat Inc
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
 Authors:
Pierre-Yves Chibon 1e2e25
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
"""
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 9af5a0
import datetime
Pierre-Yves Chibon f7d963
import json
Pierre-Yves Chibon d8110b
import os
Pierre-Yves Chibon e2a95c
import random
Pierre-Yves Chibon d133cd
import shutil
Pierre-Yves Chibon e2a95c
import string
Pierre-Yves Chibon d133cd
import tempfile
Pierre-Yves Chibon 9cb884
import uuid
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon 1e2e25
import sqlalchemy
Pierre-Yves Chibon 1e2e25
from datetime import timedelta
Pierre-Yves Chibon 1e2e25
from sqlalchemy.orm import sessionmaker
Pierre-Yves Chibon 1e2e25
from sqlalchemy.orm import scoped_session
Pierre-Yves Chibon 1e2e25
from sqlalchemy.orm.exc import NoResultFound
Pierre-Yves Chibon 1e2e25
from sqlalchemy.exc import SQLAlchemyError
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon d8110b
import pygit2
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon d8110b
import progit.exceptions
Pierre-Yves Chibon 4b55b4
import progit.notify
Pierre-Yves Chibon 1e2e25
from progit import model
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
def create_session(db_url, debug=False, pool_recycle=3600):
Pierre-Yves Chibon 695ef6
    ''' Create the Session object to use to query the database.
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
    :arg db_url: URL used to connect to the database. The URL contains
Pierre-Yves Chibon 1e2e25
    information with regards to the database engine, the host to connect
Pierre-Yves Chibon 1e2e25
    to, the user and password and the database name.
Pierre-Yves Chibon 1e2e25
      ie: <engine>://<user>:<password>@<host>/<dbname></dbname></host></password></user></engine>
Pierre-Yves Chibon 1e2e25
    :kwarg debug: a boolean specifying wether we should have the verbose
Pierre-Yves Chibon 1e2e25
        output of sqlalchemy or not.
Pierre-Yves Chibon 1e2e25
    :return a Session that can be used to query the database.
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 695ef6
    '''
Pierre-Yves Chibon 1e2e25
    engine = sqlalchemy.create_engine(
Pierre-Yves Chibon 1e2e25
        db_url, echo=debug, pool_recycle=pool_recycle)
Pierre-Yves Chibon 1e2e25
    scopedsession = scoped_session(sessionmaker(bind=engine))
Pierre-Yves Chibon 1e2e25
    return scopedsession
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon e2a95c
def id_generator(size=15, chars=string.ascii_uppercase + string.digits):
Pierre-Yves Chibon e2a95c
    """ Generates a random identifier for the given size and using the
Pierre-Yves Chibon e2a95c
    specified characters.
Pierre-Yves Chibon e2a95c
    If no size is specified, it uses 15 as default.
Pierre-Yves Chibon e2a95c
    If no characters are specified, it uses ascii char upper case and
Pierre-Yves Chibon e2a95c
    digits.
Pierre-Yves Chibon e2a95c
    :arg size: the size of the identifier to return.
Pierre-Yves Chibon e2a95c
    :arg chars: the list of characters that can be used in the
Pierre-Yves Chibon e2a95c
        idenfitier.
Pierre-Yves Chibon e2a95c
    """
Pierre-Yves Chibon e2a95c
    return ''.join(random.choice(chars) for x in range(size))
Pierre-Yves Chibon e2a95c
Pierre-Yves Chibon e2a95c
Pierre-Yves Chibon f0256a
def commit_to_patch(repo_obj, commits):
Pierre-Yves Chibon 9af5a0
    ''' For a given commit (PyGit2 commit object) of a specified git repo,
Pierre-Yves Chibon 9af5a0
    returns a string representation of the changes the commit did in a
Pierre-Yves Chibon 9af5a0
    format that allows it to be used as patch.
Pierre-Yves Chibon 9af5a0
    '''
Pierre-Yves Chibon f0256a
    if not isinstance(commits, list):
Pierre-Yves Chibon f0256a
        commits = [commits]
Pierre-Yves Chibon 9af5a0
Pierre-Yves Chibon f0256a
    patch = ""
Pierre-Yves Chibon f0256a
    for cnt, commit in enumerate(commits):
Pierre-Yves Chibon f0256a
        if commit.parents:
Pierre-Yves Chibon f0256a
            diff = commit.tree.diff_to_tree()
Pierre-Yves Chibon 9af5a0
Pierre-Yves Chibon f0256a
            parent = repo_obj.revparse_single('%s^' % commit.oid.hex)
Pierre-Yves Chibon f0256a
            diff = repo_obj.diff(parent, commit)
Pierre-Yves Chibon f0256a
        else:
Pierre-Yves Chibon f0256a
            # First commit in the repo
Pierre-Yves Chibon f0256a
            diff = commit.tree.diff_to_tree(swap=True)
Pierre-Yves Chibon f0256a
Pierre-Yves Chibon b93113
        subject = message = ''
Pierre-Yves Chibon b93113
        if '\n' in commit.message:
Pierre-Yves Chibon b93113
            subject, message = commit.message.split('\n', 1)
Pierre-Yves Chibon b93113
        else:
Pierre-Yves Chibon b93113
            subject = commit.message
Pierre-Yves Chibon f0256a
Pierre-Yves Chibon f0256a
        if len(commits) > 1:
Pierre-Yves Chibon f0256a
            subject = '[PATCH %s/%s] %s' % (cnt + 1, len(commits), subject)
Pierre-Yves Chibon f0256a
Pierre-Yves Chibon f0256a
        patch += """From %(commit)s Mon Sep 17 00:00:00 2001
Pierre-Yves Chibon 9af5a0
From: %(author_name)s <%(author_email)s>
Pierre-Yves Chibon 9af5a0
Date: %(date)s
Pierre-Yves Chibon f0256a
Subject: %(subject)s
Pierre-Yves Chibon 9af5a0
Pierre-Yves Chibon 9af5a0
%(msg)s
Pierre-Yves Chibon f0256a
---
Pierre-Yves Chibon 9af5a0
Pierre-Yves Chibon 9af5a0
%(patch)s
Pierre-Yves Chibon 9af5a0
""" % (
Pierre-Yves Chibon f0256a
            {
Pierre-Yves Chibon f0256a
                'commit': commit.oid.hex,
Pierre-Yves Chibon f0256a
                'author_name': commit.author.name,
Pierre-Yves Chibon f0256a
                'author_email': commit.author.email,
Pierre-Yves Chibon f0256a
                'date': datetime.datetime.utcfromtimestamp(
Pierre-Yves Chibon f0256a
                    commit.commit_time).strftime('%b %d %Y %H:%M:%S +0000'),
Pierre-Yves Chibon f0256a
                'subject': subject,
Pierre-Yves Chibon f0256a
                'msg': message,
Pierre-Yves Chibon f0256a
                'patch': diff.patch,
Pierre-Yves Chibon f0256a
            }
Pierre-Yves Chibon f0256a
        )
Pierre-Yves Chibon 9af5a0
    return patch
Pierre-Yves Chibon 9af5a0
Pierre-Yves Chibon 9af5a0
Pierre-Yves Chibon 03c11f
def get_user(session, username):
Pierre-Yves Chibon 03c11f
    ''' Return the user corresponding to this username, or None. '''
Pierre-Yves Chibon 03c11f
    user = session.query(
Pierre-Yves Chibon e1372f
        model.User
Pierre-Yves Chibon e1372f
    ).filter(
Pierre-Yves Chibon 03c11f
        model.User.user == username
Pierre-Yves Chibon e1372f
    ).first()
Pierre-Yves Chibon 03c11f
    return user
Pierre-Yves Chibon 03c11f
Pierre-Yves Chibon 03c11f
Pierre-Yves Chibon b81780
def get_user_by_email(session, user_mail):
Pierre-Yves Chibon b81780
    ''' Return the user corresponding to this email, or None. '''
Pierre-Yves Chibon b81780
    mail = session.query(
Pierre-Yves Chibon b81780
        model.UserEmail
Pierre-Yves Chibon b81780
    ).filter(
Pierre-Yves Chibon b81780
        model.UserEmail.email == user_mail
Pierre-Yves Chibon b81780
    ).first()
Pierre-Yves Chibon b81780
    if mail:
Pierre-Yves Chibon b81780
        return mail.user
Pierre-Yves Chibon b81780
Pierre-Yves Chibon b81780
Pierre-Yves Chibon 34cbca
def get_user_by_token(session, token):
Pierre-Yves Chibon 34cbca
    ''' Return a specified User via its token.
Pierre-Yves Chibon 34cbca
Pierre-Yves Chibon 34cbca
    :arg session: the session with which to connect to the database.
Pierre-Yves Chibon 34cbca
Pierre-Yves Chibon 34cbca
    '''
Pierre-Yves Chibon 34cbca
    user = session.query(
Pierre-Yves Chibon 34cbca
        model.User
Pierre-Yves Chibon 34cbca
    ).filter(
Pierre-Yves Chibon 34cbca
        model.User.token == token
Pierre-Yves Chibon 34cbca
    ).first()
Pierre-Yves Chibon 34cbca
    return user
Pierre-Yves Chibon 34cbca
Pierre-Yves Chibon 34cbca
Pierre-Yves Chibon 556d85
def get_all_users(session):
Pierre-Yves Chibon 556d85
    ''' Return the user corresponding to this username, or None. '''
Pierre-Yves Chibon 556d85
    users = session.query(
Pierre-Yves Chibon 556d85
        model.User
Pierre-Yves Chibon 556d85
    ).all()
Pierre-Yves Chibon 556d85
    return users
Pierre-Yves Chibon 556d85
Pierre-Yves Chibon 556d85
Pierre-Yves Chibon a34835
def add_issue_comment(session, issue, comment, user, ticketfolder):
Pierre-Yves Chibon 03c11f
    ''' Add a comment to an issue. '''
Pierre-Yves Chibon 3ddae1
    user_obj = get_user(session, user)
Pierre-Yves Chibon 496a35
    if not user_obj:
Pierre-Yves Chibon 496a35
        user_obj = get_user_by_email(session, user)
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon e1372f
    if not user_obj:
Pierre-Yves Chibon e1372f
        raise progit.exceptions.ProgitException(
Pierre-Yves Chibon e1372f
            'No user "%s" found' % user
Pierre-Yves Chibon e1372f
        )
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon 00c3f0
    issue_comment = model.IssueComment(
Pierre-Yves Chibon 359db9
        issue_id=issue.id,
Pierre-Yves Chibon 359db9
        comment=comment,
Pierre-Yves Chibon e1372f
        user_id=user_obj.id,
Pierre-Yves Chibon 359db9
    )
Pierre-Yves Chibon 00c3f0
    session.add(issue_comment)
Pierre-Yves Chibon 359db9
    # Make sure we won't have SQLAlchemy error before we create the repo
Pierre-Yves Chibon 359db9
    session.flush()
Pierre-Yves Chibon 359db9
Pierre-Yves Chibon a34835
    update_git_ticket(issue, repo=issue.project, ticketfolder=ticketfolder)
Pierre-Yves Chibon a34835
Pierre-Yves Chibon 4b55b4
    progit.notify.notify_new_comment(issue_comment)
Pierre-Yves Chibon 4b55b4
Pierre-Yves Chibon 359db9
    return 'Comment added'
Pierre-Yves Chibon 359db9
Pierre-Yves Chibon 359db9
Pierre-Yves Chibon b9cb1e
def add_user_to_project(session, project, user):
Pierre-Yves Chibon b9cb1e
    ''' Add a specified user to a specified project. '''
Pierre-Yves Chibon b9cb1e
    user_obj = get_user(session, user)
Pierre-Yves Chibon b9cb1e
    if not user_obj:
Pierre-Yves Chibon b9cb1e
        user_obj = get_user_by_email(session, user)
Pierre-Yves Chibon b9cb1e
Pierre-Yves Chibon b9cb1e
    if not user_obj:
Pierre-Yves Chibon b9cb1e
        raise progit.exceptions.ProgitException(
Pierre-Yves Chibon b9cb1e
            'No user "%s" found' % user
Pierre-Yves Chibon b9cb1e
        )
Pierre-Yves Chibon b9cb1e
Pierre-Yves Chibon b9cb1e
    project_user = model.ProjectUser(
Pierre-Yves Chibon b9cb1e
        project_id=project.id,
Pierre-Yves Chibon b9cb1e
        user_id=user_obj.id,
Pierre-Yves Chibon b9cb1e
    )
Pierre-Yves Chibon b9cb1e
    session.add(project_user)
Pierre-Yves Chibon b9cb1e
    # Make sure we won't have SQLAlchemy error before we create the repo
Pierre-Yves Chibon b9cb1e
    session.flush()
Pierre-Yves Chibon b9cb1e
Pierre-Yves Chibon b9cb1e
    return 'Comment added'
Pierre-Yves Chibon b9cb1e
Pierre-Yves Chibon a8a719
Pierre-Yves Chibon 2c11b4
def add_pull_request_comment(session, request, commit, row, comment, user):
Pierre-Yves Chibon 2c11b4
    ''' Add a comment to a pull-request. '''
Pierre-Yves Chibon 2c11b4
    user_obj = get_user(session, user)
Pierre-Yves Chibon 2c11b4
    if not user_obj:
Pierre-Yves Chibon 2c11b4
        user_obj = get_user_by_email(session, user)
Pierre-Yves Chibon 2c11b4
Pierre-Yves Chibon 2c11b4
    if not user_obj:
Pierre-Yves Chibon 2c11b4
        raise progit.exceptions.ProgitException(
Pierre-Yves Chibon 2c11b4
            'No user "%s" found' % user
Pierre-Yves Chibon 2c11b4
        )
Pierre-Yves Chibon 2c11b4
Pierre-Yves Chibon 2c11b4
    pr_comment = model.PullRequestComment(
Pierre-Yves Chibon 2c11b4
        pull_request_id=request.id,
Pierre-Yves Chibon 2c11b4
        commit_id=commit,
Pierre-Yves Chibon 2c11b4
        line=row,
Pierre-Yves Chibon 2c11b4
        comment=comment,
Pierre-Yves Chibon 2c11b4
        user_id=user_obj.id,
Pierre-Yves Chibon 2c11b4
    )
Pierre-Yves Chibon 2c11b4
    session.add(pr_comment)
Pierre-Yves Chibon 2c11b4
    # Make sure we won't have SQLAlchemy error before we create the repo
Pierre-Yves Chibon 2c11b4
    session.flush()
Pierre-Yves Chibon 2c11b4
Pierre-Yves Chibon 2c11b4
    return 'Comment added'
Pierre-Yves Chibon 2c11b4
Pierre-Yves Chibon 2c11b4
Pierre-Yves Chibon 1e2e25
def get_user_project(session, username):
Pierre-Yves Chibon 1e2e25
    ''' Retrieve the list of projects managed by a user.
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
    '''
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
    query = session.query(
Pierre-Yves Chibon 1e2e25
        model.Project
Pierre-Yves Chibon 1e2e25
    ).filter(
Pierre-Yves Chibon 1e2e25
        model.Project.user == username
Pierre-Yves Chibon 1e2e25
    )
Pierre-Yves Chibon 1e2e25
Pierre-Yves Chibon 1e2e25
    return query.all()
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon 49b899
def new_project(session, user, name, gitfolder, docfolder, ticketfolder,
Pierre-Yves Chibon d8110b
                description=None, parent_id=None):
Pierre-Yves Chibon d8110b
    ''' Create a new project based on the information provided.
Pierre-Yves Chibon d8110b
    '''
Pierre-Yves Chibon 6160b6
    gitrepo = os.path.join(gitfolder, '%s.git' % name)
Pierre-Yves Chibon d8110b
    if os.path.exists(gitrepo):
Pierre-Yves Chibon d8110b
        raise progit.exceptions.RepoExistsException(
Pierre-Yves Chibon ca47fa
            'The project repo "%s" already exists' % name
Pierre-Yves Chibon d8110b
        )
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon 3ddae1
    user_obj = get_user(session, user)
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon e1372f
    if not user_obj:
Pierre-Yves Chibon e1372f
        raise progit.exceptions.ProgitException(
Pierre-Yves Chibon e1372f
            'No user "%s" found' % user
Pierre-Yves Chibon e1372f
        )
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon d8110b
    project = model.Project(
Pierre-Yves Chibon d8110b
        name=name,
Pierre-Yves Chibon d8110b
        description=description,
Pierre-Yves Chibon e1372f
        user_id=user_obj.id,
Pierre-Yves Chibon d8110b
        parent_id=parent_id
Pierre-Yves Chibon d8110b
    )
Pierre-Yves Chibon d8110b
    session.add(project)
Pierre-Yves Chibon d8110b
    # Make sure we won't have SQLAlchemy error before we create the repo
Pierre-Yves Chibon d8110b
    session.flush()
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon d8110b
    pygit2.init_repository(gitrepo, bare=True)
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon 6160b6
    gitrepo = os.path.join(docfolder, project.path)
Pierre-Yves Chibon 6160b6
    if os.path.exists(gitrepo):
Pierre-Yves Chibon 6160b6
        raise progit.exceptions.RepoExistsException(
Pierre-Yves Chibon ca47fa
            'The docs repo "%s" already exists' % project.path
Pierre-Yves Chibon 6160b6
        )
Pierre-Yves Chibon 6160b6
    pygit2.init_repository(gitrepo, bare=True)
Pierre-Yves Chibon 6160b6
Pierre-Yves Chibon 49b899
    gitrepo = os.path.join(ticketfolder, project.path)
Pierre-Yves Chibon 49b899
    if os.path.exists(gitrepo):
Pierre-Yves Chibon 49b899
        raise progit.exceptions.RepoExistsException(
Pierre-Yves Chibon 49b899
            'The tickets repo "%s" already exists' % project.path
Pierre-Yves Chibon 49b899
        )
Pierre-Yves Chibon 49b899
    pygit2.init_repository(gitrepo, bare=True)
Pierre-Yves Chibon 49b899
Pierre-Yves Chibon d8110b
    return 'Project "%s" created' % name
Mathieu Bridon eb729c
Pierre-Yves Chibon f74093
Pierre-Yves Chibon a34835
def new_issue(session, repo, title, content, user, ticketfolder):
Pierre-Yves Chibon 33ff2c
    ''' Create a new issue for the specified repo. '''
Pierre-Yves Chibon 3ddae1
    user_obj = get_user(session, user)
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon e1372f
    if not user_obj:
Pierre-Yves Chibon e1372f
        raise progit.exceptions.ProgitException(
Pierre-Yves Chibon e1372f
            'No user "%s" found' % user
Pierre-Yves Chibon e1372f
        )
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon 33ff2c
    issue = model.Issue(
Pierre-Yves Chibon 33ff2c
        project_id=repo.id,
Pierre-Yves Chibon 33ff2c
        title=title,
Pierre-Yves Chibon 33ff2c
        content=content,
Pierre-Yves Chibon e1372f
        user_id=user_obj.id,
Pierre-Yves Chibon 9cb884
        uid=uuid.uuid4().hex,
Pierre-Yves Chibon 33ff2c
    )
Pierre-Yves Chibon 33ff2c
    session.add(issue)
Pierre-Yves Chibon 057ee1
    # Make sure we won't have SQLAlchemy error before we create the issue
Pierre-Yves Chibon 33ff2c
    session.flush()
Pierre-Yves Chibon 33ff2c
Pierre-Yves Chibon e1690f
    global_id = model.GlobalId(
Pierre-Yves Chibon e1690f
        project_id=repo.id,
Pierre-Yves Chibon e1690f
        issue_id=issue.id,
Pierre-Yves Chibon e1690f
    )
Pierre-Yves Chibon e1690f
Pierre-Yves Chibon e1690f
    session.add(global_id)
Pierre-Yves Chibon e1690f
    session.flush()
Pierre-Yves Chibon e1690f
Pierre-Yves Chibon a34835
    update_git_ticket(issue, repo=repo, ticketfolder=ticketfolder)
Pierre-Yves Chibon a34835
Pierre-Yves Chibon 142bae
    progit.notify.notify_new_issue(issue)
Pierre-Yves Chibon 142bae
Pierre-Yves Chibon 33ff2c
    return 'Issue created'
Pierre-Yves Chibon 33ff2c
Pierre-Yves Chibon 33ff2c
Pierre-Yves Chibon 62ec72
def new_pull_request(
Pierre-Yves Chibon 8e8a9f
        session, repo, repo_from, branch, title, user, stop_id,
Pierre-Yves Chibon 8e8a9f
        start_id=None):
Pierre-Yves Chibon 472a61
    ''' Create a new pull request on the specified repo. '''
Pierre-Yves Chibon 3ddae1
    user_obj = get_user(session, user)
Pierre-Yves Chibon 7d23c0
Pierre-Yves Chibon 7d23c0
    if not user_obj:
Pierre-Yves Chibon 7d23c0
        raise progit.exceptions.ProgitException(
Pierre-Yves Chibon 7d23c0
            'No user "%s" found' % user
Pierre-Yves Chibon 7d23c0
        )
Pierre-Yves Chibon 7d23c0
Pierre-Yves Chibon 472a61
    request = model.PullRequest(
Pierre-Yves Chibon 472a61
        project_id=repo.id,
Pierre-Yves Chibon 62ec72
        project_id_from=repo_from.id,
Pierre-Yves Chibon 8e8a9f
        branch=branch,
Pierre-Yves Chibon 472a61
        title=title,
Pierre-Yves Chibon 472a61
        start_id=start_id,
Pierre-Yves Chibon 472a61
        stop_id=stop_id,
Pierre-Yves Chibon 7d23c0
        user_id=user_obj.id,
Pierre-Yves Chibon 472a61
    )
Pierre-Yves Chibon 472a61
    session.add(request)
Pierre-Yves Chibon 472a61
    # Make sure we won't have SQLAlchemy error before we create the request
Pierre-Yves Chibon 472a61
    session.flush()
Pierre-Yves Chibon 472a61
Pierre-Yves Chibon 3a3f5e
    global_id = model.GlobalId(
Pierre-Yves Chibon 3a3f5e
        project_id=repo.id,
Pierre-Yves Chibon 3a3f5e
        request_id=request.id,
Pierre-Yves Chibon 3a3f5e
    )
Pierre-Yves Chibon 3a3f5e
Pierre-Yves Chibon 3a3f5e
    session.add(global_id)
Pierre-Yves Chibon 3a3f5e
    session.flush()
Pierre-Yves Chibon 3a3f5e
Pierre-Yves Chibon e0589f
    progit.notify.notify_new_pull_request(request)
Pierre-Yves Chibon e0589f
Pierre-Yves Chibon 472a61
    return 'Request created'
Pierre-Yves Chibon 472a61
Pierre-Yves Chibon 472a61
Pierre-Yves Chibon a34835
def edit_issue(session, issue, ticketfolder,
Pierre-Yves Chibon a34835
               title=None, content=None, status=None):
Pierre-Yves Chibon c71370
    ''' Edit the specified issue.
Pierre-Yves Chibon c71370
    '''
Pierre-Yves Chibon c71370
    edit = []
Pierre-Yves Chibon fb8b6c
    if title and title != issue.title:
Pierre-Yves Chibon c71370
        issue.title = title
Pierre-Yves Chibon c71370
        edit.append('title')
Pierre-Yves Chibon fb8b6c
    if content and content != issue.content:
Pierre-Yves Chibon c71370
        issue.content = content
Pierre-Yves Chibon c71370
        edit.append('content')
Pierre-Yves Chibon fb8b6c
    if status and status != issue.status:
Pierre-Yves Chibon 4d1cbe
        issue.status = status
Pierre-Yves Chibon 4d1cbe
        edit.append('status')
Pierre-Yves Chibon c71370
Pierre-Yves Chibon 65c8d7
    update_git_ticket(issue, repo=issue.project, ticketfolder=ticketfolder)
Pierre-Yves Chibon a34835
Pierre-Yves Chibon c71370
    if not edit:
Pierre-Yves Chibon c71370
        return 'No changes to edit'
Pierre-Yves Chibon c71370
    else:
Pierre-Yves Chibon c71370
        session.add(issue)
Pierre-Yves Chibon c71370
        session.flush()
Pierre-Yves Chibon c71370
        return 'Edited successfully issue #%s' % issue.id
Pierre-Yves Chibon c71370
Pierre-Yves Chibon c71370
Pierre-Yves Chibon b39aa4
def update_project_settings(session, repo, issue_tracker, project_docs):
Pierre-Yves Chibon b39aa4
    ''' Update the settings of a project. '''
Pierre-Yves Chibon b39aa4
    update = []
Pierre-Yves Chibon b39aa4
    if issue_tracker != repo.issue_tracker:
Pierre-Yves Chibon b39aa4
        repo.issue_tracker = issue_tracker
Pierre-Yves Chibon b39aa4
        update.append('issue_tracker')
Pierre-Yves Chibon b39aa4
    if project_docs != repo.project_docs:
Pierre-Yves Chibon b39aa4
        repo.project_docs = project_docs
Pierre-Yves Chibon b39aa4
        update.append('project_docs')
Pierre-Yves Chibon b39aa4
Pierre-Yves Chibon b39aa4
    if not update:
Pierre-Yves Chibon b39aa4
        return 'No settings to change'
Pierre-Yves Chibon b39aa4
    else:
Pierre-Yves Chibon b39aa4
        session.add(repo)
Pierre-Yves Chibon b39aa4
        session.flush()
Pierre-Yves Chibon b39aa4
        return 'Edited successfully setting of repo: %s' % repo.fullname
Pierre-Yves Chibon b39aa4
Pierre-Yves Chibon b39aa4
Pierre-Yves Chibon a33978
def fork_project(session, user, repo, gitfolder,
Pierre-Yves Chibon a33978
                 forkfolder, docfolder,ticketfolder):
Pierre-Yves Chibon 652c2c
    ''' Fork a given project into the user's forks. '''
Pierre-Yves Chibon df2470
    if repo.is_fork:
Pierre-Yves Chibon df2470
        reponame = os.path.join(forkfolder, repo.path)
Pierre-Yves Chibon df2470
    else:
Pierre-Yves Chibon df2470
        reponame = os.path.join(gitfolder, repo.path)
Pierre-Yves Chibon df2470
    forkreponame = '%s.git' % os.path.join(forkfolder, user, repo.name)
Pierre-Yves Chibon 652c2c
Pierre-Yves Chibon 9e91eb
    if repo.user.user == user:
Pierre-Yves Chibon 9e91eb
        raise progit.exceptions.RepoExistsException(
Pierre-Yves Chibon 9e91eb
            'You may not fork your own repo')
Pierre-Yves Chibon 9e91eb
Pierre-Yves Chibon 652c2c
    if os.path.exists(forkreponame):
Pierre-Yves Chibon 652c2c
        raise progit.exceptions.RepoExistsException(
Pierre-Yves Chibon 652c2c
            'Repo "%s/%s" already exists' % (user, repo.name))
Pierre-Yves Chibon 652c2c
Pierre-Yves Chibon 3ddae1
    user_obj = get_user(session, user)
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon e1372f
    if not user_obj:
Pierre-Yves Chibon e1372f
        raise progit.exceptions.ProgitException(
Pierre-Yves Chibon e1372f
            'No user "%s" found' % user
Pierre-Yves Chibon e1372f
        )
Pierre-Yves Chibon e1372f
Pierre-Yves Chibon 652c2c
    project = model.Project(
Pierre-Yves Chibon 652c2c
        name=repo.name,
Pierre-Yves Chibon 652c2c
        description=repo.description,
Pierre-Yves Chibon e1372f
        user_id=user_obj.id,
Pierre-Yves Chibon 652c2c
        parent_id=repo.id
Pierre-Yves Chibon 652c2c
    )
Pierre-Yves Chibon 652c2c
    session.add(project)
Pierre-Yves Chibon 652c2c
    # Make sure we won't have SQLAlchemy error before we create the repo
Pierre-Yves Chibon 652c2c
    session.flush()
Pierre-Yves Chibon 652c2c
Pierre-Yves Chibon d01de7
    pygit2.clone_repository(reponame, forkreponame, bare=True)
Pierre-Yves Chibon 652c2c
Pierre-Yves Chibon 6160b6
    gitrepo = os.path.join(docfolder, project.path)
Pierre-Yves Chibon 6160b6
    if os.path.exists(gitrepo):
Pierre-Yves Chibon 6160b6
        raise progit.exceptions.RepoExistsException(
Pierre-Yves Chibon 4b7a7d
            'The docs "%s" already exists' % project.path
Pierre-Yves Chibon 6160b6
        )
Pierre-Yves Chibon a33978
    pygit2.init_repository(gitrepo, bare=True)
Pierre-Yves Chibon a33978
Pierre-Yves Chibon a33978
    gitrepo = os.path.join(ticketfolder, project.path)
Pierre-Yves Chibon a33978
    if os.path.exists(gitrepo):
Pierre-Yves Chibon a33978
        raise progit.exceptions.RepoExistsException(
Pierre-Yves Chibon a33978
            'The tickets repo "%s" already exists' % project.path
Pierre-Yves Chibon a33978
        )
Pierre-Yves Chibon 6160b6
    pygit2.init_repository(gitrepo, bare=True)
Pierre-Yves Chibon 6160b6
Pierre-Yves Chibon 652c2c
    return 'Repo "%s" cloned to "%s/%s"' % (repo.name, user, repo.name)
Pierre-Yves Chibon 652c2c
Pierre-Yves Chibon 652c2c
Pierre-Yves Chibon ea0ee1
def list_projects(
Pierre-Yves Chibon 7752a8
        session, username=None, fork=None,
Pierre-Yves Chibon ea0ee1
        start=None, limit=None, count=False):
Pierre-Yves Chibon 695ef6
    '''List existing projects
Pierre-Yves Chibon 695ef6
    '''
Pierre-Yves Chibon 0b9290
    projects = session.query(
Pierre-Yves Chibon 0b9290
        model.Project
Pierre-Yves Chibon 0b9290
    ).order_by(
Pierre-Yves Chibon 0b9290
        model.Project.date_created
Pierre-Yves Chibon 0b9290
    )
Mathieu Bridon eb729c
Pierre-Yves Chibon ea0ee1
    if username is not None:
Pierre-Yves Chibon e1372f
        projects = projects.filter(
Pierre-Yves Chibon e1372f
            model.User.user == username
Pierre-Yves Chibon e1372f
        ).filter(
Pierre-Yves Chibon e1372f
            model.User.id == model.Project.user_id
Pierre-Yves Chibon ea0ee1
        )
Pierre-Yves Chibon ea0ee1
Pierre-Yves Chibon 7752a8
    if fork is not None:
Pierre-Yves Chibon 7752a8
        if fork is True:
Pierre-Yves Chibon 7752a8
            projects = projects.filter(
Pierre-Yves Chibon 7752a8
                model.Project.parent_id != None
Pierre-Yves Chibon 7752a8
            )
Pierre-Yves Chibon 7752a8
        elif fork is False:
Pierre-Yves Chibon 2f8732
            projects = projects.filter(
Pierre-Yves Chibon 2f8732
                model.Project.parent_id == None
Pierre-Yves Chibon 7752a8
            )
Pierre-Yves Chibon 7752a8
Mathieu Bridon eb729c
    if start is not None:
Mathieu Bridon eb729c
        projects = projects.offset(start)
Mathieu Bridon eb729c
Mathieu Bridon eb729c
    if limit is not None:
Mathieu Bridon eb729c
        projects = projects.limit(limit)
Mathieu Bridon eb729c
Pierre-Yves Chibon f74093
    if count:
Pierre-Yves Chibon f74093
        return projects.count()
Pierre-Yves Chibon f74093
    else:
Pierre-Yves Chibon f74093
        return projects.all()
Mathieu Bridon eb729c
Mathieu Bridon 998605
Pierre-Yves Chibon 929595
def get_project(session, name, user=None):
Pierre-Yves Chibon 695ef6
    '''Get a project from the database
Pierre-Yves Chibon 695ef6
    '''
Pierre-Yves Chibon 929595
    query = session.query(
Pierre-Yves Chibon f74093
        model.Project
Pierre-Yves Chibon 178d6f
    ).filter(
Pierre-Yves Chibon 178d6f
        model.Project.name == name
Pierre-Yves Chibon 929595
    )
Pierre-Yves Chibon 1ac00b
Pierre-Yves Chibon 929595
    if user is not None:
Pierre-Yves Chibon 929595
        query = query.filter(
Pierre-Yves Chibon e1372f
            model.User.user == user
Pierre-Yves Chibon e1372f
        ).filter(
Pierre-Yves Chibon e1372f
            model.User.id == model.Project.user_id
Pierre-Yves Chibon 1ac00b
        ).filter(
Pierre-Yves Chibon 1ac00b
            model.Project.parent_id != None
Pierre-Yves Chibon 929595
        )
Pierre-Yves Chibon 1ac00b
    else:
Pierre-Yves Chibon 1ac00b
        query = query.filter(
Pierre-Yves Chibon 1ac00b
            model.Project.parent_id == None
Pierre-Yves Chibon 1ac00b
        )
Pierre-Yves Chibon 1ac00b
Pierre-Yves Chibon 929595
    return query.first()
Pierre-Yves Chibon 562d75
Pierre-Yves Chibon 562d75
Pierre-Yves Chibon b6df7d
def get_issues(session, repo, status=None, closed=False):
Pierre-Yves Chibon 562d75
    ''' Retrieve all the issues associated to a project
Pierre-Yves Chibon b6df7d
Pierre-Yves Chibon b6df7d
    Watch out that the closed argument is incompatible with the status
Pierre-Yves Chibon b6df7d
    argument. The closed argument will return all the issues whose status
Pierre-Yves Chibon b6df7d
    is not 'Open', otherwise it will return the issues having the specified
Pierre-Yves Chibon b6df7d
    status.
Pierre-Yves Chibon 562d75
    '''
Pierre-Yves Chibon 77e16c
    subquery = session.query(
Pierre-Yves Chibon 77e16c
        model.GlobalId,
Pierre-Yves Chibon 77e16c
        sqlalchemy.over(
Pierre-Yves Chibon 77e16c
            sqlalchemy.func.row_number(),
Pierre-Yves Chibon 77e16c
            partition_by=model.GlobalId.project_id,
Pierre-Yves Chibon 77e16c
            order_by=model.GlobalId.id
Pierre-Yves Chibon 77e16c
        ).label('global_id')
Pierre-Yves Chibon 77e16c
    ).subquery()
Pierre-Yves Chibon 77e16c
Pierre-Yves Chibon 562d75
    query = session.query(
Pierre-Yves Chibon 77e16c
        model.Issue,
Pierre-Yves Chibon 77e16c
        subquery.c.global_id
Pierre-Yves Chibon 77e16c
    ).filter(
Pierre-Yves Chibon 77e16c
        subquery.c.issue_id == model.Issue.id
Pierre-Yves Chibon 562d75
    ).filter(
Pierre-Yves Chibon 77e16c
        subquery.c.project_id == model.Issue.project_id
Pierre-Yves Chibon 95f042
    ).filter(
Pierre-Yves Chibon 95f042
        model.Issue.project_id == repo.id
Pierre-Yves Chibon 3a2a1c
    ).order_by(
Pierre-Yves Chibon 3a2a1c
        model.Issue.id
Pierre-Yves Chibon 562d75
    )
Pierre-Yves Chibon 562d75
Pierre-Yves Chibon b6df7d
    if status is not None and not closed:
Pierre-Yves Chibon 4d1cbe
        query = query.filter(
Pierre-Yves Chibon 4d1cbe
            model.Issue.status == status
Pierre-Yves Chibon 4d1cbe
        )
Pierre-Yves Chibon b6df7d
    if closed:
Pierre-Yves Chibon b6df7d
        query = query.filter(
Pierre-Yves Chibon b6df7d
            model.Issue.status != 'Open'
Pierre-Yves Chibon b6df7d
        )
Pierre-Yves Chibon 4d1cbe
Pierre-Yves Chibon 562d75
    return query.all()
Pierre-Yves Chibon 562d75
Pierre-Yves Chibon 562d75
Pierre-Yves Chibon bcb038
def get_issue(session, projectid, issueid):
Pierre-Yves Chibon 562d75
    ''' Retrieve the specified issue
Pierre-Yves Chibon 562d75
    '''
Pierre-Yves Chibon 251d96
    subquery = session.query(
Pierre-Yves Chibon 251d96
        model.GlobalId,
Pierre-Yves Chibon 251d96
        sqlalchemy.over(
Pierre-Yves Chibon 251d96
            sqlalchemy.func.row_number(),
Pierre-Yves Chibon 251d96
            partition_by=model.GlobalId.project_id,
Pierre-Yves Chibon 251d96
            order_by=model.GlobalId.id
Pierre-Yves Chibon 251d96
        ).label('global_id')
Pierre-Yves Chibon 251d96
    ).subquery()
Pierre-Yves Chibon 251d96
Pierre-Yves Chibon 562d75
    query = session.query(
Pierre-Yves Chibon 562d75
        model.Issue
Pierre-Yves Chibon 562d75
    ).filter(
Pierre-Yves Chibon bcb038
        model.Issue.project_id == projectid
Pierre-Yves Chibon bcb038
    ).filter(
Pierre-Yves Chibon bcb038
        model.Issue.project_id == projectid
Pierre-Yves Chibon bcb038
    ).filter(
Pierre-Yves Chibon 251d96
        subquery.c.project_id == model.Issue.project_id
Pierre-Yves Chibon 251d96
    ).filter(
Pierre-Yves Chibon a93753
        subquery.c.issue_id == model.Issue.id
Pierre-Yves Chibon a93753
    ).filter(
Pierre-Yves Chibon 251d96
        subquery.c.global_id == issueid
Pierre-Yves Chibon 3a2a1c
    ).order_by(
Pierre-Yves Chibon 3a2a1c
        model.Issue.id
Pierre-Yves Chibon 562d75
    )
Pierre-Yves Chibon 562d75
Pierre-Yves Chibon 562d75
    return query.first()
Pierre-Yves Chibon 35112d
Pierre-Yves Chibon 35112d
Pierre-Yves Chibon b0265c
def get_pull_requests(
Pierre-Yves Chibon b0265c
        session, project_id=None, project_id_from=None, status=None):
Pierre-Yves Chibon b0265c
    ''' Retrieve the specified issue
Pierre-Yves Chibon b0265c
    '''
Pierre-Yves Chibon 33e9fc
Pierre-Yves Chibon 33e9fc
    subquery = session.query(
Pierre-Yves Chibon 33e9fc
        model.GlobalId,
Pierre-Yves Chibon 33e9fc
        sqlalchemy.over(
Pierre-Yves Chibon 33e9fc
            sqlalchemy.func.row_number(),
Pierre-Yves Chibon 33e9fc
            partition_by=model.GlobalId.project_id,
Pierre-Yves Chibon 33e9fc
            order_by=model.GlobalId.id
Pierre-Yves Chibon 33e9fc
        ).label('global_id')
Pierre-Yves Chibon 33e9fc
    ).subquery()
Pierre-Yves Chibon 33e9fc
Pierre-Yves Chibon b0265c
    query = session.query(
Pierre-Yves Chibon 33e9fc
        model.PullRequest,
Pierre-Yves Chibon 33e9fc
        subquery.c.global_id
Pierre-Yves Chibon 33e9fc
    ).filter(
Pierre-Yves Chibon 33e9fc
        subquery.c.request_id == model.PullRequest.id
Pierre-Yves Chibon 33e9fc
    ).filter(
Pierre-Yves Chibon 33e9fc
        subquery.c.project_id == model.PullRequest.project_id
Pierre-Yves Chibon 3a2a1c
    ).order_by(
Pierre-Yves Chibon 3a2a1c
        model.PullRequest.id
Pierre-Yves Chibon b0265c
    )
Pierre-Yves Chibon b0265c
Pierre-Yves Chibon b0265c
    if project_id:
Pierre-Yves Chibon b0265c
        query = query.filter(
Pierre-Yves Chibon b0265c
            model.PullRequest.project_id == project_id
Pierre-Yves Chibon b0265c
        )
Pierre-Yves Chibon b0265c
Pierre-Yves Chibon b0265c
    if project_id_from:
Pierre-Yves Chibon b0265c
        query = query.filter(
Pierre-Yves Chibon b0265c
            model.PullRequest.project_id_from == project_id_from
Pierre-Yves Chibon b0265c
        )
Pierre-Yves Chibon b0265c
Pierre-Yves Chibon b0265c
    if status is not None:
Pierre-Yves Chibon b0265c
        query = query.filter(
Pierre-Yves Chibon b0265c
            model.PullRequest.status == status
Pierre-Yves Chibon b0265c
        )
Pierre-Yves Chibon b0265c
Pierre-Yves Chibon b0265c
    return query.all()
Pierre-Yves Chibon b0265c
Pierre-Yves Chibon b0265c
Pierre-Yves Chibon 35112d
def get_pull_request(
Pierre-Yves Chibon 35112d
        session, requestid, project_id=None, project_id_from=None):
Pierre-Yves Chibon 35112d
    ''' Retrieve the specified issue
Pierre-Yves Chibon 35112d
    '''
Pierre-Yves Chibon 3f844b
Pierre-Yves Chibon 3f844b
    subquery = session.query(
Pierre-Yves Chibon 3f844b
        model.GlobalId,
Pierre-Yves Chibon 3f844b
        sqlalchemy.over(
Pierre-Yves Chibon 3f844b
            sqlalchemy.func.row_number(),
Pierre-Yves Chibon 3f844b
            partition_by=model.GlobalId.project_id,
Pierre-Yves Chibon 3f844b
            order_by=model.GlobalId.id
Pierre-Yves Chibon 3f844b
        ).label('global_id')
Pierre-Yves Chibon 3f844b
    ).subquery()
Pierre-Yves Chibon 3f844b
Pierre-Yves Chibon 35112d
    query = session.query(
Pierre-Yves Chibon 35112d
        model.PullRequest
Pierre-Yves Chibon 35112d
    ).filter(
Pierre-Yves Chibon 3f844b
        subquery.c.project_id == model.PullRequest.project_id
Pierre-Yves Chibon 3f844b
    ).filter(
Pierre-Yves Chibon 9a1f18
        subquery.c.request_id == model.PullRequest.id
Pierre-Yves Chibon 9a1f18
    ).filter(
Pierre-Yves Chibon 3f844b
        subquery.c.global_id == requestid
Pierre-Yves Chibon 9a1f18
    ).order_by(
Pierre-Yves Chibon 9a1f18
        model.PullRequest.id
Pierre-Yves Chibon 35112d
    )
Pierre-Yves Chibon 35112d
Pierre-Yves Chibon 35112d
    if project_id:
Pierre-Yves Chibon 35112d
        query = query.filter(
Pierre-Yves Chibon 35112d
            model.PullRequest.project_id == project_id
Pierre-Yves Chibon 35112d
        )
Pierre-Yves Chibon 35112d
Pierre-Yves Chibon 35112d
    if project_id_from:
Pierre-Yves Chibon 35112d
        query = query.filter(
Pierre-Yves Chibon 35112d
            model.PullRequest.project_id_from == project_id_from
Pierre-Yves Chibon 35112d
        )
Pierre-Yves Chibon 35112d
Pierre-Yves Chibon 35112d
    return query.first()
Pierre-Yves Chibon 42a02c
Pierre-Yves Chibon 42a02c
Pierre-Yves Chibon 375fde
def close_pull_request(session, request, user, merged=True):
Pierre-Yves Chibon 42a02c
    ''' Close the provided pull-request.
Pierre-Yves Chibon 42a02c
    '''
Pierre-Yves Chibon 42a02c
    request.status = False
Pierre-Yves Chibon 42a02c
    session.add(request)
Pierre-Yves Chibon 42a02c
    session.flush()
Pierre-Yves Chibon 4d1cbe
Johan Cwiklinski 86d9c4
    if merged == True:
Pierre-Yves Chibon 375fde
        progit.notify.notify_merge_pull_request(request, user)
Johan Cwiklinski 86d9c4
    else:
Pierre-Yves Chibon 375fde
        progit.notify.notify_cancelled_pull_request(request, user)
Pierre-Yves Chibon 5fdf85
Pierre-Yves Chibon 4d1cbe
Pierre-Yves Chibon 4d1cbe
def get_issue_statuses(session):
Pierre-Yves Chibon 4d1cbe
    ''' Return the complete list of status an issue can have.
Pierre-Yves Chibon 4d1cbe
    '''
Pierre-Yves Chibon 4d1cbe
    output = []
Pierre-Yves Chibon 4d1cbe
    statuses = session.query(model.StatusIssue).all()
Pierre-Yves Chibon 4d1cbe
    for status in statuses:
Pierre-Yves Chibon 4d1cbe
        output.append(status.status)
Pierre-Yves Chibon 4d1cbe
    return output
Pierre-Yves Chibon 7c47d2
Pierre-Yves Chibon 7c47d2
Pierre-Yves Chibon 7c47d2
def generate_gitolite_acls(session, configfile):
Pierre-Yves Chibon 7c47d2
    ''' Generate the configuration file for gitolite for all projects
Pierre-Yves Chibon 7c47d2
    on the forge.
Pierre-Yves Chibon 7c47d2
    '''
Pierre-Yves Chibon 7c47d2
    config = []
Pierre-Yves Chibon 7c47d2
    for project in session.query(model.Project).all():
Pierre-Yves Chibon 7f72e8
        if project.parent_id:
Pierre-Yves Chibon 7f72e8
            config.append('repo forks/%s' % project.fullname)
Pierre-Yves Chibon 7f72e8
        else:
Pierre-Yves Chibon 7f72e8
            config.append('repo %s' % project.fullname)
Pierre-Yves Chibon dfe989
        config.append('  RW+ = %s' % project.user.user)
Pierre-Yves Chibon 7c47d2
        for user in project.users:
Pierre-Yves Chibon 7c47d2
            if user != project.user:
Pierre-Yves Chibon 3562ab
                config.append('  RW+ = %s' % user.user.user)
Pierre-Yves Chibon 7c47d2
        config.append('')
Pierre-Yves Chibon 7c47d2
Pierre-Yves Chibon 3cec45
        config.append('repo docs/%s' % project.fullname)
Pierre-Yves Chibon 3cec45
        config.append('  RW+ = %s' % project.user.user)
Pierre-Yves Chibon 3cec45
        for user in project.users:
Pierre-Yves Chibon 3cec45
            if user != project.user:
Pierre-Yves Chibon 3562ab
                config.append('  RW+ = %s' % user.user.user)
Pierre-Yves Chibon 3cec45
        config.append('')
Pierre-Yves Chibon 3cec45
Pierre-Yves Chibon 1a37c6
        config.append('repo tickets/%s' % project.fullname)
Pierre-Yves Chibon 1a37c6
        config.append('  RW+ = %s' % project.user.user)
Pierre-Yves Chibon 1a37c6
        for user in project.users:
Pierre-Yves Chibon 1a37c6
            if user != project.user:
Pierre-Yves Chibon 3562ab
                config.append('  RW+ = %s' % user.user.user)
Pierre-Yves Chibon 1a37c6
        config.append('')
Pierre-Yves Chibon 1a37c6
Pierre-Yves Chibon edd115
    with open(configfile, 'w') as stream:
Pierre-Yves Chibon 9df3ed
        for row in config:
Pierre-Yves Chibon 30ec31
            stream.write(row + '\n')
Pierre-Yves Chibon c2aa0d
Pierre-Yves Chibon c2aa0d
Pierre-Yves Chibon c2aa0d
def set_up_user(session, username, fullname, user_email):
Pierre-Yves Chibon c2aa0d
    ''' Set up a new user into the database or update its information. '''
Pierre-Yves Chibon 03c11f
    user = get_user(session, username)
Pierre-Yves Chibon c2aa0d
    if not user:
Pierre-Yves Chibon c2aa0d
        user = model.User(
Pierre-Yves Chibon c2aa0d
            user=username,
Pierre-Yves Chibon c2aa0d
            fullname=fullname)
Pierre-Yves Chibon c2aa0d
        session.add(user)
Pierre-Yves Chibon c2aa0d
        session.flush()
Pierre-Yves Chibon c2aa0d
Pierre-Yves Chibon c2aa0d
    if user.fullname != fullname:
Pierre-Yves Chibon c2aa0d
        user.fullname = fullname
Pierre-Yves Chibon c2aa0d
        session.add(user)
Pierre-Yves Chibon c2aa0d
        session.flush()
Pierre-Yves Chibon c2aa0d
Pierre-Yves Chibon 8b2894
    emails = [email.email for email in user.emails]
Pierre-Yves Chibon 8b2894
    if user_email not in emails:
Pierre-Yves Chibon c2aa0d
        useremail = model.UserEmail(
Pierre-Yves Chibon c2aa0d
            user_id=user.id,
Pierre-Yves Chibon c2aa0d
            email=user_email)
Pierre-Yves Chibon c2aa0d
        session.add(useremail)
Pierre-Yves Chibon c2aa0d
        session.flush()
Pierre-Yves Chibon 8f25f2
Pierre-Yves Chibon 8f25f2
Pierre-Yves Chibon 8f25f2
def update_user_ssh(session, user, ssh_key):
Pierre-Yves Chibon 8f25f2
    ''' Set up a new user into the database or update its information. '''
Pierre-Yves Chibon 8f25f2
    if isinstance(user, basestring):
Pierre-Yves Chibon 8f25f2
        user = get_user(session, user)
Pierre-Yves Chibon 8f25f2
Pierre-Yves Chibon 8f25f2
    message = 'Nothing to update'
Pierre-Yves Chibon 8f25f2
Pierre-Yves Chibon 8f25f2
    if ssh_key != user.public_ssh_key:
Pierre-Yves Chibon 8f25f2
        user.public_ssh_key = ssh_key
Pierre-Yves Chibon 8f25f2
        session.add(user)
Pierre-Yves Chibon 8f25f2
        session.flush()
Pierre-Yves Chibon 8f25f2
        message = 'Public ssh key updated'
Pierre-Yves Chibon 8f25f2
Pierre-Yves Chibon 8f25f2
    return message
Pierre-Yves Chibon f7d963
Pierre-Yves Chibon f7d963
Pierre-Yves Chibon f7d963
def issue_to_json(issue):
Pierre-Yves Chibon f7d963
    """ Convert all the data related to an issue (a ticket) as json object.
Pierre-Yves Chibon f7d963
Pierre-Yves Chibon f7d963
    """
Pierre-Yves Chibon f7d963
    output = {
Pierre-Yves Chibon f7d963
        'title': issue.title,
Pierre-Yves Chibon f7d963
        'content': issue.content,
Pierre-Yves Chibon f7d963
        'status': issue.status,
Pierre-Yves Chibon f7d963
        'date_created': issue.date_created.strftime('%s'),
Pierre-Yves Chibon f7d963
        'user': {
Pierre-Yves Chibon f7d963
            'name': issue.user.user,
Pierre-Yves Chibon f7d963
            'emails': [email.email for email in issue.user.emails],
Pierre-Yves Chibon f7d963
        }
Pierre-Yves Chibon f7d963
    }
Pierre-Yves Chibon f7d963
Pierre-Yves Chibon f7d963
    comments = []
Pierre-Yves Chibon f7d963
    for comment in issue.comments:
Pierre-Yves Chibon f7d963
        cmt = {
Pierre-Yves Chibon f7d963
            'id': comment.id,
Pierre-Yves Chibon f7d963
            'comment': comment.comment,
Pierre-Yves Chibon f7d963
            'parent': comment.parent_id,
Pierre-Yves Chibon f7d963
            'date_created': comment.date_created.strftime('%s'),
Pierre-Yves Chibon f7d963
            'user': {
Pierre-Yves Chibon f7d963
                'name': comment.user.user,
Pierre-Yves Chibon f7d963
                'emails': [email.email for email in comment.user.emails],
Pierre-Yves Chibon f7d963
            }
Pierre-Yves Chibon f7d963
        }
Pierre-Yves Chibon f7d963
        comments.append(cmt)
Pierre-Yves Chibon f7d963
Pierre-Yves Chibon f7d963
    output['comments'] = comments
Pierre-Yves Chibon f7d963
Pierre-Yves Chibon f7d963
    return json.dumps(output)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
def update_git_ticket(issue, repo, ticketfolder):
Pierre-Yves Chibon d133cd
    """ Update the given issue in its git.
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    This method forks the provided repo, add/edit the issue whose file name
Pierre-Yves Chibon d133cd
    is defined by the uid field of the issue and if there are additions/
Pierre-Yves Chibon d133cd
    changes commit them and push them back to the original repo.
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    """
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Get the fork
Pierre-Yves Chibon d133cd
    repopath = os.path.join(ticketfolder, repo.path)
Pierre-Yves Chibon d133cd
    ticket_repo = pygit2.Repository(repopath)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Clone the repo into a temp folder
Pierre-Yves Chibon d133cd
    newpath = tempfile.mkdtemp()
Pierre-Yves Chibon d133cd
    new_repo = pygit2.clone_repository(repopath, newpath)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    file_path = os.path.join(newpath, issue.uid)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Get the current index
Pierre-Yves Chibon d133cd
    index = new_repo.index
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Are we adding files
Pierre-Yves Chibon d133cd
    added = False
Pierre-Yves Chibon d133cd
    if not os.path.exists(file_path):
Pierre-Yves Chibon d133cd
        added = True
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Write down what changed
Pierre-Yves Chibon d133cd
    with open(file_path, 'w') as stream:
Pierre-Yves Chibon d133cd
        stream.write(issue_to_json(issue))
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Retrieve the list of files that changed
Pierre-Yves Chibon d133cd
    diff = new_repo.diff()
Pierre-Yves Chibon d133cd
    files = [patch.new_file_path for patch in diff]
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Add the changes to the index
Pierre-Yves Chibon d133cd
    if added:
Pierre-Yves Chibon d133cd
        index.add(issue.uid)
Pierre-Yves Chibon d133cd
    for filename in files:
Pierre-Yves Chibon d133cd
        index.add(filename)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # If not change, return
Pierre-Yves Chibon 63324c
    if not files and not added:
Pierre-Yves Chibon d133cd
        shutil.rmtree(newpath)
Pierre-Yves Chibon d133cd
        return
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # See if there is a parent to this commit
Pierre-Yves Chibon d133cd
    parent = None
Pierre-Yves Chibon d133cd
    try:
Pierre-Yves Chibon d133cd
        parent = new_repo.head.get_object().oid
Pierre-Yves Chibon d133cd
    except pygit2.GitError:
Pierre-Yves Chibon d133cd
        pass
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    parents = []
Pierre-Yves Chibon d133cd
    if parent:
Pierre-Yves Chibon d133cd
        parents.append(parent)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Author/commiter will always be this one
Pierre-Yves Chibon d133cd
    author = pygit2.Signature(name='progit', email='progit')
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Actually commit
Pierre-Yves Chibon d133cd
    sha = new_repo.create_commit(
Pierre-Yves Chibon d133cd
        'refs/heads/master',
Pierre-Yves Chibon d133cd
        author,
Pierre-Yves Chibon d133cd
        author,
Pierre-Yves Chibon d133cd
        'Updated ticket %s: %s' % (issue.uid, issue.title),
Pierre-Yves Chibon d133cd
        new_repo.index.write_tree(),
Pierre-Yves Chibon d133cd
        parents)
Pierre-Yves Chibon d133cd
    index.write()
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Push to origin
Pierre-Yves Chibon d133cd
    ori_remote = new_repo.remotes[0]
Pierre-Yves Chibon d133cd
    master_ref = new_repo.lookup_reference('HEAD').resolve()
Pierre-Yves Chibon d133cd
    refname = '%s:%s' % (master_ref.name, master_ref.name)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    ori_remote.push(refname)
Pierre-Yves Chibon d133cd
Pierre-Yves Chibon d133cd
    # Remove the clone
Pierre-Yves Chibon d133cd
    shutil.rmtree(newpath)
Pierre-Yves Chibon 95245b
Pierre-Yves Chibon 95245b
Pierre-Yves Chibon 95245b
def avatar_url(username, size=64, default='retro'):
Pierre-Yves Chibon 95245b
    openid = "http://%s.id.fedoraproject.org/" % username
Pierre-Yves Chibon 95245b
    return avatar_url_from_openid(openid, size, default)
Pierre-Yves Chibon 95245b
Pierre-Yves Chibon 95245b
Pierre-Yves Chibon 95245b
def avatar_url_from_openid(openid, size=64, default='retro', dns=False):
Pierre-Yves Chibon 95245b
    """
Pierre-Yves Chibon 95245b
    Our own implementation since fas doesn't support this nicely yet.
Pierre-Yves Chibon 95245b
    """
Pierre-Yves Chibon 95245b
Pierre-Yves Chibon 95245b
    if dns:  # pragma: no cover
Pierre-Yves Chibon 95245b
        # This makes an extra DNS SRV query, which can slow down our webapps.
Pierre-Yves Chibon 95245b
        # It is necessary for libravatar federation, though.
Pierre-Yves Chibon 95245b
        import libravatar
Pierre-Yves Chibon 95245b
        return libravatar.libravatar_url(
Pierre-Yves Chibon 95245b
            openid=openid,
Pierre-Yves Chibon 95245b
            size=size,
Pierre-Yves Chibon 95245b
            default=default,
Pierre-Yves Chibon 95245b
        )
Pierre-Yves Chibon 95245b
    else:
Pierre-Yves Chibon 95245b
        import urllib, hashlib
Pierre-Yves Chibon 95245b
        query = urllib.urlencode({'s': size, 'd': default})
Pierre-Yves Chibon 95245b
        hash = hashlib.sha256(openid).hexdigest()
Pierre-Yves Chibon 95245b
        return "https://seccdn.libravatar.org/avatar/%s?%s" % (hash, query)
Pierre-Yves Chibon b0d578
Pierre-Yves Chibon b0d578
Pierre-Yves Chibon b0d578
def get_session_by_visitkey(session, sessionid):
Pierre-Yves Chibon b0d578
    ''' Return a specified VisitUser via its session identifier (visit_key).
Pierre-Yves Chibon b0d578
Pierre-Yves Chibon b0d578
    :arg session: the session with which to connect to the database.
Pierre-Yves Chibon b0d578
Pierre-Yves Chibon b0d578
    '''
Pierre-Yves Chibon b0d578
    query = session.query(
Pierre-Yves Chibon 4313f4
        model.ProgitUserVisit
Pierre-Yves Chibon b0d578
    ).filter(
Pierre-Yves Chibon 4313f4
        model.ProgitUserVisit.visit_key == sessionid
Pierre-Yves Chibon b0d578
    )
Pierre-Yves Chibon b0d578
Pierre-Yves Chibon b0d578
    return query.first()
Pierre-Yves Chibon a8a719
Pierre-Yves Chibon a8a719
Pierre-Yves Chibon a8a719
def get_groups(session):
Pierre-Yves Chibon a8a719
    ''' Return the list of groups present in the database.
Pierre-Yves Chibon a8a719
Pierre-Yves Chibon a8a719
    :arg session: the session with which to connect to the database.
Pierre-Yves Chibon a8a719
Pierre-Yves Chibon a8a719
    '''
Pierre-Yves Chibon a8a719
    query = session.query(
Pierre-Yves Chibon a8a719
        model.ProgitGroup
Pierre-Yves Chibon a8a719
    ).order_by(
Pierre-Yves Chibon a8a719
        model.ProgitGroup.group_name
Pierre-Yves Chibon a8a719
    )
Pierre-Yves Chibon a8a719
Pierre-Yves Chibon a8a719
    return query.all()
Pierre-Yves Chibon b97cda
Pierre-Yves Chibon b97cda
Pierre-Yves Chibon b97cda
def get_group(session, group):
Pierre-Yves Chibon b97cda
    ''' Return a specific group for the specified group name.
Pierre-Yves Chibon b97cda
Pierre-Yves Chibon b97cda
    :arg session: the session with which to connect to the database.
Pierre-Yves Chibon b97cda
Pierre-Yves Chibon b97cda
    '''
Pierre-Yves Chibon b97cda
    query = session.query(
Pierre-Yves Chibon b97cda
        model.ProgitGroup
Pierre-Yves Chibon b97cda
    ).filter(
Pierre-Yves Chibon b97cda
        model.ProgitGroup.group_name == group
Pierre-Yves Chibon b97cda
    ).order_by(
Pierre-Yves Chibon b97cda
        model.ProgitGroup.group_name
Pierre-Yves Chibon b97cda
    )
Pierre-Yves Chibon b97cda
Pierre-Yves Chibon b97cda
    return query.first()
Pierre-Yves Chibon 025568
Pierre-Yves Chibon 025568
Pierre-Yves Chibon 025568
def get_users_by_group(session, group):
Pierre-Yves Chibon 025568
    ''' Return the list of users for a specified group.
Pierre-Yves Chibon 025568
Pierre-Yves Chibon 025568
    :arg session: the session with which to connect to the database.
Pierre-Yves Chibon 025568
Pierre-Yves Chibon 025568
    '''
Pierre-Yves Chibon 025568
    query = session.query(
Pierre-Yves Chibon 025568
        model.User
Pierre-Yves Chibon 025568
    ).filter(
Pierre-Yves Chibon 025568
        model.User.id == model.ProgitUserGroup.user_id
Pierre-Yves Chibon 025568
    ).filter(
Pierre-Yves Chibon 025568
        model.ProgitUserGroup.group_id == model.ProgitGroup.id
Pierre-Yves Chibon 025568
    ).filter(
Pierre-Yves Chibon 025568
        model.ProgitGroup.group_name == group
Pierre-Yves Chibon 025568
    ).order_by(
Pierre-Yves Chibon 025568
        model.User.user
Pierre-Yves Chibon 025568
    )
Pierre-Yves Chibon 025568
Pierre-Yves Chibon 025568
    return query.all()
Pierre-Yves Chibon f77dac
Pierre-Yves Chibon f77dac
Pierre-Yves Chibon f77dac
def get_user_group(session, userid, groupid):
Pierre-Yves Chibon f77dac
    ''' Return a specific user_group for the specified group and user
Pierre-Yves Chibon f77dac
    identifiers.
Pierre-Yves Chibon f77dac
Pierre-Yves Chibon f77dac
    :arg session: the session with which to connect to the database.
Pierre-Yves Chibon f77dac
Pierre-Yves Chibon f77dac
    '''
Pierre-Yves Chibon f77dac
    query = session.query(
Pierre-Yves Chibon f77dac
        model.ProgitUserGroup
Pierre-Yves Chibon f77dac
    ).filter(
Pierre-Yves Chibon f77dac
        model.ProgitUserGroup.user_id == userid
Pierre-Yves Chibon f77dac
    ).filter(
Pierre-Yves Chibon f77dac
        model.ProgitUserGroup.group_id == groupid
Pierre-Yves Chibon f77dac
    )
Pierre-Yves Chibon f77dac
Pierre-Yves Chibon f77dac
    return query.first()