11import streamlit as st
22import pandas as pd
3- import tempfile
3+ import tempfile
44import os
55from googleapiclient .discovery import build
66from googleapiclient .http import MediaFileUpload
1212FOLDER_ID = st .secrets ["FOLDER_ID" ]
1313SHEET_ID = SPREADSHEET_ID
1414
15+
1516def get_gdrive_service ():
1617 """Authenticates using the OAuth Refresh Token from st.secrets"""
1718 creds_info = st .secrets ["google_oauth" ]
1819 creds = Credentials (
19- token = None ,
20+ token = None ,
2021 refresh_token = creds_info ["refresh_token" ],
2122 client_id = creds_info ["client_id" ],
2223 client_secret = creds_info ["client_secret" ],
2324 token_uri = "https://oauth2.googleapis.com/token"
2425 )
2526 if not creds .valid :
2627 creds .refresh (Request ())
27- drive_service = build (' drive' , 'v3' , credentials = creds )
28- sheets_service = build (' sheets' , 'v4' , credentials = creds )
28+ drive_service = build (" drive" , "v3" , credentials = creds )
29+ sheets_service = build (" sheets" , "v4" , credentials = creds )
2930 return drive_service , sheets_service
3031
32+
3133# Initialize the services
3234try :
3335 drive_service , sheets_service = get_gdrive_service ()
3436except Exception as e :
3537 st .error (f"Authentication Failed: { e } " )
3638 st .stop ()
3739
40+
3841# --- 2. APP INTERFACE CONFIG ---
39- st .set_page_config (page_title = "Scientific Slide Library" , layout = "wide" , page_icon = "🔬" )
42+ st .set_page_config (
43+ page_title = "Scientific Slide Library" ,
44+ layout = "wide" ,
45+ page_icon = "🔬"
46+ )
4047
41- # 5) BACKGROUND COLOR - Tag: BACKGROUND_STYLE
48+ # --- 3. GLOBAL STYLES ---
4249st .markdown (
4350 """
4451 <style>
4552 .stApp {
46- background-color: #f0f0f0 ;
53+ background-color: #e6e6e6 ;
4754 }
48- .stDataFrame {
55+
56+ div[data-testid="stDataFrame"] {
4957 background-color: white;
50- border-radius: 5px;
58+ border-radius: 8px;
59+ padding: 6px;
60+ box-shadow: 0 2px 6px rgba(0,0,0,0.08);
61+ }
62+
63+ div[data-testid="stDataFrame"] td {
64+ white-space: normal !important;
65+ word-wrap: break-word;
66+ }
67+
68+ div[data-testid="stDataFrame"] tr:hover {
69+ background-color: #f5f7fa;
70+ }
71+
72+ h1, h2, h3 {
73+ color: #2c2c2c;
5174 }
5275 </style>
5376 """ ,
5477 unsafe_allow_html = True
5578)
5679
57- # 4) HEADER TEXT - Tag: HEADER_SECTION
58- # Feel free to edit the text inside these quotes
80+ # --- 4. HEADER ---
5981st .title ("🔬 Scientific Slide Library" )
60- st .markdown ("""
61- Welcome to the central resource repository. Use this dashboard to browse through
62- curated scientific presentations, search for specific topics, and access files directly.
63- """ )
64- # --- END HEADER SECTION ---
82+ st .markdown (
83+ """
84+ Welcome to the central resource repository.
85+ Browse curated scientific presentations, search for specific topics,
86+ and access downloadable files directly.
87+ """
88+ )
6589
66- # 1) SWAP TABS - Browse Library is now first (Default)
90+ # --- 5. TABS ---
6791tab1 , tab2 = st .tabs (["📚 Browse Library" , "📤 Upload New Slides" ])
6892
93+
94+ # =========================
95+ # 📚 TAB 1: BROWSE LIBRARY
96+ # =========================
6997with tab1 :
7098 st .header ("Library Database" )
71- st .write ("Search, filter, and access all stored presentations." )
72-
99+
100+ st .subheader ("🔍 Search Library" )
101+ search_query = st .text_input (
102+ "" ,
103+ placeholder = "Search by title, description, keyword, or contact…"
104+ )
105+
73106 try :
74- # Fetch all data from the Google Sheet
75107 result = sheets_service .spreadsheets ().values ().get (
76- spreadsheetId = SHEET_ID , range = "Sheet1!A:Z"
108+ spreadsheetId = SHEET_ID ,
109+ range = "Sheet1!A:Z"
77110 ).execute ()
78-
79- values = result .get (' values' , [])
111+
112+ values = result .get (" values" , [])
80113
81114 if not values :
82115 st .info ("The library is currently empty. Start by uploading a file!" )
83116 else :
84- # Create a DataFrame (assumes headers: Name, Description, Keywords, Link, Person)
85117 df = pd .DataFrame (values [1 :], columns = values [0 ])
86-
87- # --- 3) TWO LINK COLUMNS LOGIC ---
88- # We create a "Download" link by modifying the Google Drive "View" link
89- # Converting 'file/d/ID/view' to 'uc?export=download&id=ID'
118+
119+ # --- CREATE DOWNLOAD LINK ---
90120 def make_download_url (view_url ):
91121 try :
92122 if "drive.google.com" in view_url :
93123 file_id = view_url .split ("/d/" )[1 ].split ("/" )[0 ]
94124 return f"https://drive.google.com/uc?export=download&id={ file_id } "
95125 return view_url
96- except :
126+ except Exception :
97127 return view_url
98128
99129 if "Link" in df .columns :
100130 df ["Download" ] = df ["Link" ].apply (make_download_url )
101131
102- # Interactive Search
103- search_query = st .text_input ("Quick Filter" , placeholder = "Search by name, topic, or keyword..." )
132+ # --- SEARCH FILTER ---
104133 if search_query :
105- df = df [df .apply (lambda row : row .astype (str ).str .contains (search_query , case = False ).any (), axis = 1 )]
134+ df = df [df .apply (
135+ lambda row : row .astype (str ).str .contains (search_query , case = False ).any (),
136+ axis = 1
137+ )]
138+
139+ # --- COLUMN ORDERING ---
140+ desired_order = [
141+ "Presentation Title" ,
142+ "Description" ,
143+ "Keywords" ,
144+ "Person" , # Contact
145+ "Link" , # View
146+ "Download" # Download
147+ ]
148+ df = df [[c for c in desired_order if c in df .columns ]]
106149
107- # 2) NARROWER TABLE & WORD WRAP
150+ # --- DISPLAY TABLE ---
108151 st .dataframe (
109- df ,
110- use_container_width = True ,
152+ df ,
153+ use_container_width = True ,
111154 hide_index = True ,
155+ height = 600 ,
112156 column_config = {
113- "Presentation Title" : st .column_config .TextColumn ("Title" , width = "medium" ),
114- "Description" : st .column_config .TextColumn ("Topic/Description" , width = "large" ),
115- "Keywords" : st .column_config .TextColumn ("Keywords" , width = "small" ),
116- "Link" : st .column_config .LinkColumn ("View File" , display_text = "👁️ Open Sheets" ),
117- "Download" : st .column_config .LinkColumn ("Download" , display_text = "📥 Download" )
157+ "Presentation Title" : st .column_config .TextColumn (
158+ "Title" , width = "medium"
159+ ),
160+ "Description" : st .column_config .TextColumn (
161+ "Description" , width = "large"
162+ ),
163+ "Keywords" : st .column_config .TextColumn (
164+ "Keywords" , width = "small"
165+ ),
166+ "Person" : st .column_config .TextColumn (
167+ "Contact" , width = "small"
168+ ),
169+ "Link" : st .column_config .LinkColumn (
170+ "View" , display_text = "👁️ Open"
171+ ),
172+ "Download" : st .column_config .LinkColumn (
173+ "Download" , display_text = "📥 Download"
174+ )
118175 }
119176 )
120-
121- if st .button ("Refresh Database" ):
177+
178+ if st .button ("🔄 Refresh Database" ):
122179 st .rerun ()
123180
124181 except Exception as e :
125182 st .error (f"Could not load the database: { e } " )
126183
184+
185+ # =========================
186+ # 📤 TAB 2: UPLOAD NEW FILE
187+ # =========================
127188with tab2 :
128189 st .header ("Contribute to the Library" )
129- st .write ("Upload your slides and fill out the details below ." )
130-
190+ st .write ("Upload your slides and provide the associated metadata ." )
191+
131192 with st .form ("upload_form" , clear_on_submit = True ):
132193 person = st .text_input ("Your Name" )
133194 name = st .text_input ("Presentation Title" )
134- description = st .text_area ("Topic/ Description" )
195+ description = st .text_area ("Topic / Description" )
135196 keywords = st .text_input ("Keywords (comma separated)" )
136- uploaded_file = st .file_uploader ("Choose Presentation File (PDF, PPTX, etc.)" )
137-
197+ uploaded_file = st .file_uploader (
198+ "Choose Presentation File (PDF, PPTX, etc.)"
199+ )
200+
138201 submit_button = st .form_submit_button ("Submit Entry" )
139202
140203 if submit_button :
141204 if not name or not description :
142205 st .warning ("Please provide at least a title and a description." )
143206 else :
144- with st .spinner ("Processing upload... please wait. " ):
207+ with st .spinner ("Processing upload..." ):
145208 file_link = "No file uploaded"
146-
209+
147210 if uploaded_file is not None :
148211 with tempfile .NamedTemporaryFile (delete = False ) as tmp :
149212 tmp .write (uploaded_file .getvalue ())
@@ -155,33 +218,41 @@ def make_download_url(view_url):
155218 "parents" : [FOLDER_ID ]
156219 }
157220 media = MediaFileUpload (tmp_path , resumable = True )
158-
221+
159222 uploaded_drive_file = drive_service .files ().create (
160223 body = file_metadata ,
161224 media_body = media ,
162225 fields = "id, webViewLink"
163226 ).execute ()
164-
227+
165228 file_link = uploaded_drive_file .get ("webViewLink" )
229+
166230 except Exception as e :
167231 st .error (f"Drive Error: { e } " )
232+
168233 finally :
169234 if os .path .exists (tmp_path ):
170235 os .remove (tmp_path )
171236
172237 try :
173- # Prepare row data (ensure this matches the order of your sheet columns)
174- row_data = [[name , description , keywords , file_link , person ]]
175-
238+ row_data = [[
239+ name ,
240+ description ,
241+ keywords ,
242+ file_link ,
243+ person
244+ ]]
245+
176246 sheets_service .spreadsheets ().values ().append (
177247 spreadsheetId = SHEET_ID ,
178248 range = "Sheet1!A1" ,
179249 valueInputOption = "USER_ENTERED" ,
180250 body = {"values" : row_data }
181251 ).execute ()
182-
252+
183253 st .success ("✅ Entry recorded successfully!" )
184254 st .balloons ()
185255 st .rerun ()
256+
186257 except Exception as e :
187- st .error (f"Sheets Error: { e } " )
258+ st .error (f"Sheets Error: { e } " )
0 commit comments