Skip to content

Commit ee1829c

Browse files
authored
Merge pull request #22 from richardkoehler/feature/remove-pytz
feat: improve time zone and version handling and add presets
2 parents d81867a + ea5ab9c commit ee1829c

11 files changed

Lines changed: 214 additions & 91 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
A desktop application for annotating Deep Brain Stimulation (DBS) clinical programming sessions. Built for clinicians and researchers working with DBS systems (Medtronic Percept and others).
44

5-
**Version:** 0.3.0-beta
5+
**Version:** derived from `clinical_dbs_annotator.__version__`
66
**Author:** Lucia Poma (lucia.poma@wysscenter.ch)
77

88
## For End Users
99

1010
**No installation required.** Download the pre-built executable and run it directly:
1111

1212
1. Go to the `dist/` folder
13-
2. Run `ClinicalDBSAnnot_v0_3_testing.exe`
13+
2. Run the `ClinicalDBSAnnotator_*.exe` artifact for your platform/version
1414
3. That's it — the application opens immediately
1515

1616
The executable is self-contained and includes all dependencies.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ classifiers = [
2323
"Operating System :: OS Independent",
2424
]
2525
dependencies = [
26-
"pytz>=2025.2",
26+
"tzdata>=2025.3",
2727
"pandas>=2.1",
2828
"python-docx>=1.1",
2929
"docx2pdf>=0.1.8",

scripts/build_macos.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66

77
import argparse
8+
import re
89
import shutil
910
import subprocess
1011
import sys
@@ -18,9 +19,19 @@
1819
SRC_DIR = PROJECT_ROOT / "src"
1920

2021
APP_NAME = "ClinicalDBSAnnot"
21-
VERSION = "v0.3_testing"
2222
PLATFORM = "macOS"
2323

24+
def _read_version() -> str:
25+
init_path = SRC_DIR / "clinical_dbs_annotator" / "__init__.py"
26+
text = init_path.read_text(encoding="utf-8")
27+
m = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']\s*$', text, flags=re.MULTILINE)
28+
if not m:
29+
raise RuntimeError(f"Could not determine version from {init_path}")
30+
return m.group(1)
31+
32+
33+
VERSION = _read_version()
34+
2435

2536
def update_macos_logo(png_path: Path) -> bool:
2637
"""Update logoneutral.png from the provided file and regenerate logoneutral.icns."""
@@ -80,7 +91,7 @@ def build_macos_app(*, console: bool, onefile: bool):
8091
f"--workpath={BUILD_DIR / 'pyinstaller'}",
8192
f"--specpath={BUILD_DIR / 'pyinstaller'}",
8293
f"--icon={icon_path}",
83-
"--hidden-import=pytz",
94+
"--hidden-import=tzdata",
8495
"--hidden-import=pandas",
8596
"--hidden-import=openpyxl",
8697
"--hidden-import=xlrd",

scripts/build_windows.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,37 @@
55
"""
66

77
import argparse
8+
import re
89
import subprocess
910
import sys
1011
from pathlib import Path
1112

12-
# Get project root directory
1313
PROJECT_ROOT = Path(__file__).parent.parent
1414
DIST_DIR = PROJECT_ROOT / "dist"
1515
BUILD_DIR = PROJECT_ROOT / "build"
1616
ICONS_DIR = PROJECT_ROOT / "icons"
1717
SRC_DIR = PROJECT_ROOT / "src"
1818

19-
APP_NAME = "ClinicalDBSAnnot"
20-
VERSION = "v0.3_testing"
19+
APP_NAME = "ClinicalDBSAnnotator"
2120
PLATFORM = "Windows"
2221

22+
def _read_version() -> str:
23+
init_path = SRC_DIR / "clinical_dbs_annotator" / "__init__.py"
24+
text = init_path.read_text(encoding="utf-8")
25+
m = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']\s*$', text, flags=re.MULTILINE)
26+
if not m:
27+
raise RuntimeError(f"Could not determine version from {init_path}")
28+
return m.group(1)
29+
30+
31+
VERSION = _read_version()
32+
2333

2434
def build_windows_exe(*, console: bool, onefile: bool) -> bool:
2535
"""Build Windows executable using PyInstaller."""
2636
print(f"Building {APP_NAME} {VERSION} for Windows...")
2737

2838
name = f"{APP_NAME}_{PLATFORM}_{VERSION.replace('.', '_')}"
29-
# Use run.py as entrypoint
3039
entrypoint = PROJECT_ROOT / "run.py"
3140
styles_dir = PROJECT_ROOT / "styles"
3241
config_dir = SRC_DIR / "clinical_dbs_annotator" / "config"
@@ -40,10 +49,7 @@ def build_windows_exe(*, console: bool, onefile: bool) -> bool:
4049
f"--distpath={DIST_DIR}",
4150
f"--workpath={BUILD_DIR / 'pyinstaller'}",
4251
f"--specpath={BUILD_DIR / 'pyinstaller'}",
43-
# "--exclude-module=pythoncom",
44-
# "--exclude-module=pywintypes",
45-
# "--exclude-module=win32com",
46-
"--hidden-import=pytz",
52+
"--hidden-import=tzdata",
4753
"--hidden-import=pandas",
4854
"--hidden-import=openpyxl",
4955
"--hidden-import=xlrd",
@@ -73,7 +79,6 @@ def build_windows_exe(*, console: bool, onefile: bool) -> bool:
7379
]
7480
)
7581

76-
# Run PyInstaller
7782
try:
7883
subprocess.run(cmd, check=True, cwd=PROJECT_ROOT)
7984
print("\n✓ Build successful!")

scripts/build_windows_nuitka.py

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""
1515

1616
import argparse
17+
import re
1718
import sys
1819
from pathlib import Path
1920

@@ -26,11 +27,28 @@
2627
STYLES_DIR = PROJECT_ROOT / "styles"
2728
CONFIG_DIR = SRC_DIR / "clinical_dbs_annotator" / "config"
2829

29-
# --- Build configuration ----------------------------------------------------
30-
NAME = "ClinicalDBSAnnot"
30+
NAME = "ClinicalDBSAnnotator"
3131
ENTRYPOINT = PROJECT_ROOT / "run.py"
3232

33-
# Data files to include (src_path:dest_path)
33+
def _read_version() -> str:
34+
init_path = SRC_DIR / "clinical_dbs_annotator" / "__init__.py"
35+
text = init_path.read_text(encoding="utf-8")
36+
m = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']\s*$', text, flags=re.MULTILINE)
37+
if not m:
38+
raise RuntimeError(f"Could not determine version from {init_path}")
39+
return m.group(1)
40+
41+
42+
def _base_version(version: str) -> str:
43+
m = re.search(r"(\d+\.\d+\.\d+)", version)
44+
if not m:
45+
raise RuntimeError(f"Could not extract base version from {version!r}")
46+
return m.group(1)
47+
48+
49+
VERSION = _read_version()
50+
BASE_VERSION = _base_version(VERSION)
51+
3452
DATA_FILES = [
3553
(ICONS_DIR / "logoneutral.ico", "icons"),
3654
(ICONS_DIR / "logoneutral.png", "icons"),
@@ -40,9 +58,8 @@
4058
(CONFIG_DIR / "session_scales_presets.json", "config"),
4159
]
4260

43-
# Hidden imports (modules that are imported dynamically)
4461
HIDDEN_IMPORTS = [
45-
"pytz",
62+
"tzdata",
4663
"pandas",
4764
"openpyxl",
4865
"xlrd",
@@ -58,20 +75,16 @@
5875
"PIL.ImageFont",
5976
]
6077

61-
# Qt plugins to include
6278
QT_PLUGINS = [
6379
"platforms",
6480
"imageformats",
6581
]
6682

67-
# --- Main build function --------------------------------------------------
6883
def build_nuitka(console: bool = False, onefile: bool = False) -> None:
6984
"""Build the executable with Nuitka."""
70-
# Ensure directories exist
7185
DIST_DIR.mkdir(exist_ok=True)
7286
BUILD_DIR.mkdir(exist_ok=True)
7387

74-
# Base command
7588
cmd = [
7689
sys.executable,
7790
"-m",
@@ -82,68 +95,55 @@ def build_nuitka(console: bool = False, onefile: bool = False) -> None:
8295
f"--output-filename={NAME}.exe",
8396
]
8497

85-
# Console vs windowed
8698
if console:
8799
cmd.append("--console")
88100
else:
89101
cmd.append("--windows-disable-console")
90102

91-
# Onefile vs onedir
92103
if onefile:
93104
cmd.append("--onefile")
94105
# Note: --remove-output is not needed, Nuitka handles cleanup
95106

96-
# Include data files
97107
for src_path, dest_path in DATA_FILES:
98108
if src_path.exists():
99109
cmd.append(f"--include-data-file={src_path}={dest_path}")
100110
else:
101111
print(f"Warning: Data file not found: {src_path}")
102112

103-
# Hidden imports
104113
for module in HIDDEN_IMPORTS:
105114
cmd.append(f"--include-module={module}")
106115

107-
# Include Qt plugins
108116
for plugin in QT_PLUGINS:
109117
cmd.append(f"--include-qt-plugin={plugin}")
110118

111-
# Include PySide6 completely
112119
cmd.append("--follow-imports")
113120

114-
# Optimization flags
115121
cmd.extend([
116122
"--enable-plugin=pyside6",
117123
])
118124

119-
# Icon
120125
icon_path = ICONS_DIR / "logoneutral.ico"
121126
if icon_path.exists():
122127
cmd.append(f"--windows-icon-from-ico={icon_path}")
123128

124-
# Windows metadata
125129
cmd.extend([
126130
"--windows-company-name=Brain Modulation Lab",
127131
"--windows-product-name=Clinical DBS Annotator",
128-
"--windows-file-version=0.3.0",
129-
"--windows-product-version=0.3.0",
132+
f"--windows-file-version={BASE_VERSION}",
133+
f"--windows-product-version={BASE_VERSION}",
130134
])
131135

132-
# Entry point
133136
cmd.append(str(ENTRYPOINT))
134137

135-
# Print command for debugging
136138
print("Nuitka command:")
137139
print(" ".join(f'"{arg}"' if " " in str(arg) else str(arg) for arg in cmd))
138140
print("\nBuilding... (this may take several minutes)")
139141

140-
# Execute
141142
try:
142143
import subprocess
143144
subprocess.run(cmd, check=True, cwd=PROJECT_ROOT)
144145
print("\n✅ Build completed successfully!")
145146

146-
# Show output location
147147
if onefile:
148148
exe_path = DIST_DIR / f"{NAME}.exe"
149149
else:
@@ -162,7 +162,6 @@ def build_nuitka(console: bool = False, onefile: bool = False) -> None:
162162
print("\n❌ Nuitka not found. Install it with: pip install nuitka")
163163
sys.exit(1)
164164

165-
# -------------------------------------------------------------------------
166165
if __name__ == "__main__":
167166
parser = argparse.ArgumentParser(description="Build Windows executable with Nuitka")
168167
parser.add_argument("--console", action="store_true",

scripts/create_installer.nsi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@
55
; Download from: https://nsis.sourceforge.io/
66

77
!define APP_NAME "Clinical DBS Annotator"
8-
!define APP_VERSION "0.1.0"
8+
; Derive version from the Python package (single source of truth).
9+
!searchparse /file "..\src\clinical_dbs_annotator\__init__.py" '__version__ = "' APP_VERSION '"'
910
!define APP_PUBLISHER "BML"
10-
!define APP_EXE "ClinicalDBSAnnot_v0_1.exe"
11+
!define APP_EXE "ClinicalDBSAnnot_${APP_VERSION}.exe"
1112
!define INSTALL_DIR "$PROGRAMFILES\${APP_PUBLISHER}\${APP_NAME}"
1213

1314
; Includes
1415
!include "MUI2.nsh"
1516

1617
; General settings
1718
Name "${APP_NAME}"
18-
OutFile "..\dist\ClinicalDBSAnnot_Installer_v0.1.exe"
19+
OutFile "..\dist\ClinicalDBSAnnot_Installer_${APP_VERSION}.exe"
1920
InstallDir "${INSTALL_DIR}"
2021
InstallDirRegKey HKCU "Software\${APP_PUBLISHER}\${APP_NAME}" "InstallDir"
2122
RequestExecutionLevel admin

0 commit comments

Comments
 (0)