<commit_before>"""
Django settings for oeplatform project.

Generated by 'django-admin startproject' using Django 1.8.5.

For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)

try:
    from .securitysettings import *
except:
    raise Exception("No securitysettings found")

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sessions.backends.signed_cookies',
    'bootstrap3',
    'rest_framework',
    'rest_framework.authtoken',
    'modelview',
    'modelview.templatetags.modelview_extras',
    'login',
    'base',
    'base.templatetags.base_tags',
    'widget_tweaks',
    'dataedit',
    'colorfield',
    'django_ajax',
    'literature',
    'api',
    'axes',
    'django.contrib.postgres'
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'login.middleware.DetachMiddleware'
)

ROOT_URLCONF = 'oeplatform.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'oeplatform.wsgi.application'

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'Europe/Berlin'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/

AUTH_USER_MODEL = 'login.myuser'
LOGIN_REDIRECT_URL = '/'

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}<commit_msg>Set LOGIN_URL<commit_after>"""
Django settings for oeplatform project.

Generated by 'django-admin startproject' using Django 1.8.5.

For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)

try:
    from .securitysettings import *
except:
    raise Exception("No securitysettings found")

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sessions.backends.signed_cookies',
    'bootstrap3',
    'rest_framework',
    'rest_framework.authtoken',
    'modelview',
    'modelview.templatetags.modelview_extras',
    'login',
    'base',
    'base.templatetags.base_tags',
    'widget_tweaks',
    'dataedit',
    'colorfield',
    'django_ajax',
    'literature',
    'api',
    'axes',
    'django.contrib.postgres'
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'login.middleware.DetachMiddleware'
)

ROOT_URLCONF = 'oeplatform.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'oeplatform.wsgi.application'

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'Europe/Berlin'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/

AUTH_USER_MODEL = 'login.myuser'
LOGIN_URL = '/login'
LOGIN_REDIRECT_URL = '/'

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}<|endoftext|>


<commit_before>from __future__ import print_function

from collections import namedtuple
import errno
import os
import shutil
import subprocess

import click

def validate_dir(ctx, param, value):
    if value is not None and ('/' in value or '\0' in value):
        raise click.BadParameter("must be a directory name.")
    return value

@click.group()
@click.option('-d', '--dir', envvar='STEEVE_DIR', metavar='DIR',
              default='/usr/local/stow',
              help="Set location of packages to DIR.")
@click.option('-t', '--target', envvar='STEEVE_TARGET', metavar='DIR',
              default='/usr/local',
              help="Set stow target to DIR.")
@click.option('--no-folding', envvar='STEEVE_NO_FOLDING', is_flag=True,
              help="Disable folding of newly stowed directories.")
@click.pass_context
def cli(ctx, dir, target, no_folding):
    ctx.obj = Steeve(dir, target, no_folding)

@cli.command(help="Install package from given folder.")
@click.argument('package', callback=validate_dir)
@click.argument('version', callback=validate_dir)
@click.argument('path')
@click.pass_obj
def install(steeve, package, version, path):
    steeve.install(package, version, path)

@cli.command(help="Remove the whole package or specific version.")
@click.argument('package', callback=validate_dir)
@click.argument('version', required=False, callback=validate_dir)
@click.pass_obj
def uninstall(steeve, package, version):
    steeve.uninstall(package, version)

@cli.command(help="Stow given package version into target dir.")
@click.argument('package', callback=validate_dir)
@click.argument('version', callback=validate_dir)
@click.pass_obj
def use(steeve, package, version):
    steeve.use(package, version)

@cli.command(help="Delete stowed symlinks.")
@click.argument('package', callback=validate_dir)
@click.pass_obj
def unuse(steeve, package):
    steeve.unstow(package)

@cli.command(help="List packages or package versions.")
@click.argument('package', required=False, callback=validate_dir)
@click.pass_obj
def ls(steeve, package):
    steeve.ls(package)

class Steeve(namedtuple('Steeve', 'dir target no_folding')):
    def install(self, package, version, path):
        makedirs(self.package_path(package), exist_ok=True)
        try:
            shutil.copytree(path, self.package_path(package, version))
        except OSError as err:
            if err.errno == errno.EEXIST and os.path.isdir(path):
                click.echo(err)
                click.echo("the package '{}/{}' is already installed"
                           .format(package, version),
                           err=True)
                return
            else:
                raise

        self.unstow(package)
        self.link_current(package, version)
        self.stow(package)

    def uninstall(self, package, version):
        if version is None:
            self.unstow(package)
            shutil.rmtree(self.package_path(package))
        else:
            current = self.current_version(package)
            if version == current:
                self.unstow(package)
            shutil.rmtree(self.package_path(package, version))

            # Remove empty package folder
            if not os.listdir(self.package_path(package)):
                os.rmdir(self.package_path(package))

    def use(self, package, version):
        pass
        if not os.path.exists(self.package_path(package, version)):
            click.echo("package '{}/{}' is not installed"
                       .format(package, version),
                       err=True)
            return

        self.unstow(package)
        self.link_current(package, version)
        self.stow(package)

    def ls(self, package):
        if package is None:
            packages = os.listdir(self.dir)
            for package in packages:
                click.echo(package)
        else:
            try:
                versions = os.listdir(self.package_path(package))
            except OSError as err:
                if err.errno == errno.ENOENT:
                    click.echo("no such package '{}'"
                               .format(package),
                               err=True)
                    return
                else:
                    raise
            try:
                versions.remove('current')
            except ValueError:
                pass
            current = self.current_version(package)
            for version in versions:
                used = '* ' if current == version else '  '
                click.echo(used + version)

    def link_current(self, package, version):
        current = self.package_path(package, 'current')
        try:
            os.remove(current)
        except OSError as err:
            if err.errno != errno.ENOENT:
                raise
        os.symlink(self.package_path(package, version), current)

    def current_version(self, package):
        try:
            dst = os.readlink(self.package_path(package, 'current'))
        except OSError as err:
            if err.errno == errno.ENOENT:
                return None
            else:
                raise
        return os.path.basename(dst)

    def stow(self, package):
        try:
            args = [
                'stow',
                '-t', self.target,
                '-d', self.package_path(package),
                'current'
            ]
            if self.no_folding:
                args.insert(1, '--no-folding')
            subprocess.check_output(args)
        except subprocess.CalledProcessError as err:
            click.echo('stow returned code {}'
                       .format(err.returncode),
                       err=True)

    def unstow(self, package):
        if self.current_version(package) is None:
            return
        try:
            subprocess.check_output([
                'stow',
                '-t', self.target,
                '-d', self.package_path(package),
                '-D',
                'current'])
        except subprocess.CalledProcessError as err:
            click.echo('stow returned code {}'
                       .format(err.returncode),
                       err=True)
        os.remove(self.package_path(package, 'current'))

    def package_path(self, package, version=None):
        if version is None:
            return os.path.join(self.dir, package)
        else:
            return os.path.join(self.dir, package, version)

def makedirs(name, mode=0o777, exist_ok=False):
    """makedirs(name [, mode=0o777][, exist_ok=False])

    Super-mkdir; create a leaf directory and all intermediate ones.
    Works like mkdir, except that any intermediate path segment (not
    just the rightmost) will be created if it does not exist.  If the
    target directory already exists, raise an OSError if exist_ok is
    False. Otherwise no exception is raised.  This is recursive.

    Shamelessly copied from Python 3.4.2 os module.
    """
    head, tail = os.path.split(name)
    if not tail:
        head, tail = os.path.split(head)
    if head and tail and not os.path.exists(head):
        try:
            makedirs(head, mode, exist_ok)
        except OSError, e:
            # be happy if someone already created the path
            if e.errno != errno.EEXIST:
                raise
        cdir = os.curdir
        if isinstance(tail, bytes):
            cdir = bytes(os.curdir, 'ASCII')
        if tail == cdir:  # xxx/newdir/. exists if xxx/newdir exists
            return
    try:
        os.mkdir(name, mode)
    except OSError as e:
        if not exist_ok or e.errno != errno.EEXIST or not os.path.isdir(name):
            raise

if __name__ == '__main__':
    cli()
<commit_msg>Remove print_function<commit_after>from collections import namedtuple
import errno
import os
import shutil
import subprocess

import click

def validate_dir(ctx, param, value):
    if value is not None and ('/' in value or '\0' in value):
        raise click.BadParameter("must be a directory name.")
    return value

@click.group()
@click.option('-d', '--dir', envvar='STEEVE_DIR', metavar='DIR',
              default='/usr/local/stow',
              help="Set location of packages to DIR.")
@click.option('-t', '--target', envvar='STEEVE_TARGET', metavar='DIR',
              default='/usr/local',
              help="Set stow target to DIR.")
@click.option('--no-folding', envvar='STEEVE_NO_FOLDING', is_flag=True,
              help="Disable folding of newly stowed directories.")
@click.pass_context
def cli(ctx, dir, target, no_folding):
    ctx.obj = Steeve(dir, target, no_folding)

@cli.command(help="Install package from given folder.")
@click.argument('package', callback=validate_dir)
@click.argument('version', callback=validate_dir)
@click.argument('path')
@click.pass_obj
def install(steeve, package, version, path):
    steeve.install(package, version, path)

@cli.command(help="Remove the whole package or specific version.")
@click.argument('package', callback=validate_dir)
@click.argument('version', required=False, callback=validate_dir)
@click.pass_obj
def uninstall(steeve, package, version):
    steeve.uninstall(package, version)

@cli.command(help="Stow given package version into target dir.")
@click.argument('package', callback=validate_dir)
@click.argument('version', callback=validate_dir)
@click.pass_obj
def use(steeve, package, version):
    steeve.use(package, version)

@cli.command(help="Delete stowed symlinks.")
@click.argument('package', callback=validate_dir)
@click.pass_obj
def unuse(steeve, package):
    steeve.unstow(package)

@cli.command(help="List packages or package versions.")
@click.argument('package', required=False, callback=validate_dir)
@click.pass_obj
def ls(steeve, package):
    steeve.ls(package)

class Steeve(namedtuple('Steeve', 'dir target no_folding')):
    def install(self, package, version, path):
        makedirs(self.package_path(package), exist_ok=True)
        try:
            shutil.copytree(path, self.package_path(package, version))
        except OSError as err:
            if err.errno == errno.EEXIST and os.path.isdir(path):
                click.echo(err)
                click.echo("the package '{}/{}' is already installed"
                           .format(package, version),
                           err=True)
                return
            else:
                raise

        self.unstow(package)
        self.link_current(package, version)
        self.stow(package)

    def uninstall(self, package, version):
        if version is None:
            self.unstow(package)
            shutil.rmtree(self.package_path(package))
        else:
            current = self.current_version(package)
            if version == current:
                self.unstow(package)
            shutil.rmtree(self.package_path(package, version))

            # Remove empty package folder
            if not os.listdir(self.package_path(package)):
                os.rmdir(self.package_path(package))

    def use(self, package, version):
        pass
        if not os.path.exists(self.package_path(package, version)):
            click.echo("package '{}/{}' is not installed"
                       .format(package, version),
                       err=True)
            return

        self.unstow(package)
        self.link_current(package, version)
        self.stow(package)

    def ls(self, package):
        if package is None:
            packages = os.listdir(self.dir)
            for package in packages:
                click.echo(package)
        else:
            try:
                versions = os.listdir(self.package_path(package))
            except OSError as err:
                if err.errno == errno.ENOENT:
                    click.echo("no such package '{}'"
                               .format(package),
                               err=True)
                    return
                else:
                    raise
            try:
                versions.remove('current')
            except ValueError:
                pass
            current = self.current_version(package)
            for version in versions:
                used = '* ' if current == version else '  '
                click.echo(used + version)

    def link_current(self, package, version):
        current = self.package_path(package, 'current')
        try:
            os.remove(current)
        except OSError as err:
            if err.errno != errno.ENOENT:
                raise
        os.symlink(self.package_path(package, version), current)

    def current_version(self, package):
        try:
            dst = os.readlink(self.package_path(package, 'current'))
        except OSError as err:
            if err.errno == errno.ENOENT:
                return None
            else:
                raise
        return os.path.basename(dst)

    def stow(self, package):
        try:
            args = [
                'stow',
                '-t', self.target,
                '-d', self.package_path(package),
                'current'
            ]
            if self.no_folding:
                args.insert(1, '--no-folding')
            subprocess.check_output(args)
        except subprocess.CalledProcessError as err:
            click.echo('stow returned code {}'
                       .format(err.returncode),
                       err=True)

    def unstow(self, package):
        if self.current_version(package) is None:
            return
        try:
            subprocess.check_output([
                'stow',
                '-t', self.target,
                '-d', self.package_path(package),
                '-D',
                'current'])
        except subprocess.CalledProcessError as err:
            click.echo('stow returned code {}'
                       .format(err.returncode),
                       err=True)
        os.remove(self.package_path(package, 'current'))

    def package_path(self, package, version=None):
        if version is None:
            return os.path.join(self.dir, package)
        else:
            return os.path.join(self.dir, package, version)

def makedirs(name, mode=0o777, exist_ok=False):
    """makedirs(name [, mode=0o777][, exist_ok=False])

    Super-mkdir; create a leaf directory and all intermediate ones.
    Works like mkdir, except that any intermediate path segment (not
    just the rightmost) will be created if it does not exist.  If the
    target directory already exists, raise an OSError if exist_ok is
    False. Otherwise no exception is raised.  This is recursive.

    Shamelessly copied from Python 3.4.2 os module.
    """
    head, tail = os.path.split(name)
    if not tail:
        head, tail = os.path.split(head)
    if head and tail and not os.path.exists(head):
        try:
            makedirs(head, mode, exist_ok)
        except OSError, e:
            # be happy if someone already created the path
            if e.errno != errno.EEXIST:
                raise
        cdir = os.curdir
        if isinstance(tail, bytes):
            cdir = bytes(os.curdir, 'ASCII')
        if tail == cdir:  # xxx/newdir/. exists if xxx/newdir exists
            return
    try:
        os.mkdir(name, mode)
    except OSError as e:
        if not exist_ok or e.errno != errno.EEXIST or not os.path.isdir(name):
            raise

if __name__ == '__main__':
    cli()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
""" OAuth support, facebook and google.

Session variables:

user.biv_id: User's id, survives after log out
user.oauth_type, User's oauth_type, survives after log out
user.is_logged_in, User's logged in status
user.display_name, User's full name

oauth.next_uri, redirect uri after successful login,
    cleared after authorize_complete()
oauth.state, oauth state var, cleared after authorize_complete()
oauth.<oauth_type>.token, user's oauth token, cleared during log out

    :copyright: Copyright (c) 2014 Bivio Software, Inc.  All Rights Reserved.
    :license: Apache, see LICENSE for more details.
"""

import flask
import flask_oauthlib.client
import werkzeug

from .. import controller as ppc
from ..auth import model as pam

_OAUTH_PROVIDER_DATA_PATH = {
    'facebook': 'me',
    'google': 'userinfo',
}

def add_user_to_session(user):
    """Store user info on session"""
    ppc.db.session.add(user)
    ppc.db.session.flush()
    flask.session['user.biv_id'] = user.biv_id
    flask.session['user.oauth_type'] = user.oauth_type
    flask.session['user.is_logged_in'] = True
    flask.session['user.display_name'] = user.display_name

def authorize(oauth_type, callback):
    """Call the OAuth provider's server which returns to the callback
    on completion or failer.
    Store the "next" or referrer page to return to after callback."""
    flask.session['oauth.next_uri'] = flask.request.args.get('next') \
        or flask.request.referrer or None
    # state variable is uri randomizer, verfied in authorize_complete()
    state = werkzeug.security.gen_salt(64)
    flask.session['oauth.state'] = state
    return _oauth_provider(oauth_type).authorize(
        callback=callback,
        state=state
    )

def authorize_complete(oauth_type):
    """Handle oauth callback, validate the response and update the user
    model."""
    oauth = _oauth_provider(oauth_type)
    resp = oauth.authorized_response()
    next_uri = None
    if 'oauth.next_uri' in flask.session:
        next_uri = flask.session['oauth.next_uri']
        del flask.session['oauth.next_uri']
    if _validate_auth(resp, oauth_type):
        flask.session['oauth.{}.token'.format(oauth_type)] = (
            resp['access_token'], '')
        _user_from_info(
            oauth,
            oauth_type,
            oauth.get(_OAUTH_PROVIDER_DATA_PATH[oauth_type]).data
        )
    return flask.redirect(next_uri or '/')

def logout():
    """Clear the login state from the session and redirect to root."""
    _clear_session()
    flask.flash('You have successfully logged out.')
    return flask.redirect('/')

def _avatar_url_from_info(oauth_type, info):
    """Returns a URL for the user avatar, depending on oauth_type"""
    if oauth_type == 'facebook':
        return 'https://graph.facebook.com/{}/picture?type=square'.format(
            info['id'])
    elif oauth_type == 'google':
        return info.get('picture')
    return None

def _clear_session(clear_oauth_type=False):
    """Clear the login state from the session"""
    flask.session['user.is_logged_in'] = False
    if 'oauth.state' in flask.session:
        del flask.session['oauth.state']
    oauth_type = flask.session.get('user.oauth_type')
    if oauth_type:
        key = 'oauth.{}.token'.format(oauth_type)
        if flask.session.get(key):
            del flask.session[key]
        if clear_oauth_type:
            del flask.session['user.oauth_type']

def _client_error(oauth_type, message=None):
    _clear_session(True)
    flask.flash(message \
        or '{} has denied access to this App.'.format(oauth_type))

def _get_facebook_oauth_token():
    """Callback for facebook auth"""
    return flask.session.get('oauth.facebook.token')

def _get_google_access_token():
    """Callback for google auth"""
    return flask.session.get('oauth.google.token')

def _oauth_provider(name):
    """Creates a OAuth client for the named provider."""
    attributes = None
    if name == 'facebook':
        attributes = {
            'request_token_params': {'scope': 'email'},
            'base_url': 'https://graph.facebook.com',
            'access_token_url': '/oauth/access_token',
            'authorize_url': 'https://www.facebook.com/dialog/oauth'
        }
        tokengetter = _get_facebook_oauth_token
    elif name == 'google':
        attributes = {
            'request_token_params': {
                'scope': 'https://www.googleapis.com/auth/userinfo.email'
                },
            'base_url': 'https://www.googleapis.com/oauth2/v1/',
            'access_token_method': 'POST',
            'access_token_url': 'https://accounts.google.com/o/oauth2/token',
            'authorize_url': 'https://accounts.google.com/o/oauth2/auth'
        }
        tokengetter = _get_google_access_token
    else:
        raise Exception('Unknown oauth provider name: {}'.format(name))
    c = ppc.app().config['PUBLICPRIZE'][name.upper()]
    oauth = flask_oauthlib.client.OAuth(ppc.app()).remote_app(
        name,
        consumer_key=c['app_id'],
        consumer_secret=c['app_secret'],
        request_token_url=None,
        **attributes
    )
    oauth.tokengetter(tokengetter)
    return oauth

def _user_from_info(oauth, oauth_type, info):
    """Saves oauth provider user info to user model.
    info arg contains email, id, name."""
    ppc.app().logger.info(info)
    if not info.get('email'):
        if oauth_type =='facebook':
            oauth.delete('{}/permissions'.format(info['id']), format=None)
        _client_error(
            oauth_type, 'Your email must be provided to this App to login.')
        return
    user = pam.User.query.filter_by(
        oauth_type=oauth_type,
        oauth_id=info['id']
    ).first()
    if user:
        user.display_name = info['name']
        user.user_email = info['email']
        user.avatar_url = _avatar_url_from_info(oauth_type, info)
    else:
        user = pam.User(
            display_name=info['name'],
            user_email=info['email'],
            oauth_type=oauth_type,
            oauth_id=info['id'],
            avatar_url=_avatar_url_from_info(oauth_type, info)
        )
    add_user_to_session(user)

def _validate_auth(resp, oauth_type):
    """Validates oauth providers's auth response"""
    app = ppc.app()

    if resp is None or isinstance(resp, flask_oauthlib.client.OAuthException):
        _client_error(oauth_type)
        app.logger.warn(
            'Access denied: {}, resp: {}'.format(flask.request.args, resp))
        return False
    state = flask.session['oauth.state']
    del flask.session['oauth.state']
    if state != flask.request.args.get('state'):
        _client_error(oauth_type)
        app.logger.warn(
            'Invalid oauth state, expected: {} response: {}'.format(
                state,
                flask.request.args.get('state'))
        )
        return False
    return True
<commit_msg>added linkedin oauth<commit_after># -*- coding: utf-8 -*-
""" OAuth support, facebook and google.

Session variables:

user.biv_id: User's id, survives after log out
user.oauth_type, User's oauth_type, survives after log out
user.is_logged_in, User's logged in status
user.display_name, User's full name

oauth.next_uri, redirect uri after successful login,
    cleared after authorize_complete()
oauth.state, oauth state var, cleared after authorize_complete()
oauth.<oauth_type>.token, user's oauth token, cleared during log out

    :copyright: Copyright (c) 2014 Bivio Software, Inc.  All Rights Reserved.
    :license: Apache, see LICENSE for more details.
"""

import flask
import flask_oauthlib.client
import werkzeug

from .. import controller as ppc
from ..auth import model as pam

_OAUTH_PROVIDER_DATA_PATH = {
    'facebook': 'me',
    'google': 'userinfo',
    'linkedin': 'people/~:(id,first-name,last-name,email-address)'
}

def add_user_to_session(user):
    """Store user info on session"""
    ppc.db.session.add(user)
    ppc.db.session.flush()
    flask.session['user.biv_id'] = user.biv_id
    flask.session['user.oauth_type'] = user.oauth_type
    flask.session['user.is_logged_in'] = True
    flask.session['user.display_name'] = user.display_name

def authorize(oauth_type, callback):
    """Call the OAuth provider's server which returns to the callback
    on completion or failer.
    Store the "next" or referrer page to return to after callback."""
    flask.session['oauth.next_uri'] = flask.request.args.get('next') \
        or flask.request.referrer or None
    # state variable is uri randomizer, verfied in authorize_complete()
    state = werkzeug.security.gen_salt(64)
    flask.session['oauth.state'] = state
    return _oauth_provider(oauth_type).authorize(
        callback=callback,
        state=state
    )

def authorize_complete(oauth_type):
    """Handle oauth callback, validate the response and update the user
    model."""
    oauth = _oauth_provider(oauth_type)
    resp = oauth.authorized_response()
    next_uri = None
    if 'oauth.next_uri' in flask.session:
        next_uri = flask.session['oauth.next_uri']
        del flask.session['oauth.next_uri']
    if _validate_auth(resp, oauth_type):
        flask.session['oauth.{}.token'.format(oauth_type)] = (
            resp['access_token'], '')
        _user_from_info(
            oauth,
            oauth_type,
            oauth.get(_OAUTH_PROVIDER_DATA_PATH[oauth_type]).data
        )
    return flask.redirect(next_uri or '/')

def logout():
    """Clear the login state from the session and redirect to root."""
    _clear_session()
    flask.flash('You have successfully logged out.')
    return flask.redirect('/')

def _avatar_url_from_info(oauth_type, info):
    """Returns a URL for the user avatar, depending on oauth_type"""
    if oauth_type == 'facebook':
        return 'https://graph.facebook.com/{}/picture?type=square'.format(
            info['id'])
    elif oauth_type == 'google':
        return info.get('picture')
    return None

def _change_linkedin_query(uri, headers, body):
    auth = headers.pop('Authorization')
    headers['x-li-format'] = 'json'
    if auth:
        auth = auth.replace('Bearer', '').strip()
        if '?' in uri:
            uri += '&oauth2_access_token=' + auth
        else:
            uri += '?oauth2_access_token=' + auth
    return uri, headers, body

def _clear_session(clear_oauth_type=False):
    """Clear the login state from the session"""
    flask.session['user.is_logged_in'] = False
    if 'oauth.state' in flask.session:
        del flask.session['oauth.state']
    oauth_type = flask.session.get('user.oauth_type')
    if oauth_type:
        key = 'oauth.{}.token'.format(oauth_type)
        if flask.session.get(key):
            del flask.session[key]
        if clear_oauth_type:
            del flask.session['user.oauth_type']

def _client_error(oauth_type, message=None):
    _clear_session(True)
    flask.flash(message \
        or '{} has denied access to this App.'.format(oauth_type))

def _get_facebook_oauth_token():
    """Callback for facebook auth"""
    return flask.session.get('oauth.facebook.token')

def _get_google_access_token():
    """Callback for google auth"""
    return flask.session.get('oauth.google.token')

def _get_linkedin_access_token():
    """Callback for linkedin auth"""
    return flask.session.get('oauth.linkedin.token')

def _oauth_provider(name):
    """Creates a OAuth client for the named provider."""
    attributes = None
    if name == 'facebook':
        attributes = {
            'request_token_params': {'scope': 'email'},
            'base_url': 'https://graph.facebook.com',
            'access_token_url': '/oauth/access_token',
            'authorize_url': 'https://www.facebook.com/dialog/oauth'
        }
        tokengetter = _get_facebook_oauth_token
    elif name == 'google':
        attributes = {
            'request_token_params': {
                'scope': 'https://www.googleapis.com/auth/userinfo.email'
                },
            'base_url': 'https://www.googleapis.com/oauth2/v1/',
            'access_token_method': 'POST',
            'access_token_url': 'https://accounts.google.com/o/oauth2/token',
            'authorize_url': 'https://accounts.google.com/o/oauth2/auth'
        }
        tokengetter = _get_google_access_token
    elif name == 'linkedin':
        attributes = {
            'request_token_params': {
                'scope': 'r_emailaddress'
                },
            'base_url': 'https://api.linkedin.com/v1/',
            'access_token_method': 'POST',
            'access_token_url': 'https://www.linkedin.com/uas/oauth2/accessToken',
            'authorize_url': 'https://www.linkedin.com/uas/oauth2/authorization',
        }
        tokengetter = _get_linkedin_access_token
    else:
        raise Exception('Unknown oauth provider name: {}'.format(name))
    c = ppc.app().config['PUBLICPRIZE'][name.upper()]
    oauth = flask_oauthlib.client.OAuth(ppc.app()).remote_app(
        name,
        consumer_key=c['app_id'],
        consumer_secret=c['app_secret'],
        request_token_url=None,
        **attributes
    )
    oauth.tokengetter(tokengetter)
    if name == 'linkedin':
        oauth.pre_request = _change_linkedin_query
    return oauth

def _user_from_info(oauth, oauth_type, info):
    """Saves oauth provider user info to user model.
    info arg contains email, id, name."""
    ppc.app().logger.info(info)
    if oauth_type == 'linkedin':
        info['name'] = info['firstName'] + ' ' + info['lastName']
        info['email'] = info['emailAddress']
    if not info.get('email'):
        if oauth_type == 'facebook':
            oauth.delete('{}/permissions'.format(info['id']), format=None)
        _client_error(
            oauth_type, 'Your email must be provided to this App to login.')
        return
    user = pam.User.query.filter_by(
        oauth_type=oauth_type,
        oauth_id=info['id']
    ).first()
    if user:
        user.display_name = info['name']
        user.user_email = info['email']
        user.avatar_url = _avatar_url_from_info(oauth_type, info)
    else:
        user = pam.User(
            display_name=info['name'],
            user_email=info['email'],
            oauth_type=oauth_type,
            oauth_id=info['id'],
            avatar_url=_avatar_url_from_info(oauth_type, info)
        )
    add_user_to_session(user)

def _validate_auth(resp, oauth_type):
    """Validates oauth providers's auth response"""
    app = ppc.app()

    if resp is None or isinstance(resp, flask_oauthlib.client.OAuthException):
        _client_error(oauth_type)
        app.logger.warn(
            'Access denied: {}, resp: {}'.format(flask.request.args, resp))
        return False
    state = flask.session['oauth.state']
    del flask.session['oauth.state']
    if state != flask.request.args.get('state'):
        _client_error(oauth_type)
        app.logger.warn(
            'Invalid oauth state, expected: {} response: {}'.format(
                state,
                flask.request.args.get('state'))
        )
        return False
    return True
<|endoftext|>


<commit_before># coding=utf-8
import sys
import random
import findspam
import datahandling
import chatcommunicate
from globalvars import GlobalVars
from datetime import datetime, timedelta
import parsing
import metasmoke
import excepthook
from classes import Post, PostParseError
from helpers import log, escape_format
from parsing import to_metasmoke_link
from tasks import Tasks

# noinspection PyMissingTypeHints
def should_whitelist_prevent_alert(user_url, reasons):
    is_whitelisted = datahandling.is_whitelisted_user(parsing.get_user_from_url(user_url))
    if not is_whitelisted:
        return False
    return not any(r for r in set(reasons) if "username" not in r)

def sum_weight(reasons: list):
    if not GlobalVars.reason_weights:
        datahandling.update_reason_weights()
    now = datetime.utcnow() - timedelta(minutes=15)
    if now.date() != GlobalVars.reason_weights['last_updated'] and now.hour >= 1:
        Tasks.do(datahandling.update_reason_weights)
    s = 0
    weights = GlobalVars.reason_weights
    for r in reasons:
        try:
            if "(" in r:
                r = regex.sub(r"\s*\(.*$", "", r)
            s += weights[r.lower()]
        except KeyError:
            pass  # s += 0
    return s

# noinspection PyMissingTypeHints
def check_if_spam(post):
    test, why = findspam.FindSpam.test_post(post)
    if datahandling.is_blacklisted_user(parsing.get_user_from_url(post.user_url)):
        test.append("blacklisted user")
        blacklisted_user_data = datahandling.get_blacklisted_user_data(parsing.get_user_from_url(post.user_url))
        if len(blacklisted_user_data) > 1:
            if blacklisted_user_data[1] == "metasmoke":
                blacklisted_by = "the metasmoke API"
            else:
                blacklisted_by = blacklisted_user_data[1]
            blacklisted_post_url = blacklisted_user_data[2]
            if why and why[-1] == "\n":
                why = why[:-1]
            if blacklisted_post_url:
                rel_url = blacklisted_post_url.replace("http:", "", 1)
                why += "\nBlacklisted user - blacklisted for {} ({}) by {}".format(
                    blacklisted_post_url, to_metasmoke_link(rel_url), blacklisted_by)
            else:
                why += "\n" + u"Blacklisted user - blacklisted by {}".format(blacklisted_by)
    if test:
        result = None
        if datahandling.has_already_been_posted(post.post_site, post.post_id, post.title):
            result = "post has already been reported"
        elif datahandling.is_false_positive((post.post_id, post.post_site)):
            result = "post is marked as false positive"
        elif should_whitelist_prevent_alert(post.user_url, test):
            result = "user is whitelisted"
        elif datahandling.is_ignored_post((post.post_id, post.post_site)):
            result = "post is ignored"
        elif datahandling.is_auto_ignored_post((post.post_id, post.post_site)):
            result = "post is automatically ignored"
        elif datahandling.has_community_bumped_post(post.post_url, post.body):
            result = "post is bumped by Community \u2666\uFE0F"
        # Dirty approach
        if result is None:  # Post not ignored
            return True, test, why
        else:
            return False, (test, why), result

    return False, None, ""
    # Return value: (True, reasons, why) if post is spam
    #               (False, None, "") if post is not spam
    #               (False, (reasons, why), ignore_info) if post is spam but ignored
    # This is required because !!/report will check for 3rd tuple item to decide if it's not spam or spam but ignored

# noinspection PyMissingTypeHints
def check_if_spam_json(json_data):
    try:
        post = Post(json_data=json_data)
    except PostParseError as err:
        log('error', 'Parse error {0} when parsing json_data {1!r}'.format(
            err, json_data))
        return False, '', ''
    is_spam, reason, why = check_if_spam(post)
    return is_spam, reason, why

# noinspection PyBroadException,PyProtectedMember
def handle_spam(post, reasons, why):
    post_url = parsing.to_protocol_relative(parsing.url_to_shortlink(post.post_url))
    poster_url = parsing.to_protocol_relative(parsing.user_url_to_shortlink(post.user_url))
    shortened_site = post.post_site.replace("stackexchange.com", "SE")  # site.stackexchange.com -> site.SE
    datahandling.append_to_latest_questions(post.post_site, post.post_id, post.title if not post.is_answer else "")
    if len(reasons) == 1 and ("all-caps title" in reasons or
                              "repeating characters in title" in reasons or
                              "repeating characters in body" in reasons or
                              "repeating characters in answer" in reasons or
                              "repeating words in title" in reasons or
                              "repeating words in body" in reasons or
                              "repeating words in answer" in reasons):
        datahandling.add_auto_ignored_post((post.post_id, post.post_site, datetime.now()))
    if why is not None and why != "":
        datahandling.add_why(post.post_site, post.post_id, why)
    if post.is_answer and post.post_id is not None and post.post_id is not "":
        datahandling.add_post_site_id_link((post.post_id, post.post_site, "answer"), post.parent.post_id)
    if GlobalVars.reason_weights or GlobalVars.metasmoke_key:
        reason_weight_s = " ({:,})".format(sum_weight(reasons))
    else:  # No reason weight if neither cache nor MS
        reason_weight_s = ""
    try:
        # If the post is an answer type post, the 'title' is going to be blank, so when posting the
        # message contents we need to set the post title to the *parent* title, so the message in the
        # chat is properly constructed with parent title instead. This will make things 'print'
        # in a proper way in chat messages.
        sanitized_title = parsing.sanitize_title(post.title if not post.is_answer else post.parent.title)
        sanitized_title = escape_format(sanitized_title).strip()

        prefix = u"[ [SmokeDetector](//goo.gl/eLDYqh) ]"
        if GlobalVars.metasmoke_key:
            prefix_ms = u"[ [SmokeDetector](//goo.gl/eLDYqh) | [MS]({}) ]".format(
                to_metasmoke_link(post_url, protocol=False))
        else:
            prefix_ms = prefix

        # We'll insert reason list later
        edited = '' if not post.edited else ' \u270F\uFE0F'
        if not post.user_name.strip() or (not poster_url or poster_url.strip() == ""):
            s = " {{}}{}: [{}]({}){} by a deleted user on `{}`".format(
                reason_weight_s, sanitized_title, post_url, edited, shortened_site)
            username = ""
        else:
            username = post.user_name.strip()
            escaped_username = escape_format(parsing.escape_markdown(username))
            s = " {{}}{}: [{}]({}){} by [{}]({}) on `{}`".format(
                reason_weight_s, sanitized_title, post_url, edited, escaped_username, poster_url, shortened_site)

        Tasks.do(metasmoke.Metasmoke.send_stats_on_post,
                 post.title_ignore_type, post_url, reasons, post.body, username,
                 post.user_link, why, post.owner_rep, post.post_score,
                 post.up_vote_count, post.down_vote_count)

        log('debug', GlobalVars.parser.unescape(s).encode('ascii', errors='replace'))
        GlobalVars.deletion_watcher.subscribe(post_url)

        reason = message = None
        for reason_count in range(5, 0, -1):  # Try 5 reasons and all the way down to 1
            reason = ", ".join(reasons[:reason_count])
            if len(reasons) > reason_count:
                reason += ", +{} more".format(len(reasons) - reason_count)
            reason = reason.capitalize()
            message = prefix_ms + s.format(reason)  # Insert reason list
            if len(message) <= 500:
                break  # Problem solved, stop attempting

        s = s.format(reason)  # Later code needs this variable
        if len(message) > 500:
            message = (prefix_ms + s)[:500]  # Truncate directly and keep MS link

        without_roles = tuple(["no-" + reason for reason in reasons]) + ("site-no-" + post.post_site,)

        if set(reasons) - GlobalVars.experimental_reasons == set() and \
                not why.startswith("Post manually "):
            chatcommunicate.tell_rooms(message, ("experimental",),
                                       without_roles, notify_site=post.post_site, report_data=(post_url, poster_url))
        else:
            chatcommunicate.tell_rooms(message, ("all", "site-" + post.post_site),
                                       without_roles, notify_site=post.post_site, report_data=(post_url, poster_url))
    except Exception as e:
        excepthook.uncaught_exception(*sys.exc_info())
<commit_msg>... forgot import regex<commit_after># coding=utf-8
import sys
import random
import findspam
import datahandling
import chatcommunicate
from globalvars import GlobalVars
from datetime import datetime, timedelta
import regex
import parsing
import metasmoke
import excepthook
from classes import Post, PostParseError
from helpers import log, escape_format
from parsing import to_metasmoke_link
from tasks import Tasks

# noinspection PyMissingTypeHints
def should_whitelist_prevent_alert(user_url, reasons):
    is_whitelisted = datahandling.is_whitelisted_user(parsing.get_user_from_url(user_url))
    if not is_whitelisted:
        return False
    return not any(r for r in set(reasons) if "username" not in r)

def sum_weight(reasons: list):
    if not GlobalVars.reason_weights:
        datahandling.update_reason_weights()
    now = datetime.utcnow() - timedelta(minutes=15)
    if now.date() != GlobalVars.reason_weights['last_updated'] and now.hour >= 1:
        Tasks.do(datahandling.update_reason_weights)
    s = 0
    weights = GlobalVars.reason_weights
    for r in reasons:
        try:
            if "(" in r:
                r = regex.sub(r"\s*\(.*$", "", r)
            s += weights[r.lower()]
        except KeyError:
            pass  # s += 0
    return s

# noinspection PyMissingTypeHints
def check_if_spam(post):
    test, why = findspam.FindSpam.test_post(post)
    if datahandling.is_blacklisted_user(parsing.get_user_from_url(post.user_url)):
        test.append("blacklisted user")
        blacklisted_user_data = datahandling.get_blacklisted_user_data(parsing.get_user_from_url(post.user_url))
        if len(blacklisted_user_data) > 1:
            if blacklisted_user_data[1] == "metasmoke":
                blacklisted_by = "the metasmoke API"
            else:
                blacklisted_by = blacklisted_user_data[1]
            blacklisted_post_url = blacklisted_user_data[2]
            if why and why[-1] == "\n":
                why = why[:-1]
            if blacklisted_post_url:
                rel_url = blacklisted_post_url.replace("http:", "", 1)
                why += "\nBlacklisted user - blacklisted for {} ({}) by {}".format(
                    blacklisted_post_url, to_metasmoke_link(rel_url), blacklisted_by)
            else:
                why += "\n" + u"Blacklisted user - blacklisted by {}".format(blacklisted_by)
    if test:
        result = None
        if datahandling.has_already_been_posted(post.post_site, post.post_id, post.title):
            result = "post has already been reported"
        elif datahandling.is_false_positive((post.post_id, post.post_site)):
            result = "post is marked as false positive"
        elif should_whitelist_prevent_alert(post.user_url, test):
            result = "user is whitelisted"
        elif datahandling.is_ignored_post((post.post_id, post.post_site)):
            result = "post is ignored"
        elif datahandling.is_auto_ignored_post((post.post_id, post.post_site)):
            result = "post is automatically ignored"
        elif datahandling.has_community_bumped_post(post.post_url, post.body):
            result = "post is bumped by Community \u2666\uFE0F"
        # Dirty approach
        if result is None:  # Post not ignored
            return True, test, why
        else:
            return False, (test, why), result

    return False, None, ""
    # Return value: (True, reasons, why) if post is spam
    #               (False, None, "") if post is not spam
    #               (False, (reasons, why), ignore_info) if post is spam but ignored
    # This is required because !!/report will check for 3rd tuple item to decide if it's not spam or spam but ignored

# noinspection PyMissingTypeHints
def check_if_spam_json(json_data):
    try:
        post = Post(json_data=json_data)
    except PostParseError as err:
        log('error', 'Parse error {0} when parsing json_data {1!r}'.format(
            err, json_data))
        return False, '', ''
    is_spam, reason, why = check_if_spam(post)
    return is_spam, reason, why

# noinspection PyBroadException,PyProtectedMember
def handle_spam(post, reasons, why):
    post_url = parsing.to_protocol_relative(parsing.url_to_shortlink(post.post_url))
    poster_url = parsing.to_protocol_relative(parsing.user_url_to_shortlink(post.user_url))
    shortened_site = post.post_site.replace("stackexchange.com", "SE")  # site.stackexchange.com -> site.SE
    datahandling.append_to_latest_questions(post.post_site, post.post_id, post.title if not post.is_answer else "")
    if len(reasons) == 1 and ("all-caps title" in reasons or
                              "repeating characters in title" in reasons or
                              "repeating characters in body" in reasons or
                              "repeating characters in answer" in reasons or
                              "repeating words in title" in reasons or
                              "repeating words in body" in reasons or
                              "repeating words in answer" in reasons):
        datahandling.add_auto_ignored_post((post.post_id, post.post_site, datetime.now()))
    if why is not None and why != "":
        datahandling.add_why(post.post_site, post.post_id, why)
    if post.is_answer and post.post_id is not None and post.post_id is not "":
        datahandling.add_post_site_id_link((post.post_id, post.post_site, "answer"), post.parent.post_id)
    if GlobalVars.reason_weights or GlobalVars.metasmoke_key:
        reason_weight_s = " ({:,})".format(sum_weight(reasons))
    else:  # No reason weight if neither cache nor MS
        reason_weight_s = ""
    try:
        # If the post is an answer type post, the 'title' is going to be blank, so when posting the
        # message contents we need to set the post title to the *parent* title, so the message in the
        # chat is properly constructed with parent title instead. This will make things 'print'
        # in a proper way in chat messages.
        sanitized_title = parsing.sanitize_title(post.title if not post.is_answer else post.parent.title)
        sanitized_title = escape_format(sanitized_title).strip()

        prefix = u"[ [SmokeDetector](//goo.gl/eLDYqh) ]"
        if GlobalVars.metasmoke_key:
            prefix_ms = u"[ [SmokeDetector](//goo.gl/eLDYqh) | [MS]({}) ]".format(
                to_metasmoke_link(post_url, protocol=False))
        else:
            prefix_ms = prefix

        # We'll insert reason list later
        edited = '' if not post.edited else ' \u270F\uFE0F'
        if not post.user_name.strip() or (not poster_url or poster_url.strip() == ""):
            s = " {{}}{}: [{}]({}){} by a deleted user on `{}`".format(
                reason_weight_s, sanitized_title, post_url, edited, shortened_site)
            username = ""
        else:
            username = post.user_name.strip()
            escaped_username = escape_format(parsing.escape_markdown(username))
            s = " {{}}{}: [{}]({}){} by [{}]({}) on `{}`".format(
                reason_weight_s, sanitized_title, post_url, edited, escaped_username, poster_url, shortened_site)

        Tasks.do(metasmoke.Metasmoke.send_stats_on_post,
                 post.title_ignore_type, post_url, reasons, post.body, username,
                 post.user_link, why, post.owner_rep, post.post_score,
                 post.up_vote_count, post.down_vote_count)

        log('debug', GlobalVars.parser.unescape(s).encode('ascii', errors='replace'))
        GlobalVars.deletion_watcher.subscribe(post_url)

        reason = message = None
        for reason_count in range(5, 0, -1):  # Try 5 reasons and all the way down to 1
            reason = ", ".join(reasons[:reason_count])
            if len(reasons) > reason_count:
                reason += ", +{} more".format(len(reasons) - reason_count)
            reason = reason.capitalize()
            message = prefix_ms + s.format(reason)  # Insert reason list
            if len(message) <= 500:
                break  # Problem solved, stop attempting

        s = s.format(reason)  # Later code needs this variable
        if len(message) > 500:
            message = (prefix_ms + s)[:500]  # Truncate directly and keep MS link

        without_roles = tuple(["no-" + reason for reason in reasons]) + ("site-no-" + post.post_site,)

        if set(reasons) - GlobalVars.experimental_reasons == set() and \
                not why.startswith("Post manually "):
            chatcommunicate.tell_rooms(message, ("experimental",),
                                       without_roles, notify_site=post.post_site, report_data=(post_url, poster_url))
        else:
            chatcommunicate.tell_rooms(message, ("all", "site-" + post.post_site),
                                       without_roles, notify_site=post.post_site, report_data=(post_url, poster_url))
    except Exception as e:
        excepthook.uncaught_exception(*sys.exc_info())
<|endoftext|>


<commit_before>"""
okeanidanalysis.lib
===================

Generally useful methods that span across submodules.

"""

import time, datetime, pytz, itertools
import numpy as np
import scipy as sp
import scipy.interpolate
import matplotlib as mpl
import matplotlib.patches

UNIX_EPOCH = datetime.datetime(1970, 1, 1, tzinfo=pytz.UTC)

golden_ratio = 1.61803398875

def utime(t, convention=None):
    """Convert time to mircoseconds since epoch.

    Expects time given as a datetime object.

    """
#    typet = type(t)
#    if typet is datetime.datetime: # t = t.timetuple()
#        return int(time.mktime(t.timetuple())*1e6 + t.microsecond)
#    else:
#        raise TypeError('time type {} not recognized'.format(typet))
#TODO something to handle matlab datenum convention easily
    return [ue * 1e6 for ue in python_datetime_to_unix_epoch(t)]

def python_datetime_to_unix_epoch(dto):
    if type(dto) is not list: dto = [dto] # TODO is there a more elegant way?
    try:
        return [(dt - UNIX_EPOCH).total_seconds for dt in dto]
    except TypeError: # can't subtract offset-naive and offset-aware datetimes
        dto = (pytz.UTC.localize(dt) for dt in dto)
        return [(dt - UNIX_EPOCH).total_seconds() for dt in dto]

def matlab_datenum_to_python_datetime(dn):
    day = (datetime.datetime.fromordinal(int(n)) for n in dn)
    frac = (datetime.timedelta(days=n%1) for n in dn)
    shift = datetime.timedelta(days = 366)
    return [d + f - shift for d, f in itertools.izip(day, frac)]

def injectlocals(l, skip=['self','args','kwargs'], **kwargs):
    """Update a dictionary with another, skipping specified keys."""
    if l.has_key('kwargs') : kwargs.update(l['kwargs'])
    kwargs.update(dict((k, v) for k, v in l.items() if k not in skip))
    return kwargs

def make_multiplier(n): return lambda x: x*n

def make_divider(n): return lambda x: x/n

def indexclip(s, e, *a): return [b[int(s):int(e)] for b in a]

def angle_difference(a, b, degrees=False):
    if degrees: p = 180.0
    else: p = np.pi
    return np.mod(np.unwrap(a, p) - np.unwrap(b, p) + p, 2 * p) - p

def rad2deg360(x):
    """

    """
    d = np.rad2deg(x)
    d[d < 0] += 360
    return d

def rmnans(x, *a):
    """Remove values from array(s), using NaNs in key array.

    Examples
    --------
    >>> b = np.array([0,1,nan,3])
    >>> c = np.ones(b.shape)
    >>> bn, cn = rmnans(b,c)
    >>> bn
    array([ 0.,  1.,  3.])
    >>> cn
    array([ 1.,  1.,  1.])

    """
    mask = np.isnan(x)
    y = [x[~mask]]
    for b in a: y.append(b[~mask])
    return y

def plot_date_blocks(t, v, axes, colormap=None, unit_height=False, **kw):
    #TODO logic to check edges & heights for sanity
#    if kw.has_key('ec') or kw.has_key('edgecolor'):
#        pass
#    else:
#        kw.update(dict(edgecolor='red'))
    d = np.diff(v)
    left = np.hstack((t[0],t[d.nonzero()]))
    right = np.hstack((t[d.nonzero()],mpl.dates.num2date(axes.get_xlim()[-1])))
    height = np.hstack((v[d.nonzero()], v[-1]))
    print height
    norm_height = height / np.float(np.max(height))
    for l, r, h, nh  in itertools.izip(left, right, height, norm_height):
        # draw a rectangle with the appropriate color
        if colormap:
            kw.update(dict(color = colormap(nh)))
        if unit_height:
            h = 1 # do this _after_ colormap
        ll = (mpl.dates.date2num(l),0)
        w = mpl.dates.date2num(r) - mpl.dates.date2num(l)
        axes.add_patch(mpl.patches.Rectangle(ll, w, h, **kw))
    pt = np.vstack((left, right))
    pv = np.vstack((height, height))
    print pt.shape, pv.shape
    axes.plot_date(pt.ravel('F'), pv.ravel('F'), 'k-')

# TODO modify to use PatchCollection and set colors that way
# http://matplotlib.org/examples/api/patch_collection.html

def slant_range_and_depths_to_horizontal_range(r, d1, d2):
    """
    Compute the radius of a circle on a sphere (half-chord on a circle)
    using the Pythagorean theorem, the sphere radius, and the apothem (in
    this case, the difference in depths).

    Sphere radius must be greater than difference in depths.

    Examples
    --------
    >>> depth_corrected_range(10, 5, 9)
    # TODO
    """
    return (r**2 - (d1 - d2)**2) **0.5

def loadmtx(filename):
    """Read matrix from `.mtx` file

    The first two elements in a `.mtx` file are matrix dimensions encoded as
    32-bit integers, and the rest of the file is a column major matrix of
    single precision (i.e., 32-bit float).

    Note that the endianness may be different depending on platform, so
    `.mtx` files may not be portable. This method is primarily intended to
    handle files sent to and from vehicles in the field over low-badwidth
    communication channels (e.g., Iridium SBD), and for development on
    techniques that use those files. Please use HDF5, NetCDF, MATLAB, or
    numpy.save methods for any other use cases.

    Parameters
    ----------
    filename : str
        The file to load the matrix from.

    Returns
    -------
    a : array
        A numpy array containing the matrix saved in the file.

    Examples
    --------
    TODO

    See also: savemtx, numpy.fromfile, numpy.save, numpy.savez

    """
    with open(filename, 'rb') as f:
        shape = np.fromfile(file=f, dtype=np.int32, count=2)[::-1]
        return np.fromfile(file=f, dtype=np.float32).reshape(shape).T

def savemtx(a, filename):
    """Save array as a matrix in a `.mtx` file

    The first two elements in a `.mtx` file are matrix dimensions encoded as
    32-bit integers, and the rest of the file is a column major matrix of
    single precision (i.e., 32-bit float).

    Note that the endianness may be different depending on platform, so
    `.mtx` files may not be portable. This method is primarily intended to
    handle files sent to and from vehicles in the field over low-badwidth
    communication channels (e.g., Iridium SBD), and for development on
    techniques that use those files. Please use HDF5, NetCDF, MATLAB, or
    numpy.save methods for any other use cases.

    Parameters
    ----------
    a : array-like
        The array to be saved.
    filename : str
        The file to load the matrix from.

    Examples
    --------
    TODO

    See also: loadmtx, numpy.fromfile, numpy.save, numpy.savez

    """
    with open(filename, 'wb') as f:
        f.write(np.array(a.shape, dtype=np.int32).tostring(order='F'))
        f.write(a.astype(np.float32).tostring(order='F'))

<commit_msg>flip loadmtx dimensions for consistency with SF's matlab routine fort_read<commit_after>"""
okeanidanalysis.lib
===================

Generally useful methods that span across submodules.

"""

import time, datetime, pytz, itertools
import numpy as np
import scipy as sp
import scipy.interpolate
import matplotlib as mpl
import matplotlib.patches

UNIX_EPOCH = datetime.datetime(1970, 1, 1, tzinfo=pytz.UTC)

golden_ratio = 1.61803398875

def utime(t, convention=None):
    """Convert time to mircoseconds since epoch.

    Expects time given as a datetime object.

    """
#    typet = type(t)
#    if typet is datetime.datetime: # t = t.timetuple()
#        return int(time.mktime(t.timetuple())*1e6 + t.microsecond)
#    else:
#        raise TypeError('time type {} not recognized'.format(typet))
#TODO something to handle matlab datenum convention easily
    return [ue * 1e6 for ue in python_datetime_to_unix_epoch(t)]

def python_datetime_to_unix_epoch(dto):
    if type(dto) is not list: dto = [dto] # TODO is there a more elegant way?
    try:
        return [(dt - UNIX_EPOCH).total_seconds for dt in dto]
    except TypeError: # can't subtract offset-naive and offset-aware datetimes
        dto = (pytz.UTC.localize(dt) for dt in dto)
        return [(dt - UNIX_EPOCH).total_seconds() for dt in dto]

def matlab_datenum_to_python_datetime(dn):
    day = (datetime.datetime.fromordinal(int(n)) for n in dn)
    frac = (datetime.timedelta(days=n%1) for n in dn)
    shift = datetime.timedelta(days = 366)
    return [d + f - shift for d, f in itertools.izip(day, frac)]

def injectlocals(l, skip=['self','args','kwargs'], **kwargs):
    """Update a dictionary with another, skipping specified keys."""
    if l.has_key('kwargs') : kwargs.update(l['kwargs'])
    kwargs.update(dict((k, v) for k, v in l.items() if k not in skip))
    return kwargs

def make_multiplier(n): return lambda x: x*n

def make_divider(n): return lambda x: x/n

def indexclip(s, e, *a): return [b[int(s):int(e)] for b in a]

def angle_difference(a, b, degrees=False):
    if degrees: p = 180.0
    else: p = np.pi
    return np.mod(np.unwrap(a, p) - np.unwrap(b, p) + p, 2 * p) - p

def rad2deg360(x):
    """

    """
    d = np.rad2deg(x)
    d[d < 0] += 360
    return d

def rmnans(x, *a):
    """Remove values from array(s), using NaNs in key array.

    Examples
    --------
    >>> b = np.array([0,1,nan,3])
    >>> c = np.ones(b.shape)
    >>> bn, cn = rmnans(b,c)
    >>> bn
    array([ 0.,  1.,  3.])
    >>> cn
    array([ 1.,  1.,  1.])

    """
    mask = np.isnan(x)
    y = [x[~mask]]
    for b in a: y.append(b[~mask])
    return y

def plot_date_blocks(t, v, axes, colormap=None, unit_height=False, **kw):
    #TODO logic to check edges & heights for sanity
#    if kw.has_key('ec') or kw.has_key('edgecolor'):
#        pass
#    else:
#        kw.update(dict(edgecolor='red'))
    d = np.diff(v)
    left = np.hstack((t[0],t[d.nonzero()]))
    right = np.hstack((t[d.nonzero()],mpl.dates.num2date(axes.get_xlim()[-1])))
    height = np.hstack((v[d.nonzero()], v[-1]))
    print height
    norm_height = height / np.float(np.max(height))
    for l, r, h, nh  in itertools.izip(left, right, height, norm_height):
        # draw a rectangle with the appropriate color
        if colormap:
            kw.update(dict(color = colormap(nh)))
        if unit_height:
            h = 1 # do this _after_ colormap
        ll = (mpl.dates.date2num(l),0)
        w = mpl.dates.date2num(r) - mpl.dates.date2num(l)
        axes.add_patch(mpl.patches.Rectangle(ll, w, h, **kw))
    pt = np.vstack((left, right))
    pv = np.vstack((height, height))
    print pt.shape, pv.shape
    axes.plot_date(pt.ravel('F'), pv.ravel('F'), 'k-')

# TODO modify to use PatchCollection and set colors that way
# http://matplotlib.org/examples/api/patch_collection.html

def slant_range_and_depths_to_horizontal_range(r, d1, d2):
    """
    Compute the radius of a circle on a sphere (half-chord on a circle)
    using the Pythagorean theorem, the sphere radius, and the apothem (in
    this case, the difference in depths).

    Sphere radius must be greater than difference in depths.

    Examples
    --------
    >>> depth_corrected_range(10, 5, 9)
    # TODO
    """
    return (r**2 - (d1 - d2)**2) **0.5

def loadmtx(filename):
    """Read matrix from `.mtx` file

    The first two elements in a `.mtx` file are matrix dimensions encoded as
    32-bit integers, and the rest of the file is a column major matrix of
    single precision (i.e., 32-bit float).

    Note that the endianness may be different depending on platform, so
    `.mtx` files may not be portable. This method is primarily intended to
    handle files sent to and from vehicles in the field over low-badwidth
    communication channels (e.g., Iridium SBD), and for development on
    techniques that use those files. Please use HDF5, NetCDF, MATLAB, or
    numpy.save methods for any other use cases.

    Parameters
    ----------
    filename : str
        The file to load the matrix from.

    Returns
    -------
    a : array
        A numpy array containing the matrix saved in the file.

    Examples
    --------
    TODO

    See also: savemtx, numpy.fromfile, numpy.save, numpy.savez

    """
    with open(filename, 'rb') as f:
        shape = np.fromfile(file=f, dtype=np.int32, count=2)[::-1]
        # not sure why the .mtx file has its dimensions flipped, but this
        # reads consistent with SF's fort_read & fort_readColumns
        return np.fromfile(file=f, dtype=np.float32).reshape(shape, order='F')

def savemtx(a, filename):
    """Save array as a matrix in a `.mtx` file

    The first two elements in a `.mtx` file are matrix dimensions encoded as
    32-bit integers, and the rest of the file is a column major matrix of
    single precision (i.e., 32-bit float).

    Note that the endianness may be different depending on platform, so
    `.mtx` files may not be portable. This method is primarily intended to
    handle files sent to and from vehicles in the field over low-badwidth
    communication channels (e.g., Iridium SBD), and for development on
    techniques that use those files. Please use HDF5, NetCDF, MATLAB, or
    numpy.save methods for any other use cases.

    Parameters
    ----------
    a : array-like
        The array to be saved.
    filename : str
        The file to load the matrix from.

    Examples
    --------
    TODO

    See also: loadmtx, numpy.fromfile, numpy.save, numpy.savez

    """
    with open(filename, 'wb') as f:
        f.write(np.array(a.shape, dtype=np.int32).tostring(order='F'))
        f.write(a.astype(np.float32).tostring(order='F'))

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from django.utils.translation import ugettext
from shop import settings
from shop.rest.serializers import ExtraCartRow
from shop.cart.modifiers.base import BaseCartModifier

class CartIncludeTaxModifier(BaseCartModifier):
    """
    This tax calculator presumes that unit prices are net prices, hence also the subtotal,
    and that the tax is added globally to the carts total.
    By placing this modifier before the shipping modifiers, one can add tax to
    the shipping costs. Otherwise shipping cost are considered tax free.
    """
    taxes = settings.VALUE_ADDED_TAX / 100

    def add_extra_cart_row(self, cart, request):
        """
        Add a field on cart.extra_price_fields:
        """
        amount = cart.subtotal * self.taxes
        instance = {
            'label': ugettext("+ {}% V.A.T").format(settings.VALUE_ADDED_TAX),
            'amount': amount,
        }
        cart.extra_rows[self.identifier] = ExtraCartRow(instance)
        cart.total += amount

class CartExcludedTaxModifier(BaseCartModifier):
    """
    This tax calculator presumes that unit prices are gross prices, hence also the subtotal,
    and that the tax is identified per cart but not added.
    """
    taxes = 1 - 1 / (1 + settings.VALUE_ADDED_TAX / 100)

    def add_extra_cart_row(self, cart, request):
        """
        Add a field on cart.extra_price_fields:
        """
        amount = cart.subtotal * self.taxes
        instance = {
            'label': ugettext("{}% V.A.T incl.").format(settings.VALUE_ADDED_TAX),
            'amount': amount,
        }
        cart.extra_rows[self.identifier] = ExtraCartRow(instance)
<commit_msg>Use underscore for translating<commit_after># -*- coding: utf-8 -*-
from django.utils.translation import ugettext as _
from shop import settings
from shop.rest.serializers import ExtraCartRow
from shop.cart.modifiers.base import BaseCartModifier

class CartIncludeTaxModifier(BaseCartModifier):
    """
    This tax calculator presumes that unit prices are net prices, hence also the subtotal,
    and that the tax is added globally to the carts total.
    By placing this modifier before the shipping modifiers, one can add tax to
    the shipping costs. Otherwise shipping cost are considered tax free.
    """
    taxes = settings.VALUE_ADDED_TAX / 100

    def add_extra_cart_row(self, cart, request):
        """
        Add a field on cart.extra_price_fields:
        """
        amount = cart.subtotal * self.taxes
        instance = {
            'label': _("+ {}% V.A.T").format(settings.VALUE_ADDED_TAX),
            'amount': amount,
        }
        cart.extra_rows[self.identifier] = ExtraCartRow(instance)
        cart.total += amount

class CartExcludedTaxModifier(BaseCartModifier):
    """
    This tax calculator presumes that unit prices are gross prices, hence also the subtotal,
    and that the tax is identified per cart but not added.
    """
    taxes = 1 - 1 / (1 + settings.VALUE_ADDED_TAX / 100)

    def add_extra_cart_row(self, cart, request):
        """
        Add a field on cart.extra_price_fields:
        """
        amount = cart.subtotal * self.taxes
        instance = {
            'label': _("{}% V.A.T incl.").format(settings.VALUE_ADDED_TAX),
            'amount': amount,
        }
        cart.extra_rows[self.identifier] = ExtraCartRow(instance)
<|endoftext|>


<commit_before>from django.conf import settings

from appconf import AppConf

class Open511Settings(AppConf):

    ENABLE_TEST_ENDPOINT = False
    DEFAULT_VERSION = 'v0'

    class Meta:
        prefix = 'OPEN511'
<commit_msg>Update version string<commit_after>from django.conf import settings

from appconf import AppConf

class Open511Settings(AppConf):

    ENABLE_TEST_ENDPOINT = False
    DEFAULT_VERSION = 'v1'

    class Meta:
        prefix = 'OPEN511'
<|endoftext|>


<commit_before># Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import os

from spack import *

class AwsParallelcluster(PythonPackage):
    """AWS ParallelCluster is an AWS supported Open Source cluster management
    tool to deploy and manage HPC clusters in the AWS cloud."""

    homepage = "https://github.com/aws/aws-parallelcluster"
    pypi = "aws-parallelcluster/aws-parallelcluster-2.11.3.tar.gz"

    maintainers = [
        'demartinofra', 'enrico-usai', 'lukeseawalker', 'tilne'
    ]

    version('2.11.3', sha256='7c1d74123f2f670846aed8fe1fcca5908bb46ec014e2dfc7d3ec8994447a37a0')
    version('2.11.2', sha256='60d96a5ea4dca4816ceffc4546549743abd1f6207c62f016c9c348adc64b2ec0')
    version('2.11.1', sha256='dc102eeb0db30fb183c1ee076e340223095fde7d9079004fde401b7ad658242d')
    version('2.11.0', sha256='ec2f54f752a355649ca065a41fa4ac19697f570a136e10e0a6548f5abad3ea8a')
    version('2.10.4', sha256='487dbf3e79f3e9b6750b1075cef4eb24956f7b310e5bd8de662b98d87491736c')
    version('2.10.3', sha256='02016358932375c1abb566c6b7f1c891fa617fc4a4ec4ce9969f87758d4039c7')
    version('2.10.2', sha256='95853535b82e8087c6635a946556ed886e68d3a77d67a375830c57d80f2bbd6e')
    version('2.10.1', sha256='b3d2ea836c08c9be1667d55a8999aae412d7c2b20f958ca5842e8fa440eb24e0')
    version('2.10.0', sha256='a7a27871b4f54cb913b0c1233e675131e9b2099549af0840d32c36b7e91b104b')
    version('2.9.1', sha256='12dc22286cd447a16931f1f8619bdd47d4543fd0de7905d52b6c6f83ff9db8a3')
    version('2.9.0', sha256='e98a8426bc46aca0860d9a2be89bbc4a90aab3ed2f60ca6c385b595fbbe79a78')
    version('2.8.1', sha256='c183dc3f053bc2445db724e561cea7f633dd5e7d467a7b3f9b2f2f703f7d5d49')
    version('2.8.0', sha256='4e67539d49fe987884a3ed7198dc13bc8a3a1778f0b3656dfe0ae899138678f2')
    version('2.7.0', sha256='7c34995acfcc256a6996541d330575fc711e1fd5735bf3d734d4e96c1dc8df60')
    version('2.6.1', sha256='2ce9015d90b5d4dc88b46a44cb8a82e8fb0bb2b4cca30335fc5759202ec1b343')
    version('2.6.0', sha256='aaed6962cf5027206834ac24b3d312da91e0f96ae8607f555e12cb124b869f0c')
    version('2.5.1', sha256='4fd6e14583f8cf81f9e4aa1d6188e3708d3d14e6ae252de0a94caaf58be76303')
    version('2.5.0', sha256='3b0209342ea0d9d8cc95505456103ad87c2d4e35771aa838765918194efd0ad3')

    depends_on('python@3.6:', when='@2.11:', type=('build', 'run'))
    depends_on('python@2.7:2.8,3.4:', when='@:2.10', type=('build', 'run'))

    depends_on('py-future@0.16.0:0.18.2', when='@:2.10', type=('build', 'run'))

    depends_on('py-ipaddress@1.0.22:', type=('build', 'run'))

    depends_on('py-configparser@3.5.0:3.8.1', when='^python@:2', type=('build', 'run'))

    depends_on('py-tabulate@0.8.2:0.8.9', when='@2.11:', type=('build', 'run'))
    depends_on('py-tabulate@0.8.2:0.8.3', when='@:2.8', type=('build', 'run'))
    depends_on('py-tabulate@0.8.5', when='@2.9: ^python@3.0:3.4', type=('build', 'run'))
    depends_on('py-tabulate@0.8.2:0.8.7', when='@2.9: ^python@:2,3.5:', type=('build', 'run'))

    depends_on('py-pyyaml@5.2', when='@2.6:2.8 ^python@3.0:3.4', type=('build', 'run'))
    depends_on('py-pyyaml@5.3.1:', when='@2.9: ^python@:2,3.5:', type=('build', 'run'))

    depends_on('py-jinja2@2.10.1', when='@2.9: ^python@3.0:3.4', type=('build', 'run'))
    depends_on('py-jinja2@2.11.0:', when='@2.9: ^python@:2,3.5:', type=('build', 'run'))

    depends_on('py-boto3@1.16.14:', when='@2.10:', type=('build', 'run'))
    depends_on('py-boto3@1.14.3:', when='@2.8:2.9', type=('build', 'run'))
    depends_on('py-boto3@1.10.15:', when='@:2.7', type=('build', 'run'))

    depends_on('py-setuptools', when='@2.6:', type=('build', 'run'))

    depends_on('py-enum34@1.1.6:', when='^python@:3.3', type=('build', 'run'))

    depends_on('py-pyyaml@5.1.2', when='@2.6: ^python@:2,3.5:', type=('build', 'run'))
    depends_on('py-pyyaml@5.1.2:', when='@:2.5', type=('build', 'run'))

    # https://github.com/aws/aws-parallelcluster/pull/1633
    patch('enum34.patch', when='@:2.5.1')

    @run_after('install')
    @on_package_attributes(run_tests=True)
    def install_test(self):
        # Make sure executables work
        for exe in ['awsbhosts', 'awsbkill', 'awsbout', 'awsbqueues',
                    'awsbstat', 'awsbsub', 'pcluster']:
            exe = Executable(os.path.join(self.prefix.bin, exe))
            exe('--help')
<commit_msg>aws-parallelcluster: add v2.11.4 (#28094)<commit_after># Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import os

from spack import *

class AwsParallelcluster(PythonPackage):
    """AWS ParallelCluster is an AWS supported Open Source cluster management
    tool to deploy and manage HPC clusters in the AWS cloud."""

    homepage = "https://github.com/aws/aws-parallelcluster"
    pypi = "aws-parallelcluster/aws-parallelcluster-2.11.4.tar.gz"

    maintainers = [
        'demartinofra', 'enrico-usai', 'lukeseawalker',
    ]

    version('2.11.4', sha256='449537ccda57f91f4ec6ae0c94a8e2b1a789f08f80245fadb28f44a4351d5da4')
    version('2.11.3', sha256='7c1d74123f2f670846aed8fe1fcca5908bb46ec014e2dfc7d3ec8994447a37a0')
    version('2.11.2', sha256='60d96a5ea4dca4816ceffc4546549743abd1f6207c62f016c9c348adc64b2ec0')
    version('2.11.1', sha256='dc102eeb0db30fb183c1ee076e340223095fde7d9079004fde401b7ad658242d')
    version('2.11.0', sha256='ec2f54f752a355649ca065a41fa4ac19697f570a136e10e0a6548f5abad3ea8a')
    version('2.10.4', sha256='487dbf3e79f3e9b6750b1075cef4eb24956f7b310e5bd8de662b98d87491736c')
    version('2.10.3', sha256='02016358932375c1abb566c6b7f1c891fa617fc4a4ec4ce9969f87758d4039c7')
    version('2.10.2', sha256='95853535b82e8087c6635a946556ed886e68d3a77d67a375830c57d80f2bbd6e')
    version('2.10.1', sha256='b3d2ea836c08c9be1667d55a8999aae412d7c2b20f958ca5842e8fa440eb24e0')
    version('2.10.0', sha256='a7a27871b4f54cb913b0c1233e675131e9b2099549af0840d32c36b7e91b104b')
    version('2.9.1', sha256='12dc22286cd447a16931f1f8619bdd47d4543fd0de7905d52b6c6f83ff9db8a3')
    version('2.9.0', sha256='e98a8426bc46aca0860d9a2be89bbc4a90aab3ed2f60ca6c385b595fbbe79a78')
    version('2.8.1', sha256='c183dc3f053bc2445db724e561cea7f633dd5e7d467a7b3f9b2f2f703f7d5d49')
    version('2.8.0', sha256='4e67539d49fe987884a3ed7198dc13bc8a3a1778f0b3656dfe0ae899138678f2')
    version('2.7.0', sha256='7c34995acfcc256a6996541d330575fc711e1fd5735bf3d734d4e96c1dc8df60')
    version('2.6.1', sha256='2ce9015d90b5d4dc88b46a44cb8a82e8fb0bb2b4cca30335fc5759202ec1b343')
    version('2.6.0', sha256='aaed6962cf5027206834ac24b3d312da91e0f96ae8607f555e12cb124b869f0c')
    version('2.5.1', sha256='4fd6e14583f8cf81f9e4aa1d6188e3708d3d14e6ae252de0a94caaf58be76303')
    version('2.5.0', sha256='3b0209342ea0d9d8cc95505456103ad87c2d4e35771aa838765918194efd0ad3')

    depends_on('python@3.6:', when='@2.11:', type=('build', 'run'))
    depends_on('python@2.7:2.8,3.4:', when='@:2.10', type=('build', 'run'))

    depends_on('py-future@0.16.0:0.18.2', when='@:2.10', type=('build', 'run'))

    depends_on('py-ipaddress@1.0.22:', type=('build', 'run'))

    depends_on('py-configparser@3.5.0:3.8.1', when='^python@:2', type=('build', 'run'))

    depends_on('py-tabulate@0.8.2:0.8.9', when='@2.11:', type=('build', 'run'))
    depends_on('py-tabulate@0.8.2:0.8.3', when='@:2.8', type=('build', 'run'))
    depends_on('py-tabulate@0.8.5', when='@2.9: ^python@3.0:3.4', type=('build', 'run'))
    depends_on('py-tabulate@0.8.2:0.8.7', when='@2.9: ^python@:2,3.5:', type=('build', 'run'))

    depends_on('py-pyyaml@5.2', when='@2.6:2.8 ^python@3.0:3.4', type=('build', 'run'))
    depends_on('py-pyyaml@5.3.1:', when='@2.9: ^python@:2,3.5:', type=('build', 'run'))

    depends_on('py-jinja2@2.10.1', when='@2.9: ^python@3.0:3.4', type=('build', 'run'))
    depends_on('py-jinja2@2.11.0:', when='@2.9: ^python@:2,3.5:', type=('build', 'run'))

    depends_on('py-boto3@1.16.14:', when='@2.10:', type=('build', 'run'))
    depends_on('py-boto3@1.14.3:', when='@2.8:2.9', type=('build', 'run'))
    depends_on('py-boto3@1.10.15:', when='@:2.7', type=('build', 'run'))

    depends_on('py-setuptools', when='@2.6:', type=('build', 'run'))

    depends_on('py-enum34@1.1.6:', when='^python@:3.3', type=('build', 'run'))

    depends_on('py-pyyaml@5.1.2', when='@2.6: ^python@:2,3.5:', type=('build', 'run'))
    depends_on('py-pyyaml@5.1.2:', when='@:2.5', type=('build', 'run'))

    # https://github.com/aws/aws-parallelcluster/pull/1633
    patch('enum34.patch', when='@:2.5.1')

    @run_after('install')
    @on_package_attributes(run_tests=True)
    def install_test(self):
        # Make sure executables work
        for exe in ['awsbhosts', 'awsbkill', 'awsbout', 'awsbqueues',
                    'awsbstat', 'awsbsub', 'pcluster']:
            exe = Executable(os.path.join(self.prefix.bin, exe))
            exe('--help')
<|endoftext|>