66import pandas as pd
77import numpy as np
88import sqlite3
9+
910from werkzeug .utils import secure_filename
1011import datetime
1112import re
1213import csv
13- < << << << HEAD
14+ import io , base64 , zipfile
15+ from typing import List , Dict , Optional
16+
17+
1418from google .oauth2 .service_account import Credentials
1519from googleapiclient .discovery import build
1620from googleapiclient .http import MediaIoBaseUpload
17- import io , base64 , zipfile
18- from typing import List , Dict , Optional
1921
20- == == == =
21- >> >> >> > de853a6ad379935603467611a67615b2f6aed6dc
2222
2323# ========== SETTINGS ==========
2424
3434app .config ['UPLOAD_FOLDER' ] = UPLOAD_FOLDER
3535app .secret_key = 'IronMa1deN!'
3636
37- < << << << HEAD
3837GDRIVE_SCOPES = ["https://www.googleapis.com/auth/drive" ]
3938
40- == == == =
41- >> >> >> > de853a6ad379935603467611a67615b2f6aed6dc
4239# ---------- Utility Functions ----------
4340
4441def allowed_dataset_file (filename ):
@@ -52,7 +49,6 @@ def allowed_music_file(filename):
5249
5350# ========== Helper Functions ========== #
5451
55- < << << << HEAD
5652def get_drive_service ():
5753 """
5854 Build a Drive v3 service from a Service Account.
@@ -179,8 +175,6 @@ def _drive_urls(file_id: str) -> (str, str):
179175
180176#==================================================#
181177
182- == == == =
183- >> >> >> > de853a6ad379935603467611a67615b2f6aed6dc
184178_SQLITE_RESERVED_PREFIXES = ("sqlite_" ,)
185179
186180def tableize_basename (name : str ) -> str :
@@ -240,7 +234,6 @@ def file_to_table_name(filename: str) -> str:
240234
241235#==================================================#
242236
243- < << << << HEAD
244237# Google Drive integration using service account creds
245238
246239def drive ():
@@ -312,8 +305,6 @@ def _ensure_folder(parent_id: str, name: str):
312305
313306#==================================================#
314307
315- == == == =
316- >> >> >> > de853a6ad379935603467611a67615b2f6aed6dc
317308def ensure_uploads_log_schema ():
318309 """Public catalog (uploads_log) + audit history (uploads_audit) with triggers."""
319310 with sqlite3 .connect (DB_NAME ) as conn :
@@ -548,7 +539,6 @@ def _run_startup_tasks():
548539 ensure_uploads_log_schema () # if you have this helper; otherwise drop it
549540 except Exception as e :
550541 app .logger .warning ("ensure_uploads_log_schema skipped: %s" , e )
551- < << << << HEAD
552542 # try:
553543 # auto_import_uploads()
554544 # except Exception as e:
@@ -558,16 +548,6 @@ def _run_startup_tasks():
558548 # except Exception as e:
559549 # app.logger.warning("auto_log_material_files skipped: %s", e)
560550
561- == == == =
562- try :
563- auto_import_uploads ()
564- except Exception as e :
565- app .logger .warning ("auto_import_uploads skipped: %s" , e )
566- try :
567- auto_log_material_files ()
568- except Exception as e :
569- app .logger .warning ("auto_log_material_files skipped: %s" , e )
570- >> >> >> > de853a6ad379935603467611a67615b2f6aed6dc
571551 _startup_done = True
572552
573553@app .before_request
@@ -914,11 +894,7 @@ def diag_routes():
914894
915895#########################################################
916896
917- < << << << HEAD
918897# -- View and import (admin + public, Drive-only adds) --
919- == == == =
920- # -- View and import (admin + Public) --
921- >> >> >> > de853a6ad379935603467611a67615b2f6aed6dc
922898# -- View and import (admin + Public) --
923899@app .route ('/materials/<property_name>/<tab>' , methods = ['GET' , 'POST' ])
924900def property_detail (property_name , tab ):
@@ -936,7 +912,6 @@ def property_detail(property_name, tab):
936912 edit_message = ""
937913 is_admin = bool (session .get ('admin' ))
938914
939- < << << << HEAD
940915 # ---- admin POST handlers ----
941916 if is_admin and request .method == 'POST' :
942917 try :
@@ -1119,130 +1094,24 @@ def property_detail(property_name, tab):
11191094 upload_message = f"Error: { e } "
11201095
11211096 # ---- fetch current uploads (public catalog) ----
1122- == == == =
1123- # Helper: parse Google Drive link or raw id
1124- def _extract_drive_id (link_or_id : str ):
1125- s = (link_or_id or "" ).strip ()
1126- m = re .search (r"/d/([a-zA-Z0-9_-]+)" , s )
1127- if m : return m .group (1 )
1128- m = re .search (r"[?&]id=([a-zA-Z0-9_-]+)" , s )
1129- if m : return m .group (1 )
1130- if re .match (r"^[a-zA-Z0-9_-]{10,}$" , s ):
1131- return s
1132- return None
1133-
1134- # Make sure schema exists (columns like storage/preview_url/etc.)
1135- try :
1136- ensure_uploads_log_schema ()
1137- except Exception as e :
1138- app .logger .warning ("ensure_uploads_log_schema: %s" , e )
1139-
1140- # ---- admin POST handlers ----
1141- if is_admin and request .method == 'POST' :
1142- # 1) Add Drive entry
1143- if request .form .get ('add_drive' ):
1144- drive_link = request .form .get ('drive_link' , '' ).strip ()
1145- label = request .form .get ('label' , '' ).strip ()
1146- new_source = (request .form .get ('row_source' ) or '' ).strip () if tab == 'dataset' else None
1147- new_desc = (request .form .get ('row_description' ) or '' ).strip ()
1148-
1149- # Basic ext check from label to keep tabs consistent
1150- ext = (label .rsplit ('.' , 1 )[- 1 ].lower () if '.' in label else '' )
1151- if tab == 'dataset' and ext not in ALLOWED_DATASET_EXTENSIONS :
1152- upload_message = "Label must end with .csv or .npy for datasets."
1153- elif tab == 'results' and ext not in ALLOWED_RESULTS_EXTENSIONS :
1154- upload_message = f"Label must be one of: { ', ' .join (sorted (ALLOWED_RESULTS_EXTENSIONS ))} ."
1155- else :
1156- file_id = _extract_drive_id (drive_link )
1157- if not file_id :
1158- upload_message = "Invalid Google Drive link or ID."
1159- else :
1160- preview_url = f"https://drive.google.com/file/d/{ file_id } /preview"
1161- download_url = f"https://drive.google.com/uc?export=download&id={ file_id } "
1162- with sqlite3 .connect (DB_NAME ) as conn :
1163- c = conn .cursor ()
1164- c .execute (
1165- """
1166- INSERT INTO uploads_log
1167- (property, tab, filename, uploaded_at,
1168- storage, drive_id, preview_url, download_url, source, description)
1169- VALUES (?, ?, ?, CURRENT_TIMESTAMP,
1170- 'drive', ?, ?, ?, ?, ?)
1171- ON CONFLICT(property, tab, filename)
1172- DO UPDATE SET
1173- uploaded_at = CURRENT_TIMESTAMP,
1174- storage = 'drive',
1175- drive_id = excluded.drive_id,
1176- preview_url = excluded.preview_url,
1177- download_url= excluded.download_url,
1178- source = COALESCE(excluded.source, uploads_log.source),
1179- description = COALESCE(excluded.description, uploads_log.description)
1180- """ ,
1181- (property_name , tab , label , file_id , preview_url , download_url , new_source , new_desc ),
1182- )
1183- conn .commit ()
1184- upload_message = f"Added Drive item '{ label } '."
1185-
1186- # 2) Inline edit (source/description)
1187- elif 'edit_row' in request .form :
1188- row_filename = (request .form .get ('row_filename' ) or '' ).strip ()
1189- new_desc = (request .form .get ('row_description' ) or '' ).strip ()
1190- with sqlite3 .connect (DB_NAME ) as conn :
1191- c = conn .cursor ()
1192- if tab == 'dataset' :
1193- new_source = (request .form .get ('row_source' ) or '' ).strip ()
1194- c .execute (
1195- """
1196- UPDATE uploads_log
1197- SET source = ?, description = ?
1198- WHERE property = ? AND tab = ? AND filename = ?
1199- """ ,
1200- (new_source , new_desc , property_name , tab , row_filename ),
1201- )
1202- else :
1203- c .execute (
1204- """
1205- UPDATE uploads_log
1206- SET description = ?
1207- WHERE property = ? AND tab = ? AND filename = ?
1208- """ ,
1209- (new_desc , property_name , tab , row_filename ),
1210- )
1211- conn .commit ()
1212- edit_message = f"Updated info for { row_filename } ."
1213-
1214- # ---- fetch current uploads as plain dicts (so Jinja `row.filename` works) ----
1215- > >> >> >> de853a6ad379935603467611a67615b2f6aed6dc
12161097 with sqlite3 .connect (DB_NAME ) as conn :
12171098 conn .row_factory = sqlite3 .Row
12181099 c = conn .cursor ()
12191100 c .execute (
12201101 """
1221- <<<<<<< HEAD
12221102 SELECT filename,
12231103 COALESCE(source,'') AS source,
12241104 COALESCE(description,'') AS description,
12251105 uploaded_at,
12261106 COALESCE(storage,'local') AS storage,
12271107 preview_url,
12281108 download_url
1229- =======
1230- SELECT
1231- filename,
1232- COALESCE(source,'') AS source,
1233- COALESCE(description,'') AS description,
1234- uploaded_at,
1235- COALESCE(storage,'local') AS storage,
1236- COALESCE(preview_url,'') AS preview_url,
1237- COALESCE(download_url,'') AS download_url
1238- >>>>>>> de853a6ad379935603467611a67615b2f6aed6dc
12391109 FROM uploads_log
12401110 WHERE property = ? AND tab = ?
12411111 ORDER BY uploaded_at DESC, filename
12421112 """ ,
12431113 (property_name , tab ),
12441114 )
1245- < << << << HEAD
12461115 uploads = c .fetchall ()
12471116
12481117 # Map local dataset filenames to SQL table names (for old/local files)
@@ -1252,17 +1121,6 @@ def _extract_drive_id(link_or_id: str):
12521121 if (row ['storage' ] or 'local' ) != 'drive' :
12531122 fname = row ['filename' ]
12541123 if fname and (fname .endswith ('.csv' ) or fname .endswith ('.npy' )):
1255- == == == =
1256- uploads = [dict (r ) for r in c .fetchall ()]
1257-
1258- # Map local dataset filenames to SQL table names (for potential view links)
1259- table_map = {}
1260- if tab == 'dataset' :
1261- for row in uploads :
1262- if row .get ('storage' ) != 'drive' :
1263- fname = row .get ('filename' , '' )
1264- if fname .endswith ('.csv' ) or fname .endswith ('.npy' ):
1265- > >> >> >> de853a6ad379935603467611a67615b2f6aed6dc
12661124 table_map [fname ] = file_to_table_name (fname )
12671125
12681126 return render_template (
@@ -1276,10 +1134,6 @@ def _extract_drive_id(link_or_id: str):
12761134 admin = is_admin ,
12771135 table_map = table_map ,
12781136 )
1279- < << << << HEAD
1280- == == == =
1281-
1282- >> >> >> > de853a6ad379935603467611a67615b2f6aed6dc
12831137
12841138#########################################################
12851139
0 commit comments