Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Basic report for the ventures

  • Loading branch information...
commit 7255994d2dad8805c756d2e06c4f27eb7b51920b 1 parent 6ce06a3
Radomir Dopieralski authored
View
16 src/ralph_pricing/forms.py
@@ -147,3 +147,19 @@ def clean(self):
can_delete=True,
)
+
+class DateRangeForm(forms.Form):
+ start = forms.DateField(
+ widget=DateWidget(
+ attrs={'class': 'input-small'},
+ ),
+ label='Start date',
+ )
+ end = forms.DateField(
+ widget=DateWidget(
+ attrs={'class': 'input-small'},
+ ),
+ label='End date',
+ )
+
+
View
54 src/ralph_pricing/models.py
@@ -5,6 +5,9 @@
from __future__ import print_function
from __future__ import unicode_literals
+import decimal
+from dateutil import rrule
+
from django.db import models as db
from django.utils.translation import ugettext_lazy as _
@@ -75,6 +78,51 @@ class MPTTMeta:
def __unicode__(self):
return self.name
+ def _by_venture(self, query, descendants):
+ if descendants:
+ ventures = self.get_descendants(include_self=True)
+ return query.filter(pricing_venture__in=ventures)
+ return query.filter(pricing_venture=self)
+
+ def get_assets_count_price(self, start, end, descendants=False):
+ days = (end - start).days + 1
+ query = DailyDevice.objects.all()
+ query = self._by_venture(query, descendants)
+ query = query.filter(date__gte=start, date__lte=end)
+ price = query.aggregate(db.Sum('price'))['price__sum'] or 0
+ count = query.count()
+ return count / days, price / days
+
+ def get_usages_count_price(self, start, end, type_, descendants=False):
+ count = 0
+ price = decimal.Decimal('0')
+ query = DailyUsage.objects.filter(type=type_)
+ query = self._by_venture(query, descendants)
+ for day in rrule.rrule(rrule.DAILY, dtstart=start, until=end):
+ query = query.filter(date=day)
+ daily_count = query.aggregate(db.Sum('value'))['value__sum'] or 0
+ count += daily_count
+ try:
+ daily_price = type_.get_price_at(day)
+ except (
+ UsagePrice.DoesNotExist,
+ UsagePrice.MultipleObjectsReturned,
+ ):
+ price = None
+ else:
+ if price is not None:
+ price += daily_count * daily_price
+ return count, price
+
+ def get_extra_costs(self, start, end, type_, descendants=False):
+ price = decimal.Decimal('0')
+ query = ExtraCost.objects.filter(type=type_)
+ query = self._by_venture(query, descendants)
+ for day in rrule.rrule(rrule.DAILY, dtstart=start, until=end):
+ query = query.filter(start__lte=day, end__gte=day)
+ price += query.aggregate(db.Sum('price'))['price__sum'] or 0
+ return price
+
class DailyPart(db.Model):
date = db.DateField()
@@ -157,6 +205,9 @@ class Meta:
def __unicode__(self):
return self.name
+ def get_price_at(self, date):
+ return self.usageprice_set.get(start__lte=date, end__gte=date).price
+
class UsagePrice(db.Model):
type = db.ForeignKey(UsageType, verbose_name=_("type"))
@@ -222,6 +273,9 @@ class Meta:
def __unicode__(self):
return self.name
+ def get_cost_at(self, date):
+ return 1 # XXX
+
class ExtraCost(db.Model):
start = db.DateField()
View
65 src/ralph_pricing/templates/ralph_pricing/base.html
@@ -1,9 +1,64 @@
-{% extends "ui/base.html" %}
+<!DOCTYPE html>
{% load url from future %}
+{% load icons %}
{% load bob %}
-{% block sidebar %}
-{% if sidebar_items %}
-{% sidebar_menu sidebar_items sidebar_selected %}
-{% endif %}
+<html><head>
+<meta charset="utf-8">
+<title>{% block title %}{% block titlesection %}{% block titlesubsection %}{% endblock %}{{ section.title }}{% endblock %} - Ralph Pricing{% endblock %}</title>
+<link rel="stylesheet" href="{{ STATIC_URL }}bootstrap/css/bootstrap.min.css">
+<link rel="stylesheet" href="{{ STATIC_URL }}fugue-icons.css">
+<link rel="stylesheet" href="{{ STATIC_URL }}ui/custom.css">
+<link rel="stylesheet" href="{{ STATIC_URL }}ui/datepicker.css">
+{% block extra_headers %}{% endblock %}
+</head><body>
+<div class="container-fluid browser-min-width">
+ <div class="row-fluid"><div class="span12">
+ {% block header %}
+ <p></p>
+ {% main_menu mainmenu_items section title="Ralph Pricing" search=search_url %}
+ {% endblock %}
+ </div>
+ {% if messages %}
+ <div class="row-fluid"><div class="span12 alerts">
+ {% block alerts %}
+ {% for message in messages %}
+ <div class="alert alert-{{ message.tags }} fade in">
+ <a class="close" data-dismiss="alert" href="#">&times;</a>
+ <p>{{ message.tags|alert_icon }} {{ message }}</p>
+ </div>
+ {% endfor %}
+ {% endblock %}
+ </div></div>
+ {% endif %}
+ {% block contentarea %}
+ <div class="row-fluid main-body"><div class="span2">
+ {% block sidebar %}
+ {% sidebar_menu sidebar_items sidebar_selected %}
+ {% endblock %}
+ </div><div class="span10">
+ {% block breadcrumbs %}{% endblock %}
+ {% block tabs %}{% endblock %}
+ <div class="row-fluid">
+ {% block content %}{% endblock %}
+ </div>
+ </div></div>
+ {% endblock contentarea %}
+ <div class="row-fluid"><div class="span12">
+ {% block footer %}
+ <p></p>
+ {% main_menu footer_items section white=1 %}
+ {% endblock %}
+ </div>
+</div>
+{% block scripts %}
+<script src="{{ STATIC_URL }}jquery-1.7.2.min.js"></script>
+<script src="{{ STATIC_URL }}bootstrap/js/bootstrap.min.js"></script>
+<script src="{{ STATIC_URL }}bootstrap/js/bob.js"></script>
+<script src="{{ STATIC_URL }}bootstrap-datepicker.js"></script>
+<script src="{{ STATIC_URL }}mustache.js"></script>
+<script src="{{ STATIC_URL }}ui/main.js"></script>
{% endblock %}
+</body></html>
+<!--STATS-->
+
View
45 src/ralph_pricing/templates/ralph_pricing/ventures.html
@@ -0,0 +1,45 @@
+{% extends "ralph_pricing/base.html" %}
+{% load url from future %}
+{% load icons %}
+{% load formats %}
+{% load bob %}
+
+{% block contentarea %}
+<div class="row-fluid main-body"><div class="span12">
+<form class="form form-inline daterange-form">
+<div class="form-actions">
+ {% for f in form %}
+ <div style="display:inline-block; vertical-align:top" class="control-group {% if f.errors %}error{% endif %}" >
+ {{ f }}
+ {% if f.errors %}
+ <span class="help-block">
+ {% for e in f.errors %}{{ e }}{% endfor %}
+ </span>
+ {% endif %}
+ </div>
+ {% endfor %}
+ <button type="submit" class="btn">{% spaceless %}
+ {% icon 'fugue-calendar-search-result' %}&nbsp;Update
+ {% endspaceless %}</button>
+</div>
+</form>
+</div>
+
+<table class="table table-striped table-bordered table-condensed">
+ <tr>
+ {% for column in header %}
+ <th>{{ column }}</th>
+ {% endfor %}
+ </tr>
+ {% for row in data %}
+ <tr>
+ {% for value in row %}
+ <td
+ {% if forloop.counter > 3 %}style="text-align:right"{% endif %}
+ >{{ value }}</td>
+ {% endfor %}
+ </tr>
+ {% endfor %}
+</table>
+</div></div>
+{% endblock contentarea %}
View
18 src/ralph_pricing/urls.py
@@ -11,6 +11,7 @@
from ralph_pricing.views.home import Home
from ralph_pricing.views.extra_costs import ExtraCosts
from ralph_pricing.views.usages import Usages
+from ralph_pricing.views.ventures import AllVentures, TopVentures
urlpatterns = patterns(
@@ -19,13 +20,13 @@
url(
r'^extra-costs/$',
login_required(ExtraCosts.as_view()),
- name='extra-costs',
+ name='extra_costs',
kwargs={'venture': None},
),
url(
r'^extra-costs/(?P<venture>\d+)/$',
login_required(ExtraCosts.as_view()),
- name='extra-costs',
+ name='extra_costs',
),
url(
r'^usages/$',
@@ -36,8 +37,17 @@
url(
r'^usages/(?P<usage>[^/]+)/$',
login_required(Usages.as_view()),
- name='extra-costs',
+ name='usages',
+ ),
+ url(
+ r'^all-ventures/$',
+ login_required(AllVentures.as_view()),
+ name='all_ventures',
+ ),
+ url(
+ r'^top-ventures/$',
+ login_required(TopVentures.as_view()),
+ name='top_ventures',
),
-
)
View
27 src/ralph_pricing/views/base.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
@@ -10,32 +9,40 @@
from django.conf import settings
from django.views.generic import TemplateView
from bob.menu import MenuItem
+from django.utils.translation import ugettext_lazy as _
+
MAIN_MENU = [
MenuItem(
- 'Ventures',
- name='ventures',
+ _("All ventures"),
+ name='all-ventures',
+ fugue_icon='fugue-store-medium',
+ view_name='all_ventures',
+ ),
+ MenuItem(
+ _("Top ventures"),
+ name='top-ventures',
fugue_icon='fugue-store',
- href='/pricing/ventures/',
+ view_name='top_ventures',
),
MenuItem(
- 'Devices',
+ _("Devices"),
name='devices',
fugue_icon='fugue-wooden-box',
- href='/pricing/devices/',
+ view_name='devices',
),
MenuItem(
- 'Extra costs',
+ _("Extra costs"),
name='extra-costs',
fugue_icon='fugue-money-coin',
- href='/pricing/extra-costs/',
+ view_name='extra_costs',
),
MenuItem(
- 'Usages',
+ _("Usage types"),
name='usages',
fugue_icon='fugue-beaker',
- href='/pricing/usages/',
+ view_name='usages',
),
]
View
145 src/ralph_pricing/views/ventures.py
@@ -0,0 +1,145 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import datetime
+
+from django.conf import settings
+from django.utils.translation import ugettext_lazy as _
+
+from ralph_pricing.views.base import Base
+from ralph_pricing.models import UsageType, ExtraCostType, Venture
+from ralph_pricing.forms import DateRangeForm
+
+
+def currency(value):
+ return '{:,.2f} {}'.format(value or 0, settings.CURRENCY).replace(',', ' ')
+
+
+class AllVentures(Base):
+ template_name = 'ralph_pricing/ventures.html'
+
+ def __init__(self, *args, **kwargs):
+ super(AllVentures, self).__init__(*args, **kwargs)
+ self.form = None
+
+ def get_data(self, start, end):
+ ventures = Venture.objects.order_by('name')
+ data = []
+ for venture in ventures:
+ count, price = venture.get_assets_count_price(start, end)
+ path = '/'.join(
+ v.name for v in venture.get_ancestors(include_self=True),
+ )
+ row = [
+ venture.venture_id,
+ path,
+ venture.department,
+ count,
+ currency(price),
+ ]
+ for usage_type in UsageType.objects.order_by('name'):
+ count, price = venture.get_usages_count_price(
+ start,
+ end,
+ usage_type,
+ )
+ row.append(count)
+ if price is None:
+ row.append('NO PRICE')
+ else:
+ row.append(currency(price))
+ for extra_cost_type in ExtraCostType.objects.order_by('name'):
+ row.append(currency(venture.get_extra_costs(
+ start,
+ end,
+ extra_cost_type,
+ )))
+ data.append(row)
+ return data
+
+ def get_header(self):
+ header = [
+ _("ID"),
+ _("Venture"),
+ _("Department"),
+ _("Assets count"),
+ _("Assets price"),
+ ]
+ for usage_type in UsageType.objects.order_by('name'):
+ header.append(_("{} count").format(usage_type.name))
+ header.append(_("{} price").format(usage_type.name))
+ for extra_cost_type in ExtraCostType.objects.order_by('name'):
+ header.append(extra_cost_type.name)
+ return header
+
+ def get_context_data(self, **kwargs):
+ context = super(AllVentures, self).get_context_data(**kwargs)
+ if self.request.GET.get('start'):
+ form = DateRangeForm(self.request.GET)
+ else:
+ today = datetime.date.today()
+ form = DateRangeForm(
+ initial={
+ 'start': today - datetime.timedelta(days=31),
+ 'end': today,
+ },
+ )
+ if form.is_valid():
+ start = form.cleaned_data['start']
+ end = form.cleaned_data['end']
+ data=self.get_data(start, end)
+ context.update(data=data)
+ context.update({
+ 'section': 'all-ventures',
+ 'form': form,
+ 'header': self.get_header(),
+ })
+ return context
+
+
+class TopVentures(AllVentures):
+ def get_data(self, start, end):
+ ventures = Venture.objects.root_nodes().order_by('name')
+ data = []
+ for venture in ventures:
+ count, price = venture.get_assets_count_price(
+ start,
+ end,
+ descendants=True,
+ )
+ row = [
+ venture.venture_id,
+ venture.name,
+ venture.department,
+ count,
+ currency(price),
+ ]
+ for usage_type in UsageType.objects.order_by('name'):
+ count, price = venture.get_usages_count_price(
+ start,
+ end,
+ usage_type,
+ descendants=True,
+ )
+ row.append(count)
+ row.append(currency(price))
+ for extra_cost_type in ExtraCostType.objects.order_by('name'):
+ row.append(currency(venture.get_extra_costs(
+ start,
+ end,
+ extra_cost_type,
+ descendants=True,
+ )))
+ data.append(row)
+ return data
+
+ def get_context_data(self, **kwargs):
+ context = super(TopVentures, self).get_context_data(**kwargs)
+ context.update({
+ 'section': 'top-ventures',
+ })
+ return context
Please sign in to comment.
Something went wrong with that request. Please try again.