Allow disabling, or per-day scheduling, of problem reports

This commit is contained in:
Lance Edgar 2022-11-19 17:44:09 -06:00
parent 163c65600d
commit e4392cd00a
3 changed files with 167 additions and 28 deletions

View file

@ -0,0 +1,17 @@
<div tal:define="name name|field.name;"
tal:omit-tag="">
<div tal:define="vmodel vmodel|'field_model_' + name;"
tal:omit-tag="">
<b-field grouped>
<input type="hidden" name="${name}"
tal:attributes=":value 'JSON.stringify('+vmodel+')';" />
<b-checkbox v-model="${vmodel}.day0">${day_labels[0]['abbr']}</b-checkbox>
<b-checkbox v-model="${vmodel}.day1">${day_labels[1]['abbr']}</b-checkbox>
<b-checkbox v-model="${vmodel}.day2">${day_labels[2]['abbr']}</b-checkbox>
<b-checkbox v-model="${vmodel}.day3">${day_labels[3]['abbr']}</b-checkbox>
<b-checkbox v-model="${vmodel}.day4">${day_labels[4]['abbr']}</b-checkbox>
<b-checkbox v-model="${vmodel}.day5">${day_labels[5]['abbr']}</b-checkbox>
<b-checkbox v-model="${vmodel}.day6">${day_labels[6]['abbr']}</b-checkbox>
</b-field>
</div>
</div>

View file

@ -66,6 +66,10 @@
${parent.modify_this_page_vars()} ${parent.modify_this_page_vars()}
<script type="text/javascript"> <script type="text/javascript">
% if weekdays_data is not Undefined:
${form.component_studly}Data.weekdaysData = ${json.dumps(weekdays_data)|n}
% endif
ThisPageData.runReportShowDialog = false ThisPageData.runReportShowDialog = false
ThisPageData.runReportSubmitting = false ThisPageData.runReportSubmitting = false

View file

@ -26,6 +26,8 @@ Reporting views
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import calendar
import json
import re import re
import datetime import datetime
import logging import logging
@ -563,7 +565,6 @@ class ProblemReportView(MasterView):
url_prefix = '/reports/problems' url_prefix = '/reports/problems'
creatable = False creatable = False
editable = False
deletable = False deletable = False
filterable = False filterable = False
pageable = False pageable = False
@ -571,6 +572,7 @@ class ProblemReportView(MasterView):
labels = { labels = {
'system_key': "System", 'system_key': "System",
'days': "Schedule",
} }
grid_columns = [ grid_columns = [
@ -580,32 +582,17 @@ class ProblemReportView(MasterView):
'email_recipients', 'email_recipients',
] ]
form_fields = [
'system_key',
'problem_title',
'email_key',
'email_recipients',
]
def __init__(self, request): def __init__(self, request):
super(ProblemReportView, self).__init__(request) super(ProblemReportView, self).__init__(request)
app = self.get_rattail_app() app = self.get_rattail_app()
self.handler = app.get_problem_report_handler() self.problem_handler = app.get_problem_report_handler()
# TODO: deprecate / remove this
self.handler = self.problem_handler
def normalize(self, report, keep_report=True): def normalize(self, report, keep_report=True):
data = { data = self.problem_handler.normalize_problem_report(
'system_key': report.system_key, report, include_schedule=True, include_recipients=True)
'problem_key': report.problem_key,
'problem_title': report.problem_title,
'email_key': self.handler.get_email_key(report),
}
app = self.get_rattail_app()
handler = app.get_email_handler()
email = handler.get_email(data['email_key'])
data['email_recipients'] = email.get_recips('all')
if keep_report: if keep_report:
data['_report'] = report data['_report'] = report
return data return data
@ -653,28 +640,159 @@ class ProblemReportView(MasterView):
def configure_form(self, f): def configure_form(self, f):
super(ProblemReportView, self).configure_form(f) super(ProblemReportView, self).configure_form(f)
# email_*
if self.editing:
f.remove('email_key',
'email_recipients')
else:
f.set_renderer('email_key', self.render_email_key) f.set_renderer('email_key', self.render_email_key)
f.set_renderer('email_recipients', self.render_email_recipients) f.set_renderer('email_recipients', self.render_email_recipients)
# enabled
f.set_type('enabled', 'boolean')
# days
f.set_renderer('days', self.render_days)
f.set_widget('days', DaysWidget())
f.set_vuejs_field_converter('days', self.convert_vuejs_days)
f.set_helptext('days', "NB. enabling a given day means you want the "
"report to be available that morning (assuming that "
"reports run overnight)")
# only allow edit of certain fields
if self.editing:
editable = ('enabled', 'days')
for field in f:
if field not in editable:
f.set_readonly(field)
def convert_vuejs_days(self, days):
days = dict(days)
for key in days:
if days[key] is colander.null:
days[key] = 'null'
return days
def render_email_recipients(self, report_info, field): def render_email_recipients(self, report_info, field):
recips = report_info['email_recipients'] recips = report_info['email_recipients']
return ', '.join(recips) return ', '.join(recips)
def render_days(self, report_info, field):
g = self.get_grid_factory()('days', [],
columns=['weekday_name', 'enabled'],
labels={'weekday_name': "Weekday"})
return HTML.literal(g.render_buefy_table_element(data_prop='weekdaysData'))
def template_kwargs_view(self, **kwargs):
kwargs = super(ProblemReportView, self).template_kwargs_view(**kwargs)
report_info = kwargs['instance']
data = []
for i in range(7):
data.append({
'weekday': i,
'weekday_name': calendar.day_name[i],
'enabled': "Yes" if report_info['day{}'.format(i)] else "No",
})
kwargs['weekdays_data'] = data
return kwargs
def save_edit_form(self, form):
app = self.get_rattail_app()
session = self.Session()
data = form.validated
report = self.get_instance()
key = '{}.{}'.format(report['system_key'],
report['problem_key'])
app.save_setting(session, 'rattail.problems.{}.enabled'.format(key),
six.text_type(data['enabled']).lower())
for i in range(7):
daykey = 'day{}'.format(i)
app.save_setting(session, 'rattail.problems.{}.{}'.format(key, daykey),
six.text_type(data['days'][daykey]).lower())
def execute_instance(self, report_info, user, progress=None, **kwargs): def execute_instance(self, report_info, user, progress=None, **kwargs):
report = report_info['_report'] report = report_info['_report']
problems = self.handler.run_problem_report(report, progress=progress) problems = self.handler.run_problem_report(report, progress=progress,
force=True)
return "Report found {} problems".format(len(problems)) return "Report found {} problems".format(len(problems))
class ProblemReportDays(colander.MappingSchema):
day0 = colander.SchemaNode(colander.Boolean(),
title=calendar.day_abbr[0])
day1 = colander.SchemaNode(colander.Boolean(),
title=calendar.day_abbr[1])
day2 = colander.SchemaNode(colander.Boolean(),
title=calendar.day_abbr[2])
day3 = colander.SchemaNode(colander.Boolean(),
title=calendar.day_abbr[3])
day4 = colander.SchemaNode(colander.Boolean(),
title=calendar.day_abbr[4])
day5 = colander.SchemaNode(colander.Boolean(),
title=calendar.day_abbr[5])
day6 = colander.SchemaNode(colander.Boolean(),
title=calendar.day_abbr[6])
class ProblemReportSchema(colander.MappingSchema): class ProblemReportSchema(colander.MappingSchema):
system_key = colander.SchemaNode(colander.String()) system_key = colander.SchemaNode(colander.String(),
missing=colander.null)
problem_key = colander.SchemaNode(colander.String()) problem_key = colander.SchemaNode(colander.String(),
missing=colander.null)
problem_title = colander.SchemaNode(colander.String()) problem_title = colander.SchemaNode(colander.String(),
missing=colander.null)
email_key = colander.SchemaNode(colander.String()) description = colander.SchemaNode(colander.String(),
missing=colander.null)
email_key = colander.SchemaNode(colander.String(),
missing=colander.null)
email_recipients = colander.SchemaNode(colander.String(),
missing=colander.null)
enabled = colander.SchemaNode(colander.Boolean())
days = ProblemReportDays()
class DaysWidget(dfwidget.Widget):
template = 'problem_report_days'
def serialize(self, field, cstruct, **kw):
if cstruct in (colander.null, None):
cstruct = ""
readonly = kw.get("readonly", self.readonly)
template = self.template
values = dict(kw)
if 'day_labels' not in values:
values['day_labels'] = self.get_day_labels()
values = self.get_template_values(field, cstruct, values)
return field.renderer(template, **values)
def get_day_labels(self):
labels = {}
for i in range(7):
labels[i] = {'name': calendar.day_name[i],
'abbr': calendar.day_abbr[i]}
return labels
def deserialize(self, field, pstruct):
from deform.compat import string_types
if pstruct is colander.null:
return colander.null
elif not isinstance(pstruct, string_types):
raise colander.Invalid(field.schema, "Pstruct is not a string")
pstruct = json.loads(pstruct)
return pstruct
def add_routes(config): def add_routes(config):