You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

379 lines
14 KiB

  1. from datetime import datetime
  2. from fastapi.responses import StreamingResponse
  3. from sqlalchemy.orm import Session
  4. from db.models.drivetask import DriveTask
  5. from db.models.user import User
  6. from db.models.fuelingtask import FuelingTask
  7. from db.models.maintenancejob import MaintenanceJob
  8. from db.repository.maintenancejob import calculate_total_cost
  9. from reportlab.lib import colors
  10. from reportlab.lib.pagesizes import letter
  11. from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Image, PageBreak
  12. from reportlab.lib.styles import getSampleStyleSheet
  13. from reportlab.lib.units import inch
  14. from reportlab.graphics.shapes import Drawing
  15. from reportlab.graphics.charts.linecharts import HorizontalLineChart
  16. from reportlab.graphics.widgets.markers import makeMarker
  17. from reportlab.platypus import Spacer
  18. from reportlab.graphics.charts.axes import CategoryAxis
  19. from dateutil import parser
  20. def get_repot_jobsdone_by_driver(driver_id: int, db: Session):
  21. driver = db.query(User).filter(User.Id == driver_id).first()
  22. if not driver:
  23. return "notdriver"
  24. if driver.Role != "Driver":
  25. return "notdriver"
  26. tasks = db.query(DriveTask).filter((DriveTask.DriverId == driver_id) & (DriveTask.Status == "Completed")).all()
  27. #order tasks by completion date
  28. tasks.sort(key=lambda x: x.EndDateTime)
  29. #for each task, add TOTAL distance driven at the time of completion
  30. dist = 0
  31. taskslist = []
  32. for task in tasks:
  33. dist += task.DistanceCovered
  34. taskslist.append(task.__dict__)
  35. taskslist[-1]["DistanceAtTime"] = dist
  36. res = {}
  37. res["tasks"] = taskslist
  38. res["Driver"] = driver.__dict__
  39. res["Driver"]["AssignedVehicle"] = driver.vehicle
  40. dist = 0
  41. for task in tasks:
  42. dist += task.DistanceCovered
  43. res["TotalDistance"] = dist
  44. timespent = 0.0
  45. for task in tasks:
  46. edate = task.EndDateTime
  47. sdate = task.StartDateTime
  48. if type(edate) == str:
  49. edate = datetime.strptime(edate, "%Y-%m-%d %H:%M:%S.%f")
  50. timetaken = edate - sdate
  51. timespent += timetaken.total_seconds()
  52. #time spent in hours and minutes
  53. hours = timespent // 3600
  54. minutes = (timespent % 3600) // 60
  55. res["TotalTime"] = str(hours) + " hours " + str(minutes) + " minutes"
  56. #add fuel expenditures
  57. fuel = db.query(FuelingTask).filter(FuelingTask.DriverId == driver_id).all()
  58. fuel.sort(key=lambda x: x.Date)
  59. totalfuelspent = 0
  60. fuelspent = []
  61. for f in fuel:
  62. fuelpoint = {}
  63. totalfuelspent += f.FuelRefilled
  64. fuelpoint["fuelrefilled"] = f.FuelRefilled
  65. fuelpoint["total"] = totalfuelspent
  66. fuelpoint["date"] = f.Date
  67. fuelspent.append(fuelpoint)
  68. res["FuelSpent"] = fuelspent
  69. res["TotalFuelSpent"] = totalfuelspent
  70. #add maintenance costs
  71. mainten = db.query(MaintenanceJob).filter(MaintenanceJob.VehicleDriverId == driver_id).all()
  72. mainten.sort(key=lambda x: x.Date)
  73. totalmaintenspent = 0
  74. maintenspent = []
  75. for m in mainten:
  76. maintenpoint = {}
  77. cost = calculate_total_cost(m.CarParts)
  78. totalmaintenspent += cost
  79. maintenpoint["cost"] = cost
  80. maintenpoint["total"] = totalmaintenspent
  81. maintenpoint["date"] = m.Date
  82. maintenspent.append(maintenpoint)
  83. res["MaintenanceSpent"] = maintenspent
  84. res["TotalMaintenanceSpent"] = totalmaintenspent
  85. return res
  86. def get_pdf(driver_id: int, db: Session):
  87. report_data = get_repot_jobsdone_by_driver(driver_id, db)
  88. if report_data == "notdriver":
  89. return "notdriver"
  90. # Assuming your report data is stored in a variable named 'report_data'
  91. # Create a PDF document
  92. pdf_filename = "report1.pdf"
  93. document = SimpleDocTemplate(pdf_filename, pagesize=letter)
  94. # Create a list to hold the contents of the PDF
  95. content = []
  96. # Add a title to the PDF
  97. title_style = getSampleStyleSheet()['Title']
  98. title = Paragraph("Driver Report", title_style)
  99. content.append(title)
  100. content.append(Paragraph("<br/>", title_style))
  101. print(report_data["Driver"])
  102. # Add detailed information about the driver
  103. driver_info = [
  104. ["Driver Name", report_data["Driver"]["Name"]],
  105. ["Last Name", report_data["Driver"]["LastName"]],
  106. ["Government ID", report_data["Driver"]["GovernmentId"]],
  107. ["Email", report_data["Driver"]["Email"]],
  108. ["Contact Number", report_data["Driver"]["ContactNumber"]],
  109. ["Address", report_data["Driver"]["Address"]],
  110. ["Role", report_data["Driver"]["Role"]],
  111. ]
  112. driver_table_style = TableStyle([
  113. ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
  114. ('BOX', (0, 0), (-1, -1), 1, colors.black),
  115. ])
  116. driver_table = Table(driver_info, style=driver_table_style)
  117. content.append(Paragraph("<br/><br/>", title_style))
  118. vehicle = report_data["Driver"]["AssignedVehicle"].__dict__
  119. print(vehicle)
  120. loc = vehicle["CurrentLocation"][0]
  121. if len(vehicle["CurrentLocation"]) > 1:
  122. loc += ", " + vehicle["CurrentLocation"][1]
  123. # Add vehicle details
  124. vehicle_info = [
  125. ["Vehicle Type", vehicle["Type"]],
  126. ["Vehicle Model", vehicle["Model"]],
  127. ["Year", vehicle["Year"]],
  128. ["License Plate", vehicle["LicensePlate"]],
  129. ["Current Location", loc],
  130. ["Mileage", vehicle["Mileage"]],
  131. ["Capacity", vehicle["Capacity"]],
  132. ]
  133. vehicle_table_style = TableStyle([
  134. ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
  135. ('BOX', (0, 0), (-1, -1), 1, colors.black),
  136. ])
  137. vehicle_table = Table(vehicle_info, style=vehicle_table_style)
  138. content.append(Spacer(1, 12)) # Add space between the tables
  139. content.append(Table([[driver_table, vehicle_table]]))
  140. content.append(Paragraph("<br/><br/>", title_style))
  141. total_info = [
  142. ["Total Tasks Done", report_data["tasks"].__len__()],
  143. ["Total Fuel Spent", report_data["TotalFuelSpent"]],
  144. ["Total Time Spent", report_data["TotalTime"]],
  145. ["Total Distance Driven", report_data["TotalDistance"]],
  146. ["Total Money Spent", report_data["TotalMaintenanceSpent"]],
  147. ]
  148. total_table_style = TableStyle([
  149. ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
  150. ('BOX', (0, 0), (-1, -1), 1, colors.black),
  151. ])
  152. total_table = Table(total_info, style=total_table_style)
  153. content.append(total_table)
  154. content.append(Paragraph("<br/><br/>", title_style))
  155. content.append(PageBreak())
  156. title_style = getSampleStyleSheet()["Heading1"]
  157. content.append(Paragraph("Driving Tasks", title_style))
  158. title_style = getSampleStyleSheet()["Heading2"]
  159. content.append(Paragraph("Distance Driven over Time", title_style))
  160. tasks_data = report_data["tasks"]
  161. task_values = [entry["DistanceAtTime"] for entry in tasks_data]
  162. task_dates = [parser.parse(entry['EndDateTime']).strftime("%m/%d/%Y, %H:%M:%S") for entry in tasks_data]
  163. drawing = Drawing(width=400, height=200)
  164. chart = HorizontalLineChart()
  165. chart.x = 0
  166. chart.y = 50
  167. chart.width = 500
  168. chart.height = 125
  169. chart.data = [task_values]
  170. chart.categoryAxis.labels.boxAnchor = 'e'
  171. chart.valueAxis.valueMin = 0
  172. chart.valueAxis.valueMax = max(task_values) + 10
  173. chart.lines[0].strokeColor = colors.blue
  174. chart.lines[0].strokeWidth = 2
  175. chart.categoryAxis.categoryNames = [str(date) for date in task_dates]
  176. chart.categoryAxis.labels.angle = 90
  177. drawing.add(chart)
  178. # Add space between the tables
  179. content.append(drawing)
  180. content.append(Paragraph("<br/><br/>", title_style))
  181. content.append(Paragraph("<br/><br/>", title_style))
  182. content.append(Paragraph("<br/><br/>", title_style))
  183. # Add tasks information
  184. title_style = getSampleStyleSheet()["Heading2"]
  185. content.append(Paragraph("All DriveTasks", title_style))
  186. tasks_header = ["Task ID", "Description", "Start Location", "End Location", "Distance Covered", "Status", "Start DateTime", "End DateTime"]
  187. tasks_info = []
  188. for task in tasks_data:
  189. task_info = [f"{task['Id']}", task["Description"], f"{task['StartLocation'][0]}, {task['StartLocation'][1]}", f"{task['EndLocation'][0]}, {task['EndLocation'][1]}",
  190. "{} 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")]
  191. tasks_info.append(task_info)
  192. # Define the styles
  193. styles = getSampleStyleSheet()
  194. normal_style = styles["Normal"]
  195. # Convert strings to Paragraphs to allow line breaks
  196. for row in tasks_info:
  197. for i, cell in enumerate(row):
  198. row[i] = Paragraph(cell, normal_style)
  199. tasks_table_style = TableStyle([
  200. ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
  201. ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
  202. ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
  203. ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
  204. ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
  205. ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
  206. ('GRID', (0, 0), (-1, -1), 1, colors.black),
  207. ])
  208. tasks_table = Table([tasks_header] + tasks_info, style=tasks_table_style)
  209. content.append(tasks_table)
  210. content.append(PageBreak())
  211. title_style = getSampleStyleSheet()["Heading1"]
  212. content.append(Paragraph("Fuel Spent Over Time", title_style))
  213. # Add chart of fuel spendings
  214. fuel_spent_data = report_data["FuelSpent"]
  215. fuel_values = [entry["total"] for entry in fuel_spent_data]
  216. fuel_dates = [entry["date"] for entry in fuel_spent_data]
  217. drawing = Drawing(width=400, height=200)
  218. chart = HorizontalLineChart()
  219. chart.x = 0
  220. chart.y = 50
  221. chart.width = 500
  222. chart.height = 125
  223. chart.data = [fuel_values]
  224. chart.categoryAxis.labels.boxAnchor = 'e'
  225. chart.valueAxis.valueMin = 0
  226. chart.valueAxis.valueMax = max(fuel_values) + 10
  227. chart.lines[0].strokeColor = colors.blue
  228. chart.lines[0].strokeWidth = 2
  229. chart.categoryAxis.categoryNames = [str(date) for date in fuel_dates]
  230. chart.categoryAxis.labels.angle = 90
  231. drawing.add(chart)
  232. # Add space between the tables
  233. content.append(drawing)
  234. content.append(Paragraph("<br/><br/>", title_style))
  235. fuel_header = ["Date", "Fuel Refilled", "Total Fuel Spent"]
  236. fuel_info = []
  237. for fuel in fuel_spent_data:
  238. fuel_info1 = [fuel["date"].strftime("%m/%d/%Y, %H:%M:%S"), "{} L".format(fuel["fuelrefilled"]), "{} L".format(fuel["total"])]
  239. fuel_info.append(fuel_info1)
  240. # Define the styles
  241. styles = getSampleStyleSheet()
  242. normal_style = styles["Normal"]
  243. # Convert strings to Paragraphs to allow line breaks
  244. for row in fuel_info:
  245. for i, cell in enumerate(row):
  246. row[i] = Paragraph(cell, normal_style)
  247. fuel_table_style = TableStyle([
  248. ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
  249. ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
  250. ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
  251. ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
  252. ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
  253. ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
  254. ('GRID', (0, 0), (-1, -1), 1, colors.black),
  255. ])
  256. fuel_table = Table([fuel_header] + fuel_info, style=fuel_table_style)
  257. content.append(fuel_table)
  258. content.append(PageBreak())
  259. title_style = getSampleStyleSheet()["Heading1"]
  260. content.append(Paragraph("Maintenance Over Time", title_style))
  261. # Add chart of fuel spendings
  262. maintenance_data = report_data["MaintenanceSpent"]
  263. maintenance_values = [entry["total"] for entry in maintenance_data]
  264. maintenance_dates = [entry["date"] for entry in maintenance_data]
  265. drawing = Drawing(width=400, height=200)
  266. chart = HorizontalLineChart()
  267. chart.x = 0
  268. chart.y = 50
  269. chart.width = 500
  270. chart.height = 125
  271. chart.data = [maintenance_values]
  272. chart.categoryAxis.labels.boxAnchor = 'e'
  273. chart.valueAxis.valueMin = 0
  274. chart.valueAxis.valueMax = max(maintenance_values) + 10
  275. chart.lines[0].strokeColor = colors.blue
  276. chart.lines[0].strokeWidth = 2
  277. chart.categoryAxis.categoryNames = [str(date) for date in maintenance_dates]
  278. chart.categoryAxis.labels.angle = 90
  279. drawing.add(chart)
  280. # Add space between the tables
  281. content.append(drawing)
  282. content.append(Paragraph("<br/><br/>", title_style))
  283. maintenance_header = ["Date", "Maintenance Cost", "Total Maintenance Cost"]
  284. maintenance_info = []
  285. for fuel in maintenance_data:
  286. fuel_info1 = [fuel["date"].strftime("%m/%d/%Y, %H:%M:%S"), "{} L".format(fuel["cost"]), "{} L".format(fuel["total"])]
  287. maintenance_info.append(fuel_info1)
  288. # Define the styles
  289. styles = getSampleStyleSheet()
  290. normal_style = styles["Normal"]
  291. # Convert strings to Paragraphs to allow line breaks
  292. for row in maintenance_info:
  293. for i, cell in enumerate(row):
  294. row[i] = Paragraph(cell, normal_style)
  295. maintenance_table_style = TableStyle([
  296. ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
  297. ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
  298. ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
  299. ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
  300. ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
  301. ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
  302. ('GRID', (0, 0), (-1, -1), 1, colors.black),
  303. ])
  304. maintenance_table = Table([maintenance_header] + maintenance_info, style=maintenance_table_style)
  305. content.append(maintenance_table)
  306. document.build(content)
  307. print(f"PDF generated successfully: {pdf_filename}")
  308. return pdf_filename