Modul:ProjectGraph

Aus Kyffhäuser KI
Zur Navigation springen Zur Suche springen

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 rowTitle(row)
  if row.fulltext then return row.fulltext end
  if row[1] and type(row[1]) == "string" then return row[1] end
  if row[1] and type(row[1]) == "table" and row[1].fulltext then return row[1].fulltext end
  return nil
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",
    "mainlabel=-",
    "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 = 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 out = {}
  table.insert(out, "{{#mermaid:")
  table.insert(out, "flowchart LR")

  -- Node labels
  for t, n in pairs(nodes) do
    local label = stripNs(t)
    label = mw.ustring.gsub(label, '"', '\\"')
    table.insert(out, string.format('  %s["%s"]', n.id, label))
  end

  -- edges
  for k, _ in pairs(edges) do
    local fromId, toId = k:match("^(.-)%-%-%>(.-)$")
    if fromId and toId then
      table.insert(out, string.format("  %s --> %s", fromId, toId))
    end
  end

  -- Styles (optional)
  table.insert(out, "  classDef todo fill:#fff,stroke:#333;")
  table.insert(out, "  classDef doing fill:#fff,stroke:#333,stroke-width:2px;")
  table.insert(out, "  classDef done fill:#fff,stroke:#0a0,stroke-width:2px;")
  table.insert(out, "  classDef onhold fill:#fff,stroke:#999,stroke-dasharray: 5 5;")
  table.insert(out, "  classDef blocked fill:#fff,stroke:#f00,stroke-width:2px;")

  for _, n in pairs(nodes) do
    table.insert(out, string.format("  class %s %s;", n.id, norm(n.status)))
  end

  table.insert(out, "}}")
  return table.concat(out, "\n")
end

return p