-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9e43599
commit 8dfa7ef
Showing
16 changed files
with
327 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1 @@ | ||
# TODO: Modify this Procfile to fit your needs | ||
web: gunicorn app:app | ||
web: gunicorn climbdex:create_app() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import flask | ||
|
||
import climbdex.api | ||
import climbdex.views | ||
|
||
|
||
def create_app(): | ||
app = flask.Flask(__name__, instance_relative_config=True) | ||
app.register_blueprint(climbdex.api.blueprint) | ||
app.register_blueprint(climbdex.views.blueprint) | ||
return app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import flask | ||
|
||
import climbdex.db | ||
|
||
blueprint = flask.Blueprint("api", __name__) | ||
|
||
|
||
@blueprint.route("/api/v1/<board_name>/layouts") | ||
def layouts(board_name): | ||
return flask.jsonify(climbdex.db.get_data(board_name, "layouts")) | ||
|
||
|
||
@blueprint.route("/api/v1/<board_name>/layouts/<layout_id>/sizes") | ||
def sizes(board_name, layout_id): | ||
return flask.jsonify( | ||
climbdex.db.get_data(board_name, "sizes", {"layout_id": layout_id}) | ||
) | ||
|
||
|
||
@blueprint.route("/api/v1/<board_name>/layouts/<layout_id>/sizes/<size_id>/sets") | ||
def sets(board_name, layout_id, size_id): | ||
return flask.jsonify( | ||
climbdex.db.get_data( | ||
board_name, "sets", {"layout_id": layout_id, "size_id": size_id} | ||
) | ||
) | ||
|
||
|
||
@blueprint.route("/api/v1/search/count") | ||
def resultsCount(): | ||
return flask.jsonify(climbdex.db.get_search_count(flask.request.args)) | ||
|
||
|
||
@blueprint.route("/api/v1/search") | ||
def search(): | ||
return flask.jsonify(climbdex.db.get_search_results(flask.request.args)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import flask | ||
import sqlite3 | ||
|
||
QUERIES = { | ||
"angles": """ | ||
SELECT angle | ||
FROM products_angles | ||
JOIN layouts | ||
ON layouts.product_id = products_angles.product_id | ||
WHERE layouts.id = $layout_id | ||
ORDER BY angle ASC""", | ||
"colors": """ | ||
SELECT | ||
placement_roles.id, | ||
'#' || placement_roles.screen_color | ||
FROM placement_roles | ||
JOIN layouts | ||
ON layouts.product_id = placement_roles.product_id | ||
WHERE layouts.id = $layout_id""", | ||
"grades": """ | ||
SELECT | ||
difficulty, | ||
boulder_name | ||
FROM difficulty_grades | ||
WHERE is_listed = 1 | ||
ORDER BY difficulty ASC""", | ||
"holds": """ | ||
SELECT | ||
placements.id, | ||
holes.x, | ||
holes.y | ||
FROM placements | ||
INNER JOIN holes | ||
ON placements.hole_id=holes.id | ||
WHERE placements.layout_id = $layout_id | ||
AND placements.set_id = $set_id""", | ||
"layouts": """ | ||
SELECT id, name | ||
FROM layouts | ||
WHERE is_listed=1 | ||
AND password IS NULL""", | ||
"layout_name": """ | ||
SELECT name | ||
FROM layouts | ||
WHERE id = $layout_id""", | ||
"image_filename": """ | ||
SELECT | ||
image_filename | ||
FROM product_sizes_layouts_sets | ||
WHERE layout_id = $layout_id | ||
AND product_size_id = $size_id | ||
AND set_id = $set_id""", | ||
"search": """ | ||
SELECT | ||
climbs.uuid, | ||
climbs.setter_username, | ||
climbs.name, | ||
climbs.description, | ||
climbs.frames, | ||
climb_stats.angle, | ||
climb_stats.ascensionist_count, | ||
(SELECT boulder_name FROM difficulty_grades WHERE difficulty = ROUND(climb_stats.display_difficulty)) AS difficulty, | ||
climb_stats.quality_average | ||
FROM climbs | ||
LEFT JOIN climb_stats | ||
ON climb_stats.climb_uuid = climbs.uuid | ||
INNER JOIN product_sizes | ||
ON product_sizes.id = $size_id | ||
WHERE climbs.frames_count = 1 | ||
AND climbs.is_draft = 0 | ||
AND climbs.is_listed = 1 | ||
AND climbs.layout_id = $layout_id | ||
AND climbs.edge_left >= product_sizes.edge_left | ||
AND climbs.edge_right <= product_sizes.edge_right | ||
AND climbs.edge_bottom >= product_sizes.edge_bottom | ||
AND climbs.edge_top <= product_sizes.edge_top | ||
AND climb_stats.ascensionist_count >= $min_ascents | ||
AND climb_stats.display_difficulty BETWEEN $min_grade AND $max_grade | ||
AND climb_stats.quality_average >= $min_rating | ||
""", | ||
"sets": """ | ||
SELECT | ||
sets.id, | ||
sets.name | ||
FROM sets | ||
INNER JOIN product_sizes_layouts_sets psls on sets.id = psls.set_id | ||
WHERE psls.product_size_id = $size_id | ||
AND psls.layout_id = $layout_id""", | ||
"size_name": """ | ||
SELECT | ||
product_sizes.name | ||
FROM product_sizes | ||
INNER JOIN layouts | ||
ON product_sizes.product_id = layouts.product_id | ||
WHERE layouts.id = $layout_id | ||
AND product_sizes.id = $size_id""", | ||
"sizes": """ | ||
SELECT | ||
product_sizes.id, | ||
product_sizes.name, | ||
product_sizes.description | ||
FROM product_sizes | ||
INNER JOIN layouts | ||
ON product_sizes.product_id = layouts.product_id | ||
WHERE layouts.id = $layout_id""", | ||
"size_dimensions": """ | ||
SELECT | ||
edge_left, | ||
edge_right, | ||
edge_bottom, | ||
edge_top | ||
FROM product_sizes | ||
WHERE id = $size_id""", | ||
} | ||
|
||
|
||
def get_board_database(board_name): | ||
try: | ||
return flask.g.database | ||
except AttributeError: | ||
flask.g.database = sqlite3.connect(f"data/{board_name}/db.sqlite3") | ||
return flask.g.database | ||
|
||
|
||
def get_data(board_name, query_name, binds={}): | ||
database = get_board_database(board_name) | ||
cursor = database.cursor() | ||
cursor.execute(QUERIES[query_name], binds) | ||
return cursor.fetchall() | ||
|
||
|
||
def get_search_count(args): | ||
base_sql, binds = get_search_base_sql_and_binds(args) | ||
database = get_board_database(args.get("board")) | ||
cursor = database.cursor() | ||
cursor.execute(f"SELECT COUNT(*) FROM ({base_sql})", binds) | ||
return cursor.fetchall()[0][0] | ||
|
||
|
||
def get_search_results(args): | ||
base_sql, binds = get_search_base_sql_and_binds(args) | ||
order_by_sql_name = { | ||
"ascents": "climb_stats.ascensionist_count", | ||
"difficulty": "climb_stats.display_difficulty", | ||
"name": "climbs.name", | ||
"quality": "climb_stats.quality_average", | ||
}[flask.request.args.get("sortBy")] | ||
sort_order = "ASC" if flask.request.args.get("sortOrder") == "asc" else "DESC" | ||
ordered_sql = f"{base_sql} ORDER BY {order_by_sql_name} {sort_order}" | ||
|
||
limited_sql = f"{ordered_sql} LIMIT $limit OFFSET $offset" | ||
binds["limit"] = int(flask.request.args.get("pageSize", 10)) | ||
binds["offset"] = int(flask.request.args.get("page", 0)) * int(binds["limit"]) | ||
|
||
database = get_board_database(flask.request.args.get("board")) | ||
cursor = database.cursor() | ||
results = cursor.execute(limited_sql, binds).fetchall() | ||
print(results) | ||
return results | ||
|
||
|
||
def get_search_base_sql_and_binds(args): | ||
sql = QUERIES["search"] | ||
binds = { | ||
"layout_id": args.get("layout"), | ||
"size_id": args.get("size"), | ||
"min_ascents": args.get("minAscents"), | ||
"min_grade": args.get("minGrade"), | ||
"max_grade": args.get("maxGrade"), | ||
"min_rating": args.get("minRating"), | ||
} | ||
|
||
angle = args.get("angle") | ||
if angle and angle != "any": | ||
sql += " AND climb_stats.angle = $angle" | ||
binds["angle"] = angle | ||
|
||
holds = args.get("holds") | ||
if holds: | ||
sql += " AND climbs.frames LIKE $like_string" | ||
like_string_center = "%".join( | ||
sorted( | ||
f"p{hold_string}" | ||
for hold_string in holds.split("p") | ||
if len(hold_string) > 0 | ||
) | ||
) | ||
like_string = f"%{like_string_center}%" | ||
binds["like_string"] = like_string | ||
|
||
return sql, binds |
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import boardlib.api.aurora | ||
import flask | ||
|
||
import climbdex.db | ||
|
||
blueprint = flask.Blueprint("view", __name__) | ||
|
||
|
||
@blueprint.route("/") | ||
def index(): | ||
return flask.render_template( | ||
"boardSelection.html.j2", | ||
) | ||
|
||
|
||
@blueprint.route("/filter") | ||
def filter(): | ||
board_name = flask.request.args.get("board") | ||
layout_id = flask.request.args.get("layout") | ||
size_id = flask.request.args.get("size") | ||
set_ids = flask.request.args.getlist("set") | ||
return flask.render_template( | ||
"filterSelection.html.j2", | ||
params=flask.request.args, | ||
board_name=board_name, | ||
layout_name=climbdex.db.get_data( | ||
board_name, "layout_name", {"layout_id": layout_id} | ||
)[0][0], | ||
size_name=climbdex.db.get_data( | ||
board_name, "size_name", {"layout_id": layout_id, "size_id": size_id} | ||
)[0][0], | ||
angles=climbdex.db.get_data(board_name, "angles", {"layout_id": layout_id}), | ||
grades=climbdex.db.get_data(board_name, "grades"), | ||
colors=climbdex.db.get_data(board_name, "colors", {"layout_id": layout_id}), | ||
**get_draw_board_kwargs(board_name, layout_id, size_id, set_ids), | ||
) | ||
|
||
|
||
@blueprint.route("/results") | ||
def results(): | ||
board_name = flask.request.args.get("board") | ||
layout_id = flask.request.args.get("layout") | ||
size_id = flask.request.args.get("size") | ||
set_ids = flask.request.args.getlist("set") | ||
return flask.render_template( | ||
"results.html.j2", | ||
app_url=boardlib.api.aurora.WEB_HOSTS[board_name], | ||
colors=climbdex.db.get_data(board_name, "colors", {"layout_id": layout_id}), | ||
**get_draw_board_kwargs( | ||
board_name, | ||
layout_id, | ||
size_id, | ||
set_ids, | ||
), | ||
) | ||
|
||
|
||
def get_draw_board_kwargs(board_name, layout_id, size_id, set_ids): | ||
images_to_holds = {} | ||
for set_id in set_ids: | ||
image_filename = climbdex.db.get_data( | ||
board_name, | ||
"image_filename", | ||
{"layout_id": layout_id, "size_id": size_id, "set_id": set_id}, | ||
)[0][0] | ||
image_url = f"{boardlib.api.aurora.API_HOSTS[board_name]}/img/{image_filename}" | ||
images_to_holds[image_url] = climbdex.db.get_data( | ||
board_name, "holds", {"layout_id": layout_id, "set_id": set_id} | ||
) | ||
|
||
size_dimensions = climbdex.db.get_data( | ||
board_name, "size_dimensions", {"size_id": size_id} | ||
)[0] | ||
return { | ||
"images_to_holds": images_to_holds, | ||
"edge_left": size_dimensions[0], | ||
"edge_right": size_dimensions[1], | ||
"edge_bottom": size_dimensions[2], | ||
"edge_top": size_dimensions[3], | ||
} |