Modul:ProjectGraph: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Markus (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Markus (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| (4 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 122: | Zeile 122: | ||
end | end | ||
local | local lines = {} | ||
table.insert( | table.insert(lines, "flowchart LR") | ||
-- | -- WICHTIG: keine führenden Leerzeichen am Zeilenanfang, | ||
-- sonst wird es von MediaWiki als "preformatted" behandelt. | |||
for t, n in pairs(nodes) do | for t, n in pairs(nodes) do | ||
local label = stripNs(t) | local label = stripNs(t) | ||
label = mw.ustring.gsub(label, '"', '\\"') | label = mw.ustring.gsub(label, '"', '\\"') | ||
table.insert( | table.insert(lines, string.format('%s["%s"]', n.id, label)) | ||
end | end | ||
for k, _ in pairs(edges) do | for k, _ in pairs(edges) do | ||
local fromId, toId = k:match("^(.-)%-%-%>(.-)$") | local fromId, toId = k:match("^(.-)%-%-%>(.-)$") | ||
if fromId and toId then | if fromId and toId then | ||
table.insert( | table.insert(lines, string.format("%s --> %s", fromId, toId)) | ||
end | end | ||
end | end | ||
table.insert(lines, "classDef todo fill:#fff,stroke:#333;") | |||
table.insert( | table.insert(lines, "classDef doing fill:#fff,stroke:#333,stroke-width:2px;") | ||
table.insert( | table.insert(lines, "classDef done fill:#fff,stroke:#0a0,stroke-width:2px;") | ||
table.insert( | table.insert(lines, "classDef onhold fill:#fff,stroke:#999,stroke-dasharray: 5 5;") | ||
table.insert( | table.insert(lines, "classDef blocked fill:#fff,stroke:#f00,stroke-width:2px;") | ||
table.insert( | |||
for _, n in pairs(nodes) do | for _, n in pairs(nodes) do | ||
table.insert( | table.insert(lines, string.format("class %s %s;", n.id, norm(n.status))) | ||
end | end | ||
table. | local mermaidText = table.concat(lines, "\n") | ||
return | return frame:callParserFunction("mermaid", mermaidText) | ||
end | end | ||
Aktuelle Version vom 25. Februar 2026, 05:18 Uhr
Die Dokumentation für dieses Modul kann unter Modul:ProjectGraph/Doku erstellt werden
local p = {}
local function mermaidId(title)
local base = mw.ustring.gsub(title, "[^%w]", "_")
if mw.ustring.match(base, "^%d") then base = "T_" .. base end
if #base > 40 then
local h = mw.hash.hashValue("md5", title):sub(1, 10)
base = base:sub(1, 30) .. "_" .. h
end
return base
end
local function stripNs(title)
return mw.ustring.gsub(title or "", "^[^:]+:", "")
end
local function getPrintouts(row)
if row.printouts then return row.printouts end
return row
end
local function asList(v)
if not v then return {} end
if type(v) ~= "table" then return { v } end
return v
end
local function valueToString(v)
if type(v) == "table" then
if v.fulltext then return v.fulltext end
if v.value then return tostring(v.value) end
if v[1] then return valueToString(v[1]) end
end
if type(v) == "string" then return v end
if type(v) == "number" then return tostring(v) end
return nil
end
local function canonicalTitle(s)
if not s then return nil end
s = mw.text.trim(tostring(s))
-- remove surrounding [[...]]
s = mw.ustring.gsub(s, "^%[%[", "")
s = mw.ustring.gsub(s, "%]%]$", "")
s = mw.ustring.gsub(s, "%]%].*$", "") -- if extra garbage after ]]
s = mw.ustring.gsub(s, "%[%[", "")
s = mw.ustring.gsub(s, "%]%]", "")
-- if "Title|Label" -> keep only Title
local pipePos = mw.ustring.find(s, "|", 1, true)
if pipePos then
s = mw.ustring.sub(s, 1, pipePos - 1)
end
s = mw.text.trim(s)
if s == "" then return nil end
return s
end
local function rowTitle(row)
local t = row.fulltext or row.title
if not t and row[1] then
if type(row[1]) == "string" then t = row[1]
elseif type(row[1]) == "table" and row[1].fulltext then t = row[1].fulltext end
end
return canonicalTitle(t)
end
local function norm(s)
if not s then return "todo" end
s = mw.ustring.lower(mw.text.trim(tostring(s)))
if s == "" then return "todo" end
return s
end
local function firstText(po, key)
local v = po[key]
if not v then return nil end
if type(v) == "table" and v[1] ~= nil then
return valueToString(v[1])
end
return valueToString(v)
end
function p.mermaid(frame)
local args = frame.args
local parentArgs = frame:getParent() and frame:getParent().args or {}
local project = args.project or parentArgs.project
if not project or project == "" then
return "Fehler: project=... fehlt."
end
local q = {
string.format("[[Gehört zu Projekt::%s]]", project),
"?Abhängig von",
"?Status",
"limit=2000"
}
local res = mw.smw.ask(q) or {}
local nodes = {}
local edges = {}
for _, row in ipairs(res) do
local title = rowTitle(row)
if title then
local po = getPrintouts(row)
local st = norm(firstText(po, "Status"))
nodes[title] = nodes[title] or { id = mermaidId(title), status = st }
local deps = po["Abhängig von"]
for _, dv in ipairs(asList(deps)) do
local depTitle = canonicalTitle(valueToString(dv))
if depTitle and depTitle ~= "" then
nodes[depTitle] = nodes[depTitle] or { id = mermaidId(depTitle), status = "todo" }
edges[nodes[depTitle].id .. "-->" .. nodes[title].id] = true
end
end
end
end
local lines = {}
table.insert(lines, "flowchart LR")
-- WICHTIG: keine führenden Leerzeichen am Zeilenanfang,
-- sonst wird es von MediaWiki als "preformatted" behandelt.
for t, n in pairs(nodes) do
local label = stripNs(t)
label = mw.ustring.gsub(label, '"', '\\"')
table.insert(lines, string.format('%s["%s"]', n.id, label))
end
for k, _ in pairs(edges) do
local fromId, toId = k:match("^(.-)%-%-%>(.-)$")
if fromId and toId then
table.insert(lines, string.format("%s --> %s", fromId, toId))
end
end
table.insert(lines, "classDef todo fill:#fff,stroke:#333;")
table.insert(lines, "classDef doing fill:#fff,stroke:#333,stroke-width:2px;")
table.insert(lines, "classDef done fill:#fff,stroke:#0a0,stroke-width:2px;")
table.insert(lines, "classDef onhold fill:#fff,stroke:#999,stroke-dasharray: 5 5;")
table.insert(lines, "classDef blocked fill:#fff,stroke:#f00,stroke-width:2px;")
for _, n in pairs(nodes) do
table.insert(lines, string.format("class %s %s;", n.id, norm(n.status)))
end
local mermaidText = table.concat(lines, "\n")
return frame:callParserFunction("mermaid", mermaidText)
end