Yuyu demo dashboard

This commit is contained in:
Setyo Nugroho 2023-10-02 23:10:35 +07:00
parent be62ed1b94
commit fb2455247a
76 changed files with 2286 additions and 0 deletions

19
remove_yuyu_demo.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/bash
echo "Specify Horizon Location (ex: /etc/horizon): "
read horizon_path
root=`pwd -P`
echo "Removing yuyu demo Symlink"
rm $horizon_path/openstack_dashboard/dashboards/yuyu_demo
rm $horizon_path/openstack_dashboard/local/enabled/_6600_yuyu.py
rm $horizon_path/openstack_dashboard/local/enabled/_6601_admin_billing_panel_group.py
rm $horizon_path/openstack_dashboard/local/enabled/_6602_admin_billing_overview.py
rm $horizon_path/openstack_dashboard/local/enabled/_6603_admin_billing_price_configuration.py
rm $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
rm $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
rm $horizon_path/openstack_dashboard/local/enabled/_6605_admin_billing_projects_invoice.py
rm $horizon_path/openstack_dashboard/local/enabled/_6606_admin_notification_center.py
rm $horizon_path/openstack_dashboard/local/enabled/_6607_admin_billing_projects_balance.py
echo "Yuyu Demo Removal Done"

20
setup_yuyu_demo.sh Executable file
View file

@ -0,0 +1,20 @@
#!/bin/bash
echo "Specify Horizon Location (ex: /etc/horizon): "
read horizon_path
root=`pwd -P`
echo "Creating Symlink"
ln -sf $root/yuyu_demo $horizon_path/openstack_dashboard/dashboards
ln -sf $root/yuyu_demo/local/enabled/_6600_yuyu.py $horizon_path/openstack_dashboard/local/enabled/_6600_yuyu.py
ln -sf $root/yuyu_demo/local/enabled/_6601_admin_billing_panel_group.py $horizon_path/openstack_dashboard/local/enabled/_6601_admin_billing_panel_group.py
ln -sf $root/yuyu_demo/local/enabled/_6602_admin_billing_overview.py $horizon_path/openstack_dashboard/local/enabled/_6602_admin_billing_overview.py
ln -sf $root/yuyu_demo/local/enabled/_6603_admin_billing_price_configuration.py $horizon_path/openstack_dashboard/local/enabled/_6603_admin_billing_price_configuration.py
ln -sf $root/yuyu_demo/local/enabled/_6604_admin_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
ln -sf $root/yuyu_demo/local/enabled/_6604_admin_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
ln -sf $root/yuyu_demo/local/enabled/_6605_admin_billing_projects_invoice.py $horizon_path/openstack_dashboard/local/enabled/_6605_admin_billing_projects_invoice.py
ln -sf $root/yuyu_demo/local/enabled/_6606_admin_notification_center.py $horizon_path/openstack_dashboard/local/enabled/_6605_admin_notification_center.py
ln -sf $root/yuyu_demo/local/enabled/_6607_admin_billing_projects_balance.py $horizon_path/openstack_dashboard/local/enabled/_6607_admin_billing_projects_balance.py
echo "Symlink Creation Done"
echo "Now you can configure and use yuyu dashboard"

0
yuyu_demo/__init__.py Normal file
View file

View file

@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
class BillingOverviewDemo(horizon.Panel):
name = _("Billing Overview")
slug = "billing_overview_demo"

View file

@ -0,0 +1,19 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
]

View file

@ -0,0 +1,17 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
from ....yuyu.admin.billing_overview import views
class IndexView(views.IndexView):
pass

View file

@ -0,0 +1,6 @@
from ....yuyu.admin.billing_setting import forms
class SettingForm(forms.SettingForm):
def handle(self, request, data):
return

View file

@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
class BillingSettingDemo(horizon.Panel):
name = _("Billing Setting")
slug = "billing_setting_demo"

View file

@ -0,0 +1,24 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-footer %}
{% if cancel_url %}
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
class="btn btn-default cancel">
{{ cancel_label }}
</a>
{% endif %}
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
{% endblock %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans '<b>Company Name</b> : Your Company Name that will be included in invoice' %}</p>
<p>{% trans '<b>Company Logo</b> : A logo that will be used in invoice' %}</p>
<p>{% trans '<b>Email Admin</b> : Used to send a notification related invoice and error. You can put multiple email here separated by `,` (comma). Example: admin1@company.com,admin2@company.com' %}</p>
<p>{% trans '<b>Invoice Tax</b> : Tax that will be calculated for each invoice' %}</p>
<p>{% trans '<b>Invoice Auto Deduct Balance</b> : Automatically deduct project balance and finish invoice every end of the month if project balance is sufficient.' %}</p>
<p>{% trans '<b>How To Top Up</b> : Instruction how to top up project balance that can be seen by project user' %}</p>
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Update Billing Setting" %}{% endblock %}
{% block main %}
{% include "admin/billing_setting/_form_setting.html" %}
{% endblock %}

View file

@ -0,0 +1,39 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Billing Setting" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/missing_prices.html" %}
{% include "admin/billing_setting/missing_billing_setting.html" %}
{# Form #}
<div class="row">
<div class="col-sm-12">
{{ table.render }}
</div>
</div>
<div class="row">
<div class="col-sm-12">
{% if setting.billing_enabled %}
<div class="panel panel-success">
<div class="panel-heading">Status: Billing Enabled</div>
<div class="panel-body">
<button class="btn btn-danger" data-toggle="modal" data-target="#modal_disable_billing">Disable Billing</button>
</div>
</div>
{% else %}
<div class="panel panel-danger">
<div class="panel-heading">Status: Billing Disabled</div>
<div class="panel-body">
<p>Please make sure all price is already configured before enable billing</p>
<a href="{% url 'horizon:admin:billing_setting:enable_billing' %}" class="btn btn-primary {% if missing_price.has_missing or missing_setting.has_missing %} disabled {% endif %}">Enable</a>
<a href="{% url 'horizon:admin:billing_setting:reset_billing' %}" class="btn btn-danger">Reset Billing Data</a>
</div>
</div>
{% endif %}
</div>
</div>
{% include "admin/billing_setting/modal_disable_billing.html" with modal_backdrop='static' hide=True %}
{% endblock %}

View file

@ -0,0 +1,57 @@
{% if missing_setting.company_name %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Company Name not complete
</div>
</div>
</div>
{% endif %}
{% if missing_setting.company_address %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Company Address not complete
</div>
</div>
</div>
{% endif %}
{% if missing_setting.company_logo %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Company Logo not complete
</div>
</div>
</div>
{% endif %}
{% if missing_setting.invoice_tax %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Tax not complete
</div>
</div>
</div>
{% endif %}
{% if missing_setting.email_admin %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Email Admin not complete
</div>
</div>
</div>
{% endif %}

View file

@ -0,0 +1,15 @@
{% extends "horizon/common/_modal.html" %}
{% load i18n %}
{% block modal_id %}modal_disable_billing{% endblock %}
{% block modal_backdrop %}static{% endblock %}
{% block modal-header %}{% trans "Confirmation" %}{% endblock %}
{% block modal-body %}
<p>{% trans "Are you sure to disable billing?" %}</p>
{% endblock %}
{% block modal-footer %}
<a href='#' class='btn btn-danger cancel' data-dismiss='modal'>{% trans "No" %}</a>
<a href="{% url 'horizon:admin:billing_setting:disable_billing' %}" class="btn btn-primary">{% trans "Yes" %}</a>
{% endblock %}

View file

@ -0,0 +1,25 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^enable_billing$', views.EnableBillingView.as_view(), name='enable_billing'),
url(r'^disable_billing$', views.DisableBillingView.as_view(), name='disable_billing'),
url(r'^reset_billing$', views.ResetBillingView.as_view(), name='reset_billing'),
url(r'^update_setting/$',
views.UpdateSettingView.as_view(), name='update_setting'),
]

View file

@ -0,0 +1,42 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django import shortcuts
from ....yuyu.admin.billing_setting import views
from . import forms
class IndexView(views.IndexView):
pass
class UpdateSettingView(views.UpdateSettingView):
form_class = forms.SettingForm
submit_url = ''
success_url = ''
template_name = 'yuyu_demo/billing_setting_demo/form_setting.html'
class EnableBillingView(views.EnableBillingView):
def get(self, request, *args, **kwargs):
return shortcuts.redirect("horizon:yuyu_demo:billing_setting_demo:index")
class DisableBillingView(views.DisableBillingView):
def get(self, request, *args, **kwargs):
return shortcuts.redirect("horizon:yuyu_demo:billing_setting_demo:index")
class ResetBillingView(views.ResetBillingView):
def get(self, request, *args, **kwargs):
return shortcuts.redirect("horizon:yuyu_demo:billing_setting_demo:index")

View file

@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
class NotificationCenterDemo(horizon.Panel):
name = _("Notification Center")
slug = "notification_center_demo"

View file

@ -0,0 +1,27 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<notification_id>[^/]+)/$',
views.DetailView.as_view(),
name='detail'),
url(r'^resend/(?P<notification_id>[^/]+)/$',
views.ResendView.as_view(),
name='resend'),
url(r'^read_all/(?P<selection>[^/]+)$', views.ReadAllView.as_view(), name='read_all'),
]

View file

@ -0,0 +1,32 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django import shortcuts
from .tables import NotificationTable
from ....yuyu.admin.notification_center import views
class IndexView(views.IndexView):
pass
class DetailView(views.DetailView):
pass
class ReadAllView(views.ReadAllView):
def get(self, request, *args, **kwargs):
return shortcuts.redirect("horizon:yuyu_demo:notification_center_demo:index")
class ResendView(views.ResendView):
def get(self, request, *args, **kwargs):
return shortcuts.redirect("horizon:yuyu_demo:notification_center_demo:index")

View file

@ -0,0 +1,34 @@
from django.utils.translation import ugettext_lazy as _
from horizon import forms
from ....yuyu.admin.price_configuration import forms
class FlavorPriceForm(forms.FlavorPriceForm):
def handle(self, request, data):
return
class VolumePriceForm(forms.VolumePriceForm):
def handle(self, request, data):
return
class FloatingIpPriceForm(forms.FloatingIpPriceForm):
def handle(self, request, data):
return
class RouterPriceForm(forms.RouterPriceForm):
def handle(self, request, data):
return
class SnapshotPriceForm(forms.SnapshotPriceForm):
def handle(self, request, data):
return
class ImagePriceForm(forms.SnapshotPriceForm):
def handle(self, request, data):
return

View file

@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
class PriceConfigurationDemo(horizon.Panel):
name = _("Price Configuration")
slug = "price_configuration_demo"

View file

@ -0,0 +1,267 @@
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from ....yuyu.cases.flavor_price_use_case import FlavorPriceUseCase
from ....yuyu.cases.floating_ip_price_use_case import FloatingIpPriceUseCase
from ....yuyu.cases.volume_price_use_case import VolumePriceUseCase
from ....yuyu.cases.router_price_use_case import RouterPriceUseCase
from ....yuyu.cases.snapshot_price_use_case import SnapshotPriceUseCase
from ....yuyu.cases.image_price_use_case import ImagePriceUseCase
from ....yuyu.core.pricing_admin.tables import BasePriceTable, BaseCreatePrice, \
BaseEditPrice
class BaseDeletePrice(tables.DeleteAction):
use_case = None
single_action_label = None
plural_action_label = None
def action_present(self, count):
return _(
"Delete " + self.single_action_label,
"Delete " + self.plural_action_label,
count
)
def action_past(self, count):
return _(
"Deleted " + self.single_action_label,
"Deleted " + self.plural_action_label,
count
)
def delete(self, request, obj_id):
return
def create_create_action(type_name, **kwargs):
return type(type_name, (BaseCreatePrice,), kwargs)
def create_filter_action(type_name, **kwargs):
return type(type_name, (tables.FilterAction,), kwargs)
def create_edit_action(type_name, **kwargs):
return type(type_name, (BaseEditPrice,), kwargs)
def create_delete_action(type_name, **kwargs):
return type(type_name, (BaseDeletePrice,), kwargs)
class FlavorPriceTable(BasePriceTable):
name = tables.WrappingColumn('name', verbose_name=_('Flavor Name'))
def get_object_display(self, datum):
return datum['name']
class Meta(object):
name = "flavor_price"
verbose_name = _("Flavor Price")
table_actions = (
create_filter_action(
type_name="FlavorPriceFilter",
name="flavor_price_filter"
),
create_create_action(
type_name="FlavorPriceCreate",
name="flavor_price_create",
verbose_name=_("Create Flavor Price"),
url="horizon:yuyu_demo:price_configuration_demo:flavor_price_create"
)
)
row_actions = (
create_edit_action(
type_name="FlavorPriceUpdate",
name="flavor_price_edit",
verbose_name=_("Edit Flavor Price"),
url="horizon:yuyu_demo:price_configuration_demo:flavor_price_update"
),
create_delete_action(
type_name="FlavorPriceDelete",
use_case=FlavorPriceUseCase(),
single_action_label="Flavor Price",
plural_action_label="Flavor Prices"
)
)
class VolumePriceTable(BasePriceTable):
name = tables.WrappingColumn('name', verbose_name=_('Volume Type Name'))
def get_object_display(self, datum):
return datum['name']
class Meta(object):
name = "volume_price"
verbose_name = _("Volume Price")
table_actions = (
create_filter_action(
type_name="VolumePriceFilter",
name="volume_price_filter"
),
create_create_action(
type_name="VolumePriceCreate",
name="create_volume_price",
verbose_name=_("Create Volume Price"),
url="horizon:yuyu_demo:price_configuration_demo:volume_price_create"
)
)
row_actions = (
create_edit_action(
type_name="VolumePriceEdit",
name="update_volume_price",
verbose_name=_("Edit Volume Price"),
url="horizon:yuyu_demo:price_configuration_demo:volume_price_update"
),
create_delete_action(
type_name="VolumePriceDelete",
use_case=VolumePriceUseCase(),
single_action_label="Volume Price",
plural_action_label="Volume Prices"
)
)
class FloatingIpPriceTable(BasePriceTable):
def get_object_display(self, datum):
return datum['id']
class Meta(object):
name = "floating_ip_price"
verbose_name = _("Floating IP Price")
table_actions = (
create_filter_action(
type_name="FloatingIpPriceFilter",
name="floating_ip_price_filter"
),
create_create_action(
type_name="FloatingIpPriceCreate",
name="create_floating_ip_price",
verbose_name=_("Create Floating IP Price"),
url="horizon:yuyu_demo:price_configuration_demo:floating_ip_price_create",
single_data=True,
)
)
row_actions = (
create_edit_action(
type_name="FloatingIpPriceEdit",
name="update_floating_ip_price",
verbose_name=_("Edit Floating IP Price"),
url="horizon:yuyu_demo:price_configuration_demo:floating_ip_price_update"
),
create_delete_action(
type_name="FloatingIpPriceDelete",
use_case=FloatingIpPriceUseCase(),
single_action_label="Floating IP Price",
plural_action_label="Floating IP Prices"
)
)
class RouterPriceTable(BasePriceTable):
def get_object_display(self, datum):
return datum['id']
class Meta(object):
name = "router_price"
verbose_name = _("Router Price")
table_actions = (
create_filter_action(
type_name="RouterPriceFilter",
name="router_price_filter"
),
create_create_action(
type_name="RouterPriceCreate",
name="create_router_price",
verbose_name=_("Create Router Price"),
url="horizon:yuyu_demo:price_configuration_demo:router_price_create",
single_data=True,
)
)
row_actions = (
create_edit_action(
type_name="RouterPriceEdit",
name="update_router_price",
verbose_name=_("Edit Router Price"),
url="horizon:yuyu_demo:price_configuration_demo:router_price_update"
),
create_delete_action(
type_name="RouterPriceDelete",
use_case=RouterPriceUseCase(),
single_action_label="Router Price",
plural_action_label="Router Prices"
)
)
class SnapshotPriceTable(BasePriceTable):
def get_object_display(self, datum):
return datum['id']
class Meta(object):
name = "snapshot_price"
verbose_name = _("Snapshot Price")
table_actions = (
create_filter_action(
type_name="SnapshotFilter",
name="snapshot_price_filter"
),
create_create_action(
type_name="SnapshotCreate",
name="create_snapshot_price",
verbose_name=_("Create Snapshot Price"),
url="horizon:yuyu_demo:price_configuration_demo:snapshot_price_create",
single_data=True,
)
)
row_actions = (
create_edit_action(
type_name="SnapshotEdit",
name="update_snapshot_price",
verbose_name=_("Edit Snapshot Price"),
url="horizon:yuyu_demo:price_configuration_demo:snapshot_price_update"
),
create_delete_action(
type_name="SnapshotDelete",
use_case=SnapshotPriceUseCase(),
single_action_label="Snapshot Price",
plural_action_label="Snapshot Prices"
)
)
class ImagePriceTable(BasePriceTable):
def get_object_display(self, datum):
return datum['id']
class Meta(object):
name = "image_price"
verbose_name = _("Image Price")
table_actions = (
create_filter_action(
type_name="ImageFilter",
name="image_price_filter"
),
create_create_action(
type_name="ImageCreate",
name="create_image_price",
verbose_name=_("Create Image Price"),
url="horizon:yuyu_demo:price_configuration_demo:image_price_create",
single_data=True,
)
)
row_actions = (
create_edit_action(
type_name="ImageEdit",
name="update_image_price",
verbose_name=_("Edit Image Price"),
url="horizon:yuyu_demo:price_configuration_demo:image_price_update"
),
create_delete_action(
type_name="ImageDelete",
use_case=ImagePriceUseCase(),
single_action_label="Image Price",
plural_action_label="Image Prices"
)
)

View file

@ -0,0 +1,130 @@
from horizon import exceptions, tables, tabs
from .tables import FlavorPriceTable, VolumePriceTable, \
FloatingIpPriceTable, RouterPriceTable, SnapshotPriceTable, ImagePriceTable
from django.utils.translation import ugettext_lazy as _
from openstack_dashboard.dashboards.yuyu.cases.flavor_price_use_case import FlavorPriceUseCase
from openstack_dashboard.dashboards.yuyu.cases.floating_ip_price_use_case import FloatingIpPriceUseCase
from openstack_dashboard.dashboards.yuyu.cases.volume_price_use_case import VolumePriceUseCase
from openstack_dashboard.dashboards.yuyu.cases.router_price_use_case import RouterPriceUseCase
from openstack_dashboard.dashboards.yuyu.cases.snapshot_price_use_case import SnapshotPriceUseCase
from openstack_dashboard.dashboards.yuyu.cases.image_price_use_case import ImagePriceUseCase
class FlavorTab(tabs.TableTab):
table_classes = (FlavorPriceTable,)
name = _("Flavor")
slug = "flavor"
template_name = 'horizon/common/_detail_table.html'
flavor_price_uc = FlavorPriceUseCase()
def get_flavor_price_data(self):
try:
data = self.flavor_price_uc.list(self.request)
return data
except Exception:
error_message = _('Unable to get flavor prices')
exceptions.handle(self.request, error_message)
return []
class VolumeTab(tabs.TableTab):
table_classes = (VolumePriceTable,)
name = _("Volume")
slug = "volume"
template_name = 'horizon/common/_detail_table.html'
volume_price_uc = VolumePriceUseCase()
def get_volume_price_data(self):
try:
data = self.volume_price_uc.list(self.request)
return data
except Exception:
error_message = _('Unable to get volume prices')
exceptions.handle(self.request, error_message)
return []
class FloatingIpTab(tabs.TableTab):
table_classes = (FloatingIpPriceTable,)
name = _("Floating IP")
slug = "floating_ip"
template_name = 'horizon/common/_detail_table.html'
fip_price_uc = FloatingIpPriceUseCase()
def get_floating_ip_price_data(self):
try:
data = self.fip_price_uc.list(self.request)
return data
except Exception:
error_message = _('Unable to get floating IP prices')
exceptions.handle(self.request, error_message)
return []
class RouterTab(tabs.TableTab):
table_classes = (RouterPriceTable,)
name = _("Router")
slug = "router"
template_name = 'horizon/common/_detail_table.html'
price_uc = RouterPriceUseCase()
def get_router_price_data(self):
try:
data = self.price_uc.list(self.request)
return data
except Exception:
error_message = _('Unable to get router prices')
exceptions.handle(self.request, error_message)
return []
class SnapshotTab(tabs.TableTab):
table_classes = (SnapshotPriceTable,)
name = _("Snapshot")
slug = "snapshot"
template_name = 'horizon/common/_detail_table.html'
price_uc = SnapshotPriceUseCase()
def get_snapshot_price_data(self):
try:
data = self.price_uc.list(self.request)
return data
except Exception:
error_message = _('Unable to get snapshot prices')
exceptions.handle(self.request, error_message)
return []
class ImageTab(tabs.TableTab):
table_classes = (ImagePriceTable,)
name = _("Image")
slug = "image"
template_name = 'horizon/common/_detail_table.html'
price_uc = ImagePriceUseCase()
def get_image_price_data(self):
try:
data = self.price_uc.list(self.request)
return data
except Exception:
error_message = _('Unable to get image prices')
exceptions.handle(self.request, error_message)
return []
class PriceConfigurationTabs(tabs.TabGroup):
slug = "price_config"
tabs = (FlavorTab, VolumeTab, FloatingIpTab, RouterTab, SnapshotTab, ImageTab, )
sticky = True

View file

@ -0,0 +1,18 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Create/update a flavor price.' %}</p>
{% endblock %}
{% block modal-footer %}
{% if cancel_url %}
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
class="btn btn-default cancel">
{{ cancel_label }}
</a>
{% endif %}
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
{% endblock %}

View file

@ -0,0 +1,18 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Create/update a floating IP price for each allocation.' %}</p>
{% endblock %}
{% block modal-footer %}
{% if cancel_url %}
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
class="btn btn-default cancel">
{{ cancel_label }}
</a>
{% endif %}
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
{% endblock %}

View file

@ -0,0 +1,17 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Create/update a image price for each 1GB of space.' %}</p>
{% endblock %}
{% block modal-footer %}
{% if cancel_url %}
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
class="btn btn-default cancel">
{{ cancel_label }}
</a>
{% endif %}
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
{% endblock %}

View file

@ -0,0 +1,17 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Create/update a router price for each allocation that have external network.' %}</p>
{% endblock %}
{% block modal-footer %}
{% if cancel_url %}
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
class="btn btn-default cancel">
{{ cancel_label }}
</a>
{% endif %}
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
{% endblock %}

View file

@ -0,0 +1,17 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Create/update a snapshot price for each 1GB of space.' %}</p>
{% endblock %}
{% block modal-footer %}
{% if cancel_url %}
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
class="btn btn-default cancel">
{{ cancel_label }}
</a>
{% endif %}
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
{% endblock %}

View file

@ -0,0 +1,17 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Create/update a volume price for each 1GB of space.' %}</p>
{% endblock %}
{% block modal-footer %}
{% if cancel_url %}
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
class="btn btn-default cancel">
{{ cancel_label }}
</a>
{% endif %}
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create/Update Flavor Price" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/_create_flavor.html" %}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create/Update Floating IP Price" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/_create_floating_ip.html" %}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create/Update Image Price" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/_create_image.html" %}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create/Update Router Price" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/_create_router.html" %}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create/Update Snapshot Price" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/_create_snapshot.html" %}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create/Update Volume Price" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/_create_volume.html" %}
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Price Configuration" %}{% endblock %}
{% block main %}
{% include "admin/price_configuration/missing_prices.html" %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,70 @@
{% if missing_price.flavor %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Flavor price not complete
</div>
</div>
</div>
{% endif %}
{% if missing_price.volume %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Volume price not complete
</div>
</div>
</div>
{% endif %}
{% if missing_price.fip %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Floating IP price not complete
</div>
</div>
</div>
{% endif %}
{% if missing_price.router %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Router IP price not complete
</div>
</div>
</div>
{% endif %}
{% if missing_price.snapshot %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Snapshot price not complete
</div>
</div>
</div>
{% endif %}
{% if missing_price.image %}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Image price not complete
</div>
</div>
</div>
{% endif %}

View file

@ -0,0 +1,35 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^flavor_price/create/$', views.FlavorPriceAddFormView.as_view(), name='flavor_price_create'),
url(r'^flavor_price/update/(?P<id>[^/]+)/$', views.FlavorPriceUpdateFormView.as_view(), name='flavor_price_update'),
url(r'^volume_price/create/$', views.VolumePriceAddFormView.as_view(), name='volume_price_create'),
url(r'^volume_price/update/(?P<id>[^/]+)/$', views.VolumePriceUpdateFormView.as_view(), name='volume_price_update'),
url(r'^floating_ip_price/create/$', views.FloatingIpPriceAddFormView.as_view(), name='floating_ip_price_create'),
url(r'^floating_ip_price/update/(?P<id>[^/]+)/$', views.FloatingIpPriceUpdateFormView.as_view(),
name='floating_ip_price_update'),
url(r'^router_price/create/$', views.RouterPriceAddFormView.as_view(), name='router_price_create'),
url(r'^router_price/update/(?P<id>[^/]+)/$', views.RouterPriceUpdateFormView.as_view(),
name='router_price_update'),
url(r'^snapshot_price/create/$', views.SnapshotPriceAddFormView.as_view(), name='snapshot_price_create'),
url(r'^snapshot_price/update/(?P<id>[^/]+)/$', views.SnapshotPriceUpdateFormView.as_view(),
name='snapshot_price_update'),
url(r'^image_price/create/$', views.ImagePriceAddFormView.as_view(), name='image_price_create'),
url(r'^image_price/update/(?P<id>[^/]+)/$', views.ImagePriceUpdateFormView.as_view(),
name='image_price_update'),
]

View file

@ -0,0 +1,117 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.urls import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from neutronclient.common import exceptions as neutron_exc
from .forms import FlavorPriceForm, VolumePriceForm, FloatingIpPriceForm, RouterPriceForm, SnapshotPriceForm, ImagePriceForm
from .tabs import PriceConfigurationTabs
from ....yuyu.admin.price_configuration import views
class IndexView(views.IndexView):
tab_group_class = PriceConfigurationTabs
class FlavorPriceAddFormView(views.FlavorPriceAddFormView):
form_class = FlavorPriceForm
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:flavor_price_create")
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_flavor.html'
class FlavorPriceUpdateFormView(views.FlavorPriceUpdateFormView):
form_class = FlavorPriceForm
submit_url = "horizon:yuyu_demo:price_configuration_demo:flavor_price_update"
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_flavor.html'
class VolumePriceAddFormView(views.VolumePriceAddFormView):
form_class = VolumePriceForm
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:volume_price_create")
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_volume.html'
class VolumePriceUpdateFormView(views.VolumePriceUpdateFormView):
form_class = VolumePriceForm
form_id = "volume_price_form_update"
page_title = _("Volume Price")
submit_label = _("Update Volume Price")
submit_url = "horizon:yuyu_demo:price_configuration_demo:volume_price_update"
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_volume.html'
class FloatingIpPriceAddFormView(views.FloatingIpPriceAddFormView):
form_class = FloatingIpPriceForm
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:floating_ip_price_create")
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_floating_ip.html'
class FloatingIpPriceUpdateFormView(views.FloatingIpPriceUpdateFormView):
form_class = FloatingIpPriceForm
submit_url = "horizon:yuyu_demo:price_configuration_demo:floating_ip_price_update"
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_floating_ip.html'
class RouterPriceAddFormView(views.RouterPriceAddFormView):
form_class = RouterPriceForm
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:router_price_create")
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_router.html'
class RouterPriceUpdateFormView(views.RouterPriceUpdateFormView):
form_class = RouterPriceForm
submit_url = "horizon:yuyu_demo:price_configuration_demo:router_price_update"
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_router.html'
class SnapshotPriceAddFormView(views.SnapshotPriceAddFormView):
form_class = SnapshotPriceForm
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:snapshot_price_create")
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_snapshot.html'
class SnapshotPriceUpdateFormView(views.SnapshotPriceUpdateFormView):
form_class = SnapshotPriceForm
submit_url = "horizon:yuyu_demo:price_configuration_demo:snapshot_price_update"
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_snapshot.html'
class ImagePriceAddFormView(views.ImagePriceAddFormView):
form_class = ImagePriceForm
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:image_price_create")
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_image.html'
class ImagePriceUpdateFormView(views.ImagePriceUpdateFormView):
form_class = ImagePriceForm
submit_url = "horizon:yuyu_demo:price_configuration_demo:image_price_update"
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
template_name = 'yuyu_demo/price_configuration_demo/create_image.html'

View file

@ -0,0 +1,65 @@
import base64
from django.core import validators
from django.utils.translation import ugettext_lazy as _
from djmoney.forms import MoneyField
from horizon import forms, messages, exceptions
from openstack_dashboard.dashboards.yuyu.cases.balance_use_case import BalanceUseCase
from openstack_dashboard.dashboards.yuyu.cases.setting_use_case import SettingUseCase
class TopUpForm(forms.SelfHandlingForm):
USE_CASE = BalanceUseCase()
amount = MoneyField(label=_("Amount"), min_value=0, max_digits=10)
description = forms.CharField(label=_("Description"))
def __init__(self, request, *args, **kwargs):
self.project_id = kwargs['project_id']
del kwargs['project_id']
super().__init__(request, *args, **kwargs)
def handle(self, request, data):
try:
payload = {
"amount": data['amount'].amount,
"amount_currency": data['amount'].currency.code,
'description': data['description'],
}
result = self.USE_CASE.top_up(request, self.project_id, payload)
messages.success(request, _(f"Successfully Top Up"))
return result
except Exception as e:
exceptions.handle(request,
_('Unable to top up.'))
class TopDownForm(forms.SelfHandlingForm):
USE_CASE = BalanceUseCase()
amount = MoneyField(label=_("Amount"), min_value=0, max_digits=10)
description = forms.CharField(label=_("Description"))
def __init__(self, request, *args, **kwargs):
self.project_id = kwargs['project_id']
del kwargs['project_id']
super().__init__(request, *args, **kwargs)
def handle(self, request, data):
try:
payload = {
"amount": data['amount'].amount,
"amount_currency": data['amount'].currency.code,
'description': data['description'],
}
result = self.USE_CASE.top_down(request, self.project_id, payload)
messages.success(request, _(f"Successfully Top Down"))
return result
except Exception as e:
exceptions.handle(request,
_('Unable to top down.'))

View file

@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
class ProjectsBalanceDemo(horizon.Panel):
name = _("Projects Balance")
slug = "projects_balance_demo"

View file

@ -0,0 +1,74 @@
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import tables
class DetailAction(tables.LinkAction):
name = "detail"
verbose_name = "Detail"
def get_link_url(self, datum=None, ):
return reverse("horizon:admin:projects_balance:balance_detail", kwargs={
"project_id": datum['project_id'],
})
class TopUpAction(tables.LinkAction):
name = "top_up"
verbose_name = "Top Up"
icon = "plus"
classes = ("ajax-modal",)
def get_link_url(self, datum=None, ):
return reverse("horizon:admin:projects_balance:top_up", kwargs={
"project_id": self.table.kwargs['project_id'],
})
class TopDownAction(tables.LinkAction):
name = "top_down"
verbose_name = "Top Down"
icon = "minus"
classes = ("ajax-modal",)
def get_link_url(self, datum=None, ):
return reverse("horizon:admin:projects_balance:top_down", kwargs={
"project_id": self.table.kwargs['project_id'],
})
class BalanceProjectTable(tables.DataTable):
project = tables.WrappingColumn('project', verbose_name=_('Project'))
amount = tables.WrappingColumn('amount', verbose_name=_('Amount'))
def get_object_id(self, datum):
return datum['id']
def get_object_display(self, datum):
return datum['project']
class Meta(object):
name = "balance_project_table"
hidden_title = True
verbose_name = _("Project Balance")
row_actions = (DetailAction,)
class BalanceTransactionTable(tables.DataTable):
date = tables.WrappingColumn('date', verbose_name=_('Date'))
amount = tables.WrappingColumn('amount', verbose_name=_('Amount'))
action = tables.Column('action', verbose_name=_('Action'))
description = tables.Column('description', verbose_name=_('Description'))
def get_object_id(self, datum):
return datum['id']
def get_object_display(self, datum):
return datum['date']
class Meta(object):
name = "balance_table"
hidden_title = True
verbose_name = _("Balance Transaction")
table_actions = (TopUpAction, TopDownAction,)

View file

@ -0,0 +1,9 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Top down balance for current project' %}</p>
{% endblock %}

View file

@ -0,0 +1,9 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Top up balance for current project' %}</p>
{% endblock %}

View file

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block title %}{{ page_title }}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Project Balance") %}
{% endblock page_header %}
{% block main %}
{{ table.render }}
{% endblock %}
{% block js %}
{{ block.super }}
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block title %}{{ page_title }}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=page_title %}
{% endblock page_header %}
{% block main %}
<h3>Current Balance: {{ amount }}</h3>
{{ table.render }}
{% endblock %}
{% block js %}
{{ block.super }}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Top Up" %}{% endblock %}
{% block main %}
{% include "admin/projects_balance/_form_top_down.html" %}
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Top Up" %}{% endblock %}
{% block main %}
{% include "admin/projects_balance/_form_top_up.html" %}
{% endblock %}

View file

@ -0,0 +1,24 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.conf.urls import url
from openstack_dashboard.dashboards.yuyu.admin.projects_balance import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^detail/(?P<project_id>[^/]+)/$', views.DetailBalanceView.as_view(), name='balance_detail'),
url(r'^detail/(?P<project_id>[^/]+)/topup/$',
views.TopUpView.as_view(), name='top_up'),
url(r'^detail/(?P<project_id>[^/]+)/top_down/$',
views.TopDownView.as_view(), name='top_down'),
]

View file

@ -0,0 +1,73 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import dateutil.parser
from django.urls import reverse_lazy, reverse
from django.utils import formats
from django.utils.translation import ugettext_lazy as _
from djmoney.money import Money
from horizon import exceptions, forms
from .forms import TopUpForm, TopDownForm
from .tables import BalanceProjectTable, BalanceTransactionTable
from ....yuyu.admin.projects_balance import views
class IndexView(views.IndexView):
table_class = BalanceProjectTable
class DetailBalanceView(views.DetailBalanceView):
table_class = BalanceTransactionTable
class TopUpView(views.TopDownView):
form_class = TopUpForm
template_name = 'yuyu_demo/projects_balance_demo/form_top_up.html'
def get_form_kwargs(self):
kwargs = super(TopUpView, self).get_form_kwargs()
kwargs['project_id'] = self.kwargs['project_id']
return kwargs
def get_context_data(self, **kwargs):
context = super(TopUpView, self).get_context_data(**kwargs)
context['submit_url'] = reverse('horizon:yuyu_demo:projects_balance_demo:top_up', kwargs={
'project_id': self.kwargs['project_id']
})
return context
def get_success_url(self):
return reverse('horizon:yuyu_demo:projects_balance_demo:balance_detail', kwargs={
'project_id': self.kwargs['project_id']
})
class TopDownView(views.TopDownView):
form_class = TopDownForm
template_name = 'yuyu_demo/projects_balance_demo/form_top_down.html'
def get_form_kwargs(self):
kwargs = super(TopDownView, self).get_form_kwargs()
kwargs['project_id'] = self.kwargs['project_id']
return kwargs
def get_context_data(self, **kwargs):
context = super(TopDownView, self).get_context_data(**kwargs)
context['submit_url'] = reverse('horizon:yuyu_demo:projects_balance_demo:top_down', kwargs={
'project_id': self.kwargs['project_id']
})
return context
def get_success_url(self):
return reverse('horizon:yuyu_demo:projects_balance_demo:balance_detail', kwargs={
'project_id': self.kwargs['project_id']
})

View file

@ -0,0 +1,22 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import horizon
class ProjectsInvoice(horizon.Panel):
name = _("Projects Invoice")
slug = "projects_invoice"
permissions = ('openstack.roles.admin', )

View file

@ -0,0 +1,44 @@
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import tables
class InvoiceAction(tables.LinkAction):
name = "invoice"
verbose_name = "Invoice"
def get_link_url(self, datum=None, *args, **kwargs):
return reverse("horizon:admin:projects_invoice:invoice_detail", kwargs={
"id": datum['id'],
"project_id": datum['project_id'],
})
class UsageCostAction(tables.LinkAction):
name = "usage_cost"
verbose_name = "Usage Cost"
def get_link_url(self, datum=None, *args, **kwargs):
return reverse("horizon:admin:projects_invoice:usage_cost", kwargs={
"id": datum['id'],
"project_id": datum['project_id'],
})
class InvoiceTable(tables.DataTable):
date = tables.WrappingColumn('date', verbose_name=_('Date'))
state = tables.WrappingColumn('state', verbose_name=_('State'))
total = tables.Column('total', verbose_name=_('Total'))
def get_object_id(self, datum):
return datum['id']
def get_object_display(self, datum):
return datum['date']
class Meta(object):
name = "invoice_table"
hidden_title = True
verbose_name = _("Invoice")
row_actions = (InvoiceAction, UsageCostAction)

View file

@ -0,0 +1,159 @@
<style>
.invoice {
padding: 30px;
}
.invoice h2 {
margin-top: 0px;
line-height: 0.8em;
}
.invoice .small {
font-weight: 300;
}
.invoice hr {
margin-top: 10px;
border-color: #ddd;
}
.invoice .table tr.line {
border-bottom: 1px solid #ccc;
}
.invoice .table td {
border: none;
}
.invoice .identity {
margin-top: 10px;
font-size: 1.1em;
font-weight: 300;
}
.invoice .identity strong {
font-weight: 600;
}
.grid {
position: relative;
width: 100%;
background: #fff;
color: #666666;
border-radius: 2px;
margin-bottom: 25px;
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.1);
}
</style>
<div id="invoice">
<div class="container">
<div class="row">
<!-- BEGIN INVOICE -->
<div class="col-xs-12">
<div class="grid invoice">
<div class="grid-body">
<div class="invoice-title">
<div class="row">
<div class="col-xs-12">
{{ setting.company_logo }}
</div>
</div>
<br>
<div class="row">
<div class="col-xs-12">
<h2>invoice<br>
<span class="small">order #{{ invoice.id }}</span></h2>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-6">
<address style="max-width: 30%;">
{{ setting.company_name }} <br/>
{{ setting.company_address }}
</address>
</div>
<div class="col-xs-6 text-right">
<address>
<strong>Invoice Month:</strong><br>
{{ invoice.start_date|date:"M Y" }}
<br>
<br>
<strong>Issue Date:</strong><br>
{{ invoice.start_date|date:"d M Y" }}
<br>
<br>
<strong>Invoice State:</strong><br>
{{ invoice.state_text }}
</address>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>ORDER SUMMARY</h3>
<table class="table table-striped">
<thead>
<tr class="line">
<td><strong>#</strong></td>
<td width="70%"><strong>COMPONENT</strong></td>
<td class="text-right"><strong>TOTAL COST</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>Instance</strong></td>
<td class="text-right">{{ instance_cost }}</td>
</tr>
<tr>
<td>2</td>
<td><strong>Volume</strong></td>
<td class="text-right">{{ volume_cost }}</td>
</tr>
<tr>
<td>3</td>
<td><strong>Floating IP</strong></td>
<td class="text-right">{{ fip_cost }}</td>
</tr>
<tr>
<td>4</td>
<td><strong>Router</strong></td>
<td class="text-right">{{ router_cost }}</td>
</tr>
<tr>
<td>5</td>
<td><strong>Snapshot</strong></td>
<td class="text-right">{{ snapshot_cost }}</td>
</tr>
<tr class="line">
<td>6</td>
<td><strong>Image</strong></td>
<td class="text-right">{{ image_cost }}</td>
</tr>
<tr>
<td colspan="2" class="text-right"><strong>Subtotal</strong></td>
<td class="text-right"><strong>{{ invoice.subtotal_money }}</strong></td>
</tr>
{% if invoice.state != 1 %}
<tr>
<td colspan="2" class="text-right"><strong>Tax</strong></td>
<td class="text-right"><strong>{{ invoice.tax_money }}</strong></td>
</tr>
<tr>
<td colspan="2" class="text-right"><strong>Total</strong></td>
<td class="text-right"><strong>{{ invoice.total_money }}</strong></td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- END INVOICE -->
</div>
</div>
</div>

View file

@ -0,0 +1,77 @@
{% extends 'base.html' %}
{% block title %}{{ page_title }}{% endblock %}
{% block main %}
{% if invoice %}
{% 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>
<br/>
<br/>
<div id="usage_cost">
<div>
<dl class="dl-horizontal">
<dt>Invoice Month</dt>
<dd>{{ invoice.start_date|date:"M Y" }}</dd>
<dt>Invoice State</dt>
<dd>{{ invoice.state_text }}</dd>
<dt>Subtotal</dt>
<dd>{{ invoice.subtotal_money }}</dd>
{% if invoice.state != 1 %}
<dt>Tax</dt>
<dd>{{ invoice.tax_money }}</dd>
<dt>Total</dt>
<dd>{{ invoice.total_money }}</dd>
{% endif %}
</dl>
</div>
<div id="instance-cost">
{{ instance_cost_table.render }}
</div>
<div id="volume-cost">
{{ volume_cost_table.render }}
</div>
<div id="floating-ip-cost">
{{ floating_ip_cost_table.render }}
</div>
<div id="router-cost">
{{ router_cost_table.render }}
</div>
<div id="snapshot-cost">
{{ snapshot_cost_table.render }}
</div>
<div id="image-cost">
{{ image_cost_table.render }}
</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 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'},
}
html2pdf().set(opt).from(document.getElementById('usage_cost')).save();
}
</script>
{% endblock %}

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

@ -0,0 +1,17 @@
{% load horizon %}
{% jstemplate %}
{% extends 'base.html' %}
{% load i18n %}
{% block title %}
{% trans "Price_Volume_Panel" %}
{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Price_Volume_Panel") %}
{% endblock page_header %}
{% block main %}
{% endblock %}
{% endjstemplate %}

View file

@ -0,0 +1,23 @@
{% extends 'base.html' %}
{% block title %}{{ page_title }}{% endblock %}
{% block main %}
<a class="btn btn-default" href="?print=true" target="_blank">Download PDF</a>
{% include 'admin/projects_invoice/manage_invoice_modal.html' with invoice=invoice project_balance_amount=project_balance_amount %}
<br/>
<br/>
{% include 'admin/projects_invoice/base_invoice.html' %}
{% 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() {
window.print();
}
</script>
{% endblock %}

View file

@ -0,0 +1,13 @@
<html>
<head>
<title>Invoice Download</title>
{% include "_stylesheets.html" %}
</head>
<body>
{% include 'admin/projects_invoice/base_invoice.html' %}
{% include "horizon/_scripts.html" %}
<script type="text/javascript">
window.print();
</script>
</body>
</html>

View file

@ -0,0 +1,32 @@
{% extends 'base.html' %}
{% block title %}{{ page_title }}{% endblock %}
{% block main %}
<div class="row" style="margin-bottom: 15px;">
<div class="col-md-6">
<b>Invoice Month</b>
<select id="project_select" onchange="onProjectSelect(this.value)">
{% for i in project_list %}
<option
value='{"project_id": "{{ i.id }}", "project_name": "{{ i.name }}"}'
{% if i.id == current_project_id %}selected {% endif %}
>{{ i.name }}</option>
{% endfor %}
</select>
</div>
</div>
{{ table.render }}
{% endblock %}
{% block js %}
{{ block.super }}
<script type="text/javascript">
function onProjectSelect(val) {
console.log(val)
const data = JSON.parse(val);
var search = "?project_id=" + data.project_id + "&project_name=" + data.project_name;
window.location.href = window.location.protocol + "//" + window.location.host + window.location.pathname + search;
}
</script>
{% endblock %}

View file

@ -0,0 +1,51 @@
{% if invoice.state == 2 %}
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#invoice_finish_modal">
Set to Finished
</button>
{% endif %}
{% if invoice.state == 100 %}
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#invoice_rollback_unpaid_modal">
Rollback to Unpaid
</button>
{% endif %}
<div class="modal fade" id="invoice_finish_modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Finish Invoice</h4>
</div>
<div class="modal-body">
Project Balance: {{ project_balance_amount }}
<br><br>
<a class="btn btn-warning"
href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}&skip_balance=true">Finish without Deduct Balance</a>
<a class="btn btn-success"
href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}">Finish and Deduct Balance</a>
</div>
</div>
</div>
</div>
<div class="modal fade" id="invoice_rollback_unpaid_modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Rollback to Unpaid</h4>
</div>
<div class="modal-body">
Project Balance: {{ project_balance_amount }}
<br><br>
<a class="btn btn-danger"
href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}&skip_balance=true">Rollback without Refund Balance</a>
<a class="btn btn-warning"
href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}">Rollback and Refund Balance</a>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,25 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
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.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

@ -0,0 +1,45 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import views
from .tables import InvoiceTable
from ....yuyu.admin.projects_invoice import views
class IndexView(views.IndexView):
table_class = InvoiceTable
class InvoiceView(views.InvoiceView):
pass
class UsageCostTabView(views.UsageCostTabView):
pass
class UsageCostView(views.UsageCostView):
pass
class FinishInvoice(views.FinishInvoice):
def get(self, request, *args, **kwargs):
next_url = request.GET.get('next', reverse('horizon:yuyu_demo:projects_invoice_demo:index'))
return HttpResponseRedirect(next_url)
class RollbackToUnpaidInvoice(views.RollbackToUnpaidInvoice):
def get(self, request, *args, **kwargs):
next_url = request.GET.get('next', reverse('horizon:yuyu_demo:projects_invoice_demo:index'))
return HttpResponseRedirect(next_url)

28
yuyu_demo/dashboard.py Normal file
View file

@ -0,0 +1,28 @@
# Copyright 2012 Nebula, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import gettext_lazy as _
from openstack_auth import utils
import horizon
from horizon.utils import settings as utils_settings
class YuyuDemo(horizon.Dashboard):
name = _("Admin - Yuyu Demo")
slug = "yuyu_demo"
default_panel = 'billing_overview_demo'
horizon.register(YuyuDemo)

View file

@ -0,0 +1,6 @@
ADD_INSTALLED_APPS = [
'djmoney',
'openstack_dashboard.dashboards.yuyu_demo',
]
DASHBOARD ='yuyu_demo'

View file

@ -0,0 +1,8 @@
from django.utils.translation import ugettext_lazy as _
# The slug of the panel group to be added to HORIZON_CONFIG. Required.
PANEL_GROUP = 'billing'
# The display name of the PANEL_GROUP. Required.
PANEL_GROUP_NAME = _('Billing')
# The slug of the dashboard the PANEL_GROUP associated with. Required.
PANEL_GROUP_DASHBOARD = 'yuyu_demo'

View file

@ -0,0 +1,9 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'billing_overview_demo'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'yuyu_demo'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'billing'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.billing_overview_demo.panel.BillingOverviewDemo'

View file

@ -0,0 +1,9 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'price_configuration_demo'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'yuyu_demo'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'billing'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.price_configuration_demo.panel.PriceConfigurationDemo'

View file

@ -0,0 +1,9 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'billing_setting_demo'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'yuyu_demo'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'billing'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.billing_setting_demo.panel.BillingSettingDemo'

View file

@ -0,0 +1,9 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'projects_invoice_demo'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'yuyu_demo'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'billing'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.projects_invoice_demo.panel.ProjectsInvoiceDemo'

View file

@ -0,0 +1,9 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'notification_center_demo'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'yuyu_demo'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'billing'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.notification_center_demo.panel.NotificationCenterDemo'

View file

@ -0,0 +1,9 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'projects_balance_demo'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'yuyu_demo'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'billing'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.projects_balance_demo.panel.ProjectsBalanceDemo'