initial upload
This commit is contained in:
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
20
app/dependencies.py
Normal file
20
app/dependencies.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import json
|
||||
|
||||
# Helper functions
|
||||
def load_animeguess_anime_list():
|
||||
"""
|
||||
Load in the parsed anime list
|
||||
"""
|
||||
global animeguess_anime_list
|
||||
f = open('app/parsed-anime-list-mini.json')
|
||||
animeguess_anime_list = json.load(f)
|
||||
|
||||
def return_animeguess_anime_list():
|
||||
"""
|
||||
Return the parsed anime list
|
||||
|
||||
Returns:
|
||||
JSON:
|
||||
parsed anime list
|
||||
"""
|
||||
return animeguess_anime_list
|
28
app/main.py
Normal file
28
app/main.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from .dependencies import load_animeguess_anime_list
|
||||
|
||||
from .routers import animeguess
|
||||
|
||||
# Load anime list
|
||||
load_animeguess_anime_list()
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# Middleware CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"]
|
||||
)
|
||||
|
||||
# Routes
|
||||
app.include_router(animeguess.router)
|
||||
|
||||
# Health check
|
||||
@app.get("/ping")
|
||||
async def ping():
|
||||
return {"Ping":"Pong"}
|
1
app/parsed-anime-list-mini.json
Normal file
1
app/parsed-anime-list-mini.json
Normal file
File diff suppressed because one or more lines are too long
0
app/routers/__init__.py
Normal file
0
app/routers/__init__.py
Normal file
145
app/routers/animeguess.py
Normal file
145
app/routers/animeguess.py
Normal file
@@ -0,0 +1,145 @@
|
||||
import os
|
||||
import requests
|
||||
|
||||
from ..dependencies import return_animeguess_anime_list
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
|
||||
from rapidfuzz import fuzz
|
||||
from operator import itemgetter
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Helper functions
|
||||
def return_bad():
|
||||
"""
|
||||
Returns HTTP 400 and message for a bad request
|
||||
|
||||
Returns:
|
||||
JSON:
|
||||
statusCode: 400
|
||||
headers: Content-Type text/plain
|
||||
body: 400: Bad request
|
||||
"""
|
||||
return {
|
||||
'statusCode': 400,
|
||||
'headers': {
|
||||
'Content-Type': 'text/plain'
|
||||
},
|
||||
'body': '400: Bad Request'
|
||||
}
|
||||
|
||||
# Models
|
||||
class Metadata(BaseModel):
|
||||
id: str
|
||||
|
||||
class Search(BaseModel):
|
||||
query: str
|
||||
|
||||
# Routes
|
||||
@router.post("/ag-metadata")
|
||||
async def ag_metadata(metadata: Metadata):
|
||||
"""
|
||||
Gets the metadata from MyAnimeList based on the inputted name
|
||||
|
||||
Requires:
|
||||
POST:
|
||||
'id': MyAnimeList ID of the anime
|
||||
|
||||
Returns:
|
||||
JSON:
|
||||
metadata from MyAnimeList
|
||||
"""
|
||||
|
||||
if not metadata.id.isdigit():
|
||||
return return_bad()
|
||||
|
||||
# MAL API key
|
||||
CLIENT_ID = os.getenv('MAL_CLIENT_ID')
|
||||
|
||||
# Get data from MAL
|
||||
url = f'https://api.myanimelist.net/v2/anime/{str(metadata.id)}?fields=title,studios,genres,mean,start_season'
|
||||
resp = requests.get(url, headers={
|
||||
'X-MAL-CLIENT-ID': CLIENT_ID
|
||||
})
|
||||
|
||||
return resp.json()
|
||||
|
||||
@router.post("/ag-search")
|
||||
async def ag_search(search: Search):
|
||||
"""
|
||||
Perform a fuzzy search for an anime title
|
||||
|
||||
Requires:
|
||||
POST - 'query': search query
|
||||
|
||||
Returns:
|
||||
JSON - possible anime titles
|
||||
"""
|
||||
|
||||
# Ensure search is at least 3 characters
|
||||
if len(search.query) < 3:
|
||||
return return_bad()
|
||||
search_item = search.query
|
||||
|
||||
# Keep track of titles, ids, and scores that are possible
|
||||
data = return_animeguess_anime_list()
|
||||
anime_score_list = []
|
||||
|
||||
# Cycle through parsed list
|
||||
for item in data:
|
||||
|
||||
# Perform fuzzy search against titles
|
||||
fuzzy_search = fuzz.ratio(search_item.lower(), item['title'].lower())
|
||||
|
||||
# Check if search item is in the title
|
||||
in_title = search_item.lower() in item['title'].lower()
|
||||
|
||||
# If the query is in the title or has a high fuzzy search score
|
||||
if in_title or fuzzy_search > 65:
|
||||
|
||||
# If the query is in the title, give it a higher score
|
||||
if in_title:
|
||||
# If the query is the same as the title, give it the highest score
|
||||
if search_item.lower() == item['title'].lower():
|
||||
anime_score_list.append({'title': item['title'], 'score': 200, 'mal_id': item['mal_id']})
|
||||
else:
|
||||
anime_score_list.append({'title': item['title'], 'score': 100, 'mal_id': item['mal_id']})
|
||||
# Else, set it to the fuzzy search score
|
||||
else:
|
||||
anime_score_list.append({'title': item['title'], 'score': fuzzy_search, 'mal_id': item['mal_id']})
|
||||
|
||||
# Check synonyms if it's not in the title or a fuzzy search isn't matched
|
||||
else:
|
||||
for synonym in item['synonyms']:
|
||||
|
||||
# Perform fuzzy search against each synonym
|
||||
fuzzy_search = fuzz.ratio(search_item.lower(), synonym.lower())
|
||||
|
||||
# Check if the search item is in the synonym
|
||||
in_synonym = search_item.lower() in synonym.lower()
|
||||
|
||||
# If the query is in the synonym or has a high fuzzy search score
|
||||
if in_synonym or fuzzy_search > 65:
|
||||
|
||||
# If the query is in the synonym give it a higher score
|
||||
if in_synonym:
|
||||
# If the query is the same as the synonym, give it the highest score
|
||||
if search_item.lower() == synonym.lower():
|
||||
anime_score_list.append({'title': f'{item["title"]} [{synonym}]', 'score': 200, 'mal_id': item['mal_id']})
|
||||
else:
|
||||
anime_score_list.append({'title': f'{item["title"]} [{synonym}]', 'score': 100, 'mal_id': item['mal_id']})
|
||||
break
|
||||
# Else, give it the fuzzy search score
|
||||
else:
|
||||
anime_score_list.append({'title': f'{item["title"]} [{synonym}]', 'score': fuzzy_search, 'mal_id': item['mal_id']})
|
||||
break
|
||||
|
||||
# Sort possible anime titles by score
|
||||
anime_score_list = sorted(anime_score_list, key=itemgetter('score'), reverse=True)
|
||||
|
||||
# Remove score key since it is un-needed after sort
|
||||
for anime in anime_score_list:
|
||||
anime = anime.pop('score')
|
||||
|
||||
return anime_score_list
|
Reference in New Issue
Block a user