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.

NewPresentationDialog(course_dir: pathlib.Path, parent=None)
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()
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().

staticMetaObject = PySide6.QtCore.QMetaObject("NewPresentationDialog" inherits "QDialog": )