Skip to content

Commit cbe63be

Browse files
author
SM_SAYEED
committed
Fix uploads_log upsert + dedup select
1 parent f0e4c01 commit cbe63be

File tree

1 file changed

+77
-62
lines changed

1 file changed

+77
-62
lines changed

app.py

Lines changed: 77 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -419,101 +419,119 @@ def query_sql():
419419
@app.route('/')
420420
def public_home():
421421
return render_template('landing.html')
422-
422+
#########################################################
423423
@app.route('/materials')
424424
def materials_portal():
425425
return render_template('materials_portal.html')
426-
426+
#########################################################
427427
@app.route('/materials/<property_name>/<tab>', methods=['GET', 'POST'])
428428
def property_detail(property_name, tab):
429+
# ---- titles / guards ----
429430
pretty_titles = {
430431
'bandgap': 'Band Gap',
431432
'formation_energy': 'Formation Energy',
432433
'melting_point': 'Melting Point',
433-
'oxidation_state': 'Oxidation State'
434+
'oxidation_state': 'Oxidation State',
434435
}
435-
if property_name not in pretty_titles or tab not in ['dataset', 'results']:
436+
if property_name not in pretty_titles or tab not in ('dataset', 'results'):
436437
return "Not found.", 404
437438

438439
upload_message = ""
439440
edit_message = ""
440441
is_admin = session.get('admin', False)
441442

443+
# ---- admin POST handlers ----
442444
if is_admin and request.method == 'POST':
443-
# Inline edit form (from table row)
445+
# Inline row edit
444446
if 'edit_row' in request.form:
445447
row_filename = request.form.get('row_filename')
446-
new_source = request.form.get('row_source', '').strip() if tab == 'dataset' else None
447-
new_desc = request.form.get('row_description', '').strip()
448-
with sqlite3.connect(DB_NAME) as conn:
449-
c = conn.cursor()
450-
if tab == 'dataset':
448+
safe_row_filename = secure_filename(os.path.basename(row_filename or ""))
449+
450+
new_desc = (request.form.get('row_description') or '').strip()
451+
if tab == 'dataset':
452+
new_source = (request.form.get('row_source') or '').strip()
453+
with sqlite3.connect(DB_NAME) as conn:
454+
c = conn.cursor()
451455
c.execute("""
452456
UPDATE uploads_log
453-
SET source=?, description=?
454-
WHERE property=? AND tab=? AND filename=?
455-
""", (new_source, new_desc, property_name, tab, row_filename))
456-
else:
457+
SET source = ?, description = ?
458+
WHERE property = ? AND tab = ? AND filename = ?
459+
""", (new_source, new_desc, property_name, tab, safe_row_filename))
460+
conn.commit()
461+
else:
462+
with sqlite3.connect(DB_NAME) as conn:
463+
c = conn.cursor()
457464
c.execute("""
458465
UPDATE uploads_log
459-
SET description=?
460-
WHERE property=? AND tab=? AND filename=?
461-
""", (new_desc, property_name, tab, row_filename))
462-
conn.commit()
463-
edit_message = f"Updated info for {row_filename}."
464-
# Upload form
466+
SET description = ?
467+
WHERE property = ? AND tab = ? AND filename = ?
468+
""", (new_desc, property_name, tab, safe_row_filename))
469+
conn.commit()
470+
edit_message = f"Updated info for {safe_row_filename}."
471+
472+
# New file upload
465473
elif 'file' in request.files:
466-
if request.files['file'].filename == '':
474+
f = request.files['file']
475+
if not f or f.filename == '':
467476
upload_message = "No file selected."
468477
else:
469-
file = request.files['file']
470-
# Set allowed extensions logic
478+
# Validate extension by tab
471479
if tab == 'dataset':
472-
is_allowed = allowed_dataset_file(file.filename)
480+
is_allowed = allowed_dataset_file(f.filename)
473481
allowed_types = "CSV or NPY"
474482
elif tab == 'results':
475-
is_allowed = allowed_results_file(file.filename)
483+
is_allowed = allowed_results_file(f.filename)
476484
allowed_types = "JPG, PNG, GIF, PDF, or DOCX"
477485
else:
478486
is_allowed = False
479487
allowed_types = ""
480488

481-
if file and is_allowed:
489+
if not is_allowed:
490+
upload_message = f"File type not allowed. Only {allowed_types} supported."
491+
else:
492+
# Save to disk (under /uploads/<property>/<tab>/)
482493
property_folder = os.path.join(app.config['UPLOAD_FOLDER'], property_name, tab)
483494
os.makedirs(property_folder, exist_ok=True)
484-
filename = secure_filename(file.filename)
485-
filepath = os.path.join(property_folder, filename)
486-
file.save(filepath)
487-
# LOG THE UPLOAD!
495+
safe_filename = secure_filename(os.path.basename(f.filename))
496+
filepath = os.path.join(property_folder, safe_filename)
497+
f.save(filepath)
498+
499+
# Log to DB (idempotent)
488500
with sqlite3.connect(DB_NAME) as conn:
489501
c = conn.cursor()
490-
c.execute("""
502+
c.execute(
503+
"""
491504
INSERT INTO uploads_log (property, tab, filename, uploaded_at)
492505
VALUES (?, ?, ?, ?)
493506
ON CONFLICT(property, tab, filename)
494-
DO UPDATE SET uploaded_at=excluded.uploaded_at
495-
""", (property_name, tab, filename, datetime.datetime.now().isoformat()))
507+
DO UPDATE SET uploaded_at = excluded.uploaded_at
508+
""",
509+
(property_name, tab, safe_filename, datetime.datetime.now().isoformat()),
510+
)
496511
conn.commit()
497512

498-
upload_message = f"File {filename} uploaded for {pretty_titles[property_name]} {tab.title()}!"
499-
else:
500-
upload_message = f"File type not allowed. Only {allowed_types} supported."
513+
upload_message = f"File {safe_filename} uploaded for {pretty_titles[property_name]} {tab.title()}!"
501514

502-
# Always fetch current uploads after handling POSTs
503-
uploads = []
515+
# ---- fetch current uploads (dedup to 1 row per filename; keep newest) ----
504516
with sqlite3.connect(DB_NAME) as conn:
505517
c = conn.cursor()
506518
c.execute("""
507-
SELECT filename, source, description, uploaded_at
508-
FROM uploads_log
509-
WHERE property=? AND tab=?
510-
ORDER BY uploaded_at DESC
511-
""", (property_name, tab))
519+
SELECT u.filename,
520+
COALESCE(u.source, '') AS source,
521+
COALESCE(u.description, '') AS description,
522+
u.uploaded_at
523+
FROM uploads_log AS u
524+
JOIN (
525+
SELECT filename, MAX(uploaded_at) AS max_ts
526+
FROM uploads_log
527+
WHERE property = ? AND tab = ?
528+
GROUP BY filename
529+
) AS m
530+
ON m.filename = u.filename AND u.uploaded_at = m.max_ts
531+
WHERE u.property = ? AND u.tab = ?
532+
ORDER BY u.uploaded_at DESC
533+
""", (property_name, tab, property_name, tab))
512534
uploads = c.fetchall()
513-
uploads = [
514-
(fname, source, description, uploaded_at)
515-
for (fname, source, description, uploaded_at) in uploads
516-
]
517535

518536
return render_template(
519537
'property_detail.html',
@@ -523,10 +541,9 @@ def property_detail(property_name, tab):
523541
uploads=uploads,
524542
upload_message=upload_message,
525543
edit_message=edit_message,
526-
admin=is_admin
544+
admin=is_admin,
527545
)
528-
529-
546+
#########################################################
530547
@app.route('/uploads/<path:filename>')
531548
def uploaded_file(filename):
532549
full_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
@@ -535,7 +552,7 @@ def uploaded_file(filename):
535552
print('File not found:', full_path)
536553
abort(404)
537554
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
538-
555+
#########################################################
539556
@app.route('/view_result/<property_name>/<tab>/<path:filename>')
540557
def view_result_file(property_name, tab, filename):
541558
filepath = os.path.join(app.config['UPLOAD_FOLDER'], property_name, tab, filename)
@@ -554,7 +571,7 @@ def extract_drive_id(link):
554571
if match:
555572
return match.group(1)
556573
raise ValueError("Invalid Drive link")
557-
574+
#########################################################
558575
@app.route('/clips')
559576
def public_clips():
560577
import os
@@ -587,8 +604,7 @@ def public_clips():
587604
pass
588605

589606
return render_template('clips.html', clips=clips, admin=admin)
590-
591-
607+
#########################################################
592608
@app.route('/dataset/<table>')
593609
def public_view(table):
594610
# Anyone can view any table
@@ -600,15 +616,15 @@ def public_view(table):
600616
filename=table,
601617
imported_table=table,
602618
admin=False)
603-
619+
#########################################################
604620
@app.route('/download/<table>')
605621
def download(table):
606622
with sqlite3.connect(DB_NAME) as conn:
607623
df = pd.read_sql_query(f"SELECT * FROM {table}", conn)
608624
csv_path = os.path.join(UPLOAD_FOLDER, f"{table}.csv")
609625
df.to_csv(csv_path, index=False)
610626
return send_from_directory(UPLOAD_FOLDER, f"{table}.csv", as_attachment=True)
611-
627+
#########################################################
612628
@app.route('/migrate_csv_to_db')
613629
def migrate_csv_to_db():
614630
if not session.get('admin'):
@@ -651,7 +667,7 @@ def migrate_csv_to_db():
651667
return "✅ Table recreated and data loaded from CSV!"
652668
except Exception as e:
653669
return f"❌ Error: {e}"
654-
670+
#########################################################
655671
# SEARCH ROUTE
656672
@app.route('/search')
657673
def search():
@@ -686,7 +702,7 @@ def search():
686702
except Exception:
687703
clips = []
688704
return render_template('search_results.html', query=query, materials=materials, clips=clips)
689-
705+
#########################################################
690706
# DELETE CLIP
691707
@app.route('/delete_clip/<int:clip_id>', methods=['POST'])
692708
def delete_clip(clip_id):
@@ -705,7 +721,7 @@ def delete_clip(clip_id):
705721
c.execute("DELETE FROM music_clips WHERE id = ?", (clip_id,))
706722
conn.commit()
707723
return redirect(url_for('public_clips'))
708-
724+
#########################################################
709725
# DELETE DATASET/RESULT FILE
710726
from urllib.parse import unquote
711727

@@ -740,7 +756,7 @@ def delete_dataset_file(property_name, tab, filename):
740756
conn.commit()
741757

742758
return redirect(url_for('property_detail', property_name=property_name, tab=tab))
743-
759+
#########################################################
744760
@app.route('/add_drive_clip', methods=['GET', 'POST'])
745761
def add_drive_clip():
746762
if not session.get('admin'):
@@ -780,8 +796,7 @@ def extract_drive_id(link):
780796
message = "❌ Invalid link or missing title."
781797

782798
return render_template('add_drive_clip.html', message=message)
783-
784-
799+
#########################################################
785800

786801
# ========== MAIN ==========
787802
if __name__ == '__main__':

0 commit comments

Comments
 (0)