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.
 
 

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