Files

148 lines
4.9 KiB
Python

from dataclasses import dataclass, field
SEPARATOR = "|"
FIELD_HINTS = {
"lines": "one per line",
"tags": "comma separated",
"images": "one image path per line",
"link_map": "one per line: name url",
"link_list": "one per line: label | url",
"awards": "one per line: track | place",
"socials": "one per line: name | icon | url",
"skill_map": "one per line: Category: skill, skill",
}
MULTILINE_KINDS = {"longtext", "lines", "images", "link_map", "link_list", "awards", "socials", "skill_map"}
@dataclass
class FieldSpec:
path: str
label: str
kind: str = "text"
required: bool = False
options: list[str] = field(default_factory=list)
@property
def hint(self) -> str:
return FIELD_HINTS.get(self.kind, "")
def get_path(record: dict, path: str):
value = record
for part in path.split("."):
if not isinstance(value, dict):
return None
value = value.get(part)
return value
def is_empty(value) -> bool:
return value is None or value is False or value == "" or value == [] or value == {}
def set_path(record: dict, path: str, value) -> None:
parts = path.split(".")
parents = [record]
for part in parts[:-1]:
child = parents[-1].get(part)
if not isinstance(child, dict):
child = {}
parents[-1][part] = child
parents.append(child)
if is_empty(value):
parents[-1].pop(parts[-1], None)
else:
parents[-1][parts[-1]] = value
for depth in range(len(parts) - 1, 0, -1):
if not parents[depth]:
parents[depth - 1].pop(parts[depth - 1], None)
def split_parts(line: str, count: int) -> list[str]:
parts = [part.strip() for part in line.split(SEPARATOR, count - 1)]
return parts + [""] * (count - len(parts))
def non_empty_lines(text: str) -> list[str]:
return [line.strip() for line in text.splitlines() if line.strip()]
def to_form_text(kind: str, value) -> str:
if value is None:
return ""
if kind in ("lines", "images"):
return "\n".join(value)
if kind == "tags":
return ", ".join(value)
if kind == "link_map":
return "\n".join(f"{name} {url}" for name, url in value.items())
if kind == "link_list":
return "\n".join(f"{link.get('label', '')} {SEPARATOR} {link.get('url', '')}" for link in value)
if kind == "awards":
return "\n".join(f"{award.get('track', '')} {SEPARATOR} {award.get('place', '')}" for award in value)
if kind == "socials":
return "\n".join(f"{social.get('name', '')} {SEPARATOR} {social.get('icon', '')} {SEPARATOR} {social.get('url', '')}" for social in value)
if kind == "skill_map":
return "\n".join(f"{category}: {', '.join(skills)}" for category, skills in value.items())
return str(value)
def from_form_text(kind: str, text: str):
text = text.strip()
if kind in ("lines", "images"):
return non_empty_lines(text)
if kind == "tags":
return [tag.strip() for tag in text.split(",") if tag.strip()]
if kind == "link_map":
links = {}
for line in non_empty_lines(text):
name, _, url = line.partition(" ")
if not url.strip():
raise ValueError(f"Link line needs a name and a url: {line}")
links[name.strip()] = url.strip()
return links
if kind == "link_list":
links = []
for line in non_empty_lines(text):
label, url = split_parts(line, 2)
if not url:
raise ValueError(f"Link line needs label | url: {line}")
links.append({"label": label, "url": url})
return links
if kind == "awards":
awards = []
for line in non_empty_lines(text):
track, place = split_parts(line, 2)
awards.append({"track": track, "place": place} if place else {"track": track})
return awards
if kind == "socials":
socials = []
for line in non_empty_lines(text):
name, icon, url = split_parts(line, 3)
if not url:
raise ValueError(f"Social line needs name | icon | url: {line}")
socials.append({"name": name, "icon": icon, "url": url})
return socials
if kind == "skill_map":
skills = {}
for line in non_empty_lines(text):
category, _, values = line.partition(":")
if not values.strip():
raise ValueError(f"Skill line needs Category: skill, skill: {line}")
skills[category.strip()] = [skill.strip() for skill in values.split(",") if skill.strip()]
return skills
return text
def summary_text(value) -> str:
if value is None:
return ""
if isinstance(value, bool):
return "yes" if value else ""
if isinstance(value, list):
return ", ".join(str(item) for item in value if not isinstance(item, dict))
return str(value)