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 d8110b
import os
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 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 359db9
def add_issue_comment(session, issue, comment, user):
Pierre-Yves Chibon 359db9
    ''' Add a comment to an issue. '''
Pierre-Yves Chibon 359db9
    isse_comment = model.IssueComment(
Pierre-Yves Chibon 359db9
        issue_id=issue.id,
Pierre-Yves Chibon 359db9
        comment=comment,
Pierre-Yves Chibon 359db9
        user=user,
Pierre-Yves Chibon 359db9
    )
Pierre-Yves Chibon 359db9
    session.add(isse_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 359db9
    return 'Comment added'
Pierre-Yves Chibon 359db9
Pierre-Yves Chibon 359db9
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 6160b6
def new_project(session, user, name, gitfolder, docfolder,
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 d8110b
            'The project "%s" already exists' % name
Pierre-Yves Chibon d8110b
        )
Pierre-Yves Chibon d8110b
Pierre-Yves Chibon d8110b
    project = model.Project(
Pierre-Yves Chibon d8110b
        name=name,
Pierre-Yves Chibon d8110b
        description=description,
Pierre-Yves Chibon d8110b
        user=user,
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 4b7a7d
            'The docs "%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 d8110b
    return 'Project "%s" created' % name
Mathieu Bridon eb729c
Pierre-Yves Chibon f74093
Pierre-Yves Chibon 33ff2c
def new_issue(session, repo, title, content, user):
Pierre-Yves Chibon 33ff2c
    ''' Create a new issue for the specified repo. '''
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 33ff2c
        user=user,
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 33ff2c
    return 'Issue created'
Pierre-Yves Chibon 33ff2c
Pierre-Yves Chibon 33ff2c
Pierre-Yves Chibon 62ec72
def new_pull_request(
Pierre-Yves Chibon 62ec72
        session, repo, repo_from, title, user, stop_id, start_id=None):
Pierre-Yves Chibon 472a61
    ''' Create a new pull request on the specified repo. '''
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 472a61
        title=title,
Pierre-Yves Chibon 472a61
        start_id=start_id,
Pierre-Yves Chibon 472a61
        stop_id=stop_id,
Pierre-Yves Chibon 472a61
        user=user,
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 472a61
    return 'Request created'
Pierre-Yves Chibon 472a61
Pierre-Yves Chibon 472a61
Pierre-Yves Chibon 4d1cbe
def edit_issue(session, issue, 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 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 6160b6
def fork_project(session, user, repo, gitfolder, forkfolder, docfolder):
Pierre-Yves Chibon 652c2c
    ''' Fork a given project into the user's forks. '''
Pierre-Yves Chibon e54062
    reponame = os.path.join(gitfolder, repo.path)
Pierre-Yves Chibon e54062
    forkreponame = os.path.join(forkfolder, repo.path)
Pierre-Yves Chibon 652c2c
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 652c2c
    project = model.Project(
Pierre-Yves Chibon 652c2c
        name=repo.name,
Pierre-Yves Chibon 652c2c
        description=repo.description,
Pierre-Yves Chibon 652c2c
        user=user,
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 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
    '''
Mathieu Bridon eb729c
    projects = session.query(model.Project)
Mathieu Bridon eb729c
Pierre-Yves Chibon ea0ee1
    if username is not None:
Pierre-Yves Chibon ea0ee1
        projects = projects.filter_by(
Pierre-Yves Chibon ea0ee1
            user=username
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 929595
            model.Project.user == user
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 562d75
    query = session.query(
Pierre-Yves Chibon 562d75
        model.Issue
Pierre-Yves Chibon 562d75
    ).filter(
Pierre-Yves Chibon 562d75
        model.Issue.project_id == repo.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 feb7c1
def get_issue(session, issueid):
Pierre-Yves Chibon 562d75
    ''' Retrieve the specified issue
Pierre-Yves Chibon 562d75
    '''
Pierre-Yves Chibon 562d75
    query = session.query(
Pierre-Yves Chibon 562d75
        model.Issue
Pierre-Yves Chibon 562d75
    ).filter(
Pierre-Yves Chibon 562d75
        model.Issue.id == issueid
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 b0265c
    query = session.query(
Pierre-Yves Chibon b0265c
        model.PullRequest
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 35112d
    query = session.query(
Pierre-Yves Chibon 35112d
        model.PullRequest
Pierre-Yves Chibon 35112d
    ).filter(
Pierre-Yves Chibon 35112d
        model.PullRequest.id == requestid
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 42a02c
def close_pull_request(session, request):
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
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