<commit_before>from django.db import models
from django.db.models.sql.query import LOOKUP_SEP
from django.db.models.deletion import Collector
from django.db.models.related import RelatedObject
from django.forms.forms import pretty_name
from django.utils import formats
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils import timezone
from django.utils.encoding import force_unicode, smart_unicode, smart_str
from django.utils.translation import ungettext
from django.core.urlresolvers import reverse
from django.conf import settings

if 'django.contrib.staticfiles' in settings.INSTALLED_APPS:
    from django.contrib.staticfiles.templatetags.staticfiles import static
else:
    from django.templatetags.static import static

try:
    import json
except ImportError:
    from django.utils import simplejson as json

def lookup_needs_distinct(opts, lookup_path):
    """
    Returns True if 'distinct()' should be used to query the given lookup path.
    """
    field_name = lookup_path.split('__', 1)[0]
    field = opts.get_field_by_name(field_name)[0]
    if ((hasattr(field, 'rel') and
         isinstance(field.rel, models.ManyToManyRel)) or
        (isinstance(field, models.related.RelatedObject) and
         not field.field.unique)):
         return True
    return False

def prepare_lookup_value(key, value):
    """
    Returns a lookup value prepared to be used in queryset filtering.
    """
    # if key ends with __in, split parameter into separate values
    if key.endswith('__in'):
        value = value.split(',')
    # if key ends with __isnull, special case '' and false
    if key.endswith('__isnull'):
        if value.lower() in ('', 'false'):
            value = False
        else:
            value = True
    return value

def quote(s):
    """
    Ensure that primary key values do not confuse the admin URLs by escaping
    any '/', '_' and ':' characters. Similar to urllib.quote, except that the
    quoting is slightly different so that it doesn't get automatically
    unquoted by the Web browser.
    """
    if not isinstance(s, basestring):
        return s
    res = list(s)
    for i in range(len(res)):
        c = res[i]
        if c in """:/_#?;@&=+$,"<>%\\""":
            res[i] = '_%02X' % ord(c)
    return ''.join(res)

def unquote(s):
    """
    Undo the effects of quote(). Based heavily on urllib.unquote().
    """
    if not isinstance(s, basestring):
        return s
    mychr = chr
    myatoi = int
    list = s.split('_')
    res = [list[0]]
    myappend = res.append
    del list[0]
    for item in list:
        if item[1:2]:
            try:
                myappend(mychr(myatoi(item[:2], 16)) + item[2:])
            except ValueError:
                myappend('_' + item)
        else:
            myappend('_' + item)
    return "".join(res)

def flatten_fieldsets(fieldsets):
    """Returns a list of field names from an admin fieldsets structure."""
    field_names = []
    for name, opts in fieldsets:
        for field in opts['fields']:
            # type checking feels dirty, but it seems like the best way here
            if type(field) == tuple:
                field_names.extend(field)
            else:
                field_names.append(field)
    return field_names

def get_deleted_objects(objs, opts, user, admin_site, using):
    """
    Find all objects related to ``objs`` that should also be deleted. ``objs``
    must be a homogenous iterable of objects (e.g. a QuerySet).

    Returns a nested list of strings suitable for display in the
    template with the ``unordered_list`` filter.

    """
    collector = NestedObjects(using=using)
    collector.collect(objs)
    perms_needed = set()

    def format_callback(obj):
        has_admin = obj.__class__ in admin_site._registry
        opts = obj._meta

        if has_admin:
            admin_url = reverse('%s:%s_%s_change'
                                % (admin_site.name,
                                   opts.app_label,
                                   opts.object_name.lower()),
                                None, (quote(obj._get_pk_val()),))
            p = '%s.%s' % (opts.app_label,
                           opts.get_delete_permission())
            if not user.has_perm(p):
                perms_needed.add(opts.verbose_name)
            # Display a link to the admin page.
            return mark_safe(u'%s: %s' %
                             (escape(capfirst(opts.verbose_name)),
                              admin_url,
                              escape(obj)))
        else:
            # Don't display link to edit, because it either has no
            # admin or is edited inline.
            return u'%s: %s' % (capfirst(opts.verbose_name),
                                force_unicode(obj))

    to_delete = collector.nested(format_callback)

    protected = [format_callback(obj) for obj in collector.protected]

    return to_delete, perms_needed, protected

class NestedObjects(Collector):
    def __init__(self, *args, **kwargs):
        super(NestedObjects, self).__init__(*args, **kwargs)
        self.edges = {} # {from_instance: [to_instances]}
        self.protected = set()

    def add_edge(self, source, target):
        self.edges.setdefault(source, []).append(target)

    def collect(self, objs, source_attr=None, **kwargs):
        for obj in objs:
            if source_attr:
                self.add_edge(getattr(obj, source_attr), obj)
            else:
                self.add_edge(None, obj)
        try:
            return super(NestedObjects, self).collect(objs, source_attr=source_attr, **kwargs)
        except models.ProtectedError, e:
            self.protected.update(e.protected_objects)

    def related_objects(self, related, objs):
        qs = super(NestedObjects, self).related_objects(related, objs)
        return qs.select_related(related.field.name)

    def _nested(self, obj, seen, format_callback):
        if obj in seen:
            return []
        seen.add(obj)
        children = []
        for child in self.edges.get(obj, ()):
            children.extend(self._nested(child, seen, format_callback))
        if format_callback:
            ret = [format_callback(obj)]
        else:
            ret = [obj]
        if children:
            ret.append(children)
        return ret

    def nested(self, format_callback=None):
        """
        Return the graph as a nested list.

        """
        seen = set()
        roots = []
        for root in self.edges.get(None, ()):
            roots.extend(self._nested(root, seen, format_callback))
        return roots

def model_format_dict(obj):
    """
    Return a `dict` with keys 'verbose_name' and 'verbose_name_plural',
    typically for use with string formatting.

    `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.

    """
    if isinstance(obj, (models.Model, models.base.ModelBase)):
        opts = obj._meta
    elif isinstance(obj, models.query.QuerySet):
        opts = obj.model._meta
    else:
        opts = obj
    return {
        'verbose_name': force_unicode(opts.verbose_name),
        'verbose_name_plural': force_unicode(opts.verbose_name_plural)
    }

def model_ngettext(obj, n=None):
    """
    Return the appropriate `verbose_name` or `verbose_name_plural` value for
    `obj` depending on the count `n`.

    `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.
    If `obj` is a `QuerySet` instance, `n` is optional and the length of the
    `QuerySet` is used.

    """
    if isinstance(obj, models.query.QuerySet):
        if n is None:
            n = obj.count()
        obj = obj.model
    d = model_format_dict(obj)
    singular, plural = d["verbose_name"], d["verbose_name_plural"]
    return ungettext(singular, plural, n or 0)

def lookup_field(name, obj, model_admin=None):
    opts = obj._meta
    try:
        f = opts.get_field(name)
    except models.FieldDoesNotExist:
        # For non-field values, the value is either a method, property or
        # returned via a callable.
        if callable(name):
            attr = name
            value = attr(obj)
        elif (model_admin is not None and hasattr(model_admin, name) and
          not name == '__str__' and not name == '__unicode__'):
            attr = getattr(model_admin, name)
            value = attr(obj)
        else:
            attr = getattr(obj, name)
            if callable(attr):
                value = attr()
            else:
                value = attr
        f = None
    else:
        attr = None
        value = getattr(obj, name)
    return f, attr, value

def label_for_field(name, model, model_admin=None, return_attr=False):
    """
    Returns a sensible label for a field name. The name can be a callable or the
    name of an object attributes, as well as a genuine fields. If return_attr is
    True, the resolved attribute (which could be a callable) is also returned.
    This will be None if (and only if) the name refers to a field.
    """
    attr = None
    try:
        field = model._meta.get_field_by_name(name)[0]
        if isinstance(field, RelatedObject):
            label = field.opts.verbose_name
        else:
            label = field.verbose_name
    except models.FieldDoesNotExist:
        if name == "__unicode__":
            label = force_unicode(model._meta.verbose_name)
            attr = unicode
        elif name == "__str__":
            label = smart_str(model._meta.verbose_name)
            attr = str
        else:
            if callable(name):
                attr = name
            elif model_admin is not None and hasattr(model_admin, name):
                attr = getattr(model_admin, name)
            elif hasattr(model, name):
                attr = getattr(model, name)
            else:
                message = "Unable to lookup '%s' on %s" % (name, model._meta.object_name)
                if model_admin:
                    message += " or %s" % (model_admin.__class__.__name__,)
                raise AttributeError(message)

            if hasattr(attr, "short_description"):
                label = attr.short_description
            elif callable(attr):
                if attr.__name__ == "<lambda>":
                    label = "--"
                else:
                    label = pretty_name(attr.__name__)
            else:
                label = pretty_name(name)
    if return_attr:
        return (label, attr)
    else:
        return label

def help_text_for_field(name, model):
    try:
        help_text = model._meta.get_field_by_name(name)[0].help_text
    except models.FieldDoesNotExist:
        help_text = ""
    return smart_unicode(help_text)

def admin_urlname(value, arg):
    return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)

def boolean_icon(field_val):
    icon_url = static('xadmin/img/icon-%s.png' %
                      {True: 'yes', False: 'no', None: 'unknown'}[field_val])
    return mark_safe(u'' % (icon_url, field_val))

def display_for_field(value, field):
    from xadmin.views.list import EMPTY_CHANGELIST_VALUE

    if field.flatchoices:
        return dict(field.flatchoices).get(value, EMPTY_CHANGELIST_VALUE)
    # NullBooleanField needs special-case null-handling, so it comes
    # before the general null test.
    elif isinstance(field, models.BooleanField) or isinstance(field, models.NullBooleanField):
        return boolean_icon(value)
    elif value is None:
        return EMPTY_CHANGELIST_VALUE
    elif isinstance(field, models.DateTimeField):
        return formats.localize(timezone.localtime(value))
    elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
        return formats.localize(value)
    elif isinstance(field, models.DecimalField):
        return formats.number_format(value, field.decimal_places)
    elif isinstance(field, models.FloatField):
        return formats.number_format(value)
    elif isinstance(field.rel, models.ManyToManyRel):
        return ', '.join([smart_unicode(obj) for obj in value.all()])
    else:
        return smart_unicode(value)

class NotRelationField(Exception):
    pass

def get_model_from_relation(field):
    if isinstance(field, models.related.RelatedObject):
        return field.model
    elif getattr(field, 'rel'): # or isinstance?
        return field.rel.to
    else:
        raise NotRelationField

def reverse_field_path(model, path):
    """ Create a reversed field path.

    E.g. Given (Order, "user__groups"),
    return (Group, "user__order").

    Final field must be a related model, not a data field.

    """
    reversed_path = []
    parent = model
    pieces = path.split(LOOKUP_SEP)
    for piece in pieces:
        field, model, direct, m2m = parent._meta.get_field_by_name(piece)
        # skip trailing data field if extant:
        if len(reversed_path) == len(pieces)-1: # final iteration
            try:
                get_model_from_relation(field)
            except NotRelationField:
                break
        if direct:
            related_name = field.related_query_name()
            parent = field.rel.to
        else:
            related_name = field.field.name
            parent = field.model
        reversed_path.insert(0, related_name)
    return (parent, LOOKUP_SEP.join(reversed_path))

def get_fields_from_path(model, path):
    """ Return list of Fields given path relative to model.

    e.g. (ModelX, "user__groups__name") -> [
        <django.db.models.fields.related.ForeignKey object at 0x...>,
        <django.db.models.fields.related.ManyToManyField object at 0x...>,
        <django.db.models.fields.CharField object at 0x...>,
    ]
    """
    pieces = path.split(LOOKUP_SEP)
    fields = []
    for piece in pieces:
        if fields:
            parent = get_model_from_relation(fields[-1])
        else:
            parent = model
        fields.append(parent._meta.get_field_by_name(piece)[0])
    return fields

def remove_trailing_data_field(fields):
    """ Discard trailing non-relation field if extant. """
    try:
        get_model_from_relation(fields[-1])
    except NotRelationField:
        fields = fields[:-1]
    return fields

def get_limit_choices_to_from_path(model, path):
    """ Return Q object for limiting choices if applicable.

    If final model in path is linked via a ForeignKey or ManyToManyField which
    has a `limit_choices_to` attribute, return it as a Q object.
    """

    fields = get_fields_from_path(model, path)
    fields = remove_trailing_data_field(fields)
    limit_choices_to = (
        fields and hasattr(fields[-1], 'rel') and
        getattr(fields[-1].rel, 'limit_choices_to', None))
    if not limit_choices_to:
        return models.Q() # empty Q
    elif isinstance(limit_choices_to, models.Q):
        return limit_choices_to # already a Q
    else:
        return models.Q(**limit_choices_to) # convert dict to Q
<commit_msg>for django1.5: import new method ``template_localtime``<commit_after>
import datetime
import decimal

from django.db import models
from django.db.models.sql.query import LOOKUP_SEP
from django.db.models.deletion import Collector
from django.db.models.related import RelatedObject
from django.forms.forms import pretty_name
from django.utils import formats
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.encoding import force_unicode, smart_unicode, smart_str
from django.utils.translation import ungettext
from django.core.urlresolvers import reverse
from django.conf import settings

if 'django.contrib.staticfiles' in settings.INSTALLED_APPS:
    from django.contrib.staticfiles.templatetags.staticfiles import static
else:
    from django.templatetags.static import static

try:
    import json
except ImportError:
    from django.utils import simplejson as json

try:
    from django.utils.timezone import template_localtime as tz_localtime
except ImportError:
    from django.utils.timezone import localtime as tz_localtime

def lookup_needs_distinct(opts, lookup_path):
    """
    Returns True if 'distinct()' should be used to query the given lookup path.
    """
    field_name = lookup_path.split('__', 1)[0]
    field = opts.get_field_by_name(field_name)[0]
    if ((hasattr(field, 'rel') and
         isinstance(field.rel, models.ManyToManyRel)) or
        (isinstance(field, models.related.RelatedObject) and
         not field.field.unique)):
         return True
    return False

def prepare_lookup_value(key, value):
    """
    Returns a lookup value prepared to be used in queryset filtering.
    """
    # if key ends with __in, split parameter into separate values
    if key.endswith('__in'):
        value = value.split(',')
    # if key ends with __isnull, special case '' and false
    if key.endswith('__isnull'):
        if value.lower() in ('', 'false'):
            value = False
        else:
            value = True
    return value

def quote(s):
    """
    Ensure that primary key values do not confuse the admin URLs by escaping
    any '/', '_' and ':' characters. Similar to urllib.quote, except that the
    quoting is slightly different so that it doesn't get automatically
    unquoted by the Web browser.
    """
    if not isinstance(s, basestring):
        return s
    res = list(s)
    for i in range(len(res)):
        c = res[i]
        if c in """:/_#?;@&=+$,"<>%\\""":
            res[i] = '_%02X' % ord(c)
    return ''.join(res)

def unquote(s):
    """
    Undo the effects of quote(). Based heavily on urllib.unquote().
    """
    if not isinstance(s, basestring):
        return s
    mychr = chr
    myatoi = int
    list = s.split('_')
    res = [list[0]]
    myappend = res.append
    del list[0]
    for item in list:
        if item[1:2]:
            try:
                myappend(mychr(myatoi(item[:2], 16)) + item[2:])
            except ValueError:
                myappend('_' + item)
        else:
            myappend('_' + item)
    return "".join(res)

def flatten_fieldsets(fieldsets):
    """Returns a list of field names from an admin fieldsets structure."""
    field_names = []
    for name, opts in fieldsets:
        for field in opts['fields']:
            # type checking feels dirty, but it seems like the best way here
            if type(field) == tuple:
                field_names.extend(field)
            else:
                field_names.append(field)
    return field_names

def get_deleted_objects(objs, opts, user, admin_site, using):
    """
    Find all objects related to ``objs`` that should also be deleted. ``objs``
    must be a homogenous iterable of objects (e.g. a QuerySet).

    Returns a nested list of strings suitable for display in the
    template with the ``unordered_list`` filter.

    """
    collector = NestedObjects(using=using)
    collector.collect(objs)
    perms_needed = set()

    def format_callback(obj):
        has_admin = obj.__class__ in admin_site._registry
        opts = obj._meta

        if has_admin:
            admin_url = reverse('%s:%s_%s_change'
                                % (admin_site.name,
                                   opts.app_label,
                                   opts.object_name.lower()),
                                None, (quote(obj._get_pk_val()),))
            p = '%s.%s' % (opts.app_label,
                           opts.get_delete_permission())
            if not user.has_perm(p):
                perms_needed.add(opts.verbose_name)
            # Display a link to the admin page.
            return mark_safe(u'%s: %s' %
                             (escape(capfirst(opts.verbose_name)),
                              admin_url,
                              escape(obj)))
        else:
            # Don't display link to edit, because it either has no
            # admin or is edited inline.
            return u'%s: %s' % (capfirst(opts.verbose_name),
                                force_unicode(obj))

    to_delete = collector.nested(format_callback)

    protected = [format_callback(obj) for obj in collector.protected]

    return to_delete, perms_needed, protected

class NestedObjects(Collector):
    def __init__(self, *args, **kwargs):
        super(NestedObjects, self).__init__(*args, **kwargs)
        self.edges = {} # {from_instance: [to_instances]}
        self.protected = set()

    def add_edge(self, source, target):
        self.edges.setdefault(source, []).append(target)

    def collect(self, objs, source_attr=None, **kwargs):
        for obj in objs:
            if source_attr:
                self.add_edge(getattr(obj, source_attr), obj)
            else:
                self.add_edge(None, obj)
        try:
            return super(NestedObjects, self).collect(objs, source_attr=source_attr, **kwargs)
        except models.ProtectedError, e:
            self.protected.update(e.protected_objects)

    def related_objects(self, related, objs):
        qs = super(NestedObjects, self).related_objects(related, objs)
        return qs.select_related(related.field.name)

    def _nested(self, obj, seen, format_callback):
        if obj in seen:
            return []
        seen.add(obj)
        children = []
        for child in self.edges.get(obj, ()):
            children.extend(self._nested(child, seen, format_callback))
        if format_callback:
            ret = [format_callback(obj)]
        else:
            ret = [obj]
        if children:
            ret.append(children)
        return ret

    def nested(self, format_callback=None):
        """
        Return the graph as a nested list.

        """
        seen = set()
        roots = []
        for root in self.edges.get(None, ()):
            roots.extend(self._nested(root, seen, format_callback))
        return roots

def model_format_dict(obj):
    """
    Return a `dict` with keys 'verbose_name' and 'verbose_name_plural',
    typically for use with string formatting.

    `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.

    """
    if isinstance(obj, (models.Model, models.base.ModelBase)):
        opts = obj._meta
    elif isinstance(obj, models.query.QuerySet):
        opts = obj.model._meta
    else:
        opts = obj
    return {
        'verbose_name': force_unicode(opts.verbose_name),
        'verbose_name_plural': force_unicode(opts.verbose_name_plural)
    }

def model_ngettext(obj, n=None):
    """
    Return the appropriate `verbose_name` or `verbose_name_plural` value for
    `obj` depending on the count `n`.

    `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance.
    If `obj` is a `QuerySet` instance, `n` is optional and the length of the
    `QuerySet` is used.

    """
    if isinstance(obj, models.query.QuerySet):
        if n is None:
            n = obj.count()
        obj = obj.model
    d = model_format_dict(obj)
    singular, plural = d["verbose_name"], d["verbose_name_plural"]
    return ungettext(singular, plural, n or 0)

def lookup_field(name, obj, model_admin=None):
    opts = obj._meta
    try:
        f = opts.get_field(name)
    except models.FieldDoesNotExist:
        # For non-field values, the value is either a method, property or
        # returned via a callable.
        if callable(name):
            attr = name
            value = attr(obj)
        elif (model_admin is not None and hasattr(model_admin, name) and
          not name == '__str__' and not name == '__unicode__'):
            attr = getattr(model_admin, name)
            value = attr(obj)
        else:
            attr = getattr(obj, name)
            if callable(attr):
                value = attr()
            else:
                value = attr
        f = None
    else:
        attr = None
        value = getattr(obj, name)
    return f, attr, value

def label_for_field(name, model, model_admin=None, return_attr=False):
    """
    Returns a sensible label for a field name. The name can be a callable or the
    name of an object attributes, as well as a genuine fields. If return_attr is
    True, the resolved attribute (which could be a callable) is also returned.
    This will be None if (and only if) the name refers to a field.
    """
    attr = None
    try:
        field = model._meta.get_field_by_name(name)[0]
        if isinstance(field, RelatedObject):
            label = field.opts.verbose_name
        else:
            label = field.verbose_name
    except models.FieldDoesNotExist:
        if name == "__unicode__":
            label = force_unicode(model._meta.verbose_name)
            attr = unicode
        elif name == "__str__":
            label = smart_str(model._meta.verbose_name)
            attr = str
        else:
            if callable(name):
                attr = name
            elif model_admin is not None and hasattr(model_admin, name):
                attr = getattr(model_admin, name)
            elif hasattr(model, name):
                attr = getattr(model, name)
            else:
                message = "Unable to lookup '%s' on %s" % (name, model._meta.object_name)
                if model_admin:
                    message += " or %s" % (model_admin.__class__.__name__,)
                raise AttributeError(message)

            if hasattr(attr, "short_description"):
                label = attr.short_description
            elif callable(attr):
                if attr.__name__ == "<lambda>":
                    label = "--"
                else:
                    label = pretty_name(attr.__name__)
            else:
                label = pretty_name(name)
    if return_attr:
        return (label, attr)
    else:
        return label

def help_text_for_field(name, model):
    try:
        help_text = model._meta.get_field_by_name(name)[0].help_text
    except models.FieldDoesNotExist:
        help_text = ""
    return smart_unicode(help_text)

def admin_urlname(value, arg):
    return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)

def boolean_icon(field_val):
    icon_url = static('xadmin/img/icon-%s.png' %
                      {True: 'yes', False: 'no', None: 'unknown'}[field_val])
    return mark_safe(u'' % (icon_url, field_val))

def display_for_field(value, field):
    from xadmin.views.list import EMPTY_CHANGELIST_VALUE

    if field.flatchoices:
        return dict(field.flatchoices).get(value, EMPTY_CHANGELIST_VALUE)
    # NullBooleanField needs special-case null-handling, so it comes
    # before the general null test.
    elif isinstance(field, models.BooleanField) or isinstance(field, models.NullBooleanField):
        return boolean_icon(value)
    elif value is None:
        return EMPTY_CHANGELIST_VALUE
    elif isinstance(field, models.DateTimeField):
        return formats.localize(tz_localtime(value))
    elif isinstance(field, (models.DateField, models.TimeField)):
        return formats.localize(value)
    elif isinstance(field, models.DecimalField):
        return formats.number_format(value, field.decimal_places)
    elif isinstance(field, models.FloatField):
        return formats.number_format(value)
    elif isinstance(field.rel, models.ManyToManyRel):
        return ', '.join([smart_unicode(obj) for obj in value.all()])
    else:
        return smart_unicode(value)

def display_for_value(value, boolean=False):
    from xadmin.views.list import EMPTY_CHANGELIST_VALUE

    if boolean:
        return boolean_icon(value)
    elif value is None:
        return EMPTY_CHANGELIST_VALUE
    elif isinstance(value, datetime.datetime):
        return formats.localize(tz_localtime(value))
    elif isinstance(value, (datetime.date, datetime.time)):
        return formats.localize(value)
    elif isinstance(value, (decimal.Decimal, float)):
        return formats.number_format(value)
    else:
        return smart_unicode(value)

class NotRelationField(Exception):
    pass

def get_model_from_relation(field):
    if isinstance(field, models.related.RelatedObject):
        return field.model
    elif getattr(field, 'rel'): # or isinstance?
        return field.rel.to
    else:
        raise NotRelationField

def reverse_field_path(model, path):
    """ Create a reversed field path.

    E.g. Given (Order, "user__groups"),
    return (Group, "user__order").

    Final field must be a related model, not a data field.

    """
    reversed_path = []
    parent = model
    pieces = path.split(LOOKUP_SEP)
    for piece in pieces:
        field, model, direct, m2m = parent._meta.get_field_by_name(piece)
        # skip trailing data field if extant:
        if len(reversed_path) == len(pieces)-1: # final iteration
            try:
                get_model_from_relation(field)
            except NotRelationField:
                break
        if direct:
            related_name = field.related_query_name()
            parent = field.rel.to
        else:
            related_name = field.field.name
            parent = field.model
        reversed_path.insert(0, related_name)
    return (parent, LOOKUP_SEP.join(reversed_path))

def get_fields_from_path(model, path):
    """ Return list of Fields given path relative to model.

    e.g. (ModelX, "user__groups__name") -> [
        <django.db.models.fields.related.ForeignKey object at 0x...>,
        <django.db.models.fields.related.ManyToManyField object at 0x...>,
        <django.db.models.fields.CharField object at 0x...>,
    ]
    """
    pieces = path.split(LOOKUP_SEP)
    fields = []
    for piece in pieces:
        if fields:
            parent = get_model_from_relation(fields[-1])
        else:
            parent = model
        fields.append(parent._meta.get_field_by_name(piece)[0])
    return fields

def remove_trailing_data_field(fields):
    """ Discard trailing non-relation field if extant. """
    try:
        get_model_from_relation(fields[-1])
    except NotRelationField:
        fields = fields[:-1]
    return fields

def get_limit_choices_to_from_path(model, path):
    """ Return Q object for limiting choices if applicable.

    If final model in path is linked via a ForeignKey or ManyToManyField which
    has a `limit_choices_to` attribute, return it as a Q object.
    """

    fields = get_fields_from_path(model, path)
    fields = remove_trailing_data_field(fields)
    limit_choices_to = (
        fields and hasattr(fields[-1], 'rel') and
        getattr(fields[-1].rel, 'limit_choices_to', None))
    if not limit_choices_to:
        return models.Q() # empty Q
    elif isinstance(limit_choices_to, models.Q):
        return limit_choices_to # already a Q
    else:
        return models.Q(**limit_choices_to) # convert dict to Q
<|endoftext|>


<commit_before>"""
zigbee.py

By Greg Rapp, 2010
Inspired by code written by Paul Malmsten, 2010
Inspired by code written by Amit Synderman and Marco Sangalli
gdrapp@gmail.com

This module implements an XBee ZB (ZigBee) API library.
"""
import struct
from xbee.base import XBeeBase
from xbee.python2to3 import byteToInt, intToByte

class ZigBee(XBeeBase):
    """
    Provides an implementation of the XBee API for XBee ZB (ZigBee) modules
    with recent firmware.

    Commands may be sent to a device by instantiating this class with
    a serial port object (see PySerial) and then calling the send
    method with the proper information specified by the API. Data may
    be read from a device synchronously by calling wait_read_frame.
    For asynchronous reads, see the defintion of XBeeBase.
    """
    # Packets which can be sent to an XBee

    # Format:
    #        {name of command:
    #           [{name:field name, len:field length, default: default value sent}
    #            ...
    #            ]
    #         ...
    #         }
    api_commands = {"at":
                        [{'name':'id',        'len':1,      'default':b'\x08'},
                         {'name':'frame_id',  'len':1,      'default':b'\x01'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    "queued_at":
                        [{'name':'id',        'len':1,      'default':b'\x09'},
                         {'name':'frame_id',  'len':1,      'default':b'\x01'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    "remote_at":
                        [{'name':'id',              'len':1,        'default':b'\x17'},
                         {'name':'frame_id',        'len':1,        'default':b'\x00'},
                         # dest_addr_long is 8 bytes (64 bits), so use an unsigned long long
                         {'name':'dest_addr_long',  'len':8,        'default':struct.pack('>Q', 0)},
                         {'name':'dest_addr',       'len':2,        'default':b'\xFF\xFE'},
                         {'name':'options',         'len':1,        'default':b'\x02'},
                         {'name':'command',         'len':2,        'default':None},
                         {'name':'parameter',       'len':None,     'default':None}],
                    "tx":
                        [{'name':'id',              'len':1,        'default':b'\x10'},
                         {'name':'frame_id',        'len':1,        'default':b'\x01'},
                         {'name':'dest_addr_long',  'len':8,        'default':None},
                         {'name':'dest_addr',       'len':2,        'default':None},
                         {'name':'broadcast_radius','len':1,        'default':b'\x00'},
                         {'name':'options',         'len':1,        'default':b'\x00'},
                         {'name':'data',            'len':None,     'default':None}],
                    "tx_explicit":
                        [{'name':'id',              'len':1,        'default':b'\x11'},
                         {'name':'frame_id',        'len':1,        'default':b'\x00'},
                         {'name':'dest_addr_long',  'len':8,        'default':None},
                         {'name':'dest_addr',       'len':2,        'default':None},
                         {'name':'src_endpoint',    'len':1,        'default':None},
                         {'name':'dest_endpoint',   'len':1,        'default':None},
                         {'name':'cluster',         'len':2,        'default':None},
                         {'name':'profile',         'len':2,        'default':None},
                         {'name':'broadcast_radius','len':1,        'default':b'\x00'},
                         {'name':'options',         'len':1,        'default':b'\x00'},
                         {'name':'data',            'len':None,     'default':None}]
                    }

    # Packets which can be received from an XBee

    # Format:
    #        {id byte received from XBee:
    #           {name: name of response
    #            structure:
    #                [ {'name': name of field, 'len':length of field}
    #                  ...
    #                  ]
    #            parse_as_io_samples:name of field to parse as io
    #           }
    #           ...
    #        }
    #
    api_responses = {b"\x90":
                        {'name':'rx',
                         'structure':
                            [{'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'options',         'len':1},
                             {'name':'rf_data',         'len':None}]},
                     b"\x91":
                        {'name':'rx_explicit',
                         'structure':
                            [{'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'source_endpoint', 'len':1},
                             {'name':'dest_endpoint',   'len':1},
                             {'name':'cluster',         'len':2},
                             {'name':'profile',         'len':2},
                             {'name':'options',         'len':1},
                             {'name':'rf_data',         'len':None}]},
                     b"\x92": # Checked by GDR-parse_samples_header function appears to need update to support
                        {'name':'rx_io_data_long_addr',
                         'structure':
                            [{'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'options',         'len':1},
                             {'name':'samples',         'len':None}],
                         'parsing': [('samples',
									  lambda xbee,original: xbee._parse_samples(original['samples'])
									 )]},
                     b"\x8b":
                        {'name':'tx_status',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'dest_addr',       'len':2},
                             {'name':'retries',         'len':1},
                             {'name':'deliver_status',  'len':1},
                             {'name':'discover_status', 'len':1}]},
                     b"\x8a":
                        {'name':'status',
                         'structure':
                            [{'name':'status',      'len':1}]},
                     b"\x88":
                        {'name':'at_response',
                         'structure':
                            [{'name':'frame_id',    'len':1},
                             {'name':'command',     'len':2},
                             {'name':'status',      'len':1},
                             {'name':'parameter',   'len':None}]},
                     b"\x97": #Checked GDR (not sure about parameter, could be 4 bytes)
                        {'name':'remote_at_response',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'command',         'len':2},
                             {'name':'status',          'len':1},
                             {'name':'parameter',       'len':None}],
                          'parsing': [('parameter',
									   lambda self, original: self._parse_IS_at_response(original))]
                             },
                     b"\x95":
                        {'name':'node_id_indicator',
                         'structure':
                            [{'name':'sender_addr_long','len':8},
                             {'name':'sender_addr',     'len':2},
                             {'name':'options',         'len':1},
                             {'name':'source_addr',     'len':2},
                             {'name':'source_addr_long','len':8},
                             {'name':'node_id',         'len':'null_terminated'},
                             {'name':'parent_source_addr','len':2},
                             {'name':'device_type',     'len':1},
                             {'name':'source_event',    'len':1},
                             {'name':'digi_profile_id', 'len':2},
                             {'name':'manufacturer_id', 'len':2}]}
                     }

    def _parse_IS_at_response(self, packet_info):
		"""
		If the given packet is a successful remote AT response for an IS
		command, parse the parameter field as IO data.
		"""
		if packet_info['id'] in ('at_response','remote_at_response') and packet_info['status'] == b'\x00':
			   return self._parse_samples(packet_info['parameter'])
		else:
			return packet_info['parameter']

    def __init__(self, *args, **kwargs):
        # Call the super class constructor to save the serial port
        super(ZigBee, self).__init__(*args, **kwargs)

    def _parse_samples_header(self, io_bytes):
        """
        _parse_samples_header: binary data in XBee ZB IO data format ->
                        (int, [int ...], [int ...], int, int)

        _parse_samples_header will read the first three bytes of the
        binary data given and will return the number of samples which
        follow, a list of enabled digital inputs, a list of enabled
        analog inputs, the dio_mask, and the size of the header in bytes

        _parse_samples_header is overloaded here to support the additional
        IO lines offered by the XBee ZB
        """
        header_size = 4

        # number of samples (always 1?) is the first byte
        sample_count = byteToInt(io_bytes[0])

        # bytes 1 and 2 are the DIO mask; bits 9 and 8 aren't used
        dio_mask = (byteToInt(io_bytes[1]) << 8 | byteToInt(io_bytes[2])) & 0x0E7F

        # byte 3 is the AIO mask
        aio_mask = byteToInt(io_bytes[3])

        # sorted lists of enabled channels; value is position of bit in mask
        dio_chans = []
        aio_chans = []

        for i in range(0,13):
            if dio_mask & (1 << i):
                dio_chans.append(i)

        dio_chans.sort()

        for i in range(0,8):
            if aio_mask & (1 << i):
                aio_chans.append(i)

        aio_chans.sort()

        return (sample_count, dio_chans, aio_chans, dio_mask, header_size)
<commit_msg>Replaced tabs with spaces<commit_after>"""
zigbee.py

By Greg Rapp, 2010
Inspired by code written by Paul Malmsten, 2010
Inspired by code written by Amit Synderman and Marco Sangalli
gdrapp@gmail.com

This module implements an XBee ZB (ZigBee) API library.
"""
import struct
from xbee.base import XBeeBase
from xbee.python2to3 import byteToInt, intToByte

class ZigBee(XBeeBase):
    """
    Provides an implementation of the XBee API for XBee ZB (ZigBee) modules
    with recent firmware.

    Commands may be sent to a device by instantiating this class with
    a serial port object (see PySerial) and then calling the send
    method with the proper information specified by the API. Data may
    be read from a device synchronously by calling wait_read_frame.
    For asynchronous reads, see the defintion of XBeeBase.
    """
    # Packets which can be sent to an XBee

    # Format:
    #        {name of command:
    #           [{name:field name, len:field length, default: default value sent}
    #            ...
    #            ]
    #         ...
    #         }
    api_commands = {"at":
                        [{'name':'id',        'len':1,      'default':b'\x08'},
                         {'name':'frame_id',  'len':1,      'default':b'\x01'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    "queued_at":
                        [{'name':'id',        'len':1,      'default':b'\x09'},
                         {'name':'frame_id',  'len':1,      'default':b'\x01'},
                         {'name':'command',   'len':2,      'default':None},
                         {'name':'parameter', 'len':None,   'default':None}],
                    "remote_at":
                        [{'name':'id',              'len':1,        'default':b'\x17'},
                         {'name':'frame_id',        'len':1,        'default':b'\x00'},
                         # dest_addr_long is 8 bytes (64 bits), so use an unsigned long long
                         {'name':'dest_addr_long',  'len':8,        'default':struct.pack('>Q', 0)},
                         {'name':'dest_addr',       'len':2,        'default':b'\xFF\xFE'},
                         {'name':'options',         'len':1,        'default':b'\x02'},
                         {'name':'command',         'len':2,        'default':None},
                         {'name':'parameter',       'len':None,     'default':None}],
                    "tx":
                        [{'name':'id',              'len':1,        'default':b'\x10'},
                         {'name':'frame_id',        'len':1,        'default':b'\x01'},
                         {'name':'dest_addr_long',  'len':8,        'default':None},
                         {'name':'dest_addr',       'len':2,        'default':None},
                         {'name':'broadcast_radius','len':1,        'default':b'\x00'},
                         {'name':'options',         'len':1,        'default':b'\x00'},
                         {'name':'data',            'len':None,     'default':None}],
                    "tx_explicit":
                        [{'name':'id',              'len':1,        'default':b'\x11'},
                         {'name':'frame_id',        'len':1,        'default':b'\x00'},
                         {'name':'dest_addr_long',  'len':8,        'default':None},
                         {'name':'dest_addr',       'len':2,        'default':None},
                         {'name':'src_endpoint',    'len':1,        'default':None},
                         {'name':'dest_endpoint',   'len':1,        'default':None},
                         {'name':'cluster',         'len':2,        'default':None},
                         {'name':'profile',         'len':2,        'default':None},
                         {'name':'broadcast_radius','len':1,        'default':b'\x00'},
                         {'name':'options',         'len':1,        'default':b'\x00'},
                         {'name':'data',            'len':None,     'default':None}]
                    }

    # Packets which can be received from an XBee

    # Format:
    #        {id byte received from XBee:
    #           {name: name of response
    #            structure:
    #                [ {'name': name of field, 'len':length of field}
    #                  ...
    #                  ]
    #            parse_as_io_samples:name of field to parse as io
    #           }
    #           ...
    #        }
    #
    api_responses = {b"\x90":
                        {'name':'rx',
                         'structure':
                            [{'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'options',         'len':1},
                             {'name':'rf_data',         'len':None}]},
                     b"\x91":
                        {'name':'rx_explicit',
                         'structure':
                            [{'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'source_endpoint', 'len':1},
                             {'name':'dest_endpoint',   'len':1},
                             {'name':'cluster',         'len':2},
                             {'name':'profile',         'len':2},
                             {'name':'options',         'len':1},
                             {'name':'rf_data',         'len':None}]},
                     b"\x92": # Checked by GDR-parse_samples_header function appears to need update to support
                        {'name':'rx_io_data_long_addr',
                         'structure':
                            [{'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'options',         'len':1},
                             {'name':'samples',         'len':None}],
                         'parsing': [('samples',
                                      lambda xbee,original: xbee._parse_samples(original['samples'])
                                     )]},
                     b"\x8b":
                        {'name':'tx_status',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'dest_addr',       'len':2},
                             {'name':'retries',         'len':1},
                             {'name':'deliver_status',  'len':1},
                             {'name':'discover_status', 'len':1}]},
                     b"\x8a":
                        {'name':'status',
                         'structure':
                            [{'name':'status',      'len':1}]},
                     b"\x88":
                        {'name':'at_response',
                         'structure':
                            [{'name':'frame_id',    'len':1},
                             {'name':'command',     'len':2},
                             {'name':'status',      'len':1},
                             {'name':'parameter',   'len':None}]},
                     b"\x97": #Checked GDR (not sure about parameter, could be 4 bytes)
                        {'name':'remote_at_response',
                         'structure':
                            [{'name':'frame_id',        'len':1},
                             {'name':'source_addr_long','len':8},
                             {'name':'source_addr',     'len':2},
                             {'name':'command',         'len':2},
                             {'name':'status',          'len':1},
                             {'name':'parameter',       'len':None}],
                          'parsing': [('parameter',
                                       lambda self, original: self._parse_IS_at_response(original))]
                             },
                     b"\x95":
                        {'name':'node_id_indicator',
                         'structure':
                            [{'name':'sender_addr_long','len':8},
                             {'name':'sender_addr',     'len':2},
                             {'name':'options',         'len':1},
                             {'name':'source_addr',     'len':2},
                             {'name':'source_addr_long','len':8},
                             {'name':'node_id',         'len':'null_terminated'},
                             {'name':'parent_source_addr','len':2},
                             {'name':'device_type',     'len':1},
                             {'name':'source_event',    'len':1},
                             {'name':'digi_profile_id', 'len':2},
                             {'name':'manufacturer_id', 'len':2}]}
                     }

    def _parse_IS_at_response(self, packet_info):
        """
        If the given packet is a successful remote AT response for an IS
        command, parse the parameter field as IO data.
        """
        if packet_info['id'] in ('at_response','remote_at_response') and packet_info['status'] == b'\x00':
               return self._parse_samples(packet_info['parameter'])
        else:
            return packet_info['parameter']

    def __init__(self, *args, **kwargs):
        # Call the super class constructor to save the serial port
        super(ZigBee, self).__init__(*args, **kwargs)

    def _parse_samples_header(self, io_bytes):
        """
        _parse_samples_header: binary data in XBee ZB IO data format ->
                        (int, [int ...], [int ...], int, int)

        _parse_samples_header will read the first three bytes of the
        binary data given and will return the number of samples which
        follow, a list of enabled digital inputs, a list of enabled
        analog inputs, the dio_mask, and the size of the header in bytes

        _parse_samples_header is overloaded here to support the additional
        IO lines offered by the XBee ZB
        """
        header_size = 4

        # number of samples (always 1?) is the first byte
        sample_count = byteToInt(io_bytes[0])

        # bytes 1 and 2 are the DIO mask; bits 9 and 8 aren't used
        dio_mask = (byteToInt(io_bytes[1]) << 8 | byteToInt(io_bytes[2])) & 0x0E7F

        # byte 3 is the AIO mask
        aio_mask = byteToInt(io_bytes[3])

        # sorted lists of enabled channels; value is position of bit in mask
        dio_chans = []
        aio_chans = []

        for i in range(0,13):
            if dio_mask & (1 << i):
                dio_chans.append(i)

        dio_chans.sort()

        for i in range(0,8):
            if aio_mask & (1 << i):
                aio_chans.append(i)

        aio_chans.sort()

        return (sample_count, dio_chans, aio_chans, dio_mask, header_size)
<|endoftext|>


<commit_before>"""
This monkeypatches Django to support the __html__ protocol used in Jinja
templates. Form, BoundField, ErrorList, and other form objects that
render HTML through their __unicode__ method are extended with __html__
so they can be rendered in Jinja templates without adding |safe.

Call the monkeypatch() function to execute the patch. It must be called
before django.forms is imported for the conditional_escape patch to work
properly. manage.py is the recommended location for calling monkeypatch().

Usage::

    import safe_django_forms
    safe_django_forms.monkeypatch()

The monkeypatch was developed as a real patch at
https://github.com/jbalogh/django/commits/safe.

"""
import django.utils.encoding
import django.utils.html
import django.utils.safestring

# This function gets directly imported within Django, so this change needs to
# happen before too many Django imports happen.
def conditional_escape(html):
    """
    Similar to escape(), except that it doesn't operate on pre-escaped strings.
    """
    if hasattr(html, '__html__'):
        return html.__html__()
    else:
        return escape(html)

# Django uses SafeData to mark a string that has already been escaped or
# otherwise deemed safe.
class _SafeData(object):

    def __html__(self):
        """
        Returns the html representation of a string.

        Allows interoperability with other template engines.
        """
        return self

# Django uses StrAndUnicode for classes like Form, BoundField, Widget which
# have a __unicode__ method which returns escaped html. We replace
# StrAndUnicode with SafeStrAndUnicode to get the __html__ method.
class SafeStrAndUnicode(django.utils.encoding.StrAndUnicode):
    """A class whose __str__ and __html__ returns __unicode__."""

    def __html__(self):
        return unicode(self)

def monkeypatch():
    django.utils.html.conditional_escape = conditional_escape
    django.utils.safestring.SafeData.__html__ = _SafeData.__html__

    # forms imports have to come after we patch conditional_escape.
    from django.forms import forms, formsets, util, widgets

    # Replace StrAndUnicode with SafeStrAndUnicode in the inheritance
    # for all these classes.
    classes = (
        forms.BaseForm,
        forms.BoundField,
        formsets.BaseFormSet,
        util.ErrorDict,
        util.ErrorList,
        widgets.Media,
        widgets.RadioInput,
        widgets.RadioFieldRenderer,
    )

    for cls in classes:
        bases = list(cls.__bases__)
        idx = bases.index(django.utils.encoding.StrAndUnicode)
        bases[idx] = SafeStrAndUnicode
        cls.__bases__ = tuple(bases)
<commit_msg>make __html__ a function, only patch class inheritance once<commit_after>"""
This monkeypatches Django to support the __html__ protocol used in Jinja
templates. Form, BoundField, ErrorList, and other form objects that
render HTML through their __unicode__ method are extended with __html__
so they can be rendered in Jinja templates without adding |safe.

Call the monkeypatch() function to execute the patch. It must be called
before django.forms is imported for the conditional_escape patch to work
properly. manage.py is the recommended location for calling monkeypatch().

Usage::

    import safe_django_forms
    safe_django_forms.monkeypatch()

The monkeypatch was developed as a real patch at
https://github.com/jbalogh/django/commits/safe.

"""
import django.utils.encoding
import django.utils.html
import django.utils.safestring

# This function gets directly imported within Django, so this change needs to
# happen before too many Django imports happen.
def conditional_escape(html):
    """
    Similar to escape(), except that it doesn't operate on pre-escaped strings.
    """
    if hasattr(html, '__html__'):
        return html.__html__()
    else:
        return django.utils.html.escape(html)

# Django uses SafeData to mark a string that has already been escaped or
# otherwise deemed safe. This __html__ method lets Jinja know about that too.
def __html__(self):
    """
    Returns the html representation of a string.

    Allows interoperability with other template engines.
    """
    return self

# Django uses StrAndUnicode for classes like Form, BoundField, Widget which
# have a __unicode__ method which returns escaped html. We replace
# StrAndUnicode with SafeStrAndUnicode to get the __html__ method.
class SafeStrAndUnicode(django.utils.encoding.StrAndUnicode):
    """A class whose __str__ and __html__ returns __unicode__."""

    def __html__(self):
        return unicode(self)

def monkeypatch():
    django.utils.html.conditional_escape = conditional_escape
    django.utils.safestring.SafeData.__html__ = __html__

    # forms imports have to come after we patch conditional_escape.
    from django.forms import forms, formsets, util, widgets

    # Replace StrAndUnicode with SafeStrAndUnicode in the inheritance
    # for all these classes.
    classes = (
        forms.BaseForm,
        forms.BoundField,
        formsets.BaseFormSet,
        util.ErrorDict,
        util.ErrorList,
        widgets.Media,
        widgets.RadioInput,
        widgets.RadioFieldRenderer,
    )

    for cls in classes:
        bases = list(cls.__bases__)
        if django.utils.encoding.StrAndUnicode in bases:
            idx = bases.index(django.utils.encoding.StrAndUnicode)
            bases[idx] = SafeStrAndUnicode
            cls.__bases__ = tuple(bases)
<|endoftext|>


<commit_before>import random
from asyncio import TimeoutError, sleep
from html import unescape
from bs4 import BeautifulSoup

import discord
from discord.ext import commands

import roxbot
from roxbot import guild_settings

async def _imgur_removed(url):
	page = await roxbot.http.get_page(url)
	soup = BeautifulSoup(page, 'html.parser')
	if "removed.png" in soup.img["src"]:
		return True
	else:
		return False

async def imgur_get(url):
	if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv"):
		return url
	else:
		if await _imgur_removed(url):
			return False
		page = await roxbot.http.get_page(url)
		soup = BeautifulSoup(page, 'html.parser')
		links = []
		for img in soup.find_all("img"):
			if "imgur" in img["src"]:
				if not img["src"] in links:
					links.append(img["src"])

		for video in soup.find_all("source"):
			if "imgur" in video["src"]:
				if not video["src"] in links:
					links.append(video["src"])
		if len(links) > 1:
			return url
		else:
			if "http" not in links[0]:
				links[0] = "https:" + links[0]
			return links[0]

# TODO: Reimplement eroshare, eroshae, and erome support.

async def subreddit_request(subreddit):
	options = [".json?count=1000", "/top/.json?sort=top&t=all&count=1000"]
	choice = random.choice(options)
	subreddit += choice
	url = "https://reddit.com/r/"+subreddit
	r = await roxbot.http.api_request(url)
	try:
		posts = r["data"]
		return posts
	except KeyError or TypeError:
		return {}

async def parse_url(url):
	if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv", "webm", "mp4", "webp"):
		return url
	if "imgur" in url:
		return await imgur_get(url)
	elif "eroshare" in url or "eroshae" in url or "erome" in url:
		return False
		#return ero_get(url)
	elif "gfycat" in url or "redd.it" in url or "i.reddituploads" in url or "media.tumblr" in url or "streamable" in url:
		return url
	else:
		return False

class Reddit():
	def __init__(self, bot_client):
		self.bot = bot_client
		self.post_cache = {}
		for guild in self.bot.guilds:
			self.post_cache[guild.id] = [("", "")]

	@commands.command()
	@commands.has_permissions(add_reactions=True)
	@commands.bot_has_permissions(add_reactions=True)
	async def subreddit(self, ctx, subreddit):
		"""
		Grabs an image or video (jpg, png, gif, gifv, webm, mp4) from the subreddit inputted.
		Example:
		{command_prefix}subreddit pics
		"""
		subreddit = subreddit.lower()
		links = await subreddit_request(subreddit)
		title = ""
		choice = {}

		if not links or not links.get("after") or links["children"][0]["kind"] == "t5":  # Determine if response is valid
			return await ctx.send("Error ;-; That subreddit probably doesn't exist. Please check your spelling")

		url = ""
		x = 0
		# TODO: Test possible crashing here.
		# While loop here to make sure that we check if there is any image posts in the links we have. If so, just take the first one.
		# Choosing a while loop here because, for some reason, the for loop would never exit till the end. Leading to slow times.
		while not url and x <= 20:
			choice = random.choice(links["children"])["data"]
			url = await parse_url(choice["url"])
			if url:
				x_old = int(x)
				# If the url or id are in the cache,  continue the loop. If not, proceed with the post.
				for cache in self.post_cache[ctx.guild.id]:
					if url in cache or choice["id"] in cache:
						x += 1
						break
				if x_old != x:  # Has x been incremented
					url = ""  # Restart search for new post
					if x > 20:
						break
					continue

				title = "**{}** \nby /u/{} from /r/{}\n".format(unescape(choice["title"]), unescape(choice["author"]), subreddit)
				break
			else:
				x += 1

		# Check if post is NSFW, and if it is and this channel doesn't past the NSFW check, then return with the error message.
		if choice["over_18"] and not roxbot.checks.nsfw_predicate(ctx):
			return await ctx.send("This server/channel doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
		if not url:  # If no image posts could be found with the for loop.
			return await ctx.send("I couldn't find any images from that subreddit.")

		# Put the post ID and url in the cache. The url is used in case of two posts having the same link. Handy to avoid crossposts getting through the cache.
		cache_amount = 10
		post = (choice["id"], url)
		self.post_cache[ctx.guild.id].append(post)
		if len(self.post_cache[ctx.guild.id]) >= cache_amount:
			self.post_cache[ctx.guild.id].pop(0)

		if url.split("/")[-2] == "a":
			text = "This is an album, click on the link to see more.\n"
		else:
			text = ""

		if ctx.invoked_with == "subreddit":
			# Only log the command when it is this command being used. Not the inbuilt commands.
			logging = guild_settings.get(ctx.guild).logging
			log_channel = self.bot.get_channel(logging["channel"])
			await roxbot.log(
				ctx.guild,
				log_channel,
				"subreddit",
				User=ctx.author,
				Subreddit=subreddit,
				Returned="<{}>".format(url),
				Channel=ctx.channel,
				Channel_Mention=ctx.channel.mention,
				Time="{:%a %Y/%m/%d %H:%M:%S} UTC".format(ctx.message.created_at)
			)

		# Not using a embed here because we can't use video in rich embeds but they work in embeds now :/
		output = await ctx.send(title + text + url)
		await roxbot.utils.delete_option(self.bot, ctx, output, self.bot.get_emoji(444410658101002261) or "❌")

	@commands.command()
	async def aww(self, ctx):
		"""
		Gives you cute pics from reddit
		"""
		subreddit = "aww"
		return await ctx.invoke(self.subreddit, subreddit=subreddit)

	@commands.command()
	async def feedme(self, ctx):
		"""
		Feeds you with food porn. Uses multiple subreddits.
		Yes, I was very hungry when trying to find the subreddits for this command.
		Subreddits: "foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"
		"""
		subreddits = ["foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"]
		subreddit_choice = random.choice(subreddits)
		return await ctx.invoke(self.subreddit, subreddit=subreddit_choice)

	@commands.command()
	async def feedmevegan(self, ctx):
		"""
		Feeds you with vegan food porn. Uses multiple subreddits.
		Yes, I was very hungry when trying to find the subreddits for this command.
		Subreddits: "veganrecipes", "vegangifrecipes", "veganfoodporn"
		"""
		subreddits = ["veganrecipes", "vegangifrecipes", "VeganFoodPorn"]
		subreddit_choice = random.choice(subreddits)
		return await ctx.invoke(self.subreddit, subreddit=subreddit_choice)

	@commands.command(aliases=["gssp"])
	async def gss(self, ctx):
		"""
		Gives you the best trans memes ever
		"""
		subreddit = "gaysoundsshitposts"
		return await ctx.invoke(self.subreddit, subreddit=subreddit)

	@commands.command(hidden=True, name="subreddit_dryrun")
	async def _subreddit_test(self, ctx, url):
		return await ctx.send(await parse_url(url))

def setup(bot_client):
	bot_client.add_cog(Reddit(bot_client))
<commit_msg>Cleaned up reddit cog. Also error catching may have been broken but I learned how to do except for multiple errors now.<commit_after>import random
import roxbot
from html import unescape
from bs4 import BeautifulSoup
from roxbot import guild_settings
from discord.ext import commands

async def _imgur_removed(url):
	page = await roxbot.http.get_page(url)
	soup = BeautifulSoup(page, 'html.parser')
	return bool("removed.png" in soup.img["src"])

async def imgur_get(url):
	if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv"):
		return url
	else:
		if await _imgur_removed(url):
			return False
		page = await roxbot.http.get_page(url)
		soup = BeautifulSoup(page, 'html.parser')
		links = []
		for img in soup.find_all("img"):
			if "imgur" in img["src"]:
				if not img["src"] in links:
					links.append(img["src"])

		for video in soup.find_all("source"):
			if "imgur" in video["src"]:
				if not video["src"] in links:
					links.append(video["src"])
		if len(links) > 1:
			return url
		else:
			if "http" not in links[0]:
				links[0] = "https:" + links[0]
			return links[0]

# TODO: Reimplement eroshare, eroshae, and erome support.

async def subreddit_request(subreddit):
	options = [".json?count=1000", "/top/.json?sort=top&t=all&count=1000"]
	choice = random.choice(options)
	subreddit += choice
	url = "https://reddit.com/r/"+subreddit
	r = await roxbot.http.api_request(url)
	try:
		posts = r["data"]
		return posts
	except (KeyError, TypeError):
		return {}

async def parse_url(url):
	if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv", "webm", "mp4", "webp"):
		return url
	if "imgur" in url:
		return await imgur_get(url)
	elif "eroshare" in url or "eroshae" in url or "erome" in url:
		return False
		#return ero_get(url)
	elif "gfycat" in url or "redd.it" in url or "i.reddituploads" in url or "media.tumblr" in url or "streamable" in url:
		return url
	else:
		return False

class Reddit:
	def __init__(self, bot_client):
		self.bot = bot_client
		self.post_cache = {}
		for guild in self.bot.guilds:
			self.post_cache[guild.id] = [("", "")]

	@commands.command()
	@commands.has_permissions(add_reactions=True)
	@commands.bot_has_permissions(add_reactions=True)
	async def subreddit(self, ctx, subreddit):
		"""
		Grabs an image or video (jpg, png, gif, gifv, webm, mp4) from the subreddit inputted.
		Example:
		{command_prefix}subreddit pics
		"""
		subreddit = subreddit.lower()
		links = await subreddit_request(subreddit)
		title = ""
		choice = {}

		if not links or not links.get("after") or links["children"][0]["kind"] == "t5":  # Determine if response is valid
			return await ctx.send("Error ;-; That subreddit probably doesn't exist. Please check your spelling")

		url = ""
		x = 0
		# While loop here to make sure that we check if there is any image posts in the links we have. If so, just take the first one.
		# Choosing a while loop here because, for some reason, the for loop would never exit till the end. Leading to slow times.
		while not url and x <= 20:
			choice = random.choice(links["children"])["data"]
			url = await parse_url(choice["url"])
			if url:
				x_old = int(x)
				# If the url or id are in the cache,  continue the loop. If not, proceed with the post.
				for cache in self.post_cache[ctx.guild.id]:
					if url in cache or choice["id"] in cache:
						x += 1
						break
				if x_old != x:  # Has x been incremented
					url = ""  # Restart search for new post
					if x > 20:
						break
					continue

				title = "**{}** \nby /u/{} from /r/{}\n".format(unescape(choice["title"]), unescape(choice["author"]), subreddit)
				break
			else:
				x += 1

		# Check if post is NSFW, and if it is and this channel doesn't past the NSFW check, then return with the error message.
		if choice["over_18"] and not roxbot.checks.nsfw_predicate(ctx):
			return await ctx.send("This server/channel doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
		if not url:  # If no image posts could be found with the for loop.
			return await ctx.send("I couldn't find any images from that subreddit.")

		# Put the post ID and url in the cache. The url is used in case of two posts having the same link. Handy to avoid crossposts getting through the cache.
		cache_amount = 10
		post = (choice["id"], url)
		self.post_cache[ctx.guild.id].append(post)
		if len(self.post_cache[ctx.guild.id]) >= cache_amount:
			self.post_cache[ctx.guild.id].pop(0)

		if url.split("/")[-2] == "a":
			text = "This is an album, click on the link to see more.\n"
		else:
			text = ""

		if ctx.invoked_with == "subreddit":
			# Only log the command when it is this command being used. Not the inbuilt commands.
			logging = guild_settings.get(ctx.guild).logging
			log_channel = self.bot.get_channel(logging["channel"])
			await roxbot.log(
				ctx.guild,
				log_channel,
				"subreddit",
				User=ctx.author,
				Subreddit=subreddit,
				Returned="<{}>".format(url),
				Channel=ctx.channel,
				Channel_Mention=ctx.channel.mention,
				Time="{:%a %Y/%m/%d %H:%M:%S} UTC".format(ctx.message.created_at)
			)

		# Not using a embed here because we can't use video in rich embeds but they work in embeds now :/
		output = await ctx.send(title + text + url)
		await roxbot.utils.delete_option(self.bot, ctx, output, self.bot.get_emoji(444410658101002261) or "❌")

	@commands.command()
	async def aww(self, ctx):
		"""
		Gives you cute pics from reddit
		"""
		subreddit = "aww"
		return await ctx.invoke(self.subreddit, subreddit=subreddit)

	@commands.command()
	async def feedme(self, ctx):
		"""
		Feeds you with food porn. Uses multiple subreddits.
		Yes, I was very hungry when trying to find the subreddits for this command.
		Subreddits: "foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"
		"""
		subreddits = ["foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"]
		subreddit_choice = random.choice(subreddits)
		return await ctx.invoke(self.subreddit, subreddit=subreddit_choice)

	@commands.command()
	async def feedmevegan(self, ctx):
		"""
		Feeds you with vegan food porn. Uses multiple subreddits.
		Yes, I was very hungry when trying to find the subreddits for this command.
		Subreddits: "veganrecipes", "vegangifrecipes", "veganfoodporn"
		"""
		subreddits = ["veganrecipes", "vegangifrecipes", "VeganFoodPorn"]
		subreddit_choice = random.choice(subreddits)
		return await ctx.invoke(self.subreddit, subreddit=subreddit_choice)

	@commands.command(aliases=["gssp"])
	async def gss(self, ctx):
		"""
		Gives you the best trans memes ever
		"""
		subreddit = "gaysoundsshitposts"
		return await ctx.invoke(self.subreddit, subreddit=subreddit)

	@commands.command(hidden=True, name="subreddit_dryrun")
	async def _subreddit_test(self, ctx, url):
		return await ctx.send(await parse_url(url))

def setup(bot_client):
	bot_client.add_cog(Reddit(bot_client))
<|endoftext|>


<commit_before>from django import template
from django.core.urlresolvers import reverse
from survey.interviewer_configs import MONTHS
from survey.models.helper_constants import CONDITIONS
from survey.utils.views_helper import get_ancestors
from survey.models import Survey, Question, Batch, Interviewer, MultiChoiceAnswer, \
    GroupCondition, Answer, AnswerAccessDefinition, ODKAccess
from survey.odk.utils.log import logger
from django.core.exceptions import ObjectDoesNotExist
from django.utils.safestring import mark_safe
from dateutils import relativedelta
from datetime import date
import json, inspect

register = template.Library()

@register.assignment_tag
def next(value, arg):
    try:
        return value[int(arg)+1]
    except:
        return None

@register.filter
def is_location_selected(locations_data, location):
    if locations_data.has_location_selected(location):
        return "selected='selected'"

@register.filter
def is_ea_selected(locations_data, ea):
    if locations_data.selected_ea == ea:
        return "selected='selected'"

@register.filter
def is_selected(batch, selected_batch):
    if batch == selected_batch:
        return "selected='selected'"

@register.filter
def is_batch_open_for_location(open_locations, location):
    if location in open_locations:
        return "checked='checked'"

@register.filter
def is_mobile_number(field):
    return 'mobile number' in field.lower()

@register.filter
def is_radio(field):
    if "radio" in str(field) and not "select" in str(field):
        return "radio_field"
    return ""

@register.filter
def display_list(list):
    new_list = [str(item) for item in list]
    return mark_safe(', '.join(new_list))

@register.filter
def join_list(list, delimiter):
    new_list = ['%s' % str(item) for item in list]
    return mark_safe(delimiter.join(new_list))

@register.filter
def get_value(dict, key):
    return dict.get(key, "")

@register.filter
def batches_enabled(survey, ea):
    return 'Enabled' if survey.batches_enabled(ea) else 'Not Enabled'

@register.filter
def get_month(index):
    if not str(index).isdigit() and not index :
        return "N/A"
    return MONTHS[int(index)][1]

@register.filter
def format_date(date):
    if date:
        return date.strftime("%b %d, %Y")

@register.filter
def get_url_with_ids(args, url_name):
    if not str(args).isdigit():
      arg_list = [int(arg) for arg in args.split(',')]
      return reverse(url_name, args=arg_list)
    return reverse(url_name, args=(args,))

@register.filter
def get_url_without_ids(url_name):
    return reverse(url_name)

@register.filter
def add_string(int_1, int_2):
    return "%s, %s"%(str(int_1), str(int_2))

@register.filter
def condition_text(key):
    value = CONDITIONS.get(key, "")
    return value

@register.filter
def modulo(num, val):
    return num % val == 0

@register.filter
def repeat_string(string, times):
    return string*(times-1)

@register.filter
def is_survey_selected_given(survey, selected_batch):
    if not selected_batch or not selected_batch.survey:
        return None

    if survey == selected_batch.survey:
        return "selected='selected'"

@register.filter
def non_response_is_activefor(open_locations, location):
    if location in open_locations:
       return "checked='checked'"

@register.filter
def ancestors_reversed(location):
    ancestors = get_ancestors(location)
    ancestors.reverse()
    return ancestors

@register.filter
def show_condition(flow):
    if flow.validation_test:
        return '%s ( %s )' % (flow.validation_test, ' and '.join(flow.test_arguments))
    return ""

@register.filter
def access_channels(answer_type):
    channels = AnswerAccessDefinition.objects.filter(answer_type=answer_type).values_list('channel', flat=True).order_by('channel')
    return ",".join(channels)

@register.filter
def quest_validation_opts(batch):
    opts_dict = {}
    for cls in Answer.__subclasses__():
        opts = []
        for validator in cls.validators():
            opts.append({'display': validator.__name__, 'value': validator.__name__.upper() })
        opts_dict[cls.choice_name()] = opts
    return mark_safe(json.dumps(opts_dict));

@register.filter
def validation_args(batch):
    args_map = {}
    for validator in Answer.validators():
        args_map.update({validator.__name__.upper() : len(inspect.getargspec(validator).args) - 2 }) #validator is a class method, plus answer extra pram
    return mark_safe(json.dumps(args_map));

@register.filter
def trim(value):
    return value.strip()

@register.filter
def household_completed_percent(interviewer):
#    import pdb;pdb.set_trace()
    households = interviewer.households.all()
    total = households.count()
    completed = len([hld for hld in households.all() if not hld.survey_completed() and hld.household_member.count() > 0])
    if total > 0:
        return "%s%%" % str(completed*100/total)

@register.filter
def open_survey_in_current_loc(interviewer):
    return len(Survey.currently_open_surveys(interviewer.location))

@register.filter
def households_for_open_survey(interviewer):
    open_survey = Survey.currently_open_surveys(interviewer.location)
    households = interviewer.households.filter(survey__in=open_survey).all()
    return len([hs for hs in households if hs.get_head() is not None])

@register.filter
def total_household_members(interviewer):
    households = interviewer.households.all()
    return sum([household.household_member.count() for household in households])

@register.assignment_tag
def  get_download_url(request, url_name, instance=None):
    if instance is None:
        return request.build_absolute_uri(reverse(url_name))
    else:
        return request.build_absolute_uri(reverse(url_name, args=(instance.pk, )))

@register.assignment_tag(takes_context=True)
def is_relevant_odk(context, question, interviewer, registered_households):
    batch = question.batch
    if question.pk == batch.start_question.pk:
        default_relevance = 'true()'
    else:
        default_relevance = 'false()'
    relevance_context = ' and (%s) %s' % (
                                ' or '.join(context.get(question.pk, [default_relevance, ])),
                                is_relevant_by_group(context, question, registered_households)
                                )
    flows = question.flows.all()
    node_path = '/survey/b%s/q%s' % (batch.pk, question.pk)
    flow_conditions = []
    if flows:
        for flow in flows:
            if flow.next_question:
                next_question = flow.next_question
                next_q_context = context.get(next_question.pk, ['false()', ])
                if flow.validation_test:
                    text_params = [t.param for t in flow.text_arguments]
                    answer_class = Answer.get_class(question.answer_type)
                    flow_condition = answer_class.print_odk_validation(node_path, flow.validation_test, *text_params)
                    flow_conditions.append(flow_condition)
                    next_q_context.append(flow_condition)
                    context[next_question.pk] = next_q_context
        null_flows = flows.filter(validation_test__isnull=True, next_question__isnull=False)
        if null_flows:
            null_flow = null_flows[0]
            null_condition = ["string-length(%s) > 0" % node_path, ]
            if len(flow_conditions) > 0:
                null_condition.append('not (%s)' % ' or '.join(flow_conditions))
            next_question = null_flow.next_question
            next_q_context = context.get(next_question.pk, ['false()', ])
            next_q_context.append('(%s)' % ' and '.join(null_condition))
            context[next_question.pk] = next_q_context
    return mark_safe(relevance_context)

def is_relevant_by_group(context, question, registered_households):
    question_group = question.group
    relevant_existing = []
    relevant_new = []
    attributes = {
                  'AGE': '/survey/household/householdMember/age',
                  'GENDER': '/survey/household/householdMember/sex',
                  'GENERAL': '/survey/household/householdMember/isHead'
                }
    for condition in question_group.get_all_conditions():
        relevant_new.append(condition.odk_matches(attributes))

    for household in registered_households:
        for member in household.members.all():
            if member.belongs_to(question_group):
                relevant_existing.append(" /survey/registeredHousehold/selectedMember = '%s_%s' " % (member.household.pk, member.pk))
            else:
            #get next inline question... This is another flow leading to the next inline question in case current is not applicable
                next_question = question.batch.next_inline(question, groups=member.groups, channel=ODKAccess.choice_name())
                if next_question:
                    node_path = '/survey/b%s/q%s' % (question.batch.pk, question.pk)
                    next_q_context = context.get(next_question.pk, ['false()', ])
                    next_q_context.append("string-length(%s) > 0" % node_path)
                    context[next_question.pk] = next_q_context
    relevance_builder = ['false()', ]
    if relevant_new:
       relevance_builder.append('(%s)' % ' and '.join(relevant_new))
    if relevant_existing:
        relevance_builder.append('(%s)' % ' or '.join(relevant_existing))
    return ' and (%s)' % ' or '.join(relevance_builder)

<commit_msg>fix for xform relevant next question in line<commit_after>from django import template
from django.core.urlresolvers import reverse
from survey.interviewer_configs import MONTHS
from survey.models.helper_constants import CONDITIONS
from survey.utils.views_helper import get_ancestors
from survey.models import Survey, Question, Batch, Interviewer, MultiChoiceAnswer, \
    GroupCondition, Answer, AnswerAccessDefinition, ODKAccess
from survey.odk.utils.log import logger
from django.core.exceptions import ObjectDoesNotExist
from django.utils.safestring import mark_safe
from dateutils import relativedelta
from datetime import date
import json, inspect

register = template.Library()

@register.assignment_tag
def next(value, arg):
    try:
        return value[int(arg)+1]
    except:
        return None

@register.filter
def is_location_selected(locations_data, location):
    if locations_data.has_location_selected(location):
        return "selected='selected'"

@register.filter
def is_ea_selected(locations_data, ea):
    if locations_data.selected_ea == ea:
        return "selected='selected'"

@register.filter
def is_selected(batch, selected_batch):
    if batch == selected_batch:
        return "selected='selected'"

@register.filter
def is_batch_open_for_location(open_locations, location):
    if location in open_locations:
        return "checked='checked'"

@register.filter
def is_mobile_number(field):
    return 'mobile number' in field.lower()

@register.filter
def is_radio(field):
    if "radio" in str(field) and not "select" in str(field):
        return "radio_field"
    return ""

@register.filter
def display_list(list):
    new_list = [str(item) for item in list]
    return mark_safe(', '.join(new_list))

@register.filter
def join_list(list, delimiter):
    new_list = ['%s' % str(item) for item in list]
    return mark_safe(delimiter.join(new_list))

@register.filter
def get_value(dict, key):
    return dict.get(key, "")

@register.filter
def batches_enabled(survey, ea):
    return 'Enabled' if survey.batches_enabled(ea) else 'Not Enabled'

@register.filter
def get_month(index):
    if not str(index).isdigit() and not index :
        return "N/A"
    return MONTHS[int(index)][1]

@register.filter
def format_date(date):
    if date:
        return date.strftime("%b %d, %Y")

@register.filter
def get_url_with_ids(args, url_name):
    if not str(args).isdigit():
      arg_list = [int(arg) for arg in args.split(',')]
      return reverse(url_name, args=arg_list)
    return reverse(url_name, args=(args,))

@register.filter
def get_url_without_ids(url_name):
    return reverse(url_name)

@register.filter
def add_string(int_1, int_2):
    return "%s, %s"%(str(int_1), str(int_2))

@register.filter
def condition_text(key):
    value = CONDITIONS.get(key, "")
    return value

@register.filter
def modulo(num, val):
    return num % val == 0

@register.filter
def repeat_string(string, times):
    return string*(times-1)

@register.filter
def is_survey_selected_given(survey, selected_batch):
    if not selected_batch or not selected_batch.survey:
        return None

    if survey == selected_batch.survey:
        return "selected='selected'"

@register.filter
def non_response_is_activefor(open_locations, location):
    if location in open_locations:
       return "checked='checked'"

@register.filter
def ancestors_reversed(location):
    ancestors = get_ancestors(location)
    ancestors.reverse()
    return ancestors

@register.filter
def show_condition(flow):
    if flow.validation_test:
        return '%s ( %s )' % (flow.validation_test, ' and '.join(flow.test_arguments))
    return ""

@register.filter
def access_channels(answer_type):
    channels = AnswerAccessDefinition.objects.filter(answer_type=answer_type).values_list('channel', flat=True).order_by('channel')
    return ",".join(channels)

@register.filter
def quest_validation_opts(batch):
    opts_dict = {}
    for cls in Answer.__subclasses__():
        opts = []
        for validator in cls.validators():
            opts.append({'display': validator.__name__, 'value': validator.__name__.upper() })
        opts_dict[cls.choice_name()] = opts
    return mark_safe(json.dumps(opts_dict));

@register.filter
def validation_args(batch):
    args_map = {}
    for validator in Answer.validators():
        args_map.update({validator.__name__.upper() : len(inspect.getargspec(validator).args) - 2 }) #validator is a class method, plus answer extra pram
    return mark_safe(json.dumps(args_map));

@register.filter
def trim(value):
    return value.strip()

@register.filter
def household_completed_percent(interviewer):
#    import pdb;pdb.set_trace()
    households = interviewer.households.all()
    total = households.count()
    completed = len([hld for hld in households.all() if not hld.survey_completed() and hld.household_member.count() > 0])
    if total > 0:
        return "%s%%" % str(completed*100/total)

@register.filter
def open_survey_in_current_loc(interviewer):
    return len(Survey.currently_open_surveys(interviewer.location))

@register.filter
def households_for_open_survey(interviewer):
    open_survey = Survey.currently_open_surveys(interviewer.location)
    households = interviewer.households.filter(survey__in=open_survey).all()
    return len([hs for hs in households if hs.get_head() is not None])

@register.filter
def total_household_members(interviewer):
    households = interviewer.households.all()
    return sum([household.household_member.count() for household in households])

@register.assignment_tag
def  get_download_url(request, url_name, instance=None):
    if instance is None:
        return request.build_absolute_uri(reverse(url_name))
    else:
        return request.build_absolute_uri(reverse(url_name, args=(instance.pk, )))

@register.assignment_tag(takes_context=True)
def is_relevant_odk(context, question, interviewer, registered_households):
    batch = question.batch
    if question.pk == batch.start_question.pk:
        default_relevance = 'true()'
    else:
        default_relevance = 'false()'
    relevance_context = ' and (%s) %s' % (
                                ' or '.join(context.get(question.pk, [default_relevance, ])),
                                is_relevant_by_group(context, question, registered_households)
                                )
    flows = question.flows.all()
    node_path = '/survey/b%s/q%s' % (batch.pk, question.pk)
    flow_conditions = []
    if flows:
        for flow in flows:
            if flow.next_question:
                next_question = flow.next_question
                next_q_context = context.get(next_question.pk, ['false()', ])
                if flow.validation_test:
                    text_params = [t.param for t in flow.text_arguments]
                    answer_class = Answer.get_class(question.answer_type)
                    flow_condition = answer_class.print_odk_validation(node_path, flow.validation_test, *text_params)
                    flow_conditions.append(flow_condition)
                    next_q_context.append(flow_condition)
                    context[next_question.pk] = next_q_context
        null_flows = flows.filter(validation_test__isnull=True, next_question__isnull=False)
        if null_flows:
            null_flow = null_flows[0]
            null_condition = ["string-length(%s) > 0" % node_path, ]
            if len(flow_conditions) > 0:
                null_condition.append('not (%s)' % ' or '.join(flow_conditions))
            next_question = null_flow.next_question
            next_q_context = context.get(next_question.pk, ['false()', ])
            next_q_context.append('(%s)' % ' and '.join(null_condition))
            context[next_question.pk] = next_q_context
    return mark_safe(relevance_context)

def is_relevant_by_group(context, question, registered_households):
    question_group = question.group
    relevant_existing = []
    relevant_new = []
    attributes = {
                  'AGE': '/survey/household/householdMember/age',
                  'GENDER': '/survey/household/householdMember/sex',
                  'GENERAL': '/survey/household/householdMember/isHead'
                }
    for condition in question_group.get_all_conditions():
        relevant_new.append(condition.odk_matches(attributes))

    for household in registered_households:
        for member in household.members.all():
            if member.belongs_to(question_group):
                relevant_existing.append(" /survey/registeredHousehold/selectedMember = '%s_%s' " % (member.household.pk, member.pk))
            else:
            #get next inline question... This is another flow leading to the next inline question in case current is not applicable
                connecting_flows = question.connecting_flows.filter(validation_test__isnull=True)
                if connecting_flows:
                    f_question = connecting_flows[0].question
                    next_question = f_question.batch.next_inline(f_question, groups=member.groups,
                                                                            channel=ODKAccess.choice_name())
                    if next_question:
                        node_path = '/survey/b%s/q%s' % (f_question.batch.pk, f_question.pk)
                        next_q_context = context.get(next_question.pk, ['false()', ])
                        next_q_context.append("string-length(%s) > 0" % node_path)
                        context[next_question.pk] = next_q_context
    relevance_builder = ['false()', ]
    if relevant_new:
       relevance_builder.append('(%s)' % ' and '.join(relevant_new))
    if relevant_existing:
        relevance_builder.append('(%s)' % ' or '.join(relevant_existing))
    return ' and (%s)' % ' or '.join(relevance_builder)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""Application assets."""
from flask_assets import Bundle, Environment

css = Bundle(
    'libs/bootstrap/dist/css/bootstrap.css',
    'css/style.css',
    filters='cssmin',
    output='public/css/common.css'
)

js = Bundle(
    'libs/jQuery/dist/jquery.js',
    'libs/bootstrap/dist/js/bootstrap.js',
    'js/plugins.js',
    filters='jsmin',
    output='public/js/common.js'
)

assets = Environment()

assets.register('js_all', js)
assets.register('css_all', css)
<commit_msg>update asset files<commit_after># -*- coding: utf-8 -*-
"""Application assets."""
from flask_assets import Bundle, Environment

css = Bundle(
    'vendor/bootstrap/dist/css/bootstrap.css',
    'css/x_creative.css',
    filters='cssmin',
    output='public/css/common.css'
)

js = Bundle(
    'vendor/jQuery/dist/jquery.js',
    'vendor/bootstrap/dist/js/bootstrap.js',
    'js/x_creative.js',
    filters='jsmin',
    output='public/js/common.js'
)

assets = Environment()

assets.register('js_all', js)
assets.register('css_all', css)
<|endoftext|>


<commit_before>import logging
from subprocess import Popen, PIPE

from django.conf import settings

logger = logging.getLogger('saleor')

class CheckHTML(object):
    def process_response(self, _request, response):
        if (
            settings.DEBUG
            and settings.WARN_ABOUT_INVALID_HTML5_OUTPUT
            and 200 <= response.status.code < 300
        ):
            proc = Popen(["tidy"], stdout=PIPE, stderr=PIPE, stdin=PIPE)
            _out, err = proc.communicate(response.content)
            for l in err.split('\n\n')[0].split('\n')[:-2]:
                logger.warning(l)
        return response
<commit_msg>Fix a typo<commit_after>import logging
from subprocess import Popen, PIPE

from django.conf import settings

logger = logging.getLogger('saleor')

class CheckHTML(object):
    def process_response(self, _request, response):
        if (
            settings.DEBUG
            and settings.WARN_ABOUT_INVALID_HTML5_OUTPUT
            and 200 <= response.status_code < 300
        ):
            proc = Popen(["tidy"], stdout=PIPE, stderr=PIPE, stdin=PIPE)
            _out, err = proc.communicate(response.content)
            for l in err.split('\n\n')[0].split('\n')[:-2]:
                logger.warning(l)
        return response
<|endoftext|>


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

from autotest import PathInfo, globjoin
from autotest import run_single

import sys, os
import nose.plugins.skip

class RunTest:
    def __init__(self,f):
        self.description = os.path.basename(f)

    def __call__(self,f):
        key, errcode = run_single(f)
	if key in ['incomplete']:
            raise nose.plugins.skip.SkipTest
        assert key == 'good', '%s is not good: %s'%(f,key)

def test():
    testfiles = globjoin(PathInfo.inpdir, '*.txt')
    results = {}
    for fname in testfiles:
        yield RunTest(fname), fname

def setup():
    PathInfo.add_coverage()
<commit_msg>Skip tests that can't pass (no good md5), mostly useful for hudson<commit_after># -*- coding: utf-8 -*-

from autotest import MD5Info, PathInfo, globjoin
from autotest import run_single

import sys, os
import nose.plugins.skip

class RunTest:
    def __init__(self,f):
        basename = os.path.basename(f)
        self.description = basename
        mprefix = os.path.join(PathInfo.md5dir, basename)[:-4]
        md5file = mprefix + '.json'
        info=MD5Info()
        if os.path.exists(md5file):
            f = open(md5file, 'rb')
            exec f in info
            f.close()
        if info.good_md5 in [[],['sentinel']]:
            # This is an open issue or something that can't be checked automatically
            self.skip=True
        else:
            self.skip=False
    def __call__(self,f):
        if self.skip:
            raise nose.plugins.skip.SkipTest
        else:
            key, errcode = run_single(f)
            if key in ['incomplete']:
                raise nose.plugins.skip.SkipTest
            assert key == 'good', '%s is not good: %s'%(f,key)

def test():
    testfiles = globjoin(PathInfo.inpdir, '*.txt')
    results = {}
    for fname in testfiles:
        yield RunTest(fname), fname

def setup():
    PathInfo.add_coverage()
<|endoftext|>


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

from autotest import PathInfo, run_single_test, globjoin

import sys

class RunTest:
    def __init__(self,f):
        self.description = f

    def __call__(self,f):
        key, errcode = run_single_test(self.description)
        assert key == 'good', '%s is not good: %s'%(f,key)

def test():
    testfiles = globjoin(PathInfo.inpdir, '*.txt')
    results = {}
    for fname in testfiles:
        yield RunTest(fname), fname

def setup():
    PathInfo.add_coverage()<commit_msg>avoid bogus test<commit_after># -*- coding: utf-8 -*-

from autotest import PathInfo, globjoin
from autotest import run_single_test as run_single

import sys, os

class RunTest:
    def __init__(self,f):
        self.description = os.path.basename(f)

    def __call__(self,f):
        key, errcode = run_single(f)
        assert key == 'good', '%s is not good: %s'%(f,key)

def test():
    testfiles = globjoin(PathInfo.inpdir, '*.txt')
    results = {}
    for fname in testfiles:
        yield RunTest(fname), fname

def setup():
    PathInfo.add_coverage()
<|endoftext|>