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.
 
 

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