awacke1 commited on
Commit
a59b8d9
Β·
verified Β·
1 Parent(s): 9148674

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +724 -7
app.py CHANGED
@@ -223,14 +223,16 @@ def archive_current_container(database_name, container_name, client):
223
  return f"Archive error: {str(e)} 😒"
224
 
225
  # =============================================================================
226
- # ───────────── ADVANCED COSMOS FUNCTIONS (omitted for brevity) ─────────────
 
227
  # =============================================================================
228
- # … (your existing advanced cosmos functions remain here) …
 
 
229
 
230
  # =============================================================================
231
  # ───────────── NEW COSMOSDB DEMO FUNCTIONS ─────────────
232
  # Each function below corresponds to one of your provided code snippets.
233
- # They expose a simple UI (with text inputs and buttons) to run the demo code.
234
  # =============================================================================
235
 
236
  def demo_create_database():
@@ -519,9 +521,490 @@ def demo_item_response_headers():
519
  st.error(f"Error: {str(e)}")
520
 
521
  # =============================================================================
522
- # ───────────── EXISTING FUNCTIONS (GitHub, File Management, etc.) ─────────────
523
  # =============================================================================
524
- # … (your other functions remain unchanged) …
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
 
526
  # =============================================================================
527
  # ───────────── MAIN FUNCTION ─────────────
@@ -603,8 +1086,242 @@ def main():
603
  search_documents_ui(st.session_state.get("current_container"))
604
  show_sidebar_data_grid()
605
 
606
- # … (rest of your main() function continues unchanged) …
607
- # For brevity, your remaining navigation, file management, and document editing UI remain as-is.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
  if __name__ == "__main__":
610
  main()
 
223
  return f"Archive error: {str(e)} 😒"
224
 
225
  # =============================================================================
226
+ # ───────────── ADVANCED COSMOS FUNCTIONS ─────────────
227
+ # (Include your advanced Cosmos functions here if needed)
228
  # =============================================================================
229
+ # For example, you might have functions like:
230
+ # def create_new_container(...):
231
+ # ...
232
 
233
  # =============================================================================
234
  # ───────────── NEW COSMOSDB DEMO FUNCTIONS ─────────────
235
  # Each function below corresponds to one of your provided code snippets.
 
236
  # =============================================================================
237
 
238
  def demo_create_database():
 
521
  st.error(f"Error: {str(e)}")
522
 
523
  # =============================================================================
524
+ # ───────────── FILE & MEDIA MANAGEMENT FUNCTIONS ─────────────
525
  # =============================================================================
526
+ def display_saved_files_in_sidebar():
527
+ all_files = sorted([f for f in glob.glob("*.md") if not f.lower().startswith('readme')], reverse=True)
528
+ st.sidebar.markdown("## πŸ“ Files")
529
+ for file in all_files:
530
+ col1, col2, col3 = st.sidebar.columns([6, 2, 1])
531
+ with col1:
532
+ st.markdown(f"πŸ“„ {file}")
533
+ with col2:
534
+ st.sidebar.download_button(
535
+ label="⬇️",
536
+ data=open(file, 'rb').read(),
537
+ file_name=file
538
+ )
539
+ with col3:
540
+ if st.sidebar.button("πŸ—‘", key=f"delete_{file}"):
541
+ os.remove(file)
542
+ st.rerun()
543
+
544
+ def display_file_viewer(file_path):
545
+ content = load_file(file_path)
546
+ if content:
547
+ st.markdown("### πŸ“„ File Viewer")
548
+ st.markdown(f"**{file_path}**")
549
+ file_stats = os.stat(file_path)
550
+ st.markdown(f"**Mod:** {datetime.fromtimestamp(file_stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')} | **Size:** {file_stats.st_size} bytes")
551
+ st.markdown("---")
552
+ st.markdown(content)
553
+ st.download_button("⬇️", data=content, file_name=os.path.basename(file_path), mime="text/markdown")
554
+
555
+ def display_file_editor(file_path):
556
+ if 'file_content' not in st.session_state:
557
+ st.session_state.file_content = {}
558
+ if file_path not in st.session_state.file_content:
559
+ content = load_file(file_path)
560
+ if content is not None:
561
+ st.session_state.file_content[file_path] = content
562
+ else:
563
+ return
564
+ st.markdown("### ✏️ Edit File")
565
+ st.markdown(f"**Editing:** {file_path}")
566
+ md_tab, code_tab = st.tabs(["Markdown", "Code"])
567
+ with md_tab:
568
+ st.markdown(st.session_state.file_content[file_path])
569
+ with code_tab:
570
+ new_content = st.text_area("Edit:", value=st.session_state.file_content[file_path], height=400, key=f"editor_{hash(file_path)}", on_change=lambda: None)
571
+ col1, col2 = st.columns([1, 5])
572
+ with col1:
573
+ if st.button("πŸ’Ύ Save"):
574
+ sanitized = sanitize_json_text(new_content)
575
+ try:
576
+ json.loads(sanitized)
577
+ except Exception as e:
578
+ st.error(f"Sanitization failed: {str(e)}")
579
+ return
580
+ if save_file_content(file_path, sanitized):
581
+ st.session_state.file_content[file_path] = sanitized
582
+ st.success("Saved! πŸŽ‰")
583
+ time.sleep(1)
584
+ st.rerun()
585
+ with col2:
586
+ st.download_button("⬇️", data=new_content, file_name=os.path.basename(file_path), mime="text/markdown")
587
+
588
+ def save_file_content(file_path, content):
589
+ try:
590
+ with open(file_path, 'w', encoding='utf-8') as file:
591
+ file.write(content)
592
+ return True
593
+ except Exception as e:
594
+ st.error(f"Save error: {str(e)}")
595
+ return False
596
+
597
+ def update_file_management_section():
598
+ if 'file_view_mode' not in st.session_state:
599
+ st.session_state.file_view_mode = None
600
+ if 'current_file' not in st.session_state:
601
+ st.session_state.current_file = None
602
+ if 'file_content' not in st.session_state:
603
+ st.session_state.file_content = {}
604
+ all_files = sorted(glob.glob("*.md"), reverse=True)
605
+ st.sidebar.title("πŸ“ Files")
606
+ if st.sidebar.button("πŸ—‘ Delete All"):
607
+ for file in all_files:
608
+ os.remove(file)
609
+ st.session_state.file_content = {}
610
+ st.session_state.current_file = None
611
+ st.session_state.file_view_mode = None
612
+ st.rerun()
613
+ if st.sidebar.button("⬇️ Download All"):
614
+ zip_file = create_zip_of_files(all_files)
615
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
616
+ for file in all_files:
617
+ col1, col2, col3, col4 = st.sidebar.columns([1, 3, 1, 1])
618
+ with col1:
619
+ if st.button("🌐", key=f"view_{file}"):
620
+ st.session_state.current_file = file
621
+ st.session_state.file_view_mode = 'view'
622
+ if file not in st.session_state.file_content:
623
+ content = load_file(file)
624
+ if content is not None:
625
+ st.session_state.file_content[file] = content
626
+ st.rerun()
627
+ with col2:
628
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
629
+ with col3:
630
+ if st.button("πŸ“‚", key=f"edit_{file}"):
631
+ st.session_state.current_file = file
632
+ st.session_state.file_view_mode = 'edit'
633
+ if file not in st.session_state.file_content:
634
+ content = load_file(file)
635
+ if content is not None:
636
+ st.session_state.file_content[file] = content
637
+ st.rerun()
638
+ with col4:
639
+ if st.button("πŸ—‘", key=f"delete_{file}"):
640
+ os.remove(file)
641
+ if file in st.session_state.file_content:
642
+ del st.session_state.file_content[file]
643
+ if st.session_state.current_file == file:
644
+ st.session_state.current_file = None
645
+ st.session_state.file_view_mode = None
646
+ st.rerun()
647
+ st.sidebar.markdown("---")
648
+ st.sidebar.title("External Help Links")
649
+ for link in external_links:
650
+ st.sidebar.markdown(f"{link['emoji']} [{link['title']}]({link['url']})", unsafe_allow_html=True)
651
+ if st.session_state.current_file:
652
+ if st.session_state.file_view_mode == 'view':
653
+ display_file_viewer(st.session_state.current_file)
654
+ elif st.session_state.file_view_mode == 'edit':
655
+ display_file_editor(st.session_state.current_file)
656
+
657
+ # =============================================================================
658
+ # ───────────── SIDEBAR DATA GRID (Records with formatted timestamps) ─────────────
659
+ # =============================================================================
660
+ def show_sidebar_data_grid():
661
+ if st.session_state.get("current_container"):
662
+ try:
663
+ records = get_documents(st.session_state.current_container)
664
+ data = []
665
+ for rec in records:
666
+ ts = rec.get("timestamp", "")
667
+ try:
668
+ dt = datetime.fromisoformat(ts)
669
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
670
+ except Exception:
671
+ formatted = ts
672
+ data.append({
673
+ "ID": rec.get("id", ""),
674
+ "Name": rec.get("name", ""),
675
+ "Timestamp": formatted
676
+ })
677
+ df = pd.DataFrame(data)
678
+ st.sidebar.markdown("### πŸ“Š Data Grid")
679
+ st.sidebar.dataframe(df[["Name", "Timestamp"]])
680
+ except Exception as e:
681
+ st.sidebar.error(f"Data grid error: {str(e)}")
682
+ else:
683
+ st.sidebar.info("No container selected for data grid.")
684
+
685
+ # =============================================================================
686
+ # ───────────── DOCUMENTS LIST VIEW (Editable List with Sorting) ─────────────
687
+ # =============================================================================
688
+ def edit_documents_list(container):
689
+ records = get_documents(container)
690
+ sort_option = st.selectbox("Sort by", ["Timestamp", "Name"], key="sort_option")
691
+ if sort_option == "Name":
692
+ records.sort(key=lambda r: r.get("name", "").lower())
693
+ else:
694
+ records.sort(key=lambda r: r.get("timestamp", ""), reverse=True)
695
+ data = []
696
+ for rec in records:
697
+ ts = rec.get("timestamp", "")
698
+ try:
699
+ dt = datetime.fromisoformat(ts)
700
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
701
+ except Exception:
702
+ formatted = ts
703
+ data.append({
704
+ "ID": rec.get("id", ""),
705
+ "Name": rec.get("name", ""),
706
+ "Content": rec.get("content", "")[:100] + "..." if rec.get("content", "") else "",
707
+ "Timestamp": formatted
708
+ })
709
+ df = pd.DataFrame(data)
710
+ edited_df = st.data_editor(df[["Name", "Content", "Timestamp"]], key="docs_editor", num_rows="dynamic")
711
+ if st.button("πŸ’Ύ Save List Changes"):
712
+ for idx, row in edited_df.iterrows():
713
+ original = data[idx]
714
+ if row["Name"] != original["Name"] or row["Content"] != original["Content"]:
715
+ doc_id = original["ID"]
716
+ doc = next((r for r in records if r.get("id") == doc_id), None)
717
+ if doc:
718
+ doc["name"] = row["Name"]
719
+ doc["content"] = row["Content"]
720
+ success, message = update_record(container, doc)
721
+ if success:
722
+ st.success(f"Updated {doc_id} πŸ‘")
723
+ else:
724
+ st.error(f"Error updating {doc_id}: {message}")
725
+ st.rerun()
726
+
727
+ # =============================================================================
728
+ # ───────────── VIDEO & AUDIO UI FUNCTIONS ─────────────
729
+ # =============================================================================
730
+ def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
731
+ try:
732
+ st.write("Preprocessing image...")
733
+ if isinstance(file_data, bytes):
734
+ img = Image.open(io.BytesIO(file_data))
735
+ elif hasattr(file_data, 'read'):
736
+ if hasattr(file_data, 'seek'):
737
+ file_data.seek(0)
738
+ img = Image.open(file_data)
739
+ elif isinstance(file_data, Image.Image):
740
+ img = file_data
741
+ else:
742
+ raise ValueError(f"Unsupported input: {type(file_data)}")
743
+ if img.mode != 'RGB':
744
+ img = img.convert('RGB')
745
+ aspect_ratio = img.size[0] / img.size[1]
746
+ if aspect_ratio > target_size[0] / target_size[1]:
747
+ new_width = target_size[0]
748
+ new_height = int(new_width / aspect_ratio)
749
+ else:
750
+ new_height = target_size[1]
751
+ new_width = int(new_height * aspect_ratio)
752
+ new_width = (new_width // 2) * 2
753
+ new_height = (new_height // 2) * 2
754
+ resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
755
+ final_img = Image.new('RGB', target_size, (255, 255, 255))
756
+ paste_x = (target_size[0] - new_width) // 2
757
+ paste_y = (target_size[1] - new_height) // 2
758
+ final_img.paste(resized_img, (paste_x, paste_y))
759
+ return final_img
760
+ except Exception as e:
761
+ st.error(f"Image error: {str(e)}")
762
+ return None
763
+
764
+ def add_video_generation_ui(container):
765
+ st.markdown("### πŸŽ₯ Video Gen")
766
+ col1, col2 = st.columns([2, 1])
767
+ with col1:
768
+ uploaded_file = st.file_uploader("Upload Image πŸ–ΌοΈ", type=['png', 'jpg', 'jpeg'])
769
+ with col2:
770
+ st.markdown("#### Params")
771
+ motion = st.slider("🌊 Motion", 1, 255, 127)
772
+ fps = st.slider("🎬 FPS", 1, 30, 6)
773
+ with st.expander("Advanced"):
774
+ use_custom = st.checkbox("Custom Seed")
775
+ seed = st.number_input("Seed", value=int(time.time() * 1000)) if use_custom else None
776
+ if uploaded_file is not None:
777
+ try:
778
+ file_data = uploaded_file.read()
779
+ preview1, preview2 = st.columns(2)
780
+ with preview1:
781
+ st.write("Original")
782
+ st.image(Image.open(io.BytesIO(file_data)), use_column_width=True)
783
+ with preview2:
784
+ proc_img = validate_and_preprocess_image(io.BytesIO(file_data))
785
+ if proc_img:
786
+ st.write("Processed")
787
+ st.image(proc_img, use_column_width=True)
788
+ else:
789
+ st.error("Preprocess failed")
790
+ return
791
+ if st.button("πŸŽ₯ Generate"):
792
+ with st.spinner("Generating video..."):
793
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
794
+ proc_img.save(temp_file.name, format='PNG')
795
+ try:
796
+ client = Client("awacke1/stable-video-diffusion", hf_token=os.environ.get("HUGGINGFACE_TOKEN"))
797
+ result = client.predict(
798
+ image=temp_file.name,
799
+ seed=seed if seed is not None else int(time.time() * 1000),
800
+ randomize_seed=seed is None,
801
+ motion_bucket_id=motion,
802
+ fps_id=fps,
803
+ api_name="/video"
804
+ )
805
+ if result and isinstance(result, tuple) and len(result) >= 1:
806
+ video_path = result[0].get('video') if isinstance(result[0], dict) else None
807
+ if video_path and os.path.exists(video_path):
808
+ video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
809
+ shutil.copy(video_path, video_filename)
810
+ st.success(f"Video generated! πŸŽ‰")
811
+ st.video(video_filename)
812
+ if container:
813
+ video_record = {
814
+ "id": generate_unique_id(),
815
+ "pk": generate_unique_id(),
816
+ "type": "generated_video",
817
+ "filename": video_filename,
818
+ "seed": seed if seed is not None else "random",
819
+ "motion": motion,
820
+ "fps": fps,
821
+ "timestamp": datetime.now().isoformat()
822
+ }
823
+ success, message = insert_record(container, video_record)
824
+ if success:
825
+ st.success("DB record saved!")
826
+ else:
827
+ st.error(f"DB error: {message}")
828
+ else:
829
+ st.error("Invalid result format")
830
+ else:
831
+ st.error("No result returned")
832
+ except Exception as e:
833
+ st.error(f"Video gen error: {str(e)}")
834
+ finally:
835
+ try:
836
+ os.unlink(temp_file.name)
837
+ st.write("Temp file removed")
838
+ except Exception as e:
839
+ st.warning(f"Cleanup error: {str(e)}")
840
+ except Exception as e:
841
+ st.error(f"Upload error: {str(e)}")
842
+
843
+ # =============================================================================
844
+ # ───────────── NEW ITEM & FIELD FUNCTIONS ─────────────
845
+ # =============================================================================
846
+ def new_item_default(container):
847
+ new_id = generate_unique_id()
848
+ default_doc = {
849
+ "id": new_id,
850
+ "pk": new_id,
851
+ "name": "New Sample Document",
852
+ "content": "Start editing your document here...",
853
+ "timestamp": datetime.now().isoformat(),
854
+ "type": "sample"
855
+ }
856
+ success, message = insert_record(container, default_doc)
857
+ if success:
858
+ st.success("New sample document created! ✨")
859
+ return default_doc
860
+ else:
861
+ st.error("Error creating new item: " + message)
862
+ return None
863
+
864
+ def auto_save_edit():
865
+ try:
866
+ edited_str = st.session_state.doc_editor
867
+ try:
868
+ json.loads(edited_str)
869
+ except Exception:
870
+ edited_str = sanitize_json_text(edited_str)
871
+ edited_doc = json.loads(edited_str)
872
+ container = st.session_state.current_container
873
+ container.upsert_item(edited_doc)
874
+ st.success("Auto-saved! πŸ’Ύ")
875
+ except Exception as e:
876
+ st.error(f"Auto-save error: {str(e)}")
877
+
878
+ def add_field_to_doc():
879
+ key = st.session_state.new_field_key
880
+ value = st.session_state.new_field_value
881
+ try:
882
+ doc = json.loads(st.session_state.doc_editor)
883
+ doc[key] = value
884
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
885
+ auto_save_edit()
886
+ st.success(f"Added field {key} πŸ‘")
887
+ except Exception as e:
888
+ st.error(f"Error adding field: {str(e)}")
889
+
890
+ # =============================================================================
891
+ # ───────────── VECTOR SEARCH INTERFACE ─────────────
892
+ # =============================================================================
893
+ def vector_keyword_search(keyword, container):
894
+ try:
895
+ query = f"SELECT * FROM c WHERE CONTAINS(c.content, '{keyword}')"
896
+ results = list(container.query_items(query=query, enable_cross_partition_query=True))
897
+ return results
898
+ except Exception as e:
899
+ st.error(f"Vector search error: {str(e)}")
900
+ return []
901
+
902
+ def display_search_results(keyword, container):
903
+ results = vector_keyword_search(keyword, container)
904
+ st.markdown("### πŸ” Search Results")
905
+ for res in results:
906
+ doc_id = res.get("id", "")
907
+ exp = st.expander(f"Result {doc_id}")
908
+ with exp:
909
+ edited = st.text_area("Edit Document", value=json.dumps(res, indent=2), key=f"search_{doc_id}")
910
+ if st.button(f"πŸ’Ύ Save changes for {doc_id}", key=f"save_search_{doc_id}"):
911
+ try:
912
+ updated_doc = json.loads(edited)
913
+ container.upsert_item(body=updated_doc)
914
+ st.success(f"Updated {doc_id}!")
915
+ st.rerun()
916
+ except Exception as e:
917
+ st.error(f"Error saving {doc_id}: {str(e)}")
918
+
919
+ # =============================================================================
920
+ # ───────────── NEW AI MODALITY RECORD TEMPLATES ─────────────
921
+ # =============================================================================
922
+ def new_ai_record(container):
923
+ new_id = generate_unique_id()
924
+ default_doc = {
925
+ "id": new_id,
926
+ "pk": new_id,
927
+ "name": "AI Modality Record",
928
+ "function_url": "https://example.com/function",
929
+ "input_text": "### Input (markdown)\n\nType your input here.",
930
+ "output_text": "### Output (markdown)\n\nResult will appear here.",
931
+ "timestamp": datetime.now().isoformat(),
932
+ "type": "ai_modality"
933
+ }
934
+ success, message = insert_record(container, default_doc)
935
+ if success:
936
+ st.success("New AI modality record created! πŸ’‘")
937
+ return default_doc
938
+ else:
939
+ st.error("Error creating AI record: " + message)
940
+ return None
941
+
942
+ def new_links_record(container):
943
+ new_id = generate_unique_id()
944
+ links_md = "\n".join([f"- {link['emoji']} [{link['title']}]({link['url']})" for link in external_links])
945
+ default_doc = {
946
+ "id": new_id,
947
+ "pk": new_id,
948
+ "name": "Portal Links Record",
949
+ "function_url": "",
950
+ "input_text": links_md,
951
+ "output_text": "",
952
+ "timestamp": datetime.now().isoformat(),
953
+ "type": "ai_modality"
954
+ }
955
+ success, message = insert_record(container, default_doc)
956
+ if success:
957
+ st.success("New Portal Links record created! πŸ”—")
958
+ return default_doc
959
+ else:
960
+ st.error("Error creating links record: " + message)
961
+ return None
962
+
963
+ # =============================================================================
964
+ # ───────────── SIDEBAR DATA GRID (Editable Names Grid) ─────────────
965
+ # =============================================================================
966
+ def edit_names_grid(container):
967
+ records = get_documents(container)
968
+ data = []
969
+ for rec in records:
970
+ ts = rec.get("timestamp", "")
971
+ try:
972
+ dt = datetime.fromisoformat(ts)
973
+ formatted = dt.strftime("%I:%M %p %m/%d/%Y")
974
+ except Exception:
975
+ formatted = ts
976
+ data.append({
977
+ "ID": rec.get("id", ""),
978
+ "Name": rec.get("name", ""),
979
+ "Timestamp": formatted
980
+ })
981
+ df = pd.DataFrame(data)
982
+ edited_df = st.sidebar.data_editor(df[["Name", "Timestamp"]], key="names_editor", num_rows="dynamic")
983
+ if st.sidebar.button("πŸ’Ύ Save Name Changes"):
984
+ for idx, row in edited_df.iterrows():
985
+ original = df.iloc[idx]
986
+ if row["Name"] != original["Name"]:
987
+ doc_id = original["ID"]
988
+ doc = next((r for r in records if r.get("id") == doc_id), None)
989
+ if doc:
990
+ doc["name"] = row["Name"]
991
+ success, message = update_record(container, doc)
992
+ if success:
993
+ st.sidebar.success(f"Updated Name for {doc_id} to '{row['Name']}'")
994
+ else:
995
+ st.sidebar.error(f"Update error for {doc_id}: {message}")
996
+ st.rerun()
997
+
998
+ # =============================================================================
999
+ # ───────────── SEARCH DOCUMENTS UI ─────────────
1000
+ # This function was missing and is now defined.
1001
+ # =============================================================================
1002
+ def search_documents_ui(container):
1003
+ with st.sidebar.form("search_form"):
1004
+ keyword = st.text_input("Search Keyword", key="search_keyword")
1005
+ submitted = st.form_submit_button("πŸ” Search")
1006
+ if submitted and keyword:
1007
+ display_search_results(keyword, container)
1008
 
1009
  # =============================================================================
1010
  # ───────────── MAIN FUNCTION ─────────────
 
1086
  search_documents_ui(st.session_state.get("current_container"))
1087
  show_sidebar_data_grid()
1088
 
1089
+ # (Your remaining navigation, file management, and document editing UI follow)
1090
+ try:
1091
+ if st.session_state.get("client") is None:
1092
+ st.session_state.client = CosmosClient(ENDPOINT, credential=st.session_state.primary_key)
1093
+ st.sidebar.title("πŸ™ Navigator")
1094
+ databases = get_databases(st.session_state.client)
1095
+ selected_db = st.sidebar.selectbox("πŸ—ƒοΈ DB", databases)
1096
+ if selected_db != st.session_state.get("selected_database"):
1097
+ st.session_state.selected_database = selected_db
1098
+ st.session_state.selected_container = None
1099
+ st.session_state.selected_document_id = None
1100
+ st.session_state.current_index = 0
1101
+ st.rerun()
1102
+ if st.session_state.selected_database:
1103
+ database = st.session_state.client.get_database_client(st.session_state.selected_database)
1104
+ if "show_new_container_form" not in st.session_state:
1105
+ st.session_state.show_new_container_form = False
1106
+ if st.sidebar.button("πŸ†• New Container"):
1107
+ st.session_state.show_new_container_form = True
1108
+ if st.session_state.show_new_container_form:
1109
+ with st.sidebar.form("new_container_form"):
1110
+ new_container_id = st.text_input("Container ID", value="aiml-container")
1111
+ new_partition_key = st.text_input("Partition Key", value="/pk")
1112
+ new_analytical = st.checkbox("Enable Analytical Store", value=True)
1113
+ submitted = st.form_submit_button("Create Container")
1114
+ if submitted:
1115
+ analytical_ttl = -1 if new_analytical else None
1116
+ # Assuming create_new_container is defined in your advanced functions:
1117
+ new_container = database.create_container(
1118
+ id=new_container_id,
1119
+ partition_key=PartitionKey(path=new_partition_key),
1120
+ analytical_storage_ttl=analytical_ttl
1121
+ )
1122
+ if new_container:
1123
+ st.success(f"Container '{new_container_id}' created.")
1124
+ default_id = generate_unique_id()
1125
+ default_item = {
1126
+ "id": default_id,
1127
+ "pk": default_id,
1128
+ "name": "Default Image Prompt",
1129
+ "prompt": "Enter your image prompt here",
1130
+ "timestamp": datetime.now().isoformat(),
1131
+ "type": "image_prompt"
1132
+ }
1133
+ insert_success, insert_message = insert_record(new_container, default_item)
1134
+ if insert_success:
1135
+ st.info("Default templated item created in new container.")
1136
+ else:
1137
+ st.error(f"Default item insertion error: {insert_message}")
1138
+ st.session_state.show_new_container_form = False
1139
+ st.session_state.new_container_created = new_container_id
1140
+ st.rerun()
1141
+ containers = get_containers(database)
1142
+ if "new_container_created" in st.session_state and st.session_state.new_container_created not in containers:
1143
+ containers.append(st.session_state.new_container_created)
1144
+ selected_container = st.sidebar.selectbox("πŸ“ Container", containers)
1145
+ if selected_container != st.session_state.get("selected_container"):
1146
+ st.session_state.selected_container = selected_container
1147
+ st.session_state.selected_document_id = None
1148
+ st.session_state.current_index = 0
1149
+ st.rerun()
1150
+ if st.session_state.selected_container:
1151
+ container = database.get_container_client(st.session_state.selected_container)
1152
+ st.session_state.current_container = container
1153
+ if st.sidebar.button("πŸ“¦ Export"):
1154
+ download_link = archive_current_container(st.session_state.selected_database, st.session_state.selected_container, st.session_state.client)
1155
+ if download_link.startswith('<a'):
1156
+ st.markdown(download_link, unsafe_allow_html=True)
1157
+ else:
1158
+ st.error(download_link)
1159
+ documents = get_documents(container)
1160
+ total_docs = len(documents)
1161
+ num_docs = st.slider("Docs", 1, 20, 1)
1162
+ documents_to_display = documents[:num_docs] if total_docs > num_docs else documents
1163
+ st.sidebar.info(f"Showing {len(documents_to_display)} docs")
1164
+ view_options = ['List', 'Markdown', 'Code', 'Run AI', 'Clone', 'New']
1165
+ selected_view = st.sidebar.selectbox("View", view_options, index=1)
1166
+ if selected_view == 'List':
1167
+ edit_documents_list(container)
1168
+ elif selected_view == 'Markdown':
1169
+ st.markdown("#### πŸ“„ Markdown")
1170
+ if documents:
1171
+ doc = documents[st.session_state.current_index]
1172
+ content = json.dumps(doc, indent=2)
1173
+ st.markdown(f"```json\n{content}\n```")
1174
+ col_prev, col_next = st.columns(2)
1175
+ with col_prev:
1176
+ if st.button("⬅️") and st.session_state.current_index > 0:
1177
+ st.session_state.current_index -= 1
1178
+ st.rerun()
1179
+ with col_next:
1180
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
1181
+ st.session_state.current_index += 1
1182
+ st.rerun()
1183
+ elif selected_view == 'Code':
1184
+ st.markdown("#### πŸ’» Code Editor")
1185
+ if documents:
1186
+ doc = documents[st.session_state.current_index]
1187
+ if "doc_editor" not in st.session_state:
1188
+ st.session_state.doc_editor = json.dumps(doc, indent=2)
1189
+ edited = st.text_area("Edit JSON", value=st.session_state.doc_editor, height=300, key="doc_editor", on_change=lambda: auto_save_edit())
1190
+ col_prev, col_next = st.columns(2)
1191
+ with col_prev:
1192
+ if st.button("⬅️") and st.session_state.current_index > 0:
1193
+ st.session_state.current_index -= 1
1194
+ st.rerun()
1195
+ with col_next:
1196
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
1197
+ st.session_state.current_index += 1
1198
+ st.rerun()
1199
+ col_save, col_delete = st.columns(2)
1200
+ with col_save:
1201
+ if st.button("πŸ’Ύ Save", key=f'save_{st.session_state.current_index}'):
1202
+ try:
1203
+ updated_doc = json.loads(edited)
1204
+ container.upsert_item(body=updated_doc)
1205
+ st.success(f"Saved {updated_doc['id']}")
1206
+ st.rerun()
1207
+ except Exception as e:
1208
+ st.error(f"Save err: {str(e)}")
1209
+ with col_delete:
1210
+ if st.button("πŸ—‘οΈ Delete", key=f'delete_{st.session_state.current_index}'):
1211
+ try:
1212
+ current_doc = json.loads(edited)
1213
+ success, message = delete_record(container, current_doc)
1214
+ if success:
1215
+ st.success(message)
1216
+ st.rerun()
1217
+ else:
1218
+ st.error(message)
1219
+ except Exception as e:
1220
+ st.error(f"Delete err: {str(e)}")
1221
+ elif selected_view == 'Run AI':
1222
+ st.markdown("#### πŸ€– Run AI")
1223
+ ai_query = st.text_area("Enter your query for ArXiv search:", key="arxiv_query", height=100)
1224
+ if st.button("Send"):
1225
+ st.session_state.last_query = ai_query
1226
+ # perform_ai_lookup would be your AI function call.
1227
+ st.info("Performing AI lookup (function not implemented in this demo)...")
1228
+ elif selected_view == 'Clone':
1229
+ st.markdown("#### πŸ“„ Clone")
1230
+ if documents:
1231
+ doc = documents[st.session_state.current_index]
1232
+ st.markdown(f"Original ID: {doc.get('id', '')}")
1233
+ new_id = st.text_input("New ID", value=generate_unique_id(), key='new_clone_id')
1234
+ new_name = st.text_input("New Name", value=f"Clone_{new_id[:8]}", key='new_clone_name')
1235
+ new_doc = {'id': new_id, 'pk': new_id, 'name': new_name, **{k: v for k, v in doc.items() if k not in ['id', 'name', 'pk', '_rid', '_self', '_etag', '_attachments', '_ts']}}
1236
+ doc_str = st.text_area("Edit JSON", value=json.dumps(new_doc, indent=2), height=300, key='clone_preview')
1237
+ col1, col2 = st.columns(2)
1238
+ with col1:
1239
+ if st.button("πŸ”„ Regenerate"):
1240
+ new_id = generate_unique_id()
1241
+ st.session_state.new_clone_id = new_id
1242
+ st.rerun()
1243
+ with col2:
1244
+ if st.button("πŸ’Ύ Save Clone"):
1245
+ try:
1246
+ final_doc = json.loads(doc_str)
1247
+ for field in ['_rid', '_self', '_etag', '_attachments', '_ts']:
1248
+ final_doc.pop(field, None)
1249
+ container.create_item(body=final_doc)
1250
+ st.success(f"Cloned {final_doc['id']}")
1251
+ st.rerun()
1252
+ except Exception as e:
1253
+ st.error(f"Clone err: {str(e)}")
1254
+ col_prev, col_next = st.columns(2)
1255
+ with col_prev:
1256
+ if st.button("⬅️") and st.session_state.current_index > 0:
1257
+ st.session_state.current_index -= 1
1258
+ st.rerun()
1259
+ with col_next:
1260
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
1261
+ st.session_state.current_index += 1
1262
+ st.rerun()
1263
+ elif selected_view == 'New':
1264
+ st.markdown("#### βž• New Doc")
1265
+ if st.button("πŸ€– Auto-Gen"):
1266
+ auto_doc = {
1267
+ "id": generate_unique_id(),
1268
+ "pk": generate_unique_id(),
1269
+ "name": f"Auto {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
1270
+ "content": "Auto-generated record.",
1271
+ "timestamp": datetime.now().isoformat()
1272
+ }
1273
+ success, message = insert_record(container, auto_doc)
1274
+ if success:
1275
+ st.success(message)
1276
+ st.rerun()
1277
+ else:
1278
+ st.error(message)
1279
+ else:
1280
+ new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
1281
+ default_doc = {
1282
+ "id": new_id,
1283
+ "pk": new_id,
1284
+ "name": "New Doc",
1285
+ "content": "",
1286
+ "timestamp": datetime.now().isoformat()
1287
+ }
1288
+ new_doc_str = st.text_area("JSON", value=json.dumps(default_doc, indent=2), height=300)
1289
+ if st.button("βž• Create"):
1290
+ try:
1291
+ cleaned = preprocess_text(new_doc_str)
1292
+ new_doc = json.loads(cleaned)
1293
+ new_doc['id'] = new_id
1294
+ new_doc['pk'] = new_id
1295
+ success, message = insert_record(container, new_doc)
1296
+ if success:
1297
+ st.success(f"Created {new_doc['id']}")
1298
+ st.rerun()
1299
+ else:
1300
+ st.error(message)
1301
+ except Exception as e:
1302
+ st.error(f"Create err: {str(e)}")
1303
+ st.subheader(f"πŸ“Š {st.session_state.selected_container}")
1304
+ if documents_to_display:
1305
+ df = pd.DataFrame(documents_to_display)
1306
+ st.dataframe(df)
1307
+ else:
1308
+ st.info("No docs.")
1309
+ update_file_management_section()
1310
+ except exceptions.CosmosHttpResponseError as e:
1311
+ st.error(f"Cosmos error: {str(e)} 🚨")
1312
+ except Exception as e:
1313
+ st.error(f"Error: {str(e)} 😱")
1314
+ if st.session_state.logged_in and st.sidebar.button("πŸšͺ Logout"):
1315
+ st.markdown("#### πŸšͺ Logout")
1316
+ st.session_state.logged_in = False
1317
+ st.session_state.selected_records = []
1318
+ st.session_state.client = None
1319
+ st.session_state.selected_database = None
1320
+ st.session_state.selected_container = None
1321
+ st.session_state.selected_document_id = None
1322
+ st.session_state.current_index = 0
1323
+ st.rerun()
1324
+ show_sidebar_data_grid()
1325
 
1326
  if __name__ == "__main__":
1327
  main()