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.
 
 

445 lines
16 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. if (
  140. vehicle["CurrentLocation"] is None
  141. or vehicle["CurrentLocation"] == ""
  142. or vehicle["CurrentLocation"] == False
  143. or len(vehicle["CurrentLocation"]) == 0
  144. ):
  145. vehicle["CurrentLocation"] = ["Unknown", "Unknown"]
  146. loc = vehicle["CurrentLocation"][0]
  147. if len(vehicle["CurrentLocation"]) > 1:
  148. loc += ", " + vehicle["CurrentLocation"][1]
  149. # Add vehicle details
  150. vehicle_info = [
  151. ["Vehicle Type", vehicle["Type"]],
  152. ["Vehicle Model", vehicle["Model"]],
  153. ["Year", vehicle["Year"]],
  154. ["License Plate", vehicle["LicensePlate"]],
  155. ["Current Location", loc],
  156. ["Mileage", vehicle["Mileage"]],
  157. ["Capacity", vehicle["Capacity"]],
  158. ]
  159. vehicle_table_style = TableStyle(
  160. [
  161. ("ALIGN", (0, 0), (-1, -1), "LEFT"),
  162. ("BOX", (0, 0), (-1, -1), 1, colors.black),
  163. ]
  164. )
  165. vehicle_table = Table(vehicle_info, style=vehicle_table_style)
  166. content.append(Spacer(1, 12)) # Add space between the tables
  167. content.append(Table([[driver_table, vehicle_table]]))
  168. content.append(Paragraph("<br/><br/>", title_style))
  169. total_info = [
  170. ["Total Tasks Done", report_data["tasks"].__len__()],
  171. ["Total Fuel Spent", report_data["TotalFuelSpent"]],
  172. ["Total Time Spent", report_data["TotalTime"]],
  173. ["Total Distance Driven", report_data["TotalDistance"]],
  174. ["Total Money Spent", report_data["TotalMaintenanceSpent"]],
  175. ]
  176. total_table_style = TableStyle(
  177. [
  178. ("ALIGN", (0, 0), (-1, -1), "LEFT"),
  179. ("BOX", (0, 0), (-1, -1), 1, colors.black),
  180. ]
  181. )
  182. total_table = Table(total_info, style=total_table_style)
  183. content.append(total_table)
  184. content.append(Paragraph("<br/><br/>", title_style))
  185. content.append(PageBreak())
  186. title_style = getSampleStyleSheet()["Heading1"]
  187. content.append(Paragraph("Driving Tasks", title_style))
  188. title_style = getSampleStyleSheet()["Heading2"]
  189. content.append(Paragraph("Distance Driven over Time", title_style))
  190. tasks_data = report_data["tasks"]
  191. if tasks_data.__len__() == 0:
  192. content.append(Paragraph("No tasks done.", title_style))
  193. else:
  194. task_values = [entry["DistanceAtTime"] for entry in tasks_data]
  195. task_dates = [
  196. # if type(entry["EndDateTime"]) == str parse, else just use
  197. parser.parse(entry["EndDateTime"]).strftime("%m/%d/%Y, %H:%M:%S")
  198. if type(entry["EndDateTime"]) == str
  199. else entry["EndDateTime"].strftime("%m/%d/%Y, %H:%M:%S")
  200. for entry in tasks_data
  201. ]
  202. drawing = Drawing(width=400, height=200)
  203. chart = HorizontalLineChart()
  204. chart.x = 0
  205. chart.y = 50
  206. chart.width = 500
  207. chart.height = 125
  208. chart.data = [task_values]
  209. chart.categoryAxis.labels.boxAnchor = "e"
  210. chart.valueAxis.valueMin = 0
  211. chart.valueAxis.valueMax = max(task_values) + 10
  212. chart.lines[0].strokeColor = colors.blue
  213. chart.lines[0].strokeWidth = 2
  214. chart.categoryAxis.categoryNames = [str(date) for date in task_dates]
  215. chart.categoryAxis.labels.angle = 90
  216. drawing.add(chart)
  217. # Add space between the tables
  218. content.append(drawing)
  219. content.append(Paragraph("<br/><br/>", title_style))
  220. content.append(Paragraph("<br/><br/>", title_style))
  221. content.append(Paragraph("<br/><br/>", title_style))
  222. # Add tasks information
  223. title_style = getSampleStyleSheet()["Heading2"]
  224. content.append(Paragraph("All DriveTasks", title_style))
  225. tasks_header = [
  226. "Task ID",
  227. "Description",
  228. "Start Location",
  229. "End Location",
  230. "Distance Covered",
  231. "Status",
  232. "Start DateTime",
  233. "End DateTime",
  234. ]
  235. tasks_info = []
  236. for task in tasks_data:
  237. task_info = [
  238. f"{task['Id']}",
  239. task["Description"],
  240. f"{task['StartLocation'][0]}, {task['StartLocation'][1]}",
  241. f"{task['EndLocation'][0]}, {task['EndLocation'][1]}",
  242. "{} km".format(task["DistanceCovered"]),
  243. task["Status"],
  244. task["StartDateTime"].strftime("%m/%d/%Y, %H:%M:%S"),
  245. parser.parse(task["EndDateTime"]).strftime("%m/%d/%Y, %H:%M:%S")
  246. if type(task["EndDateTime"]) == str
  247. else task["EndDateTime"].strftime("%m/%d/%Y, %H:%M:%S"),
  248. ]
  249. tasks_info.append(task_info)
  250. # Define the styles
  251. styles = getSampleStyleSheet()
  252. normal_style = styles["Normal"]
  253. # Convert strings to Paragraphs to allow line breaks
  254. for row in tasks_info:
  255. for i, cell in enumerate(row):
  256. row[i] = Paragraph(cell, normal_style)
  257. tasks_table_style = TableStyle(
  258. [
  259. ("BACKGROUND", (0, 0), (-1, 0), colors.grey),
  260. ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
  261. ("ALIGN", (0, 0), (-1, -1), "CENTER"),
  262. ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
  263. ("BOTTOMPADDING", (0, 0), (-1, 0), 12),
  264. ("BACKGROUND", (0, 1), (-1, -1), colors.beige),
  265. ("GRID", (0, 0), (-1, -1), 1, colors.black),
  266. ]
  267. )
  268. tasks_table = Table([tasks_header] + tasks_info, style=tasks_table_style)
  269. content.append(tasks_table)
  270. content.append(PageBreak())
  271. title_style = getSampleStyleSheet()["Heading1"]
  272. content.append(Paragraph("Fuel Spent Over Time", title_style))
  273. # Add chart of fuel spendings
  274. fuel_spent_data = report_data["FuelSpent"]
  275. if fuel_spent_data.__len__() == 0:
  276. content.append(Paragraph("No fuel spent.", title_style))
  277. else:
  278. fuel_values = [entry["total"] for entry in fuel_spent_data]
  279. fuel_dates = [entry["date"] for entry in fuel_spent_data]
  280. drawing = Drawing(width=400, height=200)
  281. chart = HorizontalLineChart()
  282. chart.x = 0
  283. chart.y = 50
  284. chart.width = 500
  285. chart.height = 125
  286. chart.data = [fuel_values]
  287. chart.categoryAxis.labels.boxAnchor = "e"
  288. chart.valueAxis.valueMin = 0
  289. chart.valueAxis.valueMax = max(fuel_values) + 10
  290. chart.lines[0].strokeColor = colors.blue
  291. chart.lines[0].strokeWidth = 2
  292. chart.categoryAxis.categoryNames = [str(date) for date in fuel_dates]
  293. chart.categoryAxis.labels.angle = 90
  294. drawing.add(chart)
  295. # Add space between the tables
  296. content.append(drawing)
  297. content.append(Paragraph("<br/><br/>", title_style))
  298. fuel_header = ["Date", "Fuel Refilled", "Total Fuel Spent"]
  299. fuel_info = []
  300. for fuel in fuel_spent_data:
  301. fuel_info1 = [
  302. fuel["date"].strftime("%m/%d/%Y, %H:%M:%S"),
  303. "{} L".format(fuel["fuelrefilled"]),
  304. "{} L".format(fuel["total"]),
  305. ]
  306. fuel_info.append(fuel_info1)
  307. # Define the styles
  308. styles = getSampleStyleSheet()
  309. normal_style = styles["Normal"]
  310. # Convert strings to Paragraphs to allow line breaks
  311. for row in fuel_info:
  312. for i, cell in enumerate(row):
  313. row[i] = Paragraph(cell, normal_style)
  314. fuel_table_style = TableStyle(
  315. [
  316. ("BACKGROUND", (0, 0), (-1, 0), colors.grey),
  317. ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
  318. ("ALIGN", (0, 0), (-1, -1), "CENTER"),
  319. ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
  320. ("BOTTOMPADDING", (0, 0), (-1, 0), 12),
  321. ("BACKGROUND", (0, 1), (-1, -1), colors.beige),
  322. ("GRID", (0, 0), (-1, -1), 1, colors.black),
  323. ]
  324. )
  325. fuel_table = Table([fuel_header] + fuel_info, style=fuel_table_style)
  326. content.append(fuel_table)
  327. content.append(PageBreak())
  328. title_style = getSampleStyleSheet()["Heading1"]
  329. content.append(Paragraph("Maintenance Over Time", title_style))
  330. # Add chart of fuel spendings
  331. maintenance_data = report_data["MaintenanceSpent"]
  332. if maintenance_data.__len__() == 0:
  333. content.append(Paragraph("No maintenance done.", title_style))
  334. else:
  335. maintenance_values = [entry["total"] for entry in maintenance_data]
  336. maintenance_dates = [entry["date"] for entry in maintenance_data]
  337. drawing = Drawing(width=400, height=200)
  338. chart = HorizontalLineChart()
  339. chart.x = 0
  340. chart.y = 50
  341. chart.width = 500
  342. chart.height = 125
  343. chart.data = [maintenance_values]
  344. chart.categoryAxis.labels.boxAnchor = "e"
  345. chart.valueAxis.valueMin = 0
  346. chart.valueAxis.valueMax = max(maintenance_values) + 10
  347. chart.lines[0].strokeColor = colors.blue
  348. chart.lines[0].strokeWidth = 2
  349. chart.categoryAxis.categoryNames = [str(date) for date in maintenance_dates]
  350. chart.categoryAxis.labels.angle = 90
  351. drawing.add(chart)
  352. # Add space between the tables
  353. content.append(drawing)
  354. content.append(Paragraph("<br/><br/>", title_style))
  355. maintenance_header = ["Date", "Maintenance Cost", "Total Maintenance Cost"]
  356. maintenance_info = []
  357. for fuel in maintenance_data:
  358. fuel_info1 = [
  359. fuel["date"].strftime("%m/%d/%Y, %H:%M:%S"),
  360. "{} L".format(fuel["cost"]),
  361. "{} L".format(fuel["total"]),
  362. ]
  363. maintenance_info.append(fuel_info1)
  364. # Define the styles
  365. styles = getSampleStyleSheet()
  366. normal_style = styles["Normal"]
  367. # Convert strings to Paragraphs to allow line breaks
  368. for row in maintenance_info:
  369. for i, cell in enumerate(row):
  370. row[i] = Paragraph(cell, normal_style)
  371. maintenance_table_style = TableStyle(
  372. [
  373. ("BACKGROUND", (0, 0), (-1, 0), colors.grey),
  374. ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
  375. ("ALIGN", (0, 0), (-1, -1), "CENTER"),
  376. ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
  377. ("BOTTOMPADDING", (0, 0), (-1, 0), 12),
  378. ("BACKGROUND", (0, 1), (-1, -1), colors.beige),
  379. ("GRID", (0, 0), (-1, -1), 1, colors.black),
  380. ]
  381. )
  382. maintenance_table = Table(
  383. [maintenance_header] + maintenance_info, style=maintenance_table_style
  384. )
  385. content.append(maintenance_table)
  386. document.build(content)
  387. print(f"PDF generated successfully: {pdf_filename}")
  388. return pdf_filename