Photo-Composition-Designer 0.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Photo_Composition_Designer/__init__.py +0 -0
- Photo_Composition_Designer/__main__.py +8 -0
- Photo_Composition_Designer/_version.py +34 -0
- Photo_Composition_Designer/cli/__init__.py +0 -0
- Photo_Composition_Designer/cli/__main__.py +8 -0
- Photo_Composition_Designer/cli/cli.py +106 -0
- Photo_Composition_Designer/common/Anniversaries.py +93 -0
- Photo_Composition_Designer/common/Locations.py +87 -0
- Photo_Composition_Designer/common/MoonPhase.py +85 -0
- Photo_Composition_Designer/common/Photo.py +113 -0
- Photo_Composition_Designer/common/__init__.py +0 -0
- Photo_Composition_Designer/common/logging.py +216 -0
- Photo_Composition_Designer/config/__init__.py +0 -0
- Photo_Composition_Designer/config/config.py +321 -0
- Photo_Composition_Designer/core/__init__.py +0 -0
- Photo_Composition_Designer/core/base.py +383 -0
- Photo_Composition_Designer/gui/GuiLogWriter.py +79 -0
- Photo_Composition_Designer/gui/__init__.py +0 -0
- Photo_Composition_Designer/gui/__main__.py +8 -0
- Photo_Composition_Designer/gui/gui.py +565 -0
- Photo_Composition_Designer/image/CalendarRenderer.py +319 -0
- Photo_Composition_Designer/image/CollageRenderer.py +433 -0
- Photo_Composition_Designer/image/DescriptionRenderer.py +74 -0
- Photo_Composition_Designer/image/MapRenderer.py +101 -0
- Photo_Composition_Designer/image/__init__.py +0 -0
- Photo_Composition_Designer/tools/DescriptionsFileGenerator.py +44 -0
- Photo_Composition_Designer/tools/GeoPlotter.py +211 -0
- Photo_Composition_Designer/tools/Helpers.py +18 -0
- Photo_Composition_Designer/tools/ImageDistributor.py +153 -0
- Photo_Composition_Designer/tools/__init__.py +0 -0
- __init__.py +0 -0
- firewall_handler.py +198 -0
- main.py +146 -0
- path_handler.py +10 -0
- photo_composition_designer-0.0.7.dist-info/METADATA +205 -0
- photo_composition_designer-0.0.7.dist-info/RECORD +40 -0
- photo_composition_designer-0.0.7.dist-info/WHEEL +5 -0
- photo_composition_designer-0.0.7.dist-info/entry_points.txt +3 -0
- photo_composition_designer-0.0.7.dist-info/licenses/LICENSE +24 -0
- photo_composition_designer-0.0.7.dist-info/top_level.txt +5 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Centralized logging configuration for Photo_Composition_Designer.
|
|
2
|
+
|
|
3
|
+
This module provides a unified logging setup that supports:
|
|
4
|
+
- File logging with rotation
|
|
5
|
+
- GUI integration via custom handler
|
|
6
|
+
- Configurable log levels from config
|
|
7
|
+
- Structured logging with consistent formatting
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import logging.handlers
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from Photo_Composition_Designer.config.config import ConfigParameterManager
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GuiLogHandler(logging.Handler):
|
|
19
|
+
"""Custom logging handler that can write to GUI text widgets."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, gui_writer=None):
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.gui_writer = gui_writer
|
|
24
|
+
self.setFormatter(
|
|
25
|
+
logging.Formatter("%(asctime)s - %(levelname)s - %(message)s", datefmt="%H:%M:%S")
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def emit(self, record):
|
|
29
|
+
"""Emit a log record to the GUI if writer is available."""
|
|
30
|
+
if self.gui_writer:
|
|
31
|
+
try:
|
|
32
|
+
msg = self.format(record) + "\n"
|
|
33
|
+
self.gui_writer.write(msg)
|
|
34
|
+
except Exception:
|
|
35
|
+
# Fail silently to avoid recursive logging errors
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LoggerManager:
|
|
40
|
+
"""Manages all logging configuration and handlers."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, config: ConfigParameterManager):
|
|
43
|
+
self.config = config
|
|
44
|
+
self.logger = logging.getLogger("Photo_Composition_Designer")
|
|
45
|
+
self.gui_handler = None
|
|
46
|
+
self.file_handler = None
|
|
47
|
+
self.console_handler = None
|
|
48
|
+
self._setup_logging()
|
|
49
|
+
|
|
50
|
+
def _setup_logging(self):
|
|
51
|
+
"""Configure all logging handlers and formatters."""
|
|
52
|
+
# Clear any existing handlers
|
|
53
|
+
self.logger.handlers.clear()
|
|
54
|
+
|
|
55
|
+
# Set log level from config
|
|
56
|
+
log_level = getattr(logging, self.config.app.log_level.value.upper())
|
|
57
|
+
self.logger.setLevel(log_level)
|
|
58
|
+
|
|
59
|
+
# Create formatters
|
|
60
|
+
detailed_formatter = logging.Formatter(
|
|
61
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
|
|
62
|
+
)
|
|
63
|
+
simple_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
|
64
|
+
|
|
65
|
+
# Setup file handler with rotation
|
|
66
|
+
self._setup_file_handler(detailed_formatter)
|
|
67
|
+
|
|
68
|
+
# Setup console handler
|
|
69
|
+
self._setup_console_handler(simple_formatter)
|
|
70
|
+
|
|
71
|
+
# Setup GUI handler (will be connected later if needed)
|
|
72
|
+
self._setup_gui_handler()
|
|
73
|
+
|
|
74
|
+
def _setup_file_handler(self, formatter):
|
|
75
|
+
"""Setup rotating file handler."""
|
|
76
|
+
log_dir = Path("logs")
|
|
77
|
+
log_dir.mkdir(exist_ok=True)
|
|
78
|
+
|
|
79
|
+
log_file = log_dir / "Photo_Composition_Designer.log"
|
|
80
|
+
|
|
81
|
+
# Use RotatingFileHandler to prevent huge log files
|
|
82
|
+
self.file_handler = logging.handlers.RotatingFileHandler(
|
|
83
|
+
log_file,
|
|
84
|
+
maxBytes=10 * 1024 * 1024, # 10MB
|
|
85
|
+
backupCount=5,
|
|
86
|
+
encoding="utf-8",
|
|
87
|
+
)
|
|
88
|
+
self.file_handler.setFormatter(formatter)
|
|
89
|
+
self.logger.addHandler(self.file_handler)
|
|
90
|
+
|
|
91
|
+
def _setup_console_handler(self, formatter):
|
|
92
|
+
"""Setup console handler for CLI output."""
|
|
93
|
+
self.console_handler = logging.StreamHandler(sys.stdout)
|
|
94
|
+
self.console_handler.setFormatter(formatter)
|
|
95
|
+
self.logger.addHandler(self.console_handler)
|
|
96
|
+
|
|
97
|
+
def _setup_gui_handler(self):
|
|
98
|
+
"""Setup GUI handler (initially without writer)."""
|
|
99
|
+
self.gui_handler = GuiLogHandler()
|
|
100
|
+
# Don't add to logger yet - will be done when GUI connects
|
|
101
|
+
|
|
102
|
+
def connect_gui_writer(self, gui_writer):
|
|
103
|
+
"""Connect a GUI writer to the logging system.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
gui_writer: Object with write() method (like the LogHandler from gui.py)
|
|
107
|
+
"""
|
|
108
|
+
if self.gui_handler:
|
|
109
|
+
# Remove old handler if it exists
|
|
110
|
+
if self.gui_handler in self.logger.handlers:
|
|
111
|
+
self.logger.removeHandler(self.gui_handler)
|
|
112
|
+
|
|
113
|
+
# Create new handler with GUI writer
|
|
114
|
+
self.gui_handler = GuiLogHandler(gui_writer)
|
|
115
|
+
self.logger.addHandler(self.gui_handler)
|
|
116
|
+
|
|
117
|
+
def disconnect_gui_writer(self):
|
|
118
|
+
"""Disconnect GUI writer (useful when GUI closes)."""
|
|
119
|
+
if self.gui_handler and self.gui_handler in self.logger.handlers:
|
|
120
|
+
self.logger.removeHandler(self.gui_handler)
|
|
121
|
+
|
|
122
|
+
def get_logger(self, name: str = None) -> logging.Logger:
|
|
123
|
+
"""Get a logger instance.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
name: Logger name (defaults to main project logger)
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Logger instance
|
|
130
|
+
"""
|
|
131
|
+
if name:
|
|
132
|
+
return logging.getLogger(f"Photo_Composition_Designer.{name}")
|
|
133
|
+
return self.logger
|
|
134
|
+
|
|
135
|
+
def set_log_level(self, level: str):
|
|
136
|
+
"""Change log level dynamically.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
level: New log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
140
|
+
"""
|
|
141
|
+
log_level = getattr(logging, level.upper())
|
|
142
|
+
self.logger.setLevel(log_level)
|
|
143
|
+
|
|
144
|
+
# Update config
|
|
145
|
+
self.config.app.log_level.value = level.upper()
|
|
146
|
+
|
|
147
|
+
def log_config_summary(self):
|
|
148
|
+
"""Log current configuration summary."""
|
|
149
|
+
self.logger.info("=== Configuration Summary ===")
|
|
150
|
+
self.logger.info(f"Log level: {self.config.app.log_level.value}")
|
|
151
|
+
self.logger.info("==============================")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# Global logger manager instance
|
|
155
|
+
_logger_manager = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def initialize_logging(config: ConfigParameterManager) -> LoggerManager:
|
|
159
|
+
"""Initialize the global logging system.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
config: Configuration manager instance
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
LoggerManager instance
|
|
166
|
+
"""
|
|
167
|
+
global _logger_manager
|
|
168
|
+
_logger_manager = LoggerManager(config)
|
|
169
|
+
return _logger_manager
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def get_logger(name: str = None) -> logging.Logger:
|
|
173
|
+
"""Get a logger instance.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
name: Logger name (optional)
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Logger instance
|
|
180
|
+
|
|
181
|
+
Raises:
|
|
182
|
+
RuntimeError: If logging not initialized
|
|
183
|
+
"""
|
|
184
|
+
if _logger_manager is None:
|
|
185
|
+
raise RuntimeError("Logging not initialized. Call initialize_logging() first.")
|
|
186
|
+
return _logger_manager.get_logger(name)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def get_logger_manager() -> LoggerManager:
|
|
190
|
+
"""Get the global logger manager.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
LoggerManager instance
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
RuntimeError: If logging not initialized
|
|
197
|
+
"""
|
|
198
|
+
if _logger_manager is None:
|
|
199
|
+
raise RuntimeError("Logging not initialized. Call initialize_logging() first.")
|
|
200
|
+
return _logger_manager
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def connect_gui_logging(gui_writer):
|
|
204
|
+
"""Connect GUI writer to logging system.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
gui_writer: GUI writer object with write() method
|
|
208
|
+
"""
|
|
209
|
+
if _logger_manager:
|
|
210
|
+
_logger_manager.connect_gui_writer(gui_writer)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def disconnect_gui_logging():
|
|
214
|
+
"""Disconnect GUI from logging system."""
|
|
215
|
+
if _logger_manager:
|
|
216
|
+
_logger_manager.disconnect_gui_writer()
|
|
File without changes
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Central configuration management for the new project.
|
|
3
|
+
|
|
4
|
+
This module provides a single source of truth for all configuration parameters
|
|
5
|
+
organized in categories (GENERAL, CALENDAR, COLORS, GEO, SIZE, LAYOUT).
|
|
6
|
+
It can generate config files, CLI modules, and documentation from the parameter definitions.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from config_cli_gui.config import Color, ConfigCategory, ConfigManager, ConfigParameter
|
|
13
|
+
from config_cli_gui.docs import DocumentationGenerator
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AppConfig(ConfigCategory):
|
|
17
|
+
"""Application-specific configuration parameters."""
|
|
18
|
+
|
|
19
|
+
def get_category_name(self) -> str:
|
|
20
|
+
return "app"
|
|
21
|
+
|
|
22
|
+
log_level: ConfigParameter = ConfigParameter(
|
|
23
|
+
name="log_level",
|
|
24
|
+
value="INFO",
|
|
25
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
26
|
+
help="Logging level for the application",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class GeneralConfig(ConfigCategory):
|
|
31
|
+
"""GENERAL configuration parameters."""
|
|
32
|
+
|
|
33
|
+
def get_category_name(self) -> str:
|
|
34
|
+
return "general"
|
|
35
|
+
|
|
36
|
+
photoDirectory: ConfigParameter = ConfigParameter(
|
|
37
|
+
name="photoDirectory",
|
|
38
|
+
value=Path("images"),
|
|
39
|
+
help="Path to the directory containing photos "
|
|
40
|
+
"(absolute, or relative to this config.ini file)",
|
|
41
|
+
is_cli=True,
|
|
42
|
+
required=True,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
anniversariesConfig: ConfigParameter = ConfigParameter(
|
|
46
|
+
name="anniversariesConfig",
|
|
47
|
+
value=Path("anniversaries.ini"),
|
|
48
|
+
help="Path to anniversaries.ini file (absolute, or relative to this config.ini file)",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
locationsConfig: ConfigParameter = ConfigParameter(
|
|
52
|
+
name="locationsConfig",
|
|
53
|
+
value=Path("locations_en.ini"),
|
|
54
|
+
help="Path to locations.ini file (absolute, or relative to this config.ini file)",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
compositionTitle: ConfigParameter = ConfigParameter(
|
|
58
|
+
name="compositionTitle",
|
|
59
|
+
value="This is the title of the composition",
|
|
60
|
+
help="This is the title of the composition on the first page. Leave empty if not required.",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CalendarConfig(ConfigCategory):
|
|
65
|
+
"""CALENDAR configuration parameters."""
|
|
66
|
+
|
|
67
|
+
def get_category_name(self) -> str:
|
|
68
|
+
return "calendar"
|
|
69
|
+
|
|
70
|
+
useCalendar: ConfigParameter = ConfigParameter(
|
|
71
|
+
name="useCalendar",
|
|
72
|
+
value=True,
|
|
73
|
+
help="True: Calendar elements are generated",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
language: ConfigParameter = ConfigParameter(
|
|
77
|
+
name="language",
|
|
78
|
+
value="de_DE",
|
|
79
|
+
help="Language for the calendar (e.g., de_DE, en_US)",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
holidayCountries: ConfigParameter = ConfigParameter(
|
|
83
|
+
name="holidayCountries",
|
|
84
|
+
value="SN",
|
|
85
|
+
help="Country/state codes for public holidays, e.g., NY,CA",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
startDate: ConfigParameter = ConfigParameter(
|
|
89
|
+
name="startDate",
|
|
90
|
+
value=datetime.fromisoformat("2025-12-31"),
|
|
91
|
+
help="Start date of the calendar",
|
|
92
|
+
is_cli=True,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
collagesToGenerate: ConfigParameter = ConfigParameter(
|
|
96
|
+
name="collagesToGenerate",
|
|
97
|
+
value=5,
|
|
98
|
+
help="Number of collages to be generated (e.g. number of weeks)",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ColorsConfig(ConfigCategory):
|
|
103
|
+
"""COLORS configuration parameters."""
|
|
104
|
+
|
|
105
|
+
def get_category_name(self) -> str:
|
|
106
|
+
return "colors"
|
|
107
|
+
|
|
108
|
+
backgroundColor: ConfigParameter = ConfigParameter(
|
|
109
|
+
name="backgroundColor",
|
|
110
|
+
value=Color(20, 20, 20),
|
|
111
|
+
help="Background color (RGB)",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
textColor1: ConfigParameter = ConfigParameter(
|
|
115
|
+
name="textColor1",
|
|
116
|
+
value=Color(255, 255, 255),
|
|
117
|
+
help="Primary text color",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
textColor2: ConfigParameter = ConfigParameter(
|
|
121
|
+
name="textColor2",
|
|
122
|
+
value=Color(150, 150, 150),
|
|
123
|
+
help="Secondary text color",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
holidayColor: ConfigParameter = ConfigParameter(
|
|
127
|
+
name="holidayColor",
|
|
128
|
+
value=Color(255, 0, 0),
|
|
129
|
+
help="Color for holidays",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class GeoConfig(ConfigCategory):
|
|
134
|
+
"""GEO configuration parameters."""
|
|
135
|
+
|
|
136
|
+
def get_category_name(self) -> str:
|
|
137
|
+
return "geo"
|
|
138
|
+
|
|
139
|
+
usePhotoLocationMaps: ConfigParameter = ConfigParameter(
|
|
140
|
+
name="usePhotoLocationMaps",
|
|
141
|
+
value=True,
|
|
142
|
+
help="Use GPS data to generate maps",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
minimalExtension: ConfigParameter = ConfigParameter(
|
|
146
|
+
name="minimalExtension",
|
|
147
|
+
value=7,
|
|
148
|
+
help="Minimum range for map display (degrees)",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class SizeConfig(ConfigCategory):
|
|
153
|
+
"""SIZE configuration parameters."""
|
|
154
|
+
|
|
155
|
+
def get_category_name(self) -> str:
|
|
156
|
+
return "size"
|
|
157
|
+
|
|
158
|
+
width: ConfigParameter = ConfigParameter(
|
|
159
|
+
name="width",
|
|
160
|
+
value=216,
|
|
161
|
+
help="Width of the collage in mm",
|
|
162
|
+
is_cli=True,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
height: ConfigParameter = ConfigParameter(
|
|
166
|
+
name="height",
|
|
167
|
+
value=154,
|
|
168
|
+
help="Height of the collage in mm",
|
|
169
|
+
is_cli=True,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
calendarHeight: ConfigParameter = ConfigParameter(
|
|
173
|
+
name="calendarHeight",
|
|
174
|
+
value=18,
|
|
175
|
+
help="Height of the calendar area in mm",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
mapWidth: ConfigParameter = ConfigParameter(
|
|
179
|
+
name="mapWidth",
|
|
180
|
+
value=20,
|
|
181
|
+
help="Width of the locations map in mm",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
mapHeight: ConfigParameter = ConfigParameter(
|
|
185
|
+
name="mapHeight",
|
|
186
|
+
value=20,
|
|
187
|
+
help="Height of the locations map in mm",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
dpi: ConfigParameter = ConfigParameter(
|
|
191
|
+
name="dpi",
|
|
192
|
+
value=150,
|
|
193
|
+
help="Resolution of the image in dpi",
|
|
194
|
+
is_cli=True,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
jpgQuality: ConfigParameter = ConfigParameter(
|
|
198
|
+
name="jpgQuality",
|
|
199
|
+
value=90,
|
|
200
|
+
help="JPG compression quality (1-100)",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class LayoutConfig(ConfigCategory):
|
|
205
|
+
"""LAYOUT configuration parameters."""
|
|
206
|
+
|
|
207
|
+
def get_category_name(self) -> str:
|
|
208
|
+
return "layout"
|
|
209
|
+
|
|
210
|
+
fontSizeLarge: ConfigParameter = ConfigParameter(
|
|
211
|
+
name="fontSizeLarge",
|
|
212
|
+
value=0.5,
|
|
213
|
+
help="Font size for large text",
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
fontSizeSmall: ConfigParameter = ConfigParameter(
|
|
217
|
+
name="fontSizeSmall",
|
|
218
|
+
value=0.14,
|
|
219
|
+
help="Font size for small text",
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
fontSizeAnniversaries: ConfigParameter = ConfigParameter(
|
|
223
|
+
name="fontSizeAnniversaries",
|
|
224
|
+
value=0.115,
|
|
225
|
+
help="Font size for anniversaries",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
marginTop: ConfigParameter = ConfigParameter(
|
|
229
|
+
name="marginTop",
|
|
230
|
+
value=6,
|
|
231
|
+
help="Top margin in mm",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
marginBottom: ConfigParameter = ConfigParameter(
|
|
235
|
+
name="marginBottom",
|
|
236
|
+
value=3,
|
|
237
|
+
help="Bottom margin in mm",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
marginSides: ConfigParameter = ConfigParameter(
|
|
241
|
+
name="marginSides",
|
|
242
|
+
value=3,
|
|
243
|
+
help="Side margins in mm",
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
spacing: ConfigParameter = ConfigParameter(
|
|
247
|
+
name="spacing",
|
|
248
|
+
value=2,
|
|
249
|
+
help="Spacing between elements in mm",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
useShortDayNames: ConfigParameter = ConfigParameter(
|
|
253
|
+
name="useShortDayNames",
|
|
254
|
+
value=False,
|
|
255
|
+
help="Use short weekday names (e.g., Mon, Tue)",
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
useShortMonthNames: ConfigParameter = ConfigParameter(
|
|
259
|
+
name="useShortMonthNames",
|
|
260
|
+
value=True,
|
|
261
|
+
help="Use short month names (e.g., Jan, Feb)",
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
usePhotoDescription: ConfigParameter = ConfigParameter(
|
|
265
|
+
name="usePhotoDescription",
|
|
266
|
+
value=True,
|
|
267
|
+
help="Include photo descriptions in the collage",
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
generatePdf: ConfigParameter = ConfigParameter(
|
|
271
|
+
name="generatePdf",
|
|
272
|
+
value=True,
|
|
273
|
+
help="Combine all generated collages into one pdf",
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class ConfigParameterManager(ConfigManager):
|
|
278
|
+
"""Main configuration manager that handles all parameter categories."""
|
|
279
|
+
|
|
280
|
+
app: AppConfig
|
|
281
|
+
general: GeneralConfig
|
|
282
|
+
calendar: CalendarConfig
|
|
283
|
+
colors: ColorsConfig
|
|
284
|
+
geo: GeoConfig
|
|
285
|
+
size: SizeConfig
|
|
286
|
+
layout: LayoutConfig
|
|
287
|
+
|
|
288
|
+
def __init__(self, config_file: str | None = None, **kwargs):
|
|
289
|
+
categories = (
|
|
290
|
+
AppConfig(),
|
|
291
|
+
GeneralConfig(),
|
|
292
|
+
CalendarConfig(),
|
|
293
|
+
ColorsConfig(),
|
|
294
|
+
GeoConfig(),
|
|
295
|
+
SizeConfig(),
|
|
296
|
+
LayoutConfig(),
|
|
297
|
+
)
|
|
298
|
+
super().__init__(categories, config_file, **kwargs)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def main():
|
|
302
|
+
"""Main function to generate config file and documentation."""
|
|
303
|
+
default_config: str = "config.yaml"
|
|
304
|
+
default_cli_doc: str = "docs/usage/cli.md"
|
|
305
|
+
default_config_doc: str = "docs/usage/config.md"
|
|
306
|
+
|
|
307
|
+
config_manager = ConfigParameterManager()
|
|
308
|
+
docGen = DocumentationGenerator(config_manager)
|
|
309
|
+
|
|
310
|
+
docGen.generate_default_config_file(output_file=default_config)
|
|
311
|
+
print(f"Generated: {default_config}")
|
|
312
|
+
|
|
313
|
+
docGen.generate_config_markdown_doc(output_file=default_config_doc)
|
|
314
|
+
print(f"Generated: {default_config_doc}")
|
|
315
|
+
|
|
316
|
+
docGen.generate_cli_markdown_doc(output_file=default_cli_doc)
|
|
317
|
+
print(f"Generated: {default_cli_doc}")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == "__main__":
|
|
321
|
+
main()
|
|
File without changes
|