""" Search Utilities This module contains search and fuzzy matching functionality. """ import discord from difflib import get_close_matches from typing import Optional def fuzzy_search(name, master_list): """ Perform fuzzy string matching against a list of options. Args: name: String to search for master_list: List of strings to search against Returns: Best match string or raises ValueError if no good matches """ if name.lower() in master_list: return name.lower() great_matches = get_close_matches(name, master_list, cutoff=0.8) if len(great_matches) == 1: return great_matches[0] elif len(great_matches) > 0: matches = great_matches else: matches = get_close_matches(name, master_list, n=6) if len(matches) == 1: return matches[0] if not matches: raise ValueError(f'{name.title()} was not found') return matches[0] async def fuzzy_player_search(ctx, channel, bot, name, master_list): """ Interactive fuzzy player search with Discord UI. Takes a name to search and returns the name of the best match. Args: ctx: discord context channel: discord channel bot: discord.py bot object name: string to search for master_list: list of names to search against Returns: Selected match or None if cancelled """ # Import here to avoid circular imports from discord_ui.confirmations import Question matches = fuzzy_search(name, master_list) embed = discord.Embed( title="Did You Mean...", description='Enter the number of the card you would like to see.', color=0x7FC600 ) count = 1 for x in matches: embed.add_field(name=f'{count}', value=x, inline=False) count += 1 embed.set_footer(text='These are the closest matches. Spell better if they\'re not who you want.') this_q = Question(bot, channel, None, 'int', 45, embed=embed) resp = await this_q.ask([ctx.author]) if not resp: return None if resp < count: return matches[resp - 1] else: raise ValueError(f'{resp} is not a valid response.') async def cardset_search(cardset: str, cardset_list: list) -> Optional[dict]: """ Search for a cardset by name and return the cardset data. Args: cardset: Cardset name to search for cardset_list: List of available cardset names Returns: Cardset dictionary or None if not found """ # Import here to avoid circular imports from api_calls import db_get cardset_name = fuzzy_search(cardset, cardset_list) if not cardset_name: return None c_query = await db_get('cardsets', params=[('name', cardset_name)]) if c_query['count'] == 0: return None return c_query['cardsets'][0]