151 lines
6.4 KiB
Python
151 lines
6.4 KiB
Python
from typing import Optional
|
|
import json
|
|
import logging
|
|
|
|
from .types import Pos, Text, Meme
|
|
from . import utils
|
|
|
|
DATA_FILE = utils.relative_path(__file__, "..", "memes.json")
|
|
|
|
DATA = {}
|
|
ALIASES = {}
|
|
LIST = []
|
|
|
|
logger = logging.getLogger("meme_db")
|
|
|
|
|
|
def load_memes(purge: bool = False):
|
|
global DATA, ALIASES
|
|
if purge:
|
|
DATA.clear()
|
|
ALIASES.clear()
|
|
try:
|
|
with open(DATA_FILE) as input_file:
|
|
content = "".join(input_file.readlines())
|
|
raw_data = json.loads(content)
|
|
if not (isinstance(raw_data, list)):
|
|
raise TypeError(f"Root is not a list")
|
|
for i in range(len(raw_data)):
|
|
load_item(i, raw_data[i])
|
|
except OSError as e:
|
|
logger.error(f"Could not read data file: {e}")
|
|
except json.decoder.JSONDecodeError as e:
|
|
logger.error(f"Wrong JSON syntax '{DATA_FILE}': {e}")
|
|
except TypeError as e:
|
|
logger.error(f"Invalid data file: {e}")
|
|
|
|
|
|
def load_item(i: int, item: dict):
|
|
global LIST
|
|
# TODO reduce complexity
|
|
item_id = ""
|
|
try:
|
|
if not (isinstance(item, dict)):
|
|
raise TypeError(f"root is not a dict")
|
|
item_id = utils.read_key(item, "id", types=[str])
|
|
if len(item_id.strip()) == 0:
|
|
return
|
|
if item_id in DATA:
|
|
raise NameError(f"id '{item_id}' already existing")
|
|
based_on = utils.read_key_safe(item, "based_on", types=[str])
|
|
if based_on is not None:
|
|
if based_on in DATA:
|
|
meme = DATA[based_on].clone()
|
|
meme.id = item_id
|
|
else:
|
|
raise NameError(f"Reference '{based_on}' not found in data, make sur it's placed before this one")
|
|
else:
|
|
meme = Meme(item_id)
|
|
meme.abstract = utils.read_key_safe(item, "abstract", False, types=[bool])
|
|
meme.aliases = utils.read_key_safe(item, "aliases", [], types=[str], is_list=True)
|
|
meme.info = utils.read_key_safe(item, "info", types=[str])
|
|
meme.text_base = load_text(0, item, meme.text_base)
|
|
if not meme.abstract:
|
|
meme.template = utils.read_key(item, "template", meme.template, types=[str])
|
|
raw_texts = utils.read_key(item, "texts", meme.texts, types=[dict], is_list=True)
|
|
if "texts" in item:
|
|
meme.texts = []
|
|
current_text = 1
|
|
for j in range(len(raw_texts)):
|
|
raw_text = raw_texts[j]
|
|
try:
|
|
text = load_text(current_text, raw_text)
|
|
if text.text_ref is None:
|
|
current_text += 1
|
|
elif text.text_ref < 1 or text.text_ref > len(meme.texts):
|
|
logger.warning(
|
|
f"Item '{item_id}'({i + 1}) / Text {j + 1}: invalid text reference {text.text_ref}")
|
|
continue
|
|
else:
|
|
text.text_ref -= 1
|
|
text.text = meme.texts[text.text_ref].text
|
|
if text.style_ref is not None:
|
|
if text.style_ref < 1 or text.style_ref > len(meme.texts):
|
|
logger.warning(
|
|
f"Item '{item_id}'({i + 1}) / Text {j + 1}: invalid style reference {text.style_ref}")
|
|
else:
|
|
text.style_ref -= 1
|
|
text.update(meme.texts[text.style_ref])
|
|
meme.texts += [text]
|
|
meme.texts_len = current_text - 1
|
|
except TypeError as e:
|
|
logger.warning(f"Item '{item_id}'({i + 1}) / Text {j + 1}: {e}")
|
|
for text in meme.texts:
|
|
text.update(meme.text_base)
|
|
else:
|
|
DATA[item_id] = meme
|
|
if not meme.abstract:
|
|
LIST += [item_id]
|
|
ALIASES[item_id] = item_id
|
|
for alias in meme.aliases:
|
|
if alias in ALIASES:
|
|
logger.warning(
|
|
f"Item '{item_id}'({i + 1}): alias '{alias}' already registered by '{ALIASES[alias]}'")
|
|
else:
|
|
ALIASES[alias] = item_id
|
|
logger.info(f"Loaded meme '{item_id}' with {len(meme.texts)} texts")
|
|
except KeyError as e:
|
|
logger.warning(f"Item '{item_id}'({i + 1}): key {e} not found")
|
|
except TypeError as e:
|
|
logger.warning(f"Item '{item_id}'({i + 1}): {e}")
|
|
except NameError as e:
|
|
logger.warning(f"Item '{item_id}'({i + 1}): {e}")
|
|
|
|
|
|
def load_text(current_text: int, raw_text: dict, text: Optional[Text] = None) -> Text:
|
|
if text is None:
|
|
text = Text(f"text {current_text}")
|
|
text.font = utils.read_key_safe(raw_text, "font", text.font, types=[str])
|
|
text.x_range = utils.read_key_safe(raw_text, "x_range", types=[float, int], is_list=True, is_list_size=2)
|
|
text.y_range = utils.read_key_safe(raw_text, "y_range", types=[float, int], is_list=True, is_list_size=2)
|
|
text.text_ref = utils.read_key_safe(raw_text, "text_ref", types=[int])
|
|
text.style_ref = utils.read_key_safe(raw_text, "style_ref", types=[int])
|
|
text.angle = utils.read_key_safe(raw_text, "angle", types=[float, int])
|
|
text.font_size = utils.read_key_safe(raw_text, "font_size", text.font_size, types=[float, int])
|
|
text.fill = utils.read_key_safe(raw_text, "fill", text.fill, types=[int], is_list=True, is_list_size=3)
|
|
text.stroke_width = utils.read_key_safe(raw_text, "stroke_width", text.stroke_width, types=[float, int])
|
|
text.stroke_fill = utils.read_key_safe(raw_text, "stroke_fill", text.stroke_fill, types=[int], is_list=True,
|
|
is_list_size=3)
|
|
if "position" in raw_text:
|
|
if raw_text["position"] not in [p.name for p in Pos]:
|
|
raise TypeError(f"'position' is not a valid position (ex: NW, E, SE, ...)")
|
|
text.position = getattr(Pos, raw_text["position"])
|
|
if "align" in raw_text:
|
|
if raw_text["align"] not in ["left", "center", "right"]:
|
|
raise TypeError(f"'align' is not 'left', 'center' or 'right'")
|
|
text.align = raw_text["align"]
|
|
return text
|
|
|
|
|
|
def get_meme(name: str) -> Optional[Meme]:
|
|
name = name.lower().strip().replace(" ", "_")
|
|
if name in ALIASES:
|
|
return DATA[ALIASES[name]].clone()
|
|
else:
|
|
return None
|
|
|
|
|
|
def find_nearest(word: str) -> str:
|
|
word = word.lower().strip().replace(" ", "_")
|
|
return utils.find_nearest(word, list(ALIASES))
|