DebasishDhal commited on
Commit
1bfa936
·
unverified ·
2 Parent(s): a617b7a 7934774

Merge pull request #6 from DebasishDhal/feat/performance

Browse files

- Add in-memory cache to backend routes, working.
- Put caching in background task
- Improve router names to avoid confusion
- Add .env to dynamically modify caching TTL.

Files changed (3) hide show
  1. frontend/src/components/Map.js +2 -2
  2. main.py +72 -27
  3. requirements.txt +2 -1
frontend/src/components/Map.js CHANGED
@@ -132,10 +132,10 @@ const Map = ( { onMapClick, searchQuery, contentType, setSearchQuery, setSubmitt
132
  try{
133
  let endpoint;
134
  if (contentType === 'summary') {
135
- endpoint = `${BACKEND_URL}/wiki/${pageName}`;
136
  }
137
  else if (contentType === 'full') {
138
- endpoint = `${BACKEND_URL}/wiki/search/${pageName}`;
139
  }
140
 
141
  else {
 
132
  try{
133
  let endpoint;
134
  if (contentType === 'summary') {
135
+ endpoint = `${BACKEND_URL}/wiki/search/summary/${pageName}`;
136
  }
137
  else if (contentType === 'full') {
138
+ endpoint = `${BACKEND_URL}/wiki/search/full/${pageName}`;
139
  }
140
 
141
  else {
main.py CHANGED
@@ -1,10 +1,15 @@
1
- from fastapi import FastAPI
2
  from fastapi.middleware.cors import CORSMiddleware
3
- from fastapi.responses import JSONResponse, HTMLResponse
4
  from pydantic import BaseModel, Field
5
  import requests
6
  from geopy.geocoders import Nominatim
7
  import geopy.distance
 
 
 
 
 
8
 
9
  app = FastAPI()
10
 
@@ -25,34 +30,58 @@ app.add_middleware(
25
  allow_headers=["*"],
26
  )
27
 
 
 
 
 
28
  @app.get("/")
29
  def health_check():
30
  return {"status": "ok"}
31
 
32
- @app.get("/wiki/{page_name}")
33
- async def get_wiki_page(page_name: str):
34
- response = requests.get(f"https://en.wikipedia.org/api/rest_v1/page/summary/{page_name}", timeout=10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
- if response.status_code != 200:
37
  return JSONResponse(
38
- content={"error": "Page not found"},
39
- status_code=404
 
 
 
 
 
40
  )
41
-
42
- coords = loc.geocode(page_name)
43
-
44
- return JSONResponse(
45
- content={
46
- "title": page_name,
47
- "content": f"{response.json().get('extract', 'No content available')}",
48
- "latitude": coords.latitude if coords else None,
49
- "longitude": coords.longitude if coords else None
50
- },
51
- status_code=200
52
- )
53
 
54
- @app.get("/wiki/search/{full_page}")
55
- def search_wiki(full_page: str):
 
 
 
 
56
  response = requests.get(f"https://en.wikipedia.org/wiki/{full_page}", timeout=10)
57
  try:
58
  if response.status_code != 200:
@@ -60,18 +89,25 @@ def search_wiki(full_page: str):
60
  content={"error": "Page not found"},
61
  status_code=404
62
  )
 
 
 
 
63
 
64
- coords = loc.geocode(full_page)
65
-
66
- return JSONResponse(
67
- content={
68
  "title": full_page,
69
  "content": str(response.text),
70
  "latitude": coords.latitude if coords else None,
71
  "longitude": coords.longitude if coords else None
72
- },
 
 
 
 
 
73
  status_code=200
74
  )
 
75
  except Exception as e:
76
  return JSONResponse(
77
  content={"error": str(e), 'response': str(response)},
@@ -113,4 +149,13 @@ def get_geodistance(payload: Geodistance):
113
  "lon2": lon2
114
  },
115
  status_code=200
 
 
 
 
 
 
 
 
 
116
  )
 
1
+ from fastapi import FastAPI, BackgroundTasks
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse
4
  from pydantic import BaseModel, Field
5
  import requests
6
  from geopy.geocoders import Nominatim
7
  import geopy.distance
8
+ from cachetools import TTLCache
9
+ import os
10
+ from dotenv import load_dotenv
11
+
12
+ load_dotenv()
13
 
14
  app = FastAPI()
15
 
 
30
  allow_headers=["*"],
31
  )
32
 
33
+ BACKEND_WIKI_CACHE_TTL = int(os.getenv("BACKEND_WIKI_CACHE_TTL", 300))
34
+ summary_cache = TTLCache(maxsize=100, ttl=BACKEND_WIKI_CACHE_TTL) # ttl time in seconds, then cache expires
35
+ full_page_cache = TTLCache(maxsize=100, ttl=BACKEND_WIKI_CACHE_TTL)
36
+
37
  @app.get("/")
38
  def health_check():
39
  return {"status": "ok"}
40
 
41
+ @app.get("/wiki/search/summary/{summary_page_name}")
42
+ async def get_wiki_summary(summary_page_name: str, background_tasks: BackgroundTasks):
43
+ if summary_page_name in summary_cache:
44
+ # print("Cache hit for summary:", page_name) #Working
45
+ return JSONResponse(content=summary_cache[summary_page_name], status_code=200)
46
+ try:
47
+ response = requests.get(f"https://en.wikipedia.org/api/rest_v1/page/summary/{summary_page_name}", timeout=10)
48
+
49
+ if response.status_code != 200:
50
+ return JSONResponse(
51
+ content={"error": "Page not found"},
52
+ status_code=404
53
+ )
54
+ try:
55
+ coords = loc.geocode(summary_page_name, timeout=5)
56
+ except Exception as e:
57
+ coords = None
58
+
59
+ result = {
60
+ "title": summary_page_name,
61
+ "content": f"{response.json().get('extract', 'No content available')}",
62
+ "latitude": coords.latitude if coords else None,
63
+ "longitude": coords.longitude if coords else None
64
+ }
65
+
66
+ background_tasks.add_task(lambda: summary_cache.__setitem__(summary_page_name, result))
67
+
68
 
 
69
  return JSONResponse(
70
+ content= result,
71
+ status_code=200
72
+ )
73
+ except Exception as e:
74
+ return JSONResponse(
75
+ content={"error": str(e), 'response': str(response)},
76
+ status_code=500
77
  )
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
+ @app.get("/wiki/search/full/{full_page}")
80
+ def search_wiki_full_page(full_page: str, background_tasks: BackgroundTasks):
81
+ if full_page in full_page_cache:
82
+ # print("Cache hit for full_page:", full_page) #Working
83
+ return JSONResponse(content=full_page_cache[full_page], status_code=200)
84
+
85
  response = requests.get(f"https://en.wikipedia.org/wiki/{full_page}", timeout=10)
86
  try:
87
  if response.status_code != 200:
 
89
  content={"error": "Page not found"},
90
  status_code=404
91
  )
92
+ try:
93
+ coords = loc.geocode(full_page, timeout=5)
94
+ except Exception as e:
95
+ coords = None
96
 
97
+ result = {
 
 
 
98
  "title": full_page,
99
  "content": str(response.text),
100
  "latitude": coords.latitude if coords else None,
101
  "longitude": coords.longitude if coords else None
102
+ }
103
+
104
+ background_tasks.add_task(lambda: full_page_cache.__setitem__(full_page, result))
105
+
106
+ return JSONResponse(
107
+ content= result,
108
  status_code=200
109
  )
110
+
111
  except Exception as e:
112
  return JSONResponse(
113
  content={"error": str(e), 'response': str(response)},
 
149
  "lon2": lon2
150
  },
151
  status_code=200
152
+ )
153
+
154
+ @app.get("/random")
155
+ def random():
156
+ return JSONResponse(
157
+ content={
158
+ "message": "Spare endpoint to test."
159
+ },
160
+ status_code=200
161
  )
requirements.txt CHANGED
@@ -8,4 +8,5 @@ pyjwt == 2.9.0
8
  pytest == 8.3.4
9
  geopy == 2.4.1
10
  gpxpy == 1.6.2
11
- requests == 2.30.0
 
 
8
  pytest == 8.3.4
9
  geopy == 2.4.1
10
  gpxpy == 1.6.2
11
+ requests == 2.30.0
12
+ cachetools == 5.5.1