redxican commited on
Commit
f8e5331
·
verified ·
1 Parent(s): d8f36dd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -24
app.py CHANGED
@@ -1,7 +1,7 @@
1
  #!/usr/bin/env python3
2
  """
3
  Turbo Air Viewer - Equipment Specification Database Viewer
4
- Deployment-ready version with automatic database download and inline PDF display
5
  """
6
 
7
  import streamlit as st
@@ -17,6 +17,9 @@ import io
17
  import os
18
  import requests
19
  from urllib.parse import quote
 
 
 
20
 
21
  # Streamlit page config MUST be first
22
  st.set_page_config(
@@ -27,7 +30,6 @@ st.set_page_config(
27
 
28
  # Configuration
29
  DB_FILENAME = "turbo_air_db.sqlite"
30
- # Use the exact URL you provided
31
  DB_URL = "https://huggingface.co/spaces/TurboAir/turbo-air-viewer/resolve/main/turbo_air_db.sqlite"
32
 
33
  def download_database():
@@ -153,7 +155,6 @@ if DB_PATH is None:
153
  st.error("❌ Unable to load database. Please refresh the page to try again.")
154
  st.stop()
155
 
156
- # Rest of your original code continues here...
157
  # Product type mappings
158
  PRODUCT_TYPES = {
159
  'TSR': 'Reach-In Refrigerators',
@@ -269,6 +270,20 @@ st.markdown("""
269
  background-color: #FF5722;
270
  color: white;
271
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  </style>
273
  """, unsafe_allow_html=True)
274
 
@@ -277,6 +292,74 @@ if 'selected_model' not in st.session_state:
277
  st.session_state.selected_model = None
278
  if 'bookmarked_models' not in st.session_state:
279
  st.session_state.bookmarked_models = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
  # Cache functions
282
  @st.cache_data
@@ -629,31 +712,88 @@ if not all_models:
629
  if st.session_state.bookmarked_models:
630
  st.markdown("### 📌 Bookmarked Models")
631
 
632
- view_col1, view_col2 = st.columns([2, 1])
633
  with view_col2:
634
  st.markdown(f"**{len(st.session_state.bookmarked_models)} models selected**")
635
 
636
- # List view
637
- display_limit = 5
638
- for idx, model in enumerate(st.session_state.bookmarked_models[:display_limit]):
639
- col_model, col_remove = st.columns([5, 1])
640
- with col_model:
641
- st.text(f"• {model}")
642
- with col_remove:
643
- if st.button("❌", key=f"remove_bookmark_list_{idx}", help=f"Remove {model}"):
644
- st.session_state.bookmarked_models.remove(model)
645
- st.rerun()
646
 
647
- if len(st.session_state.bookmarked_models) > display_limit:
648
- with st.expander(f"Show all {len(st.session_state.bookmarked_models)} bookmarks"):
649
- for idx, model in enumerate(st.session_state.bookmarked_models[display_limit:], display_limit):
650
- col_model, col_remove = st.columns([5, 1])
651
- with col_model:
652
- st.text(f"• {model}")
653
- with col_remove:
654
- if st.button("❌", key=f"remove_bookmark_exp_{idx}", help=f"Remove {model}"):
655
- st.session_state.bookmarked_models.remove(model)
656
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
657
 
658
  # Export section
659
  st.markdown("---")
@@ -673,6 +813,7 @@ if st.session_state.bookmarked_models:
673
  with export_col2:
674
  if st.button("🗑️ Clear All", use_container_width=True):
675
  st.session_state.bookmarked_models = []
 
676
  st.rerun()
677
  else:
678
  st.info("📌 No models bookmarked yet. Select models to create your custom list!")
@@ -684,6 +825,7 @@ with col1:
684
  st.markdown("### 💡 Quick Tips")
685
  st.write("• View PDF spec sheets")
686
  st.write("• Bookmark models for lists")
 
687
  st.write("• Google search finds prices")
688
  st.write("• Export includes all specs")
689
 
 
1
  #!/usr/bin/env python3
2
  """
3
  Turbo Air Viewer - Equipment Specification Database Viewer
4
+ Enhanced version with product image extraction and display
5
  """
6
 
7
  import streamlit as st
 
17
  import os
18
  import requests
19
  from urllib.parse import quote
20
+ from PIL import Image
21
+ import fitz # PyMuPDF
22
+ import tempfile
23
 
24
  # Streamlit page config MUST be first
25
  st.set_page_config(
 
30
 
31
  # Configuration
32
  DB_FILENAME = "turbo_air_db.sqlite"
 
33
  DB_URL = "https://huggingface.co/spaces/TurboAir/turbo-air-viewer/resolve/main/turbo_air_db.sqlite"
34
 
35
  def download_database():
 
155
  st.error("❌ Unable to load database. Please refresh the page to try again.")
156
  st.stop()
157
 
 
158
  # Product type mappings
159
  PRODUCT_TYPES = {
160
  'TSR': 'Reach-In Refrigerators',
 
270
  background-color: #FF5722;
271
  color: white;
272
  }
273
+
274
+ .bookmark-item {
275
+ border: 1px solid #444;
276
+ border-radius: 8px;
277
+ padding: 10px;
278
+ margin-bottom: 10px;
279
+ background-color: #2d2d2d;
280
+ }
281
+
282
+ .bookmark-image {
283
+ border: 1px solid #555;
284
+ border-radius: 4px;
285
+ margin-bottom: 8px;
286
+ }
287
  </style>
288
  """, unsafe_allow_html=True)
289
 
 
292
  st.session_state.selected_model = None
293
  if 'bookmarked_models' not in st.session_state:
294
  st.session_state.bookmarked_models = []
295
+ if 'text_only_view' not in st.session_state:
296
+ st.session_state.text_only_view = False
297
+ if 'product_images' not in st.session_state:
298
+ st.session_state.product_images = {}
299
+
300
+ def extract_pdf_thumbnail(pdf_url, model_name, max_width=300, max_height=400):
301
+ """Extract first page of PDF as thumbnail image"""
302
+ cache_key = f"thumb_{model_name}"
303
+
304
+ # Check if already cached in session state
305
+ if cache_key in st.session_state.product_images:
306
+ return st.session_state.product_images[cache_key]
307
+
308
+ try:
309
+ # Download PDF to temporary file
310
+ response = requests.get(pdf_url, timeout=30)
311
+ if response.status_code == 200:
312
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
313
+ tmp_file.write(response.content)
314
+ tmp_path = tmp_file.name
315
+
316
+ # Open PDF and extract first page
317
+ pdf_document = fitz.Document(tmp_path)
318
+ first_page = pdf_document[0]
319
+
320
+ # Render page as image (2x resolution for better quality)
321
+ mat = fitz.Matrix(2, 2)
322
+ display_list = first_page.get_displaylist()
323
+ pix = display_list.get_pixmap(matrix=mat)
324
+
325
+ # Convert to PIL Image
326
+ img_data = pix.tobytes("png")
327
+ img = Image.open(io.BytesIO(img_data))
328
+
329
+ # Calculate aspect ratio and resize
330
+ width, height = img.size
331
+ aspect_ratio = width / height
332
+
333
+ if width > max_width:
334
+ new_width = max_width
335
+ new_height = int(new_width / aspect_ratio)
336
+ else:
337
+ new_width = width
338
+ new_height = height
339
+
340
+ if new_height > max_height:
341
+ new_height = max_height
342
+ new_width = int(new_height * aspect_ratio)
343
+
344
+ img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
345
+
346
+ # Convert to base64 for caching
347
+ buffered = io.BytesIO()
348
+ img.save(buffered, format="PNG")
349
+ img_base64 = base64.b64encode(buffered.getvalue()).decode()
350
+
351
+ # Cache in session state
352
+ st.session_state.product_images[cache_key] = img_base64
353
+
354
+ # Cleanup
355
+ pdf_document.close()
356
+ os.unlink(tmp_path)
357
+
358
+ return img_base64
359
+
360
+ except Exception as e:
361
+ st.warning(f"Could not extract image for {model_name}: {str(e)}")
362
+ return None
363
 
364
  # Cache functions
365
  @st.cache_data
 
712
  if st.session_state.bookmarked_models:
713
  st.markdown("### 📌 Bookmarked Models")
714
 
715
+ view_col1, view_col2, view_col3 = st.columns([2, 1, 1])
716
  with view_col2:
717
  st.markdown(f"**{len(st.session_state.bookmarked_models)} models selected**")
718
 
719
+ with view_col3:
720
+ # Toggle for text-only view
721
+ if st.button(
722
+ "📷 Show Images" if st.session_state.text_only_view else "📝 Text Only",
723
+ key="toggle_view",
724
+ use_container_width=True
725
+ ):
726
+ st.session_state.text_only_view = not st.session_state.text_only_view
727
+ st.rerun()
 
728
 
729
+ # Display bookmarked models with or without images
730
+ if st.session_state.text_only_view:
731
+ # Text-only view (original compact list)
732
+ display_limit = 5
733
+ for idx, model in enumerate(st.session_state.bookmarked_models[:display_limit]):
734
+ col_model, col_remove = st.columns([5, 1])
735
+ with col_model:
736
+ st.text(f" {model}")
737
+ with col_remove:
738
+ if st.button("❌", key=f"remove_bookmark_list_{idx}", help=f"Remove {model}"):
739
+ st.session_state.bookmarked_models.remove(model)
740
+ st.rerun()
741
+
742
+ if len(st.session_state.bookmarked_models) > display_limit:
743
+ with st.expander(f"Show all {len(st.session_state.bookmarked_models)} bookmarks"):
744
+ for idx, model in enumerate(st.session_state.bookmarked_models[display_limit:], display_limit):
745
+ col_model, col_remove = st.columns([5, 1])
746
+ with col_model:
747
+ st.text(f"• {model}")
748
+ with col_remove:
749
+ if st.button("❌", key=f"remove_bookmark_exp_{idx}", help=f"Remove {model}"):
750
+ st.session_state.bookmarked_models.remove(model)
751
+ st.rerun()
752
+ else:
753
+ # Image view
754
+ # Display in grid layout
755
+ cols_per_row = 4
756
+ for i in range(0, len(st.session_state.bookmarked_models), cols_per_row):
757
+ cols = st.columns(cols_per_row)
758
+
759
+ for j, col in enumerate(cols):
760
+ if i + j < len(st.session_state.bookmarked_models):
761
+ model = st.session_state.bookmarked_models[i + j]
762
+ model_data = get_model_data(model)
763
+
764
+ with col:
765
+ # Container for each bookmarked item
766
+ st.markdown('<div class="bookmark-item">', unsafe_allow_html=True)
767
+
768
+ # Try to get and display thumbnail
769
+ if model_data and model_data.get('file_path'):
770
+ pdf_filename = model_data['file_path'].replace('\\', '/').split('/')[-1]
771
+ pdf_url = f"https://huggingface.co/spaces/TurboAir/TurboAirViewer/resolve/main/pdfs/{pdf_filename}"
772
+
773
+ # Show loading spinner while extracting image
774
+ with st.spinner(f"Loading image for {model}..."):
775
+ img_base64 = extract_pdf_thumbnail(pdf_url, model)
776
+
777
+ if img_base64:
778
+ st.markdown(
779
+ f'<img src="data:image/png;base64,{img_base64}" '
780
+ f'style="width:100%; max-height:200px; object-fit:contain;" '
781
+ f'class="bookmark-image">',
782
+ unsafe_allow_html=True
783
+ )
784
+ else:
785
+ st.info("📄 No preview available")
786
+
787
+ # Model info
788
+ st.markdown(f"**{model}**")
789
+ st.caption(get_product_type(model))
790
+
791
+ # Remove button
792
+ if st.button("Remove", key=f"remove_img_{i}_{j}", use_container_width=True):
793
+ st.session_state.bookmarked_models.remove(model)
794
+ st.rerun()
795
+
796
+ st.markdown('</div>', unsafe_allow_html=True)
797
 
798
  # Export section
799
  st.markdown("---")
 
813
  with export_col2:
814
  if st.button("🗑️ Clear All", use_container_width=True):
815
  st.session_state.bookmarked_models = []
816
+ st.session_state.product_images = {} # Clear image cache too
817
  st.rerun()
818
  else:
819
  st.info("📌 No models bookmarked yet. Select models to create your custom list!")
 
825
  st.markdown("### 💡 Quick Tips")
826
  st.write("• View PDF spec sheets")
827
  st.write("• Bookmark models for lists")
828
+ st.write("• Toggle image/text view")
829
  st.write("• Google search finds prices")
830
  st.write("• Export includes all specs")
831