<commit_before>"""
Decorator utility for function decorators.

This is intented to be a utility for signature preserving function
decorators.

"""
__all__ = [
    'getfullargspec',
    'decompile_argspec',
    'param_names',
    'Scope',
    'wraps',
    'function_decorator',
]

import functools

try:
    from inspect import getfullargspec
except ImportError:
    from inspect import getargspec
    from collections import namedtuple

    # replacement for getfullargspec (missing in python2)
    fullargspec = namedtuple(
            'FullArgSpec', [
                'args', 'varargs',
                'varkw', 'defaults',
                'kwonlyargs', 'kwonlydefaults',
                'annotations'])
    def getfullargspec(fn):
        spec = getargspec(fn)
        return fullargspec(
            args=spec.args, varargs=spec.varargs,
            varkw=spec.keywords, defaults=spec.defaults,
            kwonlyargs=[], kwonlydefaults=None,
            annotations={})

def decompile_argspec(argspec, defparam_name):
    """
    Decompile a function argspec as returned by inspect.getfullargspec().

    Returns a tuple (declaration, forward, context), where

        - declaration: parameter list as found in the function definition
        - forward: values of the parameters for forwarding to this function
        - context: dictionary of default parameters used in the declaration

    Example:

    >>> spec = getfullargspec(lambda a, b=1, *args, **kwargs: None)
    >>> decompile_argspec(spec, '_')
    ('a, b=_[0], *args, **kwargs', 'a, b, *args, **kwargs', {'_': [1]})

    >>> spec = getfullargspec(lambda a, b: None)
    >>> decompile_argspec(spec, '_p')
    ('a, b', 'a, b', {'_p': []})

    """
    ctx = { defparam_name: [] }
    decl_params = []
    call_params = []

    # Add a value to the context
    def set_value(value):
        ctx[defparam_name].append(value)
        return '%s[%d]' % (defparam_name, len(ctx[defparam_name]) - 1)

    # Add parameter
    def add_param(decl, call):
        decl_params.append(decl)
        call_params.append(call)

    # start by adding the positional arguments without defaults:
    defaults = argspec.defaults or []
    num_required = len(argspec.args) - len(defaults)
    decl_params += argspec.args[:num_required]
    call_params += argspec.args[:num_required]

    # positional arguments with defaults:
    for arg,val in zip(argspec.args[num_required:], defaults):
        add_param("%s=%s" % (arg, set_value(val)), arg)

    # add varargs:
    if argspec.varargs:
        add_param('*'+argspec.varargs, '*'+argspec.varargs)
    # need this if no varargs are given but keywordonly args are present:
    elif argspec.kwonlyargs:
        decl_params.append('*')

    # now add keyword-only arguments (this is specific to python3)
    for kwonlyarg in argspec.kwonlyargs:
        if kwonlyarg in argspec.kwonlydefaults:
            val = argspec.kwonlydefaults[kwonlyarg]
            add_param("%s=%s" % (kwonlyarg, set_value(val)),
                      "%s=%s" % (kwonlyarg, kwonlyarg))
        else:
            add_param(kwonlyarg,
                      "%s=%s" % (kwonlyarg, kwonlyarg))

    # and finally the keyword arguments
    if argspec.varkw is not None:
        add_param('**'+argspec.varkw, '**'+argspec.varkw)

    # concatenate and
    decl = ", ".join(decl_params)
    call = ", ".join(call_params)
    return decl, call, ctx

def param_names(argspec):
    """
    Iterate over all parameter names used in the argspec.
    """
    for argname in argspec.args:
        yield argname
    if argspec.varargs:
        yield argspec.varargs
    if argspec.kwonlyargs:
        for argname in argspec.kwonlyargs:
            yield argname
    if argspec.varkw:
        for argname in argspec.varkw:
            yield argname

class Scope:
    """
    Keeps track of used names in a particular scope.
    """
    def __init__(self, iterable):
        self.names = [iterable]

    """
    Generate a new name that is not present in the scope.
    """
    def new(self):
        name = '_'
        while name in self.names:
            name += '_'
        self.names.append(name)
        return name

def wraps(function, wrapper=None):
    """
    Wrap a function and copy its signature.

    >>> def real(a, b=1, *args, **kwargs):
    ...     return "%r %r %r %r" % (a, b, args, kwargs)

    >>> @wraps(real)
    ... def fake(*args, **kwargs):
    ...     return "Fake: " + real(*args, **kwargs)

    >>> assert getfullargspec(fake) == getfullargspec(real)

    >>> def check_fake(real, fake, *args, **kwargs):
    ...     rreal = real(*args, **kwargs)
    ...     rfake = fake(*args, **kwargs)
    ...     assert rfake == "Fake: " + rreal, '%r != %r' % (rreal, rfake)

    >>> check_fake(real, fake, 1, 2, 3, 4, kwonly=5, kwarg=6)

    >>> x = [3]
    >>> def real(first = x): return first
    >>> fake = wraps(real, lambda first: real(first))
    >>> assert fake() is real()

    """
    # defer creation of the actual function wrapper until called again
    # (this is for use as a decorator)
    if wrapper is None:
        return lambda wrapper: wraps(function, wrapper)

    # generate signature and call expressions
    argspec = getfullargspec(function)
    scope = Scope(param_names(argspec))
    sig, call, ctx = decompile_argspec(argspec, scope.new())

    # add the wrapped function to the scope
    wrapper_name = scope.new()
    ctx[wrapper_name] = wrapper

    # generate and evaluate the complete expression
    never_do_this_at_home = 'lambda %s: %s(%s)' % (sig, wrapper_name, call)
    it_is_evil = eval(never_do_this_at_home, ctx)

    if hasattr(function, '__annotations__'):
        it_is_evil.__annotations__ = function.__annotations__

    # call update wrapper to copy standard attributes
    return functools.update_wrapper(it_is_evil, function)

def function_decorator(decorator):
    """
    Decorator for signature preserving function decorators.

    >>> def mul(first, second = 1):
    ...     return first * second

    >>> @function_decorator
    ... def plus_one(fn):
    ...     return lambda *args, **kwargs: 1 + fn(*args, **kwargs)

    >>> fake = plus_one(mul)
    >>> assert fake(2, 3) == 7
    >>> assert fake(4) == 5

    """
    def decorate(function):
        return wraps(function, decorator(function))

    if hasattr(function, '__annotations__'):
        decorate.__annotations__ = function.__annotations__

    return functools.update_wrapper(decorate, decorator)

<commit_msg>Fix copy-paste error<commit_after>"""
Decorator utility for function decorators.

This is intented to be a utility for signature preserving function
decorators.

"""
__all__ = [
    'getfullargspec',
    'decompile_argspec',
    'param_names',
    'Scope',
    'wraps',
    'function_decorator',
]

import functools

try:
    from inspect import getfullargspec
except ImportError:
    from inspect import getargspec
    from collections import namedtuple

    # replacement for getfullargspec (missing in python2)
    fullargspec = namedtuple(
            'FullArgSpec', [
                'args', 'varargs',
                'varkw', 'defaults',
                'kwonlyargs', 'kwonlydefaults',
                'annotations'])
    def getfullargspec(fn):
        spec = getargspec(fn)
        return fullargspec(
            args=spec.args, varargs=spec.varargs,
            varkw=spec.keywords, defaults=spec.defaults,
            kwonlyargs=[], kwonlydefaults=None,
            annotations={})

def decompile_argspec(argspec, defparam_name):
    """
    Decompile a function argspec as returned by inspect.getfullargspec().

    Returns a tuple (declaration, forward, context), where

        - declaration: parameter list as found in the function definition
        - forward: values of the parameters for forwarding to this function
        - context: dictionary of default parameters used in the declaration

    Example:

    >>> spec = getfullargspec(lambda a, b=1, *args, **kwargs: None)
    >>> decompile_argspec(spec, '_')
    ('a, b=_[0], *args, **kwargs', 'a, b, *args, **kwargs', {'_': [1]})

    >>> spec = getfullargspec(lambda a, b: None)
    >>> decompile_argspec(spec, '_p')
    ('a, b', 'a, b', {'_p': []})

    """
    ctx = { defparam_name: [] }
    decl_params = []
    call_params = []

    # Add a value to the context
    def set_value(value):
        ctx[defparam_name].append(value)
        return '%s[%d]' % (defparam_name, len(ctx[defparam_name]) - 1)

    # Add parameter
    def add_param(decl, call):
        decl_params.append(decl)
        call_params.append(call)

    # start by adding the positional arguments without defaults:
    defaults = argspec.defaults or []
    num_required = len(argspec.args) - len(defaults)
    decl_params += argspec.args[:num_required]
    call_params += argspec.args[:num_required]

    # positional arguments with defaults:
    for arg,val in zip(argspec.args[num_required:], defaults):
        add_param("%s=%s" % (arg, set_value(val)), arg)

    # add varargs:
    if argspec.varargs:
        add_param('*'+argspec.varargs, '*'+argspec.varargs)
    # need this if no varargs are given but keywordonly args are present:
    elif argspec.kwonlyargs:
        decl_params.append('*')

    # now add keyword-only arguments (this is specific to python3)
    for kwonlyarg in argspec.kwonlyargs:
        if kwonlyarg in argspec.kwonlydefaults:
            val = argspec.kwonlydefaults[kwonlyarg]
            add_param("%s=%s" % (kwonlyarg, set_value(val)),
                      "%s=%s" % (kwonlyarg, kwonlyarg))
        else:
            add_param(kwonlyarg,
                      "%s=%s" % (kwonlyarg, kwonlyarg))

    # and finally the keyword arguments
    if argspec.varkw is not None:
        add_param('**'+argspec.varkw, '**'+argspec.varkw)

    # concatenate and
    decl = ", ".join(decl_params)
    call = ", ".join(call_params)
    return decl, call, ctx

def param_names(argspec):
    """
    Iterate over all parameter names used in the argspec.
    """
    for argname in argspec.args:
        yield argname
    if argspec.varargs:
        yield argspec.varargs
    if argspec.kwonlyargs:
        for argname in argspec.kwonlyargs:
            yield argname
    if argspec.varkw:
        for argname in argspec.varkw:
            yield argname

class Scope:
    """
    Keeps track of used names in a particular scope.
    """
    def __init__(self, iterable):
        self.names = [iterable]

    """
    Generate a new name that is not present in the scope.
    """
    def new(self):
        name = '_'
        while name in self.names:
            name += '_'
        self.names.append(name)
        return name

def wraps(function, wrapper=None):
    """
    Wrap a function and copy its signature.

    >>> def real(a, b=1, *args, **kwargs):
    ...     return "%r %r %r %r" % (a, b, args, kwargs)

    >>> @wraps(real)
    ... def fake(*args, **kwargs):
    ...     return "Fake: " + real(*args, **kwargs)

    >>> assert getfullargspec(fake) == getfullargspec(real)

    >>> def check_fake(real, fake, *args, **kwargs):
    ...     rreal = real(*args, **kwargs)
    ...     rfake = fake(*args, **kwargs)
    ...     assert rfake == "Fake: " + rreal, '%r != %r' % (rreal, rfake)

    >>> check_fake(real, fake, 1, 2, 3, 4, kwonly=5, kwarg=6)

    >>> x = [3]
    >>> def real(first = x): return first
    >>> fake = wraps(real, lambda first: real(first))
    >>> assert fake() is real()

    """
    # defer creation of the actual function wrapper until called again
    # (this is for use as a decorator)
    if wrapper is None:
        return lambda wrapper: wraps(function, wrapper)

    # generate signature and call expressions
    argspec = getfullargspec(function)
    scope = Scope(param_names(argspec))
    sig, call, ctx = decompile_argspec(argspec, scope.new())

    # add the wrapped function to the scope
    wrapper_name = scope.new()
    ctx[wrapper_name] = wrapper

    # generate and evaluate the complete expression
    never_do_this_at_home = 'lambda %s: %s(%s)' % (sig, wrapper_name, call)
    it_is_evil = eval(never_do_this_at_home, ctx)

    if hasattr(function, '__annotations__'):
        it_is_evil.__annotations__ = function.__annotations__

    # call update wrapper to copy standard attributes
    return functools.update_wrapper(it_is_evil, function)

def function_decorator(decorator):
    """
    Decorator for signature preserving function decorators.

    >>> def mul(first, second = 1):
    ...     return first * second

    >>> @function_decorator
    ... def plus_one(fn):
    ...     return lambda *args, **kwargs: 1 + fn(*args, **kwargs)

    >>> fake = plus_one(mul)
    >>> assert fake(2, 3) == 7
    >>> assert fake(4) == 5

    """
    def decorate(function):
        return wraps(function, decorator(function))

    if hasattr(decorator, '__annotations__'):
        decorate.__annotations__ = decorator.__annotations__

    return functools.update_wrapper(decorate, decorator)

<|endoftext|>


<commit_before>import pytz
import requests

from django.db import models, transaction
from django.utils import timezone
from django.conf import settings

from elasticsearch_dsl.filter import Range
from rest_framework.exceptions import APIException

from djes.models import Indexable

from bulbs.content.filters import Published
from bulbs.content.models import Content, ContentManager
from bulbs.utils import vault

SODAHEAD_DATE_FORMAT = '%m/%d/%y %I:%M %p'

SODAHEAD_POLL_ENDPOINT = '{}/api/polls/{{}}/'.format(settings.SODAHEAD_BASE_URL)
SODAHEAD_POLLS_ENDPOINT = '{}/api/polls/'.format(settings.SODAHEAD_BASE_URL)
SODAHEAD_DELETE_POLL_ENDPOINT = '{}/api/polls/{{}}/?access_token={{}}'.format(
    settings.SODAHEAD_BASE_URL
)

BLANK_ANSWER = 'Intentionally blank'
DEFAULT_ANSWER_1 = 'default answer 1'
DEFAULT_ANSWER_2 = 'default answer 2'

"""
Sodahead API Integration Rationale

The sodahoad api implements a POST and a PUT endpoint.

In the payload to these endpoints, answers are keyed

answer_0n      => "Question 1 Text"
answer_0n+1    => "Question 2 Text"
answer_0n+...  => "Question 3 Text"

Or however many answers you would like.

Sodahead provides no way to re-order/delete these answers.

So everytime we create an answer, we want to increment
the associated answer key.

This is especially tricky, as if we have the following
answers:

In the context of a single Poll:

poll.models.Poll(pk=1)

poll.models.Answer(pk=1) => answer_01
poll.models.Answer(pk=2) => answer_02
poll.models.Answer(pk=3) => answer_03

And we delete Answer(pk=2)

The next answer needs to be

poll.models.Answer(pk=5) => answer_04

So we cannot merely add up the answers of a poll, we
have to keep track of how many answers we have already
created.

This is done by the field `last_answer_index` on the Poll model.

It is incremented by 1 every time an Answer is saved.
"""

class SodaheadResponseError(APIException):
    status_code = 503
    default_detail = "Third-party poll provider temporarily unavailable."

    def __init__(self, detail):
        self.detail = detail

class SodaheadResponseFailure(APIException):
    status_code = 400
    default_detail = "Error from third-party poll provider"

    def __init__(self, detail):
        self.detail = detail

def Closed():  # noqa
    end_date_params = {
        "lte": timezone.now(),
    }

    return Range(end_date=end_date_params)

class PollManager(ContentManager):
    def search(self, **kwargs):
        search_query = super(PollManager, self).search(**kwargs)

        if "active" in kwargs:
            search_query = search_query.filter(Published(before=timezone.now()))

        if "closed" in kwargs:
            search_query = search_query.filter(Closed())

        return search_query

class Poll(Content):
    search_objects = PollManager()

    question_text = models.TextField(blank=True, default="")
    sodahead_id = models.CharField(max_length=20, blank=True, default="")
    last_answer_index = models.IntegerField(default=0)
    end_date = models.DateTimeField(null=True, default=None)

    # This keeps Poll out of Content.search_objects
    class Mapping(Content.Mapping):
        class Meta():
            orphaned = True

    def get_sodahead_data(self):
        response = requests.get(SODAHEAD_POLL_ENDPOINT.format(self.sodahead_id))

        if not response.ok:
            raise SodaheadResponseError(response.text)
        else:
            return response.json()

    def sodahead_payload(self):
        payload = {
            'access_token': vault.read('sodahead/token')['value'],
            'name': self.title,
            'title': self.question_text,
        }

        if self.sodahead_id:
            payload['id'] = self.sodahead_id

        if self.published:
            activation_date = self.published.astimezone(pytz.utc)
            payload['activationDate'] = activation_date.strftime(SODAHEAD_DATE_FORMAT)
        else:
            payload['activationDate'] = None

        if self.end_date:
            end_date = self.end_date.astimezone(pytz.utc)
            payload['endDate'] = end_date.strftime(SODAHEAD_DATE_FORMAT)
        else:
            payload['endDate'] = ''

        for answer in self.answers.all():
            if answer.answer_text:
                payload[answer.sodahead_answer_id] = BLANK_ANSWER
            else:
                payload[answer.sodahead_answer_id] = answer.answer_text

        if 'answer_01' not in payload:
            payload['answer_01'] = DEFAULT_ANSWER_1

        if 'answer_02' not in payload:
            payload['answer_02'] = DEFAULT_ANSWER_2

        return payload

    def save(self, *args, **kwargs):
        if not self.sodahead_id:
            response = requests.post(SODAHEAD_POLLS_ENDPOINT, self.sodahead_payload())

            if response.ok:
                self.sodahead_id = response.json()['poll']['id']
            elif response.status_code > 499:
                raise SodaheadResponseError(response.text)
            elif response.status_code > 399:
                raise SodaheadResponseFailure(response.text)
        else:
            self.sync_sodahead()

        super(Poll, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        response = requests.delete(
            SODAHEAD_DELETE_POLL_ENDPOINT.format(
                self.sodahead_id,
                vault.read('sodahead/token')['value'],
            )
         )
        if response.status_code > 499:
            raise SodaheadResponseError(response.text)
        elif response.status_code > 399:
            raise SodaheadResponseFailure(response.text)
        super(Poll, self).delete(*args, **kwargs)

    def sync_sodahead(self):
        response = requests.post(
            SODAHEAD_POLL_ENDPOINT.format(self.sodahead_id),
            self.sodahead_payload()
        )

        if response.status_code > 499:
            raise SodaheadResponseError(response.json())
        elif response.status_code > 399:
            raise SodaheadResponseFailure(response.json())

class Answer(Indexable):
    poll = models.ForeignKey(
        Poll,
        on_delete=models.CASCADE,
        related_name='answers'
    )
    sodahead_answer_id = models.CharField(max_length=20, blank=True, default="")
    answer_text = models.TextField(blank=True, default="")

    def save(self, *args, **kwargs):
        # using transaction/select_for_update here because we don't want to
        # run this block in parallel. If we did, we would end up with answers
        # with the same value for `answer.sodahead_answer_id`.
        # This is because we are deriving sodahead_answer_id based on the count
        # of answers that have ever been created for an poll.
        #   (see top of file for in depth explainer)
        with transaction.atomic():
            poll = Poll.objects.select_for_update().get(pk=self.poll_id)
            if self.sodahead_answer_id:
                super(Answer, self).save(*args, **kwargs)
            else:
                # See Rationale at top of file for in depth explanation
                # of what is going on here. We have to track how many answers
                # have ever been created for a Poll.
                # A simple count will not suffice because we dont want to
                # re-use any answer keys.
                answer_index = poll.last_answer_index + 1
                self.sodahead_answer_id = 'answer_%02d' % (answer_index)
                super(Answer, self).save(*args, **kwargs)
                poll.last_answer_index += 1
                poll.save()
<commit_msg>change conditional to catch-all else:<commit_after>import pytz
import requests

from django.db import models, transaction
from django.utils import timezone
from django.conf import settings

from elasticsearch_dsl.filter import Range
from rest_framework.exceptions import APIException

from djes.models import Indexable

from bulbs.content.filters import Published
from bulbs.content.models import Content, ContentManager
from bulbs.utils import vault

SODAHEAD_DATE_FORMAT = '%m/%d/%y %I:%M %p'

SODAHEAD_POLL_ENDPOINT = '{}/api/polls/{{}}/'.format(settings.SODAHEAD_BASE_URL)
SODAHEAD_POLLS_ENDPOINT = '{}/api/polls/'.format(settings.SODAHEAD_BASE_URL)
SODAHEAD_DELETE_POLL_ENDPOINT = '{}/api/polls/{{}}/?access_token={{}}'.format(
    settings.SODAHEAD_BASE_URL
)

BLANK_ANSWER = 'Intentionally blank'
DEFAULT_ANSWER_1 = 'default answer 1'
DEFAULT_ANSWER_2 = 'default answer 2'

"""
Sodahead API Integration Rationale

The sodahoad api implements a POST and a PUT endpoint.

In the payload to these endpoints, answers are keyed

answer_0n      => "Question 1 Text"
answer_0n+1    => "Question 2 Text"
answer_0n+...  => "Question 3 Text"

Or however many answers you would like.

Sodahead provides no way to re-order/delete these answers.

So everytime we create an answer, we want to increment
the associated answer key.

This is especially tricky, as if we have the following
answers:

In the context of a single Poll:

poll.models.Poll(pk=1)

poll.models.Answer(pk=1) => answer_01
poll.models.Answer(pk=2) => answer_02
poll.models.Answer(pk=3) => answer_03

And we delete Answer(pk=2)

The next answer needs to be

poll.models.Answer(pk=5) => answer_04

So we cannot merely add up the answers of a poll, we
have to keep track of how many answers we have already
created.

This is done by the field `last_answer_index` on the Poll model.

It is incremented by 1 every time an Answer is saved.
"""

class SodaheadResponseError(APIException):
    status_code = 503
    default_detail = "Third-party poll provider temporarily unavailable."

    def __init__(self, detail):
        self.detail = detail

class SodaheadResponseFailure(APIException):
    status_code = 400
    default_detail = "Error from third-party poll provider"

    def __init__(self, detail):
        self.detail = detail

def Closed():  # noqa
    end_date_params = {
        "lte": timezone.now(),
    }

    return Range(end_date=end_date_params)

class PollManager(ContentManager):
    def search(self, **kwargs):
        search_query = super(PollManager, self).search(**kwargs)

        if "active" in kwargs:
            search_query = search_query.filter(Published(before=timezone.now()))

        if "closed" in kwargs:
            search_query = search_query.filter(Closed())

        return search_query

class Poll(Content):
    search_objects = PollManager()

    question_text = models.TextField(blank=True, default="")
    sodahead_id = models.CharField(max_length=20, blank=True, default="")
    last_answer_index = models.IntegerField(default=0)
    end_date = models.DateTimeField(null=True, default=None)

    # This keeps Poll out of Content.search_objects
    class Mapping(Content.Mapping):
        class Meta():
            orphaned = True

    def get_sodahead_data(self):
        response = requests.get(SODAHEAD_POLL_ENDPOINT.format(self.sodahead_id))

        if not response.ok:
            raise SodaheadResponseError(response.text)
        else:
            return response.json()

    def sodahead_payload(self):
        payload = {
            'access_token': vault.read('sodahead/token')['value'],
            'name': self.title,
            'title': self.question_text,
        }

        if self.sodahead_id:
            payload['id'] = self.sodahead_id

        if self.published:
            activation_date = self.published.astimezone(pytz.utc)
            payload['activationDate'] = activation_date.strftime(SODAHEAD_DATE_FORMAT)
        else:
            payload['activationDate'] = None

        if self.end_date:
            end_date = self.end_date.astimezone(pytz.utc)
            payload['endDate'] = end_date.strftime(SODAHEAD_DATE_FORMAT)
        else:
            payload['endDate'] = ''

        for answer in self.answers.all():
            if answer.answer_text:
                payload[answer.sodahead_answer_id] = BLANK_ANSWER
            else:
                payload[answer.sodahead_answer_id] = answer.answer_text

        if 'answer_01' not in payload:
            payload['answer_01'] = DEFAULT_ANSWER_1

        if 'answer_02' not in payload:
            payload['answer_02'] = DEFAULT_ANSWER_2

        return payload

    def save(self, *args, **kwargs):
        if not self.sodahead_id:
            response = requests.post(SODAHEAD_POLLS_ENDPOINT, self.sodahead_payload())

            if response.ok:
                self.sodahead_id = response.json()['poll']['id']
            elif response.status_code > 499:
                raise SodaheadResponseError(response.text)
            else:
                raise SodaheadResponseFailure(response.text)
        else:
            self.sync_sodahead()

        super(Poll, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        response = requests.delete(
            SODAHEAD_DELETE_POLL_ENDPOINT.format(
                self.sodahead_id,
                vault.read('sodahead/token')['value'],
            )
         )
        if response.status_code > 499:
            raise SodaheadResponseError(response.text)
        elif response.status_code > 399:
            raise SodaheadResponseFailure(response.text)
        super(Poll, self).delete(*args, **kwargs)

    def sync_sodahead(self):
        response = requests.post(
            SODAHEAD_POLL_ENDPOINT.format(self.sodahead_id),
            self.sodahead_payload()
        )

        if response.status_code > 499:
            raise SodaheadResponseError(response.json())
        elif response.status_code > 399:
            raise SodaheadResponseFailure(response.json())

class Answer(Indexable):
    poll = models.ForeignKey(
        Poll,
        on_delete=models.CASCADE,
        related_name='answers'
    )
    sodahead_answer_id = models.CharField(max_length=20, blank=True, default="")
    answer_text = models.TextField(blank=True, default="")

    def save(self, *args, **kwargs):
        # using transaction/select_for_update here because we don't want to
        # run this block in parallel. If we did, we would end up with answers
        # with the same value for `answer.sodahead_answer_id`.
        # This is because we are deriving sodahead_answer_id based on the count
        # of answers that have ever been created for an poll.
        #   (see top of file for in depth explainer)
        with transaction.atomic():
            poll = Poll.objects.select_for_update().get(pk=self.poll_id)
            if self.sodahead_answer_id:
                super(Answer, self).save(*args, **kwargs)
            else:
                # See Rationale at top of file for in depth explanation
                # of what is going on here. We have to track how many answers
                # have ever been created for a Poll.
                # A simple count will not suffice because we dont want to
                # re-use any answer keys.
                answer_index = poll.last_answer_index + 1
                self.sodahead_answer_id = 'answer_%02d' % (answer_index)
                super(Answer, self).save(*args, **kwargs)
                poll.last_answer_index += 1
                poll.save()
<|endoftext|>


<commit_before># Copyright 2019 The Meson development team

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file contains the base representation for import('modname')

import typing as T

from .. import build, mesonlib
from ..mesonlib import relpath, HoldableObject, MachineChoice
from ..interpreterbase.decorators import noKwargs, noPosargs

if T.TYPE_CHECKING:
    from ..interpreter import Interpreter
    from ..interpreter.interpreterobjects import MachineHolder
    from ..interpreterbase import TYPE_var, TYPE_kwargs
    from ..programs import ExternalProgram
    from ..wrap import WrapMode

class ModuleState:
    """Object passed to all module methods.

    This is a WIP API provided to modules, it should be extended to have everything
    needed so modules does not touch any other part of Meson internal APIs.
    """

    def __init__(self, interpreter: 'Interpreter') -> None:
        # Keep it private, it should be accessed only through methods.
        self._interpreter = interpreter

        self.source_root = interpreter.environment.get_source_dir()
        self.build_to_src = relpath(interpreter.environment.get_source_dir(),
                                    interpreter.environment.get_build_dir())
        self.subproject = interpreter.subproject
        self.subdir = interpreter.subdir
        self.current_lineno = interpreter.current_lineno
        self.environment = interpreter.environment
        self.project_name = interpreter.build.project_name
        self.project_version = interpreter.build.dep_manifest[interpreter.active_projectname].version
        # The backend object is under-used right now, but we will need it:
        # https://github.com/mesonbuild/meson/issues/1419
        self.backend = interpreter.backend
        self.targets = interpreter.build.targets
        self.data = interpreter.build.data
        self.headers = interpreter.build.get_headers()
        self.man = interpreter.build.get_man()
        self.global_args = interpreter.build.global_args.host
        self.project_args = interpreter.build.projects_args.host.get(interpreter.subproject, {})
        self.build_machine = T.cast('MachineHolder', interpreter.builtin['build_machine']).held_object
        self.host_machine = T.cast('MachineHolder', interpreter.builtin['host_machine']).held_object
        self.target_machine = T.cast('MachineHolder', interpreter.builtin['target_machine']).held_object
        self.current_node = interpreter.current_node

    def get_include_args(self, include_dirs: T.Iterable[T.Union[str, build.IncludeDirs]], prefix: str = '-I') -> T.List[str]:
        if not include_dirs:
            return []

        srcdir = self.environment.get_source_dir()
        builddir = self.environment.get_build_dir()

        dirs_str: T.List[str] = []
        for dirs in include_dirs:
            if isinstance(dirs, str):
                dirs_str += [f'{prefix}{dirs}']
            else:
                dirs_str.extend([f'{prefix}{i}' for i in dirs.to_string_list(srcdir, builddir)])
                dirs_str.extend([f'{prefix}{i}' for i in dirs.get_extra_build_dirs()])

        return dirs_str

    def find_program(self, prog: T.Union[str, T.List[str]], required: bool = True,
                     version_func: T.Optional[T.Callable[['ExternalProgram'], str]] = None,
                     wanted: T.Optional[str] = None, silent: bool = False,
                     for_machine: MachineChoice = MachineChoice.HOST) -> 'ExternalProgram':
        return self._interpreter.find_program_impl(prog, required=required, version_func=version_func,
                                                   wanted=wanted, silent=silent, for_machine=for_machine)

    def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'ExternalProgram', mesonlib.File]],
             workdir: T.Optional[str] = None,
             env: T.Union[T.List[str], T.Dict[str, str], str] = None,
             depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] = None) -> None:
        kwargs = {'workdir': workdir,
                  'env': env,
                  'depends': depends,
                  }
        # typed_* takes a list, and gives a tuple to func_test. Violating that constraint
        # makes the universe (or at least use of this function) implode
        real_args = list(args)
        # TODO: Use interpreter internal API, but we need to go through @typed_kwargs
        self._interpreter.func_test(self.current_node, real_args, kwargs)

    def get_option(self, name: str, subproject: str = '',
                   machine: MachineChoice = MachineChoice.HOST,
                   lang: T.Optional[str] = None,
                   module: T.Optional[str] = None) -> T.Union[str, int, bool, 'WrapMode']:
        return self.environment.coredata.get_option(mesonlib.OptionKey(name, subproject, machine, lang, module))

    def is_user_defined_option(self, name: str, subproject: str = '',
                               machine: MachineChoice = MachineChoice.HOST,
                               lang: T.Optional[str] = None,
                               module: T.Optional[str] = None) -> bool:
        key = mesonlib.OptionKey(name, subproject, machine, lang, module)
        return key in self._interpreter.user_defined_options.cmd_line_options

class ModuleObject(HoldableObject):
    """Base class for all objects returned by modules
    """
    def __init__(self) -> None:
        self.methods: T.Dict[
            str,
            T.Callable[[ModuleState, T.List['TYPE_var'], 'TYPE_kwargs'], T.Union[ModuleReturnValue, 'TYPE_var']]
        ] = {}

class MutableModuleObject(ModuleObject):
    pass

# FIXME: Port all modules to stop using self.interpreter and use API on
# ModuleState instead. Modules should stop using this class and instead use
# ModuleObject base class.
class ExtensionModule(ModuleObject):
    def __init__(self, interpreter: 'Interpreter') -> None:
        super().__init__()
        self.interpreter = interpreter
        self.methods.update({
            'found': self.found_method,
        })

    @noPosargs
    @noKwargs
    def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
        return self.found()

    @staticmethod
    def found() -> bool:
        return True

class NewExtensionModule(ModuleObject):

    """Class for modern modules

    provides the found method.
    """

    def __init__(self) -> None:
        super().__init__()
        self.methods.update({
            'found': self.found_method,
        })

    @noPosargs
    @noKwargs
    def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
        return self.found()

    @staticmethod
    def found() -> bool:
        return True

class NotFoundExtensionModule(NewExtensionModule):

    """Class for modern modules

    provides the found method.
    """

    @staticmethod
    def found() -> bool:
        return False

def is_module_library(fname):
    '''
    Check if the file is a library-like file generated by a module-specific
    target, such as GirTarget or TypelibTarget
    '''
    if hasattr(fname, 'fname'):
        fname = fname.fname
    suffix = fname.split('.')[-1]
    return suffix in ('gir', 'typelib')

class ModuleReturnValue:
    def __init__(self, return_value: T.Optional['TYPE_var'],
                 new_objects: T.Sequence[T.Union['TYPE_var', 'build.ExecutableSerialisation']]) -> None:
        self.return_value = return_value
        assert isinstance(new_objects, list)
        self.new_objects: T.List[T.Union['TYPE_var', 'build.ExecutableSerialisation']] = new_objects

class GResourceTarget(build.CustomTarget):
    pass

class GResourceHeaderTarget(build.CustomTarget):
    pass

class GirTarget(build.CustomTarget):
    pass

class TypelibTarget(build.CustomTarget):
    pass

class VapiTarget(build.CustomTarget):
    pass
<commit_msg>modules: Make ExtensionModule inherit from NewExtensionModule<commit_after># Copyright 2019 The Meson development team

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file contains the base representation for import('modname')

import typing as T

from .. import build, mesonlib
from ..mesonlib import relpath, HoldableObject, MachineChoice
from ..interpreterbase.decorators import noKwargs, noPosargs

if T.TYPE_CHECKING:
    from ..interpreter import Interpreter
    from ..interpreter.interpreterobjects import MachineHolder
    from ..interpreterbase import TYPE_var, TYPE_kwargs
    from ..programs import ExternalProgram
    from ..wrap import WrapMode

class ModuleState:
    """Object passed to all module methods.

    This is a WIP API provided to modules, it should be extended to have everything
    needed so modules does not touch any other part of Meson internal APIs.
    """

    def __init__(self, interpreter: 'Interpreter') -> None:
        # Keep it private, it should be accessed only through methods.
        self._interpreter = interpreter

        self.source_root = interpreter.environment.get_source_dir()
        self.build_to_src = relpath(interpreter.environment.get_source_dir(),
                                    interpreter.environment.get_build_dir())
        self.subproject = interpreter.subproject
        self.subdir = interpreter.subdir
        self.current_lineno = interpreter.current_lineno
        self.environment = interpreter.environment
        self.project_name = interpreter.build.project_name
        self.project_version = interpreter.build.dep_manifest[interpreter.active_projectname].version
        # The backend object is under-used right now, but we will need it:
        # https://github.com/mesonbuild/meson/issues/1419
        self.backend = interpreter.backend
        self.targets = interpreter.build.targets
        self.data = interpreter.build.data
        self.headers = interpreter.build.get_headers()
        self.man = interpreter.build.get_man()
        self.global_args = interpreter.build.global_args.host
        self.project_args = interpreter.build.projects_args.host.get(interpreter.subproject, {})
        self.build_machine = T.cast('MachineHolder', interpreter.builtin['build_machine']).held_object
        self.host_machine = T.cast('MachineHolder', interpreter.builtin['host_machine']).held_object
        self.target_machine = T.cast('MachineHolder', interpreter.builtin['target_machine']).held_object
        self.current_node = interpreter.current_node

    def get_include_args(self, include_dirs: T.Iterable[T.Union[str, build.IncludeDirs]], prefix: str = '-I') -> T.List[str]:
        if not include_dirs:
            return []

        srcdir = self.environment.get_source_dir()
        builddir = self.environment.get_build_dir()

        dirs_str: T.List[str] = []
        for dirs in include_dirs:
            if isinstance(dirs, str):
                dirs_str += [f'{prefix}{dirs}']
            else:
                dirs_str.extend([f'{prefix}{i}' for i in dirs.to_string_list(srcdir, builddir)])
                dirs_str.extend([f'{prefix}{i}' for i in dirs.get_extra_build_dirs()])

        return dirs_str

    def find_program(self, prog: T.Union[str, T.List[str]], required: bool = True,
                     version_func: T.Optional[T.Callable[['ExternalProgram'], str]] = None,
                     wanted: T.Optional[str] = None, silent: bool = False,
                     for_machine: MachineChoice = MachineChoice.HOST) -> 'ExternalProgram':
        return self._interpreter.find_program_impl(prog, required=required, version_func=version_func,
                                                   wanted=wanted, silent=silent, for_machine=for_machine)

    def test(self, args: T.Tuple[str, T.Union[build.Executable, build.Jar, 'ExternalProgram', mesonlib.File]],
             workdir: T.Optional[str] = None,
             env: T.Union[T.List[str], T.Dict[str, str], str] = None,
             depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] = None) -> None:
        kwargs = {'workdir': workdir,
                  'env': env,
                  'depends': depends,
                  }
        # typed_* takes a list, and gives a tuple to func_test. Violating that constraint
        # makes the universe (or at least use of this function) implode
        real_args = list(args)
        # TODO: Use interpreter internal API, but we need to go through @typed_kwargs
        self._interpreter.func_test(self.current_node, real_args, kwargs)

    def get_option(self, name: str, subproject: str = '',
                   machine: MachineChoice = MachineChoice.HOST,
                   lang: T.Optional[str] = None,
                   module: T.Optional[str] = None) -> T.Union[str, int, bool, 'WrapMode']:
        return self.environment.coredata.get_option(mesonlib.OptionKey(name, subproject, machine, lang, module))

    def is_user_defined_option(self, name: str, subproject: str = '',
                               machine: MachineChoice = MachineChoice.HOST,
                               lang: T.Optional[str] = None,
                               module: T.Optional[str] = None) -> bool:
        key = mesonlib.OptionKey(name, subproject, machine, lang, module)
        return key in self._interpreter.user_defined_options.cmd_line_options

class ModuleObject(HoldableObject):
    """Base class for all objects returned by modules
    """
    def __init__(self) -> None:
        self.methods: T.Dict[
            str,
            T.Callable[[ModuleState, T.List['TYPE_var'], 'TYPE_kwargs'], T.Union[ModuleReturnValue, 'TYPE_var']]
        ] = {}

class MutableModuleObject(ModuleObject):
    pass

class NewExtensionModule(ModuleObject):

    """Class for modern modules

    provides the found method.
    """

    def __init__(self) -> None:
        super().__init__()
        self.methods.update({
            'found': self.found_method,
        })

    @noPosargs
    @noKwargs
    def found_method(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool:
        return self.found()

    @staticmethod
    def found() -> bool:
        return True

# FIXME: Port all modules to stop using self.interpreter and use API on
# ModuleState instead. Modules should stop using this class and instead use
# ModuleObject base class.
class ExtensionModule(NewExtensionModule):
    def __init__(self, interpreter: 'Interpreter') -> None:
        super().__init__()
        self.interpreter = interpreter

class NotFoundExtensionModule(NewExtensionModule):

    """Class for modern modules

    provides the found method.
    """

    @staticmethod
    def found() -> bool:
        return False

def is_module_library(fname):
    '''
    Check if the file is a library-like file generated by a module-specific
    target, such as GirTarget or TypelibTarget
    '''
    if hasattr(fname, 'fname'):
        fname = fname.fname
    suffix = fname.split('.')[-1]
    return suffix in ('gir', 'typelib')

class ModuleReturnValue:
    def __init__(self, return_value: T.Optional['TYPE_var'],
                 new_objects: T.Sequence[T.Union['TYPE_var', 'build.ExecutableSerialisation']]) -> None:
        self.return_value = return_value
        assert isinstance(new_objects, list)
        self.new_objects: T.List[T.Union['TYPE_var', 'build.ExecutableSerialisation']] = new_objects

class GResourceTarget(build.CustomTarget):
    pass

class GResourceHeaderTarget(build.CustomTarget):
    pass

class GirTarget(build.CustomTarget):
    pass

class TypelibTarget(build.CustomTarget):
    pass

class VapiTarget(build.CustomTarget):
    pass
<|endoftext|>


<commit_before>from flask import Flask, url_for, render_template

app = Flask(__name__) #__name__ = "__main__" if this is the file that was run.  Otherwise, it is the name of the file (ex. webapp)

@app.route("/")
def render_main():
    return render_template('home.html')

@app.route("/page1")
def render_page1():
    return render_template('page1.html')

@app.route("/page2")
def render_page2():
    return render_template('page2.html')

if __name__=="__main__":
    app.run(debug=False, port=54321)
<commit_msg>Update webapp.py<commit_after>from flask import Flask, url_for, render_template

app = Flask(__name__) #__name__ = "__main__" if this is the file that was run.  Otherwise, it is the name of the file (ex. webapp)

@app.route("/")
def render_main():
    return render_template('home.html')

@app.route("/login")
def render_page1():
    return render_template('login.html')

@app.route("/page2")
def render_page2():
    return render_template('page2.html')

if __name__=="__main__":
    app.run(debug=False, port=54321)
<|endoftext|>


<commit_before>import copy

try:
    # Python 2.x
    import Queue as queue
except ImportError:
    # Python 3.x
    import queue

class Timeout(Exception):
    pass

class Future(object):
    """
    A :class:`Future` is a handle to a value which are available or will be
    available in the future.

    Typically returned by calls to actor methods or accesses to actor fields.

    To get hold of the encapsulated value, call :meth:`Future.get`.
    """

    def get(self, timeout=None):
        """
        Get the value encapsulated by the future.

        If the encapsulated value is an exception, it is raised instead of
        returned.

        If ``timeout`` is :class:`None`, as default, the method will block
        until it gets a reply, potentially forever. If ``timeout`` is an
        integer or float, the method will wait for a reply for ``timeout``
        seconds, and then raise :exc:`Timeout`.

        The encapsulated value can be retrieved multiple times. The future will
        only block the first time the value is accessed.

        :param block: whether to block while waiting for a reply
        :type block: boolean

        :param timeout: seconds to wait before timeout
        :type timeout: float or :class:`None`

        :return: encapsulated value
        """
        raise NotImplementedError

    def set(self, value=None):
        """
        Set the encapsulated value.

        :param value: the encapsulated value or nothing
        :type value: any picklable object or :class:`None`
        """
        raise NotImplementedError

    def set_exception(self, exception):
        """
        Set an exception as the encapsulated value.

        :param exception: the encapsulated exception
        :type exception: exception
        """
        raise NotImplementedError

class ThreadingFuture(Future):
    """
    :class:`ThreadingFuture` implements :class:`Future` for use with
    :class:`pykka.actor.ThreadingActor`.

    The future is implemented using a :class:`Queue.Queue`.

    The encapsulated value is a copy made with :func:`copy.deepcopy`, unless
    the encapsulated value is another :class:`ThreadingFuture`, in which case
    the original future is encapsulated.
    """

    def __init__(self):
        super(ThreadingFuture, self).__init__()
        self._queue = queue.Queue()
        self._value_received = False
        self._value = None

    # pylint: disable = E0702
    def get(self, timeout=None):
        if self._value_received:
            if isinstance(self._value, Exception):
                raise self._value
            else:
                return self._value
        try:
            self._value = self._queue.get(True, timeout)
            self._value_received = True
            return self.get()
        except queue.Empty:
            raise Timeout('%s seconds' % timeout)
    # pylint: enable = E0702

    def set(self, value=None):
        if isinstance(value, ThreadingFuture):
            value = value
        else:
            value = copy.deepcopy(value)
        self._queue.put(value)

    def set_exception(self, exception):
        self.set(exception)

def get_all(futures, timeout=None):
    """
    Collect all values encapsulated in the list of futures.

    If ``timeout`` is not :class:`None`, the method will wait for a reply for
    ``timeout`` seconds, and then raise :exc:`pykka.future.Timeout`.

    :param futures: futures for the results to collect
    :type futures: list of `pykka.future.Future`

    :param timeout: seconds to wait before timeout
    :type timeout: float or :class:`None`

    :returns: list of results
    """
    return [future.get(timeout=timeout) for future in futures]
<commit_msg>Make pylint disabling of E0702 more local<commit_after>import copy

try:
    # Python 2.x
    import Queue as queue
except ImportError:
    # Python 3.x
    import queue

class Timeout(Exception):
    pass

class Future(object):
    """
    A :class:`Future` is a handle to a value which are available or will be
    available in the future.

    Typically returned by calls to actor methods or accesses to actor fields.

    To get hold of the encapsulated value, call :meth:`Future.get`.
    """

    def get(self, timeout=None):
        """
        Get the value encapsulated by the future.

        If the encapsulated value is an exception, it is raised instead of
        returned.

        If ``timeout`` is :class:`None`, as default, the method will block
        until it gets a reply, potentially forever. If ``timeout`` is an
        integer or float, the method will wait for a reply for ``timeout``
        seconds, and then raise :exc:`Timeout`.

        The encapsulated value can be retrieved multiple times. The future will
        only block the first time the value is accessed.

        :param block: whether to block while waiting for a reply
        :type block: boolean

        :param timeout: seconds to wait before timeout
        :type timeout: float or :class:`None`

        :return: encapsulated value
        """
        raise NotImplementedError

    def set(self, value=None):
        """
        Set the encapsulated value.

        :param value: the encapsulated value or nothing
        :type value: any picklable object or :class:`None`
        """
        raise NotImplementedError

    def set_exception(self, exception):
        """
        Set an exception as the encapsulated value.

        :param exception: the encapsulated exception
        :type exception: exception
        """
        raise NotImplementedError

class ThreadingFuture(Future):
    """
    :class:`ThreadingFuture` implements :class:`Future` for use with
    :class:`pykka.actor.ThreadingActor`.

    The future is implemented using a :class:`Queue.Queue`.

    The encapsulated value is a copy made with :func:`copy.deepcopy`, unless
    the encapsulated value is another :class:`ThreadingFuture`, in which case
    the original future is encapsulated.
    """

    def __init__(self):
        super(ThreadingFuture, self).__init__()
        self._queue = queue.Queue()
        self._value_received = False
        self._value = None

    def get(self, timeout=None):
        if self._value_received:
            if isinstance(self._value, Exception):
                raise self._value # pylint: disable = E0702
            else:
                return self._value
        try:
            self._value = self._queue.get(True, timeout)
            self._value_received = True
            return self.get()
        except queue.Empty:
            raise Timeout('%s seconds' % timeout)

    def set(self, value=None):
        if isinstance(value, ThreadingFuture):
            value = value
        else:
            value = copy.deepcopy(value)
        self._queue.put(value)

    def set_exception(self, exception):
        self.set(exception)

def get_all(futures, timeout=None):
    """
    Collect all values encapsulated in the list of futures.

    If ``timeout`` is not :class:`None`, the method will wait for a reply for
    ``timeout`` seconds, and then raise :exc:`pykka.future.Timeout`.

    :param futures: futures for the results to collect
    :type futures: list of `pykka.future.Future`

    :param timeout: seconds to wait before timeout
    :type timeout: float or :class:`None`

    :returns: list of results
    """
    return [future.get(timeout=timeout) for future in futures]
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from __future__ import unicode_literals

from django.db import transaction
from django.views.generic import TemplateView
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.utils.text import slugify

from braces.views import LoginRequiredMixin

from auth_helpers.views import GroupRequiredMixin, user_in_group
from elections.models import Election
from candidates.models import PostExtra, PersonExtra, MembershipExtra
from candidates.models.auth import check_creation_allowed, check_update_allowed
from candidates.views.version_data import get_change_metadata, get_client_ip
from candidates.views.people import get_call_to_action_flash_message
from candidates.models import LoggedAction
from popolo.models import Person, Membership, Organization
from official_documents.models import OfficialDocument
from moderation_queue.models import SuggestedPostLock

from . import forms

class BaseBulkAddView(LoginRequiredMixin, TemplateView):
    # required_group_name = models.TRUSTED_TO_BULK_ADD_GROUP_NAME

    def add_election_and_post_to_context(self, context):
        context['post_extra'] = PostExtra.objects.get(slug=context['post_id'])
        context['election_obj'] = Election.objects.get(slug=context['election'])
        context['parties'] = context['post_extra'].party_set.party_choices()
        return context

    def post(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        if context['formset'].is_valid():
            return self.form_valid(context)
        else:
            return self.form_invalid(context)

class BulkAddView(BaseBulkAddView):
    template_name = "bulk_add/add_form.html"

    def get_context_data(self, **kwargs):
        context = super(BulkAddView, self).get_context_data(**kwargs)
        context.update(self.add_election_and_post_to_context(context))

        context['official_documents'] = OfficialDocument.objects.filter(
            post__extra__slug=context['post_id'],
            election__slug=context['election'],
            )

        form_kwargs = {
            'parties': context['parties']
        }
        if self.request.POST:
            context['formset'] = forms.BulkAddFormSet(
                self.request.POST, **form_kwargs
            )
        else:
            context['formset'] = forms.BulkAddFormSet(
                **form_kwargs
            )

        known_people = [m.person for m in context['post_extra'].base.memberships.all()]
        known_people.sort(key=lambda i: i.name.split(' ')[-1])
        context['known_people'] = known_people

        return context

    def form_valid(self, context):
        self.request.session['bulk_add_data'] = context['formset'].cleaned_data
        return HttpResponseRedirect(
            reverse('bulk_add_review', kwargs={
                'election': context['election'],
                'post_id': context['post_id'],
            })
        )

    def form_invalid(self, context):
        return self.render_to_response(context)

class BulkAddReviewView(BaseBulkAddView):
    template_name = "bulk_add/add_review_form.html"

    def get_context_data(self, **kwargs):
        context = super(BulkAddReviewView, self).get_context_data(**kwargs)
        context.update(self.add_election_and_post_to_context(context))

        initial = [form for form in self.request.session['bulk_add_data'] if form]
        if self.request.POST:
            context['formset'] = forms.BulkAddReviewFormSet(
                self.request.POST, parties=context['parties']
            )
        else:
            context['formset'] = forms.BulkAddReviewFormSet(
                initial=initial, parties=context['parties']
            )

        return context

    def add_person(self, person_data):
        # TODO Move this out of the view layer
        person = Person.objects.create(name=person_data['name'])
        person_extra = PersonExtra.objects.create(base=person)
        check_creation_allowed(
            self.request.user, person_extra.current_candidacies
        )

        change_metadata = get_change_metadata(
            self.request, person_data['source']
        )

        person_extra.record_version(change_metadata)
        person_extra.save()

        LoggedAction.objects.create(
            user=self.request.user,
            person=person,
            action_type='person-create',
            ip_address=get_client_ip(self.request),
            popit_person_new_version=change_metadata['version_id'],
            source=change_metadata['information_source'],
        )

        # Add a message to be displayed after redirect:
        messages.add_message(
            self.request,
            messages.SUCCESS,
            get_call_to_action_flash_message(person, new_person=True),
            extra_tags='safe do-something-else'
        )
        return person_extra

    def update_person(self, context, data, person_extra):
        party = Organization.objects.get(pk=data['party'])
        post = context['post_extra'].base
        election = Election.objects.get(slug=context['election'])

        previous_memberships_in_this_election = Membership.objects.filter(
            person=person_extra.base,
            extra__election=election,
            role=election.candidate_membership_role,
        )

        previous_memberships_in_this_election.delete()

        membership, _ = Membership.objects.get_or_create(
            post=post,
            person=person_extra.base,
            extra__election=election,
            role=election.candidate_membership_role,
            defaults={
                'on_behalf_of': party,
            }
        )

        MembershipExtra.objects.get_or_create(
            base=membership,
            defaults={
                'party_list_position': None,
                'election': election,
                'elected': False,
            }
        )

        change_metadata = get_change_metadata(
            self.request, data['source']
        )

        person_extra.record_version(change_metadata)
        person_extra.save()

        LoggedAction.objects.create(
            user=self.request.user,
            person=person_extra.base,
            action_type='person-update',
            ip_address=get_client_ip(self.request),
            popit_person_new_version=change_metadata['version_id'],
            source=change_metadata['information_source'],
        )

    def form_valid(self, context):

        with transaction.atomic():
            for person_form in context['formset']:
                data = person_form.cleaned_data
                if data.get('select_person') == "_new":
                    # Add a new person
                    person_extra = self.add_person(data)
                else:
                    person_extra = PersonExtra.objects.get(
                        base__pk=int(data['select_person']))
                self.update_person(context, data, person_extra)
            if self.request.POST.get('suggest_locking') == 'on':
                SuggestedPostLock.objects.create(
                    user=self.request.user,
                    post_extra=context['post_extra'],
                )

        url = reverse('constituency', kwargs={
            'election': context['election'],
            'post_id': context['post_extra'].slug,
            'ignored_slug': slugify(context['post_extra'].base.label),
        })
        return HttpResponseRedirect(url)

    def form_invalid(self, context):
        return self.render_to_response(context)

class UnlockedWithDocumentsView(TemplateView):
    template_name = "official_documents/unlocked_with_documents.html"

    def get_context_data(self, **kwargs):
        context = super(
            UnlockedWithDocumentsView, self).get_context_data(**kwargs)

        SOPNs_qs = OfficialDocument.objects.filter(
            election__current=True).select_related('election', 'post__extra')

        SOPNs_qs = SOPNs_qs.exclude(
            post__in=SuggestedPostLock.objects.all().values(
                'post_extra__base'))

        context['unlocked_sopns'] = SOPNs_qs.filter(
            post__extra__candidates_locked=False)

        return context
<commit_msg>De-duplicate known people<commit_after># -*- coding: utf-8 -*-

from __future__ import unicode_literals

from django.db import transaction
from django.views.generic import TemplateView
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.utils.text import slugify

from braces.views import LoginRequiredMixin

from auth_helpers.views import GroupRequiredMixin, user_in_group
from elections.models import Election
from candidates.models import PostExtra, PersonExtra, MembershipExtra
from candidates.models.auth import check_creation_allowed, check_update_allowed
from candidates.views.version_data import get_change_metadata, get_client_ip
from candidates.views.people import get_call_to_action_flash_message
from candidates.models import LoggedAction
from popolo.models import Person, Membership, Organization
from official_documents.models import OfficialDocument
from moderation_queue.models import SuggestedPostLock

from . import forms

class BaseBulkAddView(LoginRequiredMixin, TemplateView):
    # required_group_name = models.TRUSTED_TO_BULK_ADD_GROUP_NAME

    def add_election_and_post_to_context(self, context):
        context['post_extra'] = PostExtra.objects.get(slug=context['post_id'])
        context['election_obj'] = Election.objects.get(slug=context['election'])
        context['parties'] = context['post_extra'].party_set.party_choices()
        return context

    def post(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        if context['formset'].is_valid():
            return self.form_valid(context)
        else:
            return self.form_invalid(context)

class BulkAddView(BaseBulkAddView):
    template_name = "bulk_add/add_form.html"

    def get_context_data(self, **kwargs):
        context = super(BulkAddView, self).get_context_data(**kwargs)
        context.update(self.add_election_and_post_to_context(context))

        context['official_documents'] = OfficialDocument.objects.filter(
            post__extra__slug=context['post_id'],
            election__slug=context['election'],
            )

        form_kwargs = {
            'parties': context['parties']
        }
        if self.request.POST:
            context['formset'] = forms.BulkAddFormSet(
                self.request.POST, **form_kwargs
            )
        else:
            context['formset'] = forms.BulkAddFormSet(
                **form_kwargs
            )

        known_people = list(set(
            [m.person for m in context['post_extra'].base.memberships.all()]))
        known_people.sort(key=lambda i: i.name.split(' ')[-1])
        context['known_people'] = known_people

        return context

    def form_valid(self, context):
        self.request.session['bulk_add_data'] = context['formset'].cleaned_data
        return HttpResponseRedirect(
            reverse('bulk_add_review', kwargs={
                'election': context['election'],
                'post_id': context['post_id'],
            })
        )

    def form_invalid(self, context):
        return self.render_to_response(context)

class BulkAddReviewView(BaseBulkAddView):
    template_name = "bulk_add/add_review_form.html"

    def get_context_data(self, **kwargs):
        context = super(BulkAddReviewView, self).get_context_data(**kwargs)
        context.update(self.add_election_and_post_to_context(context))

        initial = [form for form in self.request.session['bulk_add_data'] if form]
        if self.request.POST:
            context['formset'] = forms.BulkAddReviewFormSet(
                self.request.POST, parties=context['parties']
            )
        else:
            context['formset'] = forms.BulkAddReviewFormSet(
                initial=initial, parties=context['parties']
            )

        return context

    def add_person(self, person_data):
        # TODO Move this out of the view layer
        person = Person.objects.create(name=person_data['name'])
        person_extra = PersonExtra.objects.create(base=person)
        check_creation_allowed(
            self.request.user, person_extra.current_candidacies
        )

        change_metadata = get_change_metadata(
            self.request, person_data['source']
        )

        person_extra.record_version(change_metadata)
        person_extra.save()

        LoggedAction.objects.create(
            user=self.request.user,
            person=person,
            action_type='person-create',
            ip_address=get_client_ip(self.request),
            popit_person_new_version=change_metadata['version_id'],
            source=change_metadata['information_source'],
        )

        # Add a message to be displayed after redirect:
        messages.add_message(
            self.request,
            messages.SUCCESS,
            get_call_to_action_flash_message(person, new_person=True),
            extra_tags='safe do-something-else'
        )
        return person_extra

    def update_person(self, context, data, person_extra):
        party = Organization.objects.get(pk=data['party'])
        post = context['post_extra'].base
        election = Election.objects.get(slug=context['election'])

        previous_memberships_in_this_election = Membership.objects.filter(
            person=person_extra.base,
            extra__election=election,
            role=election.candidate_membership_role,
        )

        previous_memberships_in_this_election.delete()

        membership, _ = Membership.objects.get_or_create(
            post=post,
            person=person_extra.base,
            extra__election=election,
            role=election.candidate_membership_role,
            defaults={
                'on_behalf_of': party,
            }
        )

        MembershipExtra.objects.get_or_create(
            base=membership,
            defaults={
                'party_list_position': None,
                'election': election,
                'elected': False,
            }
        )

        change_metadata = get_change_metadata(
            self.request, data['source']
        )

        person_extra.record_version(change_metadata)
        person_extra.save()

        LoggedAction.objects.create(
            user=self.request.user,
            person=person_extra.base,
            action_type='person-update',
            ip_address=get_client_ip(self.request),
            popit_person_new_version=change_metadata['version_id'],
            source=change_metadata['information_source'],
        )

    def form_valid(self, context):

        with transaction.atomic():
            for person_form in context['formset']:
                data = person_form.cleaned_data
                if data.get('select_person') == "_new":
                    # Add a new person
                    person_extra = self.add_person(data)
                else:
                    person_extra = PersonExtra.objects.get(
                        base__pk=int(data['select_person']))
                self.update_person(context, data, person_extra)
            if self.request.POST.get('suggest_locking') == 'on':
                SuggestedPostLock.objects.create(
                    user=self.request.user,
                    post_extra=context['post_extra'],
                )

        url = reverse('constituency', kwargs={
            'election': context['election'],
            'post_id': context['post_extra'].slug,
            'ignored_slug': slugify(context['post_extra'].base.label),
        })
        return HttpResponseRedirect(url)

    def form_invalid(self, context):
        return self.render_to_response(context)

class UnlockedWithDocumentsView(TemplateView):
    template_name = "official_documents/unlocked_with_documents.html"

    def get_context_data(self, **kwargs):
        context = super(
            UnlockedWithDocumentsView, self).get_context_data(**kwargs)

        SOPNs_qs = OfficialDocument.objects.filter(
            election__current=True).select_related('election', 'post__extra')

        SOPNs_qs = SOPNs_qs.exclude(
            post__in=SuggestedPostLock.objects.all().values(
                'post_extra__base'))

        context['unlocked_sopns'] = SOPNs_qs.filter(
            post__extra__candidates_locked=False)

        return context
<|endoftext|>


<commit_before>from requests_oauthlib import OAuth2Session
from flask import Flask, session, render_template, request, redirect, url_for, flash
from flask.json import jsonify
import logging
import os
import pprint
import sys

app = Flask(__name__)

app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.ERROR)
app.secret_key=os.urandom(24)

client_id = os.environ['GITHUB_CLIENT_ID']
client_secret = os.environ['GITHUB_CLIENT_SECRET']
authorization_base_url = 'https://github.com/login/oauth/authorize'
token_url = 'https://github.com/login/oauth/access_token'
redirect_uri = 'https://polar-coast-87574.herokuapp.com/login/authorized'
github = None

@app.context_processor
def inject_logged_in():
	return dict(logged_in=(is_logged_in()))

def is_logged_in():
	return ('oauth_token' in session)

@app.route('/')
def render_home():
	return render_template('home.html')

@app.route('/login')
def login():
	session.clear()
	github = OAuth2Session(client_id, redirect_uri=redirect_uri)
	(authorization_url,state) = github.authorization_url(authorization_base_url)
	print authorization_url
	session['oauth_state'] = state
	return redirect(authorization_url)

@app.route('/login/authorized', methods=["GET"])
def authorized():
	github.state=session['oauth_state']
	token = github.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url)

	session['oauth_token']= token
	return redirect(url_for('render_home'))

@app.route('/profile', methods=["GET"])
def profile():
	github.token=session['oauth_token']
	return jsonify(github.get('https://api.github.com/user').json())

@app.route('/logout')
def logout():
	session.clear()
	flash('You were logged out!')
	return redirect(url_for('render_home'))

@app.route('/conversions')
def render_conversions_home():
	return render_template('conversion_home.html')

@app.route('/tutorials')
def render_tutorials_home():
	return render_template('tutorial_home.html')

@app.route('/jquery-animate')
def render_tutorials_animate():
	return render_template('animate_key.html')

@app.route('/jquery-slide')
def render_tutorials_slide():
	return render_template('slide.html')

@app.route('/ctof')
def render_ctof():
	return render_template('ctof.html')

@app.route('/ftoc')
def render_ftoc():
	return render_template('ftoc.html')

@app.route('/mtokm')
def render_mtokm():
	return render_template('mtokm.html')

@app.route('/ctof_result')
def render_ctof_result():
	try:
		ctemp_result = float(request.args['cTemp'])
		ftemp_result = ctof(ctemp_result)
		return render_template('ctof_result.html',cTemp=ctemp_result, fTemp=ftemp_result)
	except ValueError:
		error_message = "Sorry, could not convert..."
		flash(error_message)
		return redirect(url_for('render_home'))

@app.route('/ftoc_result')
def render_ftoc_result():
	try:
		ftemp_result = float(request.args['fTemp'])
		ctemp_result = ftoc(ftemp_result)
		return render_template('ftoc_result.html',fTemp=ftemp_result, cTemp=ctemp_result)
	except ValueError:
		error_message = "Sorry, could not convert..."
		flash(error_message)
		return redirect(url_for('render_home'))

@app.route('/mtokm_result')
def render_mtokm_result():
	try:
		miles = float(request.args['miles'])
		kilometers = mtokm(miles)
		return render_template('mtokm_result.html',miles=miles, kilometers=kilometers)
	except ValueError:
		error_message = "Sorry, could not convert..."
		flash(error_message)
		return redirect(url_for('render_home'))

def ctof(ctemp):
	return (ctemp * (9.0/5.0)) + 32.0

def ftoc(ftemp):
	return (ftemp-32.0)*(5.0/9.0)

def mtokm(miles):
	return (miles * 1.60934)

if __name__ == "__main__":
	app.run(debug=True)
<commit_msg>still texting profile access<commit_after>from requests_oauthlib import OAuth2Session
from flask import Flask, session, render_template, request, redirect, url_for, flash
from flask.json import jsonify
import logging
import os
import pprint
import sys

app = Flask(__name__)

app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.ERROR)
app.secret_key=os.urandom(24)

client_id = os.environ['GITHUB_CLIENT_ID']
client_secret = os.environ['GITHUB_CLIENT_SECRET']
authorization_base_url = 'https://github.com/login/oauth/authorize'
token_url = 'https://github.com/login/oauth/access_token'
redirect_uri = 'https://polar-coast-87574.herokuapp.com/login/authorized'
github = OAuth2Session(client_id, redirect_uri=redirect_uri)

@app.context_processor
def inject_logged_in():
	return dict(logged_in=(is_logged_in()))

def is_logged_in():
	return ('oauth_token' in session)

@app.route('/')
def render_home():
	return render_template('home.html')

@app.route('/login')
def login():
	global github
	session.clear()
	(authorization_url,state) = github.authorization_url(authorization_base_url)
	print authorization_url
	session['oauth_state'] = state
	return redirect(authorization_url)

@app.route('/login/authorized', methods=["GET"])
def authorized():
	github.state=session['oauth_state']
	token = github.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url)

	session['oauth_token']= token
	return redirect(url_for('render_home'))

@app.route('/profile', methods=["GET"])
def profile():
	github.token=session['oauth_token']
	return jsonify(github.get('https://api.github.com/user').json())

@app.route('/logout')
def logout():
	session.clear()
	flash('You were logged out!')
	return redirect(url_for('render_home'))

@app.route('/conversions')
def render_conversions_home():
	return render_template('conversion_home.html')

@app.route('/tutorials')
def render_tutorials_home():
	return render_template('tutorial_home.html')

@app.route('/jquery-animate')
def render_tutorials_animate():
	return render_template('animate_key.html')

@app.route('/jquery-slide')
def render_tutorials_slide():
	return render_template('slide.html')

@app.route('/ctof')
def render_ctof():
	return render_template('ctof.html')

@app.route('/ftoc')
def render_ftoc():
	return render_template('ftoc.html')

@app.route('/mtokm')
def render_mtokm():
	return render_template('mtokm.html')

@app.route('/ctof_result')
def render_ctof_result():
	try:
		ctemp_result = float(request.args['cTemp'])
		ftemp_result = ctof(ctemp_result)
		return render_template('ctof_result.html',cTemp=ctemp_result, fTemp=ftemp_result)
	except ValueError:
		error_message = "Sorry, could not convert..."
		flash(error_message)
		return redirect(url_for('render_home'))

@app.route('/ftoc_result')
def render_ftoc_result():
	try:
		ftemp_result = float(request.args['fTemp'])
		ctemp_result = ftoc(ftemp_result)
		return render_template('ftoc_result.html',fTemp=ftemp_result, cTemp=ctemp_result)
	except ValueError:
		error_message = "Sorry, could not convert..."
		flash(error_message)
		return redirect(url_for('render_home'))

@app.route('/mtokm_result')
def render_mtokm_result():
	try:
		miles = float(request.args['miles'])
		kilometers = mtokm(miles)
		return render_template('mtokm_result.html',miles=miles, kilometers=kilometers)
	except ValueError:
		error_message = "Sorry, could not convert..."
		flash(error_message)
		return redirect(url_for('render_home'))

def ctof(ctemp):
	return (ctemp * (9.0/5.0)) + 32.0

def ftoc(ftemp):
	return (ftemp-32.0)*(5.0/9.0)

def mtokm(miles):
	return (miles * 1.60934)

if __name__ == "__main__":
	app.run(debug=True)
<|endoftext|>


<commit_before>""" Async code checking.
"""
import logging
import threading
from os import path as op
try:
    import Queue
except ImportError:
    import queue as Queue

from .core import run

try:
    import multiprocessing

    CPU_COUNT = multiprocessing.cpu_count()

except (ImportError, NotImplementedError):
    CPU_COUNT = 1

LOGGER = logging.getLogger('pylama')

class Worker(threading.Thread):

    """ Get tasks from queue and run. """

    def __init__(self, path_queue, result_queue):
        threading.Thread.__init__(self)
        self.path_queue = path_queue
        self.result_queue = result_queue

    def run(self):
        """ Run tasks from queue. """
        while True:
            path, params = self.path_queue.get()
            errors = check_path(path, **params)
            self.result_queue.put(errors)
            self.path_queue.task_done()

def async_check_files(paths, options, rootpath=None):
    """ Check paths.

    :return list: list of errors

    """

    errors = []

    # Disable async if pylint enabled
    async = options.async and not 'pylint' in options.linters

    if not async:
        for path in paths:
            errors += check_path(path, options=options, rootpath=rootpath)
        return errors

    LOGGER.info('Async code checking is enabled.')
    path_queue = Queue.Queue()
    result_queue = Queue.Queue()

    for _ in range(CPU_COUNT):
        worker = Worker(path_queue, result_queue)
        worker.setDaemon(True)
        worker.start()

    for path in paths:
        path_queue.put((path, dict(options=options, rootpath=rootpath)))

    path_queue.join()

    while True:
        try:
            errors += result_queue.get(False)
        except Queue.Empty:
            break

    return errors

def check_path(path, options=None, rootpath=None):
    """ Check path.

    :return list: list of errors

    """

    LOGGER.info("Parse file: %s", path)
    config = dict()
    if rootpath is None:
        rootpath = '.'

    for mask in options.file_params:
        if mask.match(path):
            config.update(options.file_params[mask])

    errors = []
    for error in run(
        path, ignore=options.ignore, select=options.select,
        linters=options.linters, complexity=options.complexity,
            config=config):
        try:
            error['rel'] = op.relpath(error['filename'], rootpath)
            error['col'] = error.get('col', 1)
            errors.append(error)
        except KeyError:
            continue
    return errors
<commit_msg>Send meta options from check file<commit_after>""" Async code checking.
"""
import logging
import threading
from os import path as op
try:
    import Queue
except ImportError:
    import queue as Queue

from .core import run

try:
    import multiprocessing

    CPU_COUNT = multiprocessing.cpu_count()

except (ImportError, NotImplementedError):
    CPU_COUNT = 1

LOGGER = logging.getLogger('pylama')

class Worker(threading.Thread):

    """ Get tasks from queue and run. """

    def __init__(self, path_queue, result_queue):
        threading.Thread.__init__(self)
        self.path_queue = path_queue
        self.result_queue = result_queue

    def run(self):
        """ Run tasks from queue. """
        while True:
            path, params = self.path_queue.get()
            errors = check_path(path, **params)
            self.result_queue.put(errors)
            self.path_queue.task_done()

def async_check_files(paths, options, rootpath=None):
    """ Check paths.

    :return list: list of errors

    """

    errors = []

    # Disable async if pylint enabled
    async = options.async and not 'pylint' in options.linters

    if not async:
        for path in paths:
            errors += check_path(path, options=options, rootpath=rootpath)
        return errors

    LOGGER.info('Async code checking is enabled.')
    path_queue = Queue.Queue()
    result_queue = Queue.Queue()

    for _ in range(CPU_COUNT):
        worker = Worker(path_queue, result_queue)
        worker.setDaemon(True)
        worker.start()

    for path in paths:
        path_queue.put((path, dict(options=options, rootpath=rootpath)))

    path_queue.join()

    while True:
        try:
            errors += result_queue.get(False)
        except Queue.Empty:
            break

    return errors

def check_path(path, options=None, rootpath=None, **meta):
    """ Check path.

    :return list: list of errors

    """

    LOGGER.info("Parse file: %s", path)
    config = dict()
    if rootpath is None:
        rootpath = '.'

    for mask in options.file_params:
        if mask.match(path):
            config.update(options.file_params[mask])

    errors = []
    for error in run(
        path, ignore=options.ignore, select=options.select,
        linters=options.linters, complexity=options.complexity,
            config=config, **meta):
        try:
            error['rel'] = op.relpath(error['filename'], rootpath)
            error['col'] = error.get('col', 1)
            errors.append(error)
        except KeyError:
            continue
    return errors
<|endoftext|>


<commit_before># This file is part of Maker Keeper Framework.
#
# Copyright (C) 2018 reverendus
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import getpass
from typing import Optional

from eth_account import Account
from web3 import Web3
from web3.middleware import construct_sign_and_send_raw_middleware

from pymaker import Address

_registered_accounts = {}

def register_keys(web3: Web3, keys: Optional[list]):
    for key in keys or []:
        register_key(web3, key)

def register_key(web3: Web3, key: str):
    assert(isinstance(web3, Web3))

    parsed = {}
    for p in key.split(","):
        var, val = p.split("=")
        parsed[var] = val

    register_key_file(web3, parsed.get('key_file'), parsed.get('pass_file', None))

def register_key_file(web3: Web3, key_file: str, pass_file: Optional[str] = None):
    assert(isinstance(web3, Web3))
    assert(isinstance(key_file, str))
    assert(isinstance(pass_file, str) or (pass_file is None))

    with open(key_file) as key_file_open:
        read_key = key_file_open.read()
        if pass_file:
            with open(pass_file) as pass_file_open:
                read_pass = pass_file_open.read().replace("\n", "")
        else:
            read_pass = getpass.getpass()

        private_key = Account.decrypt(read_key, read_pass)
        register_private_key(web3, private_key)

def register_private_key(web3: Web3, private_key):
    assert(isinstance(web3, Web3))

    account = Account.privateKeyToAccount(private_key)

    _registered_accounts[(web3, Address(account.address))] = account
    web3.middleware_stack.add(construct_sign_and_send_raw_middleware(account))
<commit_msg>Including the json filename in the password prompt<commit_after># This file is part of Maker Keeper Framework.
#
# Copyright (C) 2018 reverendus
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import getpass
from typing import Optional

from eth_account import Account
from web3 import Web3
from web3.middleware import construct_sign_and_send_raw_middleware

from pymaker import Address

_registered_accounts = {}

def register_keys(web3: Web3, keys: Optional[list]):
    for key in keys or []:
        register_key(web3, key)

def register_key(web3: Web3, key: str):
    assert(isinstance(web3, Web3))

    parsed = {}
    for p in key.split(","):
        var, val = p.split("=")
        parsed[var] = val

    register_key_file(web3, parsed.get('key_file'), parsed.get('pass_file', None))

def register_key_file(web3: Web3, key_file: str, pass_file: Optional[str] = None):
    assert(isinstance(web3, Web3))
    assert(isinstance(key_file, str))
    assert(isinstance(pass_file, str) or (pass_file is None))

    with open(key_file) as key_file_open:
        read_key = key_file_open.read()
        if pass_file:
            with open(pass_file) as pass_file_open:
                read_pass = pass_file_open.read().replace("\n", "")
        else:
            read_pass = getpass.getpass(prompt=f"Password for {key_file}: ")

        private_key = Account.decrypt(read_key, read_pass)
        register_private_key(web3, private_key)

def register_private_key(web3: Web3, private_key):
    assert(isinstance(web3, Web3))

    account = Account.privateKeyToAccount(private_key)

    _registered_accounts[(web3, Address(account.address))] = account
    web3.middleware_stack.add(construct_sign_and_send_raw_middleware(account))
<|endoftext|>