aboutsummaryrefslogtreecommitdiff
path: root/tag_preview.py
diff options
context:
space:
mode:
Diffstat (limited to 'tag_preview.py')
-rw-r--r--tag_preview.py177
1 files changed, 0 insertions, 177 deletions
diff --git a/tag_preview.py b/tag_preview.py
deleted file mode 100644
index d3e28c7..0000000
--- a/tag_preview.py
+++ /dev/null
@@ -1,177 +0,0 @@
-#!/usr/bin/env python3
-"""
-Preview tag assignments for all blog posts.
-Run with --write to actually add #+filetags to org files.
-"""
-import re
-import sys
-from pathlib import Path
-
-CONTENT_DIR = Path("./content/blog")
-
-# Explicit overrides take priority over rules
-OVERRIDES = {
- "2018-11-28-cpp-compiler": ["linux"],
- "2019-12-16-password-security": ["security"],
- "2020-07-26-business-analysis": ["audit"],
- "2020-08-22-redirect-github-pages": ["web"],
- "2020-09-22-internal-audit": ["audit"],
- "2020-12-28-neon-drive": ["personal"],
- "2020-12-29-zork": ["personal"],
- "2021-08-25-audit-sampling": ["audit"],
- "2021-12-04-cisa": ["audit"],
- "2023-05-22-burnout": ["personal"],
- "2023-06-20-audit-review-template": ["audit"],
- "2023-08-18-agile-auditing": ["audit"],
- "2025-05-30-it-audit-career": ["audit"],
- "2025-11-13-wcag": ["audit", "web"],
- "2025-11-23-it-audit-ai": ["audit"],
- "2026-02-21-auditing-aws-iam": ["audit"],
- "2026-02-21-auditing-aws-passwords": ["audit"],
- "2026-03-03-auditing-aws-s3": ["audit"],
- "2020-09-01-visual-recognition": ["linux"],
- "2024-01-26-audit-dashboard": ["audit"],
- "2023-09-19-audit-sql-scripts": ["audit"],
- "2021-04-28-photography": ["personal"],
- "2022-06-22-daily-poetry": ["personal"],
- "2020-09-25-happiness-map": ["personal"],
- "2022-03-03-financial-database": ["self-hosting"],
- "2023-01-03-recent-website-changes": ["personal", "web"],
- "2025-09-25-minimalist-website-redesign": ["web"],
- "2026-02-07-indieweb-carnival-2026-02-intersecting-interests": ["personal", "web"],
-}
-
-TAG_RULES = {
- "audit": [
- "audit", "cisa", "ansoff",
- ],
- "emacs": [
- "emacs", "org-mode", "org-blog", "mu4e", "doom-emacs",
- "emacs-carnival", "gnu-stow",
- ],
- "linux": [
- "linux", "ubuntu", "debian", "fedora", "alpine",
- "customizing-ubuntu", "linux-software", "linux-display",
- "mtp-linux", "serenity-os", "fedora-i3", "fedora-login",
- "flatpak", "zfs", "ubuntu-emergency", "ubuntu-on-macos",
- "steam-on-ntfs", "bash-it", "byobu", "scli",
- "terminal-lifestyle", "curseradio", "st", "flac-to-opus",
- "pinetime", "server-build", "macos", "macos-customization",
- "deprecated-trusted-gpg", "exiftool", "graphene",
- "asahi", "aerc", "rocket-league",
- ],
- "self-hosting": [
- "self-hosting", "homelab", "vps-web-server", "git-server",
- "plex", "nextcloud", "wireguard", "backblaze", "syncthing",
- "docker", "n8n", "prometheus-grafana", "transmission",
- "goaccess", "unifi", "git-mirror", "automating-weblorg",
- ],
- "security": [
- "aes-encryption", "cryptography", "gnupg", "password",
- "server-hardening", "ssh-mfa", "ufw", "hardening",
- "authelia", "wireguard", "njalla-dns", "cloudflare-dns",
- "ditching-cloudflare", "obscura-vpn",
- ],
- "privacy": [
- "privacy", "graphene-os", "session-messenger", "fediverse",
- "digital-minimalism", "privacy-com", "local-llm",
- "email-migration", "dont-say-hello",
- ],
- "web": [
- "nginx", "apache", "php", "css", "redirect", "website-redesign",
- "gemini", "indieweb", "weblorg", "html", "digital-garden",
- "tableau",
- ],
- "personal": [
- "mediocrity", "burnout", "tuesday", "leaving-the-office",
- "vaporwave", "video-game-sales", "reliable-notes",
- "mass-unlike-tumblr", "changing-git-authors", "delete-gitlab",
- "clone-github", "this-year-i-will", "completion",
- "seum", "neon-drive", "zork",
- ],
-}
-
-def assign_tags(slug: str, title: str) -> list[str]:
- if slug in OVERRIDES:
- return OVERRIDES[slug]
- slug_lower = slug.lower()
- title_lower = title.lower()
- tags = []
- for tag, patterns in TAG_RULES.items():
- for pattern in patterns:
- if pattern in slug_lower or pattern in title_lower:
- if tag not in tags:
- tags.append(tag)
- break
- return sorted(tags)
-
-def get_title(path: Path) -> str:
- title_pat = re.compile(r"^#\+title:\s*(.+)$", re.IGNORECASE)
- with path.open("r", encoding="utf-8") as f:
- for line in f:
- m = title_pat.match(line)
- if m:
- return m.group(1).strip()
- return path.stem
-
-def has_filetags(path: Path) -> bool:
- with path.open("r", encoding="utf-8") as f:
- for line in f:
- if re.match(r"^#\+filetags:", line, re.IGNORECASE):
- return True
- return False
-
-def write_filetags(path: Path, tags: list[str]):
- tag_str = ":" + ":".join(tags) + ":"
- content = path.read_text(encoding="utf-8")
- slug_pat = re.compile(r"(#\+slug:\s*.+\n)", re.IGNORECASE)
- m = slug_pat.search(content)
- if m:
- insert_at = m.end()
- new_content = content[:insert_at] + f"#+filetags: {tag_str}\n" + content[insert_at:]
- path.write_text(new_content, encoding="utf-8")
- return True
- return False
-
-write_mode = "--write" in sys.argv
-
-untagged = []
-rows = []
-for org_path in sorted(CONTENT_DIR.glob("*.org")):
- slug = org_path.stem
- title = get_title(org_path)
- tags = assign_tags(slug, title)
- if not tags:
- untagged.append((slug, title))
- rows.append((slug, title, tags))
-
-print(f"{'SLUG':<50} {'TAGS'}")
-print("-" * 85)
-for slug, title, tags in rows:
- tag_str = ", ".join(tags) if tags else "-- UNTAGGED --"
- print(f"{slug:<50} {tag_str}")
-
-print(f"\nTotal: {len(rows)} posts | Untagged: {len(untagged)}")
-if untagged:
- print("\nUntagged posts:")
- for slug, title in untagged:
- print(f" {slug}")
-
-if write_mode:
- written = 0
- skipped = 0
- for org_path in sorted(CONTENT_DIR.glob("*.org")):
- slug = org_path.stem
- title = get_title(org_path)
- tags = assign_tags(slug, title)
- if not tags:
- skipped += 1
- continue
- if has_filetags(org_path):
- skipped += 1
- continue
- if write_filetags(org_path, tags):
- written += 1
- print(f"\nWrote filetags to {written} files, skipped {skipped}.")
-else:
- print("\nRun with --write to apply filetags to org files.")