Refactor CSV generation code to utils
Moving general CSV generation code from usage into utils so that it can be used by other dashboards. The file is named csvbase to avoid name collisions with csv module. Change-Id: I3f4a573b53cb2d85fa36e84320707d74917498ad Closes-bug: #1280475
This commit is contained in:
parent
898ccff4c9
commit
9ad412827b
144
horizon/utils/csvbase.py
Normal file
144
horizon/utils/csvbase.py
Normal file
@ -0,0 +1,144 @@
|
||||
# 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 __future__ import division
|
||||
|
||||
from csv import DictWriter # noqa
|
||||
from csv import writer # noqa
|
||||
|
||||
from StringIO import StringIO
|
||||
|
||||
from django.http import HttpResponse # noqa
|
||||
from django import template as django_template
|
||||
from django import VERSION # noqa
|
||||
|
||||
|
||||
class CsvDataMixin(object):
|
||||
|
||||
"""CSV data Mixin - provides handling for CSV data.
|
||||
|
||||
.. attribute:: columns
|
||||
|
||||
A list of CSV column definitions. If omitted - no column titles
|
||||
will be shown in the result file. Optional.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.out = StringIO()
|
||||
super(CsvDataMixin, self).__init__()
|
||||
if hasattr(self, "columns"):
|
||||
self.writer = DictWriter(self.out, map(self.encode, self.columns))
|
||||
self.is_dict = True
|
||||
else:
|
||||
self.writer = writer(self.out)
|
||||
self.is_dict = False
|
||||
|
||||
def write_csv_header(self):
|
||||
if self.is_dict:
|
||||
try:
|
||||
self.writer.writeheader()
|
||||
except AttributeError:
|
||||
# For Python<2.7
|
||||
self.writer.writerow(dict(zip(
|
||||
self.writer.fieldnames,
|
||||
self.writer.fieldnames)))
|
||||
|
||||
def write_csv_row(self, args):
|
||||
if self.is_dict:
|
||||
self.writer.writerow(dict(zip(
|
||||
self.writer.fieldnames, map(self.encode, args))))
|
||||
else:
|
||||
self.writer.writerow(map(self.encode, args))
|
||||
|
||||
def encode(self, value):
|
||||
# csv and StringIO cannot work with mixed encodings,
|
||||
# so encode all with utf-8
|
||||
return unicode(value).encode('utf-8')
|
||||
|
||||
|
||||
class BaseCsvResponse(CsvDataMixin, HttpResponse):
|
||||
|
||||
"""Base CSV response class. Provides handling of CSV data."""
|
||||
|
||||
def __init__(self, request, template, context, content_type, **kwargs):
|
||||
super(BaseCsvResponse, self).__init__()
|
||||
self['Content-Disposition'] = 'attachment; filename="%s"' % (
|
||||
kwargs.get("filename", "export.csv"),)
|
||||
self['Content-Type'] = content_type
|
||||
self.context = context
|
||||
self.header = None
|
||||
if template:
|
||||
# Display some header info if provided as a template
|
||||
header_template = django_template.loader.get_template(template)
|
||||
context = django_template.RequestContext(request, self.context)
|
||||
self.header = header_template.render(context)
|
||||
|
||||
if self.header:
|
||||
self.out.write(self.encode(self.header))
|
||||
|
||||
self.write_csv_header()
|
||||
|
||||
for row in self.get_row_data():
|
||||
self.write_csv_row(row)
|
||||
|
||||
self.out.flush()
|
||||
self.content = self.out.getvalue()
|
||||
self.out.close()
|
||||
|
||||
def get_row_data(self):
|
||||
raise NotImplementedError("You must define a get_row_data method on %s"
|
||||
% self.__class__.__name__)
|
||||
|
||||
if VERSION >= (1, 5, 0):
|
||||
|
||||
from django.http import StreamingHttpResponse # noqa
|
||||
|
||||
class BaseCsvStreamingResponse(CsvDataMixin, StreamingHttpResponse):
|
||||
|
||||
"""Base CSV Streaming class. Provides streaming response for CSV data.
|
||||
"""
|
||||
|
||||
def __init__(self, request, template, context, content_type, **kwargs):
|
||||
super(BaseCsvStreamingResponse, self).__init__()
|
||||
self['Content-Disposition'] = 'attachment; filename="%s"' % (
|
||||
kwargs.get("filename", "export.csv"),)
|
||||
self['Content-Type'] = content_type
|
||||
self.context = context
|
||||
self.header = None
|
||||
if template:
|
||||
# Display some header info if provided as a template
|
||||
header_template = django_template.loader.get_template(template)
|
||||
context = django_template.RequestContext(request, self.context)
|
||||
self.header = header_template.render(context)
|
||||
|
||||
self._closable_objects.append(self.out)
|
||||
|
||||
self.streaming_content = self.get_content()
|
||||
|
||||
def buffer(self):
|
||||
buf = self.out.getvalue()
|
||||
self.out.truncate(0)
|
||||
return buf
|
||||
|
||||
def get_content(self):
|
||||
if self.header:
|
||||
self.out.write(self.encode(self.header))
|
||||
|
||||
self.write_csv_header()
|
||||
yield self.buffer()
|
||||
|
||||
for row in self.get_row_data():
|
||||
self.write_csv_row(row)
|
||||
yield self.buffer()
|
||||
|
||||
def get_row_data(self):
|
||||
raise NotImplementedError("You must define a get_row_data method "
|
||||
"on %s" % self.__class__.__name__)
|
@ -24,13 +24,13 @@ from django.utils import translation
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon.utils import csvbase
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import usage
|
||||
from openstack_dashboard.usage import base
|
||||
|
||||
|
||||
class GlobalUsageCsvRenderer(base.BaseCsvResponse):
|
||||
class GlobalUsageCsvRenderer(csvbase.BaseCsvResponse):
|
||||
|
||||
columns = [_("Project Name"), _("VCPUs"), _("Ram (MB)"),
|
||||
_("Disk (GB)"), _("Usage (Hours)")]
|
||||
|
@ -24,11 +24,12 @@ from django.template.defaultfilters import floatformat # noqa
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import TemplateView # noqa
|
||||
|
||||
from horizon.utils import csvbase
|
||||
|
||||
from openstack_dashboard import usage
|
||||
from openstack_dashboard.usage import base
|
||||
|
||||
|
||||
class ProjectUsageCsvRenderer(base.BaseCsvResponse):
|
||||
class ProjectUsageCsvRenderer(csvbase.BaseCsvResponse):
|
||||
|
||||
columns = [_("Instance Name"), _("VCPUs"), _("Ram (MB)"),
|
||||
_("Disk (GB)"), _("Usage (Hours)"),
|
||||
|
@ -12,17 +12,10 @@
|
||||
|
||||
from __future__ import division
|
||||
|
||||
from csv import DictWriter # noqa
|
||||
from csv import writer # noqa
|
||||
|
||||
import datetime
|
||||
|
||||
from django.http import HttpResponse # noqa
|
||||
from django import template as django_template
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django import VERSION # noqa
|
||||
import six
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
@ -282,125 +275,3 @@ class ProjectUsage(BaseUsage):
|
||||
instances.append(server_usage)
|
||||
usage.server_usages = instances
|
||||
return (usage,)
|
||||
|
||||
|
||||
class CsvDataMixin(object):
|
||||
|
||||
"""CSV data Mixin - provides handling for CSV data.
|
||||
|
||||
.. attribute:: columns
|
||||
|
||||
A list of CSV column definitions. If omitted - no column titles
|
||||
will be shown in the result file. Optional.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.out = six.StringIO()
|
||||
super(CsvDataMixin, self).__init__()
|
||||
if hasattr(self, "columns"):
|
||||
self.writer = DictWriter(self.out, map(self.encode, self.columns))
|
||||
self.is_dict = True
|
||||
else:
|
||||
self.writer = writer(self.out)
|
||||
self.is_dict = False
|
||||
|
||||
def write_csv_header(self):
|
||||
if self.is_dict:
|
||||
try:
|
||||
self.writer.writeheader()
|
||||
except AttributeError:
|
||||
# For Python<2.7
|
||||
self.writer.writerow(dict(zip(
|
||||
self.writer.fieldnames,
|
||||
self.writer.fieldnames)))
|
||||
|
||||
def write_csv_row(self, args):
|
||||
if self.is_dict:
|
||||
self.writer.writerow(dict(zip(
|
||||
self.writer.fieldnames, map(self.encode, args))))
|
||||
else:
|
||||
self.writer.writerow(map(self.encode, args))
|
||||
|
||||
def encode(self, value):
|
||||
# csv and StringIO cannot work with mixed encodings,
|
||||
# so encode all with utf-8
|
||||
return unicode(value).encode('utf-8')
|
||||
|
||||
|
||||
class BaseCsvResponse(CsvDataMixin, HttpResponse):
|
||||
|
||||
"""Base CSV response class. Provides handling of CSV data."""
|
||||
|
||||
def __init__(self, request, template, context, content_type, **kwargs):
|
||||
super(BaseCsvResponse, self).__init__()
|
||||
self['Content-Disposition'] = 'attachment; filename="%s"' % (
|
||||
kwargs.get("filename", "export.csv"),)
|
||||
self['Content-Type'] = content_type
|
||||
self.context = context
|
||||
self.header = None
|
||||
if template:
|
||||
# Display some header info if provided as a template
|
||||
header_template = django_template.loader.get_template(template)
|
||||
context = django_template.RequestContext(request, self.context)
|
||||
self.header = header_template.render(context)
|
||||
|
||||
if self.header:
|
||||
self.out.write(self.encode(self.header))
|
||||
|
||||
self.write_csv_header()
|
||||
|
||||
for row in self.get_row_data():
|
||||
self.write_csv_row(row)
|
||||
|
||||
self.out.flush()
|
||||
self.content = self.out.getvalue()
|
||||
self.out.close()
|
||||
|
||||
def get_row_data(self):
|
||||
raise NotImplementedError("You must define a get_row_data method on %s"
|
||||
% self.__class__.__name__)
|
||||
|
||||
if VERSION >= (1, 5, 0):
|
||||
|
||||
from django.http import StreamingHttpResponse # noqa
|
||||
|
||||
class BaseCsvStreamingResponse(CsvDataMixin, StreamingHttpResponse):
|
||||
|
||||
"""Base CSV Streaming class. Provides streaming response for CSV data.
|
||||
"""
|
||||
|
||||
def __init__(self, request, template, context, content_type, **kwargs):
|
||||
super(BaseCsvStreamingResponse, self).__init__()
|
||||
self['Content-Disposition'] = 'attachment; filename="%s"' % (
|
||||
kwargs.get("filename", "export.csv"),)
|
||||
self['Content-Type'] = content_type
|
||||
self.context = context
|
||||
self.header = None
|
||||
if template:
|
||||
# Display some header info if provided as a template
|
||||
header_template = django_template.loader.get_template(template)
|
||||
context = django_template.RequestContext(request, self.context)
|
||||
self.header = header_template.render(context)
|
||||
|
||||
self._closable_objects.append(self.out)
|
||||
|
||||
self.streaming_content = self.get_content()
|
||||
|
||||
def buffer(self):
|
||||
buf = self.out.getvalue()
|
||||
self.out.truncate(0)
|
||||
return buf
|
||||
|
||||
def get_content(self):
|
||||
if self.header:
|
||||
self.out.write(self.encode(self.header))
|
||||
|
||||
self.write_csv_header()
|
||||
yield self.buffer()
|
||||
|
||||
for row in self.get_row_data():
|
||||
self.write_csv_row(row)
|
||||
yield self.buffer()
|
||||
|
||||
def get_row_data(self):
|
||||
raise NotImplementedError("You must define a get_row_data method "
|
||||
"on %s" % self.__class__.__name__)
|
||||
|
Loading…
Reference in New Issue
Block a user