gui.dialogs.new_presentation_dialog
1import re 2from pathlib import Path 3 4from PySide6.QtWidgets import ( 5 QDialog, QVBoxLayout, QFormLayout, QHBoxLayout, 6 QLabel, QLineEdit, QDialogButtonBox, 7) 8from PySide6.QtCore import Qt 9 10from gui.theme import c 11from lib.i18n import _ 12 13 14class NewPresentationDialog(QDialog): 15 """Diálogo para crear un nuevo fichero YAML de presentación en el curso activo.""" 16 17 def __init__(self, course_dir: Path, parent=None): 18 super().__init__(parent) 19 self.setWindowTitle(_('Nueva presentación')) 20 self.setMinimumWidth(440) 21 self._course_dir = course_dir 22 self._build_ui() 23 self._suggest_defaults() 24 25 # ── Construcción ───────────────────────────────────────────────────────── 26 27 def _build_ui(self) -> None: 28 lay = QVBoxLayout(self) 29 lay.setContentsMargins(20, 16, 20, 16) 30 lay.setSpacing(12) 31 32 form = QFormLayout() 33 form.setSpacing(10) 34 35 self._portada = QLineEdit() 36 self._portada.setPlaceholderText(_('Título visible en la portada')) 37 form.addRow(_('Título:'), self._portada) 38 39 self._unidad = QLineEdit() 40 self._unidad.setPlaceholderText('Unidad 01') 41 form.addRow(_('Unidad:'), self._unidad) 42 43 output_row = QHBoxLayout() 44 self._output = QLineEdit() 45 self._output.setPlaceholderText('01_introduccion') 46 output_row.addWidget(self._output) 47 suffix = QLabel('.yaml') 48 suffix.setObjectName('hint') 49 output_row.addWidget(suffix) 50 hint = QLabel(_('La extensión .yaml se añade automáticamente')) 51 hint.setObjectName('hint') 52 form.addRow(_('Output:'), output_row) 53 form.addRow('', hint) 54 55 lay.addLayout(form) 56 57 self._preview = QLabel() 58 self._preview.setObjectName('hint') 59 self._preview.setAlignment(Qt.AlignmentFlag.AlignCenter) 60 lay.addWidget(self._preview) 61 62 bbox = QDialogButtonBox( 63 QDialogButtonBox.StandardButton.Ok | 64 QDialogButtonBox.StandardButton.Cancel 65 ) 66 bbox.accepted.connect(self._validate_and_accept) 67 bbox.rejected.connect(self.reject) 68 lay.addWidget(bbox) 69 70 self._portada.textChanged.connect(self._update_preview) 71 self._output.textChanged.connect(self._update_preview) 72 73 # ── Lógica ──────────────────────────────────────────────────────────────── 74 75 def _suggest_defaults(self) -> None: 76 """Sugiere número de unidad y prefijo basándose en los YAMLs existentes.""" 77 existing = sorted((self._course_dir / 'content').glob('*.yaml')) 78 n = len(existing) 79 num = f'{n:02d}' 80 self._unidad.setText(f'Unidad {num}') 81 self._output.setText(f'{num}_nueva_presentacion') 82 self._update_preview() 83 84 def _update_preview(self) -> None: 85 output = self._output.text().strip() 86 course = self._course_dir.name 87 if output: 88 filename = f'{course}_{output}.yaml' 89 self._preview.setText(f'→ content/{filename}') 90 else: 91 self._preview.setText('') 92 93 def _validate_and_accept(self) -> None: 94 portada = self._portada.text().strip() 95 unidad = self._unidad.text().strip() 96 output = self._output.text().strip() 97 98 if not portada: 99 self._portada.setFocus() 100 return 101 if not unidad: 102 self._unidad.setFocus() 103 return 104 if not output: 105 self._output.setFocus() 106 return 107 108 # Limpiar output: sin espacios ni caracteres problemáticos 109 output_clean = re.sub(r'[^a-zA-Z0-9_\-]', '_', output).strip('_') 110 if not output_clean: 111 self._output.setFocus() 112 return 113 self._output.setText(output_clean) 114 115 dest = self._course_dir / 'content' / f'{self._course_dir.name}_{output_clean}.yaml' 116 if dest.exists(): 117 self._preview.setText( 118 f'⚠ {dest.name} ya existe' 119 ) 120 return 121 122 self.accept() 123 124 # ── API pública ──────────────────────────────────────────────────────────── 125 126 def create_yaml(self) -> Path: 127 """Crea el fichero YAML y devuelve su ruta. Llamar solo tras accept().""" 128 course = self._course_dir.name 129 portada = self._portada.text().strip() 130 unidad = self._unidad.text().strip() 131 output = self._output.text().strip() 132 dest = self._course_dir / 'content' / f'{course}_{output}.yaml' 133 134 dest.parent.mkdir(parents=True, exist_ok=True) 135 dest.write_text( 136 f'meta:\n' 137 f' portada: "{portada}"\n' 138 f' unidad: "{unidad}"\n' 139 f' output: "{output}"\n' 140 f'\n' 141 f'objetivos:\n' 142 f' - "Objetivo 1"\n' 143 f'\n' 144 f'indice:\n' 145 f' - "Sección 1"\n' 146 f'\n' 147 f'secciones:\n' 148 f' - indice: 1\n' 149 f' slides:\n' 150 f' - tipo: texto\n' 151 f' texto: "Texto introductorio."\n' 152 f'\n' 153 f'conclusion:\n' 154 f' - "Conclusión principal"\n' 155 f'\n' 156 f'referencias:\n' 157 f' - ["Recurso", "https://ejemplo.com"]\n', 158 encoding='utf-8', 159 ) 160 return dest
class
NewPresentationDialog(PySide6.QtWidgets.QDialog):
15class NewPresentationDialog(QDialog): 16 """Diálogo para crear un nuevo fichero YAML de presentación en el curso activo.""" 17 18 def __init__(self, course_dir: Path, parent=None): 19 super().__init__(parent) 20 self.setWindowTitle(_('Nueva presentación')) 21 self.setMinimumWidth(440) 22 self._course_dir = course_dir 23 self._build_ui() 24 self._suggest_defaults() 25 26 # ── Construcción ───────────────────────────────────────────────────────── 27 28 def _build_ui(self) -> None: 29 lay = QVBoxLayout(self) 30 lay.setContentsMargins(20, 16, 20, 16) 31 lay.setSpacing(12) 32 33 form = QFormLayout() 34 form.setSpacing(10) 35 36 self._portada = QLineEdit() 37 self._portada.setPlaceholderText(_('Título visible en la portada')) 38 form.addRow(_('Título:'), self._portada) 39 40 self._unidad = QLineEdit() 41 self._unidad.setPlaceholderText('Unidad 01') 42 form.addRow(_('Unidad:'), self._unidad) 43 44 output_row = QHBoxLayout() 45 self._output = QLineEdit() 46 self._output.setPlaceholderText('01_introduccion') 47 output_row.addWidget(self._output) 48 suffix = QLabel('.yaml') 49 suffix.setObjectName('hint') 50 output_row.addWidget(suffix) 51 hint = QLabel(_('La extensión .yaml se añade automáticamente')) 52 hint.setObjectName('hint') 53 form.addRow(_('Output:'), output_row) 54 form.addRow('', hint) 55 56 lay.addLayout(form) 57 58 self._preview = QLabel() 59 self._preview.setObjectName('hint') 60 self._preview.setAlignment(Qt.AlignmentFlag.AlignCenter) 61 lay.addWidget(self._preview) 62 63 bbox = QDialogButtonBox( 64 QDialogButtonBox.StandardButton.Ok | 65 QDialogButtonBox.StandardButton.Cancel 66 ) 67 bbox.accepted.connect(self._validate_and_accept) 68 bbox.rejected.connect(self.reject) 69 lay.addWidget(bbox) 70 71 self._portada.textChanged.connect(self._update_preview) 72 self._output.textChanged.connect(self._update_preview) 73 74 # ── Lógica ──────────────────────────────────────────────────────────────── 75 76 def _suggest_defaults(self) -> None: 77 """Sugiere número de unidad y prefijo basándose en los YAMLs existentes.""" 78 existing = sorted((self._course_dir / 'content').glob('*.yaml')) 79 n = len(existing) 80 num = f'{n:02d}' 81 self._unidad.setText(f'Unidad {num}') 82 self._output.setText(f'{num}_nueva_presentacion') 83 self._update_preview() 84 85 def _update_preview(self) -> None: 86 output = self._output.text().strip() 87 course = self._course_dir.name 88 if output: 89 filename = f'{course}_{output}.yaml' 90 self._preview.setText(f'→ content/{filename}') 91 else: 92 self._preview.setText('') 93 94 def _validate_and_accept(self) -> None: 95 portada = self._portada.text().strip() 96 unidad = self._unidad.text().strip() 97 output = self._output.text().strip() 98 99 if not portada: 100 self._portada.setFocus() 101 return 102 if not unidad: 103 self._unidad.setFocus() 104 return 105 if not output: 106 self._output.setFocus() 107 return 108 109 # Limpiar output: sin espacios ni caracteres problemáticos 110 output_clean = re.sub(r'[^a-zA-Z0-9_\-]', '_', output).strip('_') 111 if not output_clean: 112 self._output.setFocus() 113 return 114 self._output.setText(output_clean) 115 116 dest = self._course_dir / 'content' / f'{self._course_dir.name}_{output_clean}.yaml' 117 if dest.exists(): 118 self._preview.setText( 119 f'⚠ {dest.name} ya existe' 120 ) 121 return 122 123 self.accept() 124 125 # ── API pública ──────────────────────────────────────────────────────────── 126 127 def create_yaml(self) -> Path: 128 """Crea el fichero YAML y devuelve su ruta. Llamar solo tras accept().""" 129 course = self._course_dir.name 130 portada = self._portada.text().strip() 131 unidad = self._unidad.text().strip() 132 output = self._output.text().strip() 133 dest = self._course_dir / 'content' / f'{course}_{output}.yaml' 134 135 dest.parent.mkdir(parents=True, exist_ok=True) 136 dest.write_text( 137 f'meta:\n' 138 f' portada: "{portada}"\n' 139 f' unidad: "{unidad}"\n' 140 f' output: "{output}"\n' 141 f'\n' 142 f'objetivos:\n' 143 f' - "Objetivo 1"\n' 144 f'\n' 145 f'indice:\n' 146 f' - "Sección 1"\n' 147 f'\n' 148 f'secciones:\n' 149 f' - indice: 1\n' 150 f' slides:\n' 151 f' - tipo: texto\n' 152 f' texto: "Texto introductorio."\n' 153 f'\n' 154 f'conclusion:\n' 155 f' - "Conclusión principal"\n' 156 f'\n' 157 f'referencias:\n' 158 f' - ["Recurso", "https://ejemplo.com"]\n', 159 encoding='utf-8', 160 ) 161 return dest
Diálogo para crear un nuevo fichero YAML de presentación en el curso activo.
def
create_yaml(self) -> pathlib.Path:
127 def create_yaml(self) -> Path: 128 """Crea el fichero YAML y devuelve su ruta. Llamar solo tras accept().""" 129 course = self._course_dir.name 130 portada = self._portada.text().strip() 131 unidad = self._unidad.text().strip() 132 output = self._output.text().strip() 133 dest = self._course_dir / 'content' / f'{course}_{output}.yaml' 134 135 dest.parent.mkdir(parents=True, exist_ok=True) 136 dest.write_text( 137 f'meta:\n' 138 f' portada: "{portada}"\n' 139 f' unidad: "{unidad}"\n' 140 f' output: "{output}"\n' 141 f'\n' 142 f'objetivos:\n' 143 f' - "Objetivo 1"\n' 144 f'\n' 145 f'indice:\n' 146 f' - "Sección 1"\n' 147 f'\n' 148 f'secciones:\n' 149 f' - indice: 1\n' 150 f' slides:\n' 151 f' - tipo: texto\n' 152 f' texto: "Texto introductorio."\n' 153 f'\n' 154 f'conclusion:\n' 155 f' - "Conclusión principal"\n' 156 f'\n' 157 f'referencias:\n' 158 f' - ["Recurso", "https://ejemplo.com"]\n', 159 encoding='utf-8', 160 ) 161 return dest
Crea el fichero YAML y devuelve su ruta. Llamar solo tras accept().