Add basic support for Poser reports, list/create
This commit is contained in:
parent
a3195267c9
commit
72177aef0a
|
@ -674,10 +674,18 @@ class Form(object):
|
||||||
case the validator pertains to the form at large instead of
|
case the validator pertains to the form at large instead of
|
||||||
one of the fields.
|
one of the fields.
|
||||||
|
|
||||||
|
TODO: what should the validator look like?
|
||||||
|
|
||||||
:param validator: Callable validator for the node.
|
:param validator: Callable validator for the node.
|
||||||
"""
|
"""
|
||||||
self.validators[key] = validator
|
self.validators[key] = validator
|
||||||
|
|
||||||
|
# we normally apply the validator when creating the schema, so
|
||||||
|
# if this form already has a schema, then go ahead and apply
|
||||||
|
# the validator to it
|
||||||
|
if self.schema and key in self.schema:
|
||||||
|
self.schema[key].validator = validator
|
||||||
|
|
||||||
def set_required(self, key, required=True):
|
def set_required(self, key, required=True):
|
||||||
"""
|
"""
|
||||||
Set whether or not value is required for a given field.
|
Set whether or not value is required for a given field.
|
||||||
|
|
77
tailbone/templates/poser/reports/view.mako
Normal file
77
tailbone/templates/poser/reports/view.mako
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/view.mako" />
|
||||||
|
|
||||||
|
<%def name="render_form_buttons()">
|
||||||
|
<div v-if="!showUploadForm" class="buttons">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
@click="heckYeah()">
|
||||||
|
Upload Replacement Module
|
||||||
|
</b-button>
|
||||||
|
<once-button type="is-primary"
|
||||||
|
tag="a"
|
||||||
|
% if instance.get('error'):
|
||||||
|
href="#" disabled
|
||||||
|
% else:
|
||||||
|
href="${url('generate_specific_report', type_key=instance['report'].type_key)}"
|
||||||
|
% endif
|
||||||
|
text="Generate this Report">
|
||||||
|
</once-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="showUploadForm">
|
||||||
|
${h.form(master.get_action_url('replace', instance), enctype='multipart/form-data', **{'@submit': 'uploadSubmitting = true'})}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
<b-field label="New Module File" horizontal>
|
||||||
|
|
||||||
|
<b-field class="file is-primary"
|
||||||
|
:class="{'has-name': !!uploadFile}"
|
||||||
|
>
|
||||||
|
<b-upload name="replacement_module"
|
||||||
|
v-model="uploadFile"
|
||||||
|
class="file-label">
|
||||||
|
<span class="file-cta">
|
||||||
|
<b-icon class="file-icon" pack="fas" icon="upload"></b-icon>
|
||||||
|
<span class="file-label">Click to upload</span>
|
||||||
|
</span>
|
||||||
|
</b-upload>
|
||||||
|
<span v-if="uploadFile"
|
||||||
|
class="file-name">
|
||||||
|
{{ uploadFile.name }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<b-button @click="showUploadForm = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
native-type="submit"
|
||||||
|
:disabled="uploadSubmitting || !uploadFile"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="save">
|
||||||
|
{{ uploadSubmitting ? "Working, please wait..." : "Save" }}
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</b-field>
|
||||||
|
${h.end_form()}
|
||||||
|
</div>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
${parent.modify_this_page_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
${form.component_studly}Data.showUploadForm = false
|
||||||
|
|
||||||
|
${form.component_studly}Data.uploadFile = null
|
||||||
|
|
||||||
|
${form.component_studly}Data.uploadSubmitting = false
|
||||||
|
|
||||||
|
${form.component_studly}.methods.heckYeah = function() {
|
||||||
|
this.showUploadForm = true
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
57
tailbone/templates/poser/setup.mako
Normal file
57
tailbone/templates/poser/setup.mako
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/page.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">Poser Setup</%def>
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
Before you can use Poser features, ${app_title} must create the
|
||||||
|
file structure for it.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
A new folder will be created at this location:
|
||||||
|
<span class="is-family-monospace has-text-weight-bold">
|
||||||
|
${poser_dir}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
Once set up, ${app_title} can generate code for certain features,
|
||||||
|
in the Poser folder. You can then access these features from
|
||||||
|
within ${app_title}.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
You are free to edit most files in the Poser folder as well.
|
||||||
|
When you do so ${app_title} should pick up on the changes with no
|
||||||
|
need for app restart.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
Proceed?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
${h.form(request.current_route_url(), **{'@submit': 'setupSubmitting = true'})}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
<b-button type="is-primary"
|
||||||
|
native-type="submit"
|
||||||
|
:disabled="setupSubmitting">
|
||||||
|
{{ setupSubmitting ? "Working, please wait..." : "Go for it!" }}
|
||||||
|
</b-button>
|
||||||
|
${h.end_form()}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
${parent.modify_this_page_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
ThisPageData.setupSubmitting = false
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -32,7 +32,7 @@ import rattail
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.batch import consume_batch_id
|
from rattail.batch import consume_batch_id
|
||||||
from rattail.mail import send_email
|
from rattail.mail import send_email
|
||||||
from rattail.util import OrderedDict
|
from rattail.util import OrderedDict, simple_error
|
||||||
from rattail.files import resource_path
|
from rattail.files import resource_path
|
||||||
|
|
||||||
from pyramid import httpexceptions
|
from pyramid import httpexceptions
|
||||||
|
@ -188,6 +188,32 @@ class CommonView(View):
|
||||||
"""
|
"""
|
||||||
raise Exception("Congratulations, you have triggered a bogus error.")
|
raise Exception("Congratulations, you have triggered a bogus error.")
|
||||||
|
|
||||||
|
def poser_setup(self):
|
||||||
|
if not self.request.is_root:
|
||||||
|
raise self.forbidden()
|
||||||
|
|
||||||
|
use_buefy = self.get_use_buefy()
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
app_title = self.rattail_config.app_title()
|
||||||
|
poser_handler = app.get_poser_handler()
|
||||||
|
|
||||||
|
if self.request.method == 'POST':
|
||||||
|
try:
|
||||||
|
path = poser_handler.make_poser_dir()
|
||||||
|
except Exception as error:
|
||||||
|
self.request.session.flash(simple_error(error), 'error')
|
||||||
|
else:
|
||||||
|
self.request.session.flash("Poser folder created at: {}".format(path))
|
||||||
|
self.request.session.flash("Please restart the web app!", 'warning')
|
||||||
|
return self.redirect(self.request.route_url('home'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'use_buefy': use_buefy,
|
||||||
|
'app_title': app_title,
|
||||||
|
'index_title': app_title,
|
||||||
|
'poser_dir': poser_handler.get_default_poser_dir(),
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
cls._defaults(config)
|
cls._defaults(config)
|
||||||
|
@ -249,6 +275,14 @@ class CommonView(View):
|
||||||
config.add_route('bogus_error', '/bogus-error')
|
config.add_route('bogus_error', '/bogus-error')
|
||||||
config.add_view(cls, attr='bogus_error', route_name='bogus_error', permission='errors.bogus')
|
config.add_view(cls, attr='bogus_error', route_name='bogus_error', permission='errors.bogus')
|
||||||
|
|
||||||
|
# make poser dir
|
||||||
|
config.add_route('poser_setup', '/poser-setup')
|
||||||
|
config.add_view(cls, attr='poser_setup',
|
||||||
|
route_name='poser_setup',
|
||||||
|
renderer='/poser/setup.mako',
|
||||||
|
# nb. root only
|
||||||
|
permission='admin')
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
CommonView.defaults(config)
|
CommonView.defaults(config)
|
||||||
|
|
|
@ -638,7 +638,7 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
self.creating = True
|
self.creating = True
|
||||||
if form is None:
|
if form is None:
|
||||||
form = self.make_form(self.get_model_class())
|
form = self.make_create_form()
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
if self.validate_form(form):
|
if self.validate_form(form):
|
||||||
# let save_create_form() return alternate object if necessary
|
# let save_create_form() return alternate object if necessary
|
||||||
|
@ -651,6 +651,9 @@ class MasterView(View):
|
||||||
context['dform'] = form.make_deform_form()
|
context['dform'] = form.make_deform_form()
|
||||||
return self.render_to_response(template, context)
|
return self.render_to_response(template, context)
|
||||||
|
|
||||||
|
def make_create_form(self):
|
||||||
|
return self.make_form(self.get_model_class())
|
||||||
|
|
||||||
def save_create_form(self, form):
|
def save_create_form(self, form):
|
||||||
uploads = self.normalize_uploads(form)
|
uploads = self.normalize_uploads(form)
|
||||||
self.before_create(form)
|
self.before_create(form)
|
||||||
|
@ -3618,7 +3621,10 @@ class MasterView(View):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def render_downloadable_file(self, obj, field):
|
def render_downloadable_file(self, obj, field):
|
||||||
filename = getattr(obj, field)
|
if hasattr(obj, field):
|
||||||
|
filename = getattr(obj, field)
|
||||||
|
else:
|
||||||
|
filename = obj[field]
|
||||||
if not filename:
|
if not filename:
|
||||||
return ""
|
return ""
|
||||||
path = self.download_path(obj, filename)
|
path = self.download_path(obj, filename)
|
||||||
|
|
31
tailbone/views/poser/__init__.py
Normal file
31
tailbone/views/poser/__init__.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2022 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail 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 3 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Poser Views
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
config.include('tailbone.views.poser.reports')
|
294
tailbone/views/poser/reports.py
Normal file
294
tailbone/views/poser/reports.py
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2022 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail 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 3 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Poser Report Views
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from rattail.util import simple_error
|
||||||
|
|
||||||
|
import colander
|
||||||
|
from deform import widget as dfwidget
|
||||||
|
from webhelpers2.html import HTML, tags
|
||||||
|
|
||||||
|
from tailbone.views import MasterView
|
||||||
|
|
||||||
|
|
||||||
|
class PoserReportView(MasterView):
|
||||||
|
"""
|
||||||
|
Master view for Poser reports
|
||||||
|
"""
|
||||||
|
normalized_model_name = 'poserreport'
|
||||||
|
model_title = "Poser Report"
|
||||||
|
model_key = 'report_key'
|
||||||
|
route_prefix = 'poser.reports'
|
||||||
|
url_prefix = '/poser/reports'
|
||||||
|
filterable = False
|
||||||
|
pageable = False
|
||||||
|
editable = False # TODO: should allow this somehow?
|
||||||
|
downloadable = True
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
'report_key': "Poser Key",
|
||||||
|
}
|
||||||
|
|
||||||
|
grid_columns = [
|
||||||
|
'report_key',
|
||||||
|
'report_name',
|
||||||
|
'description',
|
||||||
|
'error',
|
||||||
|
]
|
||||||
|
|
||||||
|
form_fields = [
|
||||||
|
'report_key',
|
||||||
|
'report_name',
|
||||||
|
'description',
|
||||||
|
'flavor',
|
||||||
|
'include_comments',
|
||||||
|
'module_file',
|
||||||
|
'module_file_path',
|
||||||
|
'error',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
super(PoserReportView, self).__init__(request)
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
self.poser_handler = app.get_poser_handler()
|
||||||
|
|
||||||
|
# nb. pre-load all reports b/c all views potentially need
|
||||||
|
# access to the data set
|
||||||
|
self.data = self.get_data()
|
||||||
|
|
||||||
|
def get_data(self, session=None):
|
||||||
|
if hasattr(self, 'data'):
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.poser_handler.get_all_reports()
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
self.request.session.flash(simple_error(error), 'error')
|
||||||
|
|
||||||
|
if not self.request.is_root:
|
||||||
|
self.request.session.flash("You must become root in order "
|
||||||
|
"to do Poser Setup.", 'error')
|
||||||
|
else:
|
||||||
|
link = tags.link_to("Poser Setup",
|
||||||
|
self.request.route_url('poser_setup'))
|
||||||
|
msg = HTML.literal("Please see the {} page.".format(link))
|
||||||
|
self.request.session.flash(msg, 'error')
|
||||||
|
return []
|
||||||
|
|
||||||
|
def configure_grid(self, g):
|
||||||
|
super(PoserReportView, self).configure_grid(g)
|
||||||
|
|
||||||
|
g.sorters['report_key'] = g.make_simple_sorter('report_key', foldcase=True)
|
||||||
|
g.sorters['report_name'] = g.make_simple_sorter('report_name', foldcase=True)
|
||||||
|
|
||||||
|
g.set_renderer('error', self.render_report_error)
|
||||||
|
|
||||||
|
g.set_sort_defaults('report_name')
|
||||||
|
|
||||||
|
g.set_link('report_key')
|
||||||
|
g.set_link('report_name')
|
||||||
|
g.set_link('description')
|
||||||
|
g.set_link('error')
|
||||||
|
|
||||||
|
g.set_searchable('report_key')
|
||||||
|
g.set_searchable('report_name')
|
||||||
|
g.set_searchable('description')
|
||||||
|
|
||||||
|
if self.request.has_perm('report_output.create'):
|
||||||
|
g.more_actions.append(self.make_action(
|
||||||
|
'generate', icon='arrow-circle-right',
|
||||||
|
url=self.get_generate_url))
|
||||||
|
|
||||||
|
def get_generate_url(self, report, i=None):
|
||||||
|
if not report.get('error'):
|
||||||
|
return self.request.route_url('generate_specific_report',
|
||||||
|
type_key=report['report'].type_key)
|
||||||
|
|
||||||
|
def render_report_error(self, report, field):
|
||||||
|
error = report.get('error')
|
||||||
|
if error:
|
||||||
|
return HTML.tag('span', class_='has-background-warning', c=[error])
|
||||||
|
|
||||||
|
def get_instance(self):
|
||||||
|
report_key = self.request.matchdict['report_key']
|
||||||
|
for report in self.get_data():
|
||||||
|
if report['report_key'] == report_key:
|
||||||
|
return report
|
||||||
|
raise self.notfound()
|
||||||
|
|
||||||
|
def get_instance_title(self, report):
|
||||||
|
return report['report_name']
|
||||||
|
|
||||||
|
def make_form_schema(self):
|
||||||
|
return PoserReportSchema()
|
||||||
|
|
||||||
|
def make_create_form(self):
|
||||||
|
return self.make_form({})
|
||||||
|
|
||||||
|
def save_create_form(self, form):
|
||||||
|
self.before_create(form)
|
||||||
|
|
||||||
|
report = self.poser_handler.make_report(
|
||||||
|
form.validated['report_key'],
|
||||||
|
form.validated['report_name'],
|
||||||
|
form.validated['description'],
|
||||||
|
flavor=form.validated['flavor'],
|
||||||
|
include_comments=form.validated['include_comments'])
|
||||||
|
|
||||||
|
return report
|
||||||
|
|
||||||
|
def configure_form(self, f):
|
||||||
|
super(PoserReportView, self).configure_form(f)
|
||||||
|
report = f.model_instance
|
||||||
|
|
||||||
|
# report_key
|
||||||
|
f.set_default('report_key', 'cool_widgets')
|
||||||
|
f.set_helptext('report_key', "Unique computer-friendly key for the report type.")
|
||||||
|
if self.creating:
|
||||||
|
f.set_validator('report_key', self.unique_report_key)
|
||||||
|
|
||||||
|
# report_name
|
||||||
|
f.set_default('report_name', "Cool Widgets Weekly")
|
||||||
|
f.set_helptext('report_name', "Human-friendly display name for the report.")
|
||||||
|
|
||||||
|
# description
|
||||||
|
f.set_default('description', "How many cool widgets we come across each week")
|
||||||
|
f.set_helptext('description', "Brief description of the report.")
|
||||||
|
|
||||||
|
# flavor
|
||||||
|
if self.creating:
|
||||||
|
f.set_helptext('flavor', "Determines the type of sample code to generate.")
|
||||||
|
flavors = self.poser_handler.get_supported_report_flavors()
|
||||||
|
values = [(key, flavor['description'])
|
||||||
|
for key, flavor in six.iteritems(flavors)]
|
||||||
|
f.set_widget('flavor', dfwidget.SelectWidget(values=values))
|
||||||
|
f.set_validator('flavor', colander.OneOf(flavors))
|
||||||
|
if flavors:
|
||||||
|
f.set_default('flavor', list(flavors)[0])
|
||||||
|
else:
|
||||||
|
f.remove('flavor')
|
||||||
|
|
||||||
|
# include_comments
|
||||||
|
if not self.creating:
|
||||||
|
f.remove('include_comments')
|
||||||
|
|
||||||
|
# module_file
|
||||||
|
if self.creating:
|
||||||
|
f.remove('module_file')
|
||||||
|
else:
|
||||||
|
# nb. set this key as workaround for render method, which
|
||||||
|
# expects object to have this field
|
||||||
|
report['module_file'] = os.path.basename(report['module_file_path'])
|
||||||
|
f.set_renderer('module_file', self.render_downloadable_file)
|
||||||
|
|
||||||
|
# error
|
||||||
|
if self.creating or not report.get('error'):
|
||||||
|
f.remove('error')
|
||||||
|
else:
|
||||||
|
f.set_renderer('error', self.render_report_error)
|
||||||
|
|
||||||
|
def unique_report_key(self, node, value):
|
||||||
|
for report in self.get_data():
|
||||||
|
if report['report_key'] == value:
|
||||||
|
raise node.raise_invalid("Poser report key must be unique")
|
||||||
|
|
||||||
|
def download_path(self, report, filename):
|
||||||
|
return report['module_file_path']
|
||||||
|
|
||||||
|
def delete_instance(self, report):
|
||||||
|
self.poser_handler.delete_report(report['report_key'])
|
||||||
|
|
||||||
|
def replace(self):
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
report = self.get_instance()
|
||||||
|
|
||||||
|
value = self.request.POST['replacement_module']
|
||||||
|
tempdir = app.make_temp_dir()
|
||||||
|
filepath = os.path.join(tempdir, os.path.basename(value.filename))
|
||||||
|
with open(filepath, 'wb') as f:
|
||||||
|
f.write(value.file.read())
|
||||||
|
|
||||||
|
try:
|
||||||
|
newreport = self.poser_handler.replace_report(report['report_key'],
|
||||||
|
filepath)
|
||||||
|
except Exception as error:
|
||||||
|
self.request.session.flash(simple_error(error), 'error')
|
||||||
|
else:
|
||||||
|
report = newreport
|
||||||
|
finally:
|
||||||
|
os.remove(filepath)
|
||||||
|
os.rmdir(tempdir)
|
||||||
|
|
||||||
|
return self.redirect(self.get_action_url('view', report))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def defaults(cls, config):
|
||||||
|
cls._poser_report_defaults(config)
|
||||||
|
cls._defaults(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _poser_report_defaults(cls, config):
|
||||||
|
route_prefix = cls.get_route_prefix()
|
||||||
|
instance_url_prefix = cls.get_instance_url_prefix()
|
||||||
|
|
||||||
|
# replace module
|
||||||
|
config.add_route('{}.replace'.format(route_prefix),
|
||||||
|
'{}/replace'.format(instance_url_prefix),
|
||||||
|
request_method='POST')
|
||||||
|
config.add_view(cls, attr='replace',
|
||||||
|
route_name='{}.replace'.format(route_prefix),
|
||||||
|
# TODO: requires root, should add custom permission?
|
||||||
|
permission='admin')
|
||||||
|
|
||||||
|
|
||||||
|
class PoserReportSchema(colander.MappingSchema):
|
||||||
|
|
||||||
|
report_key = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
report_name = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
description = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
flavor = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
include_comments = colander.SchemaNode(colander.Bool())
|
||||||
|
|
||||||
|
|
||||||
|
def defaults(config, **kwargs):
|
||||||
|
base = globals()
|
||||||
|
|
||||||
|
PoserReportView = kwargs.get('PoserReportView', base['PoserReportView'])
|
||||||
|
PoserReportView.defaults(config)
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
defaults(config)
|
|
@ -400,7 +400,8 @@ class ReportOutputView(ExportMasterView):
|
||||||
form = forms.Form(schema=schema, request=self.request,
|
form = forms.Form(schema=schema, request=self.request,
|
||||||
use_buefy=use_buefy, helptext=helptext)
|
use_buefy=use_buefy, helptext=helptext)
|
||||||
form.submit_label = "Generate this Report"
|
form.submit_label = "Generate this Report"
|
||||||
form.cancel_url = self.request.route_url('{}.create'.format(route_prefix))
|
form.cancel_url = self.request.get_referrer(
|
||||||
|
default=self.request.route_url('{}.create'.format(route_prefix)))
|
||||||
|
|
||||||
# must declare jquery support for date fields, ugh
|
# must declare jquery support for date fields, ugh
|
||||||
# TODO: obviously would be nice for this to be automatic?
|
# TODO: obviously would be nice for this to be automatic?
|
||||||
|
|
Loading…
Reference in a new issue