120 lines
4.0 KiB
Python
120 lines
4.0 KiB
Python
import logging
|
|
|
|
from celery import shared_task
|
|
from django.utils import timezone
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@shared_task
|
|
def process_scheduled_reports():
|
|
from apps.reports.models import ReportSchedule
|
|
|
|
now = timezone.now()
|
|
schedules = ReportSchedule.objects.filter(
|
|
is_active=True,
|
|
next_run_at__lte=now,
|
|
).select_related("report")
|
|
|
|
dispatched = 0
|
|
for schedule in schedules:
|
|
generate_and_deliver_report.delay(str(schedule.id))
|
|
dispatched += 1
|
|
|
|
logger.info(f"Dispatched {dispatched} scheduled reports")
|
|
return {"dispatched": dispatched}
|
|
|
|
|
|
@shared_task
|
|
def generate_and_deliver_report(schedule_id):
|
|
from apps.reports.models import ReportSchedule, GeneratedReport
|
|
from apps.reports.services import ReportBuilderService
|
|
from apps.notifications.services import NotificationService
|
|
|
|
try:
|
|
schedule = ReportSchedule.objects.select_related("report").get(id=schedule_id)
|
|
except ReportSchedule.DoesNotExist:
|
|
logger.error(f"ReportSchedule {schedule_id} not found")
|
|
return {"status": "error", "reason": "not_found"}
|
|
|
|
try:
|
|
report = schedule.report
|
|
|
|
report_data = ReportBuilderService.generate_report_data(
|
|
data_source=report.data_source,
|
|
filter_config=report.filter_config,
|
|
column_config=report.column_config,
|
|
grouping_config=report.grouping_config,
|
|
sort_config=report.sort_config,
|
|
)
|
|
summary = ReportBuilderService.generate_summary(
|
|
data_source=report.data_source,
|
|
filter_config=report.filter_config,
|
|
)
|
|
|
|
row_count = len(report_data.get("rows", []))
|
|
|
|
generated = GeneratedReport.objects.create(
|
|
saved_report=report,
|
|
name=report.name,
|
|
data_source=report.data_source,
|
|
filter_config=report.filter_config,
|
|
column_config=report.column_config,
|
|
grouping_config=report.grouping_config,
|
|
chart_config=report.chart_config,
|
|
data=report_data,
|
|
summary=summary,
|
|
chart_data={},
|
|
row_count=row_count,
|
|
hospital=report.hospital,
|
|
)
|
|
|
|
report.last_run_at = timezone.now()
|
|
report.last_run_count = row_count
|
|
report.save(update_fields=["last_run_at", "last_run_count"])
|
|
|
|
schedule.last_run_at = timezone.now()
|
|
schedule.last_error = ""
|
|
|
|
next_run = schedule.calculate_next_run()
|
|
if schedule.frequency == "once":
|
|
schedule.is_active = False
|
|
schedule.next_run_at = None
|
|
else:
|
|
schedule.next_run_at = next_run
|
|
|
|
schedule.save(update_fields=["last_run_at", "next_run_at", "is_active", "last_error"])
|
|
|
|
subject = f"Scheduled Report: {report.name}"
|
|
message = (
|
|
f"Report: {report.name}\n"
|
|
f"Data Source: {report.get_data_source_display()}\n"
|
|
f"Rows: {row_count}\n"
|
|
f"Generated: {timezone.now().strftime('%Y-%m-%d %H:%M')}\n\n"
|
|
f"View the full report in the PX360 dashboard."
|
|
)
|
|
|
|
for recipient in schedule.recipients:
|
|
try:
|
|
NotificationService.send_email(
|
|
email=recipient,
|
|
subject=subject,
|
|
message=message,
|
|
related_object=generated,
|
|
notification_type="report",
|
|
)
|
|
except Exception as email_err:
|
|
logger.error(f"Failed to send report email to {recipient}: {email_err}")
|
|
|
|
logger.info(f"Generated and delivered report for schedule {schedule_id}")
|
|
return {"status": "success", "generated_report_id": str(generated.id), "row_count": row_count}
|
|
|
|
except Exception as e:
|
|
error_msg = str(e)
|
|
logger.error(f"Error generating report for schedule {schedule_id}: {error_msg}", exc_info=True)
|
|
|
|
schedule.last_error = error_msg
|
|
schedule.save(update_fields=["last_error"])
|
|
|
|
return {"status": "error", "reason": error_msg}
|