On Tue, May 30, 2017 at 05:25:06PM +1000, Dan Callaghan wrote:
Hi Dan,
This is V2 of my patch. Things move farther along. But now I can't
actually 'send_file'. It doesn't return anything. There is data added to
the temp file and the function returns a flask response. Do I have to
convert the flask response to a cherrypy one or something here?
Let me inline my issues in the patch.
Cheers,
Don
diff --git a/Server/assets/csv-export.js b/Server/assets/csv-export.js
new file mode 100644
index 0000000..09a3517
--- /dev/null
+++ b/Server/assets/csv-export.js
@@ -0,0 +1,43 @@
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+
+;(function () {
+
+var CSVExportSelection = Backbone.Model.extend({
+});
+
+window.CSVExport = Backbone.View.extend({
+ template: JST['csv-export'],
+ events: {
+ 'submit form': 'submit',
+ 'change .csv-fields input': 'update_selection',
+ },
+ initialize: function (options) {
+ this.csv_types = options.options['csv_types']
+ this.selection = new CSVExportSelection({
+ csv_type: this.csv_types[0]
+ });
+ this.render();
+ },
+ render: function () {
+ this.$el.html(this.template(this));
+ },
+ update_selection: function (evt) {
+ var elem = evt.currentTarget;
+ this.selection.set(elem.name, $(elem).val());
+ },
+ submit: function (evt) {
+ evt.preventDefault();
+ var xhr = $.ajax({
+ url: 'csv/action_export',
+ type: 'POST',
+ data: this.selection.attributes,
+ traditional: true,
+ });
+ },
+});
+
+})();
diff --git a/Server/assets/jst/csv-export.html b/Server/assets/jst/csv-export.html
new file mode 100644
index 0000000..2fd0366
--- /dev/null
+++ b/Server/assets/jst/csv-export.html
@@ -0,0 +1,20 @@
+<form class="form-horizontal">
+ <fieldset class="csv-fields">
+ <div class="control-group">
+ <label class="control-label">CSV Type</label>
+ <div class="controls">
+ <% _.each(csv_types, function (csv) { %>
+ <label class="radio">
+ <input type="radio" name="csv_type" value="<%-
csv.toLowerCase() %>"
+ <% if (csv == 'system' ) { %>checked<% } %>
+ />
+ <%- csv %>
+ </label>
+ <% }) %>
+ </div>
+ </div>
+ <div class="form-actions">
+ <button class="btn btn-primary" type="submit">Export
CSV</button>
+ </div>
+ </fieldset>
+</form>
diff --git a/Server/bkr/server/CSV_import_export.py
b/Server/bkr/server/CSV_import_export.py
index 41fcb26..24ad6e2 100644
--- a/Server/bkr/server/CSV_import_export.py
+++ b/Server/bkr/server/CSV_import_export.py
@@ -10,7 +10,11 @@
from bkr.server import identity
from bkr.server.xmlrpccontroller import RPCRoot
from tempfile import NamedTemporaryFile
-from cherrypy.lib.cptools import serve_file
+#from cherrypy.lib.cptools import serve_file
+from flask import send_file, request
+from bkr.server.flask_util import render_tg_template, auth_required, admin_auth_required
+
+from bkr.server.app import app
from bkr.server.model import (System, SystemType, Activity, SystemActivity,
User, Group, LabController, LabInfo,
OSMajor, OSVersion,
@@ -70,6 +74,52 @@ def line_num(self):
def fieldnames(self):
return self.reader.fieldnames
+# For XMLRPC methods in this class.
+exposed = False
+
+export_help_text = XML(u'<span>Refer to the <a
href="http://beaker-project.org/docs/'
+ 'admin-guide/interface.html#export"
target="_blank">'
+ 'documentation</a> to learn more about the exported
data.</span>').expand()
+import_help_text = XML(u'<span>Refer to the <a
href="http://beaker-project.org/docs/'
+ 'admin-guide/interface.html#import"
target="_blank">'
+ 'documentation</a> for details about the supported CSV
format.</span>').expand()
+
Not sure how to add these correctly. Through the 'options' in the
render_tg_template call?
+(a)app.route('/csv', methods=['GET'])
+@auth_required
+def index():
+ options = {}
+ options['csv_types'] = ('system', 'system_id',
'labinfo', 'power',
+ 'exclude', 'install', 'keyvalue',
'system_pool',
+ 'user_group')
I need to create a better map here, ie options['csv_types'] = {
'system':
'System', ...}???
+ #return render_template('assets.jst.csv_export.html', options=options)
+ return render_tg_template('bkr.server.templates.csv_export', {
+ 'title' : u'CSV Export Don',
+ 'options' : options
+ })
I tried to use the 'backgrid' template here, but it came falling because I
didn't define a 'grid_collection_type'. I don't think I need that and
couldn't figure out the javascript to 'if undefined skip..' logic.
So instead I use a new csv_export.kid file.
+
+(a)app.route('/csv/action_export', methods=['POST'])
+@auth_required
+def action_export():
+ log = []
+ csv_type = request.form.get('csv_type')
+
+
+ file = NamedTemporaryFile()
+ logger.debug("DON: request: %s, file %s" % (csv_type, file.name))
+ if csv_type in csv_types:
+ csv_types[csv_type]._to_csv(file)
+ else:
+ log.append("Invalid csv_type %s" % csv_type)
+ file.seek(0)
+
+ logger.debug('DON CSV export : %s', file)
+ filename = "%s.csv" % csv_type
+ ret = send_file(file.name, mimetype="text/csv",
+ as_attachment=True,
+ attachment_filename=filename)
This returns a flask response. Not sure if it is valid, it barfs with I try
to print it. Regardless, nothing happens as a result, no pop up download
window, no failure, nothing. The csv_type data is right though.
That's it for issues...
+
+ return ret
+
class CSV(RPCRoot):
# For XMLRPC methods in this class.
exposed = False
@@ -110,8 +160,6 @@ class CSV(RPCRoot):
submit_text = _(u'Export CSV'),
)
- @expose(template='bkr.server.templates.form')
- @identity.require(identity.not_anonymous())
def index(self, **kw):
return dict(
form = self.exportform,
@@ -121,8 +169,6 @@ def index(self, **kw):
value = kw,
)
- @expose(template='bkr.server.templates.form-post')
- @identity.require(identity.in_group('admin'))
def csv_import(self, **kw):
return dict(
form = self.importform,
@@ -132,13 +178,12 @@ def csv_import(self, **kw):
value = kw,
)
- @expose()
- @identity.require(identity.not_anonymous())
def action_export(self, csv_type, *args, **kw):
file = NamedTemporaryFile()
log = self.to_csv(file, csv_type)
file.seek(0)
+ logger.debug('DON CSV export with send_file type: %s', csv_type)
return serve_file(file.name, contentType="text/csv",
disposition="attachment",
name="%s.csv" % csv_type)
@@ -181,8 +226,6 @@ def _import_row(self, data, log):
raise ValueError('Invalid csv_type %s or missing required fields'
% data['csv_type'])
- @expose(template='bkr.server.templates.csv_import')
- @identity.require(identity.in_group('admin'))
def action_import(self, csv_file, *args, **kw):
"""
TurboGears method to import data from csv
@@ -247,9 +290,11 @@ def _to_csv(cls, file):
header = csv.writer(file)
header.writerow(['csv_type'] + cls.csv_keys)
writer = csv.DictWriter(file, ['csv_type'] + cls.csv_keys)
+ logger.debug("DON: %s" % cls.csv_keys)
for item in cls.query():
for data in cls.to_datastruct(item):
data['csv_type'] = cls.csv_type
+ logger.debug("DON: data is %s" % data)
# XXX remove encoding in Python 3...
writer.writerow(dict((k, unicode(v).encode('utf8')) for k, v in
data.iteritems()))
diff --git a/Server/bkr/server/assets.py b/Server/bkr/server/assets.py
index 0c5a117..71747f4 100644
--- a/Server/bkr/server/assets.py
+++ b/Server/bkr/server/assets.py
@@ -90,6 +90,7 @@ def _create_env(source_dir, output_dir, **kwargs):
'pools.js',
'query-builder.js',
'reserve-workflow.js',
+ 'csv-export.js',
'installation-model.js',
'task-library-model.js',
'scheduler-model.js',
diff --git a/Server/bkr/server/controllers.py b/Server/bkr/server/controllers.py
index cece8db..936cdc2 100644
--- a/Server/bkr/server/controllers.py
+++ b/Server/bkr/server/controllers.py
@@ -146,7 +146,7 @@ class Root(RPCRoot):
users = Users()
arches = Arches()
auth = Auth()
- csv = CSV()
+ #csv = CSV()
jobs = Jobs()
recipesets = RecipeSets()
recipes = Recipes()
diff --git a/Server/bkr/server/templates/csv_export.kid
b/Server/bkr/server/templates/csv_export.kid
new file mode 100644
index 0000000..16f7487
--- /dev/null
+++ b/Server/bkr/server/templates/csv_export.kid
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'master.kid'">
+<head>
+<title>$title</title>
+</head>
+<body>
+<div class="page-header">
+ <h1>$title</h1>
+</div>
+<div class="csv_export"></div>
+<script type="text/javascript">
+$(function () {
+ new CSVExport({
+ el: '.csv_export',
+ options: ${tg.to_json(options)},
+ });
+});
+</script>
+</body>
+</html>