mirror of
https://gitverse.ru/kpa39l/file-tree-catalog.git
synced 2026-09-29 09:15:06 +00:00
Initial commit: Hermes skill file-tree-catalog
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a navigable Markdown catalog (КАТАЛОГ_проекта.md) of a large file tree.
|
||||
|
||||
Adapt ROOT (and optionally junk/MAP/docs) for each project, then run:
|
||||
python3 build_catalog.py
|
||||
|
||||
Design rules (from real use):
|
||||
- Mechanical, deterministic: no LLM for the structure/stats.
|
||||
- Recurse top-level dirs + their immediate children only (maxdepth ~2) so the
|
||||
doc stays readable. Summarize bulk design subfolders (note+dwg) as "N файлов".
|
||||
- Tag each file's role from extension (+ naming conventions), Russian labels.
|
||||
- Filter service/junk files from the body but count them in stats.
|
||||
- Sort dirs with key=str.lower for natural locale ordering.
|
||||
|
||||
Reproduce the exact structure used for the "Винный город" СКС/ЛВС/КТСБ archive:
|
||||
<root>/
|
||||
<СИСТЕМА.N>/ e.g. КТСБ.1, ЛВС.2, СКС.3
|
||||
<дата_Rev.XX>/ e.g. 2026.05.18_Rev.01
|
||||
<шифр>.pdf итоговый комплект
|
||||
*_Изменения_*.docx список изменений
|
||||
*_Замечания*.xlsx замечания/ответы
|
||||
note+dwg/ bulk drawings + spec files (collapse to count)
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import datetime
|
||||
import collections
|
||||
|
||||
# >>> EDIT THESE for a new project >>>
|
||||
ROOT = "/mnt/vinogorod/ИТ/1. Винный город/Проекты/2026.05.22_Актуальные комплекты СКС, ЛВС, КТСБ"
|
||||
# subfolders to collapse to a count (engine+Cad sets)
|
||||
BULK_DIR_NAMES = ("note+dwg", "note", "dwg")
|
||||
# service files to exclude from the body (but still counted in the stats line)
|
||||
JUNK_NAMES = {"thumbs.db", "plot.log", "desktop.ini"}
|
||||
JUNK_EXTS = {".db", ".dwl", ".dwl2", ".bak"}
|
||||
# <<< END EDIT <<<
|
||||
|
||||
# numeric suffix so .1/.2 sort after the base in locale order
|
||||
STRIP = str.lstrip # alias to avoid binding w/ layer in comprehension below
|
||||
|
||||
|
||||
def collect(base):
|
||||
"""Return {name: ("FILE", ext_upper) | ("DIR", file_count)} for one level."""
|
||||
out = {}
|
||||
for name in sorted(os.listdir(base), key=str.lower):
|
||||
p = os.path.join(base, name)
|
||||
if name.lower() in JUNK_NAMES or name.startswith("~$"):
|
||||
continue
|
||||
if os.path.isdir(p):
|
||||
out[name] = ("DIR", sum(len(x) for _, _, x in os.walk(p)))
|
||||
else:
|
||||
ext = os.path.splitext(name)[1].lstrip(".").upper()
|
||||
if ext in {e.lstrip(".").upper() for e in JUNK_EXTS}:
|
||||
continue
|
||||
out[name] = ("FILE", ext)
|
||||
return out
|
||||
|
||||
|
||||
def describe(ext):
|
||||
return {
|
||||
"PDF": "итоговый комплект PDF",
|
||||
"DOCX": "список изменений (docx)",
|
||||
"XLSX": "замечания/ответы (xlsx)",
|
||||
"TXT": "изменения (txt)",
|
||||
"DWG": "чертёж AutoCAD (dwg)",
|
||||
"DOC": "документ (doc)",
|
||||
"LOG": "лог",
|
||||
"MD": "каталог/описание (md)",
|
||||
"BAK": "резервная копия (bak)",
|
||||
}.get(ext, ext)
|
||||
|
||||
|
||||
def build(root):
|
||||
# --- stats ---
|
||||
ext_count = collections.Counter()
|
||||
total_files = total_size = junk = 0
|
||||
for dirpath, _, filenames in os.walk(root):
|
||||
for f in filenames:
|
||||
ext = os.path.splitext(f)[1].lower() or "(нет)"
|
||||
ext_count[ext] += 1
|
||||
total_files += 1
|
||||
try:
|
||||
total_size += os.path.getsize(os.path.join(dirpath, f))
|
||||
except OSError:
|
||||
pass
|
||||
if (f.startswith("~$") or f.lower() in JUNK_NAMES
|
||||
or ext in JUNK_EXTS):
|
||||
junk += 1
|
||||
|
||||
L = ["# Каталог проекта", "",
|
||||
"**Путь:** `%s`" % root, "",
|
||||
"**Сформирован:** %s" % datetime.date.today().isoformat(), "",
|
||||
"## Общая статистика", ""]
|
||||
L.append("- Всего файлов: **%d** (~%.2f ГБ)" % (total_files, total_size / 1024**3))
|
||||
parts = []
|
||||
for e, c in ext_count.most_common():
|
||||
parts.append("**%s**: %d" % (e.lstrip(".").upper() or "без расширения", c))
|
||||
L.append("- По типам: %s" % ", ".join(parts))
|
||||
L.append("- Из них служебных (Thumbs.db, ~$, .dwl/.dwl2/.bak): %d (не включены)" % junk)
|
||||
L += ["", "## Структура и содержимое", ""]
|
||||
|
||||
for top in sorted(os.listdir(root), key=str.lower):
|
||||
topp = os.path.join(root, top)
|
||||
if not os.path.isdir(topp):
|
||||
continue
|
||||
L.append("### %s" % top)
|
||||
L.append("")
|
||||
for name, (typ, val) in collect(topp).items():
|
||||
if typ == "DIR":
|
||||
if name.lower() in BULK_DIR_NAMES:
|
||||
L.append("- **`%s/`** — %d файлов (чертежи + спецификации)" % (name, val))
|
||||
else:
|
||||
L.append("**%s**" % name)
|
||||
L.append("")
|
||||
for sub, (styp, sval) in collect(os.path.join(topp, name)).items():
|
||||
if styp == "DIR":
|
||||
if sub.lower() in BULK_DIR_NAMES:
|
||||
L.append("- **`%s/`** — %d файлов (чертежи + спецификации)" % (sub, sval))
|
||||
else:
|
||||
L.append("- **`%s/`** — %d файлов" % (sub, sval))
|
||||
else:
|
||||
L.append("- `%s` — %s" % (sub, describe(sval)))
|
||||
L.append("")
|
||||
else:
|
||||
L.append("- `%s` — %s" % (name, describe(val)))
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = sys.argv[1] if len(sys.argv) > 1 else ROOT
|
||||
text = build(root)
|
||||
out = os.path.join(root, "КАТАЛОГ_проекта.md")
|
||||
with open(out, "w") as fh:
|
||||
fh.write(text)
|
||||
print("OK ->", out, os.path.getsize(out), "байт")
|
||||
Reference in New Issue
Block a user