@@ -688,7 +688,7 @@ def diag_routes():
688688# -- View and import (admin + Public) --
689689@app .route ('/materials/<property_name>/<tab>' , methods = ['GET' , 'POST' ])
690690def property_detail (property_name , tab ):
691- # Titles / guards
691+ # ---- titles / guards ----
692692 pretty_titles = {
693693 'bandgap' : 'Band Gap' ,
694694 'formation_energy' : 'Formation Energy' ,
@@ -698,104 +698,125 @@ def property_detail(property_name, tab):
698698 if property_name not in pretty_titles or tab not in ('dataset' , 'results' ):
699699 return "Not found." , 404
700700
701- ensure_uploads_log_schema ()
702-
703701 upload_message = ""
704702 edit_message = ""
705703 is_admin = bool (session .get ('admin' ))
706704
707- # -------- Admin-only: Drive-based upload -----------
705+ # Helper: parse Google Drive link or raw id
706+ def _extract_drive_id (link_or_id : str ):
707+ s = (link_or_id or "" ).strip ()
708+ m = re .search (r"/d/([a-zA-Z0-9_-]+)" , s )
709+ if m : return m .group (1 )
710+ m = re .search (r"[?&]id=([a-zA-Z0-9_-]+)" , s )
711+ if m : return m .group (1 )
712+ if re .match (r"^[a-zA-Z0-9_-]{10,}$" , s ):
713+ return s
714+ return None
715+
716+ # ---- admin POST handlers ----
708717 if is_admin and request .method == 'POST' :
709- if 'add_drive' in request .form :
710- # Form fields (Drive)
711- drive_link = (request .form .get ('drive_link' ) or '' ).strip ()
712- label = (request .form .get ('label' ) or '' ).strip () # human filename label (required)
713- source = (request .form .get ('row_source' ) or '' ).strip () if tab == 'dataset' else None
714- desc = (request .form .get ('row_description' ) or '' ).strip ()
715-
716- # Parse Drive ID from link or raw id
717- def _extract_drive_id (link : str ):
718- m = re .search (r'/d/([a-zA-Z0-9_-]+)' , link ) or re .search (r'[?&]id=([a-zA-Z0-9_-]+)' , link )
719- if m : return m .group (1 )
720- if re .match (r'^[a-zA-Z0-9_-]{10,}$' , link ): return link
721- return None
722-
723- drive_id = _extract_drive_id (drive_link )
724- if not label or not drive_id :
725- upload_message = "❌ Provide a valid Drive link/ID and a label."
726- else :
727- preview_url = f"https://drive.google.com/file/d/{ drive_id } /preview"
728- download_url = f"https://drive.google.com/uc?export=download&id={ drive_id } "
718+ # 1) Add Drive entry
719+ if request .form .get ('add_drive' ):
720+ drive_link = request .form .get ('drive_link' , '' ).strip ()
721+ label = request .form .get ('label' , '' ).strip ()
722+ new_source = (request .form .get ('row_source' ) or '' ).strip () if tab == 'dataset' else None
723+ new_desc = (request .form .get ('row_description' ) or '' ).strip ()
729724
730- # Upsert into public catalog (dedup by key)
731- with sqlite3 .connect (DB_NAME ) as conn :
732- c = conn .cursor ()
733- c .execute ("""
734- INSERT INTO uploads_log
735- (property, tab, filename, uploaded_at, storage, drive_id, preview_url, download_url, source, description)
736- VALUES (?, ?, ?, CURRENT_TIMESTAMP, 'drive', ?, ?, ?, ?, ?)
737- ON CONFLICT(property, tab, filename)
738- DO UPDATE SET
739- uploaded_at = CURRENT_TIMESTAMP,
740- storage='drive', drive_id=excluded.drive_id,
741- preview_url=excluded.preview_url, download_url=excluded.download_url,
742- source=excluded.source, description=excluded.description
743- """ , (property_name , tab , label , drive_id , preview_url , download_url , source , desc ))
744- conn .commit ()
745- upload_message = f"✅ Added '{ label } ' from Drive."
725+ # Basic ext check from label to keep tabs consistent
726+ ext = (label .rsplit ('.' , 1 )[- 1 ].lower () if '.' in label else '' )
727+ if tab == 'dataset' and ext not in ALLOWED_DATASET_EXTENSIONS :
728+ upload_message = f"Label must end with .csv or .npy for datasets."
729+ elif tab == 'results' and ext not in ALLOWED_RESULTS_EXTENSIONS :
730+ upload_message = f"Label must be one of: { ', ' .join (sorted (ALLOWED_RESULTS_EXTENSIONS ))} ."
731+ else :
732+ file_id = _extract_drive_id (drive_link )
733+ if not file_id :
734+ upload_message = "Invalid Google Drive link or ID."
735+ else :
736+ preview_url = f"https://drive.google.com/file/d/{ file_id } /preview"
737+ download_url = f"https://drive.google.com/uc?export=download&id={ file_id } "
738+ # Upsert into uploads_log (Drive-first)
739+ with sqlite3 .connect (DB_NAME ) as conn :
740+ c = conn .cursor ()
741+ c .execute (
742+ """
743+ INSERT INTO uploads_log
744+ (property, tab, filename, uploaded_at, storage, drive_id, preview_url, download_url, source, description)
745+ VALUES (?, ?, ?, CURRENT_TIMESTAMP, 'drive', ?, ?, ?, ?, ?)
746+ ON CONFLICT(property, tab, filename)
747+ DO UPDATE SET
748+ uploaded_at = CURRENT_TIMESTAMP,
749+ storage = 'drive',
750+ drive_id = excluded.drive_id,
751+ preview_url = excluded.preview_url,
752+ download_url = excluded.download_url,
753+ source = COALESCE(excluded.source, uploads_log.source),
754+ description = COALESCE(excluded.description, uploads_log.description)
755+ """ ,
756+ (property_name , tab , label , file_id , preview_url , download_url , new_source , new_desc ),
757+ )
758+ conn .commit ()
759+ upload_message = f"Added Drive item '{ label } '."
746760
761+ # 2) Inline edit (source/description)
747762 elif 'edit_row' in request .form :
748- # Inline update (source/description)
749- row_filename = request .form .get ('row_filename' ) or ''
750- safe_row_filename = secure_filename (os .path .basename (row_filename ))
763+ row_filename = (request .form .get ('row_filename' ) or '' ).strip ()
751764 new_desc = (request .form .get ('row_description' ) or '' ).strip ()
752765 with sqlite3 .connect (DB_NAME ) as conn :
753766 c = conn .cursor ()
754767 if tab == 'dataset' :
755768 new_source = (request .form .get ('row_source' ) or '' ).strip ()
756- c .execute ("""
769+ c .execute (
770+ """
757771 UPDATE uploads_log
758- SET source=?, description=?, uploaded_at=CURRENT_TIMESTAMP
759- WHERE property=? AND tab=? AND filename=?""" ,
760- (new_source , new_desc , property_name , tab , safe_row_filename ))
772+ SET source = ?, description = ?
773+ WHERE property = ? AND tab = ? AND filename = ?
774+ """ ,
775+ (new_source , new_desc , property_name , tab , row_filename ),
776+ )
761777 else :
762- c .execute ("""
778+ c .execute (
779+ """
763780 UPDATE uploads_log
764- SET description=?, uploaded_at=CURRENT_TIMESTAMP
765- WHERE property=? AND tab=? AND filename=?""" ,
766- (new_desc , property_name , tab , safe_row_filename ))
781+ SET description = ?
782+ WHERE property = ? AND tab = ? AND filename = ?
783+ """ ,
784+ (new_desc , property_name , tab , row_filename ),
785+ )
767786 conn .commit ()
768- edit_message = f"Updated info for { safe_row_filename } ."
787+ edit_message = f"Updated info for { row_filename } ."
769788
770- # -------- Fetch current public rows ------- ----
789+ # ---- fetch current uploads ( public catalog) ----
771790 with sqlite3 .connect (DB_NAME ) as conn :
791+ conn .row_factory = sqlite3 .Row
772792 c = conn .cursor ()
773- c .execute ("""
793+ c .execute (
794+ """
774795 SELECT filename,
775796 COALESCE(source,'') AS source,
776797 COALESCE(description,'') AS description,
777798 uploaded_at,
778799 COALESCE(storage,'local') AS storage,
779- COALESCE(preview_url,'') AS preview_url,
780- COALESCE(download_url,'') AS download_url
800+ preview_url,
801+ download_url
781802 FROM uploads_log
782- WHERE property= ? AND tab= ?
803+ WHERE property = ? AND tab = ?
783804 ORDER BY uploaded_at DESC, filename
784- """ , ( property_name , tab ))
785- rows = c . fetchall ()
786-
787- # Normalize rows for the template
788- uploads = []
789- for fname , source , description , uploaded_at , storage , purl , durl in rows :
790- uploads . append ({
791- "filename" : fname ,
792- "source" : source ,
793- "description" : description ,
794- "uploaded_at" : uploaded_at ,
795- "storage" : storage ,
796- "preview_url" : purl ,
797- "download_url" : durl ,
798- } )
805+ """ ,
806+ ( property_name , tab ),
807+ )
808+ uploads = c . fetchall ()
809+
810+ # Map local dataset filenames to SQL table names (for "View" link)
811+ table_map = {}
812+ if tab == 'dataset' :
813+ table_map = {}
814+ for row in uploads :
815+ storage = ( row [ 'storage' ] or 'local' )
816+ if storage != 'drive' :
817+ fname = row [ 'filename' ]
818+ if fname and ( fname . endswith ( '.csv' ) or fname . endswith ( '.npy' )):
819+ table_map [ fname ] = file_to_table_name ( fname )
799820
800821 return render_template (
801822 'property_detail.html' ,
@@ -806,6 +827,7 @@ def _extract_drive_id(link: str):
806827 upload_message = upload_message ,
807828 edit_message = edit_message ,
808829 admin = is_admin ,
830+ table_map = table_map ,
809831 )
810832
811833#########################################################
0 commit comments