<commit_before># -*- coding: utf-8 -*-
import numpy as np

__author__ = "Romain Mormont <r.mormont@student.ulg.ac.be>"

from abc import ABCMeta, abstractmethod

def emplace(src, dest, mapping):
    """Place the values or src into dest at the indexes indicated by the mapping

    Parameters
    ----------
    src: list (size: n)
        Elements to emplace into the dest list
    dest: list (size: m)
        The list in which the elements of src must be placed
    mapping: list (size: n)
        The indexes of dest where the elements of src must be placed
    """
    for index, value in zip(mapping, src):
        dest[index] = value

def take(src, idx):
    """Generate a list containing the elements of src of which the index is contained in idx

    Parameters
    ----------
    src: list (size: n)
        Source list from which elements must be taken
    idx: list (size: m)
        Index list (indexes should be integers in [0, n[

    Returns
    -------
    list: list
        The list of taken elements
    """
    return [src[i] for i in idx]

class DispatchingRule(object):
    """
    An object for dispatching polygons
    """
    __metaclass__ = ABCMeta

    @abstractmethod
    def evaluate(self, image, polygon):
        """Evaluate a polygon

        Parameters
        ----------
        image: Image
            The image from which is extracted the polygon
        polygon: shapely.geometry.Polygon
            The polygon to evaluate

        Returns
        -------
        result: bool
            True if the dispatching rule matched the polygon, False otherwise.
        """
        pass

    def evaluate_batch(self, image, polygons):
        """Evaluate the polygons

        Parameters
        ----------
        image: Image
            The image from which is extracted the polygons
        polygons: list
            The list of polygons to dispatch

        Returns
        -------
        result: list of bool
            List of which the ith element is True if the ith polygon is evaluated to True, False otherwise
        """
        return [self.evaluate(image, polygon) for polygon in polygons]

class DispatcherClassifier(object):
    """
    A dispatcher classifier is an object that evaluates a set of dispatching rules on a polygon
    and that passes this polygon to the associated polygon classifier according to
    the first matching rule.
    """

    def __init__(self, predicates, classifiers, fail_callback=None):
        """Constructor for ClassifierDispatcher object

        Parameters
        ----------
        predicates: list of DispatchingRule objects, size : N
            A list of ordered DispatchingRule implementing the polygon dispatching logic.
        classifiers: list of PolygonClassifiers objects, size : N
            A list of classifiers associated with the predicates.
        fail_callback: callable, optional (default: None)
            A callback to which is passed the polygon if none of the predicates returns True.
            This callback should return the value expected for a polygon which doesn't match any rule.
            If the default value is passed, a custom callable which always returns None is crafted.
        """
        self._predicates = predicates
        self._classifiers = classifiers
        self._fail_callback = fail_callback if fail_callback is not None else (lambda x: None)

    def dispatch_classify(self, image, polygon):
        """Dispatch the polygon to its corresponding classifier according to the dispatching rules,
        then compute and return the associated prediction.

        Parameters
        ----------
        image: Image
            The image to which belongs the polygon
        polygon: shapely.geometry.Polygon
            The polygon of which the class must be predicted

        Returns
        -------
        prediction: int|None
            An integer code indicating the predicted class.
            If none of the dispatching rules matched a polygon, the value returned is the one produced
            by the fail callback for the given polygon. Especially, if no fail callback was registered,
            None is returned.
        dispatch: int
            The index of the dispatch rule that matched the polygon, -1 if none did
        """
        matching_rule = self._dispatch_index(image, polygon)
        if matching_rule == -1:
            return self._fail_callback(polygon), matching_rule
        else:
            return self._classifiers[matching_rule].predict(image, polygon), matching_rule

    def dispatch_classify_batch(self, image, polygons):
        """Apply the dispatching and classification steps to an ensemble of polygons.

        Parameters
        ----------
        image: Image
            The image to which belongs the polygon
        polygons: list of shapely.geometry.Polygon
            The polygons of which the classes must be predicted

        Returns
        -------
        predictions: list of int|None
            A list of integer codes indicating the predicted classes.
            If none of the dispatching rules matched the polygon, the prediction associated with it is the one produced
            by the fail callback for the given polygon. Especially, if no fail callback was registered, None is
            returned.
        dispatches: list of int
            A list of integers of which the ith one is the index of first rule that matched the ith polygon.
        """
        match_dict = dict()  # maps rule indexes with matching polygons
        poly_ind_dict = dict()  # maps rule indexes with index of the polygons in the passed array
        indexes = np.arange(len(polygons))
        # check which rule matched the polygons
        for i, rule in enumerate(self._predicates):
            if indexes.shape[0] == 0:  # if there are no more elements to evaluate
                break
            match, no_match = self._split_by_rule(image, rule, polygons, indexes)
            if len(match) > 0: # skip if there are no match
                match_dict[i] = match_dict.get(i, []) + take(polygons, match)
                poly_ind_dict[i] = poly_ind_dict.get(i, []) + list(match)
                indexes = np.setdiff1d(indexes, match, True)

        # add all polygons that didn't match any rule
        match_dict[-1] = take(polygons, indexes)
        poly_ind_dict[-1] = indexes

        # compute the prediction
        predict_list = [None] * len(polygons)
        dispatch_list = [None] * len(polygons)
        for index in match_dict.keys():
            if index == -1:
                predictions = [self._fail_callback(polygon) for polygon in match_dict[index]]
            else:
                predictions = self._classifiers[index].predict_batch(image, match_dict[index])
            # emplace prediction in prediction list
            emplace(predictions, predict_list, poly_ind_dict[index])
            # emplace dispatch id in dispatch list
            emplace(np.full((len(predictions),), index), dispatch_list, poly_ind_dict[index])
        return predict_list, dispatch_list

    def _split_by_rule(self, image, rule, polygons, poly_indexes):
        """Given a rule, splits all the poly_indexes list into two lists. The first list contains
        the indexes corresponding to the polygons that were evaluated True by the rule, the indexes that
        were evaluated False by the rule.

        Parameters
        ----------
        image: Image
            The image from which were extracted the polygons
        rule: DispatchingRule
            The rule with which the polygons must be evaluated
        polygons: list of Polygon
            The list of polygons
        poly_indexes: list of int
            The indexes of the polygons from the list polygons to process

        Returns
        -------
        true_list: list of int
            The indexes that were evaluated true
        false_list: list of int
            The indexes that were evaluated false
        """
        polygons_to_evaluate = take(polygons, poly_indexes)
        eval_results = rule.evaluate_batch(image, polygons_to_evaluate)
        np_indexes = np.array(poly_indexes)
        return np_indexes[np.where(eval_results)], np_indexes[np.where(np.logical_not(eval_results))]

    def _dispatch_index(self, image, polygon):
        """Return the index of the first rule that matched the polygon. Return -1 if none of the rules matched.
        """
        for i, rule in enumerate(self._predicates):
            if rule.evaluate(image, polygon):
                return i
        return -1
<commit_msg>remove warning by passing explicit type to np.full<commit_after># -*- coding: utf-8 -*-
import numpy as np

__author__ = "Romain Mormont <r.mormont@student.ulg.ac.be>"

from abc import ABCMeta, abstractmethod

def emplace(src, dest, mapping):
    """Place the values or src into dest at the indexes indicated by the mapping

    Parameters
    ----------
    src: list (size: n)
        Elements to emplace into the dest list
    dest: list (size: m)
        The list in which the elements of src must be placed
    mapping: list (size: n)
        The indexes of dest where the elements of src must be placed
    """
    for index, value in zip(mapping, src):
        dest[index] = value

def take(src, idx):
    """Generate a list containing the elements of src of which the index is contained in idx

    Parameters
    ----------
    src: list (size: n)
        Source list from which elements must be taken
    idx: list (size: m)
        Index list (indexes should be integers in [0, n[

    Returns
    -------
    list: list
        The list of taken elements
    """
    return [src[i] for i in idx]

class DispatchingRule(object):
    """
    An object for dispatching polygons
    """
    __metaclass__ = ABCMeta

    @abstractmethod
    def evaluate(self, image, polygon):
        """Evaluate a polygon

        Parameters
        ----------
        image: Image
            The image from which is extracted the polygon
        polygon: shapely.geometry.Polygon
            The polygon to evaluate

        Returns
        -------
        result: bool
            True if the dispatching rule matched the polygon, False otherwise.
        """
        pass

    def evaluate_batch(self, image, polygons):
        """Evaluate the polygons

        Parameters
        ----------
        image: Image
            The image from which is extracted the polygons
        polygons: list
            The list of polygons to dispatch

        Returns
        -------
        result: list of bool
            List of which the ith element is True if the ith polygon is evaluated to True, False otherwise
        """
        return [self.evaluate(image, polygon) for polygon in polygons]

class DispatcherClassifier(object):
    """
    A dispatcher classifier is an object that evaluates a set of dispatching rules on a polygon
    and that passes this polygon to the associated polygon classifier according to
    the first matching rule.
    """

    def __init__(self, predicates, classifiers, fail_callback=None):
        """Constructor for ClassifierDispatcher object

        Parameters
        ----------
        predicates: list of DispatchingRule objects, size : N
            A list of ordered DispatchingRule implementing the polygon dispatching logic.
        classifiers: list of PolygonClassifiers objects, size : N
            A list of classifiers associated with the predicates.
        fail_callback: callable, optional (default: None)
            A callback to which is passed the polygon if none of the predicates returns True.
            This callback should return the value expected for a polygon which doesn't match any rule.
            If the default value is passed, a custom callable which always returns None is crafted.
        """
        self._predicates = predicates
        self._classifiers = classifiers
        self._fail_callback = fail_callback if fail_callback is not None else (lambda x: None)

    def dispatch_classify(self, image, polygon):
        """Dispatch the polygon to its corresponding classifier according to the dispatching rules,
        then compute and return the associated prediction.

        Parameters
        ----------
        image: Image
            The image to which belongs the polygon
        polygon: shapely.geometry.Polygon
            The polygon of which the class must be predicted

        Returns
        -------
        prediction: int|None
            An integer code indicating the predicted class.
            If none of the dispatching rules matched a polygon, the value returned is the one produced
            by the fail callback for the given polygon. Especially, if no fail callback was registered,
            None is returned.
        dispatch: int
            The index of the dispatch rule that matched the polygon, -1 if none did
        """
        matching_rule = self._dispatch_index(image, polygon)
        if matching_rule == -1:
            return self._fail_callback(polygon), matching_rule
        else:
            return self._classifiers[matching_rule].predict(image, polygon), matching_rule

    def dispatch_classify_batch(self, image, polygons):
        """Apply the dispatching and classification steps to an ensemble of polygons.

        Parameters
        ----------
        image: Image
            The image to which belongs the polygon
        polygons: list of shapely.geometry.Polygon
            The polygons of which the classes must be predicted

        Returns
        -------
        predictions: list of int|None
            A list of integer codes indicating the predicted classes.
            If none of the dispatching rules matched the polygon, the prediction associated with it is the one produced
            by the fail callback for the given polygon. Especially, if no fail callback was registered, None is
            returned.
        dispatches: list of int
            A list of integers of which the ith one is the index of first rule that matched the ith polygon.
        """
        match_dict = dict()  # maps rule indexes with matching polygons
        poly_ind_dict = dict()  # maps rule indexes with index of the polygons in the passed array
        indexes = np.arange(len(polygons))
        # check which rule matched the polygons
        for i, rule in enumerate(self._predicates):
            if indexes.shape[0] == 0:  # if there are no more elements to evaluate
                break
            match, no_match = self._split_by_rule(image, rule, polygons, indexes)
            if len(match) > 0: # skip if there are no match
                match_dict[i] = match_dict.get(i, []) + take(polygons, match)
                poly_ind_dict[i] = poly_ind_dict.get(i, []) + list(match)
                indexes = np.setdiff1d(indexes, match, True)

        # add all polygons that didn't match any rule
        match_dict[-1] = take(polygons, indexes)
        poly_ind_dict[-1] = indexes

        # compute the prediction
        predict_list = [None] * len(polygons)
        dispatch_list = [None] * len(polygons)
        for index in match_dict.keys():
            if index == -1:
                predictions = [self._fail_callback(polygon) for polygon in match_dict[index]]
            else:
                predictions = self._classifiers[index].predict_batch(image, match_dict[index])
            # emplace prediction in prediction list
            emplace(predictions, predict_list, poly_ind_dict[index])
            # emplace dispatch id in dispatch list
            emplace(np.full((len(predictions),), index).astype('int'), dispatch_list, poly_ind_dict[index])
        return predict_list, dispatch_list

    def _split_by_rule(self, image, rule, polygons, poly_indexes):
        """Given a rule, splits all the poly_indexes list into two lists. The first list contains
        the indexes corresponding to the polygons that were evaluated True by the rule, the indexes that
        were evaluated False by the rule.

        Parameters
        ----------
        image: Image
            The image from which were extracted the polygons
        rule: DispatchingRule
            The rule with which the polygons must be evaluated
        polygons: list of Polygon
            The list of polygons
        poly_indexes: list of int
            The indexes of the polygons from the list polygons to process

        Returns
        -------
        true_list: list of int
            The indexes that were evaluated true
        false_list: list of int
            The indexes that were evaluated false
        """
        polygons_to_evaluate = take(polygons, poly_indexes)
        eval_results = rule.evaluate_batch(image, polygons_to_evaluate)
        np_indexes = np.array(poly_indexes)
        return np_indexes[np.where(eval_results)], np_indexes[np.where(np.logical_not(eval_results))]

    def _dispatch_index(self, image, polygon):
        """Return the index of the first rule that matched the polygon. Return -1 if none of the rules matched.
        """
        for i, rule in enumerate(self._predicates):
            if rule.evaluate(image, polygon):
                return i
        return -1
<|endoftext|>


<commit_before>from datetime import timedelta

from django.db import models, connection
from django.conf import settings
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

from django_extensions.db.fields import (
    ModificationDateTimeField, CreationDateTimeField)
from djchoices.choices import DjangoChoices, ChoiceItem
from tenant_extras.utils import TenantLanguage

from bluebottle.bb_metrics.utils import bb_track
from bluebottle.clients import properties
from bluebottle.clients.utils import tenant_url
from bluebottle.utils.managers import UpdateSignalsQuerySet
from bluebottle.utils.utils import PreviousStatusMixin
from bluebottle.utils.email_backend import send_mail

GROUP_PERMS = {
    'Staff': {
        'perms': (
            'add_task', 'change_task', 'delete_task',
            'add_taskmember', 'change_taskmember', 'delete_taskmember',
        )
    }
}

class Task(models.Model, PreviousStatusMixin):
    class TaskStatuses(DjangoChoices):
        open = ChoiceItem('open', label=_('Open'))
        in_progress = ChoiceItem('in progress', label=_('Running'))
        realized = ChoiceItem('realized', label=_('Realised'))
        closed = ChoiceItem('closed', label=_('Closed'))

    class TaskTypes(DjangoChoices):
        ongoing = ChoiceItem('ongoing', label=_('Ongoing (with deadline)'))
        event = ChoiceItem('event', label=_('Event (on set date)'))

    title = models.CharField(_('title'), max_length=100)
    description = models.TextField(_('description'))
    location = models.CharField(_('location'), max_length=200, null=True,
                                blank=True)
    people_needed = models.PositiveIntegerField(_('people needed'), default=1)

    project = models.ForeignKey('projects.Project')
    # See Django docs on issues with related name and an (abstract) base class:
    # https://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name
    author = models.ForeignKey('members.Member',
                               related_name='%(app_label)s_%(class)s_related')
    status = models.CharField(_('status'), max_length=20,
                              choices=TaskStatuses.choices,
                              default=TaskStatuses.open)
    type = models.CharField(_('type'), max_length=20,
                            choices=TaskTypes.choices,
                            default=TaskTypes.ongoing)

    date_status_change = models.DateTimeField(_('date status change'),
                                              blank=True, null=True)

    deadline = models.DateTimeField(_('deadline'), help_text=_('Deadline or event date'))

    objects = UpdateSignalsQuerySet.as_manager()

    # required resources
    time_needed = models.FloatField(
        _('time_needed'),
        help_text=_('Estimated number of hours needed to perform this task.'))

    skill = models.ForeignKey('tasks.Skill',
                              verbose_name=_('Skill needed'), null=True)

    # internal usage
    created = CreationDateTimeField(
        _('created'), help_text=_('When this task was created?'))
    updated = ModificationDateTimeField(_('updated'))

    class Meta:
        verbose_name = _(u'task')
        verbose_name_plural = _(u'tasks')

        ordering = ['-created']

    def __unicode__(self):
        return self.title

    def set_in_progress(self):
        self.status = self.TaskStatuses.in_progress
        self.save()

    def set_open(self):
        self.status = self.TaskStatuses.open
        self.save()

    def task_member_realized(self):
        """
        Called if a task member is realized. Now check if the other members
        are also realized and the deadline has expired. If so, then the task
        should also be realized. Members who are rejected, stopped, realized
        withdrew or applied can be ignored as these are not seen as active members.
        """
        if self.status == self.TaskStatuses.realized or self.deadline > timezone.now():
            return

        accepted_count = TaskMember.objects.filter(
            task=self,
            status__in=('accepted',)
        ).count()

        if accepted_count == 0:
            self.status = self.TaskStatuses.realized
            self.save()

    @property
    def members_applied(self):
        return self.members.exclude(status__in=['stopped', 'withdrew'])

    @property
    def people_applied(self):
        return self.members_applied.count()

    @property
    def people_accepted(self):
        members = self.members.filter(status__in=['accepted', 'realized'])
        total_externals = 0
        for member in members:
            total_externals += member.externals
        return members.count() + total_externals

    def get_absolute_url(self):
        """ Get the URL for the current task. """
        return 'https://{}/tasks/{}'.format(properties.tenant.domain_url, self.id)

    # This could also belong to bb_tasks.models but we need the actual, non-abstract
    # model for the signal handling anyway. Eventually, tasks/bb_tasks will have to be
    # merged.
    def deadline_reached(self):
        """
        The task deadline has been reached. Set it to realised and notify the
        owner
        """
        # send "The deadline of your task" - mail

        if self.status == 'in progress':
            self.status = 'realized'
        else:
            self.status = 'closed'
        self.save()

        data = {
            "Task": self.title,
            "Author": self.author.username
        }
        bb_track("Task Deadline Reached", data)

    def status_changed(self, oldstate, newstate):
        """ called by post_save signal handler, if status changed """
        # confirm everything with task owner

        if oldstate in ("in progress", "open", "closed") and newstate == "realized":
            self.project.check_task_status()

            with TenantLanguage(self.author.primary_language):
                subject = _("The status of your task '{0}' is set to realized").format(self.title)
                second_subject = _("Don't forget to confirm the participants of your task!")
                third_subject = _("Last chance to confirm the participants of your task")

            # Immediately send email about realized task
            send_task_realized_mail(self, 'task_status_realized', subject, connection.tenant)

            if getattr(settings, 'CELERY_RESULT_BACKEND', None):
                #  And schedule two more mails (in  3 and 6 days)
                send_task_realized_mail.apply_async(
                    [self, 'task_status_realized_reminder', second_subject, connection.tenant],
                    eta=timezone.now() + timedelta(minutes=settings.REMINDER_MAIL_DELAY)
                )
                send_task_realized_mail.apply_async(
                    [self, 'task_status_realized_second_reminder', third_subject, connection.tenant],
                    eta=timezone.now() + timedelta(minutes=2 * settings.REMINDER_MAIL_DELAY)
                )

        if oldstate in ("in progress", "open") and newstate == "closed":
            with TenantLanguage(self.author.primary_language):
                subject = _("The status of your task '{0}' is set to closed").format(self.title)

            send_mail(
                template_name="tasks/mails/task_status_closed.mail",
                subject=subject,
                title=self.title,
                to=self.author,
                site=tenant_url(),
                link='/go/tasks/{0}'.format(self.id)
            )

        if oldstate in ("in progress", "open") and newstate in ("realized", "closed"):
            data = {
                "Task": self.title,
                "Author": self.author.username,
                "Old status": oldstate,
                "New status": newstate
            }

            bb_track("Task Completed", data)

    def save(self, *args, **kwargs):
        if not self.author_id:
            self.author = self.project.owner

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

class Skill(models.Model):
    name = models.CharField(_('english name'), max_length=100, unique=True)
    description = models.TextField(_('description'), blank=True)
    disabled = models.BooleanField(_('disabled'), default=False)

    @property
    def localized_name(self):
        return _(self.name)

    def __unicode__(self):
        return unicode(self.localized_name)

    class Meta:
        ordering = ('id',)

class TaskMember(models.Model, PreviousStatusMixin):
    class TaskMemberStatuses(DjangoChoices):
        applied = ChoiceItem('applied', label=_('Applied'))
        accepted = ChoiceItem('accepted', label=_('Accepted'))
        rejected = ChoiceItem('rejected', label=_('Rejected'))
        stopped = ChoiceItem('stopped', label=_('Stopped'))
        withdrew = ChoiceItem('withdrew', label=_('Withdrew'))
        realized = ChoiceItem('realized', label=_('Realised'))

    member = models.ForeignKey('members.Member',
                               related_name='%(app_label)s_%(class)s_related')
    task = models.ForeignKey('tasks.Task', related_name="members")
    status = models.CharField(_('status'), max_length=20,
                              choices=TaskMemberStatuses.choices,
                              default=TaskMemberStatuses.applied)
    motivation = models.TextField(
        _('Motivation'), help_text=_('Motivation by applicant.'), blank=True)
    comment = models.TextField(_('Comment'),
                               help_text=_('Comment by task owner.'),
                               blank=True)
    time_spent = models.PositiveSmallIntegerField(
        _('time spent'), default=0,
        help_text=_('Time spent executing this task.'))

    externals = models.PositiveSmallIntegerField(
        _('Externals'), default=0,
        help_text=_('External people helping for this task'))

    created = CreationDateTimeField(_('created'))
    updated = ModificationDateTimeField(_('updated'))

    _initial_status = None

    objects = UpdateSignalsQuerySet.as_manager()

    class Meta:
        verbose_name = _(u'task member')
        verbose_name_plural = _(u'task members')

    def delete(self, using=None, keep_parents=False):
        super(TaskMember, self).delete(using=using, keep_parents=keep_parents)

    @property
    def time_applied_for(self):
        return self.task.time_needed

    @property
    def project(self):
        return self.task.project

class TaskFile(models.Model):
    author = models.ForeignKey('members.Member',
                               related_name='%(app_label)s_%(class)s_related')
    title = models.CharField(max_length=255)
    file = models.FileField(_('file'), upload_to='task_files/')
    created = CreationDateTimeField(_('created'))
    updated = ModificationDateTimeField(_('Updated'))
    task = models.ForeignKey('tasks.Task', related_name="files")

    class Meta:
        verbose_name = _(u'task file')
        verbose_name_plural = _(u'task files')

class TaskStatusLog(models.Model):
    task = models.ForeignKey('tasks.Task')
    status = models.CharField(_('status'), max_length=20)
    start = CreationDateTimeField(
        _('created'), help_text=_('When this task entered in this status.'))

    class Analytics:
        type = 'task'
        tags = {
            'status': 'status',
            'theme': {
                'task.project.theme.name': {'translate': True}
            },
            'theme_slug': 'task.project.theme.slug',
            'location': 'task.project.location.name',
            'location_group': 'task.project.location.group.name',
            'country': 'task.project.country_name'
        }
        fields = {
            'id': 'task.id',
            'project_id': 'task.project.id',
            'user_id': 'task.author.id',
        }

        @staticmethod
        def timestamp(obj, created):
            return obj.start

class TaskMemberStatusLog(models.Model):
    task_member = models.ForeignKey('tasks.TaskMember')
    status = models.CharField(_('status'), max_length=20)
    start = CreationDateTimeField(
        _('created'), help_text=_('When this task member entered in this status.'))

    class Analytics:
        type = 'task_member'
        tags = {
            'status': 'status',
            'location': 'task_member.project.location.name',
            'location_group': 'task_member.project.location.group.name',
            'country': 'task_member.project.country_name',
            'theme': {
                'task_member.project.theme.name': {'translate': True}
            },
            'theme_slug': 'task_member.project.theme.slug',
        }
        fields = {
            'id': 'task_member.id',
            'task_id': 'task_member.task.id',
            'project_id': 'task_member.project.id',
            'user_id': 'task_member.member.id',
        }

        @staticmethod
        def extra_fields(obj, created):
            # Force the time_spent to an int.
            return {'hours': int(obj.task_member.time_spent)}

        @staticmethod
        def timestamp(obj, created):
            return obj.start

from .taskmail import send_task_realized_mail  # noqa
from .taskwallmails import *  # noqa
from .signals import *  # noqa
<commit_msg>Fix translation of reminder mail subjects<commit_after>from datetime import timedelta

from django.db import models, connection
from django.conf import settings
from django.utils import timezone
from django.utils.translation import ugettext, ugettext_lazy as _

from django_extensions.db.fields import (
    ModificationDateTimeField, CreationDateTimeField)
from djchoices.choices import DjangoChoices, ChoiceItem
from tenant_extras.utils import TenantLanguage

from bluebottle.bb_metrics.utils import bb_track
from bluebottle.clients import properties
from bluebottle.clients.utils import tenant_url
from bluebottle.utils.managers import UpdateSignalsQuerySet
from bluebottle.utils.utils import PreviousStatusMixin
from bluebottle.utils.email_backend import send_mail

GROUP_PERMS = {
    'Staff': {
        'perms': (
            'add_task', 'change_task', 'delete_task',
            'add_taskmember', 'change_taskmember', 'delete_taskmember',
        )
    }
}

class Task(models.Model, PreviousStatusMixin):
    class TaskStatuses(DjangoChoices):
        open = ChoiceItem('open', label=_('Open'))
        in_progress = ChoiceItem('in progress', label=_('Running'))
        realized = ChoiceItem('realized', label=_('Realised'))
        closed = ChoiceItem('closed', label=_('Closed'))

    class TaskTypes(DjangoChoices):
        ongoing = ChoiceItem('ongoing', label=_('Ongoing (with deadline)'))
        event = ChoiceItem('event', label=_('Event (on set date)'))

    title = models.CharField(_('title'), max_length=100)
    description = models.TextField(_('description'))
    location = models.CharField(_('location'), max_length=200, null=True,
                                blank=True)
    people_needed = models.PositiveIntegerField(_('people needed'), default=1)

    project = models.ForeignKey('projects.Project')
    # See Django docs on issues with related name and an (abstract) base class:
    # https://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name
    author = models.ForeignKey('members.Member',
                               related_name='%(app_label)s_%(class)s_related')
    status = models.CharField(_('status'), max_length=20,
                              choices=TaskStatuses.choices,
                              default=TaskStatuses.open)
    type = models.CharField(_('type'), max_length=20,
                            choices=TaskTypes.choices,
                            default=TaskTypes.ongoing)

    date_status_change = models.DateTimeField(_('date status change'),
                                              blank=True, null=True)

    deadline = models.DateTimeField(_('deadline'), help_text=_('Deadline or event date'))

    objects = UpdateSignalsQuerySet.as_manager()

    # required resources
    time_needed = models.FloatField(
        _('time_needed'),
        help_text=_('Estimated number of hours needed to perform this task.'))

    skill = models.ForeignKey('tasks.Skill',
                              verbose_name=_('Skill needed'), null=True)

    # internal usage
    created = CreationDateTimeField(
        _('created'), help_text=_('When this task was created?'))
    updated = ModificationDateTimeField(_('updated'))

    class Meta:
        verbose_name = _(u'task')
        verbose_name_plural = _(u'tasks')

        ordering = ['-created']

    def __unicode__(self):
        return self.title

    def set_in_progress(self):
        self.status = self.TaskStatuses.in_progress
        self.save()

    def set_open(self):
        self.status = self.TaskStatuses.open
        self.save()

    def task_member_realized(self):
        """
        Called if a task member is realized. Now check if the other members
        are also realized and the deadline has expired. If so, then the task
        should also be realized. Members who are rejected, stopped, realized
        withdrew or applied can be ignored as these are not seen as active members.
        """
        if self.status == self.TaskStatuses.realized or self.deadline > timezone.now():
            return

        accepted_count = TaskMember.objects.filter(
            task=self,
            status__in=('accepted',)
        ).count()

        if accepted_count == 0:
            self.status = self.TaskStatuses.realized
            self.save()

    @property
    def members_applied(self):
        return self.members.exclude(status__in=['stopped', 'withdrew'])

    @property
    def people_applied(self):
        return self.members_applied.count()

    @property
    def people_accepted(self):
        members = self.members.filter(status__in=['accepted', 'realized'])
        total_externals = 0
        for member in members:
            total_externals += member.externals
        return members.count() + total_externals

    def get_absolute_url(self):
        """ Get the URL for the current task. """
        return 'https://{}/tasks/{}'.format(properties.tenant.domain_url, self.id)

    # This could also belong to bb_tasks.models but we need the actual, non-abstract
    # model for the signal handling anyway. Eventually, tasks/bb_tasks will have to be
    # merged.
    def deadline_reached(self):
        """
        The task deadline has been reached. Set it to realised and notify the
        owner
        """
        # send "The deadline of your task" - mail

        if self.status == 'in progress':
            self.status = 'realized'
        else:
            self.status = 'closed'
        self.save()

        data = {
            "Task": self.title,
            "Author": self.author.username
        }
        bb_track("Task Deadline Reached", data)

    def status_changed(self, oldstate, newstate):
        """ called by post_save signal handler, if status changed """
        # confirm everything with task owner

        if oldstate in ("in progress", "open", "closed") and newstate == "realized":
            self.project.check_task_status()

            with TenantLanguage(self.author.primary_language):
                subject = ugettext("The status of your task '{0}' is set to realized").format(self.title)
                second_subject = ugettext("Don't forget to confirm the participants of your task!")
                third_subject = ugettext("Last chance to confirm the participants of your task")

            # Immediately send email about realized task
            send_task_realized_mail(self, 'task_status_realized', subject, connection.tenant)

            if getattr(settings, 'CELERY_RESULT_BACKEND', None):
                #  And schedule two more mails (in  3 and 6 days)
                send_task_realized_mail.apply_async(
                    [self, 'task_status_realized_reminder', second_subject, connection.tenant],
                    eta=timezone.now() + timedelta(minutes=settings.REMINDER_MAIL_DELAY)
                )
                send_task_realized_mail.apply_async(
                    [self, 'task_status_realized_second_reminder', third_subject, connection.tenant],
                    eta=timezone.now() + timedelta(minutes=2 * settings.REMINDER_MAIL_DELAY)
                )

        if oldstate in ("in progress", "open") and newstate == "closed":
            with TenantLanguage(self.author.primary_language):
                subject = _("The status of your task '{0}' is set to closed").format(self.title)

            send_mail(
                template_name="tasks/mails/task_status_closed.mail",
                subject=subject,
                title=self.title,
                to=self.author,
                site=tenant_url(),
                link='/go/tasks/{0}'.format(self.id)
            )

        if oldstate in ("in progress", "open") and newstate in ("realized", "closed"):
            data = {
                "Task": self.title,
                "Author": self.author.username,
                "Old status": oldstate,
                "New status": newstate
            }

            bb_track("Task Completed", data)

    def save(self, *args, **kwargs):
        if not self.author_id:
            self.author = self.project.owner

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

class Skill(models.Model):
    name = models.CharField(_('english name'), max_length=100, unique=True)
    description = models.TextField(_('description'), blank=True)
    disabled = models.BooleanField(_('disabled'), default=False)

    @property
    def localized_name(self):
        return _(self.name)

    def __unicode__(self):
        return unicode(self.localized_name)

    class Meta:
        ordering = ('id',)

class TaskMember(models.Model, PreviousStatusMixin):
    class TaskMemberStatuses(DjangoChoices):
        applied = ChoiceItem('applied', label=_('Applied'))
        accepted = ChoiceItem('accepted', label=_('Accepted'))
        rejected = ChoiceItem('rejected', label=_('Rejected'))
        stopped = ChoiceItem('stopped', label=_('Stopped'))
        withdrew = ChoiceItem('withdrew', label=_('Withdrew'))
        realized = ChoiceItem('realized', label=_('Realised'))

    member = models.ForeignKey('members.Member',
                               related_name='%(app_label)s_%(class)s_related')
    task = models.ForeignKey('tasks.Task', related_name="members")
    status = models.CharField(_('status'), max_length=20,
                              choices=TaskMemberStatuses.choices,
                              default=TaskMemberStatuses.applied)
    motivation = models.TextField(
        _('Motivation'), help_text=_('Motivation by applicant.'), blank=True)
    comment = models.TextField(_('Comment'),
                               help_text=_('Comment by task owner.'),
                               blank=True)
    time_spent = models.PositiveSmallIntegerField(
        _('time spent'), default=0,
        help_text=_('Time spent executing this task.'))

    externals = models.PositiveSmallIntegerField(
        _('Externals'), default=0,
        help_text=_('External people helping for this task'))

    created = CreationDateTimeField(_('created'))
    updated = ModificationDateTimeField(_('updated'))

    _initial_status = None

    objects = UpdateSignalsQuerySet.as_manager()

    class Meta:
        verbose_name = _(u'task member')
        verbose_name_plural = _(u'task members')

    def delete(self, using=None, keep_parents=False):
        super(TaskMember, self).delete(using=using, keep_parents=keep_parents)

    @property
    def time_applied_for(self):
        return self.task.time_needed

    @property
    def project(self):
        return self.task.project

class TaskFile(models.Model):
    author = models.ForeignKey('members.Member',
                               related_name='%(app_label)s_%(class)s_related')
    title = models.CharField(max_length=255)
    file = models.FileField(_('file'), upload_to='task_files/')
    created = CreationDateTimeField(_('created'))
    updated = ModificationDateTimeField(_('Updated'))
    task = models.ForeignKey('tasks.Task', related_name="files")

    class Meta:
        verbose_name = _(u'task file')
        verbose_name_plural = _(u'task files')

class TaskStatusLog(models.Model):
    task = models.ForeignKey('tasks.Task')
    status = models.CharField(_('status'), max_length=20)
    start = CreationDateTimeField(
        _('created'), help_text=_('When this task entered in this status.'))

    class Analytics:
        type = 'task'
        tags = {
            'status': 'status',
            'theme': {
                'task.project.theme.name': {'translate': True}
            },
            'theme_slug': 'task.project.theme.slug',
            'location': 'task.project.location.name',
            'location_group': 'task.project.location.group.name',
            'country': 'task.project.country_name'
        }
        fields = {
            'id': 'task.id',
            'project_id': 'task.project.id',
            'user_id': 'task.author.id',
        }

        @staticmethod
        def timestamp(obj, created):
            return obj.start

class TaskMemberStatusLog(models.Model):
    task_member = models.ForeignKey('tasks.TaskMember')
    status = models.CharField(_('status'), max_length=20)
    start = CreationDateTimeField(
        _('created'), help_text=_('When this task member entered in this status.'))

    class Analytics:
        type = 'task_member'
        tags = {
            'status': 'status',
            'location': 'task_member.project.location.name',
            'location_group': 'task_member.project.location.group.name',
            'country': 'task_member.project.country_name',
            'theme': {
                'task_member.project.theme.name': {'translate': True}
            },
            'theme_slug': 'task_member.project.theme.slug',
        }
        fields = {
            'id': 'task_member.id',
            'task_id': 'task_member.task.id',
            'project_id': 'task_member.project.id',
            'user_id': 'task_member.member.id',
        }

        @staticmethod
        def extra_fields(obj, created):
            # Force the time_spent to an int.
            return {'hours': int(obj.task_member.time_spent)}

        @staticmethod
        def timestamp(obj, created):
            return obj.start

from .taskmail import send_task_realized_mail  # noqa
from .taskwallmails import *  # noqa
from .signals import *  # noqa
<|endoftext|>


<commit_before>
import {MingwInstallation} from 'craftr/tools/mingw'
import {GccCompiler} from './gcc'
import {extmacro} from '.'

class MingwCompiler(GccCompiler):

  name = 'mingw'
  ext_exe_macro = extmacro('.exe', '.$(0).exe')

  def __init__(self, mingw, **kwargs):
    super().__init__(**kwargs)
    self.mingw = mingw

def get_compiler(fragment):
  if fragment:
    try:
      index = int(fragment)
    except ValueError:
      inst = MingwInstallation(fragment, None)
    else:
      inst = MingwInstallation.list()[index]
  else:
    inst = MingwInstallation().list()[0]

  print('MinGW {} (gcc-{} v{}) @ {}'.format('x64' if inst.is_64 else 'x86',
      inst.gccinfo['target'], inst.gccinfo['version'], inst.binpath))

  return MingwCompiler(
    inst,
    compiler_env=inst.environ,
    linker_env=inst.environ,
    archiver_env=inst.environ
  )
<commit_msg>fix mingw compiler<commit_after>
import {MingwInstallation} from 'craftr/tools/mingw'
import {GccCompiler} from './gcc'
import {extmacro} from '.'

class MingwCompiler(GccCompiler):

  name = 'mingw'
  ext_exe_macro = extmacro('.exe', '.$(0).exe')

  def __init__(self, mingw, **kwargs):
    super().__init__(**kwargs)
    self.mingw = mingw

def get_compiler(fragment):
  if fragment:
    try:
      index = int(fragment)
    except ValueError:
      inst = MingwInstallation(fragment, None)
    else:
      inst = MingwInstallation.list()[index]
  else:
    inst = MingwInstallation.list()[0]

  print('MinGW {} (gcc-{} v{}) @ {}'.format('x64' if inst.is_64 else 'x86',
      inst.gccinfo['target'], inst.gccinfo['version'], inst.binpath))

  return MingwCompiler(
    inst,
    compiler_env=inst.environ,
    linker_env=inst.environ,
    archiver_env=inst.environ
  )
<|endoftext|>


<commit_before>import datetime, json, os, praw, random, telebot, urllib.request
from helpers import logger
from .. import auth

bot = telebot.TeleBot(auth.Telegram.api_key)
CON = logger.CON
dt = datetime.datetime
r = praw.Reddit(user_agent='Telegram:KumaKaiNi-Py:v1.0.0 (by @rekyuu_senkan)')

# Function to populate top posts of a given subreddit
def get_top_posts (name):

   if not os.path.exists('./subreddits'):
      os.makedirs('./subreddits')
   conf = open('./subreddits/' + name + '.json', 'w', encoding='utf8')

   data = {}
   data['time'] = str(dt.now())
   data['posts'] = []

   sub = r.get_subreddit(name)

   print(CON['log'], "Populating list...")
   pid = 0
   for post in sub.get_hot(limit=25):
      url = post.url
      title = post.title
      data['posts'].append({'pid': pid, 'title': title, 'url': url})
      pid += 1
   json.dump(data, conf, ensure_ascii=False)
   print(CON['log'], "List populated!")

   conf.close()

# Function that sends the image
IMAGE_TYPES = ['jpg', 'jpeg', 'gif', 'png']
def send_sub_image (msg, name):
   while True:
      try:
         conf = open('./subreddits/' + name + '.json', encoding='utf8')
         data = json.loads(conf.read())
         conf.close()
         break
      except:
         print(CON['err'], "List has not been created. Creating.")
         get_top_posts(name)

   # Checks freshness of posts.
   if dt.now() - dt.strptime(data['time'], '%Y-%m-%d %H:%M:%S.%f') >= datetime.timedelta(days=1):
      print(CON['log'], "24 hours have past since last update!")
      get_top_posts(name)

   # Chooses a random listing and downloads the image.
   while True:
      try:
         rand = random.randint(0, len(data['posts']) - 1)
         dl = data['posts'][rand]['url']
         filename = dl.split('/')[-1]
         print(CON['log'], "Downloading", filename + "...")

         if filename.split('.')[-1] not in IMAGE_TYPES:
            # If the file is not an image, it will try again.
            print(CON['err'], "Not an image. Deleting entry and trying again.")
            del data['posts'][rand]
            if len(data['posts']) == 0:
               get_top_posts(name)
         else:
            # Downloads the image.
            if not os.path.exists('./tmp'):
               os.makedirs('./tmp')
            urllib.request.urlretrieve(dl, './tmp/' + filename)

            # Sends the downloaded image and the title.
            photo = open('./tmp/' + filename, 'rb')
            out = str(data['posts'][rand]['title'])
            bot.send_photo(msg.chat.id, photo)
            print(CON['img'], filename)

            bot.send_message(msg.chat.id, out)
            print(CON['msg'], out)

            # Closes the photo and removes it from the system.
            photo.close()
            os.remove(filename)
            del data['posts'][rand]

            conf = open('./subreddits/' + name + '.json', 'w', encoding='utf8')
            json.dump(data, conf, ensure_ascii=False)
            conf.close()
            break
      except:
         print(CON['err'], "Error downloading image. Trying again.")
         raise
         del data['posts'][rand]
         if len(data['posts']) == 0:
            get_top_posts(name)<commit_msg>testing<commit_after>import datetime, json, os, praw, random, telebot, urllib.request
from helpers import logger
from .... import auth

bot = telebot.TeleBot(auth.Telegram.api_key)
CON = logger.CON
dt = datetime.datetime
r = praw.Reddit(user_agent='Telegram:KumaKaiNi-Py:v1.0.0 (by @rekyuu_senkan)')

# Function to populate top posts of a given subreddit
def get_top_posts (name):

   if not os.path.exists('./subreddits'):
      os.makedirs('./subreddits')
   conf = open('./subreddits/' + name + '.json', 'w', encoding='utf8')

   data = {}
   data['time'] = str(dt.now())
   data['posts'] = []

   sub = r.get_subreddit(name)

   print(CON['log'], "Populating list...")
   pid = 0
   for post in sub.get_hot(limit=25):
      url = post.url
      title = post.title
      data['posts'].append({'pid': pid, 'title': title, 'url': url})
      pid += 1
   json.dump(data, conf, ensure_ascii=False)
   print(CON['log'], "List populated!")

   conf.close()

# Function that sends the image
IMAGE_TYPES = ['jpg', 'jpeg', 'gif', 'png']
def send_sub_image (msg, name):
   while True:
      try:
         conf = open('./subreddits/' + name + '.json', encoding='utf8')
         data = json.loads(conf.read())
         conf.close()
         break
      except:
         print(CON['err'], "List has not been created. Creating.")
         get_top_posts(name)

   # Checks freshness of posts.
   if dt.now() - dt.strptime(data['time'], '%Y-%m-%d %H:%M:%S.%f') >= datetime.timedelta(days=1):
      print(CON['log'], "24 hours have past since last update!")
      get_top_posts(name)

   # Chooses a random listing and downloads the image.
   while True:
      try:
         rand = random.randint(0, len(data['posts']) - 1)
         dl = data['posts'][rand]['url']
         filename = dl.split('/')[-1]
         print(CON['log'], "Downloading", filename + "...")

         if filename.split('.')[-1] not in IMAGE_TYPES:
            # If the file is not an image, it will try again.
            print(CON['err'], "Not an image. Deleting entry and trying again.")
            del data['posts'][rand]
            if len(data['posts']) == 0:
               get_top_posts(name)
         else:
            # Downloads the image.
            if not os.path.exists('./tmp'):
               os.makedirs('./tmp')
            urllib.request.urlretrieve(dl, './tmp/' + filename)

            # Sends the downloaded image and the title.
            photo = open('./tmp/' + filename, 'rb')
            out = str(data['posts'][rand]['title'])
            bot.send_photo(msg.chat.id, photo)
            print(CON['img'], filename)

            bot.send_message(msg.chat.id, out)
            print(CON['msg'], out)

            # Closes the photo and removes it from the system.
            photo.close()
            os.remove(filename)
            del data['posts'][rand]

            conf = open('./subreddits/' + name + '.json', 'w', encoding='utf8')
            json.dump(data, conf, ensure_ascii=False)
            conf.close()
            break
      except:
         print(CON['err'], "Error downloading image. Trying again.")
         raise
         del data['posts'][rand]
         if len(data['posts']) == 0:
            get_top_posts(name)<|endoftext|>


<commit_before>from PyQt4.QtGui import QValidator

class IntegerFormatter(object):
    StyleNone, StyleC, StyleAsm = range(3)

    def __init__(self, base=10, style=StyleNone, padding=0, uppercase=False):
        self.base = base
        self._style = self.StyleNone
        self.style = style
        self.padding = padding
        self.uppercase = uppercase

    @property
    def style(self):
        return self._style

    @style.setter
    def style(self, new_style):
        if new_style != self._style:
            if isinstance(new_style, str):
                self._style = self.styleFromName(new_style)
            else:
                self._style = new_style

    @property
    def styleName(self):
        if self.style == self.StyleNone:
            return 'none'
        elif self.style == self.StyleC:
            return 'c'
        elif self.style == self.StyleAsm:
            return 'asm'

    def format(self, value):
        result = ''

        if value != 0:
            current = abs(value)
            while current != 0:
                remainder = current % self.base
                if remainder < 10:
                    result += str(remainder)
                else:
                    result += chr(ord('A' if self.uppercase else 'a') + remainder - 10)
                current = (current - remainder) // self.base

            result = result[::-1]
        else:
            result = '0'

        if self.padding > 0 and self.padding - self._decorationLength() - len(result) > 0:
            result = result.zfill(self.padding - self._decorationLength())

        if value < 0:
            result = '-' + result

        result = self._decorate(result)

        return result

    def parse(self, value):
        value = self._undecorate(value)
        return int(value, self.base)

    def _decorationLength(self):
        if self.style == self.StyleC:
            if self.base in (2, 8, 16):
                return 2
        elif self.style == self.StyleAsm:
            if self.base in (2, 8, 16):
                return 1
        return 0

    def _decorate(self, value):
        if self.style == self.StyleC:
            if self.base == 16:
                return '0x' + value
            elif self.base == 8:
                return '0o' + value
            elif self.base == 2:
                return '0b' + value
        elif self.style == self.StyleAsm:
            if self.base == 16:
                return value + 'h'
            elif self.base == 8:
                return value + 'o'
            elif self.base == 2:
                return value + 'b'
        return value

    def _undecorate(self, value):
        value = value.lower()
        if self.style == self.StyleC:
            if self.base == 16:
                if value.startswith('0x'):
                    return value[2:]
            elif self.base == 8:
                if value.startswith('0o'):
                    return value[2:]
            elif self.base == 2:
                if value.startswith('0b'):
                    return value[2:]
        elif self.style == self.StyleAsm:
            if self.base in (16, 8, 2):
                return value[:-1]
        return value

    @staticmethod
    def guessFormat(text):
        """Returns tuple (style, base) or None when format is not guessed. Method does not detects plain integers
        and C-style octals."""
        text = text.lower()
        if text.startswith('0x'):
            return (IntegerFormatter.StyleC, 16)
        elif text.startswith('0b'):
            return (IntegerFormatter.StyleC, 2)
        elif text.startswith('0o'):
            return (IntegerFormatter.StyleC, 8)
        elif text.endswith('h'):
            return (IntegerFormatter.StyleAsm, 16)
        elif text.endswith('o'):
            return (IntegerFormatter.StyleAsm, 8)
        elif text.endswith('b'):
            return (IntegerFormatter.StyleAsm, 2)
        return None

    def validate(self, text):
        if not text or text == '-':
            return QValidator.Intermediate
        try:
            undecorated = self._undecorate(text)
            if not undecorated:
                return QValidator.Intermediate
            self.parse(text)
            return QValidator.Acceptable
        except ValueError:
            return QValidator.Invalid

    @staticmethod
    def styleFromName(name):
        name = name.lower()
        if name == 'c':
            return IntegerFormatter.StyleC
        elif name in ('asm', 'assembler'):
            return IntegerFormatter.StyleAsm
        return IntegerFormatter.StyleNone

def parseInteger(text):
    guessed = IntegerFormatter.guessFormat(text)
    if guessed is None:
        return IntegerFormatter().parse(text)
    else:
        return IntegerFormatter(guessed[1], guessed[0]).parse(text)

class FloatFormatter(object):
    def __init__(self, precision=12):
        self.precision = precision
        self.align = True

    def format(self, value):
        format_spec = '{0:#' + (str(self.maximalWidth) if self.align else '0') + '.' + str(self.precision) + 'e}'
        return format_spec.format(value)

    def parse(self, value):
        return float(value)

    @property
    def maximalWidth(self):
        return 3 + self.precision + 2 + 3
<commit_msg>fixed: strings with whitespaces should not be accepted for input by int and float formatters<commit_after>from PyQt4.QtGui import QValidator

class IntegerFormatter(object):
    StyleNone, StyleC, StyleAsm = range(3)

    def __init__(self, base=10, style=StyleNone, padding=0, uppercase=False):
        self.base = base
        self._style = self.StyleNone
        self.style = style
        self.padding = padding
        self.uppercase = uppercase

    @property
    def style(self):
        return self._style

    @style.setter
    def style(self, new_style):
        if new_style != self._style:
            if isinstance(new_style, str):
                self._style = self.styleFromName(new_style)
            else:
                self._style = new_style

    @property
    def styleName(self):
        if self.style == self.StyleNone:
            return 'none'
        elif self.style == self.StyleC:
            return 'c'
        elif self.style == self.StyleAsm:
            return 'asm'

    def format(self, value):
        result = ''

        if value != 0:
            current = abs(value)
            while current != 0:
                remainder = current % self.base
                if remainder < 10:
                    result += str(remainder)
                else:
                    result += chr(ord('A' if self.uppercase else 'a') + remainder - 10)
                current = (current - remainder) // self.base

            result = result[::-1]
        else:
            result = '0'

        if self.padding > 0 and self.padding - self._decorationLength() - len(result) > 0:
            result = result.zfill(self.padding - self._decorationLength())

        if value < 0:
            result = '-' + result

        result = self._decorate(result)

        return result

    def parse(self, value):
        value = self._undecorate(value)
        return int(value, self.base)

    def _decorationLength(self):
        if self.style == self.StyleC:
            if self.base in (2, 8, 16):
                return 2
        elif self.style == self.StyleAsm:
            if self.base in (2, 8, 16):
                return 1
        return 0

    def _decorate(self, value):
        if self.style == self.StyleC:
            if self.base == 16:
                return '0x' + value
            elif self.base == 8:
                return '0o' + value
            elif self.base == 2:
                return '0b' + value
        elif self.style == self.StyleAsm:
            if self.base == 16:
                return value + 'h'
            elif self.base == 8:
                return value + 'o'
            elif self.base == 2:
                return value + 'b'
        return value

    def _undecorate(self, value):
        value = value.lower()
        if self.style == self.StyleC:
            if self.base == 16:
                if value.startswith('0x'):
                    return value[2:]
            elif self.base == 8:
                if value.startswith('0o'):
                    return value[2:]
            elif self.base == 2:
                if value.startswith('0b'):
                    return value[2:]
        elif self.style == self.StyleAsm:
            if self.base in (16, 8, 2):
                return value[:-1]
        return value

    @staticmethod
    def guessFormat(text):
        """Returns tuple (style, base) or None when format is not guessed. Method does not detects plain integers
        and C-style octals."""
        text = text.lower()
        if text.startswith('0x'):
            return (IntegerFormatter.StyleC, 16)
        elif text.startswith('0b'):
            return (IntegerFormatter.StyleC, 2)
        elif text.startswith('0o'):
            return (IntegerFormatter.StyleC, 8)
        elif text.endswith('h'):
            return (IntegerFormatter.StyleAsm, 16)
        elif text.endswith('o'):
            return (IntegerFormatter.StyleAsm, 8)
        elif text.endswith('b'):
            return (IntegerFormatter.StyleAsm, 2)
        return None

    def validate(self, text):
        if not text or text == '-':
            return QValidator.Intermediate
        try:
            undecorated = self._undecorate(text)
            if not undecorated:
                return QValidator.Intermediate
            elif undecorated != undecorated.strip():
                return QValidator.Invalid
            self.parse(text)
            return QValidator.Acceptable
        except ValueError:
            return QValidator.Invalid

    @staticmethod
    def styleFromName(name):
        name = name.lower()
        if name == 'c':
            return IntegerFormatter.StyleC
        elif name in ('asm', 'assembler'):
            return IntegerFormatter.StyleAsm
        return IntegerFormatter.StyleNone

def parseInteger(text):
    guessed = IntegerFormatter.guessFormat(text)
    if guessed is None:
        return IntegerFormatter().parse(text)
    else:
        return IntegerFormatter(guessed[1], guessed[0]).parse(text)

class FloatFormatter(object):
    def __init__(self, precision=12):
        self.precision = precision
        self.align = True

    def format(self, value):
        format_spec = '{0:#' + (str(self.maximalWidth) if self.align else '0') + '.' + str(self.precision) + 'e}'
        return format_spec.format(value)

    def parse(self, value):
        if value.strip() != value:
            return QValidator.Invalid
        return float(value)

    @property
    def maximalWidth(self):
        return 3 + self.precision + 2 + 3
<|endoftext|>


<commit_before>"""
Utils.
"""
import inflection
from django.core import urlresolvers
from django.conf import settings
from django.utils import six, encoding
from django.utils.translation import ugettext_lazy as _
from rest_framework.serializers import BaseSerializer, ListSerializer, ModelSerializer
from rest_framework.relations import RelatedField, HyperlinkedRelatedField, PrimaryKeyRelatedField
from rest_framework.settings import api_settings
from rest_framework.exceptions import APIException

from django.utils.six.moves.urllib.parse import urlparse

try:
    from rest_framework.compat import OrderedDict
except ImportError:
    OrderedDict = dict

try:
    from rest_framework.serializers import ManyRelatedField
except ImportError:
    ManyRelatedField = type(None)

def get_resource_name(context):
    """
    Return the name of a resource.
    """
    view = context.get('view')

    # Sanity check to make sure we have a view.
    if not view:
        raise APIException(_('Could not find view.'))

    # Check to see if there is a status code and return early
    # with the resource_name value of `errors`.
    try:
        code = str(view.response.status_code)
    except (AttributeError, ValueError):
        pass
    else:
        if code.startswith('4') or code.startswith('5'):
            return 'errors'

    try:
        resource_name = getattr(view, 'resource_name')
    except AttributeError:
        try:
            # Check the meta class
            resource_name = (getattr(view, 'serializer_class').Meta.resource_name)
        except AttributeError:
            # Use the model
            try:
                resource_name = (getattr(view, 'serializer_class').Meta.model.__name__)
            except AttributeError:
                try:
                    resource_name = view.model.__name__
                except AttributeError:
                    resource_name = view.__class__.__name__

            # if the name was calculated automatically then pluralize and format
            if not isinstance(resource_name, six.string_types):
                return resource_name

            resource_name = inflection.pluralize(resource_name.lower())

            resource_name = format_value(resource_name)

    return resource_name

def get_serializer_fields(serializer):
    if hasattr(serializer, 'child'):
        return getattr(serializer.child, 'fields')
    if hasattr(serializer, 'fields'):
        return getattr(serializer, 'fields')

def format_keys(obj, format_type=None):
    """
    Takes either a dict or list and returns it with camelized keys only if
    JSON_API_FORMAT_KEYS is set.

    :format_type: Either 'dasherize', 'camelize' or 'underscore'
    """
    if format_type is None:
        format_type = getattr(settings, 'JSON_API_FORMAT_KEYS', False)

    if format_type in ('dasherize', 'camelize', 'underscore'):

        if isinstance(obj, dict):
            formatted = OrderedDict()
            for key, value in obj.items():
                if format_type == 'dasherize':
                    formatted[inflection.dasherize(key)] \
                        = format_keys(value, format_type)
                elif format_type == 'camelize':
                    formatted[inflection.camelize(key, False)] \
                        = format_keys(value, format_type)
                elif format_type == 'underscore':
                    formatted[inflection.underscore(key)] \
                        = format_keys(value, format_type)
            return formatted
        if isinstance(obj, list):
            return [format_keys(item, format_type) for item in obj]
        else:
            return obj
    else:
        return obj

def format_value(value, format_type=None):
    format_type = getattr(settings, 'JSON_API_FORMAT_KEYS', False)
    if format_type == 'dasherize':
        value = inflection.dasherize(value)
    elif format_type == 'camelize':
        value = inflection.camelize(value)
    elif format_type == 'underscore':
        value = inflection.underscore(value)
    return value

def build_json_resource_obj(fields, resource, resource_name):
    resource_data = [
        ('type', resource_name),
        ('id', extract_id(fields, resource)),
        ('attributes', extract_attributes(fields, resource)),
    ]
    relationships = extract_relationships(fields, resource)
    if relationships:
        resource_data.append(('relationships', relationships))
    # Add 'self' link if field is present and valid
    if api_settings.URL_FIELD_NAME in resource and \
            isinstance(fields[api_settings.URL_FIELD_NAME], RelatedField):
        resource_data.append(('links', {'self': resource[api_settings.URL_FIELD_NAME]}))
    return OrderedDict(resource_data)

def get_related_resource_type(relation):
    queryset = relation.queryset
    if queryset is not None:
        relation_model = queryset.model
    else:
        parent_serializer = relation.parent
        if hasattr(parent_serializer, 'Meta'):
            parent_model = parent_serializer.Meta.model
        else:
            parent_model = parent_serializer.parent.Meta.model
        parent_model_relation = getattr(
            parent_model,
            (relation.source if relation.source else parent_serializer.field_name)
        )
        if hasattr(parent_model_relation, 'related'):
            relation_model = parent_model_relation.related.model
        elif hasattr(parent_model_relation, 'field'):
            relation_model = parent_model_relation.field.related.model
        else:
            raise APIException('Unable to find related model for relation {relation}'.format(relation=relation))
    return inflection.pluralize(relation_model.__name__).lower()

def extract_id_from_url(url):
    http_prefix = url.startswith(('http:', 'https:'))
    if http_prefix:
        # If needed convert absolute URLs to relative path
        data = urlparse(url).path
        prefix = urlresolvers.get_script_prefix()
        if data.startswith(prefix):
            url = '/' + data[len(prefix):]

    match = urlresolvers.resolve(url)
    return encoding.force_text(match.kwargs['pk'])

def extract_id(fields, resource):
    for field_name, field in six.iteritems(fields):
        if field_name == 'id':
            return encoding.force_text(resource.get(field_name))
        if field_name == api_settings.URL_FIELD_NAME:
            return extract_id_from_url(resource.get(field_name))

def extract_attributes(fields, resource):
    data = OrderedDict()
    for field_name, field in six.iteritems(fields):
        # ID is always provided in the root of JSON API so remove it from attrs
        if field_name == 'id':
            continue
        # Skip fields with relations
        if isinstance(field, (RelatedField, BaseSerializer, ManyRelatedField)):
            continue
        data.update({
            field_name: resource.get(field_name)
        })

    return format_keys(data)

def extract_relationships(fields, resource):
    data = OrderedDict()
    for field_name, field in six.iteritems(fields):
        # Skip URL field
        if field_name == api_settings.URL_FIELD_NAME:
            continue

        # Skip fields without relations
        if not isinstance(field, (RelatedField, ManyRelatedField, BaseSerializer)):
            continue

        if isinstance(field, (PrimaryKeyRelatedField, HyperlinkedRelatedField)):
            relation_type = get_related_resource_type(field)

            if resource.get(field_name) is not None:
                if isinstance(field, PrimaryKeyRelatedField):
                    relation_id = encoding.force_text(resource.get(field_name))
                elif isinstance(field, HyperlinkedRelatedField):
                    relation_id = extract_id_from_url(resource.get(field_name))
            else:
                relation_id = None

            data.update(
                {
                    field_name: {
                        'data': (OrderedDict([
                            ('type', relation_type), ('id', relation_id)
                        ]) if relation_id is not None else None)
                    }
                }
            )
            continue

        if isinstance(field, ManyRelatedField):
            relation_data = list()

            relation = field.child_relation

            relation_type = get_related_resource_type(relation)

            if isinstance(relation, HyperlinkedRelatedField):
                for link in resource.get(field_name, list()):
                    relation_data.append(OrderedDict([('type', relation_type), ('id', extract_id_from_url(link))]))

                data.update({field_name: {'data': relation_data}})
                continue

            if isinstance(relation, PrimaryKeyRelatedField):
                for pk in resource.get(field_name, list()):
                    relation_data.append(OrderedDict([('type', relation_type), ('id', encoding.force_text(pk))]))

                data.update({field_name: {'data': relation_data}})
                continue

        if isinstance(field, ListSerializer):
            relation_data = list()

            serializer = field.child
            relation_model = serializer.Meta.model
            relation_type = inflection.pluralize(relation_model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(serializer)
            serializer_data = resource.get(field_name)
            if isinstance(serializer_data, list):
                for serializer_resource in serializer_data:
                    relation_data.append(
                        OrderedDict([
                            ('type', relation_type), ('id', extract_id(serializer_fields, serializer_resource))
                        ]))

                data.update({field_name: {'data': relation_data}})
                continue

        if isinstance(field, ModelSerializer):
            relation_model = field.Meta.model
            relation_type = inflection.pluralize(relation_model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(field)
            serializer_data = resource.get(field_name)
            data.update({
                field_name: {
                    'data': (
                        OrderedDict([
                            ('type', relation_type),
                            ('id', extract_id(serializer_fields, serializer_data))
                        ]) if resource.get(field_name) else None)
                }
            })
            continue

    return format_keys(data)

def extract_included(fields, resource):
    included_data = list()
    for field_name, field in six.iteritems(fields):
        # Skip URL field
        if field_name == api_settings.URL_FIELD_NAME:
            continue

        # Skip fields without serialized data
        if not isinstance(field, BaseSerializer):
            continue

        if isinstance(field, ListSerializer):

            serializer = field.child
            model = serializer.Meta.model
            relation_type = inflection.pluralize(model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(serializer)
            serializer_data = resource.get(field_name)
            if isinstance(serializer_data, list):
                for serializer_resource in serializer_data:
                    included_data.append(build_json_resource_obj(serializer_fields, serializer_resource, relation_type))

        if isinstance(field, ModelSerializer):

            model = field.Meta.model
            relation_type = inflection.pluralize(model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(field)
            serializer_data = resource.get(field_name)
            if serializer_data:
                included_data.append(build_json_resource_obj(serializer_fields, serializer_data, relation_type))

    return format_keys(included_data)
<commit_msg>Use JSON_API_FORMAT_KEYS when formatting relation types<commit_after>"""
Utils.
"""
import inflection
from django.core import urlresolvers
from django.conf import settings
from django.utils import six, encoding
from django.utils.translation import ugettext_lazy as _
from rest_framework.serializers import BaseSerializer, ListSerializer, ModelSerializer
from rest_framework.relations import RelatedField, HyperlinkedRelatedField, PrimaryKeyRelatedField
from rest_framework.settings import api_settings
from rest_framework.exceptions import APIException

from django.utils.six.moves.urllib.parse import urlparse

try:
    from rest_framework.compat import OrderedDict
except ImportError:
    OrderedDict = dict

try:
    from rest_framework.serializers import ManyRelatedField
except ImportError:
    ManyRelatedField = type(None)

def get_resource_name(context):
    """
    Return the name of a resource.
    """
    view = context.get('view')

    # Sanity check to make sure we have a view.
    if not view:
        raise APIException(_('Could not find view.'))

    # Check to see if there is a status code and return early
    # with the resource_name value of `errors`.
    try:
        code = str(view.response.status_code)
    except (AttributeError, ValueError):
        pass
    else:
        if code.startswith('4') or code.startswith('5'):
            return 'errors'

    try:
        resource_name = getattr(view, 'resource_name')
    except AttributeError:
        try:
            # Check the meta class
            resource_name = (getattr(view, 'serializer_class').Meta.resource_name)
        except AttributeError:
            # Use the model
            try:
                resource_name = (getattr(view, 'serializer_class').Meta.model.__name__)
            except AttributeError:
                try:
                    resource_name = view.model.__name__
                except AttributeError:
                    resource_name = view.__class__.__name__

            # if the name was calculated automatically then pluralize and format
            if not isinstance(resource_name, six.string_types):
                return resource_name

            resource_name = inflection.pluralize(resource_name.lower())

            resource_name = format_value(resource_name)

    return resource_name

def get_serializer_fields(serializer):
    if hasattr(serializer, 'child'):
        return getattr(serializer.child, 'fields')
    if hasattr(serializer, 'fields'):
        return getattr(serializer, 'fields')

def format_keys(obj, format_type=None):
    """
    Takes either a dict or list and returns it with camelized keys only if
    JSON_API_FORMAT_KEYS is set.

    :format_type: Either 'dasherize', 'camelize' or 'underscore'
    """
    if format_type is None:
        format_type = getattr(settings, 'JSON_API_FORMAT_KEYS', False)

    if format_type in ('dasherize', 'camelize', 'underscore'):

        if isinstance(obj, dict):
            formatted = OrderedDict()
            for key, value in obj.items():
                if format_type == 'dasherize':
                    formatted[inflection.dasherize(key)] \
                        = format_keys(value, format_type)
                elif format_type == 'camelize':
                    formatted[inflection.camelize(key, False)] \
                        = format_keys(value, format_type)
                elif format_type == 'underscore':
                    formatted[inflection.underscore(key)] \
                        = format_keys(value, format_type)
            return formatted
        if isinstance(obj, list):
            return [format_keys(item, format_type) for item in obj]
        else:
            return obj
    else:
        return obj

def format_value(value, format_type=None):
    format_type = getattr(settings, 'JSON_API_FORMAT_KEYS', False)
    if format_type == 'dasherize':
        value = inflection.dasherize(value)
    elif format_type == 'camelize':
        value = inflection.camelize(value)
    elif format_type == 'underscore':
        value = inflection.underscore(value)
    return value

def build_json_resource_obj(fields, resource, resource_name):
    resource_data = [
        ('type', resource_name),
        ('id', extract_id(fields, resource)),
        ('attributes', extract_attributes(fields, resource)),
    ]
    relationships = extract_relationships(fields, resource)
    if relationships:
        resource_data.append(('relationships', relationships))
    # Add 'self' link if field is present and valid
    if api_settings.URL_FIELD_NAME in resource and \
            isinstance(fields[api_settings.URL_FIELD_NAME], RelatedField):
        resource_data.append(('links', {'self': resource[api_settings.URL_FIELD_NAME]}))
    return OrderedDict(resource_data)

def get_related_resource_type(relation):
    queryset = relation.queryset
    if queryset is not None:
        relation_model = queryset.model
    else:
        parent_serializer = relation.parent
        if hasattr(parent_serializer, 'Meta'):
            parent_model = parent_serializer.Meta.model
        else:
            parent_model = parent_serializer.parent.Meta.model
        parent_model_relation = getattr(
            parent_model,
            (relation.source if relation.source else parent_serializer.field_name)
        )
        if hasattr(parent_model_relation, 'related'):
            relation_model = parent_model_relation.related.model
        elif hasattr(parent_model_relation, 'field'):
            relation_model = parent_model_relation.field.related.model
        else:
            raise APIException('Unable to find related model for relation {relation}'.format(relation=relation))
    # Format the type according to JSON_API_FORMAT_KEYS
    name = list(format_keys({inflection.underscore(relation_model.__name__): None}).keys())[0]
    return name

def extract_id_from_url(url):
    http_prefix = url.startswith(('http:', 'https:'))
    if http_prefix:
        # If needed convert absolute URLs to relative path
        data = urlparse(url).path
        prefix = urlresolvers.get_script_prefix()
        if data.startswith(prefix):
            url = '/' + data[len(prefix):]

    match = urlresolvers.resolve(url)
    return encoding.force_text(match.kwargs['pk'])

def extract_id(fields, resource):
    for field_name, field in six.iteritems(fields):
        if field_name == 'id':
            return encoding.force_text(resource.get(field_name))
        if field_name == api_settings.URL_FIELD_NAME:
            return extract_id_from_url(resource.get(field_name))

def extract_attributes(fields, resource):
    data = OrderedDict()
    for field_name, field in six.iteritems(fields):
        # ID is always provided in the root of JSON API so remove it from attrs
        if field_name == 'id':
            continue
        # Skip fields with relations
        if isinstance(field, (RelatedField, BaseSerializer, ManyRelatedField)):
            continue
        data.update({
            field_name: resource.get(field_name)
        })

    return format_keys(data)

def extract_relationships(fields, resource):
    data = OrderedDict()
    for field_name, field in six.iteritems(fields):
        # Skip URL field
        if field_name == api_settings.URL_FIELD_NAME:
            continue

        # Skip fields without relations
        if not isinstance(field, (RelatedField, ManyRelatedField, BaseSerializer)):
            continue

        if isinstance(field, (PrimaryKeyRelatedField, HyperlinkedRelatedField)):
            relation_type = get_related_resource_type(field)

            if resource.get(field_name) is not None:
                if isinstance(field, PrimaryKeyRelatedField):
                    relation_id = encoding.force_text(resource.get(field_name))
                elif isinstance(field, HyperlinkedRelatedField):
                    relation_id = extract_id_from_url(resource.get(field_name))
            else:
                relation_id = None

            data.update(
                {
                    field_name: {
                        'data': (OrderedDict([
                            ('type', relation_type), ('id', relation_id)
                        ]) if relation_id is not None else None)
                    }
                }
            )
            continue

        if isinstance(field, ManyRelatedField):
            relation_data = list()

            relation = field.child_relation

            relation_type = get_related_resource_type(relation)

            if isinstance(relation, HyperlinkedRelatedField):
                for link in resource.get(field_name, list()):
                    relation_data.append(OrderedDict([('type', relation_type), ('id', extract_id_from_url(link))]))

                data.update({field_name: {'data': relation_data}})
                continue

            if isinstance(relation, PrimaryKeyRelatedField):
                for pk in resource.get(field_name, list()):
                    relation_data.append(OrderedDict([('type', relation_type), ('id', encoding.force_text(pk))]))

                data.update({field_name: {'data': relation_data}})
                continue

        if isinstance(field, ListSerializer):
            relation_data = list()

            serializer = field.child
            relation_model = serializer.Meta.model
            relation_type = inflection.pluralize(relation_model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(serializer)
            serializer_data = resource.get(field_name)
            if isinstance(serializer_data, list):
                for serializer_resource in serializer_data:
                    relation_data.append(
                        OrderedDict([
                            ('type', relation_type), ('id', extract_id(serializer_fields, serializer_resource))
                        ]))

                data.update({field_name: {'data': relation_data}})
                continue

        if isinstance(field, ModelSerializer):
            relation_model = field.Meta.model
            relation_type = inflection.pluralize(relation_model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(field)
            serializer_data = resource.get(field_name)
            data.update({
                field_name: {
                    'data': (
                        OrderedDict([
                            ('type', relation_type),
                            ('id', extract_id(serializer_fields, serializer_data))
                        ]) if resource.get(field_name) else None)
                }
            })
            continue

    return format_keys(data)

def extract_included(fields, resource):
    included_data = list()
    for field_name, field in six.iteritems(fields):
        # Skip URL field
        if field_name == api_settings.URL_FIELD_NAME:
            continue

        # Skip fields without serialized data
        if not isinstance(field, BaseSerializer):
            continue

        if isinstance(field, ListSerializer):

            serializer = field.child
            model = serializer.Meta.model
            relation_type = inflection.pluralize(model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(serializer)
            serializer_data = resource.get(field_name)
            if isinstance(serializer_data, list):
                for serializer_resource in serializer_data:
                    included_data.append(build_json_resource_obj(serializer_fields, serializer_resource, relation_type))

        if isinstance(field, ModelSerializer):

            model = field.Meta.model
            relation_type = inflection.pluralize(model.__name__).lower()

            # Get the serializer fields
            serializer_fields = get_serializer_fields(field)
            serializer_data = resource.get(field_name)
            if serializer_data:
                included_data.append(build_json_resource_obj(serializer_fields, serializer_data, relation_type))

    return format_keys(included_data)
<|endoftext|>