<commit_before>from decimal import Decimal
from datetime import date, timedelta

from django.db import connection, transaction
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.views.decorators.csrf import csrf_exempt

from .models import Prediction, Week

@login_required
def prijs_grafiek(request):
    result = "<grafiekinput>"
    result += "<meter_id>%s</meter_id>\n" % request.user.id
    result += "<naam>%s</naam>\n" % request.user.username

    begin = date(2011, 11, 1)
    end = date(2012, 5, 1)
    weeknr = 1

    cursor = connection.cursor()

    while begin + timedelta((weeknr - 1) * 7) <= end:
        this_begin = begin + timedelta((weeknr - 1) * 7)

        cursor.execute("""SELECT status, count(*) AS cnt

FROM pollster_health_status AS S,

(SELECT MAX(id) AS id, global_id
FROM pollster_results_weekly
WHERE timestamp BETWEEN %s AND %s + 7
GROUP BY global_id
ORDER BY global_id DESC
) AS W

WHERE S.pollster_results_weekly_id = W.id

GROUP BY status
""", [this_begin, this_begin])

        rows = cursor.fetchall()
        illnesses = dict(rows)

        total = sum(illnesses.values())
        ili = illnesses.get("ILI", 0)

        if total == 0:
            result += '<week nr="%s">0.00</week>' % weeknr

        else:
            percentage = (100 * Decimal(ili) / Decimal(total)).quantize(Decimal('0.01'))
            result += '<week nr="%s">%s</week>' % (weeknr, percentage)

        weeknr += 1

    result += "</grafiekinput>"
    return HttpResponse(result, 'application/xml')

@login_required
def prijs_weergave(request):
    result = "<voorspelling>\n"
    result += "<meter_id>%s</meter_id>\n" % request.user.id
    result += "<naam>%s</naam>\n" % request.user.username

    prediction_qs = Prediction.objects.filter(user=request.user)
    if prediction_qs.count() == 1:
        prediction = prediction_qs.get()
        result += "<datum>%s</datum>\n" % prediction.date.strftime("%Y-%m-%d")

        for week in prediction.week_set.all():
            result += "<week nr=\"%s\">%s</week>\n" % (week.number, week.value)

    result += "</voorspelling>\n"
    return HttpResponse(result, 'application/xml')

@login_required
@csrf_exempt
def relay2(request):
    if request.method == "GET":
        return HttpResponse("""<form method="POST" action="">
<input name="week1" value="3.32">
<input name="week2" value="3.51">
<input name="week3" value="3.37">
<input type="submit">
</form>
        """)

    prediction, created = Prediction.objects.get_or_create(user=request.user)
    if created:
        return HttpResponse("prijsvraag al ingediend")

    for week_nr in range(1, 32):
        value = request.POST.get('week%s' % week_nr)
        Week.objects.create(prediction=prediction, number=week_nr, value=value)

    return HttpResponseRedirect("/")
<commit_msg>Contest starts only half november<commit_after>from decimal import Decimal
from datetime import date, timedelta

from django.db import connection, transaction
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.views.decorators.csrf import csrf_exempt

from .models import Prediction, Week

@login_required
def prijs_grafiek(request):
    result = "<grafiekinput>"
    result += "<meter_id>%s</meter_id>\n" % request.user.id
    result += "<naam>%s</naam>\n" % request.user.username

    begin = date(2011, 11, 15)
    end = date(2012, 5, 1)
    weeknr = 1

    cursor = connection.cursor()

    while begin + timedelta((weeknr - 1) * 7) <= end:
        this_begin = begin + timedelta((weeknr - 1) * 7)

        cursor.execute("""SELECT status, count(*) AS cnt

FROM pollster_health_status AS S,

(SELECT MAX(id) AS id, global_id
FROM pollster_results_weekly
WHERE timestamp BETWEEN %s AND %s + 7
GROUP BY global_id
ORDER BY global_id DESC
) AS W

WHERE S.pollster_results_weekly_id = W.id

GROUP BY status
""", [this_begin, this_begin])

        rows = cursor.fetchall()
        illnesses = dict(rows)

        total = sum(illnesses.values())
        ili = illnesses.get("ILI", 0)

        if total == 0:
            result += '<week nr="%s">0.00</week>' % weeknr

        else:
            percentage = (100 * Decimal(ili) / Decimal(total)).quantize(Decimal('0.01'))
            result += '<week nr="%s">%s</week>' % (weeknr, percentage)

        weeknr += 1

    result += "</grafiekinput>"
    return HttpResponse(result, 'application/xml')

@login_required
def prijs_weergave(request):
    result = "<voorspelling>\n"
    result += "<meter_id>%s</meter_id>\n" % request.user.id
    result += "<naam>%s</naam>\n" % request.user.username

    prediction_qs = Prediction.objects.filter(user=request.user)
    if prediction_qs.count() == 1:
        prediction = prediction_qs.get()
        result += "<datum>%s</datum>\n" % prediction.date.strftime("%Y-%m-%d")

        for week in prediction.week_set.all():
            result += "<week nr=\"%s\">%s</week>\n" % (week.number, week.value)

    result += "</voorspelling>\n"
    return HttpResponse(result, 'application/xml')

@login_required
@csrf_exempt
def relay2(request):
    if request.method == "GET":
        return HttpResponse("""<form method="POST" action="">
<input name="week1" value="3.32">
<input name="week2" value="3.51">
<input name="week3" value="3.37">
<input type="submit">
</form>
        """)

    prediction, created = Prediction.objects.get_or_create(user=request.user)
    if created:
        return HttpResponse("prijsvraag al ingediend")

    for week_nr in range(1, 32):
        value = request.POST.get('week%s' % week_nr)
        Week.objects.create(prediction=prediction, number=week_nr, value=value)

    return HttpResponseRedirect("/")
<|endoftext|>


<commit_before>from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.db import models

##############################################################################################################
#
# Originally had my own hacky global storage of content type, but it turns out that contenttype.models
# wisely caches content types! No hit to the db beyodn the first call - no need for us to do our own
# custom caching.
#
# See ContentType.get_for_model() code for details.

class Domain(models.Model):
    '''Domain is the highest level collection of people/stuff
       in the system.  Pretty much everything happens at the
       domain-level, including user membership, permission to
       see data, reports, charts, etc.'''

    name  = models.CharField(max_length = 64, unique=True)
    is_active = models.BooleanField(default=False)
    #description = models.CharField(max_length=255, null=True, blank=True)
    #timezone = models.CharField(max_length=64,null=True)

    # Utility function - gets active domains in which user has an active membership
    # Note that User.is_active is not checked here - we're only concerned about usable
    # domains in which the user can theoretically participate, not whether the user
    # is cleared to login.
    @staticmethod
    def active_for_user(user):
        return Domain.objects.filter( membership__member_type = ContentType.objects.get_for_model(User),
                                      membership__member_id = user.id,
                                      membership__is_active=True, # Looks in membership table
                                      is_active=True) # Looks in domain table

    def __unicode__(self):
        return self.name

##############################################################################################################
#
# Use cases:
#
# Get all members in a domain:
# Member.objects.filter(member_type = 3, domain = 1) then iterate - slow, because of one query (for User) per row
# User.objects.filter(membership__domain = 2) - fast, but requires the addition of a GenericRelation to User.
# See UserInDomain, below.
#
# Get all domains to which a member belongs:
# User.objects.get(id = 1).membership.all() and then iterate to pick out domains - slow, because of one query
#       (for Domain) per row. Requires GenericRelation on User.
# Member.objects.filter(member_type = 3, member_id = 1).query.as_sql()   Generate same SQL, and require same
#       slow iteration
# Domain.objects.filter(membership__member_type = 3, membership__member_id = 1) - fast, and requires no new fields
#       (as Domain is a FK of Member)
#

member_limits = {'model__in':('user', 'patient')}

class Membership(models.Model):
    domain = models.ForeignKey(Domain)
    member_type = models.ForeignKey(ContentType, limit_choices_to=member_limits)
    member_id = models.PositiveIntegerField()
    member_object = generic.GenericForeignKey('member_type', 'member_id')
    is_active = models.BooleanField(default=False)

    def __unicode__(self):
        return str(self.member_type) + str(self.member_id) + str(self.member_object)

##############################################################################################################

class RegistrationRequest(models.Model):
    tos_confirmed = models.BooleanField(default=False)
    # No verbose name on times and IPs - filled in on server
    request_time = models.DateTimeField()
    request_ip = models.IPAddressField()
    activation_guid = models.CharField(max_length=32, unique=True)
    # confirm info is blank until a confirming click is received
    confirm_time = models.DateTimeField(null=True, blank=True)
    confirm_ip = models.IPAddressField(null=True, blank=True)
    domain = models.OneToOneField(Domain)
    new_user = models.ForeignKey(User, related_name='new_user') # Not clear if we'll always create a new user - might be many reqs to one user, thus FK
    # requesting_user is only filled in if a logged-in user requests a domain.
    requesting_user = models.ForeignKey(User, related_name='requesting_user', null=True, blank=True) # blank and null -> FK is optional.

    class Meta:
        db_table = 'domain_registration_request'

# To be added:
# language
# number pref
# currency pref
# date pref
# time pref

##############################################################################################################

class Settings(models.Model):
    domain = models.OneToOneField(Domain)
    max_users = models.PositiveIntegerField()

# To be added - all of the date, time, etc. fields that will go into RegistrationRequest

##############################################################################################################
#
# http://bolhoed.net/blog/how-to-dynamically-add-fields-to-a-django-model shows:
#
# User.add_to_class('membership', generic.GenericRelation(Membership, content_type_field='member_type', object_id_field='member_id'))
#
# Rather than that hackery, I tried to implemenet a trivial proxy model for User, containing just the
# GenericRelation field. Doesn't work, though! Django complains about a field being defined on a proxy model.
#
# Looks like we have to enable the above hackery if we want an easy means of filtering users in a domain. Makes
# life easier, too, in that views will have access to this information.
#

User.add_to_class('domain_membership',
                  generic.GenericRelation( Membership, content_type_field='member_type', object_id_field='member_id' ) )

##############################################################################################################

# Monkeypatch a function onto User to tell if user is administrator of selected domain
def _admin_p (self):
    dom = getattr(self, 'selected_domain', None)
    if dom is not None:
        return self.has_row_perm(dom, Permissions.ADMINISTRATOR)
    else:
        return False

User.is_selected_dom_admin = _admin_p <commit_msg>Adding missing import from domain models<commit_after>from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.db import models
from domain import Permissions

##############################################################################################################
#
# Originally had my own hacky global storage of content type, but it turns out that contenttype.models
# wisely caches content types! No hit to the db beyond the first call - no need for us to do our own
# custom caching.
#
# See ContentType.get_for_model() code for details.

class Domain(models.Model):
    '''Domain is the highest level collection of people/stuff
       in the system.  Pretty much everything happens at the
       domain-level, including user membership, permission to
       see data, reports, charts, etc.'''

    name  = models.CharField(max_length = 64, unique=True)
    is_active = models.BooleanField(default=False)
    #description = models.CharField(max_length=255, null=True, blank=True)
    #timezone = models.CharField(max_length=64,null=True)

    # Utility function - gets active domains in which user has an active membership
    # Note that User.is_active is not checked here - we're only concerned about usable
    # domains in which the user can theoretically participate, not whether the user
    # is cleared to login.
    @staticmethod
    def active_for_user(user):
        return Domain.objects.filter( membership__member_type = ContentType.objects.get_for_model(User),
                                      membership__member_id = user.id,
                                      membership__is_active=True, # Looks in membership table
                                      is_active=True) # Looks in domain table

    def __unicode__(self):
        return self.name

##############################################################################################################
#
# Use cases:
#
# Get all members in a domain:
# Member.objects.filter(member_type = 3, domain = 1) then iterate - slow, because of one query (for User) per row
# User.objects.filter(membership__domain = 2) - fast, but requires the addition of a GenericRelation to User.
# See UserInDomain, below.
#
# Get all domains to which a member belongs:
# User.objects.get(id = 1).membership.all() and then iterate to pick out domains - slow, because of one query
#       (for Domain) per row. Requires GenericRelation on User.
# Member.objects.filter(member_type = 3, member_id = 1).query.as_sql()   Generate same SQL, and require same
#       slow iteration
# Domain.objects.filter(membership__member_type = 3, membership__member_id = 1) - fast, and requires no new fields
#       (as Domain is a FK of Member)
#

member_limits = {'model__in':('user', 'patient')}

class Membership(models.Model):
    domain = models.ForeignKey(Domain)
    member_type = models.ForeignKey(ContentType, limit_choices_to=member_limits)
    member_id = models.PositiveIntegerField()
    member_object = generic.GenericForeignKey('member_type', 'member_id')
    is_active = models.BooleanField(default=False)

    def __unicode__(self):
        return str(self.member_type) + str(self.member_id) + str(self.member_object)

##############################################################################################################

class RegistrationRequest(models.Model):
    tos_confirmed = models.BooleanField(default=False)
    # No verbose name on times and IPs - filled in on server
    request_time = models.DateTimeField()
    request_ip = models.IPAddressField()
    activation_guid = models.CharField(max_length=32, unique=True)
    # confirm info is blank until a confirming click is received
    confirm_time = models.DateTimeField(null=True, blank=True)
    confirm_ip = models.IPAddressField(null=True, blank=True)
    domain = models.OneToOneField(Domain)
    new_user = models.ForeignKey(User, related_name='new_user') # Not clear if we'll always create a new user - might be many reqs to one user, thus FK
    # requesting_user is only filled in if a logged-in user requests a domain.
    requesting_user = models.ForeignKey(User, related_name='requesting_user', null=True, blank=True) # blank and null -> FK is optional.

    class Meta:
        db_table = 'domain_registration_request'

# To be added:
# language
# number pref
# currency pref
# date pref
# time pref

##############################################################################################################

class Settings(models.Model):
    domain = models.OneToOneField(Domain)
    max_users = models.PositiveIntegerField()

# To be added - all of the date, time, etc. fields that will go into RegistrationRequest

##############################################################################################################
#
# http://bolhoed.net/blog/how-to-dynamically-add-fields-to-a-django-model shows:
#
# User.add_to_class('membership', generic.GenericRelation(Membership, content_type_field='member_type', object_id_field='member_id'))
#
# Rather than that hackery, I tried to implemenet a trivial proxy model for User, containing just the
# GenericRelation field. Doesn't work, though! Django complains about a field being defined on a proxy model.
#
# Looks like we have to enable the above hackery if we want an easy means of filtering users in a domain. Makes
# life easier, too, in that views will have access to this information.
#

User.add_to_class('domain_membership',
                  generic.GenericRelation( Membership, content_type_field='member_type', object_id_field='member_id' ) )

##############################################################################################################

# Monkeypatch a function onto User to tell if user is administrator of selected domain
def _admin_p (self):
    dom = getattr(self, 'selected_domain', None)
    if dom is not None:
        return self.has_row_perm(dom, Permissions.ADMINISTRATOR)
    else:
        return False

User.is_selected_dom_admin = _admin_p <|endoftext|>


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

from django.conf.urls import patterns, url

urlpatterns = patterns('apps.feedback.views',
    url(r'^$', 'index', name='feedback_index'),
    url(r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/$', "feedback", name="feedback"),
    url(r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/$', "result", name="result"),
    url(r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/chartdata/$', "chart_data", name="chart_data"),
    url(r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/(?P<token>\w+)/$', "results_token", name="results_token"),
    url(r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/(?P<token>\w+)/chartdata/$', "chart_data_token", name="chart_data_token"),
    url(r'^deleteanswer/$', "delete_answer", name="delete_anwer"),
)
<commit_msg>pep8 feedback urls<commit_after># -*- coding: utf-8 -*-

from django.conf.urls import patterns, url

fb = r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/$'
r = r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/$'
cd = r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/chartdata/$'
rt = r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/(?P<token>\w+)/$'
cdt = r'^(?P<applabel>\w+)/(?P<appmodel>\w+)/(?P<object_id>\d+)/(?P<feedback_id>\d+)/results/(?P<token>\w+)/chartdata/$'

urlpatterns = patterns(
    'apps.feedback.views',
    url(r'^$', 'index', name='feedback_index'),
    url(fb, "feedback", name="feedback"),
    url(r, "result", name="result"),
    url(cd, "chart_data", name="chart_data"),
    url(rt, "results_token", name="results_token"),
    url(cdt, "chart_data_token", name="chart_data_token"),
    url(r'^deleteanswer/$', "delete_answer", name="delete_anwer"),
)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4

from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseServerError, Http404
from django.template import RequestContext
from apps.reporters.models import Location, LocationType
from apps.supply.models import Shipment, Transaction, Stock, PartialTransaction
from rapidsms.webui.utils import render_to_response
from django.db import models
# The import here newly added for serializations
from django.core import serializers
from django.core.paginator import *
from random import randrange, seed
import time
import sys

#Parameter for paging reports outputs
ITEMS_PER_PAGE = 20

#This is required for ***unicode*** characters***
# do we really need to reload it?  TIM to check
reload(sys)
sys.setdefaultencoding('utf-8')

#Views for handling summary of Reports Displayed as Location Tree
def index(req, locid=None):
    reload(sys)
    sys.setdefaultencoding('utf-8')
    # TODO (better) ajax this
    if not locid:
        locid = 1
    try:
        location = Location.objects.get(id=locid)
    except Location.DoesNotExist:
        location= None
    print "location: %s" % location
    return render_to_response(req, "nigeria/index.html",{'location':location })

#Views for handling Reports Summary Displayed as Locations Tree (State -> LGA -> Wards -> DPs -> MTs)
#def index(req):
#
#    #Variable to hold LGA dictionary
#    lga_dict={}
#
#    #TODO: Line below should be replaced by a for loop to iterate through objects for state retrieval through code="<statecode>" parameter to .get() method
#    state = Location.objects.get(code="20")
#
#    # Retrieves all LGAs in the states. TODO: This will be shifted in the forloop for state
#    lgas = state.children.all()[0:9]
#
#    for lga in lgas:
#        #Obtains list of wards from the model
#        wards = lga.children.all()
#
#        #Builds a dictionary of wards with LGA as keys
#        lga_dict[lga] = wards
#
#    #TODO: This list should be removed after creating a for loop to retrieve states  from model (above)
#    states = ['Abia','Adamawa','Akwa-Ibom','Anambra','Bauchi','Bayelsa',
#        'Benue','Borno','Cross River','Delta','Ebonyi','Edo','Ekiti','Enugu','FCT','Gombe','Imo','Jigawa','Kaduna','Kano','Katsina','Kebbi','Kogi','Kwara','Lagos','Nasarawa','Niger','Ogun','Ondo','Osun','Oyo','Plateau','Rivers','Sokoto','Taraba','Yobe','Zamfara']
#
#
#    return render_to_response(req, "nigeria/index.html",{'st':state, 'states':states,'lgas':lga_dict})

def logistics_summary(req, locid):
    # Get the location we are going to work with.
    # If there is no location set, this will default to the first
    # one in the database.  If there are no locations in the
    # database we are S.O.L.
    if not locid:
        locid = 1
    try:
        location = Location.objects.get(pk=locid)
    except Location.DoesNotExist:
        location = None

    # get all the transactions that this location was involved in
    transactions_from_loc = Transaction.objects.all().filter(shipment__origin=location)
    transactions_to_loc = Transaction.objects.all().filter(shipment__destination=location)
    transactions = (transactions_from_loc | transactions_to_loc).order_by("-shipment__sent")

    # get any place this location has shipped to or from.
    # we will use these to generate some plots
    # and pass them forward to the template
    locations_shipped_to = []
    [locations_shipped_to.append(trans.shipment.destination) for trans in transactions_from_loc if trans.shipment.destination not in locations_shipped_to]

    # set the stock value in all the children.
    # this is now handled by signals!  see supply/models.py
    #[_set_stock(child) for child in locations_shipped_to]

    # get some JSON strings for the plots
    stock_per_loc_data, stock_per_loc_options = _get_stock_per_location_strings(locations_shipped_to)
    stock_over_time_data, stock_over_time_options = _get_stock_over_time_strings([location])
    stock_over_time_child_data, stock_over_time_child_options = _get_stock_over_time_strings(locations_shipped_to)

    paginator = Paginator(transactions, ITEMS_PER_PAGE)

    try:
        page = int(req.GET['page'])
    except:
        page = 1

    try:
        transactions_pager = paginator.page(page)
    except:
        raise Http404

    # send the whole list of stuff back to the template
    return render_to_response(req, "nigeria/logistics_summary.html",
                              {'location': location,
                               'child_locations': locations_shipped_to,
                               'transactions_pager': transactions_pager,
                               'paginator': paginator,
                               'page': page,
                               'stock_per_loc_plot_data' : stock_per_loc_data,
                               'stock_per_loc_plot_options' : stock_per_loc_options,
                               'stock_over_time_data' : stock_over_time_data,
                               'stock_over_time_options' : stock_over_time_options,
                               'stock_over_time_child_data' : stock_over_time_child_data,
                               'stock_over_time_child_options' : stock_over_time_child_options
                               })

def generate(req):
    # for the demo, to quickly generate dps and teams for all wards
    all_wards = Location.objects.all().filter(type__name__iexact="ward")
    dps_per_ward = 3
    teams_per_dp = 4
    dp_type = LocationType.objects.get(name__iexact="Distribution Point")
    team_type = LocationType.objects.get(name__iexact="Mobilization Team")
    teams_created = 0
    dps_created = 0
    ward_count = 1
    for ward in all_wards:
        print "ward: %s, %s of %s" %(ward.name, ward_count, len(all_wards))
        ward_count = ward_count + 1
        # assume if this ward already has DPs we don't need to do this
        if (len(ward.children.all()) > 0):
            continue
        for dp_id in range(1, dps_per_ward + 1):
            dp_name = "%s DP %s" %(ward.name, dp_id)
            dp_code = "%s%s" % (ward.code, dp_id)
            dp = Location.objects.create(name=dp_name, type=dp_type, code=dp_code, parent=ward)
            dps_created = dps_created + 1
            for team_id in range(1, teams_per_dp + 1):
                team_name = "%s TEAM %s" %(dp_name, team_id)
                team_code = "%s%s" % (dp_code, team_id)
                team = Location.objects.create(name=team_name, type=team_type, code=team_code, parent=dp)
                teams_created = teams_created + 1
    return HttpResponse("Successfully created %s distribution points and %s teams" % (dps_created, teams_created))

def supply_summary(req, frm, to, range):
    return render_to_response(req, "nigeria/supply_summary.html")

def bednets_summary(req, frm, to, range):
    return render_to_response(req, "nigeria/bednets_summary.html")

def coupons_summary(req, frm, to, range):
    return render_to_response(req, "nigeria/coupons_summary.html")

# Periodical Reporting  by day, week, month for coupons
def coupons_daily(req, locid):

    if not locid:
        locid = 1
    try:
        location = Location.objects.get(pk=locid)
        #_set_stock(location)
    except Location.DoesNotExist:
        location = None

    #TODO: Generate and Send all plots data from here to the templates

    return render_to_response(req, "nigeria/coupons_daily.html", {'location': location})

def coupons_weekly(req, locid):
    return render_to_response(req, "nigeria/coupons_weekly.html")

def coupons_monthly(req, locid):
    return render_to_response(req, "nigeria/coupons_monthly.html")

# Periodical Reporting  by day, week, month for bednets
def bednets_daily(req, locid):
    if not locid:
        locid = 1
    try:
        location = Location.objects.get(pk=locid)
        #_set_stock(location)
    except Location.DoesNotExist:
        location = None

    return render_to_response(req, "nigeria/bednets_daily.html", {'location': location})

def bednets_weekly(req, locid):
    return render_to_response(req, "nigeria/bednets_weekly.html")

def bednets_monthly(req, locid):
    return render_to_response(req, "nigeria/bednets_monthly.html")

# Periodical Reporting  by day, week, month for supply
#Builds daily supply reporting
def supply_daily(req, locid):
    return render_to_response(req, "nigeria/supply_daily.html")

#Builds weekly supply detail
def supply_weekly(req, locid):
    return render_to_response(req, "nigeria/supply_weekly.html")

#Build Monthly supply detail
def supply_monthly(req, locid):
    return render_to_response(req, "nigeria/supply_monthly.html")

def _set_stock(location):
    '''Get the stock object associated with this.  None if none found'''
    try:
        location.stock = Stock.objects.get(location=location)
    except Stock.DoesNotExist:
        # this isn't a real error, we just don't have any stock information
        location.stock = None

def _set_net_data(location):
    '''Get the stock object associated with this.  None if none found'''
    try:
        location.stock = Stock.objects.get(location=location)
    except Stock.DoesNotExist:
        # this isn't a real error, we just don't have any stock information
        location.stock = None

def _set_net_card_data(location):
    '''Get the stock object associated with this.  None if none found'''
    try:
        location.stock = Stock.objects.get(location=location)
    except Stock.DoesNotExist:
        # this isn't a real error, we just don't have any stock information
        location.stock = None

def _get_stock_per_location_strings(locations):
    '''Get a JSON formatted list that flot can plot
       based on the data in the stock table'''
    # this is the sample format
    #data = '''[
    #        {"bars":{"show":true},"label":"Ajingi","data":[[-0.5,100]]},
    #        {"bars":{"show":true},"label":"Bebeji","data":[[0.5,650]]}
    #        ]'''

    # loop over all the locations and if they have a stock
    # create a string describing it and add it to the list.
    # count will store the index of each item.
    count = 0
    rows = []
    for location in locations:
        # if the location doesn't have any stock we won't display it
        if location.stock:
            row = '{"bars":{"show":true},"label":"%s","data":[[%s,%s]]}' % (location.name, count, location.stock.balance)
            rows.append(row)
            count = count + 1
    data = "[%s]" % ", ".join(rows)
    options = '{"grid":{"clickable":true},"xaxis":{"min":0,"ticks":[],"tickFormatter":"string"},"yaxis":{"min":0}}'
    return (data, options)

def _get_distribution_over_time_strings(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Nets Distribution Data'''

def _get_distribution_per_distribution_team_strings(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Nets Distribution Data'''

def _get_mobilization_data_over_time_strings(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Net Cards Distribution Data'''

def _get_mobilization_data_per_mobilization_team_string(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Net Cards Distribution Data'''

def _get_stock_over_time_strings(locations):
    '''Get a JSON formatted list that flot can plot
       based on the data in the stock table'''

    # this is the sample format
    #data = '''[
    #        {"label":"Ajingi","data":[[1239706800000,1000],[1239793200000,800],[1239966000000,650]]},
    #        {"label":"Bebeji","data":[[1239706800000,500],[1239793200000,350],[1239836400000,250]]},
    #        ]'''

    rows = []
    for location in locations:
        # get all the partial transactions that affected this stock
        updates = PartialTransaction.get_all_with_stock_updates(location).order_by("date")
        if len(updates) > 0:
            update_strings = []
            for update in updates:
                ud_time = str(time.mktime(update.date.timetuple()))
                udt_formatted = ud_time[0:ud_time.find(".")] + "000"
                update_strings.append("[%s,%s]" % (udt_formatted, update.stock))
            #print "adding update for %s" % location
            rows.append('{"label":"%s", "data":[%s]}' % (location.name, ",".join(update_strings)))
    data = "[%s]" % (",".join(rows))
    options = '{"bars":{"show":false},"points":{"show":true},"grid":{"clickable":false},"xaxis":{"mode":"time","timeformat":"%m/%d/%y"},"yaxis":{"min":0},"legend":{"show":true},"lines":{"show":true}}'
    return (data, options)<commit_msg>cleaning up the nigeria views file<commit_after>#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4

from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseServerError, Http404
from django.template import RequestContext
from apps.reporters.models import Location, LocationType
from apps.supply.models import Shipment, Transaction, Stock, PartialTransaction
from rapidsms.webui.utils import render_to_response
from django.db import models
# The import here newly added for serializations
from django.core import serializers
from django.core.paginator import *
from random import randrange, seed
import time
import sys

#Parameter for paging reports outputs
ITEMS_PER_PAGE = 20

#This is required for ***unicode*** characters***
# do we really need to reload it?  TIM to check
reload(sys)
sys.setdefaultencoding('utf-8')

#Views for handling summary of Reports Displayed as Location Tree
def index(req, locid=None):
    if not locid:
        locid = 1
    try:
        location = Location.objects.get(id=locid)
    except Location.DoesNotExist:
        location= None
    print "location: %s" % location
    return render_to_response(req, "nigeria/index.html",{'location':location })

def logistics_summary(req, locid):
    # Get the location we are going to work with.
    # If there is no location set, this will default to the first
    # one in the database.  If there are no locations in the
    # database we are S.O.L.
    if not locid:
        locid = 1
    try:
        location = Location.objects.get(pk=locid)
    except Location.DoesNotExist:
        location = None

    # get all the transactions that this location was involved in
    transactions_from_loc = Transaction.objects.all().filter(shipment__origin=location)
    transactions_to_loc = Transaction.objects.all().filter(shipment__destination=location)
    transactions = (transactions_from_loc | transactions_to_loc).order_by("-shipment__sent")

    # get any place this location has shipped to or from.
    # we will use these to generate some plots
    # and pass them forward to the template
    locations_shipped_to = []
    [locations_shipped_to.append(trans.shipment.destination) for trans in transactions_from_loc if trans.shipment.destination not in locations_shipped_to]

    # set the stock value in all the children.
    # this is now handled by signals!  see supply/models.py
    #[_set_stock(child) for child in locations_shipped_to]

    # get some JSON strings for the plots
    stock_per_loc_data, stock_per_loc_options = _get_stock_per_location_strings(locations_shipped_to)
    stock_over_time_data, stock_over_time_options = _get_stock_over_time_strings([location])
    stock_over_time_child_data, stock_over_time_child_options = _get_stock_over_time_strings(locations_shipped_to)

    paginator = Paginator(transactions, ITEMS_PER_PAGE)

    try:
        page = int(req.GET['page'])
    except:
        page = 1

    try:
        transactions_pager = paginator.page(page)
    except:
        raise Http404

    # send the whole list of stuff back to the template
    return render_to_response(req, "nigeria/logistics_summary.html",
                              {'location': location,
                               'child_locations': locations_shipped_to,
                               'transactions_pager': transactions_pager,
                               'paginator': paginator,
                               'page': page,
                               'stock_per_loc_plot_data' : stock_per_loc_data,
                               'stock_per_loc_plot_options' : stock_per_loc_options,
                               'stock_over_time_data' : stock_over_time_data,
                               'stock_over_time_options' : stock_over_time_options,
                               'stock_over_time_child_data' : stock_over_time_child_data,
                               'stock_over_time_child_options' : stock_over_time_child_options
                               })

def generate(req):
    # for the demo, to quickly generate dps and teams for all wards
    all_wards = Location.objects.all().filter(type__name__iexact="ward")
    dps_per_ward = 3
    teams_per_dp = 4
    dp_type = LocationType.objects.get(name__iexact="Distribution Point")
    team_type = LocationType.objects.get(name__iexact="Mobilization Team")
    teams_created = 0
    dps_created = 0
    ward_count = 1
    for ward in all_wards:
        print "ward: %s, %s of %s" %(ward.name, ward_count, len(all_wards))
        ward_count = ward_count + 1
        # assume if this ward already has DPs we don't need to do this
        if (len(ward.children.all()) > 0):
            continue
        for dp_id in range(1, dps_per_ward + 1):
            dp_name = "%s DP %s" %(ward.name, dp_id)
            dp_code = "%s%s" % (ward.code, dp_id)
            dp = Location.objects.create(name=dp_name, type=dp_type, code=dp_code, parent=ward)
            dps_created = dps_created + 1
            for team_id in range(1, teams_per_dp + 1):
                team_name = "%s TEAM %s" %(dp_name, team_id)
                team_code = "%s%s" % (dp_code, team_id)
                team = Location.objects.create(name=team_name, type=team_type, code=team_code, parent=dp)
                teams_created = teams_created + 1
    return HttpResponse("Successfully created %s distribution points and %s teams" % (dps_created, teams_created))

def supply_summary(req, frm, to, range):
    return render_to_response(req, "nigeria/supply_summary.html")

def bednets_summary(req, frm, to, range):
    return render_to_response(req, "nigeria/bednets_summary.html")

def coupons_summary(req, frm, to, range):
    return render_to_response(req, "nigeria/coupons_summary.html")

# Periodical Reporting  by day, week, month for coupons
def coupons_daily(req, locid):

    if not locid:
        locid = 1
    try:
        location = Location.objects.get(pk=locid)
        #_set_stock(location)
    except Location.DoesNotExist:
        location = None

    #TODO: Generate and Send all plots data from here to the templates

    return render_to_response(req, "nigeria/coupons_daily.html", {'location': location})

def coupons_weekly(req, locid):
    return render_to_response(req, "nigeria/coupons_weekly.html")

def coupons_monthly(req, locid):
    return render_to_response(req, "nigeria/coupons_monthly.html")

# Periodical Reporting  by day, week, month for bednets
def bednets_daily(req, locid):
    if not locid:
        locid = 1
    try:
        location = Location.objects.get(pk=locid)
        #_set_stock(location)
    except Location.DoesNotExist:
        location = None

    return render_to_response(req, "nigeria/bednets_daily.html", {'location': location})

def bednets_weekly(req, locid):
    return render_to_response(req, "nigeria/bednets_weekly.html")

def bednets_monthly(req, locid):
    return render_to_response(req, "nigeria/bednets_monthly.html")

# Periodical Reporting  by day, week, month for supply
#Builds daily supply reporting
def supply_daily(req, locid):
    return render_to_response(req, "nigeria/supply_daily.html")

#Builds weekly supply detail
def supply_weekly(req, locid):
    return render_to_response(req, "nigeria/supply_weekly.html")

#Build Monthly supply detail
def supply_monthly(req, locid):
    return render_to_response(req, "nigeria/supply_monthly.html")

def _set_stock(location):
    '''Get the stock object associated with this.  None if none found'''
    try:
        location.stock = Stock.objects.get(location=location)
    except Stock.DoesNotExist:
        # this isn't a real error, we just don't have any stock information
        location.stock = None

def _set_net_data(location):
    '''Get the stock object associated with this.  None if none found'''
    try:
        location.stock = Stock.objects.get(location=location)
    except Stock.DoesNotExist:
        # this isn't a real error, we just don't have any stock information
        location.stock = None

def _set_net_card_data(location):
    '''Get the stock object associated with this.  None if none found'''
    try:
        location.stock = Stock.objects.get(location=location)
    except Stock.DoesNotExist:
        # this isn't a real error, we just don't have any stock information
        location.stock = None

def _get_stock_per_location_strings(locations):
    '''Get a JSON formatted list that flot can plot
       based on the data in the stock table'''
    # this is the sample format
    #data = '''[
    #        {"bars":{"show":true},"label":"Ajingi","data":[[-0.5,100]]},
    #        {"bars":{"show":true},"label":"Bebeji","data":[[0.5,650]]}
    #        ]'''

    # loop over all the locations and if they have a stock
    # create a string describing it and add it to the list.
    # count will store the index of each item.
    count = 0
    rows = []
    for location in locations:
        # if the location doesn't have any stock we won't display it
        if location.stock:
            row = '{"bars":{"show":true},"label":"%s","data":[[%s,%s]]}' % (location.name, count, location.stock.balance)
            rows.append(row)
            count = count + 1
    data = "[%s]" % ", ".join(rows)
    options = '{"grid":{"clickable":true},"xaxis":{"min":0,"ticks":[],"tickFormatter":"string"},"yaxis":{"min":0}}'
    return (data, options)

def _get_distribution_over_time_strings(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Nets Distribution Data'''

def _get_distribution_per_distribution_team_strings(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Nets Distribution Data'''

def _get_mobilization_data_over_time_strings(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Net Cards Distribution Data'''

def _get_mobilization_data_per_mobilization_team_string(ward):
    '''Get a JSON formated list for flot plots on the template,
    based on data in the Net Cards Distribution Data'''

def _get_stock_over_time_strings(locations):
    '''Get a JSON formatted list that flot can plot
       based on the data in the stock table'''

    # this is the sample format
    #data = '''[
    #        {"label":"Ajingi","data":[[1239706800000,1000],[1239793200000,800],[1239966000000,650]]},
    #        {"label":"Bebeji","data":[[1239706800000,500],[1239793200000,350],[1239836400000,250]]},
    #        ]'''

    rows = []
    for location in locations:
        # get all the partial transactions that affected this stock
        updates = PartialTransaction.get_all_with_stock_updates(location).order_by("date")
        if len(updates) > 0:
            update_strings = []
            for update in updates:
                ud_time = str(time.mktime(update.date.timetuple()))
                udt_formatted = ud_time[0:ud_time.find(".")] + "000"
                update_strings.append("[%s,%s]" % (udt_formatted, update.stock))
            #print "adding update for %s" % location
            rows.append('{"label":"%s", "data":[%s]}' % (location.name, ",".join(update_strings)))
    data = "[%s]" % (",".join(rows))
    options = '{"bars":{"show":false},"points":{"show":true},"grid":{"clickable":false},"xaxis":{"mode":"time","timeformat":"%m/%d/%y"},"yaxis":{"min":0},"legend":{"show":true},"lines":{"show":true}}'
    return (data, options)<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygal
from pygal.style import BlueStyle
from _locale import gettext
from operator import add

def generate_traffic_chart(traffic_data, inline=True):
    """Create a graph object from the input traffic data with pygal.
     If inline is set, the chart is being passed the option to not add an xml
     declaration header to the beginning of the `render()` output, so it can
      be directly included in HTML code (wrapped by a `<figure>`)
    :param traffic_data: The traffic data as given by `query_trafficdata()`
    :param inline: Determines the option `disable_xml_declaration`
    :return: The graph object
    """
    traffic_chart = pygal.Bar(
        title=gettext("Traffic (MB)"),
        height=350,
        show_y_guides=True,
        human_readable=False,
        major_label_font_size=12,
        label_font_size=12,
        style=BlueStyle,
        disable_xml_declaration=inline,   # for direct html import
    )

    days, in_values, out_values, credit = zip(*traffic_data['history'])
    traffic_chart.x_labels = days
    traffic_chart.add(gettext('Input'), in_values)
    traffic_chart.add(gettext('Output'), out_values)
    traffic_chart.add(gettext('Gesamt'), map(add, in_values, out_values))

    return traffic_chart

def render_traffic_chart(traffic_data, **kwargs):
    """Generate pure svg code ready to be included in HTML inside a <figure>.
    :param traffic_data: The traffic data as in generate_traffic_chart()
    :return: String
    """
    return generate_traffic_chart(traffic_data, **kwargs).render()<commit_msg>Deactivate autofetching standard pygal js files<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygal
from pygal.style import BlueStyle
from _locale import gettext
from operator import add

def generate_traffic_chart(traffic_data, inline=True):
    """Create a graph object from the input traffic data with pygal.
     If inline is set, the chart is being passed the option to not add an xml
     declaration header to the beginning of the `render()` output, so it can
      be directly included in HTML code (wrapped by a `<figure>`)
    :param traffic_data: The traffic data as given by `query_trafficdata()`
    :param inline: Determines the option `disable_xml_declaration`
    :return: The graph object
    """
    traffic_chart = pygal.Bar(
        title=gettext("Traffic (MB)"),
        height=350,
        show_y_guides=True,
        human_readable=False,
        major_label_font_size=12,
        label_font_size=12,
        style=BlueStyle,
        disable_xml_declaration=inline,   # for direct html import
        js=[],  # prevent automatically fetching scripts from github
    )

    days, in_values, out_values, credit = zip(*traffic_data['history'])
    traffic_chart.x_labels = days
    traffic_chart.add(gettext('Input'), in_values)
    traffic_chart.add(gettext('Output'), out_values)
    traffic_chart.add(gettext('Gesamt'), map(add, in_values, out_values))

    return traffic_chart

def render_traffic_chart(traffic_data, **kwargs):
    """Generate pure svg code ready to be included in HTML inside a <figure>.
    :param traffic_data: The traffic data as in generate_traffic_chart()
    :return: String
    """
    return generate_traffic_chart(traffic_data, **kwargs).render()<|endoftext|>


<commit_before><commit_msg>Using request body for ios subscription.<commit_after><|endoftext|>


<commit_before>from fabric.api import run, cd, env
from fabric import state

DISTANT_PATH = 'click-and-deploy'

def pull():
    with cd(DISTANT_PATH):
        run('git pull')

def restart_services():
    run('sudo supervisorctl restart deploy:')

def deploy():
    pull()
    restart_services()
<commit_msg>[self.app] restart only the web part, not the queues<commit_after>from fabric.api import run, cd, env
from fabric import state

DISTANT_PATH = 'click-and-deploy'

def pull():
    with cd(DISTANT_PATH):
        run('git pull')

def restart_services():
    run('sudo supervisorctl restart deploy:deploy-web')

def deploy():
    pull()
    restart_services()
<|endoftext|>


<commit_before>version = (0, 3, 2)

__version__ = '.'.join(map(str, version))
<commit_msg>Bump version<commit_after>version = (0, 3, 3)

__version__ = '.'.join(map(str, version))
<|endoftext|>


<commit_before>##########################################################################
#
#  Copyright (c) 2016, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import os
import gc
import sys
import time
import resource
import collections

import IECore

import Gaffer

class stats( Gaffer.Application ) :

	def __init__( self ) :

		Gaffer.Application.__init__( self )

		self.parameters().addParameters(

			[
				IECore.FileNameParameter(
					name = "script",
					description = "The script to examine.",
					defaultValue = "",
					allowEmptyString = False,
					extensions = "gfr",
					check = IECore.FileNameParameter.CheckType.MustExist,
				),

				IECore.FloatParameter(
					name = "frame",
					description = "The frame to evaluate statistics at.",
					defaultValue = 1,
				),

				IECore.StringParameter(
					name = "scene",
					description = "The name of a SceneNode or ScenePlug to examine.",
					defaultValue = "",
				),

			]

		)

		self.parameters().userData()["parser"] = IECore.CompoundObject(
			{
				"flagless" : IECore.StringVectorData( [ "script" ] )
			}
		)

	def _run( self, args ) :

		self.__timers = collections.OrderedDict()
		self.__memory = collections.OrderedDict()

		self.__memory["Application"] = _Memory.maxRSS()

		script = Gaffer.ScriptNode()
		script["fileName"].setValue( os.path.abspath( args["script"].value ) )

		with _Timer() as loadingTimer :
			script.load( continueOnError = True )
		self.__timers["Loading"] = loadingTimer

		self.__memory["Script"] = _Memory.maxRSS() - self.__memory["Application"]

		with Gaffer.Context( script.context() ) as context :

			context.setFrame( args["frame"].value )

			self.__printVersion( script )

			print ""

			self.__printSettings( script )

			print ""

			self.__printVariables( script )

			print ""

			self.__printNodes( script )

			if args["scene"].value :

				self.__printScene( script, args )

		with _Timer() as shutdownTimer :
			del script
			while gc.collect() :
				IECore.RefCounted.collectGarbage()
		self.__timers["Shutdown"] = shutdownTimer

		print ""

		self.__printMemory()

		print ""

		self.__printPerformance()

	def __printVersion( self, script ) :

		numbers = [ Gaffer.Metadata.nodeValue( script, "serialiser:" + x + "Version" ) for x in ( "milestone", "major", "minor", "patch" ) ]
		if None not in numbers :
			version = ".".join( str( x ) for x in numbers )
		else :
			version = "unknown"

		print "Gaffer Version : {version}".format( version = version )

	def __printItems( self, items ) :

		width = max( [ len( x[0] ) for x in items ] ) + 4
		for name, value in items :
			print "  {name:<{width}}{value}".format( name = name, width = width, value = value )

	def __printSettings( self, script ) :

		plugsToIgnore = {
			script["fileName"],
			script["unsavedChanges"],
			script["variables"],
		}

		items = []
		def itemsWalk( p ) :

			if p in plugsToIgnore :
				return

			if hasattr( p, "getValue" ) :
				items.append( ( p.relativeName( script ), p.getValue() ) )
			else :
				for c in p.children( Gaffer.Plug ) :
					itemsWalk( c )

		itemsWalk( script )

		print "Settings :\n"
		self.__printItems( items )

	def __printVariables( self, script ) :

		items = []
		for p in script["variables"] :
			data, name = script["variables"].memberDataAndName( p )
			if data is not None :
				items.append( ( name, data ) )

		print "Variables :\n"
		self.__printItems( items )

	def __printNodes( self, script ) :

		def countWalk( node, counter ) :

			if not isinstance( node, Gaffer.ScriptNode ) :
				counter[node.typeName()] += 1

			for c in node.children( Gaffer.Node ) :
				countWalk( c, counter )

		counter = collections.Counter()
		countWalk( script, counter )

		items = [ ( nodeType.rpartition( ":" )[2], count ) for nodeType, count in counter.most_common() ]
		items.extend( [
			( "", "" ),
			( "Total", sum( counter.values() ) ),
		] )

		print "Nodes :\n"
		self.__printItems( items )

	def __printScene( self, script, args ) :

		import GafferScene
		import GafferSceneTest

		scene = script.descendant( args["scene"].value )
		if isinstance( scene, Gaffer.Node ) :
			scene = next( ( x for x in scene.children( GafferScene.ScenePlug ) ), None )

		if scene is None :
			IECore.msg( IECore.Msg.Level.Error, "stats", "Scene \"%s\" does not exist" % args["scene"].value )
			return

		memory = _Memory.maxRSS()
		with _Timer() as sceneTimer :
			GafferSceneTest.traverseScene( scene )
		self.__timers["Scene generation"] = sceneTimer
		self.__memory["Scene generation"] = _Memory.maxRSS() - memory

		## \todo Calculate and print scene stats
		#  - Locations
		#  - Unique objects, attributes etc

	def __printMemory( self ) :

		objectPool = IECore.ObjectPool.defaultObjectPool()

		items = self.__memory.items()

		items.extend( [
			( "", "" ),
			( "Cache limit", _Memory( Gaffer.ValuePlug.getCacheMemoryLimit() ) ),
			( "Cache usage", _Memory( Gaffer.ValuePlug.cacheMemoryUsage() ) ),
			( "", "" ),
			( "Object pool limit", _Memory( objectPool.getMaxMemoryUsage() ) ),
			( "Object pool usage", _Memory( objectPool.memoryUsage() ) ),
			( "", "" ),
			( "Max resident size", _Memory.maxRSS() ),
		] )

		print "Memory :\n"
		self.__printItems( items )

	def __printPerformance( self ) :

			print "Performance :\n"
			self.__printItems( self.__timers.items() )

class _Timer( object ) :

	def __enter__( self ) :

		self.__time = time.time()
		self.__clock = time.clock()

		return self

	def __exit__( self, type, value, traceBack ) :

		self.__time = time.time() - self.__time
		self.__clock = time.clock() - self.__clock

	def __str__( self ) :

		return "%.3fs (wall), %.3fs (CPU)" % ( self.__time, self.__clock )

class _Memory( object ) :

	def __init__( self, bytes ) :

		self.__bytes = bytes

	@classmethod
	def maxRSS( cls ) :

		if sys.platform == "darwin" :
			return cls( resource.getrusage( resource.RUSAGE_SELF ).ru_maxrss )
		else :
			return cls( resource.getrusage( resource.RUSAGE_SELF ).ru_maxrss * 1024 )

	def __str__( self ) :

		return "%.3fM" % ( self.__bytes / ( 1024 * 1024. ) )

	def __sub__( self, other ) :

		return _Memory( self.__bytes - other.__bytes )

IECore.registerRunTimeTyped( stats )
<commit_msg>Stats app : Fix bug when there are no items to print.<commit_after>##########################################################################
#
#  Copyright (c) 2016, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import os
import gc
import sys
import time
import resource
import collections

import IECore

import Gaffer

class stats( Gaffer.Application ) :

	def __init__( self ) :

		Gaffer.Application.__init__( self )

		self.parameters().addParameters(

			[
				IECore.FileNameParameter(
					name = "script",
					description = "The script to examine.",
					defaultValue = "",
					allowEmptyString = False,
					extensions = "gfr",
					check = IECore.FileNameParameter.CheckType.MustExist,
				),

				IECore.FloatParameter(
					name = "frame",
					description = "The frame to evaluate statistics at.",
					defaultValue = 1,
				),

				IECore.StringParameter(
					name = "scene",
					description = "The name of a SceneNode or ScenePlug to examine.",
					defaultValue = "",
				),

			]

		)

		self.parameters().userData()["parser"] = IECore.CompoundObject(
			{
				"flagless" : IECore.StringVectorData( [ "script" ] )
			}
		)

	def _run( self, args ) :

		self.__timers = collections.OrderedDict()
		self.__memory = collections.OrderedDict()

		self.__memory["Application"] = _Memory.maxRSS()

		script = Gaffer.ScriptNode()
		script["fileName"].setValue( os.path.abspath( args["script"].value ) )

		with _Timer() as loadingTimer :
			script.load( continueOnError = True )
		self.__timers["Loading"] = loadingTimer

		self.__memory["Script"] = _Memory.maxRSS() - self.__memory["Application"]

		with Gaffer.Context( script.context() ) as context :

			context.setFrame( args["frame"].value )

			self.__printVersion( script )

			print ""

			self.__printSettings( script )

			print ""

			self.__printVariables( script )

			print ""

			self.__printNodes( script )

			if args["scene"].value :

				self.__printScene( script, args )

		with _Timer() as shutdownTimer :
			del script
			while gc.collect() :
				IECore.RefCounted.collectGarbage()
		self.__timers["Shutdown"] = shutdownTimer

		print ""

		self.__printMemory()

		print ""

		self.__printPerformance()

	def __printVersion( self, script ) :

		numbers = [ Gaffer.Metadata.nodeValue( script, "serialiser:" + x + "Version" ) for x in ( "milestone", "major", "minor", "patch" ) ]
		if None not in numbers :
			version = ".".join( str( x ) for x in numbers )
		else :
			version = "unknown"

		print "Gaffer Version : {version}".format( version = version )

	def __printItems( self, items ) :

		if not len( items ) :
			return

		width = max( [ len( x[0] ) for x in items ] ) + 4
		for name, value in items :
			print "  {name:<{width}}{value}".format( name = name, width = width, value = value )

	def __printSettings( self, script ) :

		plugsToIgnore = {
			script["fileName"],
			script["unsavedChanges"],
			script["variables"],
		}

		items = []
		def itemsWalk( p ) :

			if p in plugsToIgnore :
				return

			if hasattr( p, "getValue" ) :
				items.append( ( p.relativeName( script ), p.getValue() ) )
			else :
				for c in p.children( Gaffer.Plug ) :
					itemsWalk( c )

		itemsWalk( script )

		print "Settings :\n"
		self.__printItems( items )

	def __printVariables( self, script ) :

		items = []
		for p in script["variables"] :
			data, name = script["variables"].memberDataAndName( p )
			if data is not None :
				items.append( ( name, data ) )

		print "Variables :\n"
		self.__printItems( items )

	def __printNodes( self, script ) :

		def countWalk( node, counter ) :

			if not isinstance( node, Gaffer.ScriptNode ) :
				counter[node.typeName()] += 1

			for c in node.children( Gaffer.Node ) :
				countWalk( c, counter )

		counter = collections.Counter()
		countWalk( script, counter )

		items = [ ( nodeType.rpartition( ":" )[2], count ) for nodeType, count in counter.most_common() ]
		items.extend( [
			( "", "" ),
			( "Total", sum( counter.values() ) ),
		] )

		print "Nodes :\n"
		self.__printItems( items )

	def __printScene( self, script, args ) :

		import GafferScene
		import GafferSceneTest

		scene = script.descendant( args["scene"].value )
		if isinstance( scene, Gaffer.Node ) :
			scene = next( ( x for x in scene.children( GafferScene.ScenePlug ) ), None )

		if scene is None :
			IECore.msg( IECore.Msg.Level.Error, "stats", "Scene \"%s\" does not exist" % args["scene"].value )
			return

		memory = _Memory.maxRSS()
		with _Timer() as sceneTimer :
			GafferSceneTest.traverseScene( scene )
		self.__timers["Scene generation"] = sceneTimer
		self.__memory["Scene generation"] = _Memory.maxRSS() - memory

		## \todo Calculate and print scene stats
		#  - Locations
		#  - Unique objects, attributes etc

	def __printMemory( self ) :

		objectPool = IECore.ObjectPool.defaultObjectPool()

		items = self.__memory.items()

		items.extend( [
			( "", "" ),
			( "Cache limit", _Memory( Gaffer.ValuePlug.getCacheMemoryLimit() ) ),
			( "Cache usage", _Memory( Gaffer.ValuePlug.cacheMemoryUsage() ) ),
			( "", "" ),
			( "Object pool limit", _Memory( objectPool.getMaxMemoryUsage() ) ),
			( "Object pool usage", _Memory( objectPool.memoryUsage() ) ),
			( "", "" ),
			( "Max resident size", _Memory.maxRSS() ),
		] )

		print "Memory :\n"
		self.__printItems( items )

	def __printPerformance( self ) :

			print "Performance :\n"
			self.__printItems( self.__timers.items() )

class _Timer( object ) :

	def __enter__( self ) :

		self.__time = time.time()
		self.__clock = time.clock()

		return self

	def __exit__( self, type, value, traceBack ) :

		self.__time = time.time() - self.__time
		self.__clock = time.clock() - self.__clock

	def __str__( self ) :

		return "%.3fs (wall), %.3fs (CPU)" % ( self.__time, self.__clock )

class _Memory( object ) :

	def __init__( self, bytes ) :

		self.__bytes = bytes

	@classmethod
	def maxRSS( cls ) :

		if sys.platform == "darwin" :
			return cls( resource.getrusage( resource.RUSAGE_SELF ).ru_maxrss )
		else :
			return cls( resource.getrusage( resource.RUSAGE_SELF ).ru_maxrss * 1024 )

	def __str__( self ) :

		return "%.3fM" % ( self.__bytes / ( 1024 * 1024. ) )

	def __sub__( self, other ) :

		return _Memory( self.__bytes - other.__bytes )

IECore.registerRunTimeTyped( stats )
<|endoftext|>


<commit_before>import sys
import re
import logging
import html
from PyQt5.QtCore import Qt, pyqtSignal, QObject, QPoint, QRect, QSize, QEvent, QCoreApplication
from PyQt5.QtGui import QKeySequence, QValidator, QPainter, \
  QPen, QBrush, QColor, QPixmap, QMouseEvent
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QMenu, \
  QFrame, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QLineEdit, QLayout, \
  QComboBox, QGridLayout
import numpy as np

import log

__all__ = ['TableWidget', 'HSeparator', 'HBoxLayout', 'VBoxLayout',
           'ErrorBaloon', 'ErrorCheckEdit', 'FlowLayout',
           'DescriptionWidget', 'ComboBoxWithDescriptor']

class TableWidget(QTableWidget):
  def __init__(self):
    super().__init__()
    self.menu = QMenu()
    self.keys = []

    self.addAction('&Copy selected', self.copySelected, QKeySequence.Copy)
    self.addAction('&Paste',         self.paste,        QKeySequence.Paste)

  def addAction(self, label, func, key):
    self.menu.addAction(label, func, key)
    self.keys.append((key, func))

  def keyPressEvent(self, ev):
    for key, func in self.keys:
      if isinstance(key, QKeySequence.StandardKey):
        m = ev.matches(key)
      else:
        m = (ev.key() | int(ev.modifiers())) in key

      if m:
        ev.accept()
        func()
        return
    super().keyPressEvent(ev)

  def contextMenuEvent(self, ev):
    self.menu.exec_(ev.globalPos())

  def getSelectedItemTable(self):
    col, row, val = [], [], []
    for index in self.selectedIndexes():
      col.append(self.visualColumn(index.column()))
      row.append(self.visualRow(index.row()))
      val.append(self.item(index.row(), index.column()))

    col = np.array(col) - min(col)
    row = np.array(row) - min(row)

    data = dict(zip(zip(row, col), val))
    tbl = [[data[(r, c)] for c in range(max(col)+1)] for r in range(max(row)+1)]
    return tbl

  def copySelected(self):
    tbl = self.getSelectedItemTable()
    QApplication.clipboard().setText('\n'.join([
      '\t'.join([item.text().strip() for item in r]) for r in tbl]))

  def paste(self):
    text = QApplication.clipboard().text()
    data = [[c.strip() for c in l.split('\t')] for l in re.split(r'\r?\n', text)]

    sel = self.getSelectedItemTable()

    if len(sel) == 0:
      return
    elif len(sel) == 1 and len(sel[0]) == 1:
      r0 = self.visualRow(sel[0][0].row())
      c0 = self.visualColumn(sel[0][0].column())
      v2l_r = dict([(self.visualRow(r), r)    for r in range(self.rowCount())])
      v2l_c = dict([(self.visualColumn(c), c) for c in range(self.columnCount())])
      for r, vals in enumerate(data):
        for c, text in enumerate(vals):
          item = self.item(v2l_r[r0+r], v2l_c[c0+c])
          if item and item.flags() & Qt.ItemIsEditable:
            item.setText(text)
            self.cellChanged.emit(item.row(), item.column())
      return

    selerr_msg = 'The shapes of table selection and paste data are different'
    if len(sel) != len(data):
      logging.error(selerr_msg)
      return

    for items, values in zip(sel, data):
      if len(items) != len(values):
        logging.error(selerr_msg)
        return
      for item, val in zip(items, values):
        if val and not item:
          logging.error(selerr_msg)
          return

    for items, values in zip(sel, data):
      for item, val in zip(items, values):
        if item:
          item.setText(val)

class HSeparator(QFrame):
  def __init__(self):
    super().__init__()
    self.setFrameShape(QFrame.HLine)
    self.setFrameShadow(QFrame.Sunken)

class HBoxLayout(QHBoxLayout):
  def __init__(self):
    super().__init__()
    self.setContentsMargins(0, 0, 0, 0)
    self.setSpacing(4)

class VBoxLayout(QVBoxLayout):
  def __init__(self):
    super().__init__()
    self.setContentsMargins(0, 0, 0, 0)
    self.setSpacing(4)

class ErrorBaloon(QFrame):
  def __init__(self):
    super().__init__()

    self.label = QLabel()

    vbox = VBoxLayout()
    vbox.setContentsMargins(4, 4, 4, 4)
    vbox.addWidget(self.label)
    self.setLayout(vbox)

    self.setFrameShape(QFrame.StyledPanel)
    self.setWindowFlags(Qt.ToolTip)

  def setMessage(self, text):
    self.label.setText('%s' % html.escape(text))

  def updatePosition(self, widget):
    self.adjustSize()
    r = self.rect()
    tl = widget.mapToGlobal(QPoint(0, 0))
    tr = tl + QPoint(widget.size().width(), 0)
    x = (tl.x() + tr.x())/2 - r.width()/2
    self.move(x, tl.y() - r.height())

class ErrorCheckEdit(QLineEdit):
  def __init__(self, validator, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.validator = validator
    self.baloon = ErrorBaloon()
    self.state = QValidator.Acceptable
    self.textChanged.connect(lambda t: self.checkValue())

  def checkValue(self):
    try:
      self.state, message = self.validator(self.text())
    except:
      log.warnException()
      self.state = QValidator.Invalid
      message = '%s %s' % sys.exc_info()[:2]

    self.baloon.setMessage(message)
    if self.state != QValidator.Acceptable and self.hasFocus():
      self.showBaloon()
    else:
      self.hideBaloon()

  def showBaloon(self):
    self.baloon.updatePosition(self)
    self.baloon.show()
    self.setStyleSheet('');

  def hideBaloon(self):
    self.baloon.hide()
    if self.state == QValidator.Acceptable:
      self.setStyleSheet('');
    else:
      self.setStyleSheet('background-color:red');

  def focusInEvent(self, ev):
    super().focusInEvent(ev)
    if self.state == QValidator.Acceptable:
      self.hideBaloon()
    else:
      self.showBaloon()

  def focusOutEvent(self, ev):
    super().focusOutEvent(ev)
    self.hideBaloon()

class FlowLayout(QLayout):
  def __init__(self):
    super().__init__()
    self.items = []

  def count(self):
    return len(self.items)

  def addItem(self, item):
    self.items.append(item)

  def setGeometry(self, rect):
    super().setGeometry(rect)
    self.doLayout(rect, False)

  def sizeHint(self):
    if self.count() == 0:
      return QSize(0, 0)

    s = [item.minimumSize() for item in self.items]
    w = sum([i.width() for i in s])
    h = max([i.height() for i in s])
    return QSize(w, h)

  def expandingDirections(self):
    return Qt.Orientation(0)

  def hasHeightForWidth(self):
    return True

  def heightForWidth(self, width):
    return self.doLayout(QRect(0, 0, width, 0), True)

  def itemAt(self, idx):
    try:
      return self.items[idx]
    except IndexError:
      return None

  def takeAt(self, idx):
    try:
      item = self.items[idx]
      item.widget().close()
      del self.items[idx]
      return item
    except IndexError:
      return None

  def doLayout(self, rect, test):
    l, t = rect.x(), rect.y()
    r, b = l+rect.width(), t+rect.height()

    width = 0
    col, rh = 0, 0
    x, y = l, t
    for item in self.items:
      s = item.minimumSize()

      if col > 0 and x+s.width() >= r:
        x = l
        y += rh + 4
        col, rh = 0, 0

      if not test:
        item.setGeometry(QRect(x, y, s.width(), s.height()))
      col += 1
      rh = max([rh, s.height()])
      x += s.width()+4
      width = max(width, x-4)

    return y-t+rh

class DescriptionWidget(QFrame):
  closed = pyqtSignal(QObject)

  def __init__(self):
    super().__init__()

    vbox = VBoxLayout()
    vbox.setContentsMargins(4, 4, 4, 4)
    self.setLayout(vbox)
    self.vbox = vbox

    self.setFrameShape(QFrame.StyledPanel)

  def addTitle(self, title):
    label = QLabel(title)
    label.setContentsMargins(16, 4, 16, 4)

    vbox = VBoxLayout()
    vbox.addWidget(label)

    frame = QFrame()
    frame.setFrameShape(QFrame.StyledPanel)
    frame.setContentsMargins(4, 4, 4, 4)
    frame.setLayout(vbox)
    self.vbox.addWidget(frame)

  def addLabel(self, text, **kwargs):
    label = QLabel(text)
    if kwargs.get('richtext'):
      label.setTextFormat(Qt.RichText)
      label.setTextInteractionFlags(Qt.TextBrowserInteraction)
      label.setOpenExternalLinks(True)
    self.vbox.addWidget(label)

  def addImage(self, image):
    imglabel = QLabel()
    imglabel.setContentsMargins(16, 4, 16, 4)
    imglabel.setPixmap(QPixmap.fromImage(image))
    self.vbox.addWidget(imglabel)

  def addGrid(self):
    grid = QGridLayout()
    grid.setContentsMargins(16, 4, 4, 16)
    grid.setColumnStretch(1, 1)
    grid.setHorizontalSpacing(16)
    self.vbox.addLayout(grid)
    return grid

  def closeEvent(self, event):
    super().closeEvent(event)
    self.closed.emit(self)

class ComboBoxWithDescriptor(QComboBox):
  mouseEvents = (
    QEvent.MouseButtonPress,
    QEvent.MouseButtonRelease,
    QEvent.MouseMove,
    QEvent.MouseButtonDblClick
  )

  def __init__(self):
    super().__init__()

    self.currDescriptor = None
    self.preventHide = False

    self.view().entered.connect(self.showDescriptor)

  def closeDescriptor(self):
    if self.currDescriptor:
      self.currDescriptor.close()
      self.currDescriptor = None

  def descriptorClosed(self, desc):
    QApplication.instance().removeEventFilter(self)
    desc.closed.disconnect(self.descriptorClosed)

  def showDescriptor(self, index):
    self.closeDescriptor()

    widget = index.data(Qt.UserRole+1)
    if not isinstance(widget, QWidget):
      return

    view = self.view()
    pos = view.mapToGlobal(QPoint(view.width(), view.visualRect(index).y()))

    widget.setWindowFlags(Qt.ToolTip)
    widget.move(pos)
    widget.show()

    widget.closed.connect(self.descriptorClosed)
    QApplication.instance().installEventFilter(self)

    self.currDescriptor = widget

  @classmethod
  def isDescendant(self, widget, ancestor):
    while isinstance(widget, QWidget):
      if widget == ancestor:
        return True
      widget = widget.parentWidget()
    return False

  def eventFilter(self, obj, event):
    if event.type() in self.mouseEvents and self.isDescendant(obj, self.view().window()):
      w = QApplication.widgetAt(event.globalPos())
      if self.isDescendant(w, self.currDescriptor):
        localpos = w.mapFromGlobal(event.globalPos())
        newev = QMouseEvent(
          event.type(),
          localpos,
          event.screenPos(),
          event.button(),
          event.buttons(),
          event.modifiers()
        )
        QApplication.sendEvent(w, newev)
        self.preventHide = True

    if event.type() in (QEvent.Close, QEvent.Hide) and obj == self.view().window():
      self.closeDescriptor()

    return False

  def hidePopup(self):
    if self.preventHide:
      self.preventHide = False
      return
    super().hidePopup()
<commit_msg>Add ExpanderWidget<commit_after>import sys
import re
import logging
import html
from PyQt5.QtCore import Qt, pyqtSignal, QObject, QPoint, QRect, QSize, QEvent, QCoreApplication
from PyQt5.QtGui import QKeySequence, QValidator, QPainter, \
  QPen, QBrush, QColor, QPixmap, QMouseEvent
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QMenu, \
  QFrame, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QLineEdit, QLayout, \
  QComboBox, QGridLayout, QPushButton
import numpy as np

import log

__all__ = [
  'TableWidget', 'HSeparator', 'HBoxLayout', 'VBoxLayout',
  'ErrorBaloon', 'ErrorCheckEdit', 'FlowLayout',
  'DescriptionWidget', 'ComboBoxWithDescriptor',
  'ExpanderWidget'
]

class TableWidget(QTableWidget):
  def __init__(self):
    super().__init__()
    self.menu = QMenu()
    self.keys = []

    self.addAction('&Copy selected', self.copySelected, QKeySequence.Copy)
    self.addAction('&Paste',         self.paste,        QKeySequence.Paste)

  def addAction(self, label, func, key):
    self.menu.addAction(label, func, key)
    self.keys.append((key, func))

  def keyPressEvent(self, ev):
    for key, func in self.keys:
      if isinstance(key, QKeySequence.StandardKey):
        m = ev.matches(key)
      else:
        m = (ev.key() | int(ev.modifiers())) in key

      if m:
        ev.accept()
        func()
        return
    super().keyPressEvent(ev)

  def contextMenuEvent(self, ev):
    self.menu.exec_(ev.globalPos())

  def getSelectedItemTable(self):
    col, row, val = [], [], []
    for index in self.selectedIndexes():
      col.append(self.visualColumn(index.column()))
      row.append(self.visualRow(index.row()))
      val.append(self.item(index.row(), index.column()))

    col = np.array(col) - min(col)
    row = np.array(row) - min(row)

    data = dict(zip(zip(row, col), val))
    tbl = [[data[(r, c)] for c in range(max(col)+1)] for r in range(max(row)+1)]
    return tbl

  def copySelected(self):
    tbl = self.getSelectedItemTable()
    QApplication.clipboard().setText('\n'.join([
      '\t'.join([item.text().strip() for item in r]) for r in tbl]))

  def paste(self):
    text = QApplication.clipboard().text()
    data = [[c.strip() for c in l.split('\t')] for l in re.split(r'\r?\n', text)]

    sel = self.getSelectedItemTable()

    if len(sel) == 0:
      return
    elif len(sel) == 1 and len(sel[0]) == 1:
      r0 = self.visualRow(sel[0][0].row())
      c0 = self.visualColumn(sel[0][0].column())
      v2l_r = dict([(self.visualRow(r), r)    for r in range(self.rowCount())])
      v2l_c = dict([(self.visualColumn(c), c) for c in range(self.columnCount())])
      for r, vals in enumerate(data):
        for c, text in enumerate(vals):
          item = self.item(v2l_r[r0+r], v2l_c[c0+c])
          if item and item.flags() & Qt.ItemIsEditable:
            item.setText(text)
            self.cellChanged.emit(item.row(), item.column())
      return

    selerr_msg = 'The shapes of table selection and paste data are different'
    if len(sel) != len(data):
      logging.error(selerr_msg)
      return

    for items, values in zip(sel, data):
      if len(items) != len(values):
        logging.error(selerr_msg)
        return
      for item, val in zip(items, values):
        if val and not item:
          logging.error(selerr_msg)
          return

    for items, values in zip(sel, data):
      for item, val in zip(items, values):
        if item:
          item.setText(val)

class HSeparator(QFrame):
  def __init__(self):
    super().__init__()
    self.setFrameShape(QFrame.HLine)
    self.setFrameShadow(QFrame.Sunken)

class HBoxLayout(QHBoxLayout):
  def __init__(self):
    super().__init__()
    self.setContentsMargins(0, 0, 0, 0)
    self.setSpacing(4)

class VBoxLayout(QVBoxLayout):
  def __init__(self):
    super().__init__()
    self.setContentsMargins(0, 0, 0, 0)
    self.setSpacing(4)

class ErrorBaloon(QFrame):
  def __init__(self):
    super().__init__()

    self.label = QLabel()

    vbox = VBoxLayout()
    vbox.setContentsMargins(4, 4, 4, 4)
    vbox.addWidget(self.label)
    self.setLayout(vbox)

    self.setFrameShape(QFrame.StyledPanel)
    self.setWindowFlags(Qt.ToolTip)

  def setMessage(self, text):
    self.label.setText('%s' % html.escape(text))

  def updatePosition(self, widget):
    self.adjustSize()
    r = self.rect()
    tl = widget.mapToGlobal(QPoint(0, 0))
    tr = tl + QPoint(widget.size().width(), 0)
    x = (tl.x() + tr.x())/2 - r.width()/2
    self.move(x, tl.y() - r.height())

class ErrorCheckEdit(QLineEdit):
  def __init__(self, validator, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.validator = validator
    self.baloon = ErrorBaloon()
    self.state = QValidator.Acceptable
    self.textChanged.connect(lambda t: self.checkValue())

  def checkValue(self):
    try:
      self.state, message = self.validator(self.text())
    except:
      log.warnException()
      self.state = QValidator.Invalid
      message = '%s %s' % sys.exc_info()[:2]

    self.baloon.setMessage(message)
    if self.state != QValidator.Acceptable and self.hasFocus():
      self.showBaloon()
    else:
      self.hideBaloon()

  def showBaloon(self):
    self.baloon.updatePosition(self)
    self.baloon.show()
    self.setStyleSheet('');

  def hideBaloon(self):
    self.baloon.hide()
    if self.state == QValidator.Acceptable:
      self.setStyleSheet('');
    else:
      self.setStyleSheet('background-color:red');

  def focusInEvent(self, ev):
    super().focusInEvent(ev)
    if self.state == QValidator.Acceptable:
      self.hideBaloon()
    else:
      self.showBaloon()

  def focusOutEvent(self, ev):
    super().focusOutEvent(ev)
    self.hideBaloon()

class FlowLayout(QLayout):
  def __init__(self):
    super().__init__()
    self.items = []

  def count(self):
    return len(self.items)

  def addItem(self, item):
    self.items.append(item)

  def setGeometry(self, rect):
    super().setGeometry(rect)
    self.doLayout(rect, False)

  def sizeHint(self):
    if self.count() == 0:
      return QSize(0, 0)

    s = [item.minimumSize() for item in self.items]
    w = sum([i.width() for i in s])
    h = max([i.height() for i in s])
    return QSize(w, h)

  def expandingDirections(self):
    return Qt.Orientation(0)

  def hasHeightForWidth(self):
    return True

  def heightForWidth(self, width):
    return self.doLayout(QRect(0, 0, width, 0), True)

  def itemAt(self, idx):
    try:
      return self.items[idx]
    except IndexError:
      return None

  def takeAt(self, idx):
    try:
      item = self.items[idx]
      item.widget().close()
      del self.items[idx]
      return item
    except IndexError:
      return None

  def doLayout(self, rect, test):
    l, t = rect.x(), rect.y()
    r, b = l+rect.width(), t+rect.height()

    width = 0
    col, rh = 0, 0
    x, y = l, t
    for item in self.items:
      s = item.minimumSize()

      if col > 0 and x+s.width() >= r:
        x = l
        y += rh + 4
        col, rh = 0, 0

      if not test:
        item.setGeometry(QRect(x, y, s.width(), s.height()))
      col += 1
      rh = max([rh, s.height()])
      x += s.width()+4
      width = max(width, x-4)

    return y-t+rh

class DescriptionWidget(QFrame):
  closed = pyqtSignal(QObject)

  def __init__(self):
    super().__init__()

    vbox = VBoxLayout()
    vbox.setContentsMargins(4, 4, 4, 4)
    self.setLayout(vbox)
    self.vbox = vbox

    self.setFrameShape(QFrame.StyledPanel)

  def addTitle(self, title):
    label = QLabel(title)
    label.setContentsMargins(16, 4, 16, 4)

    vbox = VBoxLayout()
    vbox.addWidget(label)

    frame = QFrame()
    frame.setFrameShape(QFrame.StyledPanel)
    frame.setContentsMargins(4, 4, 4, 4)
    frame.setLayout(vbox)
    self.vbox.addWidget(frame)

  def addLabel(self, text, **kwargs):
    label = QLabel(text)
    if kwargs.get('richtext'):
      label.setTextFormat(Qt.RichText)
      label.setTextInteractionFlags(Qt.TextBrowserInteraction)
      label.setOpenExternalLinks(True)
    self.vbox.addWidget(label)

  def addImage(self, image):
    imglabel = QLabel()
    imglabel.setContentsMargins(16, 4, 16, 4)
    imglabel.setPixmap(QPixmap.fromImage(image))
    self.vbox.addWidget(imglabel)

  def addGrid(self):
    grid = QGridLayout()
    grid.setContentsMargins(16, 4, 4, 16)
    grid.setColumnStretch(1, 1)
    grid.setHorizontalSpacing(16)
    self.vbox.addLayout(grid)
    return grid

  def closeEvent(self, event):
    super().closeEvent(event)
    self.closed.emit(self)

class ComboBoxWithDescriptor(QComboBox):
  mouseEvents = (
    QEvent.MouseButtonPress,
    QEvent.MouseButtonRelease,
    QEvent.MouseMove,
    QEvent.MouseButtonDblClick
  )

  def __init__(self):
    super().__init__()

    self.currDescriptor = None
    self.preventHide = False

    self.view().entered.connect(self.showDescriptor)

  def closeDescriptor(self):
    if self.currDescriptor:
      self.currDescriptor.close()
      self.currDescriptor = None

  def descriptorClosed(self, desc):
    QApplication.instance().removeEventFilter(self)
    desc.closed.disconnect(self.descriptorClosed)

  def showDescriptor(self, index):
    self.closeDescriptor()

    widget = index.data(Qt.UserRole+1)
    if not isinstance(widget, QWidget):
      return

    view = self.view()
    pos = view.mapToGlobal(QPoint(view.width(), view.visualRect(index).y()))

    widget.setWindowFlags(Qt.ToolTip)
    widget.move(pos)
    widget.show()

    widget.closed.connect(self.descriptorClosed)
    QApplication.instance().installEventFilter(self)

    self.currDescriptor = widget

  @classmethod
  def isDescendant(self, widget, ancestor):
    while isinstance(widget, QWidget):
      if widget == ancestor:
        return True
      widget = widget.parentWidget()
    return False

  def eventFilter(self, obj, event):
    if event.type() in self.mouseEvents and self.isDescendant(obj, self.view().window()):
      w = QApplication.widgetAt(event.globalPos())
      if self.isDescendant(w, self.currDescriptor):
        localpos = w.mapFromGlobal(event.globalPos())
        newev = QMouseEvent(
          event.type(),
          localpos,
          event.screenPos(),
          event.button(),
          event.buttons(),
          event.modifiers()
        )
        QApplication.sendEvent(w, newev)
        self.preventHide = True

    if event.type() in (QEvent.Close, QEvent.Hide) and obj == self.view().window():
      self.closeDescriptor()

    return False

  def hidePopup(self):
    if self.preventHide:
      self.preventHide = False
      return
    super().hidePopup()

class ExpanderWidget(QWidget):
  def __init__(self, label, widget):
    super().__init__()

    if isinstance(widget, QLayout):
      layout = widget
      widget = QWidget()
      widget.setLayout(layout)

    self.button = QPushButton(label)
    self.button.setCheckable(True)
    self.button.toggled.connect(self.buttonToggled)
    self.widget = widget
    self.buttonToggled()

    vbox = VBoxLayout()
    vbox.addWidget(self.button)
    vbox.addWidget(self.widget)
    self.setLayout(vbox)

  def buttonToggled(self, *args):
    self.widget.setVisible(self.button.isChecked())
<|endoftext|>