initial upload
This commit is contained in:
commit
c993ea8b6a
13 files changed
+881
No files matched your search
Whitespace-only changes.
@@ -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
@@ -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"}
|
||||
@@ -0,0 +1 @@
|
||||
[{"index":0,"title":"Fullmetal Alchemist: Brotherhood","synonyms":["FMA","FMA (2009)","FMA 2","FMA Brotherhood","FMA2","FMA: B","FMA:B","FMAB","Full Metal Alchemist 2","Full Metal Alchemist 2009","Full Metal Alchemist: Brotherhood","Full Metal Alchemist: Fraternitate","Fullmetal Alchemist (2009)","Fullmetal Alchemist 2","Fullmetal Alchemist Brotherhood","HagaRen (2009)","Hagane no Renkinjutsushi (2009)","Hagane no Renkinjutsushi 2","Hagane no Renkinjutsushi Fullmetal Alchemist (2009)","Hagane no Renkinjutsushi: FULLMETAL ALCHEMIST","Hagane no Renkinjutsushi: Full Metal Alchemist","Hagane no Renkinjutsushi: Fullmetal Alchemist","Hagaren","Metalinis Alchemikas 2","Stalowy Alchemik","Stalowy Alchemik: Misja Braci","hagaren2"],"mal_id":5114},{"index":1,"title":"Steins;Gate","synonyms":["S;G","SG","STEINS;GATE","Steins Gate","s;g","shutage"],"mal_id":9253},{"index":2,"title":"Gintama","synonyms":["Gin Tama","Gintama: Season 1","Gintama: Series 1","Silver Soul","Yorinuki Gintama-san","gintama tv"],"mal_id":918},{"index":3,"title":"Monster","synonyms":["Canavar","MONSTER","Monster TV","Monstras","Monstre","Monstro","Monstrul","Naoki Urasawa`s MONSTER","O Monstro"],"mal_id":19},{"index":4,"title":"\"Oshi no Ko\"","synonyms":["Anak Idola","Favorite Girl","Mein*Star","My Idol's Child","My Star","Oshi No Ko","Oshi no Ko","Oshi no Ko: Mein Star","[Mein*Star]","[Oshi No Ko]","[Oshi no Ko]","[Oshi no Ko] Mother and Children"],"mal_id":52034},{"index":5,"title":"Bocchi the Rock!","synonyms":["BOCCHI THE ROCK!","Bozaro","BtR"],"mal_id":47917},{"index":6,"title":"Hajime no Ippo","synonyms":["Fighting Spirit","Fighting Spirit TV","Hajime no Ippo: THE FIGHTING!","Hajime no Ippo: The Fighting","Hajime no Ippo: The Fighting!","HnI","IPPO Le Challenger","Ippo le Challenger","Ippo le challenger","The First Step","ippo"],"mal_id":263},{"index":7,"title":"Cowboy Bebop","synonyms":["Bebop","Bebop TV","CB","CBB","COWBOY BEBOP","CowBe","Kaubojus Bebopas","Kaubojus Bibopas","Kovboj Bebop","Kovboy Bebop"],"mal_id":1},{"index":8,"title":"Vinland Saga","synonyms":["VINLAND SAGA","vinsa"],"mal_id":37521},{"index":9,"title":"Ashita no Joe 2","synonyms":["Champion Joe 2","Joe 2","N\/A","Rocky Joe 2","Tomorrow's Joe 2","Tomorrow`s Joe 2","anj s2","rocky joe 2"],"mal_id":2921},{"index":10,"title":"Code Geass: Hangyaku no Lelouch","synonyms":["Code Geass","Code Geass 1","Code Geass 1st Season","Code Geass : Lelouch of the Rebellion","Code Geass R1","Code Geass: Lelouch of the Rebellion","CodeGeass","cg","cg r1","cg1","cghnl","code geass","geass","geass1"],"mal_id":1575},{"index":11,"title":"One Piece","synonyms":["1P","All'arrembaggio!","All`arrembaggio!","Budak Getah","ONE PIECE","OP","OP TV","One Piece TV","OneP","Tutti all'arrembaggio!","optv"],"mal_id":21},{"index":12,"title":"Great Teacher Onizuka","synonyms":["Didysis mokytojas Onizuka","G.T.O. - Great Teacher Onizuka","GTO","GTO - Great Teacher Onizuka","GTO - The Animation","GTO: Great Teacher Onizuka","GTO: The Animation","Nuostabusis Mokytojas Onizuka"],"mal_id":245},{"index":13,"title":"Odd Taxi","synonyms":["ODD TAXI","ODDTAXI","Oddtaxi"],"mal_id":46102},{"index":14,"title":"Violet Evergarden","synonyms":["violet"],"mal_id":33352},{"index":15,"title":"Made in Abyss","synonyms":["MADE IN ABYSS","Made In Abyss","MiA","mia"],"mal_id":34599},{"index":16,"title":"Mushishi","synonyms":["Bug Master","Bugmaster","MUSHI-SHI","Mushi","Mushi-Shi","Mushi-Shi : The Cure Lies in the Curse"],"mal_id":457},{"index":17,"title":"Shigatsu wa Kimi no Uso","synonyms":["April Is Your Lie","Bugie d'aprile","Bugie d`aprile","KimiUso","Kimiuso","Sekunden in Moll","Shigatsu Wa Kimi No Uso - Sekunden in Moll","Shigatsu wa Kimi no Uso: Sekunden in Moll","YLIA","Your Lie in April","Your lie in April","kimiuso"],"mal_id":23273},{"index":18,"title":"Jujutsu Kaisen","synonyms":["JJK","JUJUTSU KAISEN","Jujutsu Kaisen (TV)","Sorcery Fight"],"mal_id":40748},{"index":19,"title":"Tengen Toppa Gurren Lagann","synonyms":["Gurren","Gurren Lagann","Gurren Laggan","Gurren-Lagann","Heaven-Piercing Gurren Lagann","Heavenly Breakthrough Gurren Lagann","Lagann","Maiking Break-Through Gurren-Lagann","Making Break-Through Gurren Lagann","Sfondamento dei Cieli Gurren Lagann","Sfondamento dei cieli Gurren Lagann","TTGL","Tengen Toppa Gurren-Lagann","ttgl","ttgl tv"],"mal_id":2001},{"index":20,"title":"Death Note","synonyms":["Caderno da Morte","Carnet de la Mort","DEATH NOTE","DN","Quaderno della Morte","Sveska Smrti"],"mal_id":1535},{"index":21,"title":"Ping Pong the Animation","synonyms":["PPTA","Ping Pong","Ping Pong THE ANIMATION","Ping Pong The Animation","pingpong"],"mal_id":22135},{"index":22,"title":"Spy x Family","synonyms":["SPY x FAMILY","Spy Family","SxF","sxf"],"mal_id":50265},{"index":23,"title":"Hajime no Ippo: Rising","synonyms":["Fighting Spirit: Rising","Hajime No Ippo: The Fighting!","Hajime No Ippo: The Fighting! - Rising","Hajime No Ippo: The Fighting! - Rising -","Hajime no Ippo 3","Hajime no Ippo : Rising","Line truncated
|
||||
Whitespace-only changes.
@@ -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