Preeeeet commited on
Commit
c03086c
·
verified ·
1 Parent(s): 37dc7ef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -82
app.py CHANGED
@@ -5,7 +5,6 @@ from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
5
  from docx.enum.text import WD_ALIGN_PARAGRAPH
6
  from PIL import Image
7
  import pillow_heif
8
- from datetime import datetime
9
  import os
10
  import io
11
  import subprocess
@@ -19,7 +18,6 @@ def process_image(uploaded_file, compress=False, quality=60):
19
  try:
20
  uploaded_file.seek(0)
21
  img = Image.open(uploaded_file)
22
- # Handle transparency and convert to RGB
23
  if img.mode == 'RGBA':
24
  background = Image.new('RGB', img.size, (255, 255, 255))
25
  background.paste(img, mask=img.split()[3])
@@ -40,11 +38,10 @@ def process_image(uploaded_file, compress=False, quality=60):
40
  return None
41
 
42
  # --- CORE REPORT GENERATION LOGIC ---
43
- def generate_report(image_items, job_details, layout_style, compress_images, progress_bar):
44
  """Generates the DOCX and PDF report."""
45
  doc = Document()
46
  section = doc.sections[0]
47
- # Set page margins
48
  section.left_margin = section.right_margin = Inches(0.5)
49
  section.top_margin = section.bottom_margin = Inches(0.5)
50
 
@@ -55,66 +52,75 @@ def generate_report(image_items, job_details, layout_style, compress_images, pro
55
  header_table.columns[0].width = Inches(2.5)
56
  header_table.columns[1].width = Inches(5.0)
57
 
58
- # Left cell: Logo
59
  logo_cell = header_table.cell(0, 0)
60
  if os.path.exists("logo.png"):
61
  logo_cell.paragraphs[0].add_run().add_picture("logo.png", width=Inches(1.5))
62
 
63
- # Right cell: Job Details (now from a single multiline input)
64
  info_cell = header_table.cell(0, 1)
65
- p = info_cell.paragraphs[0]
66
- p.text = job_details
67
- p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
68
  info_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
69
 
70
  num_images = len(image_items)
71
 
72
- # --- Strict Two-per-Page Layout ---
73
- if layout_style == 'Two Photos per Page':
74
- # Iterate through images two at a time
75
- for i in range(0, num_images, 2):
76
- # Create a table for the pair of images. This ensures uniformity.
77
- table = doc.add_table(rows=1, cols=2)
78
- table.autofit = False
79
- table.alignment = WD_TABLE_ALIGNMENT.CENTER
80
- # Set column widths to be equal
81
- table.columns[0].width = Inches(3.7)
82
- table.columns[1].width = Inches(3.7)
83
-
84
- # --- Process and place the first image (left) ---
85
- item1 = image_items[i]
86
- cell1 = table.cell(0, 0)
87
 
88
- # Add picture, resized to fit the cell width
89
- img_stream1 = process_image(item1['file'], compress=compress_images)
90
- if img_stream1:
91
- cell1.paragraphs[0].add_run().add_picture(img_stream1, width=Inches(3.5))
92
-
93
- # Add description below the picture
94
- desc_p1 = cell1.add_paragraph()
95
- desc_p1.add_run(f"Description: ").bold = True
96
- desc_p1.add_run(item1['description'])
97
- desc_p1.alignment = WD_ALIGN_PARAGRAPH.LEFT
98
-
99
- progress_bar.progress((i + 1) / num_images)
100
-
101
- # --- Process and place the second image (right) if it exists ---
102
- if i + 1 < num_images:
103
- item2 = image_items[i + 1]
104
- cell2 = table.cell(0, 1)
105
-
106
- img_stream2 = process_image(item2['file'], compress=compress_images)
107
- if img_stream2:
108
- cell2.paragraphs[0].add_run().add_picture(img_stream2, width=Inches(3.5))
109
-
110
- desc_p2 = cell2.add_paragraph()
111
- desc_p2.add_run(f"Description: ").bold = True
112
- desc_p2.add_run(item2['description'])
113
- desc_p2.alignment = WD_ALIGN_PARAGRAPH.LEFT
114
-
115
- progress_bar.progress((i + 2) / num_images)
116
-
117
- doc.add_paragraph() # Add a little space after the table
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  # --- File Saving and Conversion ---
120
  doc_io = io.BytesIO()
@@ -127,7 +133,7 @@ def generate_report(image_items, job_details, layout_style, compress_images, pro
127
  f.write(doc_io.getvalue())
128
  subprocess.run(
129
  ['libreoffice', '--headless', '--convert-to', 'pdf', '--outdir', '.', 'temp_report.docx'],
130
- check=True, timeout=90
131
  )
132
  pdf_path = 'temp_report.pdf'
133
  if os.path.exists(pdf_path):
@@ -150,8 +156,7 @@ if 'image_dict' not in st.session_state:
150
 
151
  def reset_app():
152
  """Clears all session state and reruns the app."""
153
- for key in st.session_state.keys():
154
- del st.session_state[key]
155
  st.rerun()
156
 
157
  # --- UI Layout ---
@@ -164,28 +169,20 @@ st.markdown("---")
164
  # --- Sidebar for Settings ---
165
  with st.sidebar:
166
  st.header("1. Job Information")
167
- # A single multi-line input for all job details
168
  job_details = st.text_area(
169
  "Job Details",
170
  "T250014 Walmart 3155 Sault Ste. Marie\n446 Great Northern Rd\nSault Ste. Marie, Ontario, P6B 4Z9",
171
- height=100
172
- )
173
-
174
- st.header("2. Report Options")
175
- # Simplified layout option
176
- layout_style = st.radio(
177
- "Select Layout Style",
178
- ('Two Photos per Page',), # Only one style for now as requested
179
- help="Choose how photos are arranged in the report."
180
  )
181
- compress_images = st.checkbox("Compress Images", value=True, help="Reduces final file size.")
182
 
183
- st.header("3. Actions")
184
  generate_button = st.button("Generate Report", type="primary", use_container_width=True)
185
  reset_button = st.button("Reset All", use_container_width=True, on_click=reset_app)
186
 
187
- # --- Main Area: File Uploader and Image Editor ---
188
- st.header("4. Upload, Arrange, and Describe Images")
189
  uploaded_files = st.file_uploader(
190
  "Upload all your photos here",
191
  accept_multiple_files=True,
@@ -194,30 +191,30 @@ uploaded_files = st.file_uploader(
194
  )
195
 
196
  if uploaded_files:
197
- for i, file in enumerate(uploaded_files):
198
  if file.file_id not in st.session_state.image_dict:
199
- current_max_order = max([d['order'] for d in st.session_state.image_dict.values()], default=0)
200
  st.session_state.image_dict[file.file_id] = {
201
  'file': file,
202
  'description': '',
203
  'order': current_max_order + 1
204
  }
205
 
206
- st.info("Set the order using numbers, then add descriptions for each photo.")
207
 
208
- sorted_display_items = sorted(st.session_state.image_dict.values(), key=lambda x: x['order'])
209
 
210
  for item_data in sorted_display_items:
211
  file_id = item_data['file'].file_id
212
  st.markdown("---")
213
- cols = st.columns([1, 3])
214
 
215
  with cols[0]:
216
  st.image(item_data['file'], use_column_width=True)
217
 
218
  with cols[1]:
219
- item_data['order'] = st.number_input("Order", min_value=1, value=item_data['order'], key=f"order_{file_id}")
220
- item_data['description'] = st.text_area("Description", value=item_data['description'], key=f"desc_{file_id}")
221
 
222
  # --- Generation Logic ---
223
  if generate_button:
@@ -227,19 +224,20 @@ if generate_button:
227
  st.error("Please upload at least one image.")
228
  else:
229
  items_to_sort = list(st.session_state.image_dict.values())
230
- sorted_image_items = sorted(items_to_sort, key=lambda x: x['order'])
231
 
232
  progress_bar = st.progress(0, text="Starting report generation...")
233
  try:
234
- with st.spinner('Generating your report... Please wait.'):
 
235
  doc_io, pdf_io = generate_report(
236
  sorted_image_items,
237
  job_details,
238
- layout_style, compress_images, progress_bar
239
  )
240
  progress_bar.success("Report generated successfully!")
241
 
242
- st.header("5. Download Your Report")
243
  dl_col1, dl_col2 = st.columns(2)
244
  dl_col1.download_button(
245
  "⬇️ Download Word (.docx)", doc_io, f"HKC_Report.docx", use_container_width=True
 
5
  from docx.enum.text import WD_ALIGN_PARAGRAPH
6
  from PIL import Image
7
  import pillow_heif
 
8
  import os
9
  import io
10
  import subprocess
 
18
  try:
19
  uploaded_file.seek(0)
20
  img = Image.open(uploaded_file)
 
21
  if img.mode == 'RGBA':
22
  background = Image.new('RGB', img.size, (255, 255, 255))
23
  background.paste(img, mask=img.split()[3])
 
38
  return None
39
 
40
  # --- CORE REPORT GENERATION LOGIC ---
41
+ def generate_report(image_items, job_details, compress_images, progress_bar):
42
  """Generates the DOCX and PDF report."""
43
  doc = Document()
44
  section = doc.sections[0]
 
45
  section.left_margin = section.right_margin = Inches(0.5)
46
  section.top_margin = section.bottom_margin = Inches(0.5)
47
 
 
52
  header_table.columns[0].width = Inches(2.5)
53
  header_table.columns[1].width = Inches(5.0)
54
 
 
55
  logo_cell = header_table.cell(0, 0)
56
  if os.path.exists("logo.png"):
57
  logo_cell.paragraphs[0].add_run().add_picture("logo.png", width=Inches(1.5))
58
 
 
59
  info_cell = header_table.cell(0, 1)
60
+ p_info = info_cell.paragraphs[0]
61
+ p_info.text = job_details
62
+ p_info.alignment = WD_ALIGN_PARAGRAPH.RIGHT
63
  info_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
64
 
65
  num_images = len(image_items)
66
 
67
+ # --- NEW: Corrected Layout Logic ---
68
+ # Iterate through images, placing two items per page
69
+ for i in range(0, num_images, 2):
70
+ # Add a page break before the next set of items, but not on the first page
71
+ if i > 0:
72
+ doc.add_page_break()
 
 
 
 
 
 
 
 
 
73
 
74
+ # --- First Item on Page ---
75
+ item1 = image_items[i]
76
+ table1 = doc.add_table(rows=1, cols=2)
77
+ table1.autofit = False
78
+ table1.columns[0].width = Inches(3.0) # Description column
79
+ table1.columns[1].width = Inches(4.5) # Photo column
80
+
81
+ # Add description to the left cell
82
+ desc_cell1 = table1.cell(0, 0)
83
+ desc_cell1.text = item1['description']
84
+ desc_cell1.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
85
+
86
+ # Add photo to the right cell
87
+ img_stream1 = process_image(item1['file'], compress=compress_images)
88
+ if img_stream1:
89
+ photo_cell1 = table1.cell(0, 1)
90
+ # Must clear the cell before adding a picture to avoid extra paragraphs
91
+ photo_cell1._element.clear_content()
92
+ p = photo_cell1.add_paragraph()
93
+ p.add_run().add_picture(img_stream1, width=Inches(4.4))
94
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
95
+
96
+ progress_bar.progress((i + 1) / num_images)
97
+
98
+ # Add space between the two items on the page
99
+ doc.add_paragraph()
100
+
101
+ # --- Second Item on Page (if it exists) ---
102
+ if i + 1 < num_images:
103
+ item2 = image_items[i + 1]
104
+ table2 = doc.add_table(rows=1, cols=2)
105
+ table2.autofit = False
106
+ table2.columns[0].width = Inches(3.0)
107
+ table2.columns[1].width = Inches(4.5)
108
+
109
+ # Add description
110
+ desc_cell2 = table2.cell(0, 0)
111
+ desc_cell2.text = item2['description']
112
+ desc_cell2.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
113
+
114
+ # Add photo
115
+ img_stream2 = process_image(item2['file'], compress=compress_images)
116
+ if img_stream2:
117
+ photo_cell2 = table2.cell(0, 1)
118
+ photo_cell2._element.clear_content()
119
+ p = photo_cell2.add_paragraph()
120
+ p.add_run().add_picture(img_stream2, width=Inches(4.4))
121
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
122
+
123
+ progress_bar.progress((i + 2) / num_images)
124
 
125
  # --- File Saving and Conversion ---
126
  doc_io = io.BytesIO()
 
133
  f.write(doc_io.getvalue())
134
  subprocess.run(
135
  ['libreoffice', '--headless', '--convert-to', 'pdf', '--outdir', '.', 'temp_report.docx'],
136
+ check=True, timeout=120 # Increased timeout for larger reports
137
  )
138
  pdf_path = 'temp_report.pdf'
139
  if os.path.exists(pdf_path):
 
156
 
157
  def reset_app():
158
  """Clears all session state and reruns the app."""
159
+ st.session_state.clear()
 
160
  st.rerun()
161
 
162
  # --- UI Layout ---
 
169
  # --- Sidebar for Settings ---
170
  with st.sidebar:
171
  st.header("1. Job Information")
 
172
  job_details = st.text_area(
173
  "Job Details",
174
  "T250014 Walmart 3155 Sault Ste. Marie\n446 Great Northern Rd\nSault Ste. Marie, Ontario, P6B 4Z9",
175
+ height=100,
176
+ help="Enter all job information here. It will appear in the header."
 
 
 
 
 
 
 
177
  )
178
+ compress_images = st.checkbox("Compress Images", value=True, help="Greatly reduces final file size.")
179
 
180
+ st.header("2. Actions")
181
  generate_button = st.button("Generate Report", type="primary", use_container_width=True)
182
  reset_button = st.button("Reset All", use_container_width=True, on_click=reset_app)
183
 
184
+ # --- Main Area ---
185
+ st.header("3. Upload, Arrange, and Describe Images")
186
  uploaded_files = st.file_uploader(
187
  "Upload all your photos here",
188
  accept_multiple_files=True,
 
191
  )
192
 
193
  if uploaded_files:
194
+ for file in uploaded_files:
195
  if file.file_id not in st.session_state.image_dict:
196
+ current_max_order = max([d.get('order', 0) for d in st.session_state.image_dict.values()], default=0)
197
  st.session_state.image_dict[file.file_id] = {
198
  'file': file,
199
  'description': '',
200
  'order': current_max_order + 1
201
  }
202
 
203
+ st.info("Set the report order using numbers, then add descriptions for each photo.")
204
 
205
+ sorted_display_items = sorted(st.session_state.image_dict.values(), key=lambda x: x.get('order', 0))
206
 
207
  for item_data in sorted_display_items:
208
  file_id = item_data['file'].file_id
209
  st.markdown("---")
210
+ cols = st.columns([2, 3])
211
 
212
  with cols[0]:
213
  st.image(item_data['file'], use_column_width=True)
214
 
215
  with cols[1]:
216
+ item_data['order'] = st.number_input("Order", min_value=1, value=item_data.get('order', 1), key=f"order_{file_id}")
217
+ item_data['description'] = st.text_area("Description", value=item_data.get('description', ''), key=f"desc_{file_id}")
218
 
219
  # --- Generation Logic ---
220
  if generate_button:
 
224
  st.error("Please upload at least one image.")
225
  else:
226
  items_to_sort = list(st.session_state.image_dict.values())
227
+ sorted_image_items = sorted(items_to_sort, key=lambda x: x.get('order', 0))
228
 
229
  progress_bar = st.progress(0, text="Starting report generation...")
230
  try:
231
+ with st.spinner('Generating your report... This may take a moment.'):
232
+ # The layout style is now fixed, so we don't need to pass it as a variable
233
  doc_io, pdf_io = generate_report(
234
  sorted_image_items,
235
  job_details,
236
+ compress_images, progress_bar
237
  )
238
  progress_bar.success("Report generated successfully!")
239
 
240
+ st.header("4. Download Your Report")
241
  dl_col1, dl_col2 = st.columns(2)
242
  dl_col1.download_button(
243
  "⬇️ Download Word (.docx)", doc_io, f"HKC_Report.docx", use_container_width=True