178 lines
6.3 KiB
Python
178 lines
6.3 KiB
Python
import json
|
|
import logging
|
|
import os.path as path
|
|
|
|
from .types import Pos, Text, Meme
|
|
from . import utils
|
|
|
|
DATA_FILE = utils.relative_path(__file__, "..", "memes.json")
|
|
|
|
DATA = {}
|
|
ALIASES = {}
|
|
|
|
logger = logging.getLogger("meme_db")
|
|
|
|
|
|
def load_memes():
|
|
"""
|
|
TODO
|
|
"""
|
|
try:
|
|
with open(DATA_FILE) as f:
|
|
content = "".join(f.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, item):
|
|
"""
|
|
TODO
|
|
|
|
:param (int) i:
|
|
:param (dict) item:
|
|
"""
|
|
item_id = ""
|
|
try:
|
|
if not (isinstance(item, dict)):
|
|
raise TypeError(f"root is not a dict")
|
|
item_id = utils.read_key(item, "id")
|
|
if item_id in DATA:
|
|
raise NameError(f"id '{item_id}' already existing")
|
|
based_on = utils.read_key_safe(item, "based_on")
|
|
abstract = utils.read_key_safe(item, "abstract", False)
|
|
aliases = utils.read_key_safe(item, "aliases", [])
|
|
if not utils.is_list_of(aliases, [str]):
|
|
raise TypeError(f"'aliases' is not a list of str")
|
|
template = None
|
|
font = None
|
|
font_size = None
|
|
texts = None
|
|
if based_on is not None:
|
|
if based_on in DATA:
|
|
template = DATA[based_on].template
|
|
font = DATA[based_on].font
|
|
font_size = DATA[based_on].font_size
|
|
texts = DATA[based_on].clone_texts()
|
|
else:
|
|
raise NameError(f"Reference '{based_on}' not found in data, make sur it's placed before this one")
|
|
if not abstract:
|
|
template = utils.read_key(item, "template", template)
|
|
font = utils.read_key_safe(item, "font", font)
|
|
font_size = utils.read_key_safe(item, "font_size", font_size)
|
|
raw_texts = utils.read_key(item, "texts", texts)
|
|
if texts is None:
|
|
if not (isinstance(raw_texts, list)):
|
|
raise TypeError(f"'texts' is not a list")
|
|
texts = []
|
|
for j in range(len(raw_texts)):
|
|
raw_text = raw_texts[j]
|
|
try:
|
|
texts += [load_text(j, raw_text)]
|
|
except TypeError as e:
|
|
logger.warning(f"Item '{item_id}'({i}) / Text {j}: {e}")
|
|
if font is not None:
|
|
if not (isinstance(font, str)):
|
|
raise TypeError(f"'font' is not a str")
|
|
for text in texts:
|
|
if text.font is None:
|
|
text.font = font
|
|
if font_size is not None:
|
|
if not (isinstance(font_size, float)):
|
|
raise TypeError(f"'font_size' is not a float")
|
|
for text in texts:
|
|
if text.font_size is None:
|
|
text.font_size = font_size
|
|
if len(texts) == 0:
|
|
logger.warning(f"Item '{item_id}'({i}): no texts loaded")
|
|
else:
|
|
DATA[item_id] = Meme(item_id, aliases, abstract, template, font, font_size, texts)
|
|
for alias in aliases:
|
|
if alias in ALIASES:
|
|
logger.warning(f"Item '{item_id}'({i}): alias '{alias}' already registered by '{ALIASES[alias]}'")
|
|
else:
|
|
ALIASES[alias] = item_id
|
|
logger.info(f"Loaded meme '{item_id}' with {len(texts)} texts")
|
|
except KeyError as e:
|
|
logger.warning(f"Item '{item_id}'({i}): key {e} not found")
|
|
except TypeError as e:
|
|
logger.warning(f"Item '{item_id}'({i}): {e}")
|
|
except NameError as e:
|
|
logger.warning(f"Item '{item_id}'({i}): {e}")
|
|
|
|
|
|
def load_text(j, raw_text):
|
|
"""
|
|
TODO
|
|
|
|
:param (int) j:
|
|
:param (dict) raw_text:
|
|
:raises TypeError:
|
|
:rtype: Text
|
|
:return:
|
|
"""
|
|
if not (isinstance(raw_text, dict)):
|
|
raise TypeError(f"root is not a dict")
|
|
text = Text(f"text {j + 1}")
|
|
if "font" in raw_text:
|
|
if not (isinstance(raw_text["font"], str)):
|
|
raise TypeError(f"'font' is not a str")
|
|
text.font = raw_text["font"]
|
|
if "x_range" in raw_text:
|
|
if not (utils.is_list_of(raw_text["x_range"], [int, float], 2)):
|
|
raise TypeError(f"'x_range' is not a list of 2 float")
|
|
text.x_range = raw_text["x_range"]
|
|
if "y_range" in raw_text:
|
|
if not (utils.is_list_of(raw_text["y_range"], [int, float], 2)):
|
|
raise TypeError(f"'y_range' is not a list of 2 float")
|
|
text.y_range = raw_text["y_range"]
|
|
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 = [p for p in Pos if p.name == raw_text["position"]][0]
|
|
if "font_size" in raw_text:
|
|
if not (isinstance(raw_text["font_size"], float)):
|
|
raise TypeError(f"'font_size' is not a float")
|
|
text.font_size = raw_text["font_size"]
|
|
if "fill" in raw_text:
|
|
if not (utils.is_list_of(raw_text["fill"], [int], 3)):
|
|
raise TypeError(f"'fill' is not a list of 3 int")
|
|
text.fill = tuple(raw_text["fill"])
|
|
if "stroke_width" in raw_text:
|
|
if not (isinstance(raw_text["stroke_width"], float)):
|
|
raise TypeError(f"'stroke_width' is not a float")
|
|
text.stroke_width = raw_text["stroke_width"]
|
|
if "stroke_fill" in raw_text:
|
|
if not (utils.is_list_of(raw_text["stroke_fill"], [int], 3)):
|
|
raise TypeError(f"'stroke_fill' is not a list of 3 int")
|
|
text.stroke_fill = tuple(raw_text["stroke_fill"])
|
|
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):
|
|
"""
|
|
TODO
|
|
|
|
:param (str) name:
|
|
:rtype: Meme|None
|
|
:return:
|
|
"""
|
|
if name in DATA and not DATA[name].abstract:
|
|
return DATA[name].clone()
|
|
elif name in ALIASES:
|
|
return DATA[ALIASES[name]].clone()
|
|
else:
|
|
return None
|