from datetime import datetime from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session from db.models.drivetask import DriveTask from db.models.user import User from db.models.fuelingtask import FuelingTask from db.models.maintenancejob import MaintenanceJob from db.repository.maintenancejob import calculate_total_cost from reportlab.lib import colors from reportlab.lib.pagesizes import letter from reportlab.platypus import ( SimpleDocTemplate, Table, TableStyle, Paragraph, Image, PageBreak, ) from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.units import inch from reportlab.graphics.shapes import Drawing from reportlab.graphics.charts.linecharts import HorizontalLineChart from reportlab.graphics.widgets.markers import makeMarker from reportlab.platypus import Spacer from reportlab.graphics.charts.axes import CategoryAxis from dateutil import parser def get_repot_jobsdone_by_driver(driver_id: int, db: Session): driver = db.query(User).filter(User.Id == driver_id).first() if not driver: return "notdriver" if driver.Role != "Driver": return "notdriver" tasks = ( db.query(DriveTask) .filter((DriveTask.DriverId == driver_id) & (DriveTask.Status == "Completed")) .all() ) # order tasks by completion date tasks.sort(key=lambda x: x.EndDateTime) # for each task, add TOTAL distance driven at the time of completion dist = 0 taskslist = [] for task in tasks: dist += task.DistanceCovered taskslist.append(task.__dict__) taskslist[-1]["DistanceAtTime"] = dist res = {} res["tasks"] = taskslist res["Driver"] = driver.__dict__ res["Driver"]["AssignedVehicle"] = driver.vehicle dist = 0 for task in tasks: dist += task.DistanceCovered res["TotalDistance"] = dist timespent = 0.0 for task in tasks: edate = task.EndDateTime sdate = task.StartDateTime if edate is None or sdate is None: continue if type(edate) == str: edate = datetime.strptime(edate, "%Y-%m-%d %H:%M:%S.%f") timetaken = edate - sdate timespent += timetaken.total_seconds() # time spent in hours and minutes hours = timespent // 3600 minutes = (timespent % 3600) // 60 res["TotalTime"] = str(hours) + " hours " + str(minutes) + " minutes" # add fuel expenditures fuel = db.query(FuelingTask).filter(FuelingTask.DriverId == driver_id).all() fuel.sort(key=lambda x: x.Date) totalfuelspent = 0 fuelspent = [] for f in fuel: fuelpoint = {} totalfuelspent += f.FuelRefilled fuelpoint["fuelrefilled"] = f.FuelRefilled fuelpoint["total"] = totalfuelspent fuelpoint["date"] = f.Date fuelspent.append(fuelpoint) res["FuelSpent"] = fuelspent res["TotalFuelSpent"] = totalfuelspent # add maintenance costs mainten = ( db.query(MaintenanceJob) .filter(MaintenanceJob.VehicleDriverId == driver_id) .all() ) mainten.sort(key=lambda x: x.Date) totalmaintenspent = 0 maintenspent = [] for m in mainten: maintenpoint = {} cost = calculate_total_cost(m.CarParts) totalmaintenspent += cost maintenpoint["cost"] = cost maintenpoint["total"] = totalmaintenspent maintenpoint["date"] = m.Date maintenspent.append(maintenpoint) res["MaintenanceSpent"] = maintenspent res["TotalMaintenanceSpent"] = totalmaintenspent return res def get_pdf(driver_id: int, db: Session): report_data = get_repot_jobsdone_by_driver(driver_id, db) if report_data == "notdriver": return "notdriver" # Assuming your report data is stored in a variable named 'report_data' # Create a PDF document pdf_filename = "report1.pdf" document = SimpleDocTemplate(pdf_filename, pagesize=letter) # Create a list to hold the contents of the PDF content = [] # Add a title to the PDF title_style = getSampleStyleSheet()["Title"] title = Paragraph("Driver Report", title_style) content.append(title) content.append(Paragraph("
", title_style)) print(report_data["Driver"]) # Add detailed information about the driver driver_info = [ ["Driver Name", report_data["Driver"]["Name"]], ["Last Name", report_data["Driver"]["LastName"]], ["Government ID", report_data["Driver"]["GovernmentId"]], ["Email", report_data["Driver"]["Email"]], ["Contact Number", report_data["Driver"]["ContactNumber"]], ["Address", report_data["Driver"]["Address"]], ["Role", report_data["Driver"]["Role"]], ] driver_table_style = TableStyle( [ ("ALIGN", (0, 0), (-1, -1), "LEFT"), ("BOX", (0, 0), (-1, -1), 1, colors.black), ] ) driver_table = Table(driver_info, style=driver_table_style) content.append(Paragraph("

", title_style)) vehicle = report_data["Driver"]["AssignedVehicle"].__dict__ print(vehicle) if ( vehicle["CurrentLocation"] is None or vehicle["CurrentLocation"] == "" or vehicle["CurrentLocation"] == False or len(vehicle["CurrentLocation"]) == 0 ): vehicle["CurrentLocation"] = ["Unknown", "Unknown"] loc = vehicle["CurrentLocation"][0] if len(vehicle["CurrentLocation"]) > 1: loc += ", " + vehicle["CurrentLocation"][1] # Add vehicle details vehicle_info = [ ["Vehicle Type", vehicle["Type"]], ["Vehicle Model", vehicle["Model"]], ["Year", vehicle["Year"]], ["License Plate", vehicle["LicensePlate"]], ["Current Location", loc], ["Mileage", vehicle["Mileage"]], ["Capacity", vehicle["Capacity"]], ] vehicle_table_style = TableStyle( [ ("ALIGN", (0, 0), (-1, -1), "LEFT"), ("BOX", (0, 0), (-1, -1), 1, colors.black), ] ) vehicle_table = Table(vehicle_info, style=vehicle_table_style) content.append(Spacer(1, 12)) # Add space between the tables content.append(Table([[driver_table, vehicle_table]])) content.append(Paragraph("

", title_style)) total_info = [ ["Total Tasks Done", report_data["tasks"].__len__()], ["Total Fuel Spent", report_data["TotalFuelSpent"]], ["Total Time Spent", report_data["TotalTime"]], ["Total Distance Driven", report_data["TotalDistance"]], ["Total Money Spent", report_data["TotalMaintenanceSpent"]], ] total_table_style = TableStyle( [ ("ALIGN", (0, 0), (-1, -1), "LEFT"), ("BOX", (0, 0), (-1, -1), 1, colors.black), ] ) total_table = Table(total_info, style=total_table_style) content.append(total_table) content.append(Paragraph("

", title_style)) content.append(PageBreak()) title_style = getSampleStyleSheet()["Heading1"] content.append(Paragraph("Driving Tasks", title_style)) title_style = getSampleStyleSheet()["Heading2"] content.append(Paragraph("Distance Driven over Time", title_style)) tasks_data = report_data["tasks"] if tasks_data.__len__() == 0: content.append(Paragraph("No tasks done.", title_style)) else: task_values = [entry["DistanceAtTime"] for entry in tasks_data] task_dates = [ # if type(entry["EndDateTime"]) == str parse, else just use parser.parse(entry["EndDateTime"]).strftime("%m/%d/%Y, %H:%M:%S") if type(entry["EndDateTime"]) == str else entry["EndDateTime"].strftime("%m/%d/%Y, %H:%M:%S") for entry in tasks_data ] drawing = Drawing(width=400, height=200) chart = HorizontalLineChart() chart.x = 0 chart.y = 50 chart.width = 500 chart.height = 125 chart.data = [task_values] chart.categoryAxis.labels.boxAnchor = "e" chart.valueAxis.valueMin = 0 chart.valueAxis.valueMax = max(task_values) + 10 chart.lines[0].strokeColor = colors.blue chart.lines[0].strokeWidth = 2 chart.categoryAxis.categoryNames = [str(date) for date in task_dates] chart.categoryAxis.labels.angle = 90 drawing.add(chart) # Add space between the tables content.append(drawing) content.append(Paragraph("

", title_style)) content.append(Paragraph("

", title_style)) content.append(Paragraph("

", title_style)) # Add tasks information title_style = getSampleStyleSheet()["Heading2"] content.append(Paragraph("All DriveTasks", title_style)) tasks_header = [ "Task ID", "Description", "Start Location", "End Location", "Distance Covered", "Status", "Start DateTime", "End DateTime", ] tasks_info = [] for task in tasks_data: task_info = [ f"{task['Id']}", task["Description"], f"{task['StartLocation'][0]}, {task['StartLocation'][1]}", f"{task['EndLocation'][0]}, {task['EndLocation'][1]}", "{} km".format(task["DistanceCovered"]), task["Status"], task["StartDateTime"].strftime("%m/%d/%Y, %H:%M:%S"), parser.parse(task["EndDateTime"]).strftime("%m/%d/%Y, %H:%M:%S") if type(task["EndDateTime"]) == str else task["EndDateTime"].strftime("%m/%d/%Y, %H:%M:%S"), ] tasks_info.append(task_info) # Define the styles styles = getSampleStyleSheet() normal_style = styles["Normal"] # Convert strings to Paragraphs to allow line breaks for row in tasks_info: for i, cell in enumerate(row): row[i] = Paragraph(cell, normal_style) tasks_table_style = TableStyle( [ ("BACKGROUND", (0, 0), (-1, 0), colors.grey), ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke), ("ALIGN", (0, 0), (-1, -1), "CENTER"), ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), ("BOTTOMPADDING", (0, 0), (-1, 0), 12), ("BACKGROUND", (0, 1), (-1, -1), colors.beige), ("GRID", (0, 0), (-1, -1), 1, colors.black), ] ) tasks_table = Table([tasks_header] + tasks_info, style=tasks_table_style) content.append(tasks_table) content.append(PageBreak()) title_style = getSampleStyleSheet()["Heading1"] content.append(Paragraph("Fuel Spent Over Time", title_style)) # Add chart of fuel spendings fuel_spent_data = report_data["FuelSpent"] if fuel_spent_data.__len__() == 0: content.append(Paragraph("No fuel spent.", title_style)) else: fuel_values = [entry["total"] for entry in fuel_spent_data] fuel_dates = [entry["date"] for entry in fuel_spent_data] drawing = Drawing(width=400, height=200) chart = HorizontalLineChart() chart.x = 0 chart.y = 50 chart.width = 500 chart.height = 125 chart.data = [fuel_values] chart.categoryAxis.labels.boxAnchor = "e" chart.valueAxis.valueMin = 0 chart.valueAxis.valueMax = max(fuel_values) + 10 chart.lines[0].strokeColor = colors.blue chart.lines[0].strokeWidth = 2 chart.categoryAxis.categoryNames = [str(date) for date in fuel_dates] chart.categoryAxis.labels.angle = 90 drawing.add(chart) # Add space between the tables content.append(drawing) content.append(Paragraph("

", title_style)) fuel_header = ["Date", "Fuel Refilled", "Total Fuel Spent"] fuel_info = [] for fuel in fuel_spent_data: fuel_info1 = [ fuel["date"].strftime("%m/%d/%Y, %H:%M:%S"), "{} L".format(fuel["fuelrefilled"]), "{} L".format(fuel["total"]), ] fuel_info.append(fuel_info1) # Define the styles styles = getSampleStyleSheet() normal_style = styles["Normal"] # Convert strings to Paragraphs to allow line breaks for row in fuel_info: for i, cell in enumerate(row): row[i] = Paragraph(cell, normal_style) fuel_table_style = TableStyle( [ ("BACKGROUND", (0, 0), (-1, 0), colors.grey), ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke), ("ALIGN", (0, 0), (-1, -1), "CENTER"), ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), ("BOTTOMPADDING", (0, 0), (-1, 0), 12), ("BACKGROUND", (0, 1), (-1, -1), colors.beige), ("GRID", (0, 0), (-1, -1), 1, colors.black), ] ) fuel_table = Table([fuel_header] + fuel_info, style=fuel_table_style) content.append(fuel_table) content.append(PageBreak()) title_style = getSampleStyleSheet()["Heading1"] content.append(Paragraph("Maintenance Over Time", title_style)) # Add chart of fuel spendings maintenance_data = report_data["MaintenanceSpent"] if maintenance_data.__len__() == 0: content.append(Paragraph("No maintenance done.", title_style)) else: maintenance_values = [entry["total"] for entry in maintenance_data] maintenance_dates = [entry["date"] for entry in maintenance_data] drawing = Drawing(width=400, height=200) chart = HorizontalLineChart() chart.x = 0 chart.y = 50 chart.width = 500 chart.height = 125 chart.data = [maintenance_values] chart.categoryAxis.labels.boxAnchor = "e" chart.valueAxis.valueMin = 0 chart.valueAxis.valueMax = max(maintenance_values) + 10 chart.lines[0].strokeColor = colors.blue chart.lines[0].strokeWidth = 2 chart.categoryAxis.categoryNames = [str(date) for date in maintenance_dates] chart.categoryAxis.labels.angle = 90 drawing.add(chart) # Add space between the tables content.append(drawing) content.append(Paragraph("

", title_style)) maintenance_header = ["Date", "Maintenance Cost", "Total Maintenance Cost"] maintenance_info = [] for fuel in maintenance_data: fuel_info1 = [ fuel["date"].strftime("%m/%d/%Y, %H:%M:%S"), "{} L".format(fuel["cost"]), "{} L".format(fuel["total"]), ] maintenance_info.append(fuel_info1) # Define the styles styles = getSampleStyleSheet() normal_style = styles["Normal"] # Convert strings to Paragraphs to allow line breaks for row in maintenance_info: for i, cell in enumerate(row): row[i] = Paragraph(cell, normal_style) maintenance_table_style = TableStyle( [ ("BACKGROUND", (0, 0), (-1, 0), colors.grey), ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke), ("ALIGN", (0, 0), (-1, -1), "CENTER"), ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), ("BOTTOMPADDING", (0, 0), (-1, 0), 12), ("BACKGROUND", (0, 1), (-1, -1), colors.beige), ("GRID", (0, 0), (-1, -1), 1, colors.black), ] ) maintenance_table = Table( [maintenance_header] + maintenance_info, style=maintenance_table_style ) content.append(maintenance_table) document.build(content) print(f"PDF generated successfully: {pdf_filename}") return pdf_filename