<commit_before>#!/usr/bin/env python

import os
import subprocess
import argparse
import pprint
import logging
import string
import yaml

from signal import pause

from fabric.api import *
from fabric.utils import *
from fabric.contrib.console import confirm
from fabric.contrib.files import *
from fabric.colors import *

from fsevents import Observer
from fsevents import Stream

import gntp
import gntp.notifier

# These are a bunch of constants that identify different type of file system
#  events and the fsevents library uses.
kFSEventStreamEventFlagNone              = 0x00000000
kFSEventStreamEventFlagMustScanSubDirs   = 0x00000001
kFSEventStreamEventFlagUserDropped       = 0x00000002
kFSEventStreamEventFlagKernelDropped     = 0x00000004
kFSEventStreamEventFlagEventIdsWrapped   = 0x00000008
kFSEventStreamEventFlagHistoryDone       = 0x00000010
kFSEventStreamEventFlagRootChanged       = 0x00000020
kFSEventStreamEventFlagMount             = 0x00000040
kFSEventStreamEventFlagUnmount           = 0x00000080
kFSEventStreamEventFlagItemCreated       = 0x00000100 #256
kFSEventStreamEventFlagItemRemoved       = 0x00000200
kFSEventStreamEventFlagItemInodeMetaMod  = 0x00000400
kFSEventStreamEventFlagItemRenamed       = 0x00000800
kFSEventStreamEventFlagItemModified      = 0x00001000
kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000
kFSEventStreamEventFlagItemChangeOwner   = 0x00004000
kFSEventStreamEventFlagItemXattrMod      = 0x00008000
kFSEventStreamEventFlagItemIsFile        = 0x00010000
kFSEventStreamEventFlagItemIsDir         = 0x00020000
kFSEventStreamEventFlagItemIsSymlink     = 0x00040000

@task
def init_remote_master_repository( remote_path, local_branch, git_ignore_lines ):

    puts("Setting up %s" % remote_path)

    if not exists( remote_path ):
        abort("The remote path does not exist: %s" % remote_path)

    git_repo = get_remote_git_repo( remote_path )

    if exists ( git_repo ):
        puts("The git repo already exist: %s" % git_repo)
    else:
        with cd ( remote_path ):
            run( "git init" )

        update_git_ignore_file( remote_path, git_ignore_lines )

        with cd ( remote_path ):
            run( "git add .gitignore" )
            run( "git commit -m 'Inital Commit'" )
            run( "git add ." )
            run( "git commit -m 'add project'" )

@task
def update_git_ignore_file( remote_path, git_ignore_lines ):
    with cd ( remote_path ):
        for line in git_ignore_lines:
            if not contains( '.gitignore_new', line ):
                append( '.gitignore_new', line )

        run('mv .gitignore_new .gitignore', shell=False)

@task
def remote_has_modified_files( remote_path ):
    with cd( remote_path ):
        with settings( warn_only=True ):
            with hide('running', 'status', 'warnings', 'stderr', 'stdout'):

                git_status_output = run( "git status . " )

                git_clean_status = "nothing to commit (working directory clean)"
                puts( red( git_status_output ) )
                if git_clean_status in git_status_output:
                    puts(cyan("%s (remote) is clean." % remote_path))
                    return False
                else:
                    puts(
                        cyan(
                            " %s (remote) has uncommitted changes."
                            % remote_path
                        )
                    )
                    return True

@task
def local_has_modified_files ( local_path ):
    with lcd ( local_path ):
        with settings( warn_only=True ):
            with hide('running', 'status', 'warnings', 'stderr', 'stdout'):

                git_status_output = local( "git status . ", capture=True )

                git_clean_status = "nothing to commit (working directory clean)"
                if git_clean_status in git_status_output:
                    puts( cyan("%s (local) is clean." % local_path ) )
                    return False
                else:
                    puts(
                        cyan( "%s (local) has uncommitted changes." % local_path )
                    )
                    return True

@task
def get_remote_git_repo ( remote_path ):
    git_repo = os.path.join( remote_path, '.git' )
    return git_repo

@task
def get_local_git_clone ( remote_path, local_path ):
    local( "git clone ssh://%s/%s %s" % ( env.host, remote_path, local_path ) )

@task
def commit_remote_modified_files ( remote_path ):
    if not remote_has_modified_files( remote_path ):
        return True
    with cd( remote_path ):
        run( "git add ." )
        run( "git commit -a -m 'committing all changes from %s'" % (remote_path) )
        return True

@task
def push_remote_master ( remote_path, local_branch ):

    remote_has_local_branch( remote_path, local_branch )

    with cd ( remote_path ):
        run( "git push origin %s" % (local_branch) )
        return True

def remote_has_local_branch ( remote_path, local_branch ):
    with cd ( remote_path ):
        git_branches = run ( 'git branch' )
        puts( cyan( git_branches ) )

@task
def commit_and_push_remote ( remote_path ) :
    commit_amm_modified_files( amm_name )
    push_amm_master( amm_name )

@task
def pull_local ( local_path ):
    with lcd ( local_path ):
        local ( 'git fetch origin' )

@task
def merge_local_master ( local_path ):
    with lcd ( local_path ):
        local ( 'git merge origin/master' )

@task
def pull_and_merge_local ( local_path ):
    pull_local( local_path )
    merge_local_master( local_path )

@task
def commit_local_modified_files ( local_path ):
    with lcd ( local_path ):
        if local_has_modified_files( local_path ):
            local( "git add ." )
            local(
                "git commit -a -m 'committing all changes from a local machine'"
            )
    return True

@task
def push_local_to_remote ( local_path, local_branch ):
    if not local_has_local_branch( local_path, local_branch ):
        local_create_local_branch( local_path, local_branch )

    with lcd ( local_path ):
        local( "git push origin %s" % ( local_branch ) )

def local_create_local_branch ( local_path, local_branch ):
    with lcd ( local_path ):
        local ( 'git branch %s' % ( local_branch ), capture=True )

def local_has_local_branch ( local_path, local_branch ):

    puts( cyan( local_path ) )

    with lcd ( local_path ):
        git_branches = local ( 'git branch', capture=True )
        for branch in git_branches.split( ):
            if branch == local_branch:
                return True
        return False

@task
def merge_local_to_remote ( remote_path, local_branch ):
    with cd ( remote_path ):
        run ( 'git merge %s' % (local_branch) )

@task
def send_local_changes_to_remote ( remote_path, local_path, local_branch ):
    commit_local_modified_files( local_path )
    push_local_to_remote( local_path, local_branch )
    merge_local_to_remote( remote_path, local_branch )

@task
def send_remote_changes_to_local ( remote_path, local_path ):
    commit_remote_modified_files( remote_path )
    pull_and_merge_local( local_path )

@task
def sync ( remote_path, local_path, local_branch, git_ignore_lines ):

    if not os.path.exists( local_path ):
        init( remote_path, local_path, local_branch, git_ignore_lines )

    if remote_has_modified_files( remote_path ):
        send_remote_changes_to_local( remote_path, local_path )

    send_local_changes_to_remote( remote_path, local_path, local_branch )

def initial_sync ( remote_path, local_path, local_branch, git_ignore_lines ):
    if not os.path.exists( local_path ):
        init( remote_path, local_path, local_branch, git_ignore_lines )

    send_remote_changes_to_local( remote_path, local_path )
    send_local_changes_to_remote( remote_path, local_path, local_branch )

@task
def init ( remote_path, local_path, local_branch, git_ignore_lines ):
    init_remote_master_repository( remote_path, local_branch, git_ignore_lines )
    get_local_git_clone ( remote_path, local_path )
    local_create_local_branch( local_path, local_branch )
    with lcd( local_path ):
        local( "git checkout %s" % (local_branch) )

# Setup Parser
parser = argparse.ArgumentParser(
    description='Use git to sync a site on pion to your local machine.'
)

parser.add_argument(
    'config_file'
    , nargs='?'
    , type=argparse.FileType('r')
)

args = parser.parse_args()

# Read in config file.
config = yaml.load(args.config_file)

local_path       = config['local_path']
local_branch     = config['local_branch']
remote_path      = config['remote_path']
remote_host      = config['remote_host']
remote_user      = config['remote_user']
git_ignore_lines = config['git_ignore']
observer         = Observer()

# Sort the git ignore lines.
git_ignore_lines = sorted( git_ignore_lines )

if remote_user:
    remote_host = remote_user + '@' + remote_host

# Setup Growl notifications.
growl = gntp.notifier.GrowlNotifier(
    applicationName      = "GitSync",
    hostname             = "localhost",
    notifications        = [
        'Sync Ready',
        "Sync Starting",
        "Sync Done",
        "Sync Failed",
    ],
    defaultNotifications = [
        'Sync Ready',
        "Sync Starting",
        "Sync Done",
        "Sync Failed",
    ],
)
growl.register()

def growl_start( ):
    growl.notify(
        noteType    = "Sync Starting",
        title       = "Sync is starting",
        description = "Starting to sync %s and %s on %s" % (
            local_path,
            remote_path,
            remote_host,
        ),
    )

def growl_done( ):
    growl.notify(
        noteType    = "Sync Done",
        title       = "Sync is finished",
        description = "Completed sync of %s and %s on %s" % (
            local_path,
            remote_path,
            remote_host,
        ),
    )

def growl_failed( ):
    growl.notify(
        noteType    = "Sync Failed",
        title       = "Sync Failed",
        description = "Failed to sync %s and %s on %s" % (
            local_path,
            remote_path,
            remote_host,
        ),
    )

def run_remote_has_modified_files( ):
    result = execute(
        remote_has_modified_files
        , host             = remote_host
        , remote_path      = remote_path
    )
    return result[remote_host]

def run_send_remote_changes_to_local( ):
    result = execute(
        send_remote_changes_to_local
        , host             = remote_host
        , remote_path      = remote_path
        , local_path       = local_path
    )
    return result[remote_host]

def run_send_local_changes_to_remote( ):
    result = execute(
        send_local_changes_to_remote
        , host             = remote_host
        , remote_path      = remote_path
        , local_path       = local_path
        , local_branch     = local_branch
    )
    return result[remote_host]

def run_initial_sync( ):
    growl_start( )
    execute(
        initial_sync
        , host             = remote_host
        , remote_path      = remote_path
        , local_path       = local_path
        , local_branch     = local_branch
        , git_ignore_lines = git_ignore_lines
    )
    growl_done( )

def callback( event ):

    global observer, stream

    if event.mask == kFSEventStreamEventFlagItemCreated:
        # Sublime Text Seems to trigger a lot of these and they don't seem to
        #  warrant a new sync, so lets skip these for now.
        return

    filename = event.name
    git_dir  = os.path.join( local_path, '.git' )

    if git_dir in filename:
        #Skip sync for file change that are in the .git directory.
        return

    growl_start( )

    try:
        if run_remote_has_modified_files():
            # Stop observing.
            observer.unschedule(stream)
            observer.stop()

            run_send_remote_changes_to_local()

            # Start observing again.
            observer = Observer()
            observer.schedule(stream)
            observer.start()

        run_send_local_changes_to_remote()

    except Exception as inst:
        print "sync failed."
        print type(inst)
        print inst.args
        print inst
        growl_failed( )
        raise
    else:
        growl_done( )

run_initial_sync( )

# Start watching the directory
stream = Stream(callback, local_path, file_events=True)
observer.schedule(stream)
observer.start()

try:
    while 1:
        pause( )
except KeyboardInterrupt:
    observer.unschedule(stream)
    observer.stop( )
    observer.join( )

<commit_msg>Resolving conflicts<commit_after>#!/usr/bin/env python

import os
import subprocess
import argparse
import pprint
import logging
import string
import yaml
import socket

from signal import pause

from fabric.api import *
from fabric.utils import *
from fabric.contrib.console import confirm
from fabric.contrib.files import *
from fabric.colors import *

from fsevents import Observer
from fsevents import Stream

import gntp
import gntp.notifier
from gntp.notifier import mini

# These are a bunch of constants that identify different type of file system
#  events and the fsevents library uses.
kFSEventStreamEventFlagNone              = 0x00000000
kFSEventStreamEventFlagMustScanSubDirs   = 0x00000001
kFSEventStreamEventFlagUserDropped       = 0x00000002
kFSEventStreamEventFlagKernelDropped     = 0x00000004
kFSEventStreamEventFlagEventIdsWrapped   = 0x00000008
kFSEventStreamEventFlagHistoryDone       = 0x00000010
kFSEventStreamEventFlagRootChanged       = 0x00000020
kFSEventStreamEventFlagMount             = 0x00000040
kFSEventStreamEventFlagUnmount           = 0x00000080
kFSEventStreamEventFlagItemCreated       = 0x00000100 #256
kFSEventStreamEventFlagItemRemoved       = 0x00000200
kFSEventStreamEventFlagItemInodeMetaMod  = 0x00000400
kFSEventStreamEventFlagItemRenamed       = 0x00000800
kFSEventStreamEventFlagItemModified      = 0x00001000
kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000
kFSEventStreamEventFlagItemChangeOwner   = 0x00004000
kFSEventStreamEventFlagItemXattrMod      = 0x00008000
kFSEventStreamEventFlagItemIsFile        = 0x00010000
kFSEventStreamEventFlagItemIsDir         = 0x00020000
kFSEventStreamEventFlagItemIsSymlink     = 0x00040000

@task
def init_remote_master_repository( remote_path, local_branch, git_ignore_lines ):

    puts("Setting up %s" % remote_path)

    if not exists( remote_path ):
        abort("The remote path does not exist: %s" % remote_path)

    git_repo = get_remote_git_repo( remote_path )

    if exists ( git_repo ):
        puts("The git repo already exist: %s" % git_repo)
    else:
        with cd ( remote_path ):
            run( "git init" )

        update_git_ignore_file( remote_path, git_ignore_lines )

        with cd ( remote_path ):
            run( "git add .gitignore" )
            run( "git commit -m 'Inital Commit'" )
            run( "git add ." )
            run( "git commit -m 'add project'" )

@task
def update_git_ignore_file( remote_path, git_ignore_lines ):
    with cd ( remote_path ):
        for line in git_ignore_lines:
            if not contains( '.gitignore_new', line ):
                append( '.gitignore_new', line )

        run('mv .gitignore_new .gitignore', shell=False)

@task
def remote_has_modified_files( remote_path ):
    with cd( remote_path ):
        with settings( warn_only=True ):
            with hide('running', 'status', 'warnings', 'stderr', 'stdout'):

                git_status_output = run( "git status . " )

                git_clean_status = "nothing to commit (working directory clean)"
                puts( red( git_status_output ) )
                if git_clean_status in git_status_output:
                    puts(cyan("%s (remote) is clean." % remote_path))
                    return False
                else:
                    puts(
                        cyan(
                            " %s (remote) has uncommitted changes."
                            % remote_path
                        )
                    )
                    return True

@task
def local_has_modified_files ( local_path ):
    with lcd ( local_path ):
        with settings( warn_only=True ):
            with hide('running', 'status', 'warnings', 'stderr', 'stdout'):

                git_status_output = local( "git status . ", capture=True )

                git_clean_status = "nothing to commit (working directory clean)"
                if git_clean_status in git_status_output:
                    puts( cyan("%s (local) is clean." % local_path ) )
                    return False
                else:
                    puts(
                        cyan( "%s (local) has uncommitted changes." % local_path )
                    )
                    return True

@task
def get_remote_git_repo ( remote_path ):
    git_repo = os.path.join( remote_path, '.git' )
    return git_repo

@task
def get_local_git_clone ( remote_path, local_path ):
    local( "git clone ssh://%s/%s %s" % ( env.host, remote_path, local_path ) )

@task
def commit_remote_modified_files ( remote_path ):
    if not remote_has_modified_files( remote_path ):
        return True
    with cd( remote_path ):
        run( "git add ." )
        run( "git commit -a -m 'committing all changes from %s'" % (remote_path) )
        return True

@task
def push_remote_master ( remote_path, local_branch ):

    remote_has_local_branch( remote_path, local_branch )

    with cd ( remote_path ):
        run( "git push origin %s" % (local_branch) )
        return True

def remote_has_local_branch ( remote_path, local_branch ):
    with cd ( remote_path ):
        git_branches = run ( 'git branch' )
        puts( cyan( git_branches ) )

@task
def commit_and_push_remote ( remote_path ) :
    commit_amm_modified_files( amm_name )
    push_amm_master( amm_name )

@task
def pull_local ( local_path ):
    with lcd ( local_path ):
        local ( 'git fetch origin' )

@task
def merge_local_master ( local_path ):
    with lcd ( local_path ):
        local ( 'git merge origin/master' )

@task
def pull_and_merge_local ( local_path ):
    pull_local( local_path )
    merge_local_master( local_path )

@task
def commit_local_modified_files ( local_path ):
    with lcd ( local_path ):
        if local_has_modified_files( local_path ):
            local( "git add ." )
            local(
                "git commit -a -m 'committing all changes from a local machine'"
            )
    return True

@task
def push_local_to_remote ( local_path, local_branch ):
    if not local_has_local_branch( local_path, local_branch ):
        local_create_local_branch( local_path, local_branch )

    with lcd ( local_path ):
        local( "git push origin %s" % ( local_branch ) )

def local_create_local_branch ( local_path, local_branch ):
    with lcd ( local_path ):
        local ( 'git branch %s' % ( local_branch ), capture=True )

def local_has_local_branch ( local_path, local_branch ):

    puts( cyan( local_path ) )

    with lcd ( local_path ):
        git_branches = local ( 'git branch', capture=True )
        for branch in git_branches.split( ):
            if branch == local_branch:
                return True
        return False

@task
def merge_local_to_remote ( remote_path, local_branch ):
    with cd ( remote_path ):
        run ( 'git merge %s' % (local_branch) )

@task
def send_local_changes_to_remote ( remote_path, local_path, local_branch ):
    commit_local_modified_files( local_path )
    push_local_to_remote( local_path, local_branch )
    merge_local_to_remote( remote_path, local_branch )

@task
def send_remote_changes_to_local ( remote_path, local_path ):
    commit_remote_modified_files( remote_path )
    pull_and_merge_local( local_path )

@task
def sync ( remote_path, local_path, local_branch, git_ignore_lines ):

    if not os.path.exists( local_path ):
        init( remote_path, local_path, local_branch, git_ignore_lines )

    if remote_has_modified_files( remote_path ):
        send_remote_changes_to_local( remote_path, local_path )

    send_local_changes_to_remote( remote_path, local_path, local_branch )

def initial_sync ( remote_path, local_path, local_branch, git_ignore_lines ):
    if not os.path.exists( local_path ):
        init( remote_path, local_path, local_branch, git_ignore_lines )

    send_remote_changes_to_local( remote_path, local_path )
    send_local_changes_to_remote( remote_path, local_path, local_branch )

@task
def init ( remote_path, local_path, local_branch, git_ignore_lines ):
    init_remote_master_repository( remote_path, local_branch, git_ignore_lines )
    get_local_git_clone( remote_path, local_path )
    local_create_local_branch( local_path, local_branch )
    with lcd( local_path ):
        local( "git checkout %s" % (local_branch) )

# Setup Parser
parser = argparse.ArgumentParser(
    description='Use git to sync a site on pion to your local machine.'
)

parser.add_argument(
    'config_file'
    , nargs='?'
    , type=argparse.FileType('r')
)

args = parser.parse_args()

# Read in config file.
config = yaml.load(args.config_file)

local_path       = config['local_path']
local_branch     = config['local_branch']
remote_path      = config['remote_path']
remote_host      = config['remote_host']
remote_user      = config['remote_user']
git_ignore_lines = config['git_ignore']
observer         = Observer()

# Sort the git ignore lines.
git_ignore_lines = sorted( git_ignore_lines )

if remote_user:
    remote_host = remote_user + '@' + remote_host

def growl_start( ):
    try:
        mini(
            "Starting to sync %s and %s on %s" % (
                local_path,
                remote_path,
                remote_host,
            )
            , applicationName='GitSync'
            , noteType='Message'
            , title='GitSync Starting'
        )
    except socket.error:
        print 'Unable to send growl notification.'
        print 'Growl is probably not running.'
        print 'GitSync Starting'

def growl_done( ):
    try:
        mini(
            "Completed sync of %s and %s on %s" % (
                local_path,
                remote_path,
                remote_host,
            )
            , applicationName='GitSync'
            , noteType='Message'
            , title='GitSync Finished'
        )
    except socket.error:
        print 'Unable to send growl notification.'
        print 'Growl is probably not running.'
        print 'GitSync Finished'

def growl_failed( ):
    growl.notify(
        noteType    = "Sync Failed",
        title       = "Sync Failed",
        description = "Failed to sync %s and %s on %s" % (
            local_path,
            remote_path,
            remote_host,
        ),
    )

def run_remote_has_modified_files( ):
    result = execute(
        remote_has_modified_files
        , host             = remote_host
        , remote_path      = remote_path
    )
    return result[remote_host]

def run_send_remote_changes_to_local( ):
    result = execute(
        send_remote_changes_to_local
        , host             = remote_host
        , remote_path      = remote_path
        , local_path       = local_path
    )
    return result[remote_host]

def run_send_local_changes_to_remote( ):
    result = execute(
        send_local_changes_to_remote
        , host             = remote_host
        , remote_path      = remote_path
        , local_path       = local_path
        , local_branch     = local_branch
    )
    return result[remote_host]

def run_initial_sync( ):
    growl_start( )
    execute(
        initial_sync
        , host             = remote_host
        , remote_path      = remote_path
        , local_path       = local_path
        , local_branch     = local_branch
        , git_ignore_lines = git_ignore_lines
    )
    growl_done( )

def callback( event ):

    global observer, stream

    if event.mask == kFSEventStreamEventFlagItemCreated:
        # Sublime Text Seems to trigger a lot of these and they don't seem to
        #  warrant a new sync, so lets skip these for now.
        return

    filename = event.name
    git_dir  = os.path.join( local_path, '.git' )

    if git_dir in filename:
        #Skip sync for file change that are in the .git directory.
        return

    growl_start( )

    try:
        if run_remote_has_modified_files():
            # Stop observing.
            observer.unschedule(stream)
            observer.stop()

            run_send_remote_changes_to_local()

            # Start observing again.
            observer = Observer()
            observer.schedule(stream)
            observer.start()

        run_send_local_changes_to_remote()

    except Exception as inst:
        print "sync failed."
        print type(inst)
        print inst.args
        print inst
        growl_failed( )
        raise
    else:
        growl_done( )

run_initial_sync( )

# Start watching the directory
stream = Stream(callback, local_path, file_events=True)
observer.schedule(stream)
observer.start()

try:
    while 1:
        pause( )
except KeyboardInterrupt:
    observer.unschedule(stream)
    observer.stop( )
    observer.join( )

<|endoftext|>


<commit_before>import binascii
import json
import logging
import os
from datetime import timedelta
from functools import update_wrapper
from logging.handlers import TimedRotatingFileHandler
from multiprocessing import Process
import subprocess

import click
import git
import pushover
import strictyaml
from flask import Flask, Response, abort, current_app, jsonify, make_response, request

import settings

app = Flask(__name__)
app.debug = settings.DEBUG

logger = logging.getLogger('webhaak')
logger.setLevel(logging.DEBUG)
# Log will rotate daily with a max history of LOG_BACKUP_COUNT
fh = TimedRotatingFileHandler(
    settings.LOG_LOCATION,
    when='d',
    interval=1,
    backupCount=settings.LOG_BACKUP_COUNT
)
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)

# Load the configuration of the various projects/hooks
with open(settings.PROJECTS_FILE, 'r') as pf:
    projects = strictyaml.load(pf.read()).data

def notify_user(result, config):
    """Send a PushOver message if configured, after git and command have run

    result is a dictionary with fields:
      command_result
      status: 'OK' | 'error'
      type: 'commanderror'
      message
    """
    try:
        triggerconfig = config[1]
        projectname = '{}>{}'.format(config[0], triggerconfig['title'])
        title = ''
        branch = 'master'
        if 'repo_branch' in triggerconfig:
            branch = triggerconfig['repo_branch']
        message = 'repo: {}\nbranch: {}\ncommand: {}'.format(triggerconfig['repo'], branch, triggerconfig['command'])
        if result['status'] == 'OK':
            title = "Hook for {} ran successfully".format(projectname)
        else:
            title = "Hook for {} failed: {}".format(projectname, result['type'])
            message = message + '\n\n{}'.format(result['message'])
        logging.debug(message)
        logging.info('Sending notification...')
        client = pushover.Client(settings.PUSHOVER_USERKEY, api_token=settings.PUSHOVER_APPTOKEN)
        client.send_message(message, title=title)
        logging.info('Notification sent')
    except AttributeError:
        logging.warning('Notification through PushOver failed because of missing configuration')

def gettriggersettings(appkey, triggerkey):
    """Look up the trigger and return the repo and command to be updated and fired"""
    for project in projects:
        if projects[project]['appkey'] == appkey:
            for trigger in projects[project]['triggers']:
                if projects[project]['triggers'][trigger]['triggerkey'] == triggerkey:
                    triggerconfig = projects[project]['triggers'][trigger]
                    triggerconfig['title'] = trigger
                    return (project, triggerconfig)
    return None

def get_repo_basename(repo_url):
    """Extract repository basename from its url, as that will be the name of directory it will be cloned into"""
    result = os.path.basename(repo_url)
    filename, file_extension = os.path.splitext(result)
    if file_extension == '.git':
        # Strip the .git from the name, as Git will do the same on non-bare checkouts
        result = filename
    return result

def fetchinfo_to_str(fetchinfo):
    """git.remote.FetchInfo to human readable representation"""
    result = fetchinfo[0].note
    return result

def update_repo(config):
    """Update (pull) the Git repo"""
    projectname = config[0]
    triggerconfig = config[1]

    repo_url = triggerconfig['repo']
    repo_parent = settings.REPOS_CACHE_DIR
    if 'repoparent' in triggerconfig and triggerconfig['repoparent']:
        repo_parent = triggerconfig['repoparent']

    logger.info('[%s] Updating %s', projectname, repo_url)
    logger.info('[%s] Repo parent %s', projectname, repo_parent)

    # Ensure cache dir for webhaak exists and is writable
    if not os.path.exists(repo_parent):
        os.makedirs(repo_parent)  # throws OSError if repo_parent is not writable

    # TODO: check whether dir exists with different repository
    repo_dir = os.path.join(repo_parent, get_repo_basename(repo_url))
    logger.info('[%s] Repo dir %s', projectname, repo_dir)
    if os.path.isdir(repo_dir):
        # Repo already exists locally, do a pull
        logger.info('[%s] Repo exists, pull', projectname)

        apprepo = git.Repo(repo_dir)
        origin = apprepo.remote('origin')
        result = fetchinfo_to_str(origin.fetch())  # assure we actually have data. fetch() returns useful information
        logger.info('[%s] Fetch result: %s', projectname, result)
        origin.pull()
        logger.info('[%s] Done pulling, checkout()', projectname)
        result += ' ' + str(apprepo.git.checkout())
    else:
        # Repo needs to be cloned
        logger.info('[%s] Repo does not exist yet, clone', projectname)
        apprepo = git.Repo.init(repo_dir)
        origin = apprepo.create_remote('origin', repo_url)
        origin.fetch()                  # assure we actually have data. fetch() returns useful information
        # Setup a local tracking branch of a remote branch
        apprepo.create_head('master', origin.refs.master).set_tracking_branch(origin.refs.master)
        # push and pull behaves similarly to `git push|pull`
        result = origin.pull()
        logger.info('[%s] Done pulling, checkout()', projectname)
        result += ' ' + str(apprepo.git.checkout())
    return result

def run_command(config):
    """Run the command(s) defined for this trigger"""
    projectname = config[0]
    triggerconfig = config[1]
    if 'command' not in triggerconfig:
        # No command to execute, return
        logger.info('[%s] No command to execute', projectname)
        return None
    command = triggerconfig['command']
    # Replace some placeholders to be used in executing scripts from one of the repos
    command = command.replace('REPODIR', os.path.join(settings.REPOS_CACHE_DIR, projectname))
    command = command.replace('CACHEDIR', settings.REPOS_CACHE_DIR)
    command = command.strip()  # ensure no weird linefeeds and superfluous whitespace are there
    logger.info('[%s] Executing `%s`', projectname, command)

    # TODO: capture_output is new in Python 3.7, replaces stdout and stderr
    # result = subprocess.run(command_parts, capture_output=True, check=True, shell=True, universal_newlines=True)
    result = subprocess.run(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True,
        universal_newlines=True
    )
    return result

def do_pull_andor_command(config):
    """Asynchronous task, performing the git pulling and the specified scripting inside a Process"""
    projectname = config[0]
    result = {'application': projectname}
    result['trigger'] = config[1]
    if 'repo' in config[1]:
        try:
            result['repo_result'] = update_repo(config)
            logger.info('[%s] result repo: %s', projectname, str(result['repo_result']))
        except git.GitCommandError as e:
            result = {'status': 'error', 'type': 'giterror', 'message': str(e)}
            logger.error('[%s] giterror: %s', projectname, str(e))
            notify_user(result, config)
        except (OSError, KeyError) as e:
            result = {'status': 'error', 'type': 'oserror', 'message': str(e)}
            logger.error('[%s] oserror: %s', projectname, str(e))
            notify_user(result, config)

    cmdresult = run_command(config)
    if cmdresult and cmdresult.returncode == 0:
        logger.info('[%s] success for command: %s', projectname, str(cmdresult.stdout))
        result['status'] = 'OK'
    elif not cmdresult:
        logger.info('[%s] no command configured', projectname)
        result['status'] = 'OK'
    else:
        result['status'] = 'error'
        result['type'] = 'commanderror'
        result['message'] = cmdresult.stderr.strip()
        # TODO: seperate logfiles per job? Filename then based on appkey_triggerkey_timestamp.log
        logger.error(
            '[%s] commanderror with returncode %s: %s',
            projectname,
            str(cmdresult.returncode),
            cmdresult.stderr
        )
        logger.error('[%s] stdout: %s', projectname, cmdresult.stdout)
        logger.error('[%s] stderr: %s', projectname, cmdresult.stderr)

    notify_user(result, config)

# == API request support functions/mixins ======

def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    """Decorator to send the correct cross-domain headers
    src: https://blog.skyred.fi/articles/better-crossdomain-snippet-for-flask.html
    """
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, str):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, str):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers
            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            h['Access-Control-Allow-Credentials'] = 'true'
            h['Access-Control-Allow-Headers'] = \
                "Origin, X-Requested-With, Content-Type, Accept, Authorization"
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

class APIException(Exception):
    status_code = 400

    def __init__(self, message, status_code=None, payload=None):
        Exception.__init__(self)
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        rv['status_code'] = self.status_code
        return rv

class InvalidAPIUsage(APIException):
    status_code = 400

@app.errorhandler(404)
def page_not_found(e):
    return jsonify(error=404, text=str(e)), 404

@app.errorhandler(InvalidAPIUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    response.mimetype = 'application/json'
    return response

# == Web app endpoints ======

@app.route('/')
def indexpage():
    logger.debug('Root page requested')
    return 'Welcome to Webhaak, see the documentation to how to setup and use webhooks.'

@app.route('/admin/<secretkey>/list', methods=['GET'])
@crossdomain(origin='*')
def listtriggers(secretkey):
    """List the appkeys and triggerkeys"""
    try:
        if secretkey != settings.SECRETKEY:
            abort(404)
    except AttributeError:
        abort(404)

    server_url = request.host_url

    result = {}
    for project in projects:
        result[project] = {
            'title': project,
            'appkey': projects[project]['appkey'],
            'triggers': [],
        }
        for trigger in projects[project]['triggers']:
            result[project]['triggers'].append(
                {
                    'title': trigger,
                    'triggerkey': projects[project]['triggers'][trigger]['triggerkey'],
                    'url': '{}app/{}/{}'.format(
                        server_url,
                        projects[project]['appkey'],
                        projects[project]['triggers'][trigger]['triggerkey']
                    )
                }
            )
    return Response(
        json.dumps({'projects': result}), status=200, mimetype='application/json'
    )

@app.route('/app/<appkey>/<triggerkey>', methods=['GET', 'OPTIONS', 'POST'])
@crossdomain(origin='*')
def apptrigger(appkey, triggerkey):
    """Fire the trigger described by the configuration under `triggerkey`"""
    logger.info('%s on appkey: %s triggerkey: %s', request.method, appkey, triggerkey)
    if request.method == 'POST':
        # Likely some ping was sent, check if so
        if request.headers.get('X-GitHub-Event') == "ping":
            payload = request.get_json()
            logger.info(
                'received GitHub ping for %s hook: %s ',
                payload['repository']['full_name'],
                payload['hook']['url']
            )
            return json.dumps({'msg': 'Hi!'})
        if request.headers.get('X-GitHub-Event') != "push":
            payload = request.get_json()
            logger.info(
                'received wrong event type from GitHub for %s hook: %s',
                payload['repository']['full_name'],
                payload['hook']['url']
            )
            return json.dumps({'msg': "wrong event type"})
        payload = request.get_json()
        event_info = 'received push from GitHub for '
        if payload:
            if 'repository' in payload:
                event_info += payload['repository']['full_name']
            if 'pusher' in payload:
                event_info += ' by ' + payload['pusher']['name']
            if 'compare' in payload:
                event_info += ', compare: ' + payload['compare']
        else:
            event_info += 'unknown, as no json was received. Check that GitHub webhook content type is application/json'
        logger.info(payload)
        logger.info(event_info)

    config = gettriggersettings(appkey, triggerkey)
    if config is None:
        logger.error('appkey/triggerkey combo not found')
        return Response(json.dumps({'status': 'Error'}), status=404, mimetype='application/json')
    p = Process(target=do_pull_andor_command, args=(config,))
    p.start()
    return Response(
        json.dumps({
            'status': 'OK',
            'message': 'Command accepted and will be run in the background'
        }), status=200, mimetype='application/json'
    )

@app.route('/monitor/monitor.html')
@app.route('/monitor/')
@app.route('/monitor')
def monitor():
    """Monitoring ping"""
    result = 'OK'
    return result

def generatekey():
    """Generate a random ascii string to be used as identifier"""
    return binascii.hexlify(os.urandom(24))

@app.cli.command()
def printappkey():
    """Generate new appkey"""
    print(generatekey())

@app.route('/getappkey')
def getappkey():
    """Generate new appkey"""
    return Response(json.dumps({'key': generatekey().decode('utf-8')}), status=200, mimetype='application/json')

if __name__ == '__main__':
    if not settings.DEBUG:
        app.run(port=settings.PORT, debug=settings.DEBUG)
    else:
        app.run(host='0.0.0.0', port=settings.PORT, debug=settings.DEBUG)
<commit_msg>If a Git error occurred, don't try to execute the command(s), but abort<commit_after>import binascii
import json
import logging
import os
from datetime import timedelta
from functools import update_wrapper
from logging.handlers import TimedRotatingFileHandler
from multiprocessing import Process
import subprocess

import click
import git
import pushover
import strictyaml
from flask import Flask, Response, abort, current_app, jsonify, make_response, request

import settings

app = Flask(__name__)
app.debug = settings.DEBUG

logger = logging.getLogger('webhaak')
logger.setLevel(logging.DEBUG)
# Log will rotate daily with a max history of LOG_BACKUP_COUNT
fh = TimedRotatingFileHandler(
    settings.LOG_LOCATION,
    when='d',
    interval=1,
    backupCount=settings.LOG_BACKUP_COUNT
)
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)

# Load the configuration of the various projects/hooks
with open(settings.PROJECTS_FILE, 'r') as pf:
    projects = strictyaml.load(pf.read()).data

def notify_user(result, config):
    """Send a PushOver message if configured, after git and command have run

    result is a dictionary with fields:
      command_result
      status: 'OK' | 'error'
      type: 'commanderror'
      message
    """
    try:
        triggerconfig = config[1]
        projectname = '{}>{}'.format(config[0], triggerconfig['title'])
        title = ''
        branch = 'master'
        if 'repo_branch' in triggerconfig:
            branch = triggerconfig['repo_branch']
        message = 'repo: {}\nbranch: {}\ncommand: {}'.format(triggerconfig['repo'], branch, triggerconfig['command'])
        if result['status'] == 'OK':
            title = "Hook for {} ran successfully".format(projectname)
        else:
            title = "Hook for {} failed: {}".format(projectname, result['type'])
            message = message + '\n\n{}'.format(result['message'])
        logging.debug(message)
        logging.info('Sending notification...')
        client = pushover.Client(settings.PUSHOVER_USERKEY, api_token=settings.PUSHOVER_APPTOKEN)
        client.send_message(message, title=title)
        logging.info('Notification sent')
    except AttributeError:
        logging.warning('Notification through PushOver failed because of missing configuration')

def gettriggersettings(appkey, triggerkey):
    """Look up the trigger and return the repo and command to be updated and fired"""
    for project in projects:
        if projects[project]['appkey'] == appkey:
            for trigger in projects[project]['triggers']:
                if projects[project]['triggers'][trigger]['triggerkey'] == triggerkey:
                    triggerconfig = projects[project]['triggers'][trigger]
                    triggerconfig['title'] = trigger
                    return (project, triggerconfig)
    return None

def get_repo_basename(repo_url):
    """Extract repository basename from its url, as that will be the name of directory it will be cloned into"""
    result = os.path.basename(repo_url)
    filename, file_extension = os.path.splitext(result)
    if file_extension == '.git':
        # Strip the .git from the name, as Git will do the same on non-bare checkouts
        result = filename
    return result

def fetchinfo_to_str(fetchinfo):
    """git.remote.FetchInfo to human readable representation"""
    result = fetchinfo[0].note
    return result

def update_repo(config):
    """Update (pull) the Git repo"""
    projectname = config[0]
    triggerconfig = config[1]

    repo_url = triggerconfig['repo']
    repo_parent = settings.REPOS_CACHE_DIR
    if 'repoparent' in triggerconfig and triggerconfig['repoparent']:
        repo_parent = triggerconfig['repoparent']

    logger.info('[%s] Updating %s', projectname, repo_url)
    logger.info('[%s] Repo parent %s', projectname, repo_parent)

    # Ensure cache dir for webhaak exists and is writable
    if not os.path.exists(repo_parent):
        os.makedirs(repo_parent)  # throws OSError if repo_parent is not writable

    # TODO: check whether dir exists with different repository
    repo_dir = os.path.join(repo_parent, get_repo_basename(repo_url))
    logger.info('[%s] Repo dir %s', projectname, repo_dir)
    if os.path.isdir(repo_dir):
        # Repo already exists locally, do a pull
        logger.info('[%s] Repo exists, pull', projectname)

        apprepo = git.Repo(repo_dir)
        origin = apprepo.remote('origin')
        result = fetchinfo_to_str(origin.fetch())  # assure we actually have data. fetch() returns useful information
        logger.info('[%s] Fetch result: %s', projectname, result)
        origin.pull()
        logger.info('[%s] Done pulling, checkout()', projectname)
        result += ' ' + str(apprepo.git.checkout())
    else:
        # Repo needs to be cloned
        logger.info('[%s] Repo does not exist yet, clone', projectname)
        apprepo = git.Repo.init(repo_dir)
        origin = apprepo.create_remote('origin', repo_url)
        origin.fetch()                  # assure we actually have data. fetch() returns useful information
        # Setup a local tracking branch of a remote branch
        apprepo.create_head('master', origin.refs.master).set_tracking_branch(origin.refs.master)
        # push and pull behaves similarly to `git push|pull`
        result = origin.pull()
        logger.info('[%s] Done pulling, checkout()', projectname)
        result += ' ' + str(apprepo.git.checkout())
    return result

def run_command(config):
    """Run the command(s) defined for this trigger"""
    projectname = config[0]
    triggerconfig = config[1]
    if 'command' not in triggerconfig:
        # No command to execute, return
        logger.info('[%s] No command to execute', projectname)
        return None
    command = triggerconfig['command']
    # Replace some placeholders to be used in executing scripts from one of the repos
    command = command.replace('REPODIR', os.path.join(settings.REPOS_CACHE_DIR, projectname))
    command = command.replace('CACHEDIR', settings.REPOS_CACHE_DIR)
    command = command.strip()  # ensure no weird linefeeds and superfluous whitespace are there
    logger.info('[%s] Executing `%s`', projectname, command)

    # TODO: capture_output is new in Python 3.7, replaces stdout and stderr
    # result = subprocess.run(command_parts, capture_output=True, check=True, shell=True, universal_newlines=True)
    result = subprocess.run(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True,
        universal_newlines=True
    )
    return result

def do_pull_andor_command(config):
    """Asynchronous task, performing the git pulling and the specified scripting inside a Process"""
    projectname = config[0]
    result = {'application': projectname}
    result['trigger'] = config[1]
    if 'repo' in config[1]:
        try:
            result['repo_result'] = update_repo(config)
            logger.info('[%s] result repo: %s', projectname, str(result['repo_result']))
        except git.GitCommandError as e:
            result = {'status': 'error', 'type': 'giterror', 'message': str(e)}
            logger.error('[%s] giterror: %s', projectname, str(e))
            notify_user(result, config)
            return
        except (OSError, KeyError) as e:
            result = {'status': 'error', 'type': 'oserror', 'message': str(e)}
            logger.error('[%s] oserror: %s', projectname, str(e))
            notify_user(result, config)
            return

    cmdresult = run_command(config)
    if cmdresult and cmdresult.returncode == 0:
        logger.info('[%s] success for command: %s', projectname, str(cmdresult.stdout))
        result['status'] = 'OK'
    elif not cmdresult:
        logger.info('[%s] no command configured', projectname)
        result['status'] = 'OK'
    else:
        result['status'] = 'error'
        result['type'] = 'commanderror'
        result['message'] = cmdresult.stderr.strip()
        # TODO: seperate logfiles per job? Filename then based on appkey_triggerkey_timestamp.log
        logger.error(
            '[%s] commanderror with returncode %s: %s',
            projectname,
            str(cmdresult.returncode),
            cmdresult.stderr
        )
        logger.error('[%s] stdout: %s', projectname, cmdresult.stdout)
        logger.error('[%s] stderr: %s', projectname, cmdresult.stderr)

    notify_user(result, config)

# == API request support functions/mixins ======

def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    """Decorator to send the correct cross-domain headers
    src: https://blog.skyred.fi/articles/better-crossdomain-snippet-for-flask.html
    """
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, str):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, str):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers
            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            h['Access-Control-Allow-Credentials'] = 'true'
            h['Access-Control-Allow-Headers'] = \
                "Origin, X-Requested-With, Content-Type, Accept, Authorization"
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

class APIException(Exception):
    status_code = 400

    def __init__(self, message, status_code=None, payload=None):
        Exception.__init__(self)
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        rv['status_code'] = self.status_code
        return rv

class InvalidAPIUsage(APIException):
    status_code = 400

@app.errorhandler(404)
def page_not_found(e):
    return jsonify(error=404, text=str(e)), 404

@app.errorhandler(InvalidAPIUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    response.mimetype = 'application/json'
    return response

# == Web app endpoints ======

@app.route('/')
def indexpage():
    logger.debug('Root page requested')
    return 'Welcome to Webhaak, see the documentation to how to setup and use webhooks.'

@app.route('/admin/<secretkey>/list', methods=['GET'])
@crossdomain(origin='*')
def listtriggers(secretkey):
    """List the appkeys and triggerkeys"""
    try:
        if secretkey != settings.SECRETKEY:
            abort(404)
    except AttributeError:
        abort(404)

    server_url = request.host_url

    result = {}
    for project in projects:
        result[project] = {
            'title': project,
            'appkey': projects[project]['appkey'],
            'triggers': [],
        }
        for trigger in projects[project]['triggers']:
            result[project]['triggers'].append(
                {
                    'title': trigger,
                    'triggerkey': projects[project]['triggers'][trigger]['triggerkey'],
                    'url': '{}app/{}/{}'.format(
                        server_url,
                        projects[project]['appkey'],
                        projects[project]['triggers'][trigger]['triggerkey']
                    )
                }
            )
    return Response(
        json.dumps({'projects': result}), status=200, mimetype='application/json'
    )

@app.route('/app/<appkey>/<triggerkey>', methods=['GET', 'OPTIONS', 'POST'])
@crossdomain(origin='*')
def apptrigger(appkey, triggerkey):
    """Fire the trigger described by the configuration under `triggerkey`"""
    logger.info('%s on appkey: %s triggerkey: %s', request.method, appkey, triggerkey)
    if request.method == 'POST':
        # Likely some ping was sent, check if so
        if request.headers.get('X-GitHub-Event') == "ping":
            payload = request.get_json()
            logger.info(
                'received GitHub ping for %s hook: %s ',
                payload['repository']['full_name'],
                payload['hook']['url']
            )
            return json.dumps({'msg': 'Hi!'})
        if request.headers.get('X-GitHub-Event') != "push":
            payload = request.get_json()
            logger.info(
                'received wrong event type from GitHub for %s hook: %s',
                payload['repository']['full_name'],
                payload['hook']['url']
            )
            return json.dumps({'msg': "wrong event type"})
        payload = request.get_json()
        event_info = 'received push from GitHub for '
        if payload:
            if 'repository' in payload:
                event_info += payload['repository']['full_name']
            if 'pusher' in payload:
                event_info += ' by ' + payload['pusher']['name']
            if 'compare' in payload:
                event_info += ', compare: ' + payload['compare']
        else:
            event_info += 'unknown, as no json was received. Check that GitHub webhook content type is application/json'
        logger.info(payload)
        logger.info(event_info)

    config = gettriggersettings(appkey, triggerkey)
    if config is None:
        logger.error('appkey/triggerkey combo not found')
        return Response(json.dumps({'status': 'Error'}), status=404, mimetype='application/json')
    p = Process(target=do_pull_andor_command, args=(config,))
    p.start()
    return Response(
        json.dumps({
            'status': 'OK',
            'message': 'Command accepted and will be run in the background'
        }), status=200, mimetype='application/json'
    )

@app.route('/monitor/monitor.html')
@app.route('/monitor/')
@app.route('/monitor')
def monitor():
    """Monitoring ping"""
    result = 'OK'
    return result

def generatekey():
    """Generate a random ascii string to be used as identifier"""
    return binascii.hexlify(os.urandom(24))

@app.cli.command()
def printappkey():
    """Generate new appkey"""
    print(generatekey())

@app.route('/getappkey')
def getappkey():
    """Generate new appkey"""
    return Response(json.dumps({'key': generatekey().decode('utf-8')}), status=200, mimetype='application/json')

if __name__ == '__main__':
    if not settings.DEBUG:
        app.run(port=settings.PORT, debug=settings.DEBUG)
    else:
        app.run(host='0.0.0.0', port=settings.PORT, debug=settings.DEBUG)
<|endoftext|>


<commit_before>import os
import numpy as np
import nibabel as nb
import pandas as pd
import glob
from nltools.simulator import Simulator
from nltools.data import Brain_Data
from nltools.data import threshold
from nltools.mask import create_sphere

def test_data(tmpdir):
    sim = Simulator()
    r = 10
    sigma = 1
    y = [0, 1]
    n_reps = 3
    output_dir = str(tmpdir)
    sim.create_data(y, sigma, reps=n_reps, output_dir=output_dir)

    shape_3d = (91, 109, 91)
    shape_2d = (6, 238955)
    y=pd.read_csv(os.path.join(str(tmpdir.join('y.csv'))),header=None,index_col=None).T
    flist = glob.glob(str(tmpdir.join('centered*.nii.gz')))

    # Test load list
    dat = Brain_Data(data=flist,Y=y)

    # Test load file
    assert Brain_Data(flist[0])

    # Test to_nifti
    d = dat.to_nifti()
    assert d.shape[0:3] == shape_3d

    # Test load nibabel
    assert Brain_Data(d)

    # Test shape
    assert dat.shape() == shape_2d

    # Test Mean
    assert dat.mean().shape()[0] == shape_2d[1]

    # Test Std
    assert dat.std().shape()[0] == shape_2d[1]

    # Test add
    new = dat + dat
    assert new.shape()[0] == shape_2d[1]

    # Test subtract
    new = dat - dat
    assert new.shape()[0] == shape_2d[1]

    # Test multiply
    new = dat * dat
    assert new.shape()[0] == shape_2d[1]

    # # Test T-test
    out = dat.ttest()
    assert out['t'].shape()[0]==shape_2d[1]

    # # Test T-test - permutation method
    out = dat.ttest(threshold_dict={'permutation':'tfce','n_permutations':50,'n_jobs':1})
    assert out['t'].shape()[0]==shape_2d[1]

    # Test Regress
    dat.X = pd.DataFrame({'Intercept':np.ones(len(dat.Y)),'X1':np.array(dat.Y).flatten()},index=None)
    out = dat.regress()
    assert out['beta'].shape() == (2,shape_2d[1])

    # Test indexing
    assert out['t'][1].shape()[0] == shape_2d[1]

    # Test threshold
    i=1
    tt = threshold(out['t'][i], out['p'][i], threshold_dict={'fdr':.05})
    assert tt.shape()[0] == shape_2d[1]

    # Test write
    dat.write(os.path.join(str(tmpdir.join('test_write.nii'))))
    assert Brain_Data(os.path.join(str(tmpdir.join('test_write.nii'))))

    # Test append
    assert dat.append(dat).shape()[0]==shape_2d[0]*2

    # Test distance
    distance = dat.distance(method='euclidean')
    assert distance.shape==(shape_2d[0],shape_2d[0])

    # Test predict
    stats = dat.predict(algorithm='svm', cv_dict={'type': 'kfolds','n_folds': 2, 'n':len(dat.Y)}, plot=False,**{'kernel':"linear"})

    # Support Vector Regression, with 5 fold cross-validation with Platt Scaling
    # This will output probabilities of each class
    stats = dat.predict(algorithm='svm', cv_dict=None, plot=False,**{'kernel':'linear','probability':True})

    assert isinstance(stats['weight_map'],Brain_Data)
    # Logistic classificiation, with 5 fold stratified cross-validation.

    stats = dat.predict(algorithm='logistic', cv_dict={'type': 'kfolds','n_folds': 5, 'n':len(dat.Y)}, plot=False)
    assert isinstance(stats['weight_map'],Brain_Data)

    # Ridge classificiation, with 5 fold between-subject cross-validation, where data for each subject is held out together.
    stats = dat.predict(algorithm='ridgeClassifier', cv_dict=None,plot=False)
    assert isinstance(stats['weight_map'],Brain_Data)

    # Test Similarity
    r = dat.similarity(stats['weight_map'])
    assert len(r)==shape_2d[0]
    r2 = dat.similarity(stats['weight_map'].to_nifti())
    assert len(r2)==shape_2d[0]

    # Test apply_mask - might move part of this to test mask suite
    s1 = create_sphere([41, 64, 55], radius=10)
    assert isinstance(s1,nb.Nifti1Image)
    s2 = Brain_Data(s1)
    masked_dat = dat.apply_mask(s1)
    assert masked_dat.shape()[1]==np.sum(s2.data!=0)

    # Test extract_roi
    mask = create_sphere([41, 64, 55], radius=10)
    assert len(dat.extract_roi(mask))==shape_2d[0]

    # Test Bootstrap

    # Test multivariate_similarity

    # Test plot
<commit_msg>bug fix in test<commit_after>import os
import numpy as np
import nibabel as nb
import pandas as pd
import glob
from nltools.simulator import Simulator
from nltools.data import Brain_Data
from nltools.data import threshold
from nltools.mask import create_sphere

def test_data(tmpdir):
    sim = Simulator()
    r = 10
    sigma = 1
    y = [0, 1]
    n_reps = 3
    output_dir = str(tmpdir)
    sim.create_data(y, sigma, reps=n_reps, output_dir=output_dir)

    shape_3d = (91, 109, 91)
    shape_2d = (6, 238955)
    y=pd.read_csv(os.path.join(str(tmpdir.join('y.csv'))),header=None,index_col=None).T
    flist = glob.glob(str(tmpdir.join('centered*.nii.gz')))

    # Test load list
    dat = Brain_Data(data=flist,Y=y)

    # Test load file
    assert Brain_Data(flist[0])

    # Test to_nifti
    d = dat.to_nifti()
    assert d.shape[0:3] == shape_3d

    # Test load nibabel
    assert Brain_Data(d)

    # Test shape
    assert dat.shape() == shape_2d

    # Test Mean
    assert dat.mean().shape()[0] == shape_2d[1]

    # Test Std
    assert dat.std().shape()[0] == shape_2d[1]

    # Test add
    new = dat + dat
    assert new.shape() == shape_2d

    # Test subtract
    new = dat - dat
    assert new.shape() == shape_2d

    # Test multiply
    new = dat * dat
    assert new.shape() == shape_2d

    # # Test T-test
    out = dat.ttest()
    assert out['t'].shape()[0]==shape_2d[1]

    # # Test T-test - permutation method
    out = dat.ttest(threshold_dict={'permutation':'tfce','n_permutations':50,'n_jobs':1})
    assert out['t'].shape()[0]==shape_2d[1]

    # Test Regress
    dat.X = pd.DataFrame({'Intercept':np.ones(len(dat.Y)),'X1':np.array(dat.Y).flatten()},index=None)
    out = dat.regress()
    assert out['beta'].shape() == (2,shape_2d[1])

    # Test indexing
    assert out['t'][1].shape()[0] == shape_2d[1]

    # Test threshold
    i=1
    tt = threshold(out['t'][i], out['p'][i], .05)
    assert tt.shape()[0] == shape_2d[1]

    # Test write
    dat.write(os.path.join(str(tmpdir.join('test_write.nii'))))
    assert Brain_Data(os.path.join(str(tmpdir.join('test_write.nii'))))

    # Test append
    assert dat.append(dat).shape()[0]==shape_2d[0]*2

    # Test distance
    distance = dat.distance(method='euclidean')
    assert distance.shape==(shape_2d[0],shape_2d[0])

    # Test predict
    stats = dat.predict(algorithm='svm', cv_dict={'type': 'kfolds','n_folds': 2, 'n':len(dat.Y)}, plot=False,**{'kernel':"linear"})

    # Support Vector Regression, with 5 fold cross-validation with Platt Scaling
    # This will output probabilities of each class
    stats = dat.predict(algorithm='svm', cv_dict=None, plot=False,**{'kernel':'linear','probability':True})

    assert isinstance(stats['weight_map'],Brain_Data)
    # Logistic classificiation, with 5 fold stratified cross-validation.

    stats = dat.predict(algorithm='logistic', cv_dict={'type': 'kfolds','n_folds': 5, 'n':len(dat.Y)}, plot=False)
    assert isinstance(stats['weight_map'],Brain_Data)

    # Ridge classificiation, with 5 fold between-subject cross-validation, where data for each subject is held out together.
    stats = dat.predict(algorithm='ridgeClassifier', cv_dict=None,plot=False)
    assert isinstance(stats['weight_map'],Brain_Data)

    # Test Similarity
    r = dat.similarity(stats['weight_map'])
    assert len(r)==shape_2d[0]
    r2 = dat.similarity(stats['weight_map'].to_nifti())
    assert len(r2)==shape_2d[0]

    # Test apply_mask - might move part of this to test mask suite
    s1 = create_sphere([41, 64, 55], radius=10)
    assert isinstance(s1,nb.Nifti1Image)
    s2 = Brain_Data(s1)
    masked_dat = dat.apply_mask(s1)
    assert masked_dat.shape()[1]==np.sum(s2.data!=0)

    # Test extract_roi
    mask = create_sphere([41, 64, 55], radius=10)
    assert len(dat.extract_roi(mask))==shape_2d[0]

    # Test Bootstrap

    # Test multivariate_similarity

    # Test plot
<|endoftext|>


<commit_before>"""Installs package using distutils

Run:
    python setup.py install

to install this package.
"""

###
#RPGPlanet Wiki engine: Fuel for your Wiki system
#Copyright (C) 2006 Lukas "Almad" Linhart http://www.almad.net/ and contributors,
# for a full list see http://projects.almad.net/rpgplanet-tools
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
###

from distutils.core import setup
from distutils.command.install import INSTALL_SCHEMES
import sys

required_python_version = '2.4'

###############################################################################
# arguments for the setup command
###############################################################################
name = "sneakylang"
version = "0.1_beta3"
desc = "Extensible framework for easy creation of extensible WikiLanguages"
long_desc = """"""
classifiers=[
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
    "Operating System :: OS Independent",
    "Programming Language :: Python",
    "Topic :: Software Development :: Documentation",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Text Processing",
    "Topic :: Text Processing :: Markup :: HTML",
    "Topic :: Text Processing :: Markup :: XML"
]
author="Lukas Almad Linhart"
author_email="bugs@almad.net"
url="http://projects.almad.net/sneakylang"
cp_license="LGPL"
packages=[
    "sneakylang"
]
download_url="http://projects.almad.net/sneakylang/wiki/Download"
data_files=[]
###############################################################################
# end arguments for setup
###############################################################################

def main():
    if sys.version < required_python_version:
        s = "I'm sorry, but %s %s requires Python %s or later."
        print s % (name, version, required_python_version)
        sys.exit(1)

    # set default location for "data_files" to platform specific "site-packages"
    # location
    for scheme in INSTALL_SCHEMES.values():
        scheme['data'] = scheme['purelib']

    setup(
        name=name,
        version=version,
        description=desc,
        long_description=long_desc,
        classifiers=classifiers,
        author=author,
        author_email=author_email,
        url=url,
        license=cp_license,
        packages=packages,
        download_url=download_url,
        data_files=data_files,
    )

if __name__ == "__main__":
    main()
<commit_msg>[svn] distutils for RC 1<commit_after>"""Installs package using distutils

Run:
    python setup.py install

to install this package.
"""

###
#RPGPlanet Wiki engine: Fuel for your Wiki system
#Copyright (C) 2006 Lukas "Almad" Linhart http://www.almad.net/ and contributors,
# for a full list see http://projects.almad.net/rpgplanet-tools
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
###

from distutils.core import setup
from distutils.command.install import INSTALL_SCHEMES
import sys

required_python_version = '2.4'

###############################################################################
# arguments for the setup command
###############################################################################
name = "sneakylang"
version = "0.1_rc1"
desc = "Extensible framework for easy creation of extensible WikiLanguages"
long_desc = """"""
classifiers=[
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
    "Operating System :: OS Independent",
    "Programming Language :: Python",
    "Topic :: Software Development :: Documentation",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Text Processing",
    "Topic :: Text Processing :: Markup :: HTML",
    "Topic :: Text Processing :: Markup :: XML"
]
author="Lukas Almad Linhart"
author_email="bugs@almad.net"
url="http://projects.almad.net/sneakylang"
cp_license="LGPL"
packages=[
    "sneakylang"
]
download_url="http://projects.almad.net/sneakylang/wiki/Download"
data_files=[]
###############################################################################
# end arguments for setup
###############################################################################

def main():
    if sys.version < required_python_version:
        s = "I'm sorry, but %s %s requires Python %s or later."
        print s % (name, version, required_python_version)
        sys.exit(1)

    # set default location for "data_files" to platform specific "site-packages"
    # location
    for scheme in INSTALL_SCHEMES.values():
        scheme['data'] = scheme['purelib']

    setup(
        name=name,
        version=version,
        description=desc,
        long_description=long_desc,
        classifiers=classifiers,
        author=author,
        author_email=author_email,
        url=url,
        license=cp_license,
        packages=packages,
        download_url=download_url,
        data_files=data_files,
    )

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>"""Installs package using distutils

Run:
    python setup.py install

to install this package.
"""

###
#RPGPlanet Wiki engine: Fuel for your Wiki system
#Copyright (C) 2006 Lukas "Almad" Linhart http://www.almad.net/ and contributors,
# for a full list see http://projects.almad.net/rpgplanet-tools
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
###

from distutils.core import setup
from distutils.command.install import INSTALL_SCHEMES
import sys

required_python_version = '2.4'

###############################################################################
# arguments for the setup command
###############################################################################
name = "sneakylang"
version = "0.1_alpha1"
desc = "Extensible framework for easy creation of extensible WikiLanguages"
long_desc = """"""
classifiers=[
    "Development Status :: 1 - Planning",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
    "Operating System :: OS Independent",
    "Programming Language :: Python",
    "Topic :: Software Development :: Documentation",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Text Processing",
    "Topic :: Text Processing :: Markup :: HTML",
    "Topic :: Text Processing :: Markup :: XML"
]
author="Lukas Almad Linhart"
author_email="bugs@almad.net"
url="http://projects.almad.net/sneakylang"
cp_license="LGPL"
packages=[
    "sneakylang"
]
download_url="http://projects.almad.net/sneakylang/wiki/Download"
data_files=[]
###############################################################################
# end arguments for setup
###############################################################################

def main():
    if sys.version < required_python_version:
        s = "I'm sorry, but %s %s requires Python %s or later."
        print s % (name, version, required_python_version)
        sys.exit(1)

    # set default location for "data_files" to platform specific "site-packages"
    # location
    for scheme in INSTALL_SCHEMES.values():
        scheme['data'] = scheme['purelib']

    setup(
        name=name,
        version=version,
        description=desc,
        long_description=long_desc,
        classifiers=classifiers,
        author=author,
        author_email=author_email,
        url=url,
        license=cp_license,
        packages=packages,
        download_url=download_url,
        data_files=data_files,
    )

if __name__ == "__main__":
    main()
<commit_msg>[svn] I think we can go for beta ;)<commit_after>"""Installs package using distutils

Run:
    python setup.py install

to install this package.
"""

###
#RPGPlanet Wiki engine: Fuel for your Wiki system
#Copyright (C) 2006 Lukas "Almad" Linhart http://www.almad.net/ and contributors,
# for a full list see http://projects.almad.net/rpgplanet-tools
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
###

from distutils.core import setup
from distutils.command.install import INSTALL_SCHEMES
import sys

required_python_version = '2.4'

###############################################################################
# arguments for the setup command
###############################################################################
name = "sneakylang"
version = "0.1_beta1"
desc = "Extensible framework for easy creation of extensible WikiLanguages"
long_desc = """"""
classifiers=[
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
    "Operating System :: OS Independent",
    "Programming Language :: Python",
    "Topic :: Software Development :: Documentation",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Text Processing",
    "Topic :: Text Processing :: Markup :: HTML",
    "Topic :: Text Processing :: Markup :: XML"
]
author="Lukas Almad Linhart"
author_email="bugs@almad.net"
url="http://projects.almad.net/sneakylang"
cp_license="LGPL"
packages=[
    "sneakylang"
]
download_url="http://projects.almad.net/sneakylang/wiki/Download"
data_files=[]
###############################################################################
# end arguments for setup
###############################################################################

def main():
    if sys.version < required_python_version:
        s = "I'm sorry, but %s %s requires Python %s or later."
        print s % (name, version, required_python_version)
        sys.exit(1)

    # set default location for "data_files" to platform specific "site-packages"
    # location
    for scheme in INSTALL_SCHEMES.values():
        scheme['data'] = scheme['purelib']

    setup(
        name=name,
        version=version,
        description=desc,
        long_description=long_desc,
        classifiers=classifiers,
        author=author,
        author_email=author_email,
        url=url,
        license=cp_license,
        packages=packages,
        download_url=download_url,
        data_files=data_files,
    )

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4 nu

from __future__ import (unicode_literals, absolute_import,
                        division, print_function)
import logging

from django.core.exceptions import PermissionDenied

from snisi_core.models.Entities import Entity
from snisi_core.models.Providers import Provider
from snisi_core.models.Projects import Domain
from snisi_tools.misc import split_or

logger = logging.getLogger(__name__)

def provider_is_allowed(provider, slug, location=None):
    """ single entry point for provider permission check

        permission slugs respects the following format:

        <action>_<description>

        action in:
            * access (read data)
            * create-report (create reports)
            * download (download reports and files)
            * edit-report (modify existing reports)
            * validate-report (change validation status)
            # * view-map (maps provide overiew on larger locations)
            * monitor (for snisi-tech only)
            * manage (for snisi-admin only) """

    if not isinstance(provider, Provider):
        return False

    prole = provider.role.slug
    plocation = provider.location.casted()
    privileges = provider.privileges_dict
    action, domain, extension = split_or(slug, 3, max_split=2,
                                         char="_", default='')

    # admin is god.
    if prole == 'snisi_admin':
        return True

    if location is None:
        location = Entity.get_or_none('mali')
    else:
        location = location.casted()

    for domain in Domain.active.all():
        allow_func = domain.import_from('permissions.provider_is_allowed')
        if allow_func is None:
            continue
        ret = allow_func(prole=prole,
                         plocation=plocation,
                         privileges=privileges,
                         location=location,
                         action=action,
                         domain=domain,
                         extension=extension)
        if ret is None:
            continue
        return ret

    ret = default_permissions(prole=prole,
                              plocation=plocation,
                              privileges=privileges,
                              location=location,
                              action=action,
                              domain=domain,
                              extension=extension)
    if ret is not None:
        return ret

    # default to non-granted
    return False

def default_permissions(prole, plocation, privileges,
                        location, action, domain, extension):

    # snisi-tech can view every-thing
    if prole == 'snisi_tech' and action in ('access',
                                            'download'):
        return True

    # all roles can see data within their area
    # can view data
    if action in ('access', 'download'):
        if location in plocation.get_health_descendants() + [plocation]:
            return True

def provider_is_allowed_at_home(provider, slug):
    return provider_is_allowed(provider=provider, slug=slug,
                               location=provider.location)

def provider_allowed_or_denied(provider, slug, location=None):
    """ easy permission check for view """
    if not provider_is_allowed(provider, slug, location):
        raise PermissionDenied
    return True

def provider_allowed_or_denied_at_home(provider, slug):
    """ easy permission check on provider's own location """
    if not provider_is_allowed_at_home(provider, slug):
        raise PermissionDenied
    return True
<commit_msg>handle anonymous users<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4 nu

from __future__ import (unicode_literals, absolute_import,
                        division, print_function)
import logging

from django.core.exceptions import PermissionDenied

from snisi_core.models.Entities import Entity
from snisi_core.models.Providers import Provider
from snisi_core.models.Projects import Domain
from snisi_tools.misc import split_or

logger = logging.getLogger(__name__)

def provider_is_allowed(provider, slug, location=None):
    """ single entry point for provider permission check

        permission slugs respects the following format:

        <action>_<description>

        action in:
            * access (read data)
            * create-report (create reports)
            * download (download reports and files)
            * edit-report (modify existing reports)
            * validate-report (change validation status)
            # * view-map (maps provide overiew on larger locations)
            * monitor (for snisi-tech only)
            * manage (for snisi-admin only) """

    # not a provider or AnonymousUser
    if not isinstance(provider, Provider) or not provider.is_authenticated():
        return False

    # not properly configured provider
    if not getattr(provider, 'role', None) or \
            not getattr(provider, 'location', None):
        return False

    prole = provider.role.slug
    plocation = provider.location.casted()
    privileges = provider.privileges_dict
    action, domain, extension = split_or(slug, 3, max_split=2,
                                         char="_", default='')

    # admin is god.
    if prole == 'snisi_admin':
        return True

    if location is None:
        location = Entity.get_or_none('mali')
    else:
        location = location.casted()

    for domain in Domain.active.all():
        allow_func = domain.import_from('permissions.provider_is_allowed')
        if allow_func is None:
            continue
        ret = allow_func(prole=prole,
                         plocation=plocation,
                         privileges=privileges,
                         location=location,
                         action=action,
                         domain=domain,
                         extension=extension)
        if ret is None:
            continue
        return ret

    ret = default_permissions(prole=prole,
                              plocation=plocation,
                              privileges=privileges,
                              location=location,
                              action=action,
                              domain=domain,
                              extension=extension)
    if ret is not None:
        return ret

    # default to non-granted
    return False

def default_permissions(prole, plocation, privileges,
                        location, action, domain, extension):

    # snisi-tech can view every-thing
    if prole == 'snisi_tech' and action in ('access',
                                            'download'):
        return True

    # all roles can see data within their area
    # can view data
    if action in ('access', 'download'):
        if location in plocation.get_health_descendants() + [plocation]:
            return True

def provider_is_allowed_at_home(provider, slug):
    return provider_is_allowed(provider=provider, slug=slug,
                               location=provider.location)

def provider_allowed_or_denied(provider, slug, location=None):
    """ easy permission check for view """
    if not provider_is_allowed(provider, slug, location):
        raise PermissionDenied
    return True

def provider_allowed_or_denied_at_home(provider, slug):
    """ easy permission check on provider's own location """
    if not provider_is_allowed_at_home(provider, slug):
        raise PermissionDenied
    return True
<|endoftext|>


<commit_before>"""
Reddit OAuth2 backend, docs at:
    http://psa.matiasaguirre.net/docs/backends/reddit.html
"""
import base64

from social.backends.oauth import BaseOAuth2

class RedditOAuth2(BaseOAuth2):
    """Reddit OAuth2 authentication backend"""
    name = 'reddit'
    AUTHORIZATION_URL = 'https://ssl.reddit.com/api/v1/authorize'
    ACCESS_TOKEN_URL = 'https://ssl.reddit.com/api/v1/access_token'
    ACCESS_TOKEN_METHOD = 'POST'
    REFRESH_TOKEN_METHOD = 'POST'
    REDIRECT_STATE = False
    SCOPE_SEPARATOR = ','
    DEFAULT_SCOPE = ['identity']
    SEND_USER_AGENT = True
    EXTRA_DATA = [
        ('id', 'id'),
        ('name', 'username'),
        ('link_karma', 'link_karma'),
        ('comment_karma', 'comment_karma'),
        ('refresh_token', 'refresh_token'),
        ('expires_in', 'expires')
    ]

    def get_user_details(self, response):
        """Return user details from Reddit account"""
        return {'username': response.get('name'),
                'email': '', 'fullname': '',
                'first_name': '', 'last_name': ''}

    def user_data(self, access_token, *args, **kwargs):
        """Loads user data from service"""
        return self.get_json(
            'https://oauth.reddit.com/api/v1/me.json',
            headers={'Authorization': 'bearer ' + access_token}
        )

    def auth_headers(self):
        return {
            'Authorization': b'Basic ' + base64.urlsafe_b64encode(
                ('{0}:{1}'.format(*self.get_key_and_secret()).encode())
            ))
        }

    def refresh_token_params(self, token, redirect_uri=None, *args, **kwargs):
        params = super(RedditOAuth2, self).refresh_token_params(token)
        params['redirect_uri'] = self.redirect_uri or redirect_uri
        return params
<commit_msg>syntax fix<commit_after>"""
Reddit OAuth2 backend, docs at:
    http://psa.matiasaguirre.net/docs/backends/reddit.html
"""
import base64

from social.backends.oauth import BaseOAuth2

class RedditOAuth2(BaseOAuth2):
    """Reddit OAuth2 authentication backend"""
    name = 'reddit'
    AUTHORIZATION_URL = 'https://ssl.reddit.com/api/v1/authorize'
    ACCESS_TOKEN_URL = 'https://ssl.reddit.com/api/v1/access_token'
    ACCESS_TOKEN_METHOD = 'POST'
    REFRESH_TOKEN_METHOD = 'POST'
    REDIRECT_STATE = False
    SCOPE_SEPARATOR = ','
    DEFAULT_SCOPE = ['identity']
    SEND_USER_AGENT = True
    EXTRA_DATA = [
        ('id', 'id'),
        ('name', 'username'),
        ('link_karma', 'link_karma'),
        ('comment_karma', 'comment_karma'),
        ('refresh_token', 'refresh_token'),
        ('expires_in', 'expires')
    ]

    def get_user_details(self, response):
        """Return user details from Reddit account"""
        return {'username': response.get('name'),
                'email': '', 'fullname': '',
                'first_name': '', 'last_name': ''}

    def user_data(self, access_token, *args, **kwargs):
        """Loads user data from service"""
        return self.get_json(
            'https://oauth.reddit.com/api/v1/me.json',
            headers={'Authorization': 'bearer ' + access_token}
        )

    def auth_headers(self):
        return {
            'Authorization': b'Basic ' + base64.urlsafe_b64encode(
                ('{0}:{1}'.format(*self.get_key_and_secret()).encode())
            )
        }

    def refresh_token_params(self, token, redirect_uri=None, *args, **kwargs):
        params = super(RedditOAuth2, self).refresh_token_params(token)
        params['redirect_uri'] = self.redirect_uri or redirect_uri
        return params
<|endoftext|>