<commit_before>from django.views.generic import CreateView, TemplateView
from django.shortcuts import redirect
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse

from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response

from braces.views import LoginRequiredMixin

from core.decorators import (
    handle_exceptions_for_ajax, handle_exceptions_for_admin
)
from dataviews.models import View
from contributions.serializers import ContributionSerializer
from users.serializers import UserSerializer
from users.models import User

from .base import STATUS
from .models import Project
from .forms import ProjectCreateForm
from .serializers import ProjectSerializer

# ############################################################################
#
# Administration views
#
# ############################################################################

class ProjectCreate(LoginRequiredMixin, CreateView):
    """
    Displays the create project page
    `/admin/projects/new`
    """
    form_class = ProjectCreateForm
    template_name = 'projects/project_create.html'

    def form_valid(self, form):
        """
        Creates the project and redirects to the project overview page
        """
        data = form.cleaned_data
        project = Project.create(
            data.get('name'),
            data.get('description'),
            data.get('isprivate'),
            self.request.user
        )
        return redirect('admin:project_settings', project_id=project.id)

class ProjectSettings(LoginRequiredMixin, TemplateView):
    """
    Displays the project settings page
    `/admin/projects/:project_id`
    """
    model = Project
    template_name = 'projects/project_settings.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id):
        """
        Creates the request context for rendering the page. If the user is not
        an administrator of the project, `PermissionDenied` is caught and
        handled in the `handle_exceptions_for_admin` decorator and an error
        message is displayed.
        """
        project = Project.objects.as_admin(self.request.user, project_id)
        return {
            'project': project,
            'status_types': STATUS
        }

    def dispatch(self, request, *args, **kwargs):
        """
        Redirects page to one of the observation pages. Subject to change when
        Observation pages are removed from the system.
        """
        project_id = kwargs.get('project_id')

        try:
            project = Project.objects.get(pk=project_id)

            if project.is_admin(request.user):
                return super(ProjectSettings, self).dispatch(
                    request, *args, **kwargs)
            elif project.can_contribute(request.user):
                return redirect(reverse(
                    'admin:project_my_observations', kwargs={
                        'project_id': project_id,
                    }
                ))
            else:
                views = View.objects.get_list(request.user, project_id)

                if len(views) > 0:
                    return redirect(reverse('admin:view_observations', kwargs={
                        'project_id': project_id,
                        'view_id': views[0].id
                    }))

            return super(ProjectSettings, self).dispatch(
                request, *args, **kwargs)
        except Project.DoesNotExist:
            return super(ProjectSettings, self).dispatch(
                request, *args, **kwargs)

# ############################################################################
#
# AJAX API views
#
# ############################################################################

class ProjectUpdate(APIView):
    """
    AJAX Endpoint for a project update.
    /ajax/projects/:project_id
    """

    @handle_exceptions_for_ajax
    def put(self, request, project_id, format=None):
        """
        Updates a project. If the user is not an administrator of the project,
        `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.as_admin(request.user, project_id)
        serializer = ProjectSerializer(
            project, data=request.DATA, partial=True,
            fields=('id', 'name', 'description', 'status', 'isprivate')
        )
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @handle_exceptions_for_ajax
    def delete(self, request, project_id, format=None):
        """
        Deletes a project. If the user is not an administrator of the project,
        `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.as_admin(request.user, project_id)
        project.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

class ProjectAdmins(APIView):
    """
    AJAX Endpoint for project administrators.
    /ajax/projects/:project_id/admins
    """

    @handle_exceptions_for_ajax
    def post(self, request, project_id, format=None):
        """
        Adds a user to the admin group. . If the user is not an administrator
        of the project, `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.as_admin(request.user, project_id)

        try:
            user = User.objects.get(pk=request.DATA.get('userId'))
            project.admins.add(user)

            serializer = UserSerializer(project.admins.all(), many=True)
            return Response(
                {'users': serializer.data}, status=status.HTTP_201_CREATED)
        except User.DoesNotExist:
            return Response(
                'The user you are trying to add to the user group does ' +
                'not exist',
                status=status.HTTP_400_BAD_REQUEST
            )

class ProjectAdminsUser(APIView):
    """
    AJAX Endpoint for a single project administrator.
    /ajax/projects/:project_id/admins
    """

    @handle_exceptions_for_ajax
    def delete(self, request, project_id, user_id, format=None):
        """
        Removes a user from the user group. . If the user is not an
        administrator of the project, `PermissionDenied` is caught and handled
        in the `handle_exceptions_for_ajax` decorator and an error 403 is
        returned.
        """
        project = Project.objects.as_admin(request.user, project_id)
        user = project.admins.get(pk=user_id)
        project.admins.remove(user)
        return Response(status=status.HTTP_204_NO_CONTENT)

# ############################################################################
#
# Public API views
#
# ############################################################################

class Projects(APIView):
    """
    API Endpoint for project list in the public API.
    /api/projects/
    """
    @handle_exceptions_for_ajax
    def get(self, request, format=None):
        """
        Returns a list a all projects accessable to the user.
        """
        projects = Project.objects.get_list(
            request.user).filter(status='active')
        serializer = ProjectSerializer(
            projects, many=True, context={'user': request.user},
            fields=('id', 'name', 'description', 'is_involved', 'num_views')
        )
        return Response(serializer.data)

class SingleProject(APIView):
    """
    API Endpoint for single project in the public API.
    /api/projects/:project_id/
    """
    @handle_exceptions_for_ajax
    def get(self, request, project_id, format=None):
        """
        Returns a single project. If the user is not eligable to access the
        project, `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.get_single(request.user, project_id)
        if project.status == 'active':
            serializer = ProjectSerializer(
                project, context={'user': request.user}
            )
            return Response(serializer.data)
        else:
            raise PermissionDenied('The project is inactive and therefore '
                                   'not accessable through the public API.')

# ############################################################################
#
# To be removed
#
# ############################################################################

class ProjectObservations(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    model = Project
    template_name = 'contributions/observations.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id):
        """
        Creates the request context for rendering the page.
        """
        user = self.request.user
        project = Project.objects.get_single(user, project_id)
        views = View.objects.get_list(user, project_id)
        return {
            'project': project,
            'views': views,
            'admin': project.is_admin(user),
            'contributor': project.can_contribute(user),
        }

class ProjectMyObservations(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    model = Project
    template_name = 'contributions/observations.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id):
        """
        Creates the request context for rendering the page
        """
        user = self.request.user
        project = Project.objects.get_single(user, project_id)
        views = View.objects.get_list(user, project_id)
        return {
            'project': project,
            'views': views,
            'admin': project.is_admin(user),
            'contributor': project.can_contribute(user),
            'my_contributions': True
        }

class ProjectSingleObservation(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    template_name = 'contributions/observation.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id, observation_id):
        """
        Creates the request context for rendering the page
        """
        user = self.request.user
        project = Project.objects.as_admin(user, project_id)
        observation = project.observations.get(
            pk=observation_id)

        return {
            'project': project,
            'observation': observation
        }

class ProjectSingleMyObservation(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    template_name = 'contributions/observation.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id, observation_id):
        """
        Creates the request context for rendering the page
        """
        user = self.request.user
        project = Project.objects.as_contributor(user, project_id)
        observation = project.observations.filter(creator=user).get(
            pk=observation_id)

        return {
            'project': project,
            'observation': observation
        }

class ProjectAjaxObservations(APIView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    """
    API Endpoint for a project in the AJAX API.
    /ajax/projects/:project_id/observations/
    """
    @handle_exceptions_for_ajax
    def get(self, request, project_id, format=None):
        project = Project.objects.as_admin(request.user, project_id)
        serializer = ContributionSerializer(
            project.observations.all(), many=True)
        return Response(serializer.data)

class ProjectAjaxMyObservations(APIView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    """
    API Endpoint for a project in the AJAX API.
    /ajax/projects/:project_id/mycontributions/
    """
    @handle_exceptions_for_ajax
    def get(self, request, project_id, format=None):
        project = Project.objects.as_contributor(request.user, project_id)
        serializer = ContributionSerializer(
            project.observations.filter(creator=request.user), many=True)
        return Response(serializer.data)
<commit_msg>Fixed redirect bug to project when user is not logged in<commit_after>from django.views.generic import CreateView, TemplateView
from django.shortcuts import redirect
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse

from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response

from braces.views import LoginRequiredMixin

from core.decorators import (
    handle_exceptions_for_ajax, handle_exceptions_for_admin
)
from dataviews.models import View
from contributions.serializers import ContributionSerializer
from users.serializers import UserSerializer
from users.models import User

from .base import STATUS
from .models import Project
from .forms import ProjectCreateForm
from .serializers import ProjectSerializer

# ############################################################################
#
# Administration views
#
# ############################################################################

class ProjectCreate(LoginRequiredMixin, CreateView):
    """
    Displays the create project page
    `/admin/projects/new`
    """
    form_class = ProjectCreateForm
    template_name = 'projects/project_create.html'

    def form_valid(self, form):
        """
        Creates the project and redirects to the project overview page
        """
        data = form.cleaned_data
        project = Project.create(
            data.get('name'),
            data.get('description'),
            data.get('isprivate'),
            self.request.user
        )
        return redirect('admin:project_settings', project_id=project.id)

class ProjectSettings(LoginRequiredMixin, TemplateView):
    """
    Displays the project settings page
    `/admin/projects/:project_id`
    """
    model = Project
    template_name = 'projects/project_settings.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id):
        """
        Creates the request context for rendering the page. If the user is not
        an administrator of the project, `PermissionDenied` is caught and
        handled in the `handle_exceptions_for_admin` decorator and an error
        message is displayed.
        """
        project = Project.objects.as_admin(self.request.user, project_id)
        return {
            'project': project,
            'status_types': STATUS
        }

    def dispatch(self, request, *args, **kwargs):
        """
        Redirects page to one of the observation pages. Subject to change when
        Observation pages are removed from the system.
        """
        project_id = kwargs.get('project_id')

        try:
            project = Project.objects.get(pk=project_id)

            if not request.user.is_anonymous():
                if project.is_admin(request.user):
                    return super(ProjectSettings, self).dispatch(
                        request, *args, **kwargs)
                elif project.can_contribute(request.user):
                    return redirect(reverse(
                        'admin:project_my_observations', kwargs={
                            'project_id': project_id,
                        }
                    ))
                else:
                    views = View.objects.get_list(request.user, project_id)

                    if len(views) > 0:
                        return redirect(
                            reverse(
                                'admin:view_observations',
                                kwargs={
                                    'project_id': project_id,
                                    'view_id': views[0].id
                                }
                            )
                        )

        except Project.DoesNotExist:
            pass

        return super(ProjectSettings, self).dispatch(
            request, *args, **kwargs)

# ############################################################################
#
# AJAX API views
#
# ############################################################################

class ProjectUpdate(APIView):
    """
    AJAX Endpoint for a project update.
    /ajax/projects/:project_id
    """

    @handle_exceptions_for_ajax
    def put(self, request, project_id, format=None):
        """
        Updates a project. If the user is not an administrator of the project,
        `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.as_admin(request.user, project_id)
        serializer = ProjectSerializer(
            project, data=request.DATA, partial=True,
            fields=('id', 'name', 'description', 'status', 'isprivate')
        )
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @handle_exceptions_for_ajax
    def delete(self, request, project_id, format=None):
        """
        Deletes a project. If the user is not an administrator of the project,
        `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.as_admin(request.user, project_id)
        project.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

class ProjectAdmins(APIView):
    """
    AJAX Endpoint for project administrators.
    /ajax/projects/:project_id/admins
    """

    @handle_exceptions_for_ajax
    def post(self, request, project_id, format=None):
        """
        Adds a user to the admin group. . If the user is not an administrator
        of the project, `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.as_admin(request.user, project_id)

        try:
            user = User.objects.get(pk=request.DATA.get('userId'))
            project.admins.add(user)

            serializer = UserSerializer(project.admins.all(), many=True)
            return Response(
                {'users': serializer.data}, status=status.HTTP_201_CREATED)
        except User.DoesNotExist:
            return Response(
                'The user you are trying to add to the user group does ' +
                'not exist',
                status=status.HTTP_400_BAD_REQUEST
            )

class ProjectAdminsUser(APIView):
    """
    AJAX Endpoint for a single project administrator.
    /ajax/projects/:project_id/admins
    """

    @handle_exceptions_for_ajax
    def delete(self, request, project_id, user_id, format=None):
        """
        Removes a user from the user group. . If the user is not an
        administrator of the project, `PermissionDenied` is caught and handled
        in the `handle_exceptions_for_ajax` decorator and an error 403 is
        returned.
        """
        project = Project.objects.as_admin(request.user, project_id)
        user = project.admins.get(pk=user_id)
        project.admins.remove(user)
        return Response(status=status.HTTP_204_NO_CONTENT)

# ############################################################################
#
# Public API views
#
# ############################################################################

class Projects(APIView):
    """
    API Endpoint for project list in the public API.
    /api/projects/
    """
    @handle_exceptions_for_ajax
    def get(self, request, format=None):
        """
        Returns a list a all projects accessable to the user.
        """
        projects = Project.objects.get_list(
            request.user).filter(status='active')
        serializer = ProjectSerializer(
            projects, many=True, context={'user': request.user},
            fields=('id', 'name', 'description', 'is_involved', 'num_views')
        )
        return Response(serializer.data)

class SingleProject(APIView):
    """
    API Endpoint for single project in the public API.
    /api/projects/:project_id/
    """
    @handle_exceptions_for_ajax
    def get(self, request, project_id, format=None):
        """
        Returns a single project. If the user is not eligable to access the
        project, `PermissionDenied` is caught and handled in the
        `handle_exceptions_for_ajax` decorator and an error 403 is returned.
        """
        project = Project.objects.get_single(request.user, project_id)
        if project.status == 'active':
            serializer = ProjectSerializer(
                project, context={'user': request.user}
            )
            return Response(serializer.data)
        else:
            raise PermissionDenied('The project is inactive and therefore '
                                   'not accessable through the public API.')

# ############################################################################
#
# To be removed
#
# ############################################################################

class ProjectObservations(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    model = Project
    template_name = 'contributions/observations.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id):
        """
        Creates the request context for rendering the page.
        """
        user = self.request.user
        project = Project.objects.get_single(user, project_id)
        views = View.objects.get_list(user, project_id)
        return {
            'project': project,
            'views': views,
            'admin': project.is_admin(user),
            'contributor': project.can_contribute(user),
        }

class ProjectMyObservations(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    model = Project
    template_name = 'contributions/observations.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id):
        """
        Creates the request context for rendering the page
        """
        user = self.request.user
        project = Project.objects.get_single(user, project_id)
        views = View.objects.get_list(user, project_id)
        return {
            'project': project,
            'views': views,
            'admin': project.is_admin(user),
            'contributor': project.can_contribute(user),
            'my_contributions': True
        }

class ProjectSingleObservation(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    template_name = 'contributions/observation.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id, observation_id):
        """
        Creates the request context for rendering the page
        """
        user = self.request.user
        project = Project.objects.as_admin(user, project_id)
        observation = project.observations.get(
            pk=observation_id)

        return {
            'project': project,
            'observation': observation
        }

class ProjectSingleMyObservation(LoginRequiredMixin, TemplateView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    template_name = 'contributions/observation.html'

    @handle_exceptions_for_admin
    def get_context_data(self, project_id, observation_id):
        """
        Creates the request context for rendering the page
        """
        user = self.request.user
        project = Project.objects.as_contributor(user, project_id)
        observation = project.observations.filter(creator=user).get(
            pk=observation_id)

        return {
            'project': project,
            'observation': observation
        }

class ProjectAjaxObservations(APIView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    """
    API Endpoint for a project in the AJAX API.
    /ajax/projects/:project_id/observations/
    """
    @handle_exceptions_for_ajax
    def get(self, request, project_id, format=None):
        project = Project.objects.as_admin(request.user, project_id)
        serializer = ContributionSerializer(
            project.observations.all(), many=True)
        return Response(serializer.data)

class ProjectAjaxMyObservations(APIView):
    """
    WILL BE REMOVED IN THE FINAL VERSION
    """
    """
    API Endpoint for a project in the AJAX API.
    /ajax/projects/:project_id/mycontributions/
    """
    @handle_exceptions_for_ajax
    def get(self, request, project_id, format=None):
        project = Project.objects.as_contributor(request.user, project_id)
        serializer = ContributionSerializer(
            project.observations.filter(creator=request.user), many=True)
        return Response(serializer.data)
<|endoftext|>


<commit_before>import hashlib
import json
import os
import re
import uuid
import xml.sax
from StringIO import StringIO
from collections import namedtuple
from urlparse import urljoin
from xml.sax import ContentHandler

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.db import models

import jingo
from caching.base import CachingManager, CachingMixin
from jinja2 import Markup
from jinja2.utils import LRUCache

from snippets.base.fields import CountryField, LocaleField, RegexField
from snippets.base.managers import ClientMatchRuleManager, SnippetManager
from snippets.base.storage import OverwriteStorage

CHANNELS = ('release', 'beta', 'aurora', 'nightly')
FIREFOX_STARTPAGE_VERSIONS = ('1', '2', '3', '4')
FENNEC_STARTPAGE_VERSIONS = ('1',)
SNIPPET_WEIGHTS = ((33, 'Appear 1/3rd as often as an average snippet'),
                   (50, 'Appear half as often as an average snippet'),
                   (66, 'Appear 2/3rds as often as an average snippet'),
                   (100, 'Appear as often as an average snippet'),
                   (150, 'Appear 1.5 times as often as an average snippet'),
                   (200, 'Appear twice as often as an average snippet'),
                   (300, 'Appear three times as often as an average snippet'))

def validate_xml(data):
    data_dict = json.loads(data)

    # set up a safer XML parser that does not resolve external
    # entities
    parser = xml.sax.make_parser()
    parser.setContentHandler(ContentHandler())
    parser.setFeature(xml.sax.handler.feature_external_ges, 0)

    for name, value in data_dict.items():
        # Skip over values that aren't strings.
        if not isinstance(value, basestring):
            continue

        value = value.encode('utf-8')
        xml_str = '
{0}
'.format(value)
        try:
            parser.parse(StringIO(xml_str))
        except xml.sax.SAXParseException as e:
            error_msg = (
                'XML Error in value "{name}": {message} in column {column}'
                .format(name=name, message=e.getMessage(),
                        column=e.getColumnNumber()))
            raise ValidationError(error_msg)
    return data

# NamedTuple that represents a user's client program.
Client = namedtuple('Client', (
    'startpage_version',
    'name',
    'version',
    'appbuildid',
    'build_target',
    'locale',
    'channel',
    'os_version',
    'distribution',
    'distribution_version'
))

# Cache for compiled snippet templates. Using jinja's built in cache
# requires either an extra trip to the database/cache or jumping through
# hoops.
template_cache = LRUCache(100)

class SnippetTemplate(CachingMixin, models.Model):
    """
    A template for the body of a snippet. Can have multiple variables that the
    snippet will fill in.
    """
    name = models.CharField(max_length=255, unique=True)
    code = models.TextField()

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = CachingManager()

    def render(self, ctx):
        ctx.setdefault('snippet_id', 0)

        # Check if template is in cache, and cache it if it's not.
        cache_key = hashlib.sha1(self.code).hexdigest()
        template = template_cache.get(cache_key)
        if not template:
            template = jingo.env.from_string(self.code)
            template_cache[cache_key] = template
        return template.render(ctx)

    def __unicode__(self):
        return self.name

class SnippetTemplateVariable(CachingMixin, models.Model):
    """
    A variable for a template that an individual snippet can fill in with its
    own content.
    """
    TEXT = 0
    IMAGE = 1
    SMALLTEXT = 2
    CHECKBOX = 3
    TYPE_CHOICES = ((TEXT, 'Text'), (IMAGE, 'Image'), (SMALLTEXT, 'Small Text'),
                    (CHECKBOX, 'Checkbox'))

    template = models.ForeignKey(SnippetTemplate, related_name='variable_set')
    name = models.CharField(max_length=255)
    type = models.IntegerField(choices=TYPE_CHOICES, default=TEXT)
    description = models.TextField(blank=True, default='')

    objects = models.Manager()
    cached_objects = CachingManager()

    def __unicode__(self):
        return u'{0}: {1}'.format(self.template.name, self.name)

class ClientMatchRule(CachingMixin, models.Model):
    """Defines a rule that matches a snippet to certain clients."""
    description = models.CharField(max_length=255, unique=True)
    is_exclusion = models.BooleanField(default=False)

    startpage_version = RegexField()
    name = RegexField()
    version = RegexField()
    appbuildid = RegexField()
    build_target = RegexField()
    locale = RegexField()
    channel = RegexField()
    os_version = RegexField()
    distribution = RegexField()
    distribution_version = RegexField()

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = ClientMatchRuleManager()

    class Meta:
        ordering = ('-modified',)

    def matches(self, client):
        """Evaluate whether this rule matches the given client."""
        match = True
        for field in client._fields:
            field_value = getattr(self, field, None)
            if not field_value:
                continue

            client_field_value = getattr(client, field)
            if field_value.startswith('/'):  # Match field as a regex.
                if re.match(field_value[1:-1], client_field_value) is None:
                    match = False
                    break
            elif field_value != client_field_value:  # Match field as a string.
                match = False
                break

        # Exclusion rules match clients that do not match their rule.
        return not match if self.is_exclusion else match

    def __unicode__(self):
        return self.description

class Snippet(CachingMixin, models.Model):
    name = models.CharField(max_length=255, unique=True)
    template = models.ForeignKey(SnippetTemplate)
    data = models.TextField(default='{}', validators=[validate_xml])

    priority = models.IntegerField(default=0, blank=True)
    disabled = models.BooleanField(default=True)

    country = CountryField('Geolocation Country', blank=True, default='')

    publish_start = models.DateTimeField(blank=True, null=True)
    publish_end = models.DateTimeField(blank=True, null=True)

    on_release = models.BooleanField(default=True, verbose_name='Release')
    on_beta = models.BooleanField(default=False, verbose_name='Beta')
    on_aurora = models.BooleanField(default=False, verbose_name='Aurora')
    on_nightly = models.BooleanField(default=False, verbose_name='Nightly')

    on_startpage_1 = models.BooleanField(default=False, verbose_name='Version 1')
    on_startpage_2 = models.BooleanField(default=True, verbose_name='Version 2')
    on_startpage_3 = models.BooleanField(default=True, verbose_name='Version 3')
    on_startpage_4 = models.BooleanField(default=True, verbose_name='Version 4')

    weight = models.IntegerField(
        'Prevalence', choices=SNIPPET_WEIGHTS, default=100,
        help_text='How often should this snippet be shown to users?')

    client_match_rules = models.ManyToManyField(
        ClientMatchRule, blank=True, verbose_name='Client Match Rules')

    exclude_from_search_providers = models.ManyToManyField(
        'SearchProvider', blank=True, verbose_name='Excluded Search Providers')

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = SnippetManager()

    class Meta:
        ordering = ('-modified',)

    def render(self):
        data = json.loads(self.data)
        snippet_id = self.id or 0
        data.setdefault('snippet_id', snippet_id)

        # Add snippet ID to template variables.
        for key, value in data.items():
            if isinstance(value, basestring):
                data[key] = value.replace(u'<snippet_id>', unicode(snippet_id))

        # Use a list for attrs to make the output order predictable.
        attrs = [('data-snippet-id', self.id),
                 ('data-weight', self.weight),
                 ('class', 'snippet-metadata')]
        if self.country:
            attrs.append(('data-country', self.country))

        if self.id:
            search_engine_identifiers = self.exclude_from_search_providers.values_list('identifier', flat=True)
            if search_engine_identifiers:
                attrs.append(('data-exclude-from-search-engines', ','.join(search_engine_identifiers)))

        attr_string = ' '.join('{0}="{1}"'.format(key, value) for key, value in
                               attrs)

        rendered_snippet = u'
{content}
'.format(
            attrs=attr_string,
            content=self.template.render(data)
        )

        return Markup(rendered_snippet)

    @property
    def channels(self):
        channels = []
        for channel in CHANNELS:
            if getattr(self, 'on_{0}'.format(channel), False):
                channels.append(channel)
        return channels

    def __unicode__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('base.show', kwargs={'snippet_id': self.id})

class SnippetLocale(CachingMixin, models.Model):
    snippet = models.ForeignKey(Snippet, related_name='locale_set')
    locale = LocaleField()

    objects = models.Manager()
    cached_objects = CachingManager()

class JSONSnippet(CachingMixin, models.Model):
    name = models.CharField(max_length=255, unique=True)
    priority = models.IntegerField(default=0, blank=True)
    disabled = models.BooleanField(default=True)

    icon = models.TextField(help_text='Icon should be a 96x96px PNG.')
    text = models.CharField(max_length=140,
                            help_text='Maximum length 140 characters.')
    url = models.CharField(max_length=500)

    country = CountryField('Geolocation Country', blank=True, default='')

    publish_start = models.DateTimeField(blank=True, null=True)
    publish_end = models.DateTimeField(blank=True, null=True)

    on_release = models.BooleanField(default=True, verbose_name='Release')
    on_beta = models.BooleanField(default=False, verbose_name='Beta')
    on_aurora = models.BooleanField(default=False, verbose_name='Aurora')
    on_nightly = models.BooleanField(default=False, verbose_name='Nightly')

    on_startpage_1 = models.BooleanField(default=True, verbose_name='Version 1')

    weight = models.IntegerField(
        'Prevalence', choices=SNIPPET_WEIGHTS, default=100,
        help_text='How often should this snippet be shown to users?')

    client_match_rules = models.ManyToManyField(
        ClientMatchRule, blank=True, verbose_name='Client Match Rules')

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = SnippetManager()

    class Meta:
        ordering = ('-modified',)
        verbose_name = 'JSON Snippet'

    def __unicode__(self):
        return self.name

class JSONSnippetLocale(CachingMixin, models.Model):
    snippet = models.ForeignKey(JSONSnippet, related_name='locale_set')
    locale = LocaleField()

    objects = models.Manager()
    cached_objects = CachingManager()

class UploadedFile(models.Model):
    FILES_ROOT = 'files'  # Directory name inside MEDIA_ROOT

    def _generate_filename(instance, filename):
        """Generate a new unique filename while preserving the original
        filename extension. If an existing UploadedFile gets updated
        do not generate a new filename.
        """

        # Instance is new UploadedFile, generate a filename
        if not instance.id:
            ext = os.path.splitext(filename)[1]
            filename = str(uuid.uuid4()) + ext
            return os.path.join(UploadedFile.FILES_ROOT, filename)

        # Use existing filename.
        obj = UploadedFile.objects.get(id=instance.id)
        return obj.file.name

    file = models.FileField(storage=OverwriteStorage(), upload_to=_generate_filename)
    name = models.CharField(max_length=255)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return self.name

    @property
    def url(self):
        site_url = getattr(settings, 'CDN_URL', settings.SITE_URL)
        full_url = urljoin(site_url, self.file.url)
        return full_url

    @property
    def snippets(self):
        return Snippet.objects.filter(
            models.Q(data__contains=self.file.url) |
            models.Q(template__code__contains=self.file.url)
        )

class SearchProvider(CachingMixin, models.Model):
    name = models.CharField(max_length=255, unique=True)
    identifier = models.CharField(max_length=255)

    objects = CachingManager()

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ('id',)
<commit_msg>Avoid values_list() to fetch excluded saerch providers.<commit_after>import hashlib
import json
import os
import re
import uuid
import xml.sax
from StringIO import StringIO
from collections import namedtuple
from urlparse import urljoin
from xml.sax import ContentHandler

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.db import models

import jingo
from caching.base import CachingManager, CachingMixin
from jinja2 import Markup
from jinja2.utils import LRUCache

from snippets.base.fields import CountryField, LocaleField, RegexField
from snippets.base.managers import ClientMatchRuleManager, SnippetManager
from snippets.base.storage import OverwriteStorage

CHANNELS = ('release', 'beta', 'aurora', 'nightly')
FIREFOX_STARTPAGE_VERSIONS = ('1', '2', '3', '4')
FENNEC_STARTPAGE_VERSIONS = ('1',)
SNIPPET_WEIGHTS = ((33, 'Appear 1/3rd as often as an average snippet'),
                   (50, 'Appear half as often as an average snippet'),
                   (66, 'Appear 2/3rds as often as an average snippet'),
                   (100, 'Appear as often as an average snippet'),
                   (150, 'Appear 1.5 times as often as an average snippet'),
                   (200, 'Appear twice as often as an average snippet'),
                   (300, 'Appear three times as often as an average snippet'))

def validate_xml(data):
    data_dict = json.loads(data)

    # set up a safer XML parser that does not resolve external
    # entities
    parser = xml.sax.make_parser()
    parser.setContentHandler(ContentHandler())
    parser.setFeature(xml.sax.handler.feature_external_ges, 0)

    for name, value in data_dict.items():
        # Skip over values that aren't strings.
        if not isinstance(value, basestring):
            continue

        value = value.encode('utf-8')
        xml_str = '
{0}
'.format(value)
        try:
            parser.parse(StringIO(xml_str))
        except xml.sax.SAXParseException as e:
            error_msg = (
                'XML Error in value "{name}": {message} in column {column}'
                .format(name=name, message=e.getMessage(),
                        column=e.getColumnNumber()))
            raise ValidationError(error_msg)
    return data

# NamedTuple that represents a user's client program.
Client = namedtuple('Client', (
    'startpage_version',
    'name',
    'version',
    'appbuildid',
    'build_target',
    'locale',
    'channel',
    'os_version',
    'distribution',
    'distribution_version'
))

# Cache for compiled snippet templates. Using jinja's built in cache
# requires either an extra trip to the database/cache or jumping through
# hoops.
template_cache = LRUCache(100)

class SnippetTemplate(CachingMixin, models.Model):
    """
    A template for the body of a snippet. Can have multiple variables that the
    snippet will fill in.
    """
    name = models.CharField(max_length=255, unique=True)
    code = models.TextField()

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = CachingManager()

    def render(self, ctx):
        ctx.setdefault('snippet_id', 0)

        # Check if template is in cache, and cache it if it's not.
        cache_key = hashlib.sha1(self.code).hexdigest()
        template = template_cache.get(cache_key)
        if not template:
            template = jingo.env.from_string(self.code)
            template_cache[cache_key] = template
        return template.render(ctx)

    def __unicode__(self):
        return self.name

class SnippetTemplateVariable(CachingMixin, models.Model):
    """
    A variable for a template that an individual snippet can fill in with its
    own content.
    """
    TEXT = 0
    IMAGE = 1
    SMALLTEXT = 2
    CHECKBOX = 3
    TYPE_CHOICES = ((TEXT, 'Text'), (IMAGE, 'Image'), (SMALLTEXT, 'Small Text'),
                    (CHECKBOX, 'Checkbox'))

    template = models.ForeignKey(SnippetTemplate, related_name='variable_set')
    name = models.CharField(max_length=255)
    type = models.IntegerField(choices=TYPE_CHOICES, default=TEXT)
    description = models.TextField(blank=True, default='')

    objects = models.Manager()
    cached_objects = CachingManager()

    def __unicode__(self):
        return u'{0}: {1}'.format(self.template.name, self.name)

class ClientMatchRule(CachingMixin, models.Model):
    """Defines a rule that matches a snippet to certain clients."""
    description = models.CharField(max_length=255, unique=True)
    is_exclusion = models.BooleanField(default=False)

    startpage_version = RegexField()
    name = RegexField()
    version = RegexField()
    appbuildid = RegexField()
    build_target = RegexField()
    locale = RegexField()
    channel = RegexField()
    os_version = RegexField()
    distribution = RegexField()
    distribution_version = RegexField()

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = ClientMatchRuleManager()

    class Meta:
        ordering = ('-modified',)

    def matches(self, client):
        """Evaluate whether this rule matches the given client."""
        match = True
        for field in client._fields:
            field_value = getattr(self, field, None)
            if not field_value:
                continue

            client_field_value = getattr(client, field)
            if field_value.startswith('/'):  # Match field as a regex.
                if re.match(field_value[1:-1], client_field_value) is None:
                    match = False
                    break
            elif field_value != client_field_value:  # Match field as a string.
                match = False
                break

        # Exclusion rules match clients that do not match their rule.
        return not match if self.is_exclusion else match

    def __unicode__(self):
        return self.description

class Snippet(CachingMixin, models.Model):
    name = models.CharField(max_length=255, unique=True)
    template = models.ForeignKey(SnippetTemplate)
    data = models.TextField(default='{}', validators=[validate_xml])

    priority = models.IntegerField(default=0, blank=True)
    disabled = models.BooleanField(default=True)

    country = CountryField('Geolocation Country', blank=True, default='')

    publish_start = models.DateTimeField(blank=True, null=True)
    publish_end = models.DateTimeField(blank=True, null=True)

    on_release = models.BooleanField(default=True, verbose_name='Release')
    on_beta = models.BooleanField(default=False, verbose_name='Beta')
    on_aurora = models.BooleanField(default=False, verbose_name='Aurora')
    on_nightly = models.BooleanField(default=False, verbose_name='Nightly')

    on_startpage_1 = models.BooleanField(default=False, verbose_name='Version 1')
    on_startpage_2 = models.BooleanField(default=True, verbose_name='Version 2')
    on_startpage_3 = models.BooleanField(default=True, verbose_name='Version 3')
    on_startpage_4 = models.BooleanField(default=True, verbose_name='Version 4')

    weight = models.IntegerField(
        'Prevalence', choices=SNIPPET_WEIGHTS, default=100,
        help_text='How often should this snippet be shown to users?')

    client_match_rules = models.ManyToManyField(
        ClientMatchRule, blank=True, verbose_name='Client Match Rules')

    exclude_from_search_providers = models.ManyToManyField(
        'SearchProvider', blank=True, verbose_name='Excluded Search Providers')

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = SnippetManager()

    class Meta:
        ordering = ('-modified',)

    def render(self):
        data = json.loads(self.data)
        snippet_id = self.id or 0
        data.setdefault('snippet_id', snippet_id)

        # Add snippet ID to template variables.
        for key, value in data.items():
            if isinstance(value, basestring):
                data[key] = value.replace(u'<snippet_id>', unicode(snippet_id))

        # Use a list for attrs to make the output order predictable.
        attrs = [('data-snippet-id', self.id),
                 ('data-weight', self.weight),
                 ('class', 'snippet-metadata')]
        if self.country:
            attrs.append(('data-country', self.country))

        if self.id:
            # Avoid using values_list() because django-cache-machine
            # does not support it.
            search_engine_identifiers = [
                provider.identifier for provider in self.exclude_from_search_providers.all()
            ]
            if search_engine_identifiers:
                attrs.append(('data-exclude-from-search-engines', ','.join(search_engine_identifiers)))

        attr_string = ' '.join('{0}="{1}"'.format(key, value) for key, value in
                               attrs)

        rendered_snippet = u'
{content}
'.format(
            attrs=attr_string,
            content=self.template.render(data)
        )

        return Markup(rendered_snippet)

    @property
    def channels(self):
        channels = []
        for channel in CHANNELS:
            if getattr(self, 'on_{0}'.format(channel), False):
                channels.append(channel)
        return channels

    def __unicode__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('base.show', kwargs={'snippet_id': self.id})

class SnippetLocale(CachingMixin, models.Model):
    snippet = models.ForeignKey(Snippet, related_name='locale_set')
    locale = LocaleField()

    objects = models.Manager()
    cached_objects = CachingManager()

class JSONSnippet(CachingMixin, models.Model):
    name = models.CharField(max_length=255, unique=True)
    priority = models.IntegerField(default=0, blank=True)
    disabled = models.BooleanField(default=True)

    icon = models.TextField(help_text='Icon should be a 96x96px PNG.')
    text = models.CharField(max_length=140,
                            help_text='Maximum length 140 characters.')
    url = models.CharField(max_length=500)

    country = CountryField('Geolocation Country', blank=True, default='')

    publish_start = models.DateTimeField(blank=True, null=True)
    publish_end = models.DateTimeField(blank=True, null=True)

    on_release = models.BooleanField(default=True, verbose_name='Release')
    on_beta = models.BooleanField(default=False, verbose_name='Beta')
    on_aurora = models.BooleanField(default=False, verbose_name='Aurora')
    on_nightly = models.BooleanField(default=False, verbose_name='Nightly')

    on_startpage_1 = models.BooleanField(default=True, verbose_name='Version 1')

    weight = models.IntegerField(
        'Prevalence', choices=SNIPPET_WEIGHTS, default=100,
        help_text='How often should this snippet be shown to users?')

    client_match_rules = models.ManyToManyField(
        ClientMatchRule, blank=True, verbose_name='Client Match Rules')

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    cached_objects = SnippetManager()

    class Meta:
        ordering = ('-modified',)
        verbose_name = 'JSON Snippet'

    def __unicode__(self):
        return self.name

class JSONSnippetLocale(CachingMixin, models.Model):
    snippet = models.ForeignKey(JSONSnippet, related_name='locale_set')
    locale = LocaleField()

    objects = models.Manager()
    cached_objects = CachingManager()

class UploadedFile(models.Model):
    FILES_ROOT = 'files'  # Directory name inside MEDIA_ROOT

    def _generate_filename(instance, filename):
        """Generate a new unique filename while preserving the original
        filename extension. If an existing UploadedFile gets updated
        do not generate a new filename.
        """

        # Instance is new UploadedFile, generate a filename
        if not instance.id:
            ext = os.path.splitext(filename)[1]
            filename = str(uuid.uuid4()) + ext
            return os.path.join(UploadedFile.FILES_ROOT, filename)

        # Use existing filename.
        obj = UploadedFile.objects.get(id=instance.id)
        return obj.file.name

    file = models.FileField(storage=OverwriteStorage(), upload_to=_generate_filename)
    name = models.CharField(max_length=255)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return self.name

    @property
    def url(self):
        site_url = getattr(settings, 'CDN_URL', settings.SITE_URL)
        full_url = urljoin(site_url, self.file.url)
        return full_url

    @property
    def snippets(self):
        return Snippet.objects.filter(
            models.Q(data__contains=self.file.url) |
            models.Q(template__code__contains=self.file.url)
        )

class SearchProvider(CachingMixin, models.Model):
    name = models.CharField(max_length=255, unique=True)
    identifier = models.CharField(max_length=255)

    objects = CachingManager()

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ('id',)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''Public section, including homepage and signup.'''
import calendar
import dateutil.parser
from datetime import datetime
from flask import (Blueprint, render_template)
from solarpi.public.helper import get_operating_hours
from solarpi.pvdata.helper import get_todays_max_power, get_max_daily_energy_last_seven_days, get_current_values, \
    get_last_years_energy, get_yearly_data, get_current_month_prediction
from solarpi.electricity.models import Electricity
from solarpi.electricity.helper import get_todays_electricity, get_last_year_export
from solarpi.weather.helper import get_weather_icon, get_current_weather

blueprint = Blueprint('public', __name__, static_folder="../static")

@blueprint.route("/")
def home():
    operating_hours = int(get_operating_hours())
    now = datetime.now()

    # weather
    w = get_current_weather()
    current_temp = w.temp
    current_weather = get_weather_icon(w.weather_id)

    # photovoltaic data
    pv = get_current_values()
    current_power = pv.current_power
    daily_energy = pv.daily_energy
    total_energy = pv.total_energy
    pac = pv.ac_1_p + pv.ac_2_p + pv.ac_3_p
    pdc = pv.dc_1_u * pv.dc_1_i + pv.dc_2_u * pv.dc_2_i + pv.dc_3_u * pv.dc_3_i
    if pdc > 0:
        efficiency = pac / pdc
    else:
        efficiency = 0.0

    last_updated = dateutil.parser.parse(pv.created_at).strftime('%Y-%m-%d %H:%M')

    todays_max_power = get_todays_max_power()
    max_daily_energy_last_seven_days = get_max_daily_energy_last_seven_days()

    # electricity im- and export
    todays_import, todays_export = 0.0, 0.0
    total_electricity = Electricity.query.order_by(Electricity.id.desc()).first()
    total_import = total_electricity.meter_180
    total_export = total_electricity.meter_280
    last_year_export = get_last_year_export()
    current_year_export = total_export - last_year_export.meter_280

    todays_electricity = get_todays_electricity()

    if todays_electricity:
        todays_import = todays_electricity.todays_import
        todays_export = todays_electricity.todays_export

    last_year_energy = get_last_years_energy()
    current_year_energy = total_energy - last_year_energy.total_energy

    last_year_series = [int(x[1]) for x in get_yearly_data(datetime.now().year - 1)]
    current_year_series = [int(x[1]) for x in get_yearly_data(datetime.now().year)]

    last_year_current_month_avg = last_year_series[now.month - 1] / calendar.monthrange(now.year, now.month)[1]
    current_month_prediction = get_current_month_prediction(current_year_series[-1], last_year_current_month_avg)

    return render_template("public/home.html",
                           current_power=current_power, daily_energy=daily_energy,
                           total_energy=total_energy, efficiency=efficiency,
                           current_temp=current_temp, current_weather=current_weather,
                           ac_1_p=pv.ac_1_p, ac_2_p=pv.ac_2_p, ac_3_p=pv.ac_3_p,
                           ac_1_u=pv.ac_1_u, ac_2_u=pv.ac_2_u, ac_3_u=pv.ac_3_u,
                           dc_1_u=pv.dc_1_u, dc_2_u=pv.dc_2_u, dc_3_u=pv.dc_3_u,
                           dc_1_i=pv.dc_1_i, dc_2_i=pv.dc_2_i, dc_3_i=pv.dc_3_i,
                           series_2013=last_year_series, series_2014=current_year_series,
                           current_month_pred=current_month_prediction,
                           current_year_energy=current_year_energy,
                           max_daily_energy_last_seven_days=max_daily_energy_last_seven_days,
                           todays_max_power=todays_max_power, last_updated=last_updated,
                           operating_hours=operating_hours, total_export=total_export,
                           total_import=total_import, todays_export=todays_export,
                           todays_import=todays_import, current_year_export=current_year_export)

@blueprint.route("/about/")
def about():
    return render_template("public/about.html")<commit_msg>deal with null values after midnight<commit_after># -*- coding: utf-8 -*-
'''Public section, including homepage and signup.'''
import calendar
import dateutil.parser
from datetime import datetime
from flask import (Blueprint, render_template)
from solarpi.public.helper import get_operating_hours
from solarpi.pvdata.helper import get_todays_max_power, get_max_daily_energy_last_seven_days, get_current_values, \
    get_last_years_energy, get_yearly_data, get_current_month_prediction
from solarpi.electricity.models import Electricity
from solarpi.electricity.helper import get_todays_electricity, get_last_year_export
from solarpi.weather.helper import get_weather_icon, get_current_weather

blueprint = Blueprint('public', __name__, static_folder="../static")

@blueprint.route("/")
def home():
    operating_hours = int(get_operating_hours())
    now = datetime.now()

    # weather
    w = get_current_weather()
    current_temp = w.temp
    current_weather = get_weather_icon(w.weather_id)

    # photovoltaic data
    pv = get_current_values()
    current_power = pv.current_power
    daily_energy = pv.daily_energy
    total_energy = pv.total_energy
    pac = pv.ac_1_p + pv.ac_2_p + pv.ac_3_p
    pdc = pv.dc_1_u * pv.dc_1_i + pv.dc_2_u * pv.dc_2_i + pv.dc_3_u * pv.dc_3_i
    if pdc > 0:
        efficiency = pac / pdc
    else:
        efficiency = 0.0

    last_updated = dateutil.parser.parse(pv.created_at).strftime('%Y-%m-%d %H:%M')

    todays_max_power = get_todays_max_power()
    if not todays_max_power:
        todays_max_power = 0
        daily_energy = 0
        current_temp = None

    max_daily_energy_last_seven_days = get_max_daily_energy_last_seven_days()

    # electricity im- and export
    todays_import, todays_export = 0.0, 0.0
    total_electricity = Electricity.query.order_by(Electricity.id.desc()).first()
    total_import = total_electricity.meter_180
    total_export = total_electricity.meter_280
    last_year_export = get_last_year_export()
    current_year_export = total_export - last_year_export.meter_280

    todays_electricity = get_todays_electricity()

    if todays_electricity:
        todays_import = todays_electricity.todays_import
        todays_export = todays_electricity.todays_export

    last_year_energy = get_last_years_energy()
    current_year_energy = total_energy - last_year_energy.total_energy

    last_year_series = [int(x[1]) for x in get_yearly_data(datetime.now().year - 1)]
    current_year_series = [int(x[1]) for x in get_yearly_data(datetime.now().year)]

    last_year_current_month_avg = last_year_series[now.month - 1] / calendar.monthrange(now.year, now.month)[1]
    current_month_prediction = get_current_month_prediction(current_year_series[-1], last_year_current_month_avg)

    return render_template("public/home.html",
                           current_power=current_power, daily_energy=daily_energy,
                           total_energy=total_energy, efficiency=efficiency,
                           current_temp=current_temp, current_weather=current_weather,
                           ac_1_p=pv.ac_1_p, ac_2_p=pv.ac_2_p, ac_3_p=pv.ac_3_p,
                           ac_1_u=pv.ac_1_u, ac_2_u=pv.ac_2_u, ac_3_u=pv.ac_3_u,
                           dc_1_u=pv.dc_1_u, dc_2_u=pv.dc_2_u, dc_3_u=pv.dc_3_u,
                           dc_1_i=pv.dc_1_i, dc_2_i=pv.dc_2_i, dc_3_i=pv.dc_3_i,
                           series_2013=last_year_series, series_2014=current_year_series,
                           current_month_pred=current_month_prediction,
                           current_year_energy=current_year_energy,
                           max_daily_energy_last_seven_days=max_daily_energy_last_seven_days,
                           todays_max_power=todays_max_power, last_updated=last_updated,
                           operating_hours=operating_hours, total_export=total_export,
                           total_import=total_import, todays_export=todays_export,
                           todays_import=todays_import, current_year_export=current_year_export)

@blueprint.route("/about/")
def about():
    return render_template("public/about.html")<|endoftext|>


<commit_before>"""
    prudent.stream
    ~~~~~~~~~~~~~~
    Implements a stream object that retains the state
    of iterations and can be used as the basis for
    lazily evaluated streams.
"""

def pipe(functions, data):
    """
    Pipe functions onto data, returning a value that
    is obtained by calling the consecutive functions
    with the return value of the previous function.

    :param functions: Iterable of functions.
    :param data: An object to pipe.
    """
    for func in functions:
        data = func(data)
    return data

class Stream(object):
    """
    A stream represents a series of iterables that
    can be iterated over and will retain their
    iterated state.
    """

    def __init__(self, iterable):
        """
        Create a new Stream object from a given
        iterable.

        :param iterable: An iterable.
        """
        self.iterables = []
        self.functions = []
        self.extend(iterable)

    def __iter__(self):
        for item in self.iterables[:]:
            for datum in item:
                yield pipe(self.functions, datum)
            self.iterables.remove(item)

    def extend(self, other):
        """
        Include another iterable into the stream,
        therefore extending the stream.

        :param other: The other iterable.
        """
        self.iterables.append(iter(other))
        return self

    __lshift__ = extend

    def use(self, function):
        """
        Register a function to be used by the stream
        for piped processing of each of the datum
        inside the stream.

        :param function: The function to add to the
            internal pipeline.
        """
        self.functions.append(function)
        return self
<commit_msg>make Stream.extend more sane<commit_after>"""
    prudent.stream
    ~~~~~~~~~~~~~~
    Implements a stream object that retains the state
    of iterations and can be used as the basis for
    lazily evaluated streams.
"""

def pipe(functions, data):
    """
    Pipe functions onto data, returning a value that
    is obtained by calling the consecutive functions
    with the return value of the previous function.

    :param functions: Iterable of functions.
    :param data: An object to pipe.
    """
    for func in functions:
        data = func(data)
    return data

class Stream(object):
    """
    A stream represents a series of iterables that
    can be iterated over and will retain their
    iterated state.
    """

    def __init__(self, iterable):
        """
        Create a new Stream object from a given
        iterable.

        :param iterable: An iterable.
        """
        self.iterables = []
        self.functions = []
        self.extend(iterable)

    def __iter__(self):
        for item in self.iterables[:]:
            for datum in item:
                yield pipe(self.functions, datum)
            self.iterables.remove(item)

    def extend(self, other):
        """
        Include another iterable into the stream,
        therefore extending the stream.

        :param other: The other iterable.
        """
        self.iterables.append(iter(other))

    def use(self, function):
        """
        Register a function to be used by the stream
        for piped processing of each of the datum
        inside the stream.

        :param function: The function to add to the
            internal pipeline.
        """
        self.functions.append(function)
        return self
<|endoftext|>


<commit_before>#!/usr/bin/env python
#
# $Id$
#
# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Module which provides compatibility with older Python versions."""

__all__ = ["namedtuple", "property"]

from operator import itemgetter as _itemgetter
from keyword import iskeyword as _iskeyword
import sys as _sys
import __builtin__

try:
    from collections import namedtuple
except ImportError:
    def namedtuple(typename, field_names, verbose=False, rename=False):
        """A collections.namedtuple implementation written in Python
        to support Python versions < 2.6.

        Taken from: http://code.activestate.com/recipes/500261/
        """
        # Parse and validate the field names.  Validation serves two
        # purposes, generating informative error messages and preventing
        # template injection attacks.
        if isinstance(field_names, basestring):
             # names separated by whitespace and/or commas
            field_names = field_names.replace(',', ' ').split()
        field_names = tuple(map(str, field_names))
        if rename:
            names = list(field_names)
            seen = set()
            for i, name in enumerate(names):
                if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name)
                    or not name or name[0].isdigit() or name.startswith('_')
                    or name in seen):
                        names[i] = '_%d' % i
                seen.add(name)
            field_names = tuple(names)
        for name in (typename,) + field_names:
            if not min(c.isalnum() or c=='_' for c in name):
                raise ValueError('Type names and field names can only contain ' \
                                 'alphanumeric characters and underscores: %r'
                                 % name)
            if _iskeyword(name):
                raise ValueError('Type names and field names cannot be a keyword: %r' \
                                 % name)
            if name[0].isdigit():
                raise ValueError('Type names and field names cannot start with a ' \
                                 'number: %r' % name)
        seen_names = set()
        for name in field_names:
            if name.startswith('_') and not rename:
                raise ValueError('Field names cannot start with an underscore: %r'
                                 % name)
            if name in seen_names:
                raise ValueError('Encountered duplicate field name: %r' % name)
            seen_names.add(name)

        # Create and fill-in the class template
        numfields = len(field_names)
        # tuple repr without parens or quotes
        argtxt = repr(field_names).replace("'", "")[1:-1]
        reprtxt = ', '.join('%s=%%r' % name for name in field_names)
        template = '''class %(typename)s(tuple):
        '%(typename)s(%(argtxt)s)' \n
        __slots__ = () \n
        _fields = %(field_names)r \n
        def __new__(_cls, %(argtxt)s):
            return _tuple.__new__(_cls, (%(argtxt)s)) \n
        @classmethod
        def _make(cls, iterable, new=tuple.__new__, len=len):
            'Make a new %(typename)s object from a sequence or iterable'
            result = new(cls, iterable)
            if len(result) != %(numfields)d:
                raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
            return result \n
        def __repr__(self):
            return '%(typename)s(%(reprtxt)s)' %% self \n
        def _asdict(self):
            'Return a new dict which maps field names to their values'
            return dict(zip(self._fields, self)) \n
        def _replace(_self, **kwds):
            'Return a new %(typename)s object replacing specified fields with new values'
            result = _self._make(map(kwds.pop, %(field_names)r, _self))
            if kwds:
                raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
            return result \n
        def __getnewargs__(self):
            return tuple(self) \n\n''' % locals()
        for i, name in enumerate(field_names):
            template += '        %s = _property(_itemgetter(%d))\n' % (name, i)
        if verbose:
            print template

        # Execute the template string in a temporary namespace
        namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
                         _property=property, _tuple=tuple)
        try:
            exec template in namespace
        except SyntaxError, e:
            raise SyntaxError(e.message + ':\n' + template)
        result = namespace[typename]

        # For pickling to work, the __module__ variable needs to be set
        # to the frame where the named tuple is created.  Bypass this
        # step in enviroments where sys._getframe is not defined (Jython
        # for example) or sys._getframe is not defined for arguments
        # greater than 0 (IronPython).
        try:
            result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            pass

        return result

# dirty hack to support property.setter on python < 2.6
property = property

if not hasattr(property, "setter"):

    class property(property):

        def __init__(self, fget, *args, **kwargs):
            self.__doc__ = fget.__doc__
            super(property, self).__init__(fget, *args, **kwargs)

        def setter(self, fset):
            cls_ns = _sys._getframe(1).f_locals
            for k, v in cls_ns.iteritems():
              if v == self:
                  propname = k
                  break
            cls_ns[propname] = property(self.fget, fset,
                                        self.fdel, self.__doc__)
            return cls_ns[propname]

        def deleter(self, fdel):
            cls_ns = _sys._getframe(1).f_locals
            for k, v in cls_ns.iteritems():
              if v == self:
                  propname = k
                  break
            cls_ns[propname] = property(self.fget, self.fset,
                                        fdel, self.__doc__)
            return cls_ns[propname]

    __builtin__.property = property

<commit_msg>update compatibilty hack for property setters/deleters (py < 2.6) with a cleaner and more efficient version<commit_after>#!/usr/bin/env python
#
# $Id$
#
# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Module which provides compatibility with older Python versions."""

__all__ = ["namedtuple", "property"]

from operator import itemgetter as _itemgetter
from keyword import iskeyword as _iskeyword
import sys as _sys
import __builtin__

try:
    from collections import namedtuple
except ImportError:
    def namedtuple(typename, field_names, verbose=False, rename=False):
        """A collections.namedtuple implementation written in Python
        to support Python versions < 2.6.

        Taken from: http://code.activestate.com/recipes/500261/
        """
        # Parse and validate the field names.  Validation serves two
        # purposes, generating informative error messages and preventing
        # template injection attacks.
        if isinstance(field_names, basestring):
             # names separated by whitespace and/or commas
            field_names = field_names.replace(',', ' ').split()
        field_names = tuple(map(str, field_names))
        if rename:
            names = list(field_names)
            seen = set()
            for i, name in enumerate(names):
                if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name)
                    or not name or name[0].isdigit() or name.startswith('_')
                    or name in seen):
                        names[i] = '_%d' % i
                seen.add(name)
            field_names = tuple(names)
        for name in (typename,) + field_names:
            if not min(c.isalnum() or c=='_' for c in name):
                raise ValueError('Type names and field names can only contain ' \
                                 'alphanumeric characters and underscores: %r'
                                 % name)
            if _iskeyword(name):
                raise ValueError('Type names and field names cannot be a keyword: %r' \
                                 % name)
            if name[0].isdigit():
                raise ValueError('Type names and field names cannot start with a ' \
                                 'number: %r' % name)
        seen_names = set()
        for name in field_names:
            if name.startswith('_') and not rename:
                raise ValueError('Field names cannot start with an underscore: %r'
                                 % name)
            if name in seen_names:
                raise ValueError('Encountered duplicate field name: %r' % name)
            seen_names.add(name)

        # Create and fill-in the class template
        numfields = len(field_names)
        # tuple repr without parens or quotes
        argtxt = repr(field_names).replace("'", "")[1:-1]
        reprtxt = ', '.join('%s=%%r' % name for name in field_names)
        template = '''class %(typename)s(tuple):
        '%(typename)s(%(argtxt)s)' \n
        __slots__ = () \n
        _fields = %(field_names)r \n
        def __new__(_cls, %(argtxt)s):
            return _tuple.__new__(_cls, (%(argtxt)s)) \n
        @classmethod
        def _make(cls, iterable, new=tuple.__new__, len=len):
            'Make a new %(typename)s object from a sequence or iterable'
            result = new(cls, iterable)
            if len(result) != %(numfields)d:
                raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
            return result \n
        def __repr__(self):
            return '%(typename)s(%(reprtxt)s)' %% self \n
        def _asdict(self):
            'Return a new dict which maps field names to their values'
            return dict(zip(self._fields, self)) \n
        def _replace(_self, **kwds):
            'Return a new %(typename)s object replacing specified fields with new values'
            result = _self._make(map(kwds.pop, %(field_names)r, _self))
            if kwds:
                raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
            return result \n
        def __getnewargs__(self):
            return tuple(self) \n\n''' % locals()
        for i, name in enumerate(field_names):
            template += '        %s = _property(_itemgetter(%d))\n' % (name, i)
        if verbose:
            print template

        # Execute the template string in a temporary namespace
        namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
                         _property=property, _tuple=tuple)
        try:
            exec template in namespace
        except SyntaxError, e:
            raise SyntaxError(e.message + ':\n' + template)
        result = namespace[typename]

        # For pickling to work, the __module__ variable needs to be set
        # to the frame where the named tuple is created.  Bypass this
        # step in enviroments where sys._getframe is not defined (Jython
        # for example) or sys._getframe is not defined for arguments
        # greater than 0 (IronPython).
        try:
            result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            pass

        return result

# hack to support property.setter/deleter on python < 2.6
# http://docs.python.org/library/functions.html?highlight=property#property
if hasattr(property, 'setter'):
    property = property
else:
    class property(__builtin__.property):
        __metaclass__ = type

        def __init__(self, fget, *args, **kwargs):
            super(property, self).__init__(fget, *args, **kwargs)
            self.__doc__ = fget.__doc__

        def getter(self, method):
            return property(method, self.fset, self.fdel)

        def setter(self, method):
            return property(self.fget, method, self.fdel)

        def deleter(self, method):
            return property(self.fget, self.fset, method)
<|endoftext|>