From 7cb9ff614fbe642b55e295b75108ca1cf0fe9519 Mon Sep 17 00:00:00 2001 From: charvenetis Date: Thu, 17 Sep 2026 18:15:40 +0300 Subject: [PATCH 1/3] Move hardcoded text into JSON resources --- README.md | 13 ++- setup.py | 3 + src/unidesk/credits.py | 33 ------- src/unidesk/home.py | 88 ++++++++++--------- src/unidesk/links.py | 32 ------- src/unidesk/pages.py | 72 --------------- src/unidesk/resources/__init__.py | 0 src/unidesk/resources/text_data/__init__.py | 0 src/unidesk/resources/text_data/credits.json | 48 ++++++++++ src/unidesk/resources/text_data/faq.json | 24 +++++ src/unidesk/resources/text_data/links.json | 32 +++++++ .../resources/text_data/navigation.json | 28 ++++++ src/unidesk/resources/text_data/pages.json | 20 +++++ .../resources/text_data/ui_strings.json | 22 +++++ src/unidesk/text_data.py | 31 +++++++ 15 files changed, 265 insertions(+), 181 deletions(-) delete mode 100644 src/unidesk/credits.py delete mode 100644 src/unidesk/links.py delete mode 100644 src/unidesk/pages.py create mode 100644 src/unidesk/resources/__init__.py create mode 100644 src/unidesk/resources/text_data/__init__.py create mode 100644 src/unidesk/resources/text_data/credits.json create mode 100644 src/unidesk/resources/text_data/faq.json create mode 100644 src/unidesk/resources/text_data/links.json create mode 100644 src/unidesk/resources/text_data/navigation.json create mode 100644 src/unidesk/resources/text_data/pages.json create mode 100644 src/unidesk/resources/text_data/ui_strings.json create mode 100644 src/unidesk/text_data.py diff --git a/README.md b/README.md index 14458c6..4be73c4 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,19 @@ unidesk src/unidesk/ main.py entry point home.py main window and all navigation logic - pages.py text content for each informational page - credits.py list of contributors - links.py external links shown in the Links page + text_data.py loads the text below from the bundled JSON academic_institutions.py known universities and their departments academic_config.py reads/writes the shared academic profile + resources/text_data/ + pages.json body text for each informational page + faq.json FAQ entries as question/answer pairs + credits.json list of contributors + links.json external links shown in the Links page + navigation.json nav button labels and footer links + ui_strings.json buttons, titles and other interface text ``` -To update any page content just open `pages.py` and edit the body text for that page. To add a new contributor open `credits.py`. +All user-facing text lives in `resources/text_data/`. To update page content edit `pages.json`; to add a contributor edit `credits.json`. No Python changes are needed to change copy. ## Academic profile diff --git a/setup.py b/setup.py index 39848ca..76c9f85 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,9 @@ # Crucial for the new structure: package_dir={"": "src"}, packages=find_packages(where="src"), + package_data={ + "unidesk.resources.text_data": ["*.json"], + }, install_requires=["PyQt6"], entry_points={ 'console_scripts': [ diff --git a/src/unidesk/credits.py b/src/unidesk/credits.py deleted file mode 100644 index c9ebbd8..0000000 --- a/src/unidesk/credits.py +++ /dev/null @@ -1,33 +0,0 @@ -# Author: Emanuela Goxha 2026 - -CREDITS = [ - { - "name": "Apostolos Chalis", - "role": "Main Developer", - "projects": ["UniOS", "UniBackpack", "UniDesk"], - }, - { - "name": "Emanuela Goxha", - "role": "Developer & Designer", - "projects": ["UniMate", "UniDesk"], - }, - { - "name": "Malamatenia Soulioti", - "role": "Main Designer", - }, - { - "name": "Ioannis Michadasis", - "role": "Developer", - "projects": ["UniOS", "UniBackpack", "UniDesk"], - }, - { - "name": "Alexandra Iordanidou", - "role": "Community Moderator", - "projects": ["UniDesk"], - }, - { - "name": "Konstantina Deligianni", - "role": "Developer", - "projects": ["UniOS"], - }, -] diff --git a/src/unidesk/home.py b/src/unidesk/home.py index 56ba648..47c2ff6 100644 --- a/src/unidesk/home.py +++ b/src/unidesk/home.py @@ -20,11 +20,15 @@ ) from PyQt6.QtCore import Qt, QUrl from PyQt6.QtGui import QDesktopServices -from pages import PAGES -from credits import CREDITS -from links import LINKS from academic_config import load_academic_config, save_academic_config from academic_institutions import UNIVERSITIES +from text_data import load, pages + +PAGES = pages() +CREDITS = load("credits")["people"] +LINKS = load("links")["links"] +UI = load("ui_strings") +_NAV = load("navigation") AUTOSTART_PATH = os.path.expanduser("~/.config/autostart/unidesk.desktop") @@ -36,17 +40,10 @@ def _is_autostart_disabled(): return "Hidden=true" in f.read() -FOOTER_LINKS = [ - {"label": "Discord", "url": "https://discord.gg/QA9AxTdppX"}, - {"label": "GitHub", "url": "https://github.com/open-source-uom"}, - { - "label": "Website", - "url": "https://open-source-uom.github.io/UniOS-landing-page/", - }, -] +FOOTER_LINKS = _NAV["footer_links"] -NAV_LEFT = ["Introduction", "Features", "Links", "FAQ"] -NAV_RIGHT = ["Community", "Source Code", "Contribute", "Credits"] +NAV_LEFT = _NAV["nav_left"] +NAV_RIGHT = _NAV["nav_right"] # Helpers @@ -78,7 +75,7 @@ def _back_bar(title, on_back): layout = QHBoxLayout(bar) layout.setContentsMargins(12, 0, 12, 0) - btn = QPushButton("← Back") + btn = QPushButton(UI["back_button"]) btn.setFixedWidth(70) btn.setStyleSheet( "background: transparent; border: none; color: #a6adc8; " @@ -135,11 +132,11 @@ def _footer(on_configure=None): footer.setStyleSheet("background: #110d1a;") ft = QHBoxLayout(footer) ft.setContentsMargins(14, 0, 14, 0) - ft.addWidget(_qlabel("Open Source UoM · 2026", size=11, color="#585b70")) + ft.addWidget(_qlabel(UI["footer_copyright"], size=11, color="#585b70")) ft.addStretch() if on_configure is not None: - cfg = QPushButton("Configure UniOS") + cfg = QPushButton(UI["configure_button"]) cfg.setStyleSheet( "background: transparent; border: none; color: #8b5897; font-size: 11px;" ) @@ -174,7 +171,7 @@ def build_text_page(key, on_back): def build_credits_page(on_back): - widget, cl = _scroll_page(on_back, "Credits") + widget, cl = _scroll_page(on_back, UI["credits_page_title"]) for person in CREDITS: frame = QFrame() @@ -200,7 +197,9 @@ def build_credits_page(on_back): if person.get("projects"): proj = _qlabel( - "Projects: " + ", ".join(person["projects"]), size=11, color="#8b5897" + UI["projects_prefix"] + ", ".join(person["projects"]), + size=11, + color="#8b5897", ) proj.setStyleSheet( proj.styleSheet() + " background: transparent; border: none;" @@ -214,7 +213,7 @@ def build_credits_page(on_back): def build_links_page(on_back): - widget, cl = _scroll_page(on_back, "Links") + widget, cl = _scroll_page(on_back, UI["links_page_title"]) for link in LINKS: frame = QFrame() @@ -232,7 +231,7 @@ def build_links_page(on_back): ) fl.addWidget(name) - btn = QPushButton("Open Link") + btn = QPushButton(UI["open_link_button"]) btn.setCursor(Qt.CursorShape.PointingHandCursor) btn.setStyleSheet( "QPushButton { background-color: #89b4fa; color: #1e1e2e; font-weight: bold; " @@ -249,11 +248,10 @@ def build_links_page(on_back): def build_academic_config_page(on_back): - widget, cl = _scroll_page(on_back, "Configure UniOS") + widget, cl = _scroll_page(on_back, UI["academic_config_page_title"]) intro = _qlabel( - "Set your university and department. This profile is shared with other " - "UniOS apps, so pick the entries that match your studies.", + UI["academic_config_intro"], size=12, color="#a6adc8", wrap=True, @@ -267,18 +265,22 @@ def build_academic_config_page(on_back): "selection-background-color: #3d2a52; }" ) - cl.addWidget(_qlabel("University", size=12, color="#cdd6f4", bold=True)) + cl.addWidget( + _qlabel(UI["academic_university_label"], size=12, color="#cdd6f4", bold=True) + ) university_combo = QComboBox() university_combo.setStyleSheet(combo_style) - university_combo.setPlaceholderText("Select university") + university_combo.setPlaceholderText(UI["academic_university_placeholder"]) university_combo.addItems(list(UNIVERSITIES.keys())) university_combo.setCurrentIndex(-1) cl.addWidget(university_combo) - cl.addWidget(_qlabel("Department", size=12, color="#cdd6f4", bold=True)) + cl.addWidget( + _qlabel(UI["academic_department_label"], size=12, color="#cdd6f4", bold=True) + ) department_combo = QComboBox() department_combo.setStyleSheet(combo_style) - department_combo.setPlaceholderText("Select department") + department_combo.setPlaceholderText(UI["academic_department_placeholder"]) department_combo.setCurrentIndex(-1) cl.addWidget(department_combo) @@ -303,7 +305,7 @@ def refresh_departments(): if saved_department in UNIVERSITIES[saved_university]: department_combo.setCurrentText(saved_department) - save_btn = QPushButton("Save") + save_btn = QPushButton(UI["academic_save_button"]) save_btn.setCursor(Qt.CursorShape.PointingHandCursor) save_btn.setStyleSheet( "QPushButton { background-color: #89b4fa; color: #1e1e2e; font-weight: bold; " @@ -315,11 +317,15 @@ def on_save(): university = university_combo.currentText() department = department_combo.currentText() if not university or not department: - status.setText("Please select a university and department.") + status.setText(UI["academic_error_incomplete"]) status.setStyleSheet(status.styleSheet().replace("#a6adc8", "#f38ba8")) return save_academic_config(university, department) - status.setText(f"Saved: {university} · {department}") + status.setText( + UI["academic_saved_template"].format( + university=university, department=department + ) + ) status.setStyleSheet(status.styleSheet().replace("#f38ba8", "#a6adc8")) save_btn.clicked.connect(lambda _: on_save()) @@ -368,7 +374,7 @@ def __init__(self, label, align_right=False): class UniOSWelcome(QMainWindow): def __init__(self): super().__init__() - self.setWindowTitle("UniDesk") + self.setWindowTitle(UI["window_title"]) self.setMinimumSize(580, 500) self.setStyleSheet("background-color: #1a1226;") @@ -393,12 +399,12 @@ def _build_main(self): hl.setContentsMargins(20, 20, 20, 16) hl.setSpacing(4) - hero_title = _qlabel("Welcome to UniOS", size=26, color="#cba6f7", bold=True) + hero_title = _qlabel(UI["hero_title"], size=26, color="#cba6f7", bold=True) hero_title.setAlignment(Qt.AlignmentFlag.AlignCenter) hl.addWidget(hero_title) hero_sub = _qlabel( - "A custom Linux distribution for the modern Greek university", + UI["hero_subtitle"], size=12, color="#a6adc8", ) @@ -425,7 +431,7 @@ def _build_main(self): bottom_row = QHBoxLayout() bottom_row.setContentsMargins(28, 0, 28, 14) - self._autostart_cb = QCheckBox("Auto start") + self._autostart_cb = QCheckBox(UI["autostart_checkbox"]) self._autostart_cb.setChecked(True) _svg = b"" _f = tempfile.NamedTemporaryFile(suffix=".svg", delete=False) @@ -464,8 +470,8 @@ def _build_main(self): ) self._autostart_cb.stateChanged.connect(self._toggle_autostart) - cfg_btn = QPushButton("Configure UniOS") - cfg_btn = QPushButton("Configure UniOS") + cfg_btn = QPushButton(UI["configure_button"]) + cfg_btn = QPushButton(UI["configure_button"]) cfg_btn.setFixedHeight(28) cfg_btn.setStyleSheet(""" QPushButton { @@ -505,9 +511,11 @@ def _build_main(self): def _build_subpages(self): subpages = { **{key: build_text_page(key, self._show_main) for key in PAGES}, - "Credits": build_credits_page(self._show_main), - "Links": build_links_page(self._show_main), - "Configure UniOS": build_academic_config_page(self._show_main), + UI["credits_page_title"]: build_credits_page(self._show_main), + UI["links_page_title"]: build_links_page(self._show_main), + UI["academic_config_page_title"]: build_academic_config_page( + self._show_main + ), } for key, widget in subpages.items(): self._page_indices[key] = self._stack.addWidget(widget) @@ -532,7 +540,7 @@ def _show_main(self): self._stack.setCurrentIndex(0) def _show_academic_config(self): - self._stack.setCurrentIndex(self._page_indices["Configure UniOS"]) + self._stack.setCurrentIndex(self._page_indices[UI["academic_config_page_title"]]) def main(): diff --git a/src/unidesk/links.py b/src/unidesk/links.py deleted file mode 100644 index 6ca54c8..0000000 --- a/src/unidesk/links.py +++ /dev/null @@ -1,32 +0,0 @@ -# Author: Emanuela Goxha 2026 - -LINKS = [ - { - "label": "Discord Channel", - "url": "https://discord.gg/QA9AxTdppX", - }, - { - "label": "Department's software list", - "url": "https://docs.google.com/forms/d/e/1FAIpQLSfX27PSSPFWNH55Uq4GX2pxhh2egHBLEAk_rCPdye5924wz6Q/viewform", - }, - { - "label": "Github", - "url": "https://github.com/open-source-uom", - }, - { - "label": "UniMate", - "url": "https://open-source-uom.github.io/UniMate/", - }, - { - "label": "Uni OS Official Webpage", - "url": "https://open-source-uom.github.io/UniOS-landing-page/", - }, - { - "label": "UniOS Docs", - "url": "https://open-source-uom.github.io/UniOS-docs/", - }, - { - "label": "Open Source UoM", - "url": "http://opensource.uom.gr/", - }, -] diff --git a/src/unidesk/pages.py b/src/unidesk/pages.py deleted file mode 100644 index 83fc38b..0000000 --- a/src/unidesk/pages.py +++ /dev/null @@ -1,72 +0,0 @@ -# All sub page content here -PAGES = { - "Introduction": { - "body": ( - "UniOS is a custom Linux distribution built specifically to be used " - "by students & staff at all Greek Universities.\n\n" - "It is based on Kubuntu and ships with a carefully selected set of tools " - "for academic work, programming, and everyday use, all pre-configured so " - "you can get started without any manual setup. It's developed and maintained " - "by the Open Source UoM community." - ), - }, - "Features": { - "body": ( - "• KDE Plasma desktop, preconfigured with a clean academic theme\n" - "• UniBackpack integration for installing additional software\n" - "• Greek and English language support\n" - "• Lightweight footprint - runs well on older hardware" - ), - }, - "FAQ": { - "body": ( - "Q: Can I install UniOS alongside Windows?\n" - "A: Yes, the installer supports dual boot.\n\n" - "Q: How do I update the system?\n" - "A: Run `sudo apt update && sudo apt upgrade` in a terminal.\n\n" - "Q: Something is broken. Where do I report it?\n" - "A: Open an issue on our GitHub repository or reach out on Discord.\n\n" - "Q: Can I use UniOS on my personal laptop?\n" - "A: Absolutely - it is designed for any hardware.\n\n" - "Q: Is UniOS free?\n" - "A: Yes, UniOS is free as in freedom not as in beer." - ), - }, - "Community": { - "body": ( - "UniOS is built by students, for students.\n\n" - "Join the Open Source UoM community to:\n" - "• Get help and support from fellow students\n" - "• Share tips, tweaks, and configurations\n" - "• Participate in workshops and coding events\n" - "• Stay updated on new releases and features\n\n" - "You can find us on Discord — the invite link is in the footer.\n" - "Everyone is welcome, regardless of experience level." - ), - }, - "Source Code": { - "body": ( - "UniOS is fully open source.\n\n" - "All build scripts and code for the apps are publicly available on GitHub under an open license.\n\n" - "Repositories:\n" - "• UniOS - The Linux distro you are currently using.\n" - "• UniBackpack - software installer tool for all Universities.\n" - "• UniMate - A Webpage that helps you install various Linux apps.\n" - "• UniDesk - this desktop launcher app.\n\n" - "Feel free to fork, study, and adapt the code for your own projects. " - "Pull requests and bug reports are always welcome." - ), - }, - "Contribute": { - "body": ( - "Want to help build UniOS? There are many ways to contribute:\n\n" - "• Code - fix bugs, add features, improve scripts\n" - "• Design - UI/UX improvements, icons, wallpapers\n" - "• Documentation - write guides, translate content\n" - "• Testing - try new builds and report issues\n" - "• Community - help other students in Discord\n\n" - "No contribution is too small. Check our " - "GitHub repositories to get started." - ), - }, -} diff --git a/src/unidesk/resources/__init__.py b/src/unidesk/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/unidesk/resources/text_data/__init__.py b/src/unidesk/resources/text_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/unidesk/resources/text_data/credits.json b/src/unidesk/resources/text_data/credits.json new file mode 100644 index 0000000..6149af5 --- /dev/null +++ b/src/unidesk/resources/text_data/credits.json @@ -0,0 +1,48 @@ +{ + "people": [ + { + "name": "Apostolos Chalis", + "role": "Main Developer", + "projects": [ + "UniOS", + "UniBackpack", + "UniDesk" + ] + }, + { + "name": "Emanuela Goxha", + "role": "Developer & Designer", + "projects": [ + "UniMate", + "UniDesk" + ] + }, + { + "name": "Malamatenia Soulioti", + "role": "Main Designer" + }, + { + "name": "Ioannis Michadasis", + "role": "Developer", + "projects": [ + "UniOS", + "UniBackpack", + "UniDesk" + ] + }, + { + "name": "Alexandra Iordanidou", + "role": "Community Moderator", + "projects": [ + "UniDesk" + ] + }, + { + "name": "Konstantina Deligianni", + "role": "Developer", + "projects": [ + "UniOS" + ] + } + ] +} diff --git a/src/unidesk/resources/text_data/faq.json b/src/unidesk/resources/text_data/faq.json new file mode 100644 index 0000000..7cfff7e --- /dev/null +++ b/src/unidesk/resources/text_data/faq.json @@ -0,0 +1,24 @@ +{ + "entries": [ + { + "question": "Can I install UniOS alongside Windows?", + "answer": "Yes, the installer supports dual boot." + }, + { + "question": "How do I update the system?", + "answer": "Run `sudo apt update && sudo apt upgrade` in a terminal." + }, + { + "question": "Something is broken. Where do I report it?", + "answer": "Open an issue on our GitHub repository or reach out on Discord." + }, + { + "question": "Can I use UniOS on my personal laptop?", + "answer": "Absolutely - it is designed for any hardware." + }, + { + "question": "Is UniOS free?", + "answer": "Yes, UniOS is free as in freedom not as in beer." + } + ] +} diff --git a/src/unidesk/resources/text_data/links.json b/src/unidesk/resources/text_data/links.json new file mode 100644 index 0000000..4464586 --- /dev/null +++ b/src/unidesk/resources/text_data/links.json @@ -0,0 +1,32 @@ +{ + "links": [ + { + "label": "Discord Channel", + "url": "https://discord.gg/QA9AxTdppX" + }, + { + "label": "Department's software list", + "url": "https://docs.google.com/forms/d/e/1FAIpQLSfX27PSSPFWNH55Uq4GX2pxhh2egHBLEAk_rCPdye5924wz6Q/viewform" + }, + { + "label": "Github", + "url": "https://github.com/open-source-uom" + }, + { + "label": "UniMate", + "url": "https://open-source-uom.github.io/UniMate/" + }, + { + "label": "Uni OS Official Webpage", + "url": "https://open-source-uom.github.io/UniOS-landing-page/" + }, + { + "label": "UniOS Docs", + "url": "https://open-source-uom.github.io/UniOS-docs/" + }, + { + "label": "Open Source UoM", + "url": "http://opensource.uom.gr/" + } + ] +} diff --git a/src/unidesk/resources/text_data/navigation.json b/src/unidesk/resources/text_data/navigation.json new file mode 100644 index 0000000..92a97a6 --- /dev/null +++ b/src/unidesk/resources/text_data/navigation.json @@ -0,0 +1,28 @@ +{ + "nav_left": [ + "Introduction", + "Features", + "Links", + "FAQ" + ], + "nav_right": [ + "Community", + "Source Code", + "Contribute", + "Credits" + ], + "footer_links": [ + { + "label": "Discord", + "url": "https://discord.gg/QA9AxTdppX" + }, + { + "label": "GitHub", + "url": "https://github.com/open-source-uom" + }, + { + "label": "Website", + "url": "https://open-source-uom.github.io/UniOS-landing-page/" + } + ] +} diff --git a/src/unidesk/resources/text_data/pages.json b/src/unidesk/resources/text_data/pages.json new file mode 100644 index 0000000..cbda102 --- /dev/null +++ b/src/unidesk/resources/text_data/pages.json @@ -0,0 +1,20 @@ +{ + "Introduction": { + "body": "UniOS is a custom Linux distribution built specifically to be used by students & staff at all Greek Universities.\n\nIt is based on Kubuntu and ships with a carefully selected set of tools for academic work, programming, and everyday use, all pre-configured so you can get started without any manual setup. It's developed and maintained by the Open Source UoM community." + }, + "Features": { + "body": "• KDE Plasma desktop, preconfigured with a clean academic theme\n• UniBackpack integration for installing additional software\n• Greek and English language support\n• Lightweight footprint - runs well on older hardware" + }, + "FAQ": { + "body_from": "faq" + }, + "Community": { + "body": "UniOS is built by students, for students.\n\nJoin the Open Source UoM community to:\n• Get help and support from fellow students\n• Share tips, tweaks, and configurations\n• Participate in workshops and coding events\n• Stay updated on new releases and features\n\nYou can find us on Discord — the invite link is in the footer.\nEveryone is welcome, regardless of experience level." + }, + "Source Code": { + "body": "UniOS is fully open source.\n\nAll build scripts and code for the apps are publicly available on GitHub under an open license.\n\nRepositories:\n• UniOS - The Linux distro you are currently using.\n• UniBackpack - software installer tool for all Universities.\n• UniMate - A Webpage that helps you install various Linux apps.\n• UniDesk - this desktop launcher app.\n\nFeel free to fork, study, and adapt the code for your own projects. Pull requests and bug reports are always welcome." + }, + "Contribute": { + "body": "Want to help build UniOS? There are many ways to contribute:\n\n• Code - fix bugs, add features, improve scripts\n• Design - UI/UX improvements, icons, wallpapers\n• Documentation - write guides, translate content\n• Testing - try new builds and report issues\n• Community - help other students in Discord\n\nNo contribution is too small. Check our GitHub repositories to get started." + } +} diff --git a/src/unidesk/resources/text_data/ui_strings.json b/src/unidesk/resources/text_data/ui_strings.json new file mode 100644 index 0000000..deb5d4e --- /dev/null +++ b/src/unidesk/resources/text_data/ui_strings.json @@ -0,0 +1,22 @@ +{ + "window_title": "UniDesk", + "hero_title": "Welcome to UniOS", + "hero_subtitle": "A custom Linux distribution for the modern Greek university", + "back_button": "← Back", + "footer_copyright": "Open Source UoM · 2026", + "open_link_button": "Open Link", + "projects_prefix": "Projects: ", + "autostart_checkbox": "Auto start", + "configure_button": "Configure UniOS", + "credits_page_title": "Credits", + "links_page_title": "Links", + "academic_config_page_title": "Configure UniOS", + "academic_config_intro": "Set your university and department. This profile is shared with other UniOS apps, so pick the entries that match your studies.", + "academic_university_label": "University", + "academic_department_label": "Department", + "academic_university_placeholder": "Select university", + "academic_department_placeholder": "Select department", + "academic_save_button": "Save", + "academic_error_incomplete": "Please select a university and department.", + "academic_saved_template": "Saved: {university} · {department}" +} diff --git a/src/unidesk/text_data.py b/src/unidesk/text_data.py new file mode 100644 index 0000000..c505769 --- /dev/null +++ b/src/unidesk/text_data.py @@ -0,0 +1,31 @@ +"""Loads UniDesk's user-facing text from the bundled JSON files.""" + +import json +from functools import lru_cache +from importlib.resources import files + +_TEXT_DATA_PACKAGE = "unidesk.resources.text_data" + + +@lru_cache(maxsize=None) +def load(name): + """Return the parsed contents of .json.""" + path = files(_TEXT_DATA_PACKAGE).joinpath(f"{name}.json") + return json.loads(path.read_text(encoding="utf-8")) + + +def faq_body(): + """Flatten the structured FAQ entries into the page body text.""" + entries = load("faq")["entries"] + return "\n\n".join(f"Q: {e['question']}\nA: {e['answer']}" for e in entries) + + +def pages(): + """Page bodies keyed by page title, in display order.""" + resolved = {} + for key, page in load("pages").items(): + if "body_from" in page: + resolved[key] = {"body": faq_body()} + else: + resolved[key] = {"body": page["body"]} + return resolved From a1d99cea50d1bf9032a3e8b461398193dce07879 Mon Sep 17 00:00:00 2001 From: charvenetis Date: Thu, 17 Sep 2026 18:37:09 +0300 Subject: [PATCH 2/3] Fix import sort order and lru_cache lint in this PR's new code --- src/unidesk/home.py | 24 ++++++++++++------------ src/unidesk/text_data.py | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/unidesk/home.py b/src/unidesk/home.py index 47c2ff6..276fd79 100644 --- a/src/unidesk/home.py +++ b/src/unidesk/home.py @@ -4,24 +4,24 @@ sys.path.insert(0, os.path.dirname(__file__)) +from academic_config import load_academic_config, save_academic_config +from academic_institutions import UNIVERSITIES +from PyQt6.QtCore import Qt, QUrl +from PyQt6.QtGui import QDesktopServices from PyQt6.QtWidgets import ( - QWidget, - QVBoxLayout, + QApplication, + QCheckBox, + QComboBox, + QFrame, QHBoxLayout, QLabel, - QFrame, + QMainWindow, QPushButton, - QStackedWidget, QScrollArea, - QApplication, - QMainWindow, - QCheckBox, - QComboBox, + QStackedWidget, + QVBoxLayout, + QWidget, ) -from PyQt6.QtCore import Qt, QUrl -from PyQt6.QtGui import QDesktopServices -from academic_config import load_academic_config, save_academic_config -from academic_institutions import UNIVERSITIES from text_data import load, pages PAGES = pages() diff --git a/src/unidesk/text_data.py b/src/unidesk/text_data.py index c505769..e2b4675 100644 --- a/src/unidesk/text_data.py +++ b/src/unidesk/text_data.py @@ -1,13 +1,13 @@ """Loads UniDesk's user-facing text from the bundled JSON files.""" import json -from functools import lru_cache +from functools import cache from importlib.resources import files _TEXT_DATA_PACKAGE = "unidesk.resources.text_data" -@lru_cache(maxsize=None) +@cache def load(name): """Return the parsed contents of .json.""" path = files(_TEXT_DATA_PACKAGE).joinpath(f"{name}.json") From a47654a36108f86497f41face542bc65b893ef08 Mon Sep 17 00:00:00 2001 From: charvenetis Date: Sun, 20 Sep 2026 15:39:23 +0300 Subject: [PATCH 3/3] Move UNIVERSITIES into JSON; rename resources/ to assets/ --- README.md | 8 ++++---- setup.py | 2 +- src/unidesk/academic_institutions.py | 9 --------- src/unidesk/{resources => assets}/__init__.py | 0 .../{resources => assets}/text_data/__init__.py | 0 .../assets/text_data/academic_institutions.json | 17 +++++++++++++++++ .../text_data/credits.json | 0 .../{resources => assets}/text_data/faq.json | 0 .../{resources => assets}/text_data/links.json | 0 .../text_data/navigation.json | 0 .../{resources => assets}/text_data/pages.json | 0 .../text_data/ui_strings.json | 0 src/unidesk/home.py | 2 +- src/unidesk/text_data.py | 2 +- 14 files changed, 24 insertions(+), 16 deletions(-) delete mode 100644 src/unidesk/academic_institutions.py rename src/unidesk/{resources => assets}/__init__.py (100%) rename src/unidesk/{resources => assets}/text_data/__init__.py (100%) create mode 100644 src/unidesk/assets/text_data/academic_institutions.json rename src/unidesk/{resources => assets}/text_data/credits.json (100%) rename src/unidesk/{resources => assets}/text_data/faq.json (100%) rename src/unidesk/{resources => assets}/text_data/links.json (100%) rename src/unidesk/{resources => assets}/text_data/navigation.json (100%) rename src/unidesk/{resources => assets}/text_data/pages.json (100%) rename src/unidesk/{resources => assets}/text_data/ui_strings.json (100%) diff --git a/README.md b/README.md index 4be73c4..9f1200a 100644 --- a/README.md +++ b/README.md @@ -37,22 +37,22 @@ src/unidesk/ main.py entry point home.py main window and all navigation logic text_data.py loads the text below from the bundled JSON - academic_institutions.py known universities and their departments academic_config.py reads/writes the shared academic profile - resources/text_data/ + assets/text_data/ pages.json body text for each informational page faq.json FAQ entries as question/answer pairs credits.json list of contributors links.json external links shown in the Links page navigation.json nav button labels and footer links ui_strings.json buttons, titles and other interface text + academic_institutions.json known universities and their departments ``` -All user-facing text lives in `resources/text_data/`. To update page content edit `pages.json`; to add a contributor edit `credits.json`. No Python changes are needed to change copy. +All user-facing text lives in `assets/text_data/`. To update page content edit `pages.json`; to add a contributor edit `credits.json`. No Python changes are needed to change copy. ## Academic profile -The footer on the home screen has a **Configure UniOS** button. It opens a page where you pick your university and department from dropdowns, which is saved to `~/.unios/academicConfig.json`. Other UniOS apps (such as UniBackpack) read this file, so the available choices live in `academic_institutions.py` and must stay in sync with those apps. If UniBackpack adds new universities or departments, mirror them in `academic_institutions.py`. +The footer on the home screen has a **Configure UniOS** button. It opens a page where you pick your university and department from dropdowns, which is saved to `~/.unios/academicConfig.json`. Other UniOS apps (such as UniBackpack) read this file, so the available choices live in `assets/text_data/academic_institutions.json` and must stay in sync with those apps. If UniBackpack adds new universities or departments, mirror them in `assets/text_data/academic_institutions.json`. ## Contributing diff --git a/setup.py b/setup.py index 76c9f85..def382b 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ package_dir={"": "src"}, packages=find_packages(where="src"), package_data={ - "unidesk.resources.text_data": ["*.json"], + "unidesk.assets.text_data": ["*.json"], }, install_requires=["PyQt6"], entry_points={ diff --git a/src/unidesk/academic_institutions.py b/src/unidesk/academic_institutions.py deleted file mode 100644 index 0bf4baa..0000000 --- a/src/unidesk/academic_institutions.py +++ /dev/null @@ -1,9 +0,0 @@ -# Academic institutions known to UniOS. -# -# IMPORTANT: This list must stay in sync with UniBackpack's hardcoded values -# (UniBackpack/src/MainWindow.cpp). -UNIVERSITIES = { - "Aristotle University of Thessaloniki": ["Informatics", "Physics"], - "University of Western Macedonia": ["Informatics", "Mechanical Engineering"], - "University of Macedonia": ["Applied Informatics", "Economics"], -} diff --git a/src/unidesk/resources/__init__.py b/src/unidesk/assets/__init__.py similarity index 100% rename from src/unidesk/resources/__init__.py rename to src/unidesk/assets/__init__.py diff --git a/src/unidesk/resources/text_data/__init__.py b/src/unidesk/assets/text_data/__init__.py similarity index 100% rename from src/unidesk/resources/text_data/__init__.py rename to src/unidesk/assets/text_data/__init__.py diff --git a/src/unidesk/assets/text_data/academic_institutions.json b/src/unidesk/assets/text_data/academic_institutions.json new file mode 100644 index 0000000..bfa8c03 --- /dev/null +++ b/src/unidesk/assets/text_data/academic_institutions.json @@ -0,0 +1,17 @@ +{ + "_comment": "This list must stay in sync with UniBackpack's hardcoded values (UniBackpack/src/MainWindow.cpp).", + "universities": { + "Aristotle University of Thessaloniki": [ + "Informatics", + "Physics" + ], + "University of Western Macedonia": [ + "Informatics", + "Mechanical Engineering" + ], + "University of Macedonia": [ + "Applied Informatics", + "Economics" + ] + } +} diff --git a/src/unidesk/resources/text_data/credits.json b/src/unidesk/assets/text_data/credits.json similarity index 100% rename from src/unidesk/resources/text_data/credits.json rename to src/unidesk/assets/text_data/credits.json diff --git a/src/unidesk/resources/text_data/faq.json b/src/unidesk/assets/text_data/faq.json similarity index 100% rename from src/unidesk/resources/text_data/faq.json rename to src/unidesk/assets/text_data/faq.json diff --git a/src/unidesk/resources/text_data/links.json b/src/unidesk/assets/text_data/links.json similarity index 100% rename from src/unidesk/resources/text_data/links.json rename to src/unidesk/assets/text_data/links.json diff --git a/src/unidesk/resources/text_data/navigation.json b/src/unidesk/assets/text_data/navigation.json similarity index 100% rename from src/unidesk/resources/text_data/navigation.json rename to src/unidesk/assets/text_data/navigation.json diff --git a/src/unidesk/resources/text_data/pages.json b/src/unidesk/assets/text_data/pages.json similarity index 100% rename from src/unidesk/resources/text_data/pages.json rename to src/unidesk/assets/text_data/pages.json diff --git a/src/unidesk/resources/text_data/ui_strings.json b/src/unidesk/assets/text_data/ui_strings.json similarity index 100% rename from src/unidesk/resources/text_data/ui_strings.json rename to src/unidesk/assets/text_data/ui_strings.json diff --git a/src/unidesk/home.py b/src/unidesk/home.py index 276fd79..f63d155 100644 --- a/src/unidesk/home.py +++ b/src/unidesk/home.py @@ -5,7 +5,6 @@ sys.path.insert(0, os.path.dirname(__file__)) from academic_config import load_academic_config, save_academic_config -from academic_institutions import UNIVERSITIES from PyQt6.QtCore import Qt, QUrl from PyQt6.QtGui import QDesktopServices from PyQt6.QtWidgets import ( @@ -28,6 +27,7 @@ CREDITS = load("credits")["people"] LINKS = load("links")["links"] UI = load("ui_strings") +UNIVERSITIES = load("academic_institutions")["universities"] _NAV = load("navigation") AUTOSTART_PATH = os.path.expanduser("~/.config/autostart/unidesk.desktop") diff --git a/src/unidesk/text_data.py b/src/unidesk/text_data.py index e2b4675..a5765a5 100644 --- a/src/unidesk/text_data.py +++ b/src/unidesk/text_data.py @@ -4,7 +4,7 @@ from functools import cache from importlib.resources import files -_TEXT_DATA_PACKAGE = "unidesk.resources.text_data" +_TEXT_DATA_PACKAGE = "unidesk.assets.text_data" @cache