HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/textanalyse/tools/rhetorical_structure_tool.py
# tools/rhetorical_structure_tool.py
try:
    import spacy
except Exception as e:
    print(f"Warning: spaCy import disabled in rhetorical_structure_tool: {e}")
    spacy = None

from typing import Any, Dict, List, Tuple

# Lade spaCy‑Modell für Deutsch
if spacy:
    try:
        nlp = spacy.load("de_core_news_sm")
    except Exception as e:
        print(f"Warning: could not load spaCy model: {e}")
        nlp = None
else:
    nlp = None


# -----------------------------------------------------------
# Hilfsfunktionen
# -----------------------------------------------------------

def _segment_edus(doc: "spacy.tokens.Doc") -> List[str]:
    """
    Schneidet einen spaCy‑Doc in Elementary Discourse Units (EDUs).
    Heuristiken:
      • Satzende
      • Komma vor/ nach Subjunktion oder Relativpronomen
      • Gedankenstrich, Semikolon, Doppelpunkt
    """
    edus: List[str] = []
    current_tokens: List[str] = []

    def _flush():
        txt = " ".join(current_tokens).strip()
        if txt:
            edus.append(txt)
        current_tokens.clear()

    for sent in doc.sents:
        for tok in sent:
            current_tokens.append(tok.text)
            # starker Cut
            if tok.is_sent_end or tok.text in {";", ":", "–", "—"}:
                _flush()
            # weicher Cut bei Kommata + Subjunktion/Relativ
            elif tok.text == ",":
                if tok.i + 1 < len(doc):
                    nxt = doc[tok.i + 1]
                    if nxt.dep_ in {"mark", "advmod", "relcl"} or nxt.pos_ == "SCONJ":
                        _flush()
        _flush()
    return edus


def _classify_relation(head_edu: str, dep_edu: str) -> str:
    """
    Grobe Klassifikation der RST‑Relation zwischen zwei EDUs
    (nur für Demo‑Zwecke, keine linguistische Vollständigkeit).
    """
    causal_markers = {"weil", "da", "denn", "zumal"}
    contrast_markers = {"aber", "jedoch", "trotzdem", "doch"}
    cond_markers = {"wenn", "falls", "sofern"}

    dep_first = dep_edu.split()[0].lower()

    if dep_first in causal_markers:
        return "Cause"
    if dep_first in contrast_markers:
        return "Contrast"
    if dep_first in cond_markers:
        return "Condition"
    if dep_first.endswith("lich") or dep_first == "daher":
        return "Result"
    # Default
    return "Elaboration"


def _build_binary_tree(edus: List[str]) -> Dict[str, Any]:
    """
    Baut rekursiv einen binären RST‑Baum (rechtsverzweigend).
    Die Wurzel ist die erste EDU (Nucleus); jede nachfolgende
    EDU wird als Satellite angehängt.
    """
    if len(edus) == 1:
        return {"text": edus[0], "relation": "span", "children": []}

    nucleus, satellite = edus[0], edus[1]
    relation = _classify_relation(nucleus, satellite)

    node = {
        "text": nucleus,
        "relation": relation,
        "children": [
            {"text": satellite, "relation": "satellite", "children": []},
            _build_binary_tree(edus[1:])  # rechtsrekursiv
        ],
    }
    return node


# -----------------------------------------------------------
# Öffentliches API
# -----------------------------------------------------------

def parse_rhetorical_structure(text: str) -> Dict[str, Any]:
    # Stub when spaCy disabled
    if nlp is None:
        return {"error": "Rhetorical structure tool disabled due to spaCy incompatibility."}

    doc = nlp(text)
    edus = _segment_edus(doc)

    if not edus:
        return {"text": text.strip(), "relation": "span", "children": []}

    return _build_binary_tree(edus)