from dataclasses import dataclass, field import jsonfile from fields import FieldSpec, get_path @dataclass class Collection: id: str title: str file: str fields: list[FieldSpec] columns: list[tuple[str, str]] = field(default_factory=list) single: bool = False @property def title_field(self) -> FieldSpec: return self.fields[0] PROJECT_BASICS = [ FieldSpec("name", "Name", required=True), FieldSpec("description", "Description", "longtext"), FieldSpec("featured", "Featured", "flag"), FieldSpec("tags", "Tags", "tags"), ] PROJECT_COLUMNS = [("Name", "name"), ("Featured", "featured"), ("Tags", "tags")] PROFILE = Collection( "profile", "Profile", "profile.json", [ FieldSpec("name", "Full name", required=True), FieldSpec("displayName", "Display name"), FieldSpec("title", "Title"), FieldSpec("tagline", "Tagline", "longtext"), FieldSpec("bio", "Bio", "longtext"), FieldSpec("email", "Email"), FieldSpec("photo", "Portfolio photo", "image"), FieldSpec("avatar", "Avatar", "image"), FieldSpec("education.school", "School"), FieldSpec("education.degree", "Degree"), FieldSpec("education.graduation", "Graduation"), FieldSpec("interests", "Interests", "tags"), FieldSpec("skills", "Skills", "skill_map"), FieldSpec("socials", "Socials", "socials"), FieldSpec("donateUrl", "Donate url"), FieldSpec("giteaUrl", "Gitea url"), ], single=True, ) EXPERIENCE = Collection( "experience", "Experience", "experience.json", [ FieldSpec("role", "Role", required=True), FieldSpec("organization", "Organization"), FieldSpec("school", "School"), FieldSpec("period", "Period"), FieldSpec("employmentType", "Employment type"), FieldSpec("highlights", "Highlights", "lines"), FieldSpec("tech", "Tech", "tags"), FieldSpec("awards", "Awards", "lines"), FieldSpec("links", "Links", "link_list"), ], [("Role", "role"), ("Organization", "organization"), ("Period", "period")], ) LEADERSHIP = Collection( "leadership", "Leadership", "leadership.json", [ FieldSpec("role", "Role", required=True), FieldSpec("organization", "Organization"), FieldSpec("url", "Url"), ], [("Role", "role"), ("Organization", "organization")], ) PROJECT_COLLECTIONS = [ Collection( "opensource", "Open Source", "projects/opensource.json", [*PROJECT_BASICS, FieldSpec("version", "Version"), FieldSpec("period", "Period"), FieldSpec("image", "Image", "image"), FieldSpec("links", "Links", "link_map")], PROJECT_COLUMNS, ), Collection( "packages", "Packages", "projects/packages.json", [*PROJECT_BASICS, FieldSpec("deprecated", "Deprecated", "flag"), FieldSpec("links", "Links", "link_map")], PROJECT_COLUMNS, ), Collection( "hackathons", "Hackathons", "projects/hackathons.json", [ *PROJECT_BASICS, FieldSpec("image", "Image", "image"), FieldSpec("event.name", "Event"), FieldSpec("event.university", "University"), FieldSpec("event.location", "Location"), FieldSpec("event.year", "Year"), FieldSpec("awards", "Awards", "awards"), FieldSpec("links", "Links", "link_map"), ], [("Name", "name"), ("Event", "event.name"), ("Featured", "featured")], ), Collection( "research", "Research groups", "projects/research.json", [ FieldSpec("name", "Name", required=True), FieldSpec("shortName", "Short name"), FieldSpec("institution", "Institution"), FieldSpec("role", "Role"), FieldSpec("period", "Period"), FieldSpec("description", "Description", "longtext"), FieldSpec("lab.name", "Lab name"), FieldSpec("lab.url", "Lab url"), FieldSpec("focus", "Focus", "tags"), FieldSpec("links", "Links", "link_list"), ], [("Name", "name"), ("Role", "role"), ("Period", "period")], ), Collection( "organizations", "Organizations", "projects/organizations.json", [ *PROJECT_BASICS, FieldSpec("organization", "Roles"), FieldSpec("awards", "Awards", "awards"), FieldSpec("gallery", "Gallery", "images"), FieldSpec("links", "Links", "link_map"), FieldSpec("socials", "Socials", "socials"), ], PROJECT_COLUMNS, ), ] def find_collection(collection_id: str) -> Collection: for collection in [PROFILE, EXPERIENCE, LEADERSHIP, *PROJECT_COLLECTIONS]: if collection.id == collection_id: return collection raise ValueError(f"Unknown collection {collection_id}") def load_records(collection: Collection): return jsonfile.load(collection.file) def save_records(collection: Collection, records) -> None: jsonfile.save(collection.file, records) def record_title(collection: Collection, record: dict) -> str: return str(get_path(record, collection.title_field.path) or "untitled") def check_required(collection: Collection, record: dict) -> None: for spec in collection.fields: if spec.required and not get_path(record, spec.path): raise ValueError(f"{spec.label} is required") def save_record(collection: Collection, index: int | None, record: dict) -> int: check_required(collection, record) if collection.single: save_records(collection, record) return 0 records = load_records(collection) if index is None: records.append(record) index = len(records) - 1 else: records[index] = record save_records(collection, records) return index def remove_record(collection: Collection, index: int) -> dict: records = load_records(collection) removed = records.pop(index) save_records(collection, records) return removed def move_record(collection: Collection, index: int, step: int) -> int: records = load_records(collection) target = index + step if not 0 <= target < len(records): return index records[index], records[target] = records[target], records[index] save_records(collection, records) return target