Spaces:
Sleeping
Sleeping
Upload 9 files
Browse files- Dockerfile +18 -0
- api/endpoints/location.py +47 -0
- core/config.py +8 -0
- core/init_nlp.py +43 -0
- core/init_supabase.py +64 -0
- main.py +17 -0
- models/location_models.py +18 -0
- requirements.txt +96 -0
- services/location_service.py +62 -0
Dockerfile
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.12.2-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
COPY . /app
|
6 |
+
|
7 |
+
RUN pip install -r requirements.txt
|
8 |
+
|
9 |
+
RUN useradd -m -u 1000 user
|
10 |
+
|
11 |
+
USER user
|
12 |
+
|
13 |
+
# Copy the rest of the application code into the container at /app
|
14 |
+
COPY --chown=user . /app/
|
15 |
+
|
16 |
+
|
17 |
+
# Command to run your application
|
18 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860" , "--workers" , "5"]
|
api/endpoints/location.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException , Body
|
2 |
+
from models.location_models import BodyData,ErrorResponse
|
3 |
+
from services.location_service import LocationService
|
4 |
+
import core.init_supabase as sp
|
5 |
+
router = APIRouter()
|
6 |
+
|
7 |
+
@router.post("/get_coordinates")
|
8 |
+
async def get_coordinates(data: BodyData = Body(...)):
|
9 |
+
token = data.jwt_token
|
10 |
+
user_id = sp.authenticate_user(token)
|
11 |
+
if user_id == "Exceptional error":
|
12 |
+
return {"User not Authenticated!"}
|
13 |
+
else:
|
14 |
+
print(user_id)
|
15 |
+
supabase_user_data = sp.fetch_data(user_id)
|
16 |
+
print("supabase data")
|
17 |
+
print(supabase_user_data)
|
18 |
+
coords=[]*len(supabase_user_data)
|
19 |
+
for cord in supabase_user_data:
|
20 |
+
result = LocationService.get_coordinates(cord)
|
21 |
+
coords.append(result)
|
22 |
+
if isinstance(result, ErrorResponse):
|
23 |
+
print(HTTPException(status_code=400, detail=result.error))
|
24 |
+
return {"message":"An unexpected error occured please try again !!"}
|
25 |
+
print(coords)
|
26 |
+
return {"data":coords}
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
@router.post("/get_card_reccomendation")
|
31 |
+
async def get_coordinates(data: BodyData = Body(...)):
|
32 |
+
token = data.jwt_token
|
33 |
+
user_id = sp.authenticate_user(token)
|
34 |
+
if user_id == "Exceptional error":
|
35 |
+
return {"User not Authenticated!"}
|
36 |
+
else:
|
37 |
+
print(user_id)
|
38 |
+
try:
|
39 |
+
supabase_card_data = sp.fetch_cards_data(user_id)
|
40 |
+
print("supabase data")
|
41 |
+
print(supabase_card_data)
|
42 |
+
except Exception as e:
|
43 |
+
print(HTTPException(status_code=400, detail=e))
|
44 |
+
return {"message":"An unexpected error occured please try again !!"}
|
45 |
+
print("Returning the data")
|
46 |
+
return {"data":supabase_card_data}
|
47 |
+
|
core/config.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic_settings import BaseSettings
|
2 |
+
|
3 |
+
class Settings(BaseSettings):
|
4 |
+
APP_NAME: str = "Location Analytics API"
|
5 |
+
DEBUG_MODE: bool = False
|
6 |
+
SPACY_MODEL: str = "en_core_web_sm" # or any other model you prefer
|
7 |
+
|
8 |
+
settings = Settings()
|
core/init_nlp.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import nltk
|
2 |
+
import nltk.downloader
|
3 |
+
import spacy
|
4 |
+
from core.config import settings
|
5 |
+
|
6 |
+
def initialize_nlp():
|
7 |
+
print("Initializing NLP resources...")
|
8 |
+
|
9 |
+
# Download NLTK resources
|
10 |
+
nltk_resources = [
|
11 |
+
'maxent_ne_chunker',
|
12 |
+
'words',
|
13 |
+
'treebank',
|
14 |
+
'maxent_treebank_pos_tagger',
|
15 |
+
'punkt',
|
16 |
+
'averaged_perceptron_tagger'
|
17 |
+
]
|
18 |
+
|
19 |
+
for resource in nltk_resources:
|
20 |
+
nltk.downloader.download(resource, quiet=True)
|
21 |
+
|
22 |
+
# Load spaCy model
|
23 |
+
spacy.load(settings.SPACY_MODEL)
|
24 |
+
|
25 |
+
print("NLP resources initialized successfully.")
|
26 |
+
|
27 |
+
# Global variables to store initialized resources
|
28 |
+
nlp = None
|
29 |
+
nltk_initialized = False
|
30 |
+
|
31 |
+
def get_nlp():
|
32 |
+
global nlp
|
33 |
+
if nlp is None:
|
34 |
+
nlp = spacy.load(settings.SPACY_MODEL)
|
35 |
+
return nlp
|
36 |
+
|
37 |
+
def get_nltk():
|
38 |
+
global nltk_initialized
|
39 |
+
if not nltk_initialized:
|
40 |
+
nltk.downloader.download('punkt', quiet=True)
|
41 |
+
nltk.download('averaged_perceptron_tagger', quiet=True)
|
42 |
+
nltk_initialized = True
|
43 |
+
return nltk
|
core/init_supabase.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
from supabase import create_client
|
4 |
+
from functools import lru_cache
|
5 |
+
import os
|
6 |
+
load_dotenv()
|
7 |
+
@lru_cache(maxsize=1)
|
8 |
+
class Supabase_Settings(BaseModel):
|
9 |
+
|
10 |
+
SUPABASE_URL: str = os.getenv("SUPABASE_URL")
|
11 |
+
SUPABASE_ANON_KEY: str = os.getenv("SUPABASE_ANON_KEY")
|
12 |
+
SUPABASE_SERVICE_KEY: str = os.getenv("SUPABASE_SERVICE_KEY")
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
@lru_cache(maxsize=1)
|
17 |
+
class Supabase_Clients:
|
18 |
+
values = Supabase_Settings()
|
19 |
+
anon_supabase = create_client(values.SUPABASE_URL, values.SUPABASE_ANON_KEY)
|
20 |
+
service_supabase = create_client(values.SUPABASE_URL, values.SUPABASE_SERVICE_KEY)
|
21 |
+
|
22 |
+
def fetch_data(user_id:str):
|
23 |
+
supabase_clients=Supabase_Clients()
|
24 |
+
supabase = supabase_clients.anon_supabase
|
25 |
+
response_0 = (
|
26 |
+
supabase.table("receipt_radar_structured_data")
|
27 |
+
.select("location")
|
28 |
+
.eq("user_id",f"{user_id}")
|
29 |
+
.eq("brand_category","Travel and Leisure")
|
30 |
+
.execute()
|
31 |
+
)
|
32 |
+
print("Printing fetched data")
|
33 |
+
print(response_0)
|
34 |
+
return response_0.data
|
35 |
+
|
36 |
+
def authenticate_user(apitoken: str):
|
37 |
+
supabase_clients = Supabase_Clients()
|
38 |
+
supabase = supabase_clients.anon_supabase
|
39 |
+
try:
|
40 |
+
user = supabase.auth.get_user(apitoken)
|
41 |
+
return str(user.user.id)
|
42 |
+
except Exception as e:
|
43 |
+
print("Exceptional error")
|
44 |
+
return "Exceptional error"
|
45 |
+
|
46 |
+
def fetch_cards_data(user_id:str):
|
47 |
+
supabase_clients=Supabase_Clients()
|
48 |
+
supabase = supabase_clients.anon_supabase
|
49 |
+
response_0 = (
|
50 |
+
supabase.table("receipt_radar_structured_data")
|
51 |
+
.select("brand")
|
52 |
+
.eq("user_id",f"{user_id}")
|
53 |
+
.execute()
|
54 |
+
).data
|
55 |
+
response_1 = (
|
56 |
+
supabase.table("card_market")
|
57 |
+
.select("brand_name")
|
58 |
+
.execute()
|
59 |
+
).data
|
60 |
+
brands_response_1_set = set(item['brand_name'].lower() for item in response_1)
|
61 |
+
common_brands = [item['brand'] for item in response_0 if item['brand'].lower() in brands_response_1_set]
|
62 |
+
print("common_brands")
|
63 |
+
print(common_brands)
|
64 |
+
return common_brands
|
main.py
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from core.init_nlp import initialize_nlp
|
3 |
+
from contextlib import asynccontextmanager
|
4 |
+
import logging
|
5 |
+
from api.endpoints import location
|
6 |
+
|
7 |
+
|
8 |
+
@asynccontextmanager
|
9 |
+
async def lifespan(app: FastAPI):
|
10 |
+
print("Initialising nlp spacy libs")
|
11 |
+
logging.info("Initialising nlp spacy libs")
|
12 |
+
initialize_nlp()
|
13 |
+
yield
|
14 |
+
|
15 |
+
app = FastAPI(lifespan=lifespan)
|
16 |
+
app.include_router(location.router, prefix="/location/api/v1")
|
17 |
+
|
models/location_models.py
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel
|
2 |
+
from typing import Optional,List
|
3 |
+
|
4 |
+
class LocationData(BaseModel):
|
5 |
+
location: Optional[str] = None
|
6 |
+
|
7 |
+
class BodyData(BaseModel):
|
8 |
+
jwt_token:Optional[str]=None
|
9 |
+
|
10 |
+
|
11 |
+
|
12 |
+
class Coordinates(BaseModel):
|
13 |
+
latitude: Optional[float] = None
|
14 |
+
longitude: Optional[float] = None
|
15 |
+
|
16 |
+
|
17 |
+
class ErrorResponse(BaseModel):
|
18 |
+
error: str
|
requirements.txt
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
annotated-types==0.7.0
|
2 |
+
anyio==4.4.0
|
3 |
+
beautifulsoup4==4.12.3
|
4 |
+
blis==0.7.11
|
5 |
+
catalogue==2.0.10
|
6 |
+
certifi==2024.7.4
|
7 |
+
charset-normalizer==3.3.2
|
8 |
+
click==8.1.7
|
9 |
+
cloudpathlib==0.18.1
|
10 |
+
confection==0.1.5
|
11 |
+
cssselect==1.2.0
|
12 |
+
cymem==2.0.8
|
13 |
+
deprecation==2.1.0
|
14 |
+
dnspython==2.6.1
|
15 |
+
email_validator==2.2.0
|
16 |
+
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1-py3-none-any.whl#sha256=86cc141f63942d4b2c5fcee06630fd6f904788d2f0ab005cce45aadb8fb73889
|
17 |
+
fastapi==0.111.1
|
18 |
+
fastapi-cli==0.0.4
|
19 |
+
feedfinder2==0.0.4
|
20 |
+
feedparser==6.0.11
|
21 |
+
filelock==3.15.4
|
22 |
+
geographiclib==2.0
|
23 |
+
geopy==2.4.1
|
24 |
+
gotrue==2.5.5
|
25 |
+
h11==0.14.0
|
26 |
+
h2==4.1.0
|
27 |
+
hpack==4.0.0
|
28 |
+
httpcore==1.0.5
|
29 |
+
httptools==0.6.1
|
30 |
+
httpx==0.27.0
|
31 |
+
hyperframe==6.0.1
|
32 |
+
idna==3.7
|
33 |
+
jieba3k==0.35.1
|
34 |
+
Jinja2==3.1.4
|
35 |
+
joblib==1.4.2
|
36 |
+
langcodes==3.4.0
|
37 |
+
language_data==1.2.0
|
38 |
+
locationtagger==0.0.1
|
39 |
+
lxml==5.2.2
|
40 |
+
lxml_html_clean==0.1.1
|
41 |
+
marisa-trie==1.2.0
|
42 |
+
markdown-it-py==3.0.0
|
43 |
+
MarkupSafe==2.1.5
|
44 |
+
mdurl==0.1.2
|
45 |
+
murmurhash==1.0.10
|
46 |
+
newspaper3k==0.2.8
|
47 |
+
nltk==3.8.1
|
48 |
+
numpy==1.26.4
|
49 |
+
packaging==24.1
|
50 |
+
pillow==10.4.0
|
51 |
+
postgrest==0.16.9
|
52 |
+
preshed==3.0.9
|
53 |
+
pycountry==24.6.1
|
54 |
+
pydantic==2.8.2
|
55 |
+
pydantic-settings==2.3.4
|
56 |
+
pydantic_core==2.20.1
|
57 |
+
Pygments==2.18.0
|
58 |
+
python-dateutil==2.9.0.post0
|
59 |
+
python-dotenv==1.0.1
|
60 |
+
python-multipart==0.0.9
|
61 |
+
PyYAML==6.0.1
|
62 |
+
realtime==1.0.6
|
63 |
+
regex==2024.5.15
|
64 |
+
requests==2.32.3
|
65 |
+
requests-file==2.1.0
|
66 |
+
rich==13.7.1
|
67 |
+
setuptools==70.3.0
|
68 |
+
sgmllib3k==1.0.0
|
69 |
+
shellingham==1.5.4
|
70 |
+
six==1.16.0
|
71 |
+
smart-open==7.0.4
|
72 |
+
sniffio==1.3.1
|
73 |
+
soupsieve==2.5
|
74 |
+
spacy==3.7.5
|
75 |
+
spacy-legacy==3.0.12
|
76 |
+
spacy-loggers==1.0.5
|
77 |
+
srsly==2.4.8
|
78 |
+
starlette==0.37.2
|
79 |
+
storage3==0.7.7
|
80 |
+
StrEnum==0.4.15
|
81 |
+
supabase==2.5.3
|
82 |
+
supafunc==0.4.7
|
83 |
+
thinc==8.2.5
|
84 |
+
tinysegmenter==0.3
|
85 |
+
tldextract==5.1.2
|
86 |
+
tqdm==4.66.4
|
87 |
+
typer==0.12.3
|
88 |
+
typing_extensions==4.12.2
|
89 |
+
urllib3==2.2.2
|
90 |
+
uvicorn==0.30.1
|
91 |
+
uvloop==0.19.0
|
92 |
+
wasabi==1.1.3
|
93 |
+
watchfiles==0.22.0
|
94 |
+
weasel==0.4.1
|
95 |
+
websockets==12.0
|
96 |
+
wrapt==1.16.0
|
services/location_service.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import locationtagger
|
2 |
+
from geopy.geocoders import Nominatim
|
3 |
+
from geopy.exc import GeocoderTimedOut, GeocoderUnavailable
|
4 |
+
from models.location_models import LocationData, Coordinates, ErrorResponse
|
5 |
+
|
6 |
+
class LocationService:
|
7 |
+
@staticmethod
|
8 |
+
def get_coordinates(data:dict) -> Coordinates | ErrorResponse:
|
9 |
+
print("Inside get coordinates")
|
10 |
+
print(data)
|
11 |
+
try:
|
12 |
+
location = data.get('location')
|
13 |
+
except:
|
14 |
+
return Coordinates(latitude=None , longitude=None)
|
15 |
+
location_string=None
|
16 |
+
city=None
|
17 |
+
state=None
|
18 |
+
country=None
|
19 |
+
if location:
|
20 |
+
place_entity = locationtagger.find_locations(text=location)
|
21 |
+
|
22 |
+
extracted_cities = list(place_entity.cities)
|
23 |
+
extracted_regions = list(place_entity.regions)
|
24 |
+
extracted_countries = list(place_entity.countries)
|
25 |
+
|
26 |
+
if extracted_cities:
|
27 |
+
city = extracted_cities[0]
|
28 |
+
if extracted_regions:
|
29 |
+
state = extracted_regions[0]
|
30 |
+
if extracted_countries:
|
31 |
+
country = extracted_countries[0]
|
32 |
+
|
33 |
+
# location_string = ' '.join(filter(None, [city, state, country]))
|
34 |
+
|
35 |
+
if not location_string:
|
36 |
+
location_string = location
|
37 |
+
else:
|
38 |
+
return ErrorResponse(error="No location information provided")
|
39 |
+
|
40 |
+
geolocator = Nominatim(user_agent="Geolocation")
|
41 |
+
print("Printing location string")
|
42 |
+
print(location_string)
|
43 |
+
if city or state or country :
|
44 |
+
location_string = city
|
45 |
+
elif country is None:
|
46 |
+
location_string = city
|
47 |
+
elif city is None:
|
48 |
+
location_string = state
|
49 |
+
elif state is None:
|
50 |
+
location_string = city
|
51 |
+
|
52 |
+
try:
|
53 |
+
getLoc = geolocator.geocode(location_string)
|
54 |
+
print(getLoc.latitude)
|
55 |
+
print(getLoc.longitude)
|
56 |
+
return Coordinates(
|
57 |
+
latitude=getLoc.latitude,
|
58 |
+
longitude=getLoc.longitude
|
59 |
+
)
|
60 |
+
except Exception as e:
|
61 |
+
print(f"Error {e}")
|
62 |
+
return Coordinates(latitude=None , longitude=None)
|