diff --git a/climbdex/__init__.py b/climbdex/__init__.py index 8eebd2c..01e32aa 100644 --- a/climbdex/__init__.py +++ b/climbdex/__init__.py @@ -1,8 +1,15 @@ +import sys import flask +import logging import climbdex.api import climbdex.views +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + handlers=[logging.StreamHandler(sys.stdout)], +) def create_app(): app = flask.Flask(__name__, instance_relative_config=True) diff --git a/climbdex/api.py b/climbdex/api.py index 7b29399..62bd68c 100644 --- a/climbdex/api.py +++ b/climbdex/api.py @@ -1,11 +1,44 @@ +from flask_parameter_validation import ValidateParameters, Query + import boardlib.api.aurora import flask import requests +import json +import logging import climbdex.db blueprint = flask.Blueprint("api", __name__) +def parameter_error(e): + code = 400 + name = str(type(e).__name__) + description = f"Parameters were missing and/or misconfigured. If the issue persists, please report it (code: {code})" + + response = { + "error": True, + "code": code, + "name": name, + "description": description, + }, code + + logging.error(response) + return response + +@blueprint.errorhandler(Exception) +def handle_exception(e): + response = e.get_response() + response.data = json.dumps({ + "error": True, + "code": e.code, + "name": e.name, + "description": f"There was a problem while getting results from the server. If the issue persists, please report it (code: {e.code})", + }) + response.content_type = "application/json" + logging.error(response.data) + return response + + @blueprint.route("/api/v1//layouts") def layouts(board_name): @@ -29,12 +62,29 @@ def sets(board_name, layout_id, size_id): @blueprint.route("/api/v1/search/count") -def resultsCount(): +@ValidateParameters(parameter_error) +def resultsCount( + gradeAccuracy: float = Query(), + layout: int = Query(), + maxGrade: int = Query(), + minAscents: int = Query(), + minGrade: int = Query(), + minRating: float = Query(), + size: int = Query(), +): return flask.jsonify(climbdex.db.get_search_count(flask.request.args)) - @blueprint.route("/api/v1/search") -def search(): +@ValidateParameters(parameter_error) +def search( + gradeAccuracy: float = Query(), + layout: int = Query(), + maxGrade: int = Query(), + minAscents: int = Query(), + minGrade: int = Query(), + minRating: float = Query(), + size: int = Query(), +): return flask.jsonify(climbdex.db.get_search_results(flask.request.args)) diff --git a/climbdex/static/css/common.css b/climbdex/static/css/common.css new file mode 100644 index 0000000..f526d21 --- /dev/null +++ b/climbdex/static/css/common.css @@ -0,0 +1,7 @@ +.alert { + display: none; +} + +.show-alert { + display: inherit; +} diff --git a/climbdex/static/js/common.js b/climbdex/static/js/common.js index d9fe01b..2098850 100644 --- a/climbdex/static/js/common.js +++ b/climbdex/static/js/common.js @@ -1,3 +1,5 @@ +var alert = document.querySelector('.alert') + function drawBoard( svgElementId, imagesToHolds, diff --git a/climbdex/static/js/results.js b/climbdex/static/js/results.js index 320df08..d7de350 100644 --- a/climbdex/static/js/results.js +++ b/climbdex/static/js/results.js @@ -86,7 +86,13 @@ async function fetchResultsCount() { const urlParams = new URLSearchParams(window.location.search); const response = await fetch("/api/v1/search/count?" + urlParams); const resultsCount = await response.json(); - return resultsCount; + + if (resultsCount['error'] == true) { + alert.querySelector('.alert-content').innerHTML = resultsCount['description'] + alert.classList.add('show-alert') + } else { + return resultsCount; + } } async function fetchResults(pageNumber, pageSize) { @@ -95,7 +101,13 @@ async function fetchResults(pageNumber, pageSize) { urlParams.append("pageSize", pageSize); const response = await fetch("/api/v1/search?" + urlParams); const results = await response.json(); - return results; + + if (results['error'] == true) { + alert.querySelector('.alert-content').innerHTML = resultsCount['description'] + alert.classList.add('show-alert') + } else { + return results; + } } function clickClimbButton(index, pageSize, resultsCount) { @@ -230,7 +242,7 @@ function drawResultsPage(results, pageNumber, pageSize, resultsCount) { } const backAnchor = document.getElementById("anchor-back"); -backAnchor.href = location.origin + "/filter?" + location.search; +backAnchor.href = location.origin + "/filter" + location.search; if (document.referrer && new URL(document.referrer).origin == location.origin) { backAnchor.addEventListener("click", function (event) { event.preventDefault(); diff --git a/climbdex/templates/alert.html.j2 b/climbdex/templates/alert.html.j2 new file mode 100644 index 0000000..723c28d --- /dev/null +++ b/climbdex/templates/alert.html.j2 @@ -0,0 +1,8 @@ +
+
+ +
+
diff --git a/climbdex/templates/beta.html.j2 b/climbdex/templates/beta.html.j2 index fae8aca..298ed0a 100644 --- a/climbdex/templates/beta.html.j2 +++ b/climbdex/templates/beta.html.j2 @@ -16,6 +16,7 @@
{% include 'heading.html.j2' %} + {% include 'alert.html.j2' %}
@@ -52,6 +53,9 @@ {% include 'footer.html.j2'%}
+ diff --git a/climbdex/templates/boardSelection.html.j2 b/climbdex/templates/boardSelection.html.j2 index 041957c..12cfba2 100644 --- a/climbdex/templates/boardSelection.html.j2 +++ b/climbdex/templates/boardSelection.html.j2 @@ -16,6 +16,7 @@
{% include 'heading.html.j2' %} + {% include 'alert.html.j2' %}
@@ -100,7 +101,9 @@
- + diff --git a/climbdex/templates/climbCreation.html.j2 b/climbdex/templates/climbCreation.html.j2 index 6a8c0fc..74cdca2 100644 --- a/climbdex/templates/climbCreation.html.j2 +++ b/climbdex/templates/climbCreation.html.j2 @@ -14,6 +14,7 @@
{% include 'heading.html.j2' %} + {% include 'alert.html.j2' %}

Setup: {{board.capitalize()}} - {{layout_name}} - {{size_name}}

@@ -33,9 +34,12 @@
{% include 'footer.html.j2' %}
+ + -