Skip to content

Commit 49e0489

Browse files
committed
change everything to fastapi
1 parent 3f4e262 commit 49e0489

File tree

21 files changed

+356
-153
lines changed

21 files changed

+356
-153
lines changed
Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from fastapi import FastAPI
2+
13
from findthatpostcode.blueprints import (
24
addtocsv,
35
areas,
@@ -10,14 +12,14 @@
1012
tools,
1113
)
1214

15+
app = FastAPI()
1316

14-
def init_app(app):
15-
app.register_blueprint(areas.bp)
16-
app.register_blueprint(areatypes.bp)
17-
app.register_blueprint(postcodes.bp)
18-
app.register_blueprint(points.bp)
19-
app.register_blueprint(reconcile.bp)
20-
app.register_blueprint(addtocsv.bp)
21-
app.register_blueprint(places.bp)
22-
app.register_blueprint(search.bp)
23-
app.register_blueprint(tools.bp)
17+
app.include_router(areas.bp)
18+
app.include_router(addtocsv.bp)
19+
app.include_router(areatypes.bp)
20+
app.include_router(places.bp)
21+
app.include_router(points.bp)
22+
# app.include_router(postcodes.bp)
23+
# app.include_router(reconcile.bp)
24+
# app.include_router(search.bp)
25+
# app.include_router(tools.bp)
Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
import codecs
22
import os
33
import tempfile
4+
from typing import Annotated
45

5-
from flask import Blueprint, make_response, render_template, request
6+
from fastapi import APIRouter, Form, UploadFile
67

8+
from findthatpostcode.blueprints.areas import CSVResponse
79
from findthatpostcode.blueprints.process_csv import process_csv
810
from findthatpostcode.controllers.areas import area_types_count
9-
from findthatpostcode.db import get_db
11+
from findthatpostcode.db import ElasticsearchDep
1012
from findthatpostcode.metadata import (
1113
BASIC_UPLOAD_FIELDS,
1214
DEFAULT_UPLOAD_FIELDS,
1315
STATS_FIELDS,
1416
)
17+
from findthatpostcode.utils import templates
1518

16-
bp = Blueprint("addtocsv", __name__, url_prefix="/addtocsv")
19+
bp = APIRouter(prefix="/addtocsv")
1720

1821

19-
@bp.route("/", strict_slashes=False, methods=["GET"])
20-
def addtocsv():
21-
ats = area_types_count(get_db())
22-
return render_template(
22+
@bp.get("/")
23+
def addtocsv(es: ElasticsearchDep):
24+
ats = area_types_count(es)
25+
return templates.TemplateResponse(
2326
"addtocsv.html.j2",
2427
result=ats,
2528
basic_fields=BASIC_UPLOAD_FIELDS,
@@ -28,11 +31,13 @@ def addtocsv():
2831
)
2932

3033

31-
@bp.route("/", strict_slashes=False, methods=["POST"])
32-
def return_csv():
33-
upload = request.files.get("csvfile")
34-
column_name = request.form.get("column_name", "postcode")
35-
fields = request.form.getlist("fields")
34+
@bp.post("/")
35+
def return_csv(
36+
es: ElasticsearchDep,
37+
csvfile: UploadFile,
38+
column_name: Annotated[str, Form()],
39+
fields: Annotated[list[str], Form()],
40+
):
3641
if not fields:
3742
fields = DEFAULT_UPLOAD_FIELDS
3843

@@ -56,23 +61,23 @@ def return_csv():
5661
fields.append("lep2_name")
5762
fields.remove("lep_name")
5863

59-
_, ext = os.path.splitext(upload.filename)
64+
_, ext = os.path.splitext(csvfile.filename)
6065
if ext not in [".csv"]:
6166
return "File extension not allowed."
6267

6368
with tempfile.SpooledTemporaryFile(mode="w+", newline="") as output:
6469
process_csv(
65-
codecs.iterdecode(upload.stream, "utf-8"),
70+
codecs.iterdecode(csvfile.file, "utf-8"),
6671
output,
67-
get_db(),
72+
es,
6873
column_name,
6974
fields,
7075
)
7176
output.seek(0)
7277

73-
response = make_response(output.read())
78+
response = CSVResponse(output.read())
7479
response.headers["Content-Type"] = "text/csv"
7580
response.headers["Content-Disposition"] = 'attachment; filename="{}"'.format(
76-
upload.filename
81+
csvfile.filename
7782
)
7883
return response

findthatpostcode/blueprints/areas.py

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,30 @@
22
import io
33
import re
44

5-
from flask import (
6-
Blueprint,
7-
abort,
8-
jsonify,
9-
make_response,
10-
redirect,
11-
render_template,
12-
request,
13-
url_for,
14-
)
5+
from fastapi import APIRouter, Request
6+
from fastapi.responses import JSONResponse, RedirectResponse
157

168
from findthatpostcode.blueprints.utils import return_result
179
from findthatpostcode.controllers.areas import Area, get_all_areas
18-
from findthatpostcode.db import get_db
10+
from findthatpostcode.db import ElasticsearchDep
11+
from findthatpostcode.utils import CSVResponse, templates
1912

20-
bp = Blueprint("areas", __name__, url_prefix="/areas")
13+
bp = APIRouter(prefix="/areas")
2114

2215

23-
@bp.route("/search")
24-
@bp.route("/search.<filetype>")
25-
def area_search(filetype="json"):
26-
return redirect(url_for("search.search_index", q=request.values.get("q")), code=301)
16+
@bp.get("/search")
17+
@bp.get("/search.<filetype>")
18+
def area_search(request: Request, filetype="json", q: str | None = None):
19+
return RedirectResponse(
20+
request.url_for("search.search_index", q=q), status_code=301
21+
)
2722

2823

29-
@bp.route("/names.csv")
30-
def all_names():
24+
@bp.get("/names.csv")
25+
def all_names(es: ElasticsearchDep, types: str = ""):
3126
areas = get_all_areas(
32-
get_db(),
33-
areatypes=[
34-
a.strip().lower() for a in request.values.get("types", "").split(",") if a
35-
],
27+
es,
28+
areatypes=[a.strip().lower() for a in types.split(",") if a],
3629
)
3730
return areas_csv(areas, "names.csv")
3831

@@ -49,15 +42,14 @@ def areas_csv(areas, filename):
4942
if re.match(r"[A-Z][0-9]{8}", row.get("code")):
5043
writer.writerow(row)
5144

52-
output = make_response(si.getvalue())
45+
output = CSVResponse(si.getvalue())
5346
output.headers["Content-Disposition"] = "attachment; filename={}".format(filename)
5447
output.headers["Content-type"] = "text/csv"
5548
return output
5649

5750

58-
@bp.route("/<areacodes>.geojson")
59-
def get_area_boundary(areacodes):
60-
es = get_db()
51+
@bp.get("/<areacodes>.geojson")
52+
def get_area_boundary(areacodes: str, es: ElasticsearchDep):
6153
areacodes = areacodes.split("+")
6254
features = []
6355
for areacode in areacodes:
@@ -66,13 +58,12 @@ def get_area_boundary(areacodes):
6658
if status == 200:
6759
features.extend(r.get("features"))
6860
if status != 200:
69-
return abort(make_response(jsonify(message=r), status))
70-
return jsonify({"type": "FeatureCollection", "features": features})
61+
return JSONResponse(message=r, status_code=status)
62+
return JSONResponse({"type": "FeatureCollection", "features": features})
7163

7264

73-
@bp.route("/<areacode>/children/<areatype>.geojson")
74-
def get_area_children_boundary(areacode, areatype):
75-
es = get_db()
65+
@bp.get("/<areacode>/children/<areatype>.geojson")
66+
def get_area_children_boundary(areacode: str, areatype: str, es: ElasticsearchDep):
7667
area = Area.get_from_es(areacode, es, boundary=False, examples_count=0)
7768
features = []
7869
errors = {}
@@ -84,31 +75,36 @@ def get_area_children_boundary(areacode, areatype):
8475
else:
8576
errors[child_area.id] = r
8677
if not features:
87-
return abort(make_response(jsonify(message=errors), status))
88-
return jsonify({"type": "FeatureCollection", "features": features})
78+
return JSONResponse(message=errors, status_code=status)
79+
return JSONResponse({"type": "FeatureCollection", "features": features})
8980

9081

9182
@bp.route("/<areacode>")
9283
@bp.route("/<areacode>.<filetype>")
93-
def get_area(areacode, filetype="json"):
84+
def get_area(
85+
areacode: str,
86+
filetype: str = "json",
87+
es: ElasticsearchDep = None,
88+
child: str | None = None,
89+
):
9490
result = Area.get_from_es(
9591
areacode,
96-
get_db(),
92+
es,
9793
boundary=(filetype == "geojson"),
9894
examples_count=(0 if filetype == "geojson" else 5),
9995
)
10096

10197
if filetype == "geojson":
10298
status, r = result.geoJSON()
10399
if status != 200:
104-
return abort(make_response(jsonify(message=r), status))
105-
return jsonify(r)
100+
return JSONResponse(message=r, status_code=status)
101+
return JSONResponse(r)
106102

107103
return return_result(
108104
result,
109105
filetype,
110106
"area.html.j2",
111-
child=request.values.get("child"),
107+
child=child,
112108
example_postcode_json=[
113109
p.attributes.get("location")
114110
for p in result.relationships["example_postcodes"]
@@ -119,4 +115,4 @@ def get_area(areacode, filetype="json"):
119115

120116
@bp.route("/<areacodes>/map")
121117
def get_area_map(areacodes):
122-
return render_template("area_map.html.j2", areacodes=areacodes)
118+
return templates.TemplateResponse("area_map.html.j2", {"areacodes": areacodes})

findthatpostcode/blueprints/areatypes.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
from flask import Blueprint, render_template, request, url_for
1+
from fastapi import APIRouter, Request
22

33
from findthatpostcode.blueprints.areas import areas_csv
44
from findthatpostcode.blueprints.utils import return_result
5-
from findthatpostcode.controllers.areas import Areatype, area_types_count, get_all_areas
5+
from findthatpostcode.controllers.areas import (
6+
Areatype,
7+
area_types_count,
8+
get_all_areas,
9+
)
610
from findthatpostcode.controllers.controller import Pagination
711
from findthatpostcode.db import get_db
12+
from findthatpostcode.utils import templates
813

9-
bp = Blueprint("areatypes", __name__, url_prefix="/areatypes")
14+
bp = APIRouter(prefix="/areatypes")
1015

1116

12-
@bp.route("/", strict_slashes=False)
17+
@bp.get("/")
1318
def all():
1419
ats = area_types_count(get_db())
15-
return render_template("areatypes.html.j2", result=ats)
20+
return templates.TemplateResponse("areatypes.html.j2", {"result": ats})
1621

1722

18-
@bp.route("/<areacode>")
19-
@bp.route("/<areacode>.<filetype>")
20-
def get_areatype(areacode, filetype="json"):
23+
@bp.get("/<areacode>")
24+
@bp.get("/<areacode>.<filetype>")
25+
def get_areatype(areacode: str, request: Request, filetype: str = "json"):
2126
if filetype == "csv":
2227
areas = get_all_areas(get_db(), areatypes=[areacode.strip().lower()])
2328
return areas_csv(areas, "{}.csv".format(areacode))
@@ -27,7 +32,7 @@ def get_areatype(areacode, filetype="json"):
2732

2833
pagination.set_pagination(result.attributes["count_areas"])
2934
nav = {
30-
p: url_for(
35+
p: request.url_for(
3136
"areatypes.get_areatype", areacode=areacode, filetype=filetype, **args
3237
)
3338
if isinstance(args, dict)
Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
from flask import Blueprint, jsonify, redirect, request, url_for
1+
from fastapi import APIRouter, Request
2+
from fastapi.responses import JSONResponse, RedirectResponse
23

34
from findthatpostcode.blueprints.utils import return_result
45
from findthatpostcode.controllers.places import Place
5-
from findthatpostcode.db import get_db
6+
from findthatpostcode.db import ElasticsearchDep
67

7-
bp = Blueprint("places", __name__, url_prefix="/places")
8+
bp = APIRouter(prefix="/places")
89

910

10-
@bp.route("/redirect")
11-
def point_redirect():
12-
lat = float(request.args.get("lat"))
13-
lon = float(request.args.get("lon"))
14-
return redirect(
15-
url_for("places.nearest", lat=lat, lon=lon, filetype="html"), code=303
11+
@bp.get("/redirect")
12+
def point_redirect(lat: float, lon: float, request: Request):
13+
return RedirectResponse(
14+
request.url_for("places.nearest", lat=lat, lon=lon, filetype="html"), code=303
1615
)
1716

1817

19-
@bp.route("/nearest/<lat>,<lon>")
20-
@bp.route("/nearest/<lat>,<lon>.<filetype>")
21-
def nearest(lat, lon, filetype="json"):
22-
es = get_db()
18+
@bp.get("/nearest/{lat},{lon}")
19+
@bp.get("/nearest/{lat},{lon}.{filetype}")
20+
def nearest(lat: float, lon: float, es: ElasticsearchDep, filetype: str = "json"):
2321
query = {
2422
"query": {"match_all": {}},
2523
"sort": [
@@ -34,11 +32,11 @@ def nearest(lat, lon, filetype="json"):
3432
size=10,
3533
_source_excludes=[],
3634
)
37-
return jsonify(data)
35+
return JSONResponse(content=data)
3836

3937

40-
@bp.route("/<areacode>")
41-
@bp.route("/<areacode>.<filetype>")
42-
def get_place(areacode, filetype="json"):
43-
result = Place.get_from_es(areacode, get_db())
38+
@bp.get("/{areacode}")
39+
@bp.get("/{areacode}.{filetype}")
40+
def get_place(areacode: str, es: ElasticsearchDep, filetype: str = "json"):
41+
result = Place.get_from_es(areacode, es)
4442
return return_result(result, filetype, "place.html.j2")

findthatpostcode/blueprints/points.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
1-
from flask import Blueprint, redirect, request, url_for
1+
from fastapi import APIRouter, Request
2+
from fastapi.responses import RedirectResponse
23

34
from findthatpostcode.blueprints.utils import return_result
45
from findthatpostcode.controllers.points import Point
5-
from findthatpostcode.db import get_db
6+
from findthatpostcode.db import ElasticsearchDep
67

7-
bp = Blueprint("points", __name__, url_prefix="/points")
8+
bp = APIRouter(prefix="/points")
89

910

1011
@bp.route("/redirect")
11-
def point_redirect():
12-
lat = float(request.args.get("lat"))
13-
lon = float(request.args.get("lon"))
14-
return redirect(
15-
url_for("points.get", latlon="{},{}.html".format(lat, lon)), code=303
12+
def point_redirect(lat: float, lon: float, request: Request):
13+
return RedirectResponse(
14+
request.url_for(
15+
"points.get", latlon="{},{}.html".format(lat, lon), filetype="html"
16+
),
17+
code=303,
1618
)
1719

1820

19-
@bp.route("/<latlon>")
20-
def get(latlon):
21+
@bp.get("/{latlon}")
22+
def get(latlon, es: ElasticsearchDep):
2123
filetype = "json"
2224
if latlon.endswith(".json"):
2325
latlon = latlon[:-5]
2426
elif latlon.endswith(".html"):
2527
latlon = latlon[:-5]
2628
filetype = "html"
2729
lat, lon = latlon.split(",")
28-
es = get_db()
2930
result = Point.get_from_es((float(lat), float(lon)), es)
3031
errors = result.get_errors()
3132
if errors:

0 commit comments

Comments
 (0)