2025-01-11 21:35:06 -06:00
|
|
|
# -*- coding: utf-8; -*-
|
|
|
|
|
|
|
|
import datetime
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
|
|
|
|
from wuttjamaican.reports import Report
|
|
|
|
|
|
|
|
import colander
|
|
|
|
from pyramid.httpexceptions import HTTPNotFound
|
|
|
|
|
|
|
|
from wuttaweb.views import reports as mod
|
|
|
|
from wuttaweb.testing import WebTestCase
|
|
|
|
|
|
|
|
|
|
|
|
class SomeRandomReport(Report):
|
|
|
|
"""
|
|
|
|
This report shows something random.
|
|
|
|
"""
|
2025-08-31 12:26:43 -05:00
|
|
|
|
|
|
|
report_key = "testing_some_random"
|
2025-01-11 21:35:06 -06:00
|
|
|
report_title = "Random Test Report"
|
|
|
|
|
|
|
|
def add_params(self, schema):
|
|
|
|
|
2025-08-31 12:26:43 -05:00
|
|
|
schema.add(
|
|
|
|
colander.SchemaNode(colander.String(), name="foo", missing=colander.null)
|
|
|
|
)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
2025-08-31 12:26:43 -05:00
|
|
|
schema.add(
|
|
|
|
colander.SchemaNode(
|
|
|
|
colander.Date(), name="start_date", missing=colander.null
|
|
|
|
)
|
|
|
|
)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def get_output_columns(self):
|
2025-08-31 12:26:43 -05:00
|
|
|
return ["foo"]
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def make_data(self, params, **kwargs):
|
|
|
|
return {
|
2025-08-31 12:26:43 -05:00
|
|
|
"output_title": "Testing Output",
|
|
|
|
"data": [{"foo": "bar"}],
|
2025-01-11 21:35:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class TestReportViews(WebTestCase):
|
|
|
|
|
|
|
|
def make_view(self):
|
|
|
|
return mod.ReportView(self.request)
|
|
|
|
|
|
|
|
def test_includeme(self):
|
2025-08-31 12:26:43 -05:00
|
|
|
self.pyramid_config.include("wuttaweb.views.reports")
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_get_grid_data(self):
|
|
|
|
view = self.make_view()
|
2025-01-11 23:55:40 -06:00
|
|
|
providers = dict(self.app.providers)
|
2025-08-31 12:26:43 -05:00
|
|
|
providers["wuttatest"] = MagicMock(report_modules=["tests.views.test_reports"])
|
|
|
|
with patch.object(self.app, "providers", new=providers):
|
2025-01-11 23:55:40 -06:00
|
|
|
|
|
|
|
data = view.get_grid_data()
|
|
|
|
self.assertIsInstance(data, list)
|
2025-08-31 12:26:43 -05:00
|
|
|
self.assertTrue(data) # 1+ reports
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_normalize_report(self):
|
|
|
|
view = self.make_view()
|
|
|
|
report = SomeRandomReport(self.config)
|
|
|
|
normal = view.normalize_report(report)
|
2025-08-31 12:26:43 -05:00
|
|
|
help_text = normal.pop("help_text").strip()
|
2025-01-11 21:35:06 -06:00
|
|
|
self.assertEqual(help_text, "This report shows something random.")
|
2025-08-31 12:26:43 -05:00
|
|
|
self.assertEqual(
|
|
|
|
normal,
|
|
|
|
{
|
|
|
|
"report_key": "testing_some_random",
|
|
|
|
"report_title": "Random Test Report",
|
|
|
|
},
|
|
|
|
)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_configure_grid(self):
|
|
|
|
view = self.make_view()
|
|
|
|
grid = view.make_model_grid()
|
2025-08-31 12:26:43 -05:00
|
|
|
self.assertIn("report_title", grid.searchable_columns)
|
|
|
|
self.assertIn("help_text", grid.searchable_columns)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_get_instance(self):
|
|
|
|
view = self.make_view()
|
|
|
|
providers = {
|
2025-08-31 12:26:43 -05:00
|
|
|
"wuttatest": MagicMock(report_modules=["tests.views.test_reports"]),
|
2025-01-11 21:35:06 -06:00
|
|
|
}
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(self.app, "providers", new=providers):
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
# normal
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(
|
|
|
|
self.request, "matchdict", new={"report_key": "testing_some_random"}
|
|
|
|
):
|
2025-01-11 21:35:06 -06:00
|
|
|
report = view.get_instance()
|
|
|
|
self.assertIsInstance(report, dict)
|
2025-08-31 12:26:43 -05:00
|
|
|
self.assertEqual(report["report_key"], "testing_some_random")
|
|
|
|
self.assertEqual(report["report_title"], "Random Test Report")
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
# not found
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(
|
|
|
|
self.request, "matchdict", new={"report_key": "this-should_notEXIST"}
|
|
|
|
):
|
2025-01-11 21:35:06 -06:00
|
|
|
self.assertRaises(HTTPNotFound, view.get_instance)
|
|
|
|
|
|
|
|
def test_get_instance_title(self):
|
|
|
|
view = self.make_view()
|
2025-08-31 12:26:43 -05:00
|
|
|
result = view.get_instance_title({"report_title": "whatever"})
|
|
|
|
self.assertEqual(result, "whatever")
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_view(self):
|
2025-08-31 12:26:43 -05:00
|
|
|
self.pyramid_config.add_route("home", "/")
|
|
|
|
self.pyramid_config.add_route("login", "/auth/login")
|
|
|
|
self.pyramid_config.add_route("reports", "/reports/")
|
|
|
|
self.pyramid_config.add_route("reports.view", "/reports/{report_key}")
|
2025-01-11 21:35:06 -06:00
|
|
|
view = self.make_view()
|
|
|
|
providers = dict(self.app.providers)
|
2025-08-31 12:26:43 -05:00
|
|
|
providers["wuttatest"] = MagicMock(report_modules=["tests.views.test_reports"])
|
|
|
|
with patch.object(self.app, "providers", new=providers):
|
|
|
|
with patch.object(
|
|
|
|
self.request, "matchdict", new={"report_key": "testing_some_random"}
|
|
|
|
):
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
# initial view
|
|
|
|
response = view.view()
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# nb. there's a button in there somewhere, but no output title
|
|
|
|
self.assertIn("Run Report", response.text)
|
|
|
|
self.assertNotIn("Testing Output", response.text)
|
|
|
|
|
|
|
|
# run the report
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(
|
|
|
|
self.request,
|
|
|
|
"GET",
|
|
|
|
new={
|
|
|
|
"__start__": "start_date:mapping",
|
|
|
|
"date": "2025-01-11",
|
|
|
|
"__end__": "start_date",
|
|
|
|
},
|
|
|
|
):
|
2025-01-11 21:35:06 -06:00
|
|
|
response = view.view()
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# nb. there's a button in there somewhere, *and* an output title
|
|
|
|
self.assertIn("Run Report", response.text)
|
|
|
|
self.assertIn("Testing Output", response.text)
|
|
|
|
|
|
|
|
def test_configure_form(self):
|
|
|
|
view = self.make_view()
|
|
|
|
providers = dict(self.app.providers)
|
2025-08-31 12:26:43 -05:00
|
|
|
providers["wuttatest"] = MagicMock(report_modules=["tests.views.test_reports"])
|
|
|
|
with patch.object(self.app, "providers", new=providers):
|
2025-01-11 21:35:06 -06:00
|
|
|
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(
|
|
|
|
self.request, "matchdict", new={"report_key": "testing_some_random"}
|
|
|
|
):
|
2025-01-11 21:35:06 -06:00
|
|
|
report = view.get_instance()
|
|
|
|
form = view.make_model_form(report)
|
2025-08-31 12:26:43 -05:00
|
|
|
self.assertIn("help_text", form.readonly_fields)
|
|
|
|
self.assertIn("foo", form)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_normalize_columns(self):
|
|
|
|
view = self.make_view()
|
|
|
|
|
2025-08-31 12:26:43 -05:00
|
|
|
columns = view.normalize_columns(["foo"])
|
|
|
|
self.assertEqual(
|
|
|
|
columns,
|
|
|
|
[
|
|
|
|
{"name": "foo", "label": "foo"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
columns = view.normalize_columns([{"name": "foo"}])
|
|
|
|
self.assertEqual(
|
|
|
|
columns,
|
|
|
|
[
|
|
|
|
{"name": "foo", "label": "foo"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
columns = view.normalize_columns([{"name": "foo", "label": "FOO"}])
|
|
|
|
self.assertEqual(
|
|
|
|
columns,
|
|
|
|
[
|
|
|
|
{"name": "foo", "label": "FOO"},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
columns = view.normalize_columns(
|
|
|
|
[{"name": "foo", "label": "FOO", "numeric": True}]
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
columns,
|
|
|
|
[
|
|
|
|
{"name": "foo", "label": "FOO", "numeric": True},
|
|
|
|
],
|
|
|
|
)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_run_report(self):
|
|
|
|
view = self.make_view()
|
|
|
|
providers = dict(self.app.providers)
|
2025-08-31 12:26:43 -05:00
|
|
|
providers["wuttatest"] = MagicMock(report_modules=["tests.views.test_reports"])
|
|
|
|
with patch.object(self.app, "providers", new=providers):
|
2025-01-11 21:35:06 -06:00
|
|
|
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(
|
|
|
|
self.request, "matchdict", new={"report_key": "testing_some_random"}
|
|
|
|
):
|
|
|
|
report = view.report_handler.get_report("testing_some_random")
|
2025-01-11 21:35:06 -06:00
|
|
|
normal = view.normalize_report(report)
|
|
|
|
form = view.make_model_form(normal)
|
|
|
|
|
|
|
|
# typical
|
2025-08-31 12:26:43 -05:00
|
|
|
context = view.run_report(report, {"form": form})
|
|
|
|
self.assertEqual(
|
|
|
|
sorted(context["report_params"]), ["foo", "start_date"]
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
context["report_data"],
|
|
|
|
{
|
|
|
|
"output_title": "Testing Output",
|
|
|
|
"data": [{"foo": "bar"}],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assertIn("report_generated", context)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
# invalid params
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(self.request, "GET", new={"start_date": "NOT_GOOD"}):
|
|
|
|
context = view.run_report(report, {"form": form})
|
|
|
|
self.assertNotIn("report_params", context)
|
|
|
|
self.assertNotIn("report_data", context)
|
|
|
|
self.assertNotIn("report_generated", context)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
# custom formatter
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(report, "get_output_columns") as get_output_columns:
|
2025-01-11 21:35:06 -06:00
|
|
|
get_output_columns.return_value = [
|
2025-08-31 12:26:43 -05:00
|
|
|
"foo",
|
|
|
|
{
|
|
|
|
"name": "start_date",
|
|
|
|
"formatter": lambda val: "FORMATTED VALUE",
|
|
|
|
},
|
2025-01-11 21:35:06 -06:00
|
|
|
]
|
|
|
|
|
2025-08-31 12:26:43 -05:00
|
|
|
with patch.object(report, "make_data") as make_data:
|
2025-01-11 21:35:06 -06:00
|
|
|
make_data.return_value = [
|
2025-08-31 12:26:43 -05:00
|
|
|
{"foo": "bar", "start_date": datetime.date(2025, 1, 11)},
|
2025-01-11 21:35:06 -06:00
|
|
|
]
|
|
|
|
|
2025-08-31 12:26:43 -05:00
|
|
|
context = view.run_report(report, {"form": form})
|
2025-01-11 21:35:06 -06:00
|
|
|
get_output_columns.assert_called_once_with()
|
2025-08-31 12:26:43 -05:00
|
|
|
self.assertEqual(len(context["report_columns"]), 2)
|
|
|
|
self.assertEqual(context["report_columns"][0]["name"], "foo")
|
|
|
|
self.assertEqual(
|
|
|
|
context["report_columns"][1]["name"], "start_date"
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
context["report_data"],
|
|
|
|
{
|
|
|
|
"output_title": "Random Test Report",
|
|
|
|
"data": [
|
|
|
|
{"foo": "bar", "start_date": "FORMATTED VALUE"}
|
|
|
|
],
|
|
|
|
},
|
|
|
|
)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_download_data(self):
|
|
|
|
view = self.make_view()
|
|
|
|
providers = dict(self.app.providers)
|
2025-08-31 12:26:43 -05:00
|
|
|
providers["wuttatest"] = MagicMock(report_modules=["tests.views.test_reports"])
|
|
|
|
with patch.object(self.app, "providers", new=providers):
|
|
|
|
with patch.object(
|
|
|
|
self.request, "matchdict", new={"report_key": "testing_some_random"}
|
|
|
|
):
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
params, columns, data = view.get_download_data()
|
|
|
|
self.assertEqual(params, {})
|
2025-08-31 12:26:43 -05:00
|
|
|
self.assertEqual(columns, [{"name": "foo", "label": "foo"}])
|
|
|
|
self.assertEqual(
|
|
|
|
data,
|
|
|
|
{
|
|
|
|
"output_title": "Testing Output",
|
|
|
|
"data": [{"foo": "bar"}],
|
|
|
|
},
|
|
|
|
)
|
2025-01-11 21:35:06 -06:00
|
|
|
|
|
|
|
def test_download_path(self):
|
|
|
|
view = self.make_view()
|
2025-08-31 12:26:43 -05:00
|
|
|
data = {"output_title": "My Report"}
|
|
|
|
path = view.get_download_path(data, "csv")
|
|
|
|
self.assertTrue(path.endswith("My Report.csv"))
|