LaTeX dynamic building
This commit is contained in:
parent
289db0cb02
commit
f1d022b19b
7
.gitignore
vendored
7
.gitignore
vendored
@ -102,6 +102,13 @@ uploads/
|
|||||||
downloads/
|
downloads/
|
||||||
cache/
|
cache/
|
||||||
|
|
||||||
|
# LaTeX worktrees
|
||||||
|
backend/latex-qsm/
|
||||||
|
backend/latex-vsm/
|
||||||
|
|
||||||
|
# Generated PDFs
|
||||||
|
backend/assets/*.pdf
|
||||||
|
|
||||||
# Certificates
|
# Certificates
|
||||||
*.pem
|
*.pem
|
||||||
*.key
|
*.key
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "backend/latex-templates"]
|
||||||
|
path = backend/latex-templates
|
||||||
|
url = ssh://git@git.beimgraben.net:222/frederik/PA_Vorlage.git
|
||||||
@ -13,6 +13,7 @@ A comprehensive system for managing student parliament (STUPA) funding applicati
|
|||||||
- **API Documentation**: Auto-generated OpenAPI/Swagger documentation
|
- **API Documentation**: Auto-generated OpenAPI/Swagger documentation
|
||||||
- **Rate Limiting**: Configurable rate limits for API protection
|
- **Rate Limiting**: Configurable rate limits for API protection
|
||||||
- **Database UI**: Integrated Adminer for database management
|
- **Database UI**: Integrated Adminer for database management
|
||||||
|
- **LaTeX Integration**: Integrated LaTeX templates for QSM and VSM forms
|
||||||
|
|
||||||
## 📋 Prerequisites
|
## 📋 Prerequisites
|
||||||
|
|
||||||
@ -34,6 +35,9 @@ stupa-pdf-api/
|
|||||||
│ │ ├── pdf_to_struct.py # PDF parsing
|
│ │ ├── pdf_to_struct.py # PDF parsing
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── assets/ # PDF templates
|
│ ├── assets/ # PDF templates
|
||||||
|
│ ├── latex-templates/ # LaTeX source (git submodule)
|
||||||
|
│ ├── latex-qsm/ # QSM LaTeX worktree
|
||||||
|
│ ├── latex-vsm/ # VSM LaTeX worktree
|
||||||
│ ├── requirements.txt # Python dependencies
|
│ ├── requirements.txt # Python dependencies
|
||||||
│ └── Dockerfile # Backend container definition
|
│ └── Dockerfile # Backend container definition
|
||||||
├── frontend/ # React frontend application
|
├── frontend/ # React frontend application
|
||||||
@ -129,6 +133,7 @@ Comprehensive documentation is available in the `/docs` directory:
|
|||||||
- [Quick Start Guide](./docs/QUICK_START.md)
|
- [Quick Start Guide](./docs/QUICK_START.md)
|
||||||
- [API Reference](./docs/API_REFERENCE.md)
|
- [API Reference](./docs/API_REFERENCE.md)
|
||||||
- [Development Guide](./docs/DEVELOPMENT.md)
|
- [Development Guide](./docs/DEVELOPMENT.md)
|
||||||
|
- [PDF Integration](./docs/PDF_INTEGRATION.md)
|
||||||
|
|
||||||
## 🔧 Configuration
|
## 🔧 Configuration
|
||||||
|
|
||||||
|
|||||||
103
backend/.dockerignore
Normal file
103
backend/.dockerignore
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.hypothesis/
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
.pyre/
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env/
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Git worktrees (we need the main latex-templates repo, but not the worktrees)
|
||||||
|
# Note: latex-qsm and latex-vsm are needed for Docker build, so they're not ignored
|
||||||
|
|
||||||
|
# LaTeX build artifacts
|
||||||
|
*.aux
|
||||||
|
*.log
|
||||||
|
*.out
|
||||||
|
*.fdb_latexmk
|
||||||
|
*.fls
|
||||||
|
*.synctex.gz
|
||||||
|
*.bbl
|
||||||
|
*.blg
|
||||||
|
*.idx
|
||||||
|
*.ilg
|
||||||
|
*.ind
|
||||||
|
*.toc
|
||||||
|
*.lot
|
||||||
|
*.lof
|
||||||
|
*.nav
|
||||||
|
*.snm
|
||||||
|
*.vrb
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
*.md
|
||||||
|
docs/
|
||||||
|
LICENSE
|
||||||
|
|
||||||
|
# Development
|
||||||
|
dev.sh
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.backup
|
||||||
|
|
||||||
|
# Test data
|
||||||
|
tests/
|
||||||
|
test_*.py
|
||||||
|
*_test.py
|
||||||
|
|
||||||
|
# CI/CD
|
||||||
|
.github/
|
||||||
|
.gitlab-ci.yml
|
||||||
|
.travis.yml
|
||||||
|
|
||||||
|
# Assets folder (we'll generate PDFs in Docker, not copy existing ones)
|
||||||
|
assets/
|
||||||
@ -1,10 +1,41 @@
|
|||||||
# ---------- Base ----------
|
# ---------- LaTeX Builder Stage ----------
|
||||||
|
FROM texlive/texlive:latest AS latex-builder
|
||||||
|
|
||||||
|
# Install additional dependencies for LaTeX
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
git \
|
||||||
|
inkscape \
|
||||||
|
make \
|
||||||
|
fonts-liberation \
|
||||||
|
fonts-dejavu-core \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& inkscape --version
|
||||||
|
|
||||||
|
WORKDIR /latex
|
||||||
|
|
||||||
|
# Copy the LaTeX source files from local worktrees
|
||||||
|
COPY latex-qsm /latex/qsm
|
||||||
|
COPY latex-vsm /latex/vsm
|
||||||
|
|
||||||
|
# Build QSM PDF
|
||||||
|
WORKDIR /latex/qsm
|
||||||
|
RUN latexmk -xelatex -interaction=nonstopmode -halt-on-error -shell-escape Main.tex && \
|
||||||
|
cp Main.pdf /latex/qsm.pdf && \
|
||||||
|
latexmk -c
|
||||||
|
|
||||||
|
# Build VSM PDF
|
||||||
|
WORKDIR /latex/vsm
|
||||||
|
RUN latexmk -xelatex -interaction=nonstopmode -shell-escape Main.tex && \
|
||||||
|
cp Main.pdf /latex/vsm.pdf && \
|
||||||
|
latexmk -c
|
||||||
|
|
||||||
|
# ---------- Base Python Stage ----------
|
||||||
FROM python:3.11-slim AS base
|
FROM python:3.11-slim AS base
|
||||||
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
# System deps (optional: tzdata for correct time)
|
# System deps
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
tzdata ca-certificates \
|
tzdata ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
@ -16,31 +47,21 @@ COPY requirements.txt /app/requirements.txt
|
|||||||
RUN pip install --no-cache-dir -r /app/requirements.txt
|
RUN pip install --no-cache-dir -r /app/requirements.txt
|
||||||
|
|
||||||
# ---------- App ----------
|
# ---------- App ----------
|
||||||
# Struktur-Annahme:
|
# Copy application code
|
||||||
# - src/ (alle .py Module, inkl. service_api.py, pdf_to_struct.py, pdf_filler.py, etc.)
|
|
||||||
# - assets/ (qsm.pdf, vsm.pdf)
|
|
||||||
COPY src/ /app/src/
|
COPY src/ /app/src/
|
||||||
COPY assets/ /app/assets/
|
|
||||||
|
|
||||||
# Falls deine Module relative Imports nutzen, src ins PYTHONPATH aufnehmen
|
# Copy pre-built PDFs from latex-builder stage
|
||||||
|
COPY --from=latex-builder /latex/qsm.pdf /app/assets/qsm.pdf
|
||||||
|
COPY --from=latex-builder /latex/vsm.pdf /app/assets/vsm.pdf
|
||||||
|
|
||||||
|
# Set Python path
|
||||||
ENV PYTHONPATH=/app/src
|
ENV PYTHONPATH=/app/src
|
||||||
|
|
||||||
# pdf_filler.py sucht standardmäßig assets relativ zum Modul.
|
# Configure PDF template paths
|
||||||
# Wir überschreiben die Template-Pfade per ENV, da die PDFs im Build-Root unter /app/assets liegen.
|
|
||||||
ENV QSM_TEMPLATE=/app/assets/qsm.pdf \
|
ENV QSM_TEMPLATE=/app/assets/qsm.pdf \
|
||||||
VSM_TEMPLATE=/app/assets/vsm.pdf
|
VSM_TEMPLATE=/app/assets/vsm.pdf
|
||||||
|
|
||||||
# Optional: Master-Key / DB-Config kommen zur Laufzeit per -e oder .env (docker run --env-file)
|
|
||||||
# ENV MASTER_KEY=change_me \
|
|
||||||
# MYSQL_HOST=mysql \
|
|
||||||
# MYSQL_PORT=3306 \
|
|
||||||
# MYSQL_DB=stupa \
|
|
||||||
# MYSQL_USER=stupa \
|
|
||||||
# MYSQL_PASSWORD=secret
|
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
# ---------- Run ----------
|
# ---------- Run ----------
|
||||||
# Starte die FastAPI
|
|
||||||
# Hinweis: service_api.py muss in src/ liegen und die App als "app" exportieren.
|
|
||||||
CMD ["uvicorn", "service_api:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "service_api:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
1
backend/latex-templates
Submodule
1
backend/latex-templates
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit b14c230d5a7d550122abf53eca5de15924ab2085
|
||||||
156
backend/scripts/make_fields_readonly.py
Normal file
156
backend/scripts/make_fields_readonly.py
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Make all form fields in LaTeX documents readonly by adding readonly=true attribute.
|
||||||
|
This script carefully parses LaTeX commands to avoid breaking the syntax.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def add_readonly_to_textfield(content):
|
||||||
|
"""Add readonly=true to CustomTextFieldDefault commands."""
|
||||||
|
# Pattern to match CustomTextFieldDefault{...}{...}{...}{params}
|
||||||
|
pattern = r'(\\CustomTextFieldDefault\{[^}]*\}\{[^}]*\}\{[^}]*\}\{)([^}]*)\}'
|
||||||
|
|
||||||
|
def replacer(match):
|
||||||
|
prefix = match.group(1)
|
||||||
|
params = match.group(2)
|
||||||
|
|
||||||
|
# Check if readonly is already present
|
||||||
|
if 'readonly=' in params:
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
# Add readonly=true to the parameters
|
||||||
|
if params.strip():
|
||||||
|
new_params = params + ',readonly=true'
|
||||||
|
else:
|
||||||
|
new_params = 'readonly=true'
|
||||||
|
|
||||||
|
return prefix + new_params + '}'
|
||||||
|
|
||||||
|
return re.sub(pattern, replacer, content)
|
||||||
|
|
||||||
|
|
||||||
|
def add_readonly_to_choicemenu(content):
|
||||||
|
"""Add readonly=true to CustomChoiceMenuDefault commands."""
|
||||||
|
# Pattern to match CustomChoiceMenuDefault{...}{...}{params}{...}
|
||||||
|
pattern = r'(\\CustomChoiceMenuDefault\{[^}]*\}\{[^}]*\}\{)([^}]*)\}(\{[^}]*\})'
|
||||||
|
|
||||||
|
def replacer(match):
|
||||||
|
prefix = match.group(1)
|
||||||
|
params = match.group(2)
|
||||||
|
suffix = match.group(3)
|
||||||
|
|
||||||
|
# Check if readonly is already present
|
||||||
|
if 'readonly=' in params:
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
# Add readonly=true to the parameters
|
||||||
|
if params.strip():
|
||||||
|
new_params = params + ',readonly=true'
|
||||||
|
else:
|
||||||
|
new_params = 'readonly=true'
|
||||||
|
|
||||||
|
return prefix + new_params + '}' + suffix
|
||||||
|
|
||||||
|
return re.sub(pattern, replacer, content)
|
||||||
|
|
||||||
|
|
||||||
|
def add_readonly_to_checkbox(content):
|
||||||
|
"""Add readonly=true to CheckBox commands."""
|
||||||
|
# Pattern to match CheckBox[params]
|
||||||
|
pattern = r'(\\CheckBox\[)([^\]]*)\]'
|
||||||
|
|
||||||
|
def replacer(match):
|
||||||
|
prefix = match.group(1)
|
||||||
|
params = match.group(2)
|
||||||
|
|
||||||
|
# Check if readonly is already present
|
||||||
|
if 'readonly=' in params:
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
# Add readonly=true to the parameters
|
||||||
|
params_lines = params.split('\n')
|
||||||
|
|
||||||
|
# Find a good place to insert readonly=true (after first parameter)
|
||||||
|
for i, line in enumerate(params_lines):
|
||||||
|
if line.strip() and not line.strip().startswith('%'):
|
||||||
|
# Insert after this line
|
||||||
|
params_lines.insert(i + 1, '\t\t\t\treadonly=true,')
|
||||||
|
break
|
||||||
|
|
||||||
|
new_params = '\n'.join(params_lines)
|
||||||
|
return prefix + new_params + ']'
|
||||||
|
|
||||||
|
return re.sub(pattern, replacer, content, flags=re.MULTILINE | re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
def add_readonly_to_textfield_multiline(content):
|
||||||
|
"""Add readonly=true to TextField commands (multiline text fields)."""
|
||||||
|
# Pattern to match TextField[params] for multiline fields
|
||||||
|
pattern = r'(\\TextField\[)([^\]]*name=pa-project-description[^\]]*)\]'
|
||||||
|
|
||||||
|
def replacer(match):
|
||||||
|
prefix = match.group(1)
|
||||||
|
params = match.group(2)
|
||||||
|
|
||||||
|
# Check if readonly is already present
|
||||||
|
if 'readonly=' in params:
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
# Add readonly=true to the parameters
|
||||||
|
params_lines = params.split('\n')
|
||||||
|
|
||||||
|
# Find a good place to insert readonly=true (after multiline parameter)
|
||||||
|
for i, line in enumerate(params_lines):
|
||||||
|
if 'multiline' in line:
|
||||||
|
# Insert after this line
|
||||||
|
params_lines.insert(i + 1, '\t\t\t\treadonly=true,')
|
||||||
|
break
|
||||||
|
|
||||||
|
new_params = '\n'.join(params_lines)
|
||||||
|
return prefix + new_params + ']'
|
||||||
|
|
||||||
|
return re.sub(pattern, replacer, content, flags=re.MULTILINE | re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
def process_file(filepath):
|
||||||
|
"""Process a single LaTeX file to make all fields readonly."""
|
||||||
|
print(f"Processing {filepath}...")
|
||||||
|
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# Apply transformations
|
||||||
|
content = add_readonly_to_textfield(content)
|
||||||
|
content = add_readonly_to_choicemenu(content)
|
||||||
|
content = add_readonly_to_checkbox(content)
|
||||||
|
content = add_readonly_to_textfield_multiline(content)
|
||||||
|
|
||||||
|
# Write back
|
||||||
|
with open(filepath, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f"✓ Processed {filepath}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to process QSM and VSM LaTeX files."""
|
||||||
|
base_dir = Path(__file__).parent.parent
|
||||||
|
|
||||||
|
files_to_process = [
|
||||||
|
base_dir / "latex-qsm" / "Content" / "01_content.tex",
|
||||||
|
base_dir / "latex-vsm" / "Content" / "01_content.tex",
|
||||||
|
]
|
||||||
|
|
||||||
|
for filepath in files_to_process:
|
||||||
|
if filepath.exists():
|
||||||
|
process_file(filepath)
|
||||||
|
else:
|
||||||
|
print(f"✗ File not found: {filepath}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -22,12 +22,6 @@ TEXT_MAPPING_COMMON: dict = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
# --- Applicant ---
|
# --- Applicant ---
|
||||||
'pa-applicant-type': {
|
|
||||||
'required': True,
|
|
||||||
'target-key': 'pa.applicant.type',
|
|
||||||
'type': 'enum',
|
|
||||||
'values': [('person', 'Person'), ('institution', 'Institution')]
|
|
||||||
},
|
|
||||||
'pa-institution-type': {
|
'pa-institution-type': {
|
||||||
'required': True,
|
'required': True,
|
||||||
'target-key': 'pa.applicant.institution.type',
|
'target-key': 'pa.applicant.institution.type',
|
||||||
@ -88,8 +82,7 @@ TEXT_MAPPING_COMMON: dict = {
|
|||||||
'pa-cost-{a;1:24}-name': {'required': True, 'target-key': 'pa.project.costs[{a}].name', 'type': str},
|
'pa-cost-{a;1:24}-name': {'required': True, 'target-key': 'pa.project.costs[{a}].name', 'type': str},
|
||||||
'pa-cost-{a;1:24}-amount-euro': {'required': True, 'target-key': 'pa.project.costs[{a}].amountEur', 'type': float},
|
'pa-cost-{a;1:24}-amount-euro': {'required': True, 'target-key': 'pa.project.costs[{a}].amountEur', 'type': float},
|
||||||
|
|
||||||
# --- Attachments common ---
|
|
||||||
'pa-anh-vergleichsangebote': {'required': False, 'target-key': 'pa.attachments.comparativeOffers', 'type': bool},
|
|
||||||
|
|
||||||
# --- Misc ---
|
# --- Misc ---
|
||||||
'warning-not-supported': {'required': False, 'target-key': 'warning.notSupported', 'type': str},
|
'warning-not-supported': {'required': False, 'target-key': 'warning.notSupported', 'type': str},
|
||||||
@ -119,7 +112,7 @@ TEXT_MAPPING_QSM = {
|
|||||||
'pa-qsm-individuell': {'required': False, 'target-key': 'pa.project.financing.qsm.flags.individuell', 'type': bool},
|
'pa-qsm-individuell': {'required': False, 'target-key': 'pa.project.financing.qsm.flags.individuell', 'type': bool},
|
||||||
'pa-qsm-exkursion-genehmigt': {'required': False, 'target-key': 'pa.project.financing.qsm.flags.exkursionGenehmigt', 'type': bool},
|
'pa-qsm-exkursion-genehmigt': {'required': False, 'target-key': 'pa.project.financing.qsm.flags.exkursionGenehmigt', 'type': bool},
|
||||||
'pa-qsm-exkursion-bezuschusst': {'required': False, 'target-key': 'pa.project.financing.qsm.flags.exkursionBezuschusst', 'type': bool},
|
'pa-qsm-exkursion-bezuschusst': {'required': False, 'target-key': 'pa.project.financing.qsm.flags.exkursionBezuschusst', 'type': bool},
|
||||||
'pa-anh-fakultaet': {'required': False, 'target-key': 'pa.attachments.fakultaet', 'type': bool},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- VSM-specific fields (first variant; include for completeness) ---
|
# --- VSM-specific fields (first variant; include for completeness) ---
|
||||||
|
|||||||
@ -160,13 +160,11 @@ def fill_pdf(payload: Dict[str, Any], variant: str, out_path: Optional[str] = No
|
|||||||
|
|
||||||
# Calculate total amount from costs
|
# Calculate total amount from costs
|
||||||
total_amount = 0.0
|
total_amount = 0.0
|
||||||
costs = flat.get("pa.project.costs", [])
|
# After flattening, costs are in format: pa.project.costs[0].amountEur, pa.project.costs[1].amountEur, etc.
|
||||||
if isinstance(costs, list):
|
for key, value in flat.items():
|
||||||
for cost in costs:
|
if key.startswith("pa.project.costs[") and key.endswith("].amountEur"):
|
||||||
if isinstance(cost, dict) and "amountEur" in cost:
|
if value is not None and isinstance(value, (int, float)):
|
||||||
amount = cost.get("amountEur")
|
total_amount += float(value)
|
||||||
if amount is not None and isinstance(amount, (int, float)):
|
|
||||||
total_amount += float(amount)
|
|
||||||
|
|
||||||
# AcroForm übernehmen + NeedAppearances
|
# AcroForm übernehmen + NeedAppearances
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -73,9 +73,17 @@ services:
|
|||||||
- action: sync
|
- action: sync
|
||||||
path: ./backend/assets
|
path: ./backend/assets
|
||||||
target: /app/assets
|
target: /app/assets
|
||||||
|
- action: sync
|
||||||
|
path: ./backend/latex-qsm
|
||||||
|
target: /app/latex-qsm
|
||||||
|
- action: sync
|
||||||
|
path: ./backend/latex-vsm
|
||||||
|
target: /app/latex-vsm
|
||||||
volumes:
|
volumes:
|
||||||
- ./backend/src:/app/src
|
- ./backend/src:/app/src
|
||||||
- ./backend/assets:/app/assets
|
- ./backend/assets:/app/assets
|
||||||
|
- ./backend/latex-qsm:/app/latex-qsm
|
||||||
|
- ./backend/latex-vsm:/app/latex-vsm
|
||||||
|
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer:4
|
image: adminer:4
|
||||||
|
|||||||
260
docs/PDF_INTEGRATION.md
Normal file
260
docs/PDF_INTEGRATION.md
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
# PDF Integration Guide
|
||||||
|
|
||||||
|
This document describes how the STUPA PDF API integrates with LaTeX templates to generate and process PDF forms.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The STUPA PDF API system works with two types of funding application forms:
|
||||||
|
- **QSM** (Qualitätssicherungsmittel) - Quality assurance funding
|
||||||
|
- **VSM** (Verfasste Studierendenschaft Mittel) - Student body funding
|
||||||
|
|
||||||
|
## LaTeX Templates
|
||||||
|
|
||||||
|
### Repository Structure
|
||||||
|
|
||||||
|
The LaTeX templates are maintained in a separate Git repository and integrated as a submodule:
|
||||||
|
- Repository: `git@git.beimgraben.net:frederik/PA_Vorlage.git`
|
||||||
|
- Location: `/backend/latex-templates/`
|
||||||
|
|
||||||
|
### Branch Organization
|
||||||
|
|
||||||
|
Different form types are maintained in separate branches:
|
||||||
|
- `v1.2/QSM` - QSM application forms
|
||||||
|
- `v1.2/VSM` - VSM application forms
|
||||||
|
- `v1.2/VGL` - VGL forms (deprecated, not used)
|
||||||
|
|
||||||
|
### Working with Templates
|
||||||
|
|
||||||
|
The project uses Git worktrees to access multiple template versions simultaneously:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Templates are checked out to:
|
||||||
|
/backend/latex-qsm/ # QSM templates (branch: v1.2/QSM)
|
||||||
|
/backend/latex-vsm/ # VSM templates (branch: v1.2/VSM)
|
||||||
|
```
|
||||||
|
|
||||||
|
## PDF Generation Workflow
|
||||||
|
|
||||||
|
### 1. Template Compilation
|
||||||
|
|
||||||
|
LaTeX templates are compiled to PDF using XeLaTeX:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build PDFs from LaTeX sources
|
||||||
|
./scripts/build-pdfs.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This script:
|
||||||
|
- Sets up Git worktrees for QSM and VSM branches
|
||||||
|
- Compiles LaTeX to PDF using `latexmk -xelatex`
|
||||||
|
- Copies generated PDFs to `/backend/assets/`
|
||||||
|
|
||||||
|
### 2. Form Field Mapping
|
||||||
|
|
||||||
|
The system uses pre-compiled PDFs with form fields:
|
||||||
|
- `/backend/assets/qsm.pdf` - QSM form template
|
||||||
|
- `/backend/assets/vsm.pdf` - VSM form template
|
||||||
|
|
||||||
|
These PDFs contain named form fields that correspond to the application data structure.
|
||||||
|
|
||||||
|
### 3. PDF Processing Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
User uploads PDF → Parse form data → Store in database → Generate filled PDF
|
||||||
|
```
|
||||||
|
|
||||||
|
## LaTeX Template Structure
|
||||||
|
|
||||||
|
### Main Components
|
||||||
|
|
||||||
|
```
|
||||||
|
Main.tex # Main document file
|
||||||
|
Content/
|
||||||
|
├── 01_content.tex # Form content and fields
|
||||||
|
└── 99_glossary.tex # Glossary definitions
|
||||||
|
HSRTReport/ # Custom document class
|
||||||
|
TeX/
|
||||||
|
├── Preamble.tex # Package imports and settings
|
||||||
|
└── Settings/ # Configuration files
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Fields
|
||||||
|
|
||||||
|
LaTeX form fields are defined using custom commands:
|
||||||
|
|
||||||
|
```latex
|
||||||
|
\CustomTextFieldDefault{pa-project-name}{}{Projektname}{width=\linewidth}
|
||||||
|
\CustomChoiceMenuDefault{pa-course}{}{width=\linewidth,default=-}{-,INF,ESB,LS,TEC,TEX,NXT}
|
||||||
|
\CheckBox[name=pa-qsm-studierende,width=1em,height=1em]{}
|
||||||
|
```
|
||||||
|
|
||||||
|
Field naming convention: `pa-` prefix followed by the field identifier.
|
||||||
|
|
||||||
|
## Docker Integration
|
||||||
|
|
||||||
|
### Build Requirements
|
||||||
|
|
||||||
|
The Docker image includes all necessary LaTeX packages:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
RUN apt-get install -y \
|
||||||
|
texlive-full \
|
||||||
|
texlive-xetex \
|
||||||
|
texlive-lang-german \
|
||||||
|
texlive-fonts-extra \
|
||||||
|
latexmk \
|
||||||
|
git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
```env
|
||||||
|
# PDF template paths
|
||||||
|
QSM_TEMPLATE=/app/assets/qsm.pdf
|
||||||
|
VSM_TEMPLATE=/app/assets/vsm.pdf
|
||||||
|
|
||||||
|
# LaTeX source paths
|
||||||
|
LATEX_QSM_PATH=/app/latex-qsm
|
||||||
|
LATEX_VSM_PATH=/app/latex-vsm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### 1. Modifying Templates
|
||||||
|
|
||||||
|
To modify PDF templates:
|
||||||
|
|
||||||
|
1. Navigate to the appropriate worktree:
|
||||||
|
```bash
|
||||||
|
cd backend/latex-qsm # or latex-vsm
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit the LaTeX files
|
||||||
|
|
||||||
|
3. Build the PDF:
|
||||||
|
```bash
|
||||||
|
./scripts/build-pdfs.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Test the new PDF with the application
|
||||||
|
|
||||||
|
### 2. Adding New Form Fields
|
||||||
|
|
||||||
|
1. Add field definition in LaTeX:
|
||||||
|
```latex
|
||||||
|
\CustomTextFieldDefault{pa-new-field}{}{Field Label}{width=\linewidth}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update the field mapping in `pdf_field_mapping.py`
|
||||||
|
|
||||||
|
3. Add corresponding database fields if needed
|
||||||
|
|
||||||
|
4. Rebuild the PDF template
|
||||||
|
|
||||||
|
### 3. Testing PDF Generation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test PDF generation in Docker
|
||||||
|
docker compose exec api python -c "
|
||||||
|
from pdf_filler import fill_pdf
|
||||||
|
# Test code here
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Field Mapping Reference
|
||||||
|
|
||||||
|
### Common Fields (Both QSM and VSM)
|
||||||
|
|
||||||
|
| LaTeX Field | Database Field | Description |
|
||||||
|
|-------------|----------------|-------------|
|
||||||
|
| `pa-applicant-type` | `applicantType` | Person or Institution |
|
||||||
|
| `pa-institution` | `institution` | Institution name |
|
||||||
|
| `pa-first-name` | `firstName` | Applicant first name |
|
||||||
|
| `pa-last-name` | `lastName` | Applicant last name |
|
||||||
|
| `pa-email` | `email` | Contact email |
|
||||||
|
| `pa-phone` | `phone` | Phone number |
|
||||||
|
| `pa-project-name` | `name` | Project name |
|
||||||
|
|
||||||
|
### QSM-Specific Fields
|
||||||
|
|
||||||
|
| LaTeX Field | Database Field | Description |
|
||||||
|
|-------------|----------------|-------------|
|
||||||
|
| `pa-qsm-*` | Various | QSM-specific checkboxes |
|
||||||
|
| `pa-cost-*` | `costs[].name/amountEur` | Cost positions |
|
||||||
|
|
||||||
|
### VSM-Specific Fields
|
||||||
|
|
||||||
|
| LaTeX Field | Database Field | Description |
|
||||||
|
|-------------|----------------|-------------|
|
||||||
|
| `pa-vsm-*` | Various | VSM-specific fields |
|
||||||
|
| `pa-financing-*` | `financing.*` | Financing options |
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **PDF build fails with XeLaTeX error**
|
||||||
|
- Ensure all LaTeX dependencies are installed
|
||||||
|
- Check for syntax errors in .tex files
|
||||||
|
- Verify fonts are available
|
||||||
|
|
||||||
|
2. **Form fields not filling**
|
||||||
|
- Check field names match between LaTeX and mapping
|
||||||
|
- Verify PDF has form fields (use PDF reader)
|
||||||
|
- Check data types match expected format
|
||||||
|
|
||||||
|
3. **Git worktree errors**
|
||||||
|
- Remove existing worktrees: `git worktree prune`
|
||||||
|
- Re-run setup script
|
||||||
|
|
||||||
|
### Debugging Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List form fields in PDF
|
||||||
|
docker compose exec api python -c "
|
||||||
|
import PyPDF2
|
||||||
|
with open('/app/assets/qsm.pdf', 'rb') as f:
|
||||||
|
pdf = PyPDF2.PdfReader(f)
|
||||||
|
fields = pdf.get_form_text_fields()
|
||||||
|
for name, value in fields.items():
|
||||||
|
print(f'{name}: {value}')
|
||||||
|
"
|
||||||
|
|
||||||
|
# Check LaTeX compilation log
|
||||||
|
cd backend/latex-qsm
|
||||||
|
cat Main.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Version Control**
|
||||||
|
- Keep LaTeX templates in sync with main repo
|
||||||
|
- Tag releases when updating PDF templates
|
||||||
|
- Document field changes in commit messages
|
||||||
|
|
||||||
|
2. **Testing**
|
||||||
|
- Test both empty and filled PDFs
|
||||||
|
- Verify all form fields are accessible
|
||||||
|
- Check PDF compatibility across readers
|
||||||
|
|
||||||
|
3. **Performance**
|
||||||
|
- Pre-compile PDFs rather than generating on demand
|
||||||
|
- Cache compiled PDFs in Docker image
|
||||||
|
- Minimize LaTeX package dependencies
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
1. **Dynamic PDF Generation**
|
||||||
|
- Generate PDFs on-demand from LaTeX
|
||||||
|
- Support custom form layouts
|
||||||
|
- Template versioning system
|
||||||
|
|
||||||
|
2. **Field Validation**
|
||||||
|
- Implement LaTeX-side validation
|
||||||
|
- Sync validation rules with frontend
|
||||||
|
- Generate field documentation from LaTeX
|
||||||
|
|
||||||
|
3. **Multi-language Support**
|
||||||
|
- Internationalize LaTeX templates
|
||||||
|
- Support multiple PDF languages
|
||||||
|
- Dynamic language selection
|
||||||
@ -28,6 +28,7 @@ Welcome to the STUPA PDF API documentation. This directory contains comprehensiv
|
|||||||
- **[Attachment Feature](./ATTACHMENT_FEATURE.md)** - File attachment functionality
|
- **[Attachment Feature](./ATTACHMENT_FEATURE.md)** - File attachment functionality
|
||||||
- **[Comparison Offers Feature](./COMPARISON_OFFERS_FEATURE.md)** - Managing comparison offers
|
- **[Comparison Offers Feature](./COMPARISON_OFFERS_FEATURE.md)** - Managing comparison offers
|
||||||
- **[PDF Processing](./PDF_PROCESSING.md)** - PDF generation and parsing
|
- **[PDF Processing](./PDF_PROCESSING.md)** - PDF generation and parsing
|
||||||
|
- **[PDF Integration](./PDF_INTEGRATION.md)** - LaTeX templates and PDF form integration
|
||||||
|
|
||||||
### 🚢 Deployment
|
### 🚢 Deployment
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
Admin Dashboard
|
Admin Dashboard
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" color="text.secondary">
|
<Typography variant="body1" color="text.secondary">
|
||||||
Verwaltung aller Studienanträge
|
Verwaltung aller Projektanträge
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
|
|||||||
206
scripts/build-pdfs.sh
Executable file
206
scripts/build-pdfs.sh
Executable file
@ -0,0 +1,206 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# STUPA PDF API - Build PDFs from LaTeX sources
|
||||||
|
# This script compiles the LaTeX templates into PDF forms
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Project directories
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
BACKEND_DIR="${PROJECT_ROOT}/backend"
|
||||||
|
ASSETS_DIR="${BACKEND_DIR}/assets"
|
||||||
|
|
||||||
|
# LaTeX template directories
|
||||||
|
LATEX_TEMPLATES_DIR="${BACKEND_DIR}/latex-templates"
|
||||||
|
LATEX_QSM_DIR="${BACKEND_DIR}/latex-qsm"
|
||||||
|
LATEX_VSM_DIR="${BACKEND_DIR}/latex-vsm"
|
||||||
|
|
||||||
|
# Function to check if required tools are installed
|
||||||
|
check_requirements() {
|
||||||
|
echo -e "${BLUE}Checking requirements...${NC}"
|
||||||
|
|
||||||
|
local missing=()
|
||||||
|
|
||||||
|
# Check for git
|
||||||
|
if ! command -v git &> /dev/null; then
|
||||||
|
missing+=("git")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for xelatex
|
||||||
|
if ! command -v xelatex &> /dev/null; then
|
||||||
|
missing+=("xelatex (texlive-xetex)")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for latexmk
|
||||||
|
if ! command -v latexmk &> /dev/null; then
|
||||||
|
missing+=("latexmk")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#missing[@]} -ne 0 ]; then
|
||||||
|
echo -e "${RED}Missing required tools: ${missing[*]}${NC}"
|
||||||
|
echo "Please install them first:"
|
||||||
|
echo " sudo apt-get install git texlive-full texlive-xetex texlive-lang-german texlive-fonts-extra latexmk"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}All requirements satisfied!${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to setup git worktrees
|
||||||
|
setup_worktrees() {
|
||||||
|
echo -e "${BLUE}Setting up LaTeX template worktrees...${NC}"
|
||||||
|
|
||||||
|
cd "${LATEX_TEMPLATES_DIR}"
|
||||||
|
|
||||||
|
# Remove existing worktrees if they exist
|
||||||
|
if [ -d "${LATEX_QSM_DIR}" ]; then
|
||||||
|
echo -e "${YELLOW}Removing existing QSM worktree...${NC}"
|
||||||
|
git worktree remove --force "${LATEX_QSM_DIR}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "${LATEX_VSM_DIR}" ]; then
|
||||||
|
echo -e "${YELLOW}Removing existing VSM worktree...${NC}"
|
||||||
|
git worktree remove --force "${LATEX_VSM_DIR}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create new worktrees
|
||||||
|
echo -e "${BLUE}Creating QSM worktree...${NC}"
|
||||||
|
git worktree add "${LATEX_QSM_DIR}" v1.2/QSM
|
||||||
|
|
||||||
|
echo -e "${BLUE}Creating VSM worktree...${NC}"
|
||||||
|
git worktree add "${LATEX_VSM_DIR}" v1.2/VSM
|
||||||
|
|
||||||
|
echo -e "${GREEN}Worktrees created successfully!${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to build a PDF from LaTeX
|
||||||
|
build_pdf() {
|
||||||
|
local template_name=$1
|
||||||
|
local source_dir=$2
|
||||||
|
local output_name=$3
|
||||||
|
|
||||||
|
echo -e "${BLUE}Building ${template_name} PDF...${NC}"
|
||||||
|
|
||||||
|
# Change to source directory
|
||||||
|
cd "${source_dir}"
|
||||||
|
|
||||||
|
# Clean previous builds
|
||||||
|
rm -f *.aux *.log *.out *.fdb_latexmk *.fls *.synctex.gz *.pdf
|
||||||
|
|
||||||
|
# Build PDF using latexmk with xelatex
|
||||||
|
echo -e "${YELLOW}Running latexmk with xelatex...${NC}"
|
||||||
|
if latexmk -xelatex -interaction=nonstopmode -halt-on-error Main.tex; then
|
||||||
|
echo -e "${GREEN}Build successful!${NC}"
|
||||||
|
|
||||||
|
# Copy to assets directory
|
||||||
|
if [ -f "Main.pdf" ]; then
|
||||||
|
cp "Main.pdf" "${ASSETS_DIR}/${output_name}"
|
||||||
|
echo -e "${GREEN}Copied to ${ASSETS_DIR}/${output_name}${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Error: Main.pdf not found after build${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED}Build failed for ${template_name}${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up build artifacts
|
||||||
|
echo -e "${YELLOW}Cleaning up...${NC}"
|
||||||
|
rm -f *.aux *.log *.out *.fdb_latexmk *.fls *.synctex.gz
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to verify PDFs
|
||||||
|
verify_pdfs() {
|
||||||
|
echo -e "${BLUE}Verifying generated PDFs...${NC}"
|
||||||
|
|
||||||
|
local all_good=true
|
||||||
|
|
||||||
|
# Check QSM PDF
|
||||||
|
if [ -f "${ASSETS_DIR}/qsm.pdf" ]; then
|
||||||
|
local size=$(stat -c%s "${ASSETS_DIR}/qsm.pdf" 2>/dev/null || stat -f%z "${ASSETS_DIR}/qsm.pdf" 2>/dev/null)
|
||||||
|
if [ "$size" -gt 10000 ]; then
|
||||||
|
echo -e "${GREEN}✓ qsm.pdf ($(($size / 1024)) KB)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ qsm.pdf is too small (${size} bytes)${NC}"
|
||||||
|
all_good=false
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ qsm.pdf not found${NC}"
|
||||||
|
all_good=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check VSM PDF
|
||||||
|
if [ -f "${ASSETS_DIR}/vsm.pdf" ]; then
|
||||||
|
local size=$(stat -c%s "${ASSETS_DIR}/vsm.pdf" 2>/dev/null || stat -f%z "${ASSETS_DIR}/vsm.pdf" 2>/dev/null)
|
||||||
|
if [ "$size" -gt 10000 ]; then
|
||||||
|
echo -e "${GREEN}✓ vsm.pdf ($(($size / 1024)) KB)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ vsm.pdf is too small (${size} bytes)${NC}"
|
||||||
|
all_good=false
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ vsm.pdf not found${NC}"
|
||||||
|
all_good=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$all_good" = true ]; then
|
||||||
|
echo -e "${GREEN}All PDFs verified successfully!${NC}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}PDF verification failed!${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
main() {
|
||||||
|
echo -e "${GREEN}STUPA PDF Builder${NC}"
|
||||||
|
echo "=================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check requirements
|
||||||
|
check_requirements
|
||||||
|
|
||||||
|
# Create assets directory if it doesn't exist
|
||||||
|
mkdir -p "${ASSETS_DIR}"
|
||||||
|
|
||||||
|
# Setup worktrees if needed
|
||||||
|
if [ ! -d "${LATEX_QSM_DIR}" ] || [ ! -d "${LATEX_VSM_DIR}" ]; then
|
||||||
|
setup_worktrees
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build QSM PDF
|
||||||
|
if ! build_pdf "QSM" "${LATEX_QSM_DIR}" "qsm.pdf"; then
|
||||||
|
echo -e "${RED}Failed to build QSM PDF${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build VSM PDF
|
||||||
|
if ! build_pdf "VSM" "${LATEX_VSM_DIR}" "vsm.pdf"; then
|
||||||
|
echo -e "${RED}Failed to build VSM PDF${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify results
|
||||||
|
echo ""
|
||||||
|
verify_pdfs
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}PDF build complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Generated PDFs:"
|
||||||
|
echo " - ${ASSETS_DIR}/qsm.pdf"
|
||||||
|
echo " - ${ASSETS_DIR}/vsm.pdf"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
91
scripts/setup-latex-worktrees.sh
Executable file
91
scripts/setup-latex-worktrees.sh
Executable file
@ -0,0 +1,91 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# STUPA PDF API - Setup LaTeX Worktrees
|
||||||
|
# This script sets up the Git worktrees needed for building PDFs
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Project directories
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
BACKEND_DIR="${PROJECT_ROOT}/backend"
|
||||||
|
|
||||||
|
# LaTeX directories
|
||||||
|
LATEX_TEMPLATES_DIR="${BACKEND_DIR}/latex-templates"
|
||||||
|
LATEX_QSM_DIR="${BACKEND_DIR}/latex-qsm"
|
||||||
|
LATEX_VSM_DIR="${BACKEND_DIR}/latex-vsm"
|
||||||
|
|
||||||
|
echo -e "${GREEN}STUPA PDF API - LaTeX Worktree Setup${NC}"
|
||||||
|
echo "====================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if latex-templates submodule is initialized
|
||||||
|
if [ ! -d "${LATEX_TEMPLATES_DIR}" ] || [ ! -f "${LATEX_TEMPLATES_DIR}/.git" ]; then
|
||||||
|
echo -e "${YELLOW}Initializing latex-templates submodule...${NC}"
|
||||||
|
cd "${PROJECT_ROOT}"
|
||||||
|
git submodule update --init --recursive
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Navigate to latex-templates directory
|
||||||
|
cd "${LATEX_TEMPLATES_DIR}"
|
||||||
|
|
||||||
|
# Fetch latest branches
|
||||||
|
echo -e "${BLUE}Fetching latest branches...${NC}"
|
||||||
|
git fetch origin
|
||||||
|
|
||||||
|
# Remove existing worktrees if they exist
|
||||||
|
if [ -d "${LATEX_QSM_DIR}" ]; then
|
||||||
|
echo -e "${YELLOW}Removing existing QSM worktree...${NC}"
|
||||||
|
git worktree remove --force "${LATEX_QSM_DIR}" 2>/dev/null || rm -rf "${LATEX_QSM_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "${LATEX_VSM_DIR}" ]; then
|
||||||
|
echo -e "${YELLOW}Removing existing VSM worktree...${NC}"
|
||||||
|
git worktree remove --force "${LATEX_VSM_DIR}" 2>/dev/null || rm -rf "${LATEX_VSM_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up any stale worktree entries
|
||||||
|
git worktree prune
|
||||||
|
|
||||||
|
# Create QSM worktree
|
||||||
|
echo -e "${BLUE}Creating QSM worktree...${NC}"
|
||||||
|
git worktree add "${LATEX_QSM_DIR}" origin/v1.2/QSM
|
||||||
|
|
||||||
|
# Create VSM worktree
|
||||||
|
echo -e "${BLUE}Creating VSM worktree...${NC}"
|
||||||
|
git worktree add "${LATEX_VSM_DIR}" origin/v1.2/VSM
|
||||||
|
|
||||||
|
# Verify worktrees
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Verifying worktrees...${NC}"
|
||||||
|
|
||||||
|
if [ -f "${LATEX_QSM_DIR}/Main.tex" ]; then
|
||||||
|
echo -e "${GREEN}✓ QSM worktree created successfully${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ QSM worktree creation failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${LATEX_VSM_DIR}/Main.tex" ]; then
|
||||||
|
echo -e "${GREEN}✓ VSM worktree created successfully${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ VSM worktree creation failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}LaTeX worktrees setup complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Worktree locations:"
|
||||||
|
echo " QSM: ${LATEX_QSM_DIR}"
|
||||||
|
echo " VSM: ${LATEX_VSM_DIR}"
|
||||||
|
echo ""
|
||||||
|
echo "You can now build the Docker image with:"
|
||||||
|
echo " docker compose build api"
|
||||||
Loading…
Reference in New Issue
Block a user