Skip to content

Commit 308cd45

Browse files
author
SM_SAYEED
committed
property navigation tuned
1 parent 0d5b499 commit 308cd45

File tree

1 file changed

+114
-7
lines changed

1 file changed

+114
-7
lines changed

app.py

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,65 @@ def allowed_music_file(filename):
3838

3939
# ========== Helper Functions ========== #
4040

41+
_SQLITE_RESERVED_PREFIXES = ("sqlite_",)
42+
43+
def tableize_basename(name: str) -> str:
44+
"""
45+
Convert a *basename only* (e.g., 'Featurized Band Gap Data.csv') into a
46+
safe SQLite table name. Preserves case (to match your existing tables).
47+
Ensures:
48+
- No path separators
49+
- Allowed chars: [A-Za-z0-9_]
50+
- No leading 'sqlite_' prefix
51+
- Not empty; if empty, returns 't_unnamed'
52+
- Collapses multiple underscores
53+
- Appends an extension suffix (_csv/_npy) if the original had one
54+
"""
55+
56+
base = os.path.basename(name or "").strip()
57+
if not base:
58+
return "t_unnamed"
59+
60+
# Split extension (if any) for suffixing
61+
stem, ext = os.path.splitext(base)
62+
ext_suffix = ""
63+
if ext:
64+
e = ext.lstrip(".").lower()
65+
if e in ("csv", "npy"):
66+
ext_suffix = f"_{e}"
67+
else:
68+
# Non-dataset extension: still keep it to avoid collisions
69+
ext_suffix = f"_{e}"
70+
71+
# Replace separators and disallowed chars with underscores
72+
s = stem.replace(".", "_").replace("-", "_").replace(" ", "_")
73+
s = re.sub(r"[^0-9A-Za-z_]", "_", s)
74+
s = re.sub(r"_+", "_", s).strip("_")
75+
76+
if not s:
77+
s = "t_unnamed"
78+
79+
# Avoid reserved internal prefix
80+
lowered = s.lower()
81+
if any(lowered.startswith(p) for p in _SQLITE_RESERVED_PREFIXES):
82+
s = "t_" + s
83+
84+
# names in reasonable length
85+
if len(s) > 120:
86+
s = s[:120].rstrip("_")
87+
88+
return f"{s}{ext_suffix}"
89+
90+
def file_to_table_name(filename: str) -> str:
91+
"""
92+
Small wrapper that ensures we only pass a basename to the canonical function.
93+
Use this everywhere you need to turn a filename into a table name.
94+
"""
95+
import os
96+
return tableize_basename(os.path.basename(filename or ""))
97+
98+
#==================================================#
99+
41100
def ensure_uploads_log_schema():
42101
"""Create/upgrade uploads_log to the expected schema; ensure uniqueness."""
43102
with sqlite3.connect(DB_NAME) as conn:
@@ -141,12 +200,6 @@ def auto_import_uploads():
141200
ALLOWED_IMPORT_EXTS = {'csv', 'npy'}
142201
imported = 0
143202

144-
def tableize(name: str) -> str:
145-
# Stable, safe table name from filename only (not full path)
146-
# e.g. "bandgap.csv" -> "bandgap_csv"
147-
t = name.replace('.', '_').replace('-', '_').replace(' ', '_')
148-
return re.sub(r'[^0-9a-zA-Z_]', '_', t)
149-
150203
with sqlite3.connect(DB_NAME) as conn:
151204
c = conn.cursor()
152205
# Track file mtimes to avoid unnecessary re-imports
@@ -174,7 +227,7 @@ def tableize(name: str) -> str:
174227
filepath = os.path.join(root, filename)
175228
relpath = os.path.relpath(filepath, UPLOAD_FOLDER)
176229
mtime = os.path.getmtime(filepath)
177-
table_name = tableize(filename)
230+
table_name = file_to_table_name(filename)
178231

179232
# Check etag (mtime)
180233
c.execute("SELECT mtime FROM import_etag WHERE relpath=?", (relpath,))
@@ -775,6 +828,60 @@ def public_clips():
775828

776829
#########################################################
777830

831+
@app.route('/view_table/<path:filename>', methods=['GET'])
832+
def view_table(filename):
833+
"""
834+
Used by the 'View' link in property_detail.html.
835+
Accepts 'property/tab/file.csv' and renders the dataset:
836+
- Prefer reading the imported SQLite table (created by auto_import_uploads()).
837+
- If missing, fall back to reading the file from uploads/ directly.
838+
"""
839+
admin = bool(session.get('admin'))
840+
safe_name = os.path.basename(filename)
841+
table = tableize_basename(safe_name)
842+
843+
df = None
844+
845+
# Try SQLite first
846+
try:
847+
with sqlite3.connect(DB_NAME) as conn:
848+
df = pd.read_sql_query(f'SELECT * FROM "{table}"', conn)
849+
except Exception:
850+
# Fallback: read the source file
851+
path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
852+
if not os.path.isfile(path):
853+
abort(404)
854+
ext = (safe_name.rsplit('.', 1)[-1] if '.' in safe_name else '').lower()
855+
try:
856+
if ext == 'csv':
857+
df = pd.read_csv(path)
858+
elif ext == 'npy':
859+
arr = np.load(path, allow_pickle=True)
860+
if isinstance(arr, np.ndarray):
861+
if arr.ndim == 2:
862+
df = pd.DataFrame(arr)
863+
elif arr.ndim == 1 and hasattr(arr.dtype, 'names') and arr.dtype.names:
864+
df = pd.DataFrame(arr.tolist(), columns=list(arr.dtype.names))
865+
else:
866+
df = pd.DataFrame(arr)
867+
else:
868+
return "Unsupported NPY structure.", 415
869+
else:
870+
return f"Unsupported dataset type: {ext}", 415
871+
except Exception as e:
872+
return f"Failed to open dataset: {e}", 500
873+
874+
return render_template(
875+
'view_table.html',
876+
tables=[df.to_html(classes='data', index=False)],
877+
titles=getattr(df, 'columns', []),
878+
filename=safe_name,
879+
imported_table=table,
880+
admin=admin
881+
)
882+
883+
#########################################################
884+
778885
@app.route('/dataset/<table>')
779886
def public_view(table):
780887
# Anyone can view any table

0 commit comments

Comments
 (0)