Nenhuma Descrição

gen_report.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. from openpyxl import load_workbook, Workbook
  2. from datetime import datetime
  3. from openpyxl.drawing.image import Image
  4. import re
  5. from openpyxl.drawing.spreadsheet_drawing import AbsoluteAnchor
  6. from openpyxl.drawing.xdr import XDRPoint2D, XDRPositiveSize2D
  7. from openpyxl.utils.units import pixels_to_EMU
  8. from openpyxl.utils import get_column_letter, column_index_from_string
  9. from django.db import models
  10. import os
  11. from django.db.models.fields.files import ImageFieldFile
  12. from pprint import pprint
  13. from PIL import Image as PILImage
  14. from openpyxl.drawing.xdr import XDRPoint2D, XDRPositiveSize2D
  15. from openpyxl.utils.units import pixels_to_EMU
  16. from openpyxl.drawing.spreadsheet_drawing import AnchorMarker, TwoCellAnchor
  17. def set_image_with_offset_old(sheet, img, cell_coordinate, offset_x=0, offset_y=0):
  18. """
  19. Add an image to the sheet with an offset relative to the top-left corner of a cell.
  20. :param sheet: The worksheet
  21. :param img: The openpyxl Image object
  22. :param cell_coordinate: Cell to place the image (e.g., "B2")
  23. :param offset_x: Horizontal offset in pixels
  24. :param offset_y: Vertical offset in pixels
  25. """
  26. col_letter = ''.join(filter(str.isalpha, cell_coordinate)) # Extract column letter
  27. row_number = int(''.join(filter(str.isdigit, cell_coordinate))) # Extract row number
  28. # Get column width and row height in pixels
  29. col_width = sheet.column_dimensions[get_column_letter(column_index_from_string(col_letter))].width or 10
  30. row_height = sheet.row_dimensions[row_number].height or 15
  31. # Approximate conversion of Excel units to pixels
  32. col_pixels = col_width * 7.5 # Excel's ~7.5 pixels per width unit
  33. row_pixels = row_height * 0.75 # Approximation for row height in pixels
  34. # Calculate absolute positions based on offsets
  35. anchor_x = col_pixels + offset_x
  36. anchor_y = row_pixels + offset_y
  37. # Set the anchor for the image
  38. img.anchor = cell_coordinate
  39. img.anchor.dx = int(anchor_x * 9525) # Convert to EMUs (Excel Measurement Units)
  40. img.anchor.dy = int(anchor_y * 9525) # Convert to EMUs (Excel Measurement Units)
  41. sheet.add_image(img)
  42. def set_image_with_offset(sheet, img, cell_coordinate, offset_x=0, offset_y=0):
  43. """
  44. Add an image to the sheet with an offset relative to the top-left corner of a cell.
  45. :param sheet: The worksheet
  46. :param img: The openpyxl Image object
  47. :param cell_coordinate: Cell to place the image (e.g., "B2")
  48. :param offset_x: Horizontal offset in pixels
  49. :param offset_y: Vertical offset in pixels
  50. """
  51. # Extract the column and row from the cell coordinate
  52. col_letter = ''.join(filter(str.isalpha, cell_coordinate)) # Extract column letter
  53. row_number = int(''.join(filter(str.isdigit, cell_coordinate))) # Extract row number
  54. # Get the zero-based indices for the cell
  55. col_idx = column_index_from_string(col_letter) - 1
  56. row_idx = row_number - 1
  57. # Approximate column width and row height to pixels
  58. col_width = sheet.column_dimensions[col_letter].width or 10 # Default column width
  59. row_height = sheet.row_dimensions[row_number].height or 15 # Default row height
  60. # Convert column width and row height to pixels
  61. col_pixels = col_width * 7.5 # Approximation: ~7.5 pixels per width unit
  62. row_pixels = row_height * 0.75 # Approximation: ~0.75 pixels per height unit
  63. # Calculate the position in pixels for the top-left corner of the cell
  64. cell_x = col_idx * col_pixels
  65. cell_y = row_idx * row_pixels
  66. # Apply the offsets
  67. final_x = cell_x + offset_x
  68. final_y = cell_y + offset_y
  69. # Convert to EMUs
  70. pos = XDRPoint2D(pixels_to_EMU(final_x), pixels_to_EMU(final_y))
  71. size = XDRPositiveSize2D(pixels_to_EMU(img.width), pixels_to_EMU(img.height))
  72. # Set the image's anchor with the position and size
  73. img.anchor = AbsoluteAnchor(pos=pos, ext=size)
  74. # Add the image to the worksheet
  75. sheet.add_image(img)
  76. def center_image_in_cell(sheet, img, cell_coordinate):
  77. """
  78. Center an image inside a specified cell.
  79. :param sheet: The worksheet
  80. :param img: The openpyxl Image object
  81. :param cell_coordinate: The cell to center the image in (e.g., "C3")
  82. """
  83. # Extract column and row from the cell coordinate
  84. col_letter = ''.join(filter(str.isalpha, cell_coordinate)) # Extract column letter
  85. row_number = int(''.join(filter(str.isdigit, cell_coordinate))) # Extract row number
  86. col_idx = column_index_from_string(col_letter) - 1 # Convert to zero-based column index
  87. # Get cell dimensions
  88. col_width = sheet.column_dimensions[col_letter].width or 10 # Default width if not set
  89. row_height = sheet.row_dimensions[row_number].height or 15 # Default height if not set
  90. # Convert dimensions to pixels (approximation)
  91. col_pixels = col_width * 7.5 # 1 Excel column width unit ≈ 7.5 pixels
  92. row_pixels = row_height * 0.75 # 1 Excel row height unit ≈ 0.75 pixels
  93. # Get image dimensions
  94. img_width, img_height = img.width, img.height
  95. # Calculate offsets to center the image
  96. offset_x = int((col_pixels - img_width) / 2 * pixels_to_EMU(1)) # Center horizontally
  97. offset_y = int((row_pixels - img_height) / 2 * pixels_to_EMU(1)) # Center vertically
  98. # Define the anchor for the image
  99. _from = AnchorMarker(col=col_idx, row=row_number - 1, colOff=offset_x, rowOff=offset_y)
  100. to = AnchorMarker(col=col_idx + 1, row=row_number, colOff=-offset_x, rowOff=-offset_y)
  101. # Use TwoCellAnchor for positioning
  102. img.anchor = TwoCellAnchor(editAs="oneCell", _from=_from, to=to)
  103. # Add the image to the sheet
  104. sheet.add_image(img)
  105. def gen_xlsx(template_file, selected_sheets, prefix_filename, data):
  106. """
  107. Generate an Excel file from a template, fill placeholders, and include only selected sheets.
  108. Args:
  109. template_file (str): Path to the Excel template file.
  110. selected_sheets (list): List of sheet names to include in the output file.
  111. prefix_filename (str): Prefix for the output filename.
  112. data (dict): Data dictionary with sheet-specific keys and fallback keys.
  113. Returns:
  114. str: Path of the generated Excel file.
  115. """
  116. checked_image_path = "/app/report/checkbox_checked.jpg" # Path to the checked checkbox image
  117. unchecked_image_path = "/app/report/checkbox_unchecked.jpg" # Path to the unchecked checkbox image
  118. # Load the template workbook
  119. workbook = load_workbook(template_file)
  120. # Remove sheets not in selected_sheets
  121. for sheet_name in workbook.sheetnames:
  122. if sheet_name not in selected_sheets:
  123. del workbook[sheet_name]
  124. # Process the selected sheets
  125. for sheet_name in selected_sheets:
  126. if sheet_name not in workbook.sheetnames:
  127. raise ValueError(f"Sheet '{sheet_name}' not found in the template.")
  128. sheet = workbook[sheet_name]
  129. # Replace placeholders with actual values
  130. # Handle hiding rows based on patterns in data
  131. for row in sheet.iter_rows():
  132. for cell in row:
  133. if cell.value and isinstance(cell.value, str) and cell.value.startswith("<") and cell.value.endswith(">"):
  134. placeholder = cell.value.strip("<>")
  135. # Determine value priority: `sheet_name.key` > `key`
  136. value = None
  137. sheet_specific_key = f"{sheet_name}.{placeholder}"
  138. if sheet_specific_key in data:
  139. value = data[sheet_specific_key]
  140. elif placeholder in data:
  141. value = data[placeholder]
  142. if value is not None:
  143. if isinstance(value, ImageFieldFile):
  144. pprint("ImageField")
  145. image_path = value.path
  146. if os.path.exists(image_path):
  147. # img = Image(image_path)
  148. # img.height = 40 # Adjust size as needed
  149. pil_img = PILImage.open(image_path)
  150. original_width, original_height = pil_img.size
  151. # Desired height (e.g., 40), calculate the new width to maintain aspect ratio
  152. desired_height = 40
  153. aspect_ratio = original_width / original_height
  154. new_width = int(desired_height * aspect_ratio)
  155. # Resize the image using Pillow (optional, for saving memory during export)
  156. resized_img = pil_img.resize((new_width, desired_height), PILImage.Resampling.LANCZOS)
  157. resized_img.save(image_path) # Save the resized image back to the same path
  158. # Insert the resized image into the Excel sheet
  159. img = Image(image_path)
  160. img.width, img.height = new_width, desired_height # Set the dimensions
  161. # sheet.add_image(img, cell.coordinate)
  162. center_image_in_cell(sheet, img, cell.coordinate, )
  163. cell.value = None # Clear placeholder
  164. elif value is True:
  165. img = Image(checked_image_path)
  166. img.width = img.height = 10
  167. print(f"{cell.coordinate}")
  168. # sheet.add_image(img, cell.coordinate)
  169. # set_image_with_offset(sheet, img, cell.coordinate, offset_x=100)
  170. center_image_in_cell(sheet,img, cell.coordinate, )
  171. cell.value = None # Remove the placeholder text
  172. elif value is False:
  173. img = Image(unchecked_image_path)
  174. img.width = img.height = 10
  175. # sheet.add_image(img, cell.coordinate)
  176. # set_image_with_offset(sheet, img, cell.coordinate, offset_x=100)
  177. center_image_in_cell(sheet, img, cell.coordinate, )
  178. cell.value = None # Remove the placeholder text
  179. else:
  180. # Insert the text value directly
  181. cell.value = value
  182. for key, value in data.items():
  183. if isinstance(value, str) and re.match(r"^\d+\[\d+:\d+\]$", value):
  184. # Parse the prefix and row range
  185. prefix, row_range = value.split("[")
  186. row_start, row_end = map(int, row_range[:-1].split(":"))
  187. # Hide rows if the prefix matches the condition
  188. if prefix == "0": # Adjust the condition as needed
  189. sheet.row_dimensions.group(row_start, row_end, hidden=True)
  190. # Generate the output filename with a timestamp
  191. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  192. output_path = f"{prefix_filename}_{timestamp}.xlsx"
  193. workbook.save(output_path)
  194. return output_path
  195. if __name__ == "__main__":
  196. # Example usage
  197. data = {
  198. "customer": "Tum Coder",
  199. "inspect_date": "2025-01-15",
  200. "lot_no": "12345",
  201. "staff_name": "Tum 8888",
  202. "man_name": "Tum 999",
  203. "size": "Large",
  204. "pcs": "10 pcs",
  205. "spec": "Spec-A",
  206. "hardness.d1_act": "10",
  207. "hardness.d2_act": "0[24:28]", # Hide rows 24 to 28 if the prefix is "0"
  208. "hardness.acc": True, # Hide rows 24 to 28 if the prefix is "0"
  209. "hardness.spe_acc": False, # Hide rows 24 to 28 if the prefix is "0"
  210. "dimension_app.d1_act": "33",
  211. "dimension_app.d2_act": "0[26:32]", # Hide rows 24 to 28 if the prefix is "0"
  212. "dimension_app.acc": True, # Hide rows 24 to 28 if the prefix is "0"
  213. "dimension_app.spe_acc": True, # Hide rows 24 to 28 if the prefix is "0"
  214. }
  215. output_file = gen_xlsx(
  216. template_file="./hardness.xlsx",
  217. selected_sheets=["hardness", "dimension_app"], # Replace with your actual sheet names
  218. prefix_filename="./output/output",
  219. data=data
  220. )
  221. print(f"Generated file: {output_file}")