feat: caiete experiențiale M1-M5 + prompt reutilizabil + ghid facilitare + md_to_pdf
Adaugă al 4-lea nivel de sumarizare: caiete de facilitator cu corelații
cross-disciplinare (IFS, CNV, attachment theory, Brené Brown, neuroștiință),
exerciții cu script complet, și ghiduri de aplicare cu secvențe de cadrare
pentru 4 contexte (grup NLP, cercetași, CNV, self-coaching).
Fișiere noi (în summaries/, ne-tracked de .gitignore):
- 5 caiete: MODUL{1-5}_{CONCEPT}.md
- 5 aplicări: MODUL{1-5}_{CONCEPT}_APLICARI.md
- 5 cross-modul: INDEX_EXERCITII, HARTA_CONEXIUNI, GLOSAR_CREDINTE,
GHID_FACILITARE, METAFORE_POVESTI
Tracked:
- PROMPT_EXPERIENTIAL.md: prompt reutilizabil pentru M6
- TODOS.md: instrucțiuni complete pentru M6 (run.bat 6 + sumarizări + caiet)
- md_to_pdf.py: convertor markdown→PDF pentru summaries
- PROCES_SUMARIZARE.md: documentație proces sumarizare standard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -32,6 +32,7 @@ audio_wav/
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
.venv/
|
.venv/
|
||||||
|
.venv_pdf/
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ python transcribe.py --modules 1 # transcribe module 1 only
|
|||||||
python summarize.py # print summary prompts to stdout
|
python summarize.py # print summary prompts to stdout
|
||||||
python summarize.py --compile # compile SUPORT_CURS.md from existing summaries
|
python summarize.py --compile # compile SUPORT_CURS.md from existing summaries
|
||||||
|
|
||||||
|
# MD → PDF (from WSL2, uses .venv_pdf)
|
||||||
|
.venv_pdf/bin/python md_to_pdf.py # all MODUL*_*.md → summaries/pdf/
|
||||||
|
.venv_pdf/bin/python md_to_pdf.py --modules 1-3 # specific modules
|
||||||
|
.venv_pdf/bin/python md_to_pdf.py summaries/X.md # specific file
|
||||||
|
|
||||||
# Setup components individually
|
# Setup components individually
|
||||||
python setup_whisper.py whisper # download whisper.cpp binary
|
python setup_whisper.py whisper # download whisper.cpp binary
|
||||||
python setup_whisper.py model # download Whisper model
|
python setup_whisper.py model # download Whisper model
|
||||||
|
|||||||
49
PROCES_SUMARIZARE.md
Normal file
49
PROCES_SUMARIZARE.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Proces Sumarizare Module NLP Master
|
||||||
|
|
||||||
|
## Scop
|
||||||
|
Generare notițe de recapitulare pentru testarea finală, din transcrierile audio ale cursului NLP Master Practitioner.
|
||||||
|
|
||||||
|
## Output — 3 fișiere per modul
|
||||||
|
|
||||||
|
| Fișier | Conținut | Uz |
|
||||||
|
|--------|----------|----|
|
||||||
|
| `summaries/MODUL{N}_EXHAUSTIV.md` | Toate conceptele, tehnicile pas-cu-pas, exercițiile cu instrucțiuni, exemple, metafore, citate, referințe bibliografice, mindmap text | Studiu aprofundat |
|
||||||
|
| `summaries/MODUL{N}_CONCENTRAT.md` | Concepte cheie + tehnici principale + mindmap condensat (fără anecdote/exemple extinse) | Recapitulare rapidă |
|
||||||
|
| `summaries/MODUL{N}_CHEATSHEET.md` | Ultra-condensat: termeni + definiții scurte + tehnici într-o linie | Revisie înainte de test |
|
||||||
|
|
||||||
|
## Pași
|
||||||
|
|
||||||
|
### 1. Identifică transcrierile
|
||||||
|
```
|
||||||
|
transcripts/Master 25M{N} Z{zi}{parte}.txt
|
||||||
|
```
|
||||||
|
- Ziua 1: Z1A, Z1B, Z1C, Z1D (4 părți)
|
||||||
|
- Ziua 2: Z2A, Z2B, Z2C (3 părți)
|
||||||
|
- Numărul de părți poate varia per modul
|
||||||
|
|
||||||
|
### 2. Extragere exhaustivă (per fișier transcript)
|
||||||
|
Din fiecare transcript se extrag:
|
||||||
|
1. **Concepte și modele NLP** — cu explicații
|
||||||
|
2. **Tehnici/instrumente** — pași detaliați dacă sunt descriși
|
||||||
|
3. **Exerciții** — cu instrucțiuni complete
|
||||||
|
4. **Exemple și metafore** — cele folosite de trainer
|
||||||
|
5. **Citate/principii importante**
|
||||||
|
6. **Referințe** — cărți, autori, resurse externe
|
||||||
|
7. **Fluxul sesiunii** — structura și ordinea subiectelor
|
||||||
|
|
||||||
|
### 3. Compilare în cele 3 fișiere
|
||||||
|
- **Limba:** Română
|
||||||
|
- **Structură:** pe zile și sesiuni (Ziua 1 / Ziua 2)
|
||||||
|
- Exhaustiv → include tot ce are valoare educațională
|
||||||
|
- Concentrat → filtrează anecdotele, păstrează esența
|
||||||
|
- Cheatsheet → format tabel/listă, maxim 1-2 pagini
|
||||||
|
|
||||||
|
### 4. Verificare
|
||||||
|
- Toate conceptele cheie apar în toate cele 3 nivele
|
||||||
|
- Tehnicile au pași clari în versiunea exhaustivă
|
||||||
|
- Cheatsheet-ul e suficient de scurt pentru o trecere rapidă
|
||||||
|
|
||||||
|
## Comandă rapidă (în Claude Code)
|
||||||
|
```
|
||||||
|
Sumarizare modul {N} — procesul din PROCES_SUMARIZARE.md
|
||||||
|
```
|
||||||
142
PROMPT_EXPERIENTIAL.md
Normal file
142
PROMPT_EXPERIENTIAL.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# Prompt Experiențial — Caiet de Facilitator NLP Master
|
||||||
|
|
||||||
|
## Cum se folosește
|
||||||
|
|
||||||
|
3 pași per modul:
|
||||||
|
1. **Per zi:** Citește transcrierile unei zile și aplică PROMPT_ZI
|
||||||
|
2. **Merge:** Citește drafturile zilelor + EXHAUSTIV-ul modulului și aplică PROMPT_MERGE
|
||||||
|
3. **Aplicări:** Din jurnalul final, aplică PROMPT_APLICARI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROMPT_ZI
|
||||||
|
|
||||||
|
```
|
||||||
|
Ești un facilitator NLP cu cunoștințe solide de psihologie, neuroștiință, CNV, coaching,
|
||||||
|
attachment theory, IFS (Internal Family Systems), și filozofie practică. Ai participat
|
||||||
|
la acest modul de curs NLP Master Practitioner și scrii un caiet de procesare.
|
||||||
|
|
||||||
|
Limba: română. Ton: direct, dens, fără florituri.
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════════════
|
||||||
|
REGULI
|
||||||
|
═══════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
1. FĂRĂ VOCE DE POVESTITOR
|
||||||
|
NU scrie: "Nimeni nu bănuia că...", "Și aici apare ceva...", "Sala a tăcut."
|
||||||
|
SCRIE direct: ce s-a spus, ce înseamnă, cu ce se corelează.
|
||||||
|
Caiet de gânditor, nu roman.
|
||||||
|
|
||||||
|
2. ȚESE, NU CATALOGA
|
||||||
|
Nu secțiuni separate de metafore/exerciții/credințe. Fluxul zilei e structura.
|
||||||
|
Fiecare element apare în contextul lui, legat de ce vine înainte și după.
|
||||||
|
|
||||||
|
3. CORELAȚII PROPRII — cel mai important
|
||||||
|
După fiecare concept/exercițiu/moment, ADAUGĂ corelații din:
|
||||||
|
- Psihologie: Jung (evident), dar și Freud, Winnicott, Bowlby, Erikson
|
||||||
|
- Attachment theory: cum se leagă de stilurile de atașament
|
||||||
|
- IFS (Internal Family Systems): părți protectoare, exilați, manageri
|
||||||
|
- Brené Brown: vulnerabilitate, rușine, curaj
|
||||||
|
- CNV (Marshall Rosenberg): nevoi universale, strategii vs nevoi
|
||||||
|
- Neuroștiință: amigdala, cortex prefrontal, polyvagal theory (Stephen Porges)
|
||||||
|
- Filozofie: stoicism, existențialism, budism (non-atașament, non-dualitate)
|
||||||
|
- Coaching: modele (GROW, clean language, coaching ontologic)
|
||||||
|
- Alte modele NLP sau terapeutice relevante
|
||||||
|
|
||||||
|
Nu forța corelațiile. Adaugă-le doar unde sunt genuine și utile.
|
||||||
|
Formatul: scurt, direct. "Asta e în esență ce Brené Brown numește 'vulnerability
|
||||||
|
hangover' — momentul de după în care regrezi că te-ai deschis."
|
||||||
|
|
||||||
|
4. ANTI-GENERICISM
|
||||||
|
NU: "Trainerul a explicat conceptul X"
|
||||||
|
DA: Cuvintele lui exacte (ușor adaptate), apoi ce înseamnă, apoi cu ce se corelează.
|
||||||
|
|
||||||
|
5. EXERCIȚII COMPLETE
|
||||||
|
Scriptul facilitatorului rămâne complet — pași, cuvinte, ce observi, debriefing,
|
||||||
|
ce poate ieși greșit. Exercițiile sunt cel mai practic element, nu le comprima.
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
STRUCTURA:
|
||||||
|
|
||||||
|
### Firul zilei
|
||||||
|
Narativa densă a zilei. Concepte → citate relevante ale trainerului → corelații proprii →
|
||||||
|
exerciții (cu script complet) → ce deschide fiecare element. Tot într-un fir.
|
||||||
|
|
||||||
|
Stil: paragraf dens care gândește, nu care povestește. Cuvintele trainerului apar ca
|
||||||
|
citate scurte integrate în text, nu ca blocuri dramatice.
|
||||||
|
|
||||||
|
### Constelația de conexiuni
|
||||||
|
Legăturile non-evidente. Thread-uri deschise. Corelații cross-disciplinare.
|
||||||
|
|
||||||
|
### Scripturi de exerciții (referință rapidă)
|
||||||
|
Pașii extrași, formatați pentru uz practic. Duplică intenționat.
|
||||||
|
|
||||||
|
---
|
||||||
|
TRANSCRIERILE ZILEI:
|
||||||
|
[se inserează aici]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROMPT_MERGE
|
||||||
|
|
||||||
|
```
|
||||||
|
Ai drafturile jurnalului de facilitator pentru fiecare zi a modulului {N} ({concept}).
|
||||||
|
Combină-le într-un jurnal coerent.
|
||||||
|
|
||||||
|
REGULI:
|
||||||
|
- Păstrează tot conținutul — nu comprima exercițiile sau corelațiile
|
||||||
|
- Adaugă firul roșu al modulului (arc conceptual, nu rezumat)
|
||||||
|
- Adaugă constelația modulului (conexiuni tematice cross-zi + cross-modul)
|
||||||
|
- Verifică cu EXHAUSTIV-ul că nu ratezi concepte
|
||||||
|
- Ton: direct, dens, fără florituri de povestitor
|
||||||
|
- Unde găsești corelații noi cross-zi, adaugă-le
|
||||||
|
|
||||||
|
STRUCTURA:
|
||||||
|
|
||||||
|
# MODUL {N} — {CONCEPT}
|
||||||
|
## Caiet de Facilitator
|
||||||
|
|
||||||
|
### Firul roșu al modulului
|
||||||
|
[Arc conceptual și emoțional. Direct, 3-5 paragrafe.]
|
||||||
|
|
||||||
|
### Ziua 1: [titlu scurt]
|
||||||
|
[Conținutul din draft, integral]
|
||||||
|
|
||||||
|
### Ziua 2: [titlu scurt]
|
||||||
|
[Conținutul din draft, integral]
|
||||||
|
|
||||||
|
### Constelația modulului
|
||||||
|
[Harta conexiunilor tematice + corelații cross-disciplinare + thread-uri deschise]
|
||||||
|
|
||||||
|
### Scripturi de exerciții (referință rapidă)
|
||||||
|
[Pași extrași pentru uz practic]
|
||||||
|
|
||||||
|
### Lecturi și resurse
|
||||||
|
[Cărți/documentare menționate]
|
||||||
|
|
||||||
|
---
|
||||||
|
REFERINȚĂ: [EXHAUSTIV-ul modulului]
|
||||||
|
DRAFTURILE: [drafturile zilelor]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROMPT_APLICARI
|
||||||
|
|
||||||
|
```
|
||||||
|
Din jurnalul modulului {N} ({concept}), generează un ghid de aplicare.
|
||||||
|
|
||||||
|
Fii SPECIFIC — CUM adaptezi, cu ce cuvinte, pentru câți oameni, ce efect urmărești.
|
||||||
|
Arată CONEXIUNILE — de ce funcționează în acest context, ce nevoie adresează.
|
||||||
|
Ton: direct, practic, fără florituri.
|
||||||
|
|
||||||
|
# MODUL {N} — {CONCEPT}: Ghid de Aplicare
|
||||||
|
|
||||||
|
## Pentru grup de sprijin NLP
|
||||||
|
## Pentru cercetași adulți
|
||||||
|
## Pentru întâlniri CNV
|
||||||
|
## Pentru self-coaching
|
||||||
|
## Conexiuni cross-modul
|
||||||
|
```
|
||||||
43
TODOS.md
43
TODOS.md
@@ -1,8 +1,43 @@
|
|||||||
# TODOS
|
# TODOS
|
||||||
|
|
||||||
## Re-run pipeline for Module 6
|
## Re-run pipeline for Module 6
|
||||||
- **What:** Re-run `download.py` when module 6 becomes available on cursuri.aresens.ro/curs/26
|
- **What:** Re-run pipeline when module 6 becomes available on cursuri.aresens.ro/curs/26
|
||||||
- **Why:** Course has 6 modules total, only 5 are currently available. Pipeline is designed to be re-runnable — manifest.json + resumability means it discovers new modules and skips already-downloaded files.
|
- **Why:** Course has 6 modules total, only 5 are currently available.
|
||||||
- **How:** Run `python download.py` → check manifest for new files → run `python transcribe.py` → generate summaries → update SUPORT_CURS.md
|
- **How:** Din Windows nativ: `run.bat 6` (descarcă + transcrie doar modulul 6, skip-uiește M1-M5 deja complete)
|
||||||
- **Depends on:** Course provider publishing module 6
|
- **Depends on:** Course provider publishing module 6 (~2026-04-08)
|
||||||
- **Added:** 2026-03-24
|
- **Added:** 2026-03-24
|
||||||
|
|
||||||
|
## Modulul 6 — Sumarizări standard (EXHAUSTIV/CONCENTRAT/CHEATSHEET)
|
||||||
|
- **When:** După ce transcrierile M6 sunt gata (~2026-04-08 + timp de transcriere)
|
||||||
|
- **How:**
|
||||||
|
1. `run.bat 6` — descarcă + transcrie modulul 6 (sau `run.bat` fără argumente pentru tot)
|
||||||
|
2. Aplică procesul standard de sumarizare (vezi `PROCES_SUMARIZARE.md`):
|
||||||
|
- Citește toate transcrierile M6 (`transcripts/*M6*.txt`)
|
||||||
|
- Generează `summaries/MODUL6_EXHAUSTIV.md` (toate conceptele, tehnici pas cu pas, exerciții, exemple, citate)
|
||||||
|
- Generează `summaries/MODUL6_CONCENTRAT.md` (esența, fără anecdote)
|
||||||
|
- Generează `summaries/MODUL6_CHEATSHEET.md` (tabel/listă, 1-2 pagini)
|
||||||
|
4. `python summarize.py --compile` — recompilează `SUPORT_CURS.md` cu M6
|
||||||
|
|
||||||
|
## Modulul 6 — Caiet experiențial + aplicări
|
||||||
|
- **When:** După ce EXHAUSTIV-ul M6 e gata
|
||||||
|
- **How:**
|
||||||
|
1. Identifică tema modulului din transcrieri (probabil integrare finală / examen / încheiere)
|
||||||
|
2. Aplică promptul din `PROMPT_EXPERIENTIAL.md`:
|
||||||
|
- **Pas 1:** Per zi — citește transcrierile fiecărei zile, aplică PROMPT_ZI → drafturi per zi
|
||||||
|
- **Pas 2:** Merge — citește drafturile + MODUL6_EXHAUSTIV.md, aplică PROMPT_MERGE → `summaries/MODUL6_{CONCEPT}.md`
|
||||||
|
- **Pas 3:** Aplicări — aplică PROMPT_APLICARI → `summaries/MODUL6_{CONCEPT}_APLICARI.md`
|
||||||
|
3. Verifică calitatea: ton direct/dens, corelații cross-disciplinare, exerciții cu script complet, cadrări în aplicări
|
||||||
|
- **Reminders:**
|
||||||
|
- Tonul: caiet de gânditor, NU roman. Fără voce de povestitor.
|
||||||
|
- Corelații proprii din: IFS, CNV, attachment theory, Brené Brown, neuroștiință, filozofie
|
||||||
|
- Aplicările includ secvențe de cadrare complete (concepte ÎNAINTE, metafore, container siguranță)
|
||||||
|
|
||||||
|
## Modulul 6 — Actualizare fișiere cross-modul
|
||||||
|
- **When:** După ce caietul experiențial M6 e gata
|
||||||
|
- **How:** Actualizează cele 5 fișiere cross-modul cu conținutul M6:
|
||||||
|
1. `summaries/INDEX_EXERCITII.md` — adaugă exercițiile M6
|
||||||
|
2. `summaries/HARTA_CONEXIUNI.md` — adaugă conexiunile M6 și actualizează mindmap-ul
|
||||||
|
3. `summaries/GLOSAR_CREDINTE.md` — adaugă credințele/reframe-urile M6
|
||||||
|
4. `summaries/GHID_FACILITARE.md` — adaugă exercițiile M6 în matricele de selecție
|
||||||
|
5. `summaries/METAFORE_POVESTI.md` — adaugă metaforele M6
|
||||||
|
- **Also:** Deschide plicul sigilat din M1 (se deschide la finalul masterului)
|
||||||
|
|||||||
239
md_to_pdf.py
Normal file
239
md_to_pdf.py
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Convert Markdown summaries to print-friendly PDFs."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import markdown2
|
||||||
|
from weasyprint import HTML
|
||||||
|
|
||||||
|
SUMMARIES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "summaries")
|
||||||
|
PDF_DIR = os.path.join(SUMMARIES_DIR, "pdf")
|
||||||
|
|
||||||
|
CSS = """
|
||||||
|
@page {
|
||||||
|
size: A4;
|
||||||
|
margin: 2cm;
|
||||||
|
@bottom-right {
|
||||||
|
content: counter(page);
|
||||||
|
font-size: 9pt;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Segoe UI", "Noto Sans", "DejaVu Sans", sans-serif;
|
||||||
|
font-size: 11pt;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 18pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
border-bottom: 1.5pt solid #333;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
page-break-before: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:first-of-type {
|
||||||
|
page-break-before: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 14pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0.8em;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0.4em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
margin: 0.3em 0;
|
||||||
|
padding-left: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0.15em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 0.5pt solid #999;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
font-size: 10pt;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #e8e8e8;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
padding: 5pt 8pt;
|
||||||
|
border: 0.5pt solid #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 4pt 8pt;
|
||||||
|
border: 0.5pt solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) td {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border: 0.5pt solid #ccc;
|
||||||
|
border-radius: 3pt;
|
||||||
|
padding: 8pt 10pt;
|
||||||
|
font-family: "Consolas", "DejaVu Sans Mono", monospace;
|
||||||
|
font-size: 9pt;
|
||||||
|
line-height: 1.35;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: "Consolas", "DejaVu Sans Mono", monospace;
|
||||||
|
font-size: 9.5pt;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
padding: 1pt 3pt;
|
||||||
|
border-radius: 2pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: 2pt solid #999;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
padding: 0.3em 0 0.3em 1em;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def md_to_pdf(md_path, pdf_path):
|
||||||
|
"""Convert a single Markdown file to PDF."""
|
||||||
|
with open(md_path, "r", encoding="utf-8") as f:
|
||||||
|
md_text = f.read()
|
||||||
|
|
||||||
|
html_body = markdown2.markdown(
|
||||||
|
md_text,
|
||||||
|
extras=["tables", "fenced-code-blocks", "header-ids", "break-on-newline"],
|
||||||
|
)
|
||||||
|
|
||||||
|
html_doc = f"""<!DOCTYPE html>
|
||||||
|
<html lang="ro">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>{CSS}</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{html_body}
|
||||||
|
</body>
|
||||||
|
</html>"""
|
||||||
|
|
||||||
|
HTML(string=html_doc).write_pdf(pdf_path)
|
||||||
|
print(f" {os.path.basename(md_path)} -> {os.path.basename(pdf_path)}")
|
||||||
|
|
||||||
|
|
||||||
|
def find_files(modules=None):
|
||||||
|
"""Find MODUL*_*.md files, optionally filtered by module numbers."""
|
||||||
|
pattern = os.path.join(SUMMARIES_DIR, "MODUL*_*.md")
|
||||||
|
files = sorted(glob.glob(pattern))
|
||||||
|
|
||||||
|
if modules:
|
||||||
|
filtered = []
|
||||||
|
for f in files:
|
||||||
|
basename = os.path.basename(f)
|
||||||
|
# Extract module number from MODUL{N}_...
|
||||||
|
try:
|
||||||
|
num = int(basename.split("_")[0].replace("MODUL", ""))
|
||||||
|
if num in modules:
|
||||||
|
filtered.append(f)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
files = filtered
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def parse_modules(spec):
|
||||||
|
"""Parse module spec like '1-3' or '2,4,5' into a set of ints."""
|
||||||
|
modules = set()
|
||||||
|
for part in spec.split(","):
|
||||||
|
part = part.strip()
|
||||||
|
if "-" in part:
|
||||||
|
start, end = part.split("-", 1)
|
||||||
|
modules.update(range(int(start), int(end) + 1))
|
||||||
|
else:
|
||||||
|
modules.add(int(part))
|
||||||
|
return modules
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Convert MD summaries to PDF")
|
||||||
|
parser.add_argument("files", nargs="*", help="Specific MD files to convert")
|
||||||
|
parser.add_argument(
|
||||||
|
"--modules", "-m", help="Module filter, e.g. '1-3' or '2,4,5'"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
os.makedirs(PDF_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
if args.files:
|
||||||
|
md_files = [os.path.abspath(f) for f in args.files]
|
||||||
|
else:
|
||||||
|
modules = parse_modules(args.modules) if args.modules else None
|
||||||
|
md_files = find_files(modules)
|
||||||
|
|
||||||
|
if not md_files:
|
||||||
|
print("No MD files found to convert.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"Converting {len(md_files)} file(s) to PDF...")
|
||||||
|
for md_path in md_files:
|
||||||
|
basename = os.path.splitext(os.path.basename(md_path))[0]
|
||||||
|
pdf_path = os.path.join(PDF_DIR, basename + ".pdf")
|
||||||
|
md_to_pdf(md_path, pdf_path)
|
||||||
|
|
||||||
|
print(f"Done. PDFs saved to {PDF_DIR}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
requests
|
requests
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
markdown2
|
||||||
|
weasyprint
|
||||||
|
|||||||
Reference in New Issue
Block a user