<commit_before>import math
import csv

from django.shortcuts import render, render_to_response, redirect
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.template import RequestContext
from django.contrib import messages
from django.core.context_processors import csrf
from django.core.exceptions import ObjectDoesNotExist
from django import forms

import control.settings as settings

from models import Dashboard, UserDashboard
from subscription.models import (Message,
                                 Subscription,
                                 MessageSet)
from servicerating.models import Response
from subscription.forms import (MessageFindForm,
                                MessageUpdateForm,
                                MessageConfirmForm,
                                SubscriptionFindForm,
                                SubscriptionConfirmCancelForm,
                                SubscriptionConfirmBabyForm,
                                SubscriptionCancelForm,
                                SubscriptionBabyForm,
                                )

def get_user_dashboards(request):
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):
        user_dashboards = UserDashboard.objects.get(user=request.user)
        dashboards = {}
        for dash in user_dashboards.dashboards.all():
            dashboards[dash.id] = dash.name
        return {"dashboards": dashboards}
    else:
        return {"dashboards": {}}

@login_required(login_url='/controlinterface/login/')
def index(request):
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        user_dashboards = UserDashboard.objects.get(user=request.user)
        return redirect('dashboard',
                        dashboard_id=user_dashboards.default_dashboard.id)
    else:
        return render(request,
                      'controlinterface/index_nodash.html')

@login_required(login_url='/controlinterface/login/')
def dashboard(request, dashboard_id):
    context = get_user_dashboards(request)
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        try:
            access = Dashboard.objects.get(
                id=dashboard_id).dashboards.filter(
                user=request.user).count()
            if access == 1:
                dashboard = Dashboard.objects.get(id=dashboard_id)
                dashboard_widgets = dashboard.widgets.all()

                widgets = {}
                for widget in dashboard_widgets:
                    widgets[widget.id] = {
                        "config": widget,
                        "data": widget.data.all()
                    }

                context.update({
                    "widgets": widgets,
                    "dashboard_api_key": settings.DASHBOARD_API_KEY
                })

                return render(request,
                              'controlinterface/index.html',
                              context)
            else:
                return render(request,
                              'controlinterface/index_notdashallowed.html')
        except ObjectDoesNotExist:
            # User tried to access a dashboard they're not allowed to
            return render(request,
                          'controlinterface/index_notdashallowed.html')
    else:
        return render(request,
                      'controlinterface/index_nodash.html')

@login_required(login_url='/controlinterface/login/')
def message_edit(request):
    context = get_user_dashboards(request)
    if request.method == "POST" and request.POST["messageaction"] == "find":
        # Locate the record

        form = MessageFindForm(request.POST)
        if form.is_valid():
            try:
                message = Message.objects.get(
                    message_set_id=form.cleaned_data['message_set'],
                    sequence_number=form.cleaned_data['sequence_number'],
                    lang=form.cleaned_data['lang'])
                updateform = MessageUpdateForm()
                updateform.fields["message_id"].initial = message.id
                updateform.fields["content"].initial = message.content
                context.update({
                    "updateform": updateform,
                    "contentlength": len(message.content)
                })
                context.update(csrf(request))

            except ObjectDoesNotExist:
                messages.error(request,
                               "Message could not be found",
                               extra_tags="danger")
                context = {"form": form}
                context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["messageaction"] == "update":
        # Update the record
        updateform = MessageUpdateForm(request.POST)
        if updateform.is_valid():
            confirmform = MessageConfirmForm()
            confirmform.fields[
                "message_id"].initial = updateform.cleaned_data['message_id']
            confirmform.fields[
                "content"].initial = updateform.cleaned_data['content']
            context.update({"confirmform": confirmform,
                            "content": updateform.cleaned_data['content']})
            context.update(csrf(request))
        else:
            # Errors are handled by bootstrap form
            context.update({"updateform": updateform})
        context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["messageaction"] == "confirm":
        # Update the record
        confirmform = MessageConfirmForm(request.POST)
        if confirmform.is_valid():
            try:
                message = Message.objects.get(
                    pk=confirmform.cleaned_data['message_id'])
                message.content = confirmform.cleaned_data['content']
                message.save()
                messages.success(request,
                                 "Message has been updated",
                                 extra_tags="success")
                # Load the blank find form again
                form = MessageFindForm()
                context.update({"form": form})
                context.update(csrf(request))
            except ObjectDoesNotExist:
                messages.error(request,
                               "Message could not be found",
                               extra_tags="danger")
                context.update({"confirmform": confirmform})
                context.update(csrf(request))

        else:
            # Errors are handled by bootstrap form
            context.update({"confirmform": confirmform})
        context.update(csrf(request))
    else:
        form = MessageFindForm()
        context.update({"form": form})
        context.update(csrf(request))

    return render_to_response("controlinterface/messages.html",
                              context,
                              context_instance=RequestContext(request))

@login_required(login_url='/controlinterface/login/')
def subscription_edit(request):
    context = get_user_dashboards(request)
    if request.method == "POST" and request.POST["subaction"] == "find":
        # Locate the record
        form = SubscriptionFindForm(request.POST)
        if form.is_valid():
            subscriptions = Subscription.objects.filter(
                to_addr=form.cleaned_data['msisdn'])
            if subscriptions.count() == 0:
                messages.error(request,
                               "Subscriber could not be found",
                               extra_tags="danger")
                context.update({"form": form})
                context.update(csrf(request))
            else:
                confirmcancelform = SubscriptionConfirmCancelForm()
                confirmcancelform.fields[
                    "msisdn"].initial = form.cleaned_data['msisdn']
                confirmbabyform = SubscriptionConfirmBabyForm()
                confirmbabyform.fields["msisdn"].initial = \
                    form.cleaned_data['msisdn']
                confirmbabyform.fields["existing_id"].initial = \
                    subscriptions[0].id
                context.update({
                    "subscriptions": subscriptions,
                    "confirmcancelform": confirmcancelform,
                    "confirmbabyform": confirmbabyform,
                })
                context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "confirmcancel":
        # Confirm before update the record

        confirmcancelform = SubscriptionConfirmCancelForm(request.POST)
        if confirmcancelform.is_valid():
            cancelform = SubscriptionCancelForm()
            cancelform.fields["msisdn"].initial = \
                confirmcancelform.cleaned_data['msisdn']
            form = SubscriptionFindForm()
            form.fields["msisdn"].widget = forms.HiddenInput()
            form.fields["msisdn"].initial = \
                confirmcancelform.cleaned_data['msisdn']
            context.update({
                "cancelform": cancelform,
                "form": form
            })
            context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "confirmbaby":
        # Confirm before update the record

        confirmbabyform = SubscriptionConfirmBabyForm(request.POST)
        if confirmbabyform.is_valid():
            babyform = SubscriptionBabyForm()
            babyform.fields["msisdn"].initial = \
                confirmbabyform.cleaned_data['msisdn']
            babyform.fields["existing_id"].initial = \
                confirmbabyform.cleaned_data['existing_id']
            form = SubscriptionFindForm()
            form.fields["msisdn"].widget = forms.HiddenInput()
            form.fields["msisdn"].initial = \
                confirmbabyform.cleaned_data['msisdn']
            context.update({
                "babyform": babyform,
                "form": form
            })
            context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "cancel":
        # Update the record
        cancelform = SubscriptionCancelForm(request.POST)
        if cancelform.is_valid():
            subscriptions = Subscription.objects.filter(
                to_addr=cancelform.cleaned_data['msisdn']).update(
                active=False)
            messages.success(request,
                             "All subscriptions for %s have been cancelled" %
                             cancelform.cleaned_data['msisdn'],
                             extra_tags="success")
            form = SubscriptionFindForm()
            form.fields[
                "msisdn"].initial = cancelform.cleaned_data['msisdn']
            context.update({"form": form})
            context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "baby":
        # Update the record
        babyform = SubscriptionBabyForm(request.POST)
        if babyform.is_valid():
            # deactivate all
            subscriptions = Subscription.objects.filter(
                to_addr=babyform.cleaned_data['msisdn']).update(
                active=False)
            # load existing to clone
            subscription = Subscription.objects.get(
                pk=babyform.cleaned_data['existing_id'])
            subscription.pk = None
            subscription.process_status = 0  # Ready
            subscription.active = True
            subscription.completed = False
            subscription.next_sequence_number = 1
            newsub = subscription
            baby_message_set = MessageSet.objects.get(short_name="baby1")
            newsub.message_set = baby_message_set
            newsub.schedule = (
                baby_message_set.default_schedule)
            newsub.save()

            messages.success(request,
                             "All active subscriptions for %s have been "
                             "cancelled and baby subscription added" %
                             babyform.cleaned_data['msisdn'],
                             extra_tags="success")
            # Load the blank find form again
            form = SubscriptionFindForm()
            form.fields[
                "msisdn"].initial = babyform.cleaned_data['msisdn']
            context.update({"form": form})
        context.update(csrf(request))
    else:
        form = SubscriptionFindForm()
        context.update({"form": form})
        context.update(csrf(request))

    return render_to_response("controlinterface/subscription.html",
                              context,
                              context_instance=RequestContext(request))

def empty_response_map():
    response_map = {
        'question_1_friendliness': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        },
        'question_2_waiting_times_feel': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        },
        'question_3_waiting_times_length': {
            'less-than-an-hour': 0,
            'between-1-and-3-hours': 0,
            'more-than-4-hours': 0,
            'all-day': 0
        },
        'question_4_cleanliness': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        },
        'question_5_privacy': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        }
    }
    return response_map

@login_required(login_url='/controlinterface/login/')
def servicerating(request):
    context = get_user_dashboards(request)
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        averages = {}

        all_responses = Response.objects.all()
        num_questions = 5.0
        total_responses = 0

        response_map = empty_response_map()

        for response in all_responses:
            total_responses += 1
            response_map[response.key][response.value] += 1

        num_ratings = math.ceil(total_responses / num_questions)

        averages_questions = [
            'question_1_friendliness',
            'question_2_waiting_times_feel',
            'question_4_cleanliness',
            'question_5_privacy'
        ]

        question_3_map = response_map['question_3_waiting_times_length']
        waiting_times = {
            'less_than_an_hour': round(
                (question_3_map['less-than-an-hour'] / num_ratings * 100),
                1),
            'between_1_and_3_hours': round(
                (question_3_map['between-1-and-3-hours'] / num_ratings * 100),
                1),
            'more_than_4_hours': round(
                (question_3_map['more-than-4-hours'] / num_ratings * 100),
                1),
            'all_day': round(
                (question_3_map['all-day'] / num_ratings * 100),
                1)
        }

        for question in averages_questions:
            averages[question] = round((
                (response_map[question]['very-satisfied'] * 4) +
                (response_map[question]['satisfied'] * 3) +
                (response_map[question]['not-satisfied'] * 2) +
                (response_map[question]['very-unsatisfied'] * 1)
            ) / num_ratings, 1)

        context.update({
            'averages': averages,
            'waiting_times': waiting_times
        })

        return render(request, 'controlinterface/serviceratings.html', context)

@login_required(login_url='/controlinterface/login/')
def servicerating_report(request):
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        qs = Response.objects.raw("""
            SELECT servicerating_response.*, servicerating_extra.value
            AS clinic_code from servicerating_response
            INNER JOIN servicerating_extra ON
            servicerating_response.contact_id = servicerating_extra.contact_id
            WHERE servicerating_extra.key = 'clinic_code'""")

        # Create the HttpResponse object with the appropriate CSV header.
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = (
            'attachment; filename="servicerating_incl_clinic_code.csv"')

        writer = csv.writer(response)

        writer.writerow(["Rating ID", "Contact ID", "Key", "Value",
                         "Created At", "Updated At", "Clinic Code"])
        for obj in qs:
            writer.writerow([obj.id, obj.contact_id, obj.key, obj.value,
                             obj.created_at, obj.updated_at, obj.clinic_code])

        return response
<commit_msg>Update error on messages gt 160<commit_after>import math
import csv

from django.shortcuts import render, render_to_response, redirect
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.template import RequestContext
from django.contrib import messages
from django.core.context_processors import csrf
from django.core.exceptions import ObjectDoesNotExist
from django import forms

import control.settings as settings

from models import Dashboard, UserDashboard
from subscription.models import (Message,
                                 Subscription,
                                 MessageSet)
from servicerating.models import Response
from subscription.forms import (MessageFindForm,
                                MessageUpdateForm,
                                MessageConfirmForm,
                                SubscriptionFindForm,
                                SubscriptionConfirmCancelForm,
                                SubscriptionConfirmBabyForm,
                                SubscriptionCancelForm,
                                SubscriptionBabyForm,
                                )

def get_user_dashboards(request):
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):
        user_dashboards = UserDashboard.objects.get(user=request.user)
        dashboards = {}
        for dash in user_dashboards.dashboards.all():
            dashboards[dash.id] = dash.name
        return {"dashboards": dashboards}
    else:
        return {"dashboards": {}}

@login_required(login_url='/controlinterface/login/')
def index(request):
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        user_dashboards = UserDashboard.objects.get(user=request.user)
        return redirect('dashboard',
                        dashboard_id=user_dashboards.default_dashboard.id)
    else:
        return render(request,
                      'controlinterface/index_nodash.html')

@login_required(login_url='/controlinterface/login/')
def dashboard(request, dashboard_id):
    context = get_user_dashboards(request)
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        try:
            access = Dashboard.objects.get(
                id=dashboard_id).dashboards.filter(
                user=request.user).count()
            if access == 1:
                dashboard = Dashboard.objects.get(id=dashboard_id)
                dashboard_widgets = dashboard.widgets.all()

                widgets = {}
                for widget in dashboard_widgets:
                    widgets[widget.id] = {
                        "config": widget,
                        "data": widget.data.all()
                    }

                context.update({
                    "widgets": widgets,
                    "dashboard_api_key": settings.DASHBOARD_API_KEY
                })

                return render(request,
                              'controlinterface/index.html',
                              context)
            else:
                return render(request,
                              'controlinterface/index_notdashallowed.html')
        except ObjectDoesNotExist:
            # User tried to access a dashboard they're not allowed to
            return render(request,
                          'controlinterface/index_notdashallowed.html')
    else:
        return render(request,
                      'controlinterface/index_nodash.html')

@login_required(login_url='/controlinterface/login/')
def message_edit(request):
    context = get_user_dashboards(request)
    if request.method == "POST" and request.POST["messageaction"] == "find":
        # Locate the record

        form = MessageFindForm(request.POST)
        if form.is_valid():
            try:
                message = Message.objects.get(
                    message_set_id=form.cleaned_data['message_set'],
                    sequence_number=form.cleaned_data['sequence_number'],
                    lang=form.cleaned_data['lang'])
                updateform = MessageUpdateForm()
                updateform.fields["message_id"].initial = message.id
                updateform.fields["content"].initial = message.content
                context.update({
                    "updateform": updateform,
                    "contentlength": len(message.content)
                })
                context.update(csrf(request))

            except ObjectDoesNotExist:
                messages.error(request,
                               "Message could not be found",
                               extra_tags="danger")
                context = {"form": form}
                context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["messageaction"] == "update":
        # Update the record
        updateform = MessageUpdateForm(request.POST)
        if updateform.is_valid():
            if len(updateform.cleaned_data['content']) > 160:
                messages.error(request,
                               "SMS messages cannot be longer than 160 "
                               "characters. Please edit this message to be "
                               "under 160 characters in order to save your "
                               "changes",
                               extra_tags="danger")
                context.update({"updateform": updateform})
            else:
                confirmform = MessageConfirmForm()
                confirmform.fields[
                    "message_id"].initial = updateform.cleaned_data['message_id']
                confirmform.fields[
                    "content"].initial = updateform.cleaned_data['content']
                context.update({"confirmform": confirmform,
                                "content": updateform.cleaned_data['content']})
                context.update(csrf(request))
        else:
            # Errors are handled by bootstrap form
            context.update({"updateform": updateform})
        context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["messageaction"] == "confirm":
        # Update the record
        confirmform = MessageConfirmForm(request.POST)
        if confirmform.is_valid():
            try:
                message = Message.objects.get(
                    pk=confirmform.cleaned_data['message_id'])
                message.content = confirmform.cleaned_data['content']
                message.save()
                messages.success(request,
                                 "Message has been updated",
                                 extra_tags="success")
                # Load the blank find form again
                form = MessageFindForm()
                context.update({"form": form})
                context.update(csrf(request))
            except ObjectDoesNotExist:
                messages.error(request,
                               "Message could not be found",
                               extra_tags="danger")
                context.update({"confirmform": confirmform})
                context.update(csrf(request))

        else:
            # Errors are handled by bootstrap form
            context.update({"confirmform": confirmform})
        context.update(csrf(request))
    else:
        form = MessageFindForm()
        context.update({"form": form})
        context.update(csrf(request))

    return render_to_response("controlinterface/messages.html",
                              context,
                              context_instance=RequestContext(request))

@login_required(login_url='/controlinterface/login/')
def subscription_edit(request):
    context = get_user_dashboards(request)
    if request.method == "POST" and request.POST["subaction"] == "find":
        # Locate the record
        form = SubscriptionFindForm(request.POST)
        if form.is_valid():
            subscriptions = Subscription.objects.filter(
                to_addr=form.cleaned_data['msisdn'])
            if subscriptions.count() == 0:
                messages.error(request,
                               "Subscriber could not be found",
                               extra_tags="danger")
                context.update({"form": form})
                context.update(csrf(request))
            else:
                confirmcancelform = SubscriptionConfirmCancelForm()
                confirmcancelform.fields[
                    "msisdn"].initial = form.cleaned_data['msisdn']
                confirmbabyform = SubscriptionConfirmBabyForm()
                confirmbabyform.fields["msisdn"].initial = \
                    form.cleaned_data['msisdn']
                confirmbabyform.fields["existing_id"].initial = \
                    subscriptions[0].id
                context.update({
                    "subscriptions": subscriptions,
                    "confirmcancelform": confirmcancelform,
                    "confirmbabyform": confirmbabyform,
                })
                context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "confirmcancel":
        # Confirm before update the record

        confirmcancelform = SubscriptionConfirmCancelForm(request.POST)
        if confirmcancelform.is_valid():
            cancelform = SubscriptionCancelForm()
            cancelform.fields["msisdn"].initial = \
                confirmcancelform.cleaned_data['msisdn']
            form = SubscriptionFindForm()
            form.fields["msisdn"].widget = forms.HiddenInput()
            form.fields["msisdn"].initial = \
                confirmcancelform.cleaned_data['msisdn']
            context.update({
                "cancelform": cancelform,
                "form": form
            })
            context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "confirmbaby":
        # Confirm before update the record

        confirmbabyform = SubscriptionConfirmBabyForm(request.POST)
        if confirmbabyform.is_valid():
            babyform = SubscriptionBabyForm()
            babyform.fields["msisdn"].initial = \
                confirmbabyform.cleaned_data['msisdn']
            babyform.fields["existing_id"].initial = \
                confirmbabyform.cleaned_data['existing_id']
            form = SubscriptionFindForm()
            form.fields["msisdn"].widget = forms.HiddenInput()
            form.fields["msisdn"].initial = \
                confirmbabyform.cleaned_data['msisdn']
            context.update({
                "babyform": babyform,
                "form": form
            })
            context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "cancel":
        # Update the record
        cancelform = SubscriptionCancelForm(request.POST)
        if cancelform.is_valid():
            subscriptions = Subscription.objects.filter(
                to_addr=cancelform.cleaned_data['msisdn']).update(
                active=False)
            messages.success(request,
                             "All subscriptions for %s have been cancelled" %
                             cancelform.cleaned_data['msisdn'],
                             extra_tags="success")
            form = SubscriptionFindForm()
            form.fields[
                "msisdn"].initial = cancelform.cleaned_data['msisdn']
            context.update({"form": form})
            context.update(csrf(request))
    elif request.method == "POST" and \
            request.POST["subaction"] == "baby":
        # Update the record
        babyform = SubscriptionBabyForm(request.POST)
        if babyform.is_valid():
            # deactivate all
            subscriptions = Subscription.objects.filter(
                to_addr=babyform.cleaned_data['msisdn']).update(
                active=False)
            # load existing to clone
            subscription = Subscription.objects.get(
                pk=babyform.cleaned_data['existing_id'])
            subscription.pk = None
            subscription.process_status = 0  # Ready
            subscription.active = True
            subscription.completed = False
            subscription.next_sequence_number = 1
            newsub = subscription
            baby_message_set = MessageSet.objects.get(short_name="baby1")
            newsub.message_set = baby_message_set
            newsub.schedule = (
                baby_message_set.default_schedule)
            newsub.save()

            messages.success(request,
                             "All active subscriptions for %s have been "
                             "cancelled and baby subscription added" %
                             babyform.cleaned_data['msisdn'],
                             extra_tags="success")
            # Load the blank find form again
            form = SubscriptionFindForm()
            form.fields[
                "msisdn"].initial = babyform.cleaned_data['msisdn']
            context.update({"form": form})
        context.update(csrf(request))
    else:
        form = SubscriptionFindForm()
        context.update({"form": form})
        context.update(csrf(request))

    return render_to_response("controlinterface/subscription.html",
                              context,
                              context_instance=RequestContext(request))

def empty_response_map():
    response_map = {
        'question_1_friendliness': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        },
        'question_2_waiting_times_feel': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        },
        'question_3_waiting_times_length': {
            'less-than-an-hour': 0,
            'between-1-and-3-hours': 0,
            'more-than-4-hours': 0,
            'all-day': 0
        },
        'question_4_cleanliness': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        },
        'question_5_privacy': {
            'very-satisfied': 0,
            'satisfied': 0,
            'not-satisfied': 0,
            'very-unsatisfied': 0
        }
    }
    return response_map

@login_required(login_url='/controlinterface/login/')
def servicerating(request):
    context = get_user_dashboards(request)
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        averages = {}

        all_responses = Response.objects.all()
        num_questions = 5.0
        total_responses = 0

        response_map = empty_response_map()

        for response in all_responses:
            total_responses += 1
            response_map[response.key][response.value] += 1

        num_ratings = math.ceil(total_responses / num_questions)

        averages_questions = [
            'question_1_friendliness',
            'question_2_waiting_times_feel',
            'question_4_cleanliness',
            'question_5_privacy'
        ]

        question_3_map = response_map['question_3_waiting_times_length']
        waiting_times = {
            'less_than_an_hour': round(
                (question_3_map['less-than-an-hour'] / num_ratings * 100),
                1),
            'between_1_and_3_hours': round(
                (question_3_map['between-1-and-3-hours'] / num_ratings * 100),
                1),
            'more_than_4_hours': round(
                (question_3_map['more-than-4-hours'] / num_ratings * 100),
                1),
            'all_day': round(
                (question_3_map['all-day'] / num_ratings * 100),
                1)
        }

        for question in averages_questions:
            averages[question] = round((
                (response_map[question]['very-satisfied'] * 4) +
                (response_map[question]['satisfied'] * 3) +
                (response_map[question]['not-satisfied'] * 2) +
                (response_map[question]['very-unsatisfied'] * 1)
            ) / num_ratings, 1)

        context.update({
            'averages': averages,
            'waiting_times': waiting_times
        })

        return render(request, 'controlinterface/serviceratings.html', context)

@login_required(login_url='/controlinterface/login/')
def servicerating_report(request):
    if (request.user.has_perm('controlinterface.view_dashboard_private') or
            request.user.has_perm('controlinterface.view_dashboard_summary')):

        qs = Response.objects.raw("""
            SELECT servicerating_response.*, servicerating_extra.value
            AS clinic_code from servicerating_response
            INNER JOIN servicerating_extra ON
            servicerating_response.contact_id = servicerating_extra.contact_id
            WHERE servicerating_extra.key = 'clinic_code'""")

        # Create the HttpResponse object with the appropriate CSV header.
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = (
            'attachment; filename="servicerating_incl_clinic_code.csv"')

        writer = csv.writer(response)

        writer.writerow(["Rating ID", "Contact ID", "Key", "Value",
                         "Created At", "Updated At", "Clinic Code"])
        for obj in qs:
            writer.writerow([obj.id, obj.contact_id, obj.key, obj.value,
                             obj.created_at, obj.updated_at, obj.clinic_code])

        return response
<|endoftext|>


<commit_before>import os

from sendgrid import sendgrid, Email
from sendgrid.helpers.mail import Content, Mail, MailSettings, SandBoxMode

def send_email(subject, content):
    sg = sendgrid.SendGridAPIClient(apikey=os.environ.get("SENDGRID_API_KEY"))
    from_email = Email(name="TV sort", email="tvsortsl@gmail.com")
    to_email = Email(name="TV sort", email="tvsortsl@gmail.com")
    content = Content("text/plain", content)
    mail = Mail(from_email, subject, to_email, content)
    sand_box = os.environ.get("SAND_BOX")

    if sand_box == "true":
        mail_settings = MailSettings()
        mail_settings.sandbox_mode = SandBoxMode(True)
        mail.mail_settings = mail_settings

    return sg.client.mail.send.post(request_body=mail.get())
<commit_msg>test pre-commits<commit_after>import os

from sendgrid import sendgrid, Email
from sendgrid.helpers.mail import Content, Mail, MailSettings, SandBoxMode

def Send_email(subject, content):
    sg = sendgrid.SendGridAPIClient(apikey=os.environ.get("SENDGRID_API_KEY"))
    from_email = Email(name="TV sort", email="tvsortsl@gmail.com")
    to_email = Email(name="TV sort", email="tvsortsl@gmail.com")
    content = Content("text/plain", content)
    mail = Mail(from_email, subject, to_email, content)
    sand_box = os.environ.get("SAND_BOX")

    if sand_box == "true":
        mail_settings = MailSettings()
        mail_settings.sandbox_mode = SandBoxMode(True)
        mail.mail_settings = mail_settings

    return sg.client.mail.send.post(request_body=mail.get())
<|endoftext|>


<commit_before>import logging
import datetime

from pajbot.modules import BaseModule, ModuleSetting
from pajbot.models.command import Command, CommandExample
from pajbot.models.handler import HandlerManager

from numpy import random

log = logging.getLogger(__name__)

def init_dueling_variables(user):
    if hasattr(user, 'duel_request'):
        return False
    user.duel_request = False
    user.duel_target = False
    user.duel_price = 0

class DuelModule(BaseModule):

    ID = __name__.split('.')[-1]
    NAME = 'Duel (mini game)'
    DESCRIPTION = 'Let players duel to win or lose points.'
    SETTINGS = [
            ModuleSetting(
                key='max_pot',
                label='How many points you can duel for at most',
                type='number',
                required=True,
                placeholder='',
                default=420,
                constraints={
                    'min_value': 0,
                    'max_value': 69000,
                    }),
            ModuleSetting(
                key='message_won',
                label='Winner message | Available arguments: {winner}, {loser}',
                type='text',
                required=True,
                placeholder='{winner} won the duel vs {loser} PogChamp',
                default='{winner} won the duel vs {loser} PogChamp',
                constraints={
                    'min_str_len': 10,
                    'max_str_len': 400,
                    }),
            ModuleSetting(
                key='message_won_points',
                label='Points message | Available arguments: {winner}, {loser}, {total_pot}, {extra_points}',
                type='text',
                required=True,
                placeholder='{winner} won the duel vs {loser} PogChamp . The pot was {total_pot}, the winner gets their bet back + {extra_points} points',
                default='{winner} won the duel vs {loser} PogChamp . The pot was {total_pot}, the winner gets their bet back + {extra_points} points',
                constraints={
                    'min_str_len': 10,
                    'max_str_len': 400,
                    }),
            ModuleSetting(
                key='online_global_cd',
                label='Global cooldown (seconds)',
                type='number',
                required=True,
                placeholder='',
                default=0,
                constraints={
                    'min_value': 0,
                    'max_value': 120,
                    }),
            ModuleSetting(
                key='online_user_cd',
                label='Per-user cooldown (seconds)',
                type='number',
                required=True,
                placeholder='',
                default=5,
                constraints={
                    'min_value': 0,
                    'max_value': 240,
                    }),
                ]

    def load_commands(self, **options):
        self.commands['duel'] = Command.raw_command(self.initiate_duel,
                delay_all=self.settings['online_global_cd'],
                delay_user=self.settings['online_user_cd'],
                description='Initiate a duel with a user',
                examples=[
                    CommandExample(None, '0-point duel',
                        chat='user:!duel Karl_Kons\n'
                        'bot>user:You have challenged Karl_Kons for 0 points',
                        description='Duel Karl_Kons for 0 points').parse(),
                    CommandExample(None, '69-point duel',
                        chat='user:!duel Karl_Kons 69\n'
                        'bot>user:You have challenged Karl_Kons for 69 points',
                        description='Duel Karl_Kons for 69 points').parse(),
                    ],
                )
        self.commands['cancelduel'] = Command.raw_command(self.cancel_duel,
                delay_all=0,
                delay_user=10)
        self.commands['accept'] = Command.raw_command(self.accept_duel,
                delay_all=0,
                delay_user=0)
        self.commands['decline'] = Command.raw_command(self.decline_duel,
                delay_all=0,
                delay_user=0)
        self.commands['deny'] = self.commands['decline']
        self.commands['duelstatus'] = Command.raw_command(self.status_duel,
                delay_all=0,
                delay_user=5)
        self.commands['duelstats'] = Command.raw_command(self.get_duel_stats,
                delay_all=0,
                delay_user=120)

    def initiate_duel(self, **options):
        """
        Initiate a duel with a user.
        You can also bet points on the winner.
        By default, the maximum amount of points you can spend is 420.

        How to add: !add funccommand duel initiate_duel --cd 0 --usercd 5
        How to use: !duel USERNAME POINTS_TO_BET
        """

        bot = options['bot']
        source = options['source']
        message = options['message']

        if message is None:
            return False

        max_pot = self.settings['max_pot']

        init_dueling_variables(source)

        msg_split = message.split()
        username = msg_split[0]
        user = bot.users.find(username)
        duel_price = 0
        if user is None:
            # No user was found with this username
            return False

        if len(msg_split) > 1:
            try:
                duel_price = int(msg_split[1])
                if duel_price < 0:
                    return False

                if duel_price > max_pot:
                    duel_price = max_pot
            except ValueError:
                pass

        if source.duel_target is not False:
            bot.whisper(source.username, 'You already have a duel request active with {}. Type !cancelduel to cancel your duel request.'.format(source.duel_target.username_raw))
            return False

        if user == source:
            # You cannot duel yourself
            return False

        if user.last_active is None or (datetime.datetime.now() - user._last_active).total_seconds() > 5 * 60:
            bot.whisper(source.username, 'This user has not been active in chat within the last 5 minutes. Get them to type in chat before sending another challenge')
            return False

        if not user.can_afford(duel_price) or not source.can_afford(duel_price):
            bot.whisper(source.username, 'You or your target do not have more than {} points, therefore you cannot duel for that amount.'.format(duel_price))
            return False

        init_dueling_variables(user)

        if user.duel_request is False:
            user.duel_request = source
            source.duel_target = user
            user.duel_price = duel_price
            bot.whisper(user.username, 'You have been challenged to a duel by {} for {} points. You can either !accept or !deny this challenge.'.format(source.username_raw, duel_price))
            bot.whisper(source.username, 'You have challenged {} for {} points'.format(user.username_raw, duel_price))
        else:
            bot.whisper(source.username, 'This person is already being challenged by {}. Ask them to answer the offer by typing !deny or !accept'.format(user.duel_request.username_raw))

    def cancel_duel(self, **options):
        """
        Cancel any duel requests you've sent.

        How to add: !add funccomand cancelduel|duelcancel cancel_duel --cd 0 --usercd 10
        How to use: !cancelduel
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)

        if source.duel_target is not False:
            bot.whisper(source.username, 'You have cancelled the duel vs {}'.format(source.duel_target.username_raw))
            source.duel_target.duel_request = False
            source.duel_target = False
            source.duel_request = False

    def accept_duel(self, **options):
        """
        Accepts any active duel requests you've received.

        How to add: !add funccommand accept accept_duel --cd 0 --usercd 0
        How to use: !accept
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)
        duel_tax = 0.3  # 30% tax

        if source.duel_request is not False:
            if not source.can_afford(source.duel_price) or not source.duel_request.can_afford(source.duel_price):
                bot.whisper(source.username, 'Your duel request with {} was cancelled due to one of you not having enough points.'.format(source.duel_request.username_raw))
                bot.whisper(source.duel_request.username, 'Your duel request with {} was cancelled due to one of you not having enough points.'.format(source.username_raw))
                source.duel_request.duel_target = False
                source.duel_request = False
                return False
            source.points -= source.duel_price
            source.duel_request.points -= source.duel_price
            winning_pot = int(source.duel_price * (1.0 - duel_tax))
            participants = [source, source.duel_request]
            winner = random.choice(participants)
            participants.remove(winner)
            loser = participants.pop()
            winner.points += source.duel_price
            winner.points += winning_pot

            bot.duel_manager.user_won(winner, winning_pot)
            bot.duel_manager.user_lost(loser, source.duel_price)

            arguments = {
                    'winner': winner.username,
                    'loser': loser.username,
                    'total_pot': source.duel_price,
                    'extra_points': winning_pot,
                    }

            if source.duel_price > 0:
                message = self.get_phrase('message_won_points', **arguments)
                bot.websocket_manager.emit('notification', {'message': '{} won the duel vs {}'.format(winner.username_raw, loser.username_raw)})
            else:
                message = self.get_phrase('message_won', **arguments)
            bot.say(message)
            source.duel_request.duel_target = False
            source.duel_request = False
            source.duel_price = 0
            HandlerManager.trigger('on_duel_complete',
                    winner, loser,
                    winning_pot, source.duel_price)

    def decline_duel(self, **options):
        """
        Declines any active duel requests you've received.

        How to add: !add funccommand deny|decline decline_duel --cd 0 --usercd 0
        How to use: !decline
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)

        if source.duel_request is not False:
            bot.whisper(source.username, 'You have declined the duel vs {}'.format(source.duel_request.username_raw))
            bot.whisper(source.duel_request.username, '{} declined the duel challenge with you.'.format(source.username_raw))
            source.duel_request.duel_target = False
            source.duel_request = False

    def status_duel(self, **options):
        """
        Whispers you the current status of your active duel requests/duel targets

        How to add: !add funccommand duelstatus|statusduel status_duel --cd 0 --usercd 5
        How to use: !duelstatus
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)

        msg = []
        if source.duel_request is not False:
            msg.append('You have a duel request for {} points by {}'.format(source.duel_price, source.duel_request.username_raw))

        if source.duel_target is not False:
            msg.append('You have a duel request against for {} points by {}'.format(source.duel_target.duel_price, source.duel_target.username_raw))

        if len(msg) > 0:
            bot.whisper(source.username, '. '.join(msg))
        else:
            bot.whisper(source.username, 'You have no duel request or duel target. Type !duel USERNAME POT to duel someone!')

    def get_duel_stats(self, **options):
        """
        Whispers the users duel winratio to the user
        """

        bot = options['bot']
        source = options['source']

        if source.duel_stats is None:
            bot.whisper(source.username, 'You have no recorded duels.')
            return True

        bot.whisper(source.username, 'duels: {ds.duels_total} winrate: {ds.winrate:.2f}% streak: {ds.current_streak} profit: {ds.profit}'.format(ds=source.duel_stats))
<commit_msg>Added description for duel commands<commit_after>import logging
import datetime

from pajbot.modules import BaseModule, ModuleSetting
from pajbot.models.command import Command, CommandExample
from pajbot.models.handler import HandlerManager

from numpy import random

log = logging.getLogger(__name__)

def init_dueling_variables(user):
    if hasattr(user, 'duel_request'):
        return False
    user.duel_request = False
    user.duel_target = False
    user.duel_price = 0

class DuelModule(BaseModule):

    ID = __name__.split('.')[-1]
    NAME = 'Duel (mini game)'
    DESCRIPTION = 'Let players duel to win or lose points.'
    SETTINGS = [
            ModuleSetting(
                key='max_pot',
                label='How many points you can duel for at most',
                type='number',
                required=True,
                placeholder='',
                default=420,
                constraints={
                    'min_value': 0,
                    'max_value': 69000,
                    }),
            ModuleSetting(
                key='message_won',
                label='Winner message | Available arguments: {winner}, {loser}',
                type='text',
                required=True,
                placeholder='{winner} won the duel vs {loser} PogChamp',
                default='{winner} won the duel vs {loser} PogChamp',
                constraints={
                    'min_str_len': 10,
                    'max_str_len': 400,
                    }),
            ModuleSetting(
                key='message_won_points',
                label='Points message | Available arguments: {winner}, {loser}, {total_pot}, {extra_points}',
                type='text',
                required=True,
                placeholder='{winner} won the duel vs {loser} PogChamp . The pot was {total_pot}, the winner gets their bet back + {extra_points} points',
                default='{winner} won the duel vs {loser} PogChamp . The pot was {total_pot}, the winner gets their bet back + {extra_points} points',
                constraints={
                    'min_str_len': 10,
                    'max_str_len': 400,
                    }),
            ModuleSetting(
                key='online_global_cd',
                label='Global cooldown (seconds)',
                type='number',
                required=True,
                placeholder='',
                default=0,
                constraints={
                    'min_value': 0,
                    'max_value': 120,
                    }),
            ModuleSetting(
                key='online_user_cd',
                label='Per-user cooldown (seconds)',
                type='number',
                required=True,
                placeholder='',
                default=5,
                constraints={
                    'min_value': 0,
                    'max_value': 240,
                    }),
                ]

    def load_commands(self, **options):
        self.commands['duel'] = Command.raw_command(self.initiate_duel,
                delay_all=self.settings['online_global_cd'],
                delay_user=self.settings['online_user_cd'],
                description='Initiate a duel with a user',
                examples=[
                    CommandExample(None, '0-point duel',
                        chat='user:!duel Karl_Kons\n'
                        'bot>user:You have challenged Karl_Kons for 0 points',
                        description='Duel Karl_Kons for 0 points').parse(),
                    CommandExample(None, '69-point duel',
                        chat='user:!duel Karl_Kons 69\n'
                        'bot>user:You have challenged Karl_Kons for 69 points',
                        description='Duel Karl_Kons for 69 points').parse(),
                    ],
                )
        self.commands['cancelduel'] = Command.raw_command(self.cancel_duel,
                delay_all=0,
                delay_user=10,
                description='Cancel your duel request'
                )
        self.commands['accept'] = Command.raw_command(self.accept_duel,
                delay_all=0,
                delay_user=0
                description='Accept a duel request'
                )
        self.commands['decline'] = Command.raw_command(self.decline_duel,
                delay_all=0,
                delay_user=0
                description='Decline a duel request'
                )
        self.commands['deny'] = self.commands['decline']
        self.commands['duelstatus'] = Command.raw_command(self.status_duel,
                delay_all=0,
                delay_user=5
                description='Current duel request info')
        self.commands['duelstats'] = Command.raw_command(self.get_duel_stats,
                delay_all=0,
                delay_user=120
                description='Get your duel statistics')

    def initiate_duel(self, **options):
        """
        Initiate a duel with a user.
        You can also bet points on the winner.
        By default, the maximum amount of points you can spend is 420.

        How to add: !add funccommand duel initiate_duel --cd 0 --usercd 5
        How to use: !duel USERNAME POINTS_TO_BET
        """

        bot = options['bot']
        source = options['source']
        message = options['message']

        if message is None:
            return False

        max_pot = self.settings['max_pot']

        init_dueling_variables(source)

        msg_split = message.split()
        username = msg_split[0]
        user = bot.users.find(username)
        duel_price = 0
        if user is None:
            # No user was found with this username
            return False

        if len(msg_split) > 1:
            try:
                duel_price = int(msg_split[1])
                if duel_price < 0:
                    return False

                if duel_price > max_pot:
                    duel_price = max_pot
            except ValueError:
                pass

        if source.duel_target is not False:
            bot.whisper(source.username, 'You already have a duel request active with {}. Type !cancelduel to cancel your duel request.'.format(source.duel_target.username_raw))
            return False

        if user == source:
            # You cannot duel yourself
            return False

        if user.last_active is None or (datetime.datetime.now() - user._last_active).total_seconds() > 5 * 60:
            bot.whisper(source.username, 'This user has not been active in chat within the last 5 minutes. Get them to type in chat before sending another challenge')
            return False

        if not user.can_afford(duel_price) or not source.can_afford(duel_price):
            bot.whisper(source.username, 'You or your target do not have more than {} points, therefore you cannot duel for that amount.'.format(duel_price))
            return False

        init_dueling_variables(user)

        if user.duel_request is False:
            user.duel_request = source
            source.duel_target = user
            user.duel_price = duel_price
            bot.whisper(user.username, 'You have been challenged to a duel by {} for {} points. You can either !accept or !deny this challenge.'.format(source.username_raw, duel_price))
            bot.whisper(source.username, 'You have challenged {} for {} points'.format(user.username_raw, duel_price))
        else:
            bot.whisper(source.username, 'This person is already being challenged by {}. Ask them to answer the offer by typing !deny or !accept'.format(user.duel_request.username_raw))

    def cancel_duel(self, **options):
        """
        Cancel any duel requests you've sent.

        How to add: !add funccomand cancelduel|duelcancel cancel_duel --cd 0 --usercd 10
        How to use: !cancelduel
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)

        if source.duel_target is not False:
            bot.whisper(source.username, 'You have cancelled the duel vs {}'.format(source.duel_target.username_raw))
            source.duel_target.duel_request = False
            source.duel_target = False
            source.duel_request = False

    def accept_duel(self, **options):
        """
        Accepts any active duel requests you've received.

        How to add: !add funccommand accept accept_duel --cd 0 --usercd 0
        How to use: !accept
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)
        duel_tax = 0.3  # 30% tax

        if source.duel_request is not False:
            if not source.can_afford(source.duel_price) or not source.duel_request.can_afford(source.duel_price):
                bot.whisper(source.username, 'Your duel request with {} was cancelled due to one of you not having enough points.'.format(source.duel_request.username_raw))
                bot.whisper(source.duel_request.username, 'Your duel request with {} was cancelled due to one of you not having enough points.'.format(source.username_raw))
                source.duel_request.duel_target = False
                source.duel_request = False
                return False
            source.points -= source.duel_price
            source.duel_request.points -= source.duel_price
            winning_pot = int(source.duel_price * (1.0 - duel_tax))
            participants = [source, source.duel_request]
            winner = random.choice(participants)
            participants.remove(winner)
            loser = participants.pop()
            winner.points += source.duel_price
            winner.points += winning_pot

            bot.duel_manager.user_won(winner, winning_pot)
            bot.duel_manager.user_lost(loser, source.duel_price)

            arguments = {
                    'winner': winner.username,
                    'loser': loser.username,
                    'total_pot': source.duel_price,
                    'extra_points': winning_pot,
                    }

            if source.duel_price > 0:
                message = self.get_phrase('message_won_points', **arguments)
                bot.websocket_manager.emit('notification', {'message': '{} won the duel vs {}'.format(winner.username_raw, loser.username_raw)})
            else:
                message = self.get_phrase('message_won', **arguments)
            bot.say(message)
            source.duel_request.duel_target = False
            source.duel_request = False
            source.duel_price = 0
            HandlerManager.trigger('on_duel_complete',
                    winner, loser,
                    winning_pot, source.duel_price)

    def decline_duel(self, **options):
        """
        Declines any active duel requests you've received.

        How to add: !add funccommand deny|decline decline_duel --cd 0 --usercd 0
        How to use: !decline
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)

        if source.duel_request is not False:
            bot.whisper(source.username, 'You have declined the duel vs {}'.format(source.duel_request.username_raw))
            bot.whisper(source.duel_request.username, '{} declined the duel challenge with you.'.format(source.username_raw))
            source.duel_request.duel_target = False
            source.duel_request = False

    def status_duel(self, **options):
        """
        Whispers you the current status of your active duel requests/duel targets

        How to add: !add funccommand duelstatus|statusduel status_duel --cd 0 --usercd 5
        How to use: !duelstatus
        """

        bot = options['bot']
        source = options['source']

        init_dueling_variables(source)

        msg = []
        if source.duel_request is not False:
            msg.append('You have a duel request for {} points by {}'.format(source.duel_price, source.duel_request.username_raw))

        if source.duel_target is not False:
            msg.append('You have a duel request against for {} points by {}'.format(source.duel_target.duel_price, source.duel_target.username_raw))

        if len(msg) > 0:
            bot.whisper(source.username, '. '.join(msg))
        else:
            bot.whisper(source.username, 'You have no duel request or duel target. Type !duel USERNAME POT to duel someone!')

    def get_duel_stats(self, **options):
        """
        Whispers the users duel winratio to the user
        """

        bot = options['bot']
        source = options['source']

        if source.duel_stats is None:
            bot.whisper(source.username, 'You have no recorded duels.')
            return True

        bot.whisper(source.username, 'duels: {ds.duels_total} winrate: {ds.winrate:.2f}% streak: {ds.current_streak} profit: {ds.profit}'.format(ds=source.duel_stats))
<|endoftext|>


<commit_before># -*- coding:utf-8 -*-
__author__ = 'TwIStOy'

import os
import urllib
import re
import Log
import Util
from datetime import datetime

def get_post_meta_from_file(fp):
    """Takes a file and return a dict containing meta-data from the text file. It will try to extract
    meta-info from every line before it reach the first empty line, which separates the meta-date and the content.
    Each meta-info should be in the form of "attr:value". Attributes are case-insensitive during analyzing
    and output attribute will be in lower-case. Any text unrecognized as meta info will be treated as content.
    :return: a dict containing meta-info
    """
    meta = dict()
    try:
        fp.seek(0)
    except:
        pass
    for line in fp.readline():
        if line == '':
            return meta
        result = re.search('^(.+?):(.+)$', line, re.IGNORECASE)
        if result:
            attr, value = result.group(1).strip().lower(), result.group(2).strip()
            meta[attr] = value
    return meta

class PostError(Exception):
    pass

# TODO: write error handling to make this class more robust
class MetaInfo(object):
    def __init__(self):
        self.author = None
        self.category = None
        self.datetime = None
        self.tags = []
        self.title = None
        self.url = None

    def add_tags(self, string):
        self.tags += [tag.strip() for tag in string.split(',')]

    def set_tags(self, string):
        self.add_tags(string)

    def set_datetime(self, string):
        self.datetime = datetime.strptime(string, '%Y-%m-%d')

    def set_category(self, string):
        self.category = string

    def set_author(self, string):
        self.author = string

    def set_url(self, string):
        self.url = string

    def set_title(self, string):
        self.title = string

    def set_attr_value(self, attr, value):
        """set attribute value pair"""
        if attr in ['author', 'category', 'datetime', 'tags', 'title', 'url']:
            getattr(self, "set_{}".format(attr))(value)
        else:
            Log.debug('Error attr in <set_attr_value> (attr="{attr}", value="{value}")'.format(
                attr=attr, value=value
            ))

    def init(self, config):
        """initialize self into a legal meta data suitable for blog content generation.
        legalize self by applying some default value from config
        """
        if not self.author:
            self.author = config.default_author
        if not self.category:
            self.category = config.default_category
        if not self.title:
            self.title = config.default_title
        if not self.datetime:
            self.datetime = config.default_datetime
        if not self.url:
            self._base_url = urllib.urlencode(self.title)

    def load_from_file(self, filename, config):
        """load meta data from a file. return self upon finishing"""
        try:
            file = open(filename, 'r', 'utf-8')
            for line in file.readline():
                if line == '':
                    break
                result = re.search('^(.+?):(.+)$', line, re.IGNORECASE)
                if result:
                    attr = result.group(1).strip().lower()
                    value = result.group(2).strip()
                    self.set_attr_value(attr, value)
        except Exception, e:
            Log.warning('Cannot retrieve meta info from' + filename + '!\n' + str(e))
        return self.init(config)

    def __le__(self, other):
        return self.datetime < other.datetime

class Post(object):
    def __init__(self, filename):
        self.meta_info = MetaInfo()
        self.raw_content = ''
        self.need_compilation = True
        self.filename = filename

    def generate_html_content(self):
        """Generate html content"""
        with open(self.filename, 'r') as fp:
            self.content = Util.mk_transfer(self.fp.read())
        return self

    def get_url(self, urls):
        """Get the real url of the post. Resolve conflicts by appending -n to the base url"""
        now = self.meta_info.url
        if now in urls:
            now = now + '-'
            addition = 1
            while now + str(addition) in urls:
                addition += 1
            now = now + str(addition)
        self.url = now
        return self

    def __le__(self, other):
        return self.meta_info < other.meta_info
<commit_msg>modified encoding<commit_after># -*- coding:utf-8 -*-
__author__ = 'TwIStOy'

import os
import urllib
import re
import Log
import Util
from datetime import datetime
import codecs

def get_post_meta_from_file(fp):
    """Takes a file and return a dict containing meta-data from the text file. It will try to extract
    meta-info from every line before it reach the first empty line, which separates the meta-date and the content.
    Each meta-info should be in the form of "attr:value". Attributes are case-insensitive during analyzing
    and output attribute will be in lower-case. Any text unrecognized as meta info will be treated as content.
    :return: a dict containing meta-info
    """
    meta = dict()
    try:
        fp.seek(0)
    except:
        pass
    for line in fp.readline():
        if line == '':
            return meta
        result = re.search('^(.+?):(.+)$', line, re.IGNORECASE)
        if result:
            attr, value = result.group(1).strip().lower(), result.group(2).strip()
            meta[attr] = value
    return meta

class PostError(Exception):
    pass

# TODO: write error handling to make this class more robust
class MetaInfo(object):
    def __init__(self):
        self.author = None
        self.category = None
        self.datetime = None
        self.tags = []
        self.title = None
        self.url = None

    def add_tags(self, string):
        self.tags += [tag.strip() for tag in string.split(',')]

    def set_tags(self, string):
        self.add_tags(string)

    def set_datetime(self, string):
        self.datetime = datetime.strptime(string, '%Y-%m-%d')

    def set_category(self, string):
        self.category = string

    def set_author(self, string):
        self.author = string

    def set_url(self, string):
        self.url = string

    def set_title(self, string):
        self.title = string

    def set_attr_value(self, attr, value):
        """set attribute value pair"""
        if attr in ['author', 'category', 'datetime', 'tags', 'title', 'url']:
            getattr(self, "set_{}".format(attr))(value)
        else:
            Log.debug('Error attr in <set_attr_value> (attr="{attr}", value="{value}")'.format(
                attr=attr, value=value
            ))

    def init(self, config):
        """initialize self into a legal meta data suitable for blog content generation.
        legalize self by applying some default value from config
        """
        if not self.author:
            self.author = config.default_author
        if not self.category:
            self.category = config.default_category
        if not self.title:
            self.title = config.default_title
        if not self.datetime:
            self.datetime = config.default_datetime
        if not self.url:
            self._base_url = urllib.urlencode(self.title)

    def load_from_file(self, filename, config):
        """load meta data from a file. return self upon finishing"""
        try:
            file = codecs.open(filename, 'r', 'utf-8')
            for line in file.readline():
                if line == '':
                    break
                result = re.search('^(.+?):(.+)$', line, re.IGNORECASE)
                if result:
                    attr = result.group(1).strip().lower()
                    value = result.group(2).strip()
                    self.set_attr_value(attr, value)
        except Exception, e:
            Log.warning('Cannot retrieve meta info from' + filename + '!\n' + str(e))
        return self.init(config)

    def __le__(self, other):
        return self.datetime < other.datetime

class Post(object):
    def __init__(self, filename):
        self.meta_info = MetaInfo()
        self.raw_content = ''
        self.need_compilation = True
        self.filename = filename

    def generate_html_content(self):
        """Generate html content"""
        with codecs.open(self.filename, 'r', 'utf-8') as fp:
            self.content = Util.mk_transfer(self.fp.read())
        return self

    def get_url(self, urls):
        """Get the real url of the post. Resolve conflicts by appending -n to the base url"""
        now = self.meta_info.url
        if now in urls:
            now = now + '-'
            addition = 1
            while now + str(addition) in urls:
                addition += 1
            now = now + str(addition)
        self.url = now
        return self

    def __le__(self, other):
        return self.meta_info < other.meta_info
<|endoftext|>


<commit_before># pylint: disable-msg=W0402

from datetime import datetime
import random
import string
import sys

from numpy.random import randn
import numpy as np

from pandas.core.common import isnull
import pandas.core.index as index
import pandas.core.daterange as daterange
import pandas.core.series as series
import pandas.core.frame as frame
import pandas.core.panel as panel

# to_reload = ['index', 'daterange', 'series', 'frame', 'matrix', 'panel']
# for mod in to_reload:
#     reload(locals()[mod])

DateRange = daterange.DateRange
Index = index.Index
Series = series.Series
DataFrame = frame.DataFrame
WidePanel = panel.WidePanel

N = 30
K = 4

def rands(n):
    choices = string.letters + string.digits
    return ''.join([random.choice(choices) for _ in xrange(n)])

#-------------------------------------------------------------------------------
# Console debugging tools

def debug(f, *args, **kwargs):
    set_trace()
    return f(*args, **kwargs)

def set_trace():
    from IPython.core.debugger import Pdb
    try:
        Pdb(color_scheme='Linux').set_trace(sys._getframe().f_back)
    except:
        from pdb import Pdb as OldPdb
        OldPdb().set_trace(sys._getframe().f_back)

#-------------------------------------------------------------------------------
# Comparators

def equalContents(arr1, arr2):
    """Checks if the set of unique elements of arr1 and arr2 are equivalent.
    """
    return frozenset(arr1) == frozenset(arr2)

def isiterable(obj):
    return hasattr(obj, '__iter__')

def assert_almost_equal(a, b):
    if isinstance(a, dict) or isinstance(b, dict):
        return assert_dict_equal(a, b)

    if isiterable(a):
        np.testing.assert_(isiterable(b))
        np.testing.assert_equal(len(a), len(b))
        for i in xrange(len(a)):
            assert_almost_equal(a[i], b[i])
        return True

    err_msg = lambda a, b: 'expected %.5f but got %.5f' % (a, b)

    if isnull(a):
        np.testing.assert_(isnull(b))
        return

    if isinstance(a, (bool, float, int)):
        # case for zero
        if abs(a) < 1e-5:
            np.testing.assert_almost_equal(
                a, b, decimal=5, err_msg=err_msg(a, b), verbose=False)
        else:
            np.testing.assert_almost_equal(
                1, a/b, decimal=5, err_msg=err_msg(a, b), verbose=False)
    else:
        assert(a == b)

def is_sorted(seq):
    return assert_almost_equal(seq, np.sort(np.array(seq)))

def assert_dict_equal(a, b, compare_keys=True):
    a_keys = frozenset(a.keys())
    b_keys = frozenset(b.keys())

    if compare_keys:
        assert(a_keys == b_keys)

    for k in a_keys:
        assert_almost_equal(a[k], b[k])

def assert_series_equal(left, right):
    assert(left.dtype == right.dtype)
    assert_almost_equal(left, right)
    assert(left.index.equals(right.index))

def assert_frame_equal(left, right):
    for col, series in left.iteritems():
        assert(col in right)
        assert_series_equal(series, right[col])
    for col in right:
        assert(col in left)
    assert(left.columns.equals(right.columns))

def assert_panel_equal(left, right):
    assert(left.items.equals(right.items))
    assert(left.major_axis.equals(right.major_axis))
    assert(left.minor_axis.equals(right.minor_axis))

    for col, series in left.iteritems():
        assert(col in right)
        assert_frame_equal(series, right[col])

    for col in right:
        assert(col in left)

def assert_contains_all(iterable, dic):
    for k in iterable:
        assert(k in dic)

def getCols(k):
    return string.ascii_uppercase[:k]

def makeStringIndex(k):
    return Index([rands(10) for _ in xrange(k)])

def makeIntIndex(k):
    return Index(np.arange(k))

def makeDateIndex(k):
    dates = list(DateRange(datetime(2000, 1, 1), periods=k))
    return Index(dates)

def makeFloatSeries():
    index = makeStringIndex(N)
    return Series(randn(N), index=index)

def makeStringSeries():
    index = makeStringIndex(N)
    return Series(randn(N), index=index)

def makeObjectSeries():
    dateIndex = makeDateIndex(N)
    index = makeStringIndex(N)
    return Series(dateIndex, index=index)

def makeTimeSeries():
    return Series(randn(N), index=makeDateIndex(N))

def getArangeMat():
    return np.arange(N * K).reshape((N, K))

def getSeriesData():
    index = makeStringIndex(N)

    return dict((c, Series(randn(N), index=index)) for c in getCols(K))

def getTimeSeriesData():
    return dict((c, makeTimeSeries()) for c in getCols(K))

def getMixedTypeDict():
    index = Index(['a', 'b', 'c', 'd', 'e'])

    data = {
        'A' : [0., 1., 2., 3., 4.],
        'B' : [0., 1., 0., 1., 0.],
        'C' : ['foo1', 'foo2', 'foo3', 'foo4', 'foo5'],
        'D' : DateRange('1/1/2009', periods=5)
    }

    return index, data

def makeDataFrame():
    data = getSeriesData()
    return DataFrame(data)

def makeTimeDataFrame():
    data = getTimeSeriesData()
    return DataFrame(data)

def makeWidePanel():
    cols = ['Item' + c for c in string.ascii_uppercase[:K - 1]]
    data = dict((c, makeTimeDataFrame()) for c in cols)
    return WidePanel.fromDict(data)

def add_nans(panel):
    I, J, N = panel.shape
    for i, item in enumerate(panel.items):
        dm = panel[item]
        for j, col in enumerate(dm.columns):
            dm[col][:i + j] = np.NaN

def makeLongPanel():
    wp = makeWidePanel()
    add_nans(wp)

    return wp.toLong()

<commit_msg>ENH: rkern patch to debug<commit_after># pylint: disable-msg=W0402

from datetime import datetime
import random
import string
import sys

from numpy.random import randn
import numpy as np

from pandas.core.common import isnull
import pandas.core.index as index
import pandas.core.daterange as daterange
import pandas.core.series as series
import pandas.core.frame as frame
import pandas.core.panel as panel

# to_reload = ['index', 'daterange', 'series', 'frame', 'matrix', 'panel']
# for mod in to_reload:
#     reload(locals()[mod])

DateRange = daterange.DateRange
Index = index.Index
Series = series.Series
DataFrame = frame.DataFrame
WidePanel = panel.WidePanel

N = 30
K = 4

def rands(n):
    choices = string.letters + string.digits
    return ''.join([random.choice(choices) for _ in xrange(n)])

#-------------------------------------------------------------------------------
# Console debugging tools

def debug(f, *args, **kwargs):
    from pdb import Pdb as OldPdb
    try:
        from IPython.core.debugger import Pdb
        kw = dict(color_scheme='Linux')
    except ImportError:
        Pdb = OldPdb
        kw = {}
    pdb = Pdb(**kw)
    return pdb.runcall(f, *args, **kwargs)

def set_trace():
    from IPython.core.debugger import Pdb
    try:
        Pdb(color_scheme='Linux').set_trace(sys._getframe().f_back)
    except:
        from pdb import Pdb as OldPdb
        OldPdb().set_trace(sys._getframe().f_back)

#-------------------------------------------------------------------------------
# Comparators

def equalContents(arr1, arr2):
    """Checks if the set of unique elements of arr1 and arr2 are equivalent.
    """
    return frozenset(arr1) == frozenset(arr2)

def isiterable(obj):
    return hasattr(obj, '__iter__')

def assert_almost_equal(a, b):
    if isinstance(a, dict) or isinstance(b, dict):
        return assert_dict_equal(a, b)

    if isiterable(a):
        np.testing.assert_(isiterable(b))
        np.testing.assert_equal(len(a), len(b))
        for i in xrange(len(a)):
            assert_almost_equal(a[i], b[i])
        return True

    err_msg = lambda a, b: 'expected %.5f but got %.5f' % (a, b)

    if isnull(a):
        np.testing.assert_(isnull(b))
        return

    if isinstance(a, (bool, float, int)):
        # case for zero
        if abs(a) < 1e-5:
            np.testing.assert_almost_equal(
                a, b, decimal=5, err_msg=err_msg(a, b), verbose=False)
        else:
            np.testing.assert_almost_equal(
                1, a/b, decimal=5, err_msg=err_msg(a, b), verbose=False)
    else:
        assert(a == b)

def is_sorted(seq):
    return assert_almost_equal(seq, np.sort(np.array(seq)))

def assert_dict_equal(a, b, compare_keys=True):
    a_keys = frozenset(a.keys())
    b_keys = frozenset(b.keys())

    if compare_keys:
        assert(a_keys == b_keys)

    for k in a_keys:
        assert_almost_equal(a[k], b[k])

def assert_series_equal(left, right):
    assert(left.dtype == right.dtype)
    assert_almost_equal(left, right)
    assert(left.index.equals(right.index))

def assert_frame_equal(left, right):
    for col, series in left.iteritems():
        assert(col in right)
        assert_series_equal(series, right[col])
    for col in right:
        assert(col in left)
    assert(left.columns.equals(right.columns))

def assert_panel_equal(left, right):
    assert(left.items.equals(right.items))
    assert(left.major_axis.equals(right.major_axis))
    assert(left.minor_axis.equals(right.minor_axis))

    for col, series in left.iteritems():
        assert(col in right)
        assert_frame_equal(series, right[col])

    for col in right:
        assert(col in left)

def assert_contains_all(iterable, dic):
    for k in iterable:
        assert(k in dic)

def getCols(k):
    return string.ascii_uppercase[:k]

def makeStringIndex(k):
    return Index([rands(10) for _ in xrange(k)])

def makeIntIndex(k):
    return Index(np.arange(k))

def makeDateIndex(k):
    dates = list(DateRange(datetime(2000, 1, 1), periods=k))
    return Index(dates)

def makeFloatSeries():
    index = makeStringIndex(N)
    return Series(randn(N), index=index)

def makeStringSeries():
    index = makeStringIndex(N)
    return Series(randn(N), index=index)

def makeObjectSeries():
    dateIndex = makeDateIndex(N)
    index = makeStringIndex(N)
    return Series(dateIndex, index=index)

def makeTimeSeries():
    return Series(randn(N), index=makeDateIndex(N))

def getArangeMat():
    return np.arange(N * K).reshape((N, K))

def getSeriesData():
    index = makeStringIndex(N)

    return dict((c, Series(randn(N), index=index)) for c in getCols(K))

def getTimeSeriesData():
    return dict((c, makeTimeSeries()) for c in getCols(K))

def getMixedTypeDict():
    index = Index(['a', 'b', 'c', 'd', 'e'])

    data = {
        'A' : [0., 1., 2., 3., 4.],
        'B' : [0., 1., 0., 1., 0.],
        'C' : ['foo1', 'foo2', 'foo3', 'foo4', 'foo5'],
        'D' : DateRange('1/1/2009', periods=5)
    }

    return index, data

def makeDataFrame():
    data = getSeriesData()
    return DataFrame(data)

def makeTimeDataFrame():
    data = getTimeSeriesData()
    return DataFrame(data)

def makeWidePanel():
    cols = ['Item' + c for c in string.ascii_uppercase[:K - 1]]
    data = dict((c, makeTimeDataFrame()) for c in cols)
    return WidePanel.fromDict(data)

def add_nans(panel):
    I, J, N = panel.shape
    for i, item in enumerate(panel.items):
        dm = panel[item]
        for j, col in enumerate(dm.columns):
            dm[col][:i + j] = np.NaN

def makeLongPanel():
    wp = makeWidePanel()
    add_nans(wp)

    return wp.toLong()

<|endoftext|>


<commit_before>import sys

from twisted.internet import error
from twisted.web import sux, microdom

class NoopParser(object):
    def __init__(self, n):
        self.name = n
        self.done = False
    def gotTagStart(self, name, attrs):
        pass
    def gotTagEnd(self, name, data):
        self.done = (name == self.name)
    def value(self):
        # don't store anything on the object after parsing this
        return None

class BaseXMLHandler(object):

    def __init__(self, n, handler_dict={}, enter_unknown=False):
        self.done = False
        self.current_ob = None
        self.tag_name = n
        self.before_delegates = {}
        self.after_delegates = {}
        self.handler_dict = handler_dict
        self.enter_unknown = enter_unknown

        for p in self.handler_dict:
            self.__dict__[self.cleanup(p)] = None

    def setBeforeDelegate(self, name, fn):
        self.before_delegates[name] = fn

    def setAfterDelegate(self, name, fn):
        self.after_delegates[name] = fn

    def setDelegate(self, name, before=None, after=None):
        if before:
            self.setBeforeDelegate(name, before)
        if after:
            self.setAfterDelegate(name, after)

    def setPredefDelegate(self, type, before=None, after=None):
        self.setDelegate(type.MY_TAG, before, after)

    def objectStarted(self, name, o):
        if name in self.before_delegates:
            self.before_delegates[name](o)

    def objectFinished(self, name, o):
        if name in self.after_delegates:
            self.after_delegates[name](o)

    def gotTagStart(self, name, attrs):
        if self.current_ob:
            self.current_ob.gotTagStart(name, attrs)
        elif name in self.handler_dict:
            self.current_ob = self.handler_dict[name](name)
            self.objectStarted(name, self.current_ob)
        elif not self.enter_unknown:
            sys.stderr.write("Got unknown tag %s in %s\n" % (name, self.__class__))
            self.current_ob = NoopParser(name)

    def gotTagEnd(self, name, data):
        if self.current_ob:
            self.current_ob.gotTagEnd(name, data)
            if self.current_ob.done:
                v = self.current_ob.value()
                if v is not None:
                    self.__dict__[self.cleanup(name)] = v
                    self.objectFinished(name, v)
                self.current_ob = None
        elif name == self.tag_name:
            self.done = True
            del self.current_ob
            self.gotFinalData(data)

    def gotFinalData(self, data):
        pass

    def value(self):
        # by default, the resulting value is the handler object itself,
        # but XMLStringHandler overwrites this
        return self

    def cleanup(self, n):
        return n.replace(':', '_')

    def __repr__(self):
        return "{%s %s}" % (self.tag_name, self.__dict__)

class XMLStringHandler(BaseXMLHandler):
    """XML data handler for simple string fields"""
    def gotFinalData(self, data):
        self.data = data

    def value(self):
        return self.data

class PredefinedXMLHandler(BaseXMLHandler):
    MY_TAG = ''
    SIMPLE_PROPS = []
    COMPLEX_PROPS = []

    # if set to True, contents inside unknown tags
    # will be parsed as if the unknown tags weren't
    # around it.
    ENTER_UNKNOWN = False

    def __init__(self, n):
        handler_dict = dict([(p.MY_TAG,p) for p in self.COMPLEX_PROPS])
        handler_dict.update([(p,XMLStringHandler) for p in self.SIMPLE_PROPS])
        super(PredefinedXMLHandler, self).__init__(n, handler_dict, self.ENTER_UNKNOWN)

class Author(PredefinedXMLHandler):
    MY_TAG = 'author'
    SIMPLE_PROPS = [ 'name', 'uri' ]

class Entry(PredefinedXMLHandler):
    MY_TAG = 'entry'
    SIMPLE_PROPS = ['id', 'published', 'title', 'content', 'link', 'updated',
                    'twitter:source', 'twitter:lang']
    COMPLEX_PROPS = [Author]

    def gotTagStart(self, name, attrs):
        super(Entry, self).gotTagStart(name, attrs)
        if name == 'link':
            self.__dict__[attrs['rel']] = attrs['href']

    def gotTagEnd(self, name, data):
        super(Entry, self).gotTagEnd(name, data)
        if name == 'link':
            del self.link

class Status(PredefinedXMLHandler):
    MY_TAG = 'status'
    SIMPLE_PROPS = ['created_at', 'id', 'text', 'source', 'truncated',
        'in_reply_to_status_id', 'in_reply_to_screen_name',
        'in_reply_to_user_id', 'favorited', 'user_id', 'geo']

class User(PredefinedXMLHandler):
    MY_TAG = 'user'
    SIMPLE_PROPS = ['id', 'name', 'screen_name', 'location', 'description',
        'profile_image_url', 'url', 'protected', 'followers_count',
        'profile_background_color', 'profile_text_color', 'profile_link_color',
        'profile_sidebar_fill_color', 'profile_sidebar_border_color',
        'friends_count', 'created_at', 'favourites_count', 'utc_offset',
        'time_zone', 'following', 'notifications', 'statuses_count',
        'profile_background_image_url', 'profile_background_tile', 'verified',
        'geo_enabled']
    COMPLEX_PROPS = [Status]

# Hack to patch this in...

Status.COMPLEX_PROPS = [User]

class SenderUser(User):
    MY_TAG = 'sender'

class RecipientUser(User):
    MY_TAG = 'recipient'

class DirectMessage(PredefinedXMLHandler):
    MY_TAG = 'direct_message'
    SIMPLE_PROPS = ['id', 'sender_id', 'text', 'recipient_id', 'created_at',
        'sender_screen_name', 'recipient_screen_name']
    COMPLEX_PROPS = [SenderUser, RecipientUser]

### simple object list handlers:

class SimpleListHandler(BaseXMLHandler):
    """Class for simple handlers that work with just a single type of element"""
    ITEM_TYPE = Entry
    ITEM_TAG = None

    @classmethod
    def item_tag(klass):
        tag = klass.ITEM_TAG
        if tag is None:
            tag = klass.ITEM_TYPE.MY_TAG
        return tag

    def __init__(self, n):
        type = self.ITEM_TYPE
        tag = self.item_tag()
        super(SimpleListHandler, self).__init__(n,
                 handler_dict={tag:type}, enter_unknown=True)

def topLevelXMLHandler(toplevel_type):
    """Used to create a BaseXMLHandler object that just handles a single type of tag"""
    return BaseXMLHandler(None,
                          handler_dict={toplevel_type.MY_TAG:toplevel_type},
                          enter_unknown=True)

class Parser(sux.XMLParser):

    """A file-like thingy that parses a friendfeed feed with SUX."""
    def __init__(self, handler):
        self.connectionMade()
        self.data=[]
        self.handler=handler

    def write(self, b):
        self.dataReceived(b)
    def close(self):
        self.connectionLost(error.ConnectionDone())
    def open(self):
        pass
    def read(self):
        return None

    # XML Callbacks
    def gotTagStart(self, name, attrs):
        self.data=[]
        self.handler.gotTagStart(name, attrs)

    def gotTagEnd(self, name):
        self.handler.gotTagEnd(name, ''.join(self.data).decode('utf8'))

    def gotText(self, data):
        self.data.append(data)

    def gotEntityReference(self, data):
        e = {'quot': '"', 'lt': '<', 'gt': '>', 'amp': '&'}
        if e.has_key(data):
            self.data.append(e[data])
        elif data[0] == '#':
            self.data.append('&' + data + ';')
        else:
            sys.stderr.write("Unhandled entity reference: %s\n" % (data))

def createParser(toplevel_type, delegate, extra_args=None):
    if extra_args:
        args = (extra_args,)
    else:
        args = ()

    def do_delegate(e):
        delegate(e, *args)

    handler = topLevelXMLHandler(toplevel_type)
    handler.setPredefDelegate(toplevel_type, after=do_delegate)
    return Parser(handler)

def simpleParserFactory(toplevel_type):
    """Used for simple toplevel_tag/toplevel_type parsers"""
    def create(delegate, extra_args=None):
        """Create a Parser object for the specific tag type, on the fly"""
        return createParser(toplevel_type, delegate, extra_args)
    return create

Feed       = simpleParserFactory(Entry)

Users      = simpleParserFactory(User)

Direct     = simpleParserFactory(DirectMessage)

Statuses = simpleParserFactory(Status)

HoseFeed   = simpleParserFactory(Status)

def parseXML(xml):
    return microdom.parseXMLString(xml)

def parseUpdateResponse(xml):
    return parseXML(xml).getElementsByTagName("id")[0].firstChild().data

# vim: set expandtab:
<commit_msg>txml: create *List XML handler classes<commit_after>import sys

from twisted.internet import error
from twisted.web import sux, microdom

class NoopParser(object):
    def __init__(self, n):
        self.name = n
        self.done = False
    def gotTagStart(self, name, attrs):
        pass
    def gotTagEnd(self, name, data):
        self.done = (name == self.name)
    def value(self):
        # don't store anything on the object after parsing this
        return None

class BaseXMLHandler(object):

    def __init__(self, n, handler_dict={}, enter_unknown=False):
        self.done = False
        self.current_ob = None
        self.tag_name = n
        self.before_delegates = {}
        self.after_delegates = {}
        self.handler_dict = handler_dict
        self.enter_unknown = enter_unknown

        for p in self.handler_dict:
            self.__dict__[self.cleanup(p)] = None

    def setBeforeDelegate(self, name, fn):
        self.before_delegates[name] = fn

    def setAfterDelegate(self, name, fn):
        self.after_delegates[name] = fn

    def setDelegate(self, name, before=None, after=None):
        if before:
            self.setBeforeDelegate(name, before)
        if after:
            self.setAfterDelegate(name, after)

    def setPredefDelegate(self, type, before=None, after=None):
        self.setDelegate(type.MY_TAG, before, after)

    def objectStarted(self, name, o):
        if name in self.before_delegates:
            self.before_delegates[name](o)

    def objectFinished(self, name, o):
        if name in self.after_delegates:
            self.after_delegates[name](o)

    def gotTagStart(self, name, attrs):
        if self.current_ob:
            self.current_ob.gotTagStart(name, attrs)
        elif name in self.handler_dict:
            self.current_ob = self.handler_dict[name](name)
            self.objectStarted(name, self.current_ob)
        elif not self.enter_unknown:
            sys.stderr.write("Got unknown tag %s in %s\n" % (name, self.__class__))
            self.current_ob = NoopParser(name)

    def gotTagEnd(self, name, data):
        if self.current_ob:
            self.current_ob.gotTagEnd(name, data)
            if self.current_ob.done:
                v = self.current_ob.value()
                if v is not None:
                    self.__dict__[self.cleanup(name)] = v
                    self.objectFinished(name, v)
                self.current_ob = None
        elif name == self.tag_name:
            self.done = True
            del self.current_ob
            self.gotFinalData(data)

    def gotFinalData(self, data):
        pass

    def value(self):
        # by default, the resulting value is the handler object itself,
        # but XMLStringHandler overwrites this
        return self

    def cleanup(self, n):
        return n.replace(':', '_')

    def __repr__(self):
        return "{%s %s}" % (self.tag_name, self.__dict__)

class XMLStringHandler(BaseXMLHandler):
    """XML data handler for simple string fields"""
    def gotFinalData(self, data):
        self.data = data

    def value(self):
        return self.data

class PredefinedXMLHandler(BaseXMLHandler):
    MY_TAG = ''
    SIMPLE_PROPS = []
    COMPLEX_PROPS = []

    # if set to True, contents inside unknown tags
    # will be parsed as if the unknown tags weren't
    # around it.
    ENTER_UNKNOWN = False

    def __init__(self, n):
        handler_dict = dict([(p.MY_TAG,p) for p in self.COMPLEX_PROPS])
        handler_dict.update([(p,XMLStringHandler) for p in self.SIMPLE_PROPS])
        super(PredefinedXMLHandler, self).__init__(n, handler_dict, self.ENTER_UNKNOWN)

class Author(PredefinedXMLHandler):
    MY_TAG = 'author'
    SIMPLE_PROPS = [ 'name', 'uri' ]

class Entry(PredefinedXMLHandler):
    MY_TAG = 'entry'
    SIMPLE_PROPS = ['id', 'published', 'title', 'content', 'link', 'updated',
                    'twitter:source', 'twitter:lang']
    COMPLEX_PROPS = [Author]

    def gotTagStart(self, name, attrs):
        super(Entry, self).gotTagStart(name, attrs)
        if name == 'link':
            self.__dict__[attrs['rel']] = attrs['href']

    def gotTagEnd(self, name, data):
        super(Entry, self).gotTagEnd(name, data)
        if name == 'link':
            del self.link

class Status(PredefinedXMLHandler):
    MY_TAG = 'status'
    SIMPLE_PROPS = ['created_at', 'id', 'text', 'source', 'truncated',
        'in_reply_to_status_id', 'in_reply_to_screen_name',
        'in_reply_to_user_id', 'favorited', 'user_id', 'geo']

class User(PredefinedXMLHandler):
    MY_TAG = 'user'
    SIMPLE_PROPS = ['id', 'name', 'screen_name', 'location', 'description',
        'profile_image_url', 'url', 'protected', 'followers_count',
        'profile_background_color', 'profile_text_color', 'profile_link_color',
        'profile_sidebar_fill_color', 'profile_sidebar_border_color',
        'friends_count', 'created_at', 'favourites_count', 'utc_offset',
        'time_zone', 'following', 'notifications', 'statuses_count',
        'profile_background_image_url', 'profile_background_tile', 'verified',
        'geo_enabled']
    COMPLEX_PROPS = [Status]

# Hack to patch this in...

Status.COMPLEX_PROPS = [User]

class SenderUser(User):
    MY_TAG = 'sender'

class RecipientUser(User):
    MY_TAG = 'recipient'

class DirectMessage(PredefinedXMLHandler):
    MY_TAG = 'direct_message'
    SIMPLE_PROPS = ['id', 'sender_id', 'text', 'recipient_id', 'created_at',
        'sender_screen_name', 'recipient_screen_name']
    COMPLEX_PROPS = [SenderUser, RecipientUser]

### simple object list handlers:

class SimpleListHandler(BaseXMLHandler):
    """Class for simple handlers that work with just a single type of element"""
    ITEM_TYPE = Entry
    ITEM_TAG = None

    @classmethod
    def item_tag(klass):
        tag = klass.ITEM_TAG
        if tag is None:
            tag = klass.ITEM_TYPE.MY_TAG
        return tag

    def __init__(self, n):
        type = self.ITEM_TYPE
        tag = self.item_tag()
        super(SimpleListHandler, self).__init__(n,
                 handler_dict={tag:type}, enter_unknown=True)

class EntryList(SimpleListHandler):
    MY_TAG = 'feed'
    ITEM_TYPE = Entry

class UserList(SimpleListHandler):
    MY_TAG = 'users'
    ITEM_TYPE = User

class DirectMessageList(SimpleListHandler):
    MY_TAG = 'direct-messages'
    ITEM_TYPE = DirectMessage

class StatusList(SimpleListHandler):
    MY_TAG = 'statuses'
    ITEM_TYPE = Status

def topLevelXMLHandler(toplevel_type):
    """Used to create a BaseXMLHandler object that just handles a single type of tag"""
    return BaseXMLHandler(None,
                          handler_dict={toplevel_type.MY_TAG:toplevel_type},
                          enter_unknown=True)

class Parser(sux.XMLParser):

    """A file-like thingy that parses a friendfeed feed with SUX."""
    def __init__(self, handler):
        self.connectionMade()
        self.data=[]
        self.handler=handler

    def write(self, b):
        self.dataReceived(b)
    def close(self):
        self.connectionLost(error.ConnectionDone())
    def open(self):
        pass
    def read(self):
        return None

    # XML Callbacks
    def gotTagStart(self, name, attrs):
        self.data=[]
        self.handler.gotTagStart(name, attrs)

    def gotTagEnd(self, name):
        self.handler.gotTagEnd(name, ''.join(self.data).decode('utf8'))

    def gotText(self, data):
        self.data.append(data)

    def gotEntityReference(self, data):
        e = {'quot': '"', 'lt': '<', 'gt': '>', 'amp': '&'}
        if e.has_key(data):
            self.data.append(e[data])
        elif data[0] == '#':
            self.data.append('&' + data + ';')
        else:
            sys.stderr.write("Unhandled entity reference: %s\n" % (data))

def listParser(list_type, delegate, extra_args=None):
    toplevel_type = list_type.ITEM_TYPE

    if extra_args:
        args = (extra_args,)
    else:
        args = ()

    def do_delegate(e):
        delegate(e, *args)

    handler = list_type(None)
    handler.setPredefDelegate(toplevel_type, after=do_delegate)
    return Parser(handler)

def simpleListFactory(list_type):
    """Used for simple parsers that support only one type of object"""
    def create(delegate, extra_args=None):
        """Create a Parser object for the specific tag type, on the fly"""
        return listParser(list_type, delegate, extra_args)
    return create

Feed     = simpleListFactory(EntryList)

Users    = simpleListFactory(UserList)

Direct   = simpleListFactory(DirectMessageList)

Statuses = simpleListFactory(StatusList)

HoseFeed = simpleListFactory(StatusList)

def parseXML(xml):
    return microdom.parseXMLString(xml)

def parseUpdateResponse(xml):
    return parseXML(xml).getElementsByTagName("id")[0].firstChild().data

# vim: set expandtab:
<|endoftext|>