mirror of
https://github.com/Xevion/the-office.git
synced 2025-12-11 10:08:57 -06:00
145 lines
4.6 KiB
Python
145 lines
4.6 KiB
Python
"""
|
|
helpers.py
|
|
|
|
|
|
"""
|
|
import random
|
|
import re
|
|
import string
|
|
import unicodedata
|
|
from collections import OrderedDict
|
|
from difflib import SequenceMatcher
|
|
from heapq import nlargest as _nlargest
|
|
from typing import List, Optional, Tuple
|
|
|
|
import unidecode
|
|
|
|
episode_counts = [6, 22, 23, 14, 26, 24, 24, 24, 23]
|
|
|
|
|
|
def check_validity(season: int, episode: int):
|
|
"""Shorthand function for checking if a specific episode is valid."""
|
|
return (1 <= season <= 9) and (1 <= episode <= episode_counts[season])
|
|
|
|
|
|
def default(value, other):
|
|
"""Value default, similar to dict.get, but better."""
|
|
return value if value is not None else other
|
|
|
|
|
|
def get_neighbors(array: List, index: int, distance: int = 2) -> Tuple[List, List]:
|
|
"""Get neighbors above and below a specific index in an array. Returns maximum number of items possible."""
|
|
top, below = [], []
|
|
for i in range(1, distance + 1):
|
|
top_index = index - i
|
|
below_index = index + i
|
|
if top_index >= 0:
|
|
top.append(array[top_index])
|
|
if below_index < len(array):
|
|
below.append(array[below_index])
|
|
return top[::-1], below
|
|
|
|
|
|
def algolia_transform(old_dictionary: dict, key_list: List[Tuple[str, Optional[str]]]) -> dict:
|
|
"""
|
|
Transforms a dictionary object of a quote (from algolia.json) into a API-ready quote.
|
|
Used for cli.character (i.e. characters.json)
|
|
:param old_dictionary: The original Algolia dictionary
|
|
:param key_list: A list of keys to keep in the dictionary in a tuple. One item tuple to keep the tuple's name, a
|
|
second item requests a 'rename' for the quote.
|
|
:return: The reformatted dictionary.
|
|
"""
|
|
|
|
new_dictionary = {}
|
|
for keyItem in key_list:
|
|
if len(keyItem) > 1:
|
|
new_dictionary[keyItem[1]] = old_dictionary[keyItem[0]]
|
|
else:
|
|
new_dictionary[keyItem[0]] = old_dictionary[keyItem[0]]
|
|
|
|
return new_dictionary
|
|
|
|
|
|
def is_main_character(name: str) -> bool:
|
|
return None
|
|
|
|
|
|
def character_id(name: str) -> str:
|
|
return '-'.join(name.split(' ')).lower()
|
|
|
|
|
|
alphabet: str = string.ascii_letters + string.digits
|
|
|
|
|
|
def random_id(length: int = 8) -> str:
|
|
"""Generate a random {length} character long string."""
|
|
return ''.join(random.choices(alphabet, k=length))
|
|
|
|
|
|
def char_filter(string):
|
|
latin = re.compile('[a-zA-Z]+')
|
|
for char in unicodedata.normalize('NFC', string):
|
|
decoded = unidecode.unidecode(char)
|
|
if latin.match(decoded):
|
|
yield char
|
|
else:
|
|
yield decoded
|
|
|
|
|
|
def clean_string(string):
|
|
return "".join(char_filter(string))
|
|
|
|
|
|
def get_close_matches_indexes(word, possibilities, n=3, cutoff=0.6):
|
|
"""Use SequenceMatcher to return a list of the indexes of the best
|
|
"good enough" matches. word is a sequence for which close matches
|
|
are desired (typically a string).
|
|
possibilities is a list of sequences against which to match word
|
|
(typically a list of strings).
|
|
Optional arg n (default 3) is the maximum number of close matches to
|
|
return. n must be > 0.
|
|
Optional arg cutoff (default 0.6) is a float in [0, 1]. Possibilities
|
|
that don't score at least that similar to word are ignored.
|
|
"""
|
|
|
|
if not n > 0:
|
|
raise ValueError("n must be > 0: %r" % (n,))
|
|
if not 0.0 <= cutoff <= 1.0:
|
|
raise ValueError("cutoff must be in [0.0, 1.0]: %r" % (cutoff,))
|
|
result = []
|
|
s = SequenceMatcher()
|
|
s.set_seq2(word)
|
|
for idx, x in enumerate(possibilities):
|
|
s.set_seq1(x)
|
|
if s.real_quick_ratio() >= cutoff and \
|
|
s.quick_ratio() >= cutoff and \
|
|
s.ratio() >= cutoff:
|
|
result.append((s.ratio(), idx))
|
|
|
|
# Move the best scorers to head of list
|
|
result = _nlargest(n, result)
|
|
|
|
# Strip scores for the best n matches
|
|
return [x for score, x in result]
|
|
|
|
|
|
def marked_item_merge(keys: List[str], values: List[int]) -> Tuple[List[str], List[str]]:
|
|
"""Add the values of identical keys together, then return both the keys and values"""
|
|
merge = OrderedDict()
|
|
for key, value in zip(keys, values):
|
|
# Already inserted, now make/keep it negative
|
|
if key in merge.keys():
|
|
# Keys that haven't been turned over need to be made negative
|
|
if merge[key] > 0:
|
|
merge[key] = -merge[key]
|
|
|
|
# And then subtract the value in all cases
|
|
merge[key] -= value
|
|
else:
|
|
# Values that are positive didn't merge with other counts.
|
|
merge[key] = value
|
|
|
|
keys, values = zip(*merge.items())
|
|
values = [f'{-value}*' if value < 0 else str(value) for value in values]
|
|
return keys, values
|