From b991534aef9d5116b31c244d3eb24e6729981fb3 Mon Sep 17 00:00:00 2001
From: Rieven {% translate "Report schedule" %}
or monthly. If you need the report just for a single occasion, select the one-time option.
{% endblocktranslate %}
- {% blocktranslate trimmed %} - Define a custom name format for your report(s). This format will be applied to all generated - (sub)reports. - {% endblocktranslate %} -
-- {% blocktranslate trimmed %} - To make the report names more descriptive, you can include placeholders for the - object name, the report type and/or the reference date. For subreports and reports over a single object, - use the placeholder "${ooi}" for the object name, "${report_type}" for the report type and use a - Python strftime code for the reference - date. For reports over multiple objects, use "${oois_count}" for the number of objects in the report. - {% endblocktranslate %} -
-- {% blocktranslate trimmed %} - For example, the format "${report_type} for ${ooi} at %x" could generate: - "DNS Report for example.com at 01/01/25". - {% endblocktranslate %} -
-{% else %} -- {% blocktranslate trimmed %} - Give your report(s) a custom name and optionally add the reports' reference date - to the name. To do so you can select a standard option or use a Python - strftime code in the report name. - {% endblocktranslate %} -
-{% endif %} ++ {% blocktranslate trimmed %} + Define a custom name format for your report(s). This format will be applied to all generated + (sub)reports. + {% endblocktranslate %} +
++ {% blocktranslate trimmed %} + To make the report names more descriptive, you can include placeholders for the + object name, the report type and/or the reference date. For subreports and reports over a single object, + use the placeholder "${ooi}" for the object name, "${report_type}" for the report type and use a + Python strftime code for the reference + date. For reports over multiple objects, use "${oois_count}" for the number of objects in the report. + {% endblocktranslate %} +
++ {% blocktranslate trimmed %} + For example, the format "${report_type} for ${ooi} at %x" could generate: + "DNS Report for example.com at 01/01/25". + {% endblocktranslate %} +
diff --git a/rocky/reports/views/base.py b/rocky/reports/views/base.py index 2d8514a3f18..d019323366d 100644 --- a/rocky/reports/views/base.py +++ b/rocky/reports/views/base.py @@ -267,8 +267,15 @@ def is_scheduled_report(self) -> bool: recurrence_choice = self.request.POST.get("choose_recurrence", "once") return recurrence_choice == "repeat" + def is_single_report(self) -> bool: + return len(self.get_report_type_ids()) == 1 + def create_report_recipe( - self, report_name_format: str, subreport_name_format: str, parent_report_type: str | None, schedule: str + self, + report_name_format: str, + subreport_name_format: str | None, + parent_report_type: str | None, + schedule: str | None, ) -> ReportRecipe: report_recipe = ReportRecipe( recipe_id=uuid4(), @@ -302,6 +309,7 @@ def get_context_data(self, **kwargs): context["all_oois_selected"] = self.all_oois_selected() context["selected_oois"] = self.selected_oois context["selected_report_types"] = self.selected_report_types + context["is_single_report"] = self.is_single_report() return context @@ -449,132 +457,64 @@ def get_context_data(self, **kwargs): class ReportFinalSettingsView(BaseReportView, ReportBreadcrumbs, SchedulerView, TemplateView): report_type: type[BaseReport] | None = None task_type = "report" - is_a_scheduled_report = False def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: if not self.get_report_type_ids(): messages.error(request, self.NONE_REPORT_TYPE_SELECTION_MESSAGE) return PostRedirect(self.get_previous()) - - self.is_a_scheduled_report = self.is_scheduled_report() - return super().get(request, *args, **kwargs) - @staticmethod - def create_report_names(oois: list[OOI], report_types: list[type[BaseReport]]) -> dict[str, str]: - reports = {} - oois_count = len(oois) - report_types_count = len(report_types) - ooi = oois[0].human_readable - report_type = report_types[0].name - - # Create name for parent report - if not (report_types_count == 1 and oois_count == 1): - if report_types_count > 1 and oois_count > 1: - name = _("Concatenated Report for {oois_count} objects").format( - report_type=report_type, oois_count=oois_count - ) - elif report_types_count > 1 and oois_count == 1: - name = _("Concatenated Report for {ooi}").format(ooi=ooi) - elif report_types_count == 1 and oois_count > 1: - name = _("{report_type} for {oois_count} objects").format( - report_type=report_type, oois_count=oois_count - ) - reports[name] = "" - - # Create name for subreports or single reports - for ooi in oois: - for report_type_ in report_types: - name = _("{report_type} for {ooi}").format(report_type=report_type_.name, ooi=ooi.human_readable) - reports[name] = "" - - return reports - - def get_report_names(self) -> dict[str, str] | list[str]: - if self.report_type is not None and self.report_type == AggregateOrganisationReport: - return [_("Aggregate Report")] - if self.report_type is not None and self.report_type == MultiOrganizationReport: - return [_("Sector Report")] - - return self.create_report_names(self.get_oois(), self.get_report_types()) + def get_initial_report_name(self) -> str: + oois = self.get_total_oois() + if oois == 1: + return "${report_type} for ${ooi}" + if oois > 1: + return "${report_type} for ${oois_count} objects" + return "" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["reports"] = self.get_report_names() - + context["initial_report_name"] = self.get_initial_report_name() context["report_schedule_form_start_date"] = self.get_report_schedule_form_start_date() - context["report_schedule_form_recurrence_choice"] = self.get_report_schedule_form_recurrence_choice() context["report_schedule_form_recurrence"] = self.get_report_schedule_form_recurrence() - - context["report_parent_name_form"] = self.get_report_parent_name_form() - context["report_child_name_form"] = self.get_report_child_name_form() - - context["is_scheduled_report"] = self.is_a_scheduled_report - - context["created_at"] = datetime.now() return context class SaveReportView(BaseReportView, ReportBreadcrumbs, SchedulerView): task_type = "report" + def get_parent_report_type(self): + if self.report_type is not None: + return self.report_type.id + if not self.is_single_report(): + return ConcatenatedReport.id + return self.report_type + def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: - old_report_names = request.POST.getlist("old_report_name") - report_names = request.POST.getlist("report_name", []) - reference_dates = request.POST.getlist("reference_date") - - if not self.is_scheduled_report() and report_names: - final_report_names = list(zip(old_report_names, self.finalise_report_names(report_names, reference_dates))) - report_ooi = self.save_report(final_report_names) - - return redirect( - reverse("view_report", kwargs={"organization_code": self.organization.code}) - + "?" - + urlencode({"report_id": report_ooi.reference}) - ) - elif self.is_scheduled_report(): - report_name_format = request.POST.get("parent_report_name", "") - subreport_name_format = request.POST.get("child_report_name", "") - recurrence = request.POST.get("recurrence", "") - deadline_at = request.POST.get("start_date", datetime.now(timezone.utc).date()) - - parent_report_type = None - if self.report_type is not None: - parent_report_type = self.report_type.id - elif not self.report_type and subreport_name_format: - parent_report_type = ConcatenatedReport.id - - schedule = self.convert_recurrence_to_cron_expressions(recurrence) - - report_recipe = self.create_report_recipe( - report_name_format, subreport_name_format, parent_report_type, schedule - ) + deadline_at = request.POST.get("start_date") + start_date_time: datetime = ( + datetime.now(timezone.utc) if deadline_at is None else datetime.fromisoformat(deadline_at) + ) - self.create_report_schedule(report_recipe, deadline_at) + recurrence = request.POST.get("recurrence") + schedule = ( + self.convert_schedule_to_cron_expressions(start_date_time, recurrence) + if recurrence is not None and recurrence != "once" + else None + ) - return redirect(reverse("scheduled_reports", kwargs={"organization_code": self.organization.code})) + parent_report_type = self.get_parent_report_type() - messages.error(request, _("Empty name should not be possible.")) - return PostRedirect(self.get_previous()) + parent_report_name_format = request.POST.get("parent_report_name_format", "") + subreport_name_format = request.POST.get("subreport_name_format") - @staticmethod - def finalise_report_names(report_names: list[str], reference_dates: list[str]) -> list[str]: - final_report_names = [] - - if len(report_names) == len(reference_dates): - for index, report_name in enumerate(report_names): - date_format = "" - if reference_dates[index] and reference_dates[index] != "": - date_format = " - " - if reference_dates[index] == "week": - date_format += _("Week %W, %Y") - else: - date_format += reference_dates[index] - final_report_name = f"{report_name} {date_format}".strip() - final_report_names.append(final_report_name) - if not final_report_names: - return report_names - return final_report_names + report_recipe = self.create_report_recipe( + parent_report_name_format, subreport_name_format, parent_report_type, schedule + ) + + self.create_report_schedule(report_recipe, start_date_time.isoformat()) + + return redirect(reverse("scheduled_reports", kwargs={"organization_code": self.organization.code})) class ViewReportView(ObservedAtMixin, OrganizationView, TemplateView): diff --git a/rocky/rocky/views/scheduler.py b/rocky/rocky/views/scheduler.py index f2284980693..5021ba18d8b 100644 --- a/rocky/rocky/views/scheduler.py +++ b/rocky/rocky/views/scheduler.py @@ -1,5 +1,5 @@ import uuid -from datetime import datetime, timezone +from datetime import datetime from typing import Any from django.contrib import messages @@ -7,8 +7,6 @@ from django.utils.translation import gettext_lazy as _ from katalogus.client import Boefje, Normalizer from reports.forms import ( - ChildReportNameForm, - ParentReportNameForm, ReportRecurrenceChoiceForm, ReportScheduleRecurrenceForm, ReportScheduleStartDateChoiceForm, @@ -53,9 +51,6 @@ class SchedulerView(OctopoesView): report_schedule_form_recurrence_choice = ReportRecurrenceChoiceForm # once or repeat report_schedule_form_recurrence = ReportScheduleRecurrenceForm # select interval (daily, weekly, etc..) - report_parent_name_form = ParentReportNameForm # parent name format - report_child_name_form = ChildReportNameForm # child name format - def setup(self, request, *args, **kwargs): super().setup(request, *args, **kwargs) self.scheduler_client = scheduler_client(self.organization.code) @@ -105,12 +100,6 @@ def get_report_schedule_form_recurrence_choice(self): def get_report_schedule_form_recurrence(self): return self.report_schedule_form_recurrence() - def get_report_parent_name_form(self): - return self.report_parent_name_form() - - def get_report_child_name_form(self): - return self.report_child_name_form() - def get_task_details(self, task_id: str) -> Task | None: task = self.scheduler_client.get_task_details(task_id) @@ -262,28 +251,31 @@ def run_boefje_for_oois(self, boefje: Boefje, oois: list[OOI]) -> None: except SchedulerError as error: messages.error(self.request, error.message) - def convert_recurrence_to_cron_expressions(self, recurrence: str) -> str: + def convert_schedule_to_cron_expressions(self, start_date_time: datetime, recurrence: str) -> str: """ Because there is no time defined for the start date, we use midnight 00:00 for all expressions. """ - start_date = datetime.now(tz=timezone.utc).date() # for now, not set by user - - if start_date and recurrence: - day = start_date.day - month = start_date.month - week = start_date.strftime("%w").upper() # ex. 4 + if start_date_time and recurrence: + day = start_date_time.day + month = start_date_time.month + week = start_date_time.strftime("%w").upper() # ex. 4 + hour = start_date_time.hour + minute = start_date_time.minute cron_expr = { - "daily": "0 0 * * *", # Recurres every day at 00:00 - "weekly": f"0 0 * * {week}", # Recurres every week on the {week} at 00:00 - "yearly": f"0 0 {day} {month} *", # Recurres every year on the {day} of the {month} at 00:00 + "daily": f"{minute} {hour} * * *", # Recurres every day at the selected time + "weekly": f"{minute} {hour} * * {week}", # Recurres every week on the {week} at the selected time + "yearly": f"{minute} {hour} {day} {month} *", + # Recurres every year on the {day} of the {month} at the selected time } if 28 <= day <= 31: - cron_expr["monthly"] = "0 0 28-31 * *" + cron_expr["monthly"] = f"{minute} {hour} L * *" else: - cron_expr["monthly"] = f"0 0 {day} * *" # Recurres on the exact {day} of the month at 00:00 + cron_expr["monthly"] = ( + f"{minute} {hour} {day} * *" # Recurres on the exact {day} of the month at the selected time + ) return cron_expr.get(recurrence, "") return "" From 7351eda0df8a9cd67948ed2a4dcfd457c802610b Mon Sep 17 00:00:00 2001 From: Rieven