usage cost page using tab styling (#1)

* usage cost page using tab styling

* refactor admin usage cost page
This commit is contained in:
Muhammad Irfan 2023-05-18 19:48:00 +07:00 committed by GitHub
parent c5e8b01793
commit 15a794fe97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 479 additions and 4 deletions

View file

@ -0,0 +1,114 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Usage Cost" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/missing_prices.html" %}
{% if invoice %}
<div id="usage_cost">
<div class="row" style="margin-bottom: 15px;">
<div class="col-md-6">
Invoice Month <b>{{ invoice.start_date|date:"M Y" }}</b>
</div>
<div class="col-md-6">
<span class="pull-right" data-html2canvas-ignore="true">
{% include 'admin/projects_invoice/manage_invoice_modal.html' with invoice=invoice project_balance_amount=project_balance_amount %}
<button onclick="javascript:downloadPdf();" class="btn btn-default">Download PDF</button>
</span>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Invoice State</h3>
</div>
<div class="panel-body">
<h2> {{ invoice.state_text }} </h2>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Subtotal</h3>
</div>
<div class="panel-body">
<h2>{{ invoice.subtotal_money }}</h2>
</div>
</div>
</div>
</div>
{% if invoice.state != 1 %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Tax</h3>
</div>
<div class="panel-body">
<h2> {{ invoice.tax_money }} </h2>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Total</h3>
</div>
<div class="panel-body">
<h2>{{ invoice.total_money }}</h2>
</div>
</div>
</div>
</div>
{% endif %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
</div>
{% else %}
<h1>Billing not enabled or you don't have any usage yet</h1> <br/>
{% endif %}
{% endblock %}
{% block js %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
function downloadPdf() {
let opt = {
filename: 'usage_cost.pdf',
margin: [16, 16],
enableLinks: true,
image: {type: 'jpeg', quality: 0.98},
pagebreak: { mode: 'avoid-all', },
jsPDF: {unit: 'mm', format: 'a4', orientation: 'portrait'},
}
// clone element to modify
const ucElement = document.getElementById('usage_cost');
const ucElementClone = ucElement.cloneNode(true);
// modify element
const navTabs = ucElementClone.querySelector('.nav-tabs');
navTabs.style.display = 'none';
const tabContent = ucElementClone.querySelectorAll('.tab-pane');
tabContent.forEach(content => {
content.classList.remove("tab-pane");
});
html2pdf().set(opt).from(ucElementClone).save();
}
</script>
{% endblock %}

View file

@ -14,10 +14,11 @@ from django.conf.urls import url
from openstack_dashboard.dashboards.yuyu.admin.projects_invoice import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^invoice/detail/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.InvoiceView.as_view(), name='invoice_detail'),
url(r'^invoice/usage/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.UsageCostView.as_view(), name='usage_cost'),
url(r'^invoice/usage/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.UsageCostTabView.as_view(), name='usage_cost'),
url(r'^invoice/finish/(?P<id>[^/]+)/$', views.FinishInvoice.as_view(), name='finish_invoice'),
url(r'^invoice/rollback_to_unpaid/(?P<id>[^/]+)/$', views.RollbackToUnpaidInvoice.as_view(),
name='rollback_to_unpaid'),

View file

@ -24,6 +24,8 @@ from djmoney.money import Money
from horizon import exceptions
from horizon import tables
from horizon import views
from horizon import tabs
from openstack_dashboard import api
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
from .tables import InvoiceTable
@ -31,6 +33,7 @@ from ...cases.balance_use_case import BalanceUseCase
from ...cases.setting_use_case import SettingUseCase
from ...core.usage_cost.tables import InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, \
SnapshotCostTable, ImageCostTable
from ...core.usage_cost.tabs import UsageCostTabs
from ...core.utils.invoice_utils import state_to_text
@ -106,6 +109,31 @@ class InvoiceView(views.APIView):
return sum(instance_prices)
class UsageCostTabView(tabs.TabbedTableView):
tab_group_class = UsageCostTabs
page_title = _("Usage Cost")
template_name = "admin/projects_invoice/cost_tabs.html"
invoice_uc = InvoiceUseCase()
balance_uc = BalanceUseCase()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def get(self, request, *args, **kwargs):
request.invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'],
tenant_id=self.kwargs['project_id'])
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
balance = self.balance_uc.retrieve_by_project(self.request, self.kwargs['project_id'])
context['invoice'] = self.request.invoice
context['project_balance_amount'] = Money(amount=balance['amount'],
currency=balance['amount_currency']) if balance else 0
return context
class UsageCostView(tables.MultiTableView):
table_classes = (
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable)

View file

@ -0,0 +1,181 @@
import dateutil.parser
from django.utils.timesince import timesince
from django.utils.translation import ugettext_lazy as _
from djmoney.money import Money
from horizon import exceptions, tabs
from openstack_dashboard import api
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tables import (
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable
)
class InstanceTab(tabs.TableTab):
table_classes = (InstanceCostTable,)
name = _("Instance")
slug = "instance"
template_name = 'horizon/common/_detail_table.html'
def _get_flavor_name(self, flavor_id):
try:
return api.nova.flavor_get(self.request, flavor_id).name
except Exception:
return 'Invalid Flavor'
def get_instance_cost_data(self):
try:
datas = map(lambda x: {
"id": x['id'],
"name": x['name'],
"flavor": self._get_flavor_name(x['flavor_id']),
"usage": timesince(
dateutil.parser.isoparse(x['start_date']),
dateutil.parser.isoparse(x['adjusted_end_date'])
),
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
}, self.request.invoice.get('instances', []))
return datas
except Exception:
error_message = _('Unable to get instance cost')
exceptions.handle(self.request, error_message)
return []
class VolumeTab(tabs.TableTab):
table_classes = (VolumeCostTable,)
name = _("Volume")
slug = "volume"
template_name = 'horizon/common/_detail_table.html'
def _get_volume_name(self, volume_type_id):
try:
return api.cinder.volume_type_get(self.request, volume_type_id).name
except Exception:
return 'Invalid Volume'
def get_volume_cost_data(self):
try:
datas = map(lambda x: {
"id": x['id'],
"name": x['volume_name'],
"usage": timesince(
dateutil.parser.isoparse(x['start_date']),
dateutil.parser.isoparse(x['adjusted_end_date'])
),
'type': self._get_volume_name(x['volume_type_id']),
'size': x['space_allocation_gb'],
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
}, self.request.invoice.get('volumes', []))
return datas
except Exception:
error_message = _('Unable to get volume cost')
exceptions.handle(self.request, error_message)
return []
class FloatingIpTab(tabs.TableTab):
table_classes = (FloatingIpCostTable,)
name = _("Floating IP")
slug = "floating_ip"
template_name = 'horizon/common/_detail_table.html'
def get_floating_ip_cost_data(self):
try:
datas = map(lambda x: {
"id": x['id'],
"name": x['ip'],
"usage": timesince(
dateutil.parser.isoparse(x['start_date']),
dateutil.parser.isoparse(x['adjusted_end_date'])
),
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
}, self.request.invoice.get('floating_ips', []))
return datas
except Exception:
error_message = _('Unable to get floating ip cost')
exceptions.handle(self.request, error_message)
return []
class RouterTab(tabs.TableTab):
table_classes = (RouterCostTable,)
name = _("Router")
slug = "router"
template_name = 'horizon/common/_detail_table.html'
def get_router_cost_data(self):
try:
datas = map(lambda x: {
"id": x['id'],
"name": x['name'],
"usage": timesince(
dateutil.parser.isoparse(x['start_date']),
dateutil.parser.isoparse(x['adjusted_end_date'])
),
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
}, self.request.invoice.get('routers', []))
return datas
except Exception:
error_message = _('Unable to get router cost')
exceptions.handle(self.request, error_message)
return []
class SnapshotTab(tabs.TableTab):
table_classes = (SnapshotCostTable,)
name = _("Snapshot")
slug = "snapshot"
template_name = 'horizon/common/_detail_table.html'
def get_snapshot_cost_data(self):
try:
datas = map(lambda x: {
"id": x['id'],
"name": x['name'],
'size': x['space_allocation_gb'],
"usage": timesince(
dateutil.parser.isoparse(x['start_date']),
dateutil.parser.isoparse(x['adjusted_end_date'])
),
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
}, self.request.invoice.get('snapshots', []))
return datas
except Exception:
error_message = _('Unable to get snapshot cost')
exceptions.handle(self.request, error_message)
return []
class ImageTab(tabs.TableTab):
table_classes = (ImageCostTable,)
name = _("Image")
slug = "image"
template_name = 'horizon/common/_detail_table.html'
def get_image_cost_data(self):
try:
datas = map(lambda x: {
"id": x['id'],
"name": x['name'],
'size': x['space_allocation_gb'],
"usage": timesince(
dateutil.parser.isoparse(x['start_date']),
dateutil.parser.isoparse(x['adjusted_end_date'])
),
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
}, self.request.invoice.get('images', []))
return datas
except Exception:
error_message = _('Unable to get images cost')
exceptions.handle(self.request, error_message)
return []
class UsageCostTabs(tabs.TabGroup):
slug = "usage_cost"
tabs = (InstanceTab, VolumeTab, FloatingIpTab, RouterTab, SnapshotTab, ImageTab, )
sticky = True

View file

@ -0,0 +1,122 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Usage Cost" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/missing_prices.html" %}
{% if invoice %}
<div id="usage_cost">
<div class="row" style="margin-bottom: 15px;">
<div class="col-md-6">
Invoice Month
<select id="invoice_select" onchange="onInvoiceChange(this.value)">
{% for i in invoice_list %}
<option value="{{ i.id }}" {% if i.id == invoice.id %}
selected {% endif %}>{{ i.start_date|date:"M Y" }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<button onclick="javascript:downloadPdf();" class="btn btn-default pull-right" data-html2canvas-ignore="true">Download PDF</button>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Invoice State</h3>
</div>
<div class="panel-body">
<h2> {{ invoice.state_text }} </h2>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Subtotal</h3>
</div>
<div class="panel-body">
<h2>{{ invoice.subtotal_money }}</h2>
</div>
</div>
</div>
</div>
{% if invoice.state != 1 %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Tax</h3>
</div>
<div class="panel-body">
<h2> {{ invoice.tax_money }} </h2>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Total</h3>
</div>
<div class="panel-body">
<h2>{{ invoice.total_money }}</h2>
</div>
</div>
</div>
</div>
{% endif %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
</div>
{% else %}
<h1>You don't have any usage yet</h1> <br/>
{% endif %}
{% endblock %}
{% block js %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
function onInvoiceChange(val) {
var search = "?invoice_id=" + val;
window.location.href = window.location.protocol + "//" + window.location.host + window.location.pathname + search;
}
function downloadPdf() {
let opt = {
filename: 'usage_cost.pdf',
margin: [16, 16],
enableLinks: true,
image: {type: 'jpeg', quality: 0.98},
pagebreak: { mode: 'avoid-all', },
jsPDF: {unit: 'mm', format: 'a4', orientation: 'portrait'},
}
// clone element to modify
const ucElement = document.getElementById('usage_cost');
const ucElementClone = ucElement.cloneNode(true);
// modify element
const navTabs = ucElementClone.querySelector('.nav-tabs');
navTabs.style.display = 'none';
const tabContent = ucElementClone.querySelectorAll('.tab-pane');
tabContent.forEach(content => {
content.classList.remove("tab-pane");
});
html2pdf().set(opt).from(ucElementClone).save();
}
</script>
{% endblock %}

View file

@ -16,5 +16,5 @@ from openstack_dashboard.dashboards.yuyu.project.usage_cost import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^$', views.IndexViewTab.as_view(), name='index'),
]

View file

@ -14,14 +14,43 @@ from django.utils.timesince import timesince
from django.utils.translation import ugettext_lazy as _
from djmoney.money import Money
from horizon import exceptions
from horizon import tables
from horizon import exceptions, tables, tabs
from openstack_dashboard import api
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tables import InstanceCostTable, VolumeCostTable, \
FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tabs import UsageCostTabs
class IndexViewTab(tabs.TabbedTableView):
tab_group_class = UsageCostTabs
page_title = _("Usage Cost")
template_name = "project/usage_cost/index_tabs.html"
invoice_uc = InvoiceUseCase()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def get(self, request, *args, **kwargs):
request.invoice_list = self.invoice_uc.get_simple_list(self.request)
request.has_invoice = len(request.invoice_list) != 0
invoice_id = self.request.GET.get('invoice_id', None)
if not invoice_id and request.has_invoice:
invoice_id = request.invoice_list[0]['id']
request.invoice = self.invoice_uc.get_invoice(self.request, invoice_id) if request.has_invoice else {}
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['invoice_list'] = self.request.invoice_list
context['invoice'] = self.request.invoice
return context
class IndexView(tables.MultiTableView):
table_classes = (
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable)