Meier Lukas d1e008be5f
feat: add issue_templates for creation of icons (#935)
Co-authored-by: Dashboard Icons Bot <homarr-labs@proton.me>
2025-02-15 15:59:12 +01:00

223 lines
7.8 KiB
Python

import re
from common import convert_to_kebab_case
from datetime import datetime
import json
from enum import Enum
from metadata import load_metadata
class IconConvertion:
def __init__(self, name: str, source: str):
self.name = name
self.source = source
class Icon:
def __init__(self, name: str, type: str, categories: list, aliases: list):
self.name = name
self.type = type
self.categories = categories
self.aliases = aliases
def to_metadata(self, author: dict) -> dict:
return {
"base": self.type,
"aliases": self.aliases,
"categories": self.categories,
"update": {
"timestamp": datetime.now().isoformat(),
"author": author
}
}
def convertions(self) -> list[IconConvertion]:
raise NotImplementedError("Method 'files' must be implemented in subclass")
class NormalIcon(Icon):
def __init__(self, icon: str, name: str, type: str, categories: list, aliases: list):
super().__init__(name, type, categories, aliases)
self.icon = icon
def convertions(self) -> list[IconConvertion]:
return [
IconConvertion(self.name, self.icon)
]
def from_addition_issue_form(input: dict):
return NormalIcon(
mapUrlFromMarkdownImage(input, "Paste icon"),
convert_to_kebab_case(mapFromRequired(input, "Icon name")),
mapFileTypeFrom(input, "Icon type"),
mapListFrom(input, "Categories"),
mapListFrom(input, "Aliases")
)
def from_update_issue_form(input: dict):
try:
name = convert_to_kebab_case(mapFromRequired(input, "Icon name"))
metadata = load_metadata(name)
return NormalIcon(
mapUrlFromMarkdownImage(input, "Paste icon"),
mapFromRequired(input, "Icon name"),
mapFileTypeFrom(input, "Icon type"),
metadata["categories"],
metadata["aliases"]
)
except Exception as exeption:
raise ValueError(f"Icon '{name}' does not exist", exeption)
def from_metadata_update_issue_form(input: dict):
name = convert_to_kebab_case(mapFromRequired(input, "Icon name"))
metadata = load_metadata(name)
return NormalIcon(
None,
name,
metadata["base"],
mapListFrom(input, "Categories"),
mapListFrom(input, "Aliases")
)
class MonochromeIcon(Icon):
def __init__(self, lightIcon: str, darkIcon: str, name: str, type: str, categories: list, aliases: list):
super().__init__(name, type, categories, aliases)
self.lightIcon = lightIcon
self.darkIcon = darkIcon
def to_colors(self) -> dict:
try:
metadata = load_metadata(self.name)
return {
"light": f"{metadata['colors']['light']}",
"dark": f"{metadata['colors']['dark']}"
}
except:
return {
"light": f"{self.name}",
"dark": f"{self.name}-dark"
}
def to_metadata(self, author: dict) -> dict:
metadata = super().to_metadata(author)
metadata["colors"] = self.to_colors()
return metadata
def convertions(self) -> list[IconConvertion]:
colorNames = self.to_colors()
return [
IconConvertion(colorNames["light"], self.lightIcon),
IconConvertion(colorNames["dark"], self.darkIcon),
]
def from_addition_issue_form(input: dict):
return MonochromeIcon(
mapUrlFromMarkdownImage(input, "Paste light mode icon"),
mapUrlFromMarkdownImage(input, "Paste dark mode icon"),
convert_to_kebab_case(mapFromRequired(input, "Icon name")),
mapFileTypeFrom(input, "Icon type"),
mapListFrom(input, "Categories"),
mapListFrom(input, "Aliases")
)
def from_update_issue_form(input: dict):
try:
name = convert_to_kebab_case(mapFromRequired(input, "Icon name"))
metadata = load_metadata(name)
return MonochromeIcon(
mapUrlFromMarkdownImage(input, "Paste light mode icon"),
mapUrlFromMarkdownImage(input, "Paste dark mode icon"),
mapFromRequired(input, "Icon name"),
mapFileTypeFrom(input, "Icon type"),
metadata["categories"],
metadata["aliases"]
)
except Exception as exeption:
raise ValueError(f"Icon '{name}' does not exist", exeption)
def from_metadata_update_issue_form(input: dict):
name = convert_to_kebab_case(mapFromRequired(input, "Icon name"))
metadata = load_metadata(name)
return MonochromeIcon(
None,
None,
name,
metadata["base"],
mapListFrom(input, "Categories"),
mapListFrom(input, "Aliases")
)
def checkType(type: str):
if type not in ["normal", "monochrome"]:
raise ValueError(f"Invalid icon type: '{type}'")
return type
def checkAction(action: str):
if action == "addition":
return IssueFormType.ADDITION
elif action == "update":
return IssueFormType.UPDATE
elif action == "metadata_update":
return IssueFormType.METADATA_UPDATE
raise ValueError(f"Invalid action: '{action}'")
class IssueFormType(Enum):
ADDITION = "addition"
UPDATE = "update"
METADATA_UPDATE = "metadata_update"
def iconFactory(type: str, issue_form: str, issue_form_type: IssueFormType):
if type == "normal":
if (issue_form_type == IssueFormType.ADDITION):
return NormalIcon.from_addition_issue_form(json.loads(issue_form))
elif (issue_form_type == IssueFormType.UPDATE):
return NormalIcon.from_update_issue_form(json.loads(issue_form))
elif (issue_form_type == IssueFormType.METADATA_UPDATE):
return NormalIcon.from_metadata_update_issue_form(json.loads(issue_form))
else:
raise ValueError(f"Invalid issue form type: '{issue_form_type}'")
elif type == "monochrome":
if (issue_form_type == IssueFormType.ADDITION):
return MonochromeIcon.from_addition_issue_form(json.loads(issue_form))
elif (issue_form_type == IssueFormType.UPDATE):
return MonochromeIcon.from_update_issue_form(json.loads(issue_form))
elif (issue_form_type == IssueFormType.METADATA_UPDATE):
return MonochromeIcon.from_metadata_update_issue_form(json.loads(issue_form))
else:
raise ValueError(f"Invalid issue form type: '{issue_form_type}'")
raise ValueError(f"Invalid icon type: '{type}'")
def mapFrom(input: dict, label: str) -> str:
return input.get(label, None)
def mapFromRequired(input: dict, label: str) -> str:
value = mapFrom(input, label)
if value is None:
raise ValueError(f"Missing required field: '{label}'")
return value
def mapFileTypeFrom(input: dict, label: str) -> str:
fileType = mapFromRequired(input, label)
if fileType not in ["SVG", "PNG"]:
raise ValueError(f"Invalid file type: '{fileType}'")
return fileType.lower()
def mapListFrom(input: dict, label: str) -> list:
stringList = mapFrom(input, label)
if stringList is None:
return []
return list(map(str.strip, stringList.split(",")))
def mapUrlFromMarkdownImage(input: dict, label: str) -> re.Match[str]:
markdown = mapFromRequired(input, label)
try:
return re.match(r"!\[[^\]]+\]\((https:[^\)]+)\)", markdown)[1]
except IndexError:
raise ValueError(f"Invalid markdown image: '{markdown}'")