diff --git a/2-5-23.txt b/2-5-23.txt index 1615af5..7046576 100644 --- a/2-5-23.txt +++ b/2-5-23.txt @@ -2,11 +2,11 @@ - Show how to make a network request in R to the server - Version Control (what date a compound was added) - - createdAt, but configurable. + - createdAt, but configurable. DONE - Compare the database to the list of compunds and you only want compounds - from that date or before? + from that date or before? DONE -- Emphasizing Local? - Accessible through a url. +- Emphasizing Local? - Accessible through a url. DONE, all fields accessible. - Shiny app - Permissions System (revoke, access API keys) diff --git a/app.py b/app.py index f9eeb1c..7a591fb 100755 --- a/app.py +++ b/app.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from datetime import date from flask import Flask, render_template, session, request, abort, redirect, url_for, jsonify from flask_sqlalchemy import SQLAlchemy from sqlalchemy import inspect, and_ @@ -69,7 +70,7 @@ class Chemical(db.Model): name = db.Column(db.String, nullable=False) formula = db.Column(db.String, nullable=False) mass = db.Column(db.Float, nullable=False) - + pubchem_cid = db.Column(db.Integer) pubmed_refcount = db.Column(db.Integer) standard_class = db.Column(db.String) @@ -86,6 +87,7 @@ class Chemical(db.Model): msms_detected = db.Column(db.Boolean) msms_purity = db.Column(db.Float) + # serialized into datetime.date createdAt = db.Column(db.Date) @@ -123,13 +125,15 @@ def admin_create(): if request.method == "GET": return render_template("register.html") else: - username, pw = request.form.get('username'), request.form.get('password') + username, pw = request.form.get( + 'username'), request.form.get('password') if username is None or pw is None: return render_template("register.html", fail="Invalid Input.") elif db.session.execute(db.select(Admin).filter_by(username=username)).fetchone(): return render_template("register.html", fail="Username already exists.") else: - db.session.add(Admin(username=username, password=Admin.generate_password(pw))) + db.session.add( + Admin(username=username, password=Admin.generate_password(pw))) db.session.commit() return render_template("register.html", success=True) @@ -137,7 +141,8 @@ def admin_create(): @app.route('/admin/login', methods=['GET', 'POST']) def admin_login(): if request.method == "POST": - username, pw = request.form.get('username', ''), request.form.get('password', '') + username, pw = request.form.get( + 'username', ''), request.form.get('password', '') if Admin.authenticate(username, pw): return render_template("login.html", success=True) else: @@ -184,7 +189,7 @@ def chemical_create(): def chemical_update(id: int): if not session.get('admin'): abort(403) - current_chemical:Chemical = Chemical.query.filter_by(id=id).one_or_404() + current_chemical: Chemical = Chemical.query.filter_by(id=id).one_or_404() dct = object_as_dict(current_chemical) if request.method == "POST": form = ChemicalForm(**request.form) @@ -206,7 +211,7 @@ def chemical_update(id: int): def chemical_delete(id: int): if not session.get('admin'): abort(403) - current_chemical:Chemical = Chemical.query.filter_by(id=id).one_or_404() + current_chemical: Chemical = Chemical.query.filter_by(id=id).one_or_404() db.session.delete(current_chemical) db.session.commit() return render_template("delete_chemical.html", id=id) @@ -214,7 +219,7 @@ def chemical_delete(id: int): @app.route("/chemical//view") def chemical_view(id: int): - current_chemical:Chemical = Chemical.query.filter_by(id=id).one_or_404() + current_chemical: Chemical = Chemical.query.filter_by(id=id).one_or_404() dct = object_as_dict(current_chemical) return render_template("view_chemical.html", id=id, chemical=dct) @@ -223,40 +228,42 @@ def chemical_view(id: int): def chemical_all(): if not session.get('admin'): abort(403) - result = Chemical.query.all() + result: list[Chemical] = Chemical.query.all() data = [] for x in result: - data.append({"url": url_for("chemical_view", id=x.id), "name": x.name, "mz": x.final_mz, "rt": x.final_rt}) + data.append({c.name: getattr(x, c.name) for c in x.__table__.columns}) return jsonify(data) -@app.route("/chemical/search") + +@app.route("/chemical/search", methods=["POST"]) def search_api(): - mz_min, mz_max = request.args.get('mz_min'), request.args.get('mz_max') - rt_min, rt_max = request.args.get('rt_min'), request.args.get('rt_max') - if (mz_min is None and mz_max is None) or (rt_min is None and rt_max is None): - return jsonify({"error": "invalid data"}), 400 + query = request.json + if query is None: + return jsonify([]) + for field in query: + query[field] = float(query[field]) + mz_min, mz_max = query.get('mz_min'), query.get('mz_max') + rt_min, rt_max = query.get('rt_min'), query.get('rt_max') + year_max, month_max, day_max = int(query.get( + 'year_max')), int(query.get('month_max')), int(query.get('day_max')) + try: - if mz_min is not None and mz_max is None: - mz_max = float(mz_min) + 3 - elif mz_max is not None and mz_min is None: - mz_min = float(mz_max) - 3 - if rt_min is not None and rt_max is None: - rt_max = float(rt_min) + 3 - elif rt_max is not None and rt_min is None: - rt_min = float(rt_max) - 3 - mz_min, mz_max = float(mz_min), float(mz_max) - rt_min, rt_max = float(rt_min), float(rt_max) - except ValueError: - return jsonify({"error": "invalid data"}), 400 - - mz_filter = and_(mz_max > Chemical.final_mz, Chemical.final_mz > mz_min) - rt_filter = and_(rt_max > Chemical.final_rt, Chemical.final_rt > rt_min) + mz_filter = and_(mz_max > Chemical.final_mz, + Chemical.final_mz > mz_min) + rt_filter = and_(rt_max > Chemical.final_rt, + Chemical.final_rt > rt_min) + date_filter = date(year_max, month_max, day_max) >= Chemical.createdAt + except ValueError as e: + return jsonify({"error": str(e)}), 400 + result = Chemical.query.filter( - and_(mz_filter, rt_filter) - ).limit(10).all() + and_(mz_filter, rt_filter, date_filter) + ).limit(20).all() + data = [] for x in result: - data.append({"url": url_for("chemical_view", id=x.id), "name": x.name, "mz": x.final_mz, "rt": x.final_rt}) + data.append({"url": url_for("chemical_view", id=x.id), + "name": x.name, "mz": x.final_mz, "rt": x.final_rt}) return jsonify(data) diff --git a/templates/search.html b/templates/search.html index 007a794..584e8fc 100644 --- a/templates/search.html +++ b/templates/search.html @@ -26,28 +26,55 @@ mz_max: 0, rt_min: 0, rt_max: 0, + // query parameters for the maximum date possible. + year_max: 2021, + month_max: 1, + day_max: 31, + // results results: [], error: null, } }, methods: { // methods - async fetch_data() { - if (!(valid(this.mz_min) && valid(this.mz_max) && valid(this.rt_max) && valid(this.rt_min))) { - return + prepare_query() { + // validation + let query = {} + const fields = ["mz_min", "mz_max", "rt_min", "rt_max", + "year_max", "month_max", "day_max"] + for (let i = 0; i < fields.length; i++) { + const field = fields[i]; + if (!valid(this[field])) { + console.log(query, field, this[field]) + return null; + } else { + query[field] = this[field] } - const url = `/chemical/search?mz_min=${this.mz_min}&mz_max=${this.mz_max}&rt_min=${this.rt_min}&rt_max=${this.rt_max}` - fetch(url).then(res => { + } + return query + }, + async fetch_data() { + const query = this.prepare_query() + if (query === null) return; + const api = `/chemical/search`; + fetch(api, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(query) + }).then(async res => { if (res.status !== 200) { console.log(`Error Status, ${res.status}`) + const json = await res.json() + throw Error(json.error); } return res.json() }).then(json => { this.results = json; this.error = null; }).catch(e => { - console.error(e); - this.error = true; + this.error = e.message; }) } }, @@ -67,7 +94,7 @@ - + @@ -75,7 +102,7 @@ - + @@ -83,7 +110,7 @@ - + @@ -91,21 +118,32 @@ - + + + + + + + + + + + -
{% raw %}