maddog 0.0.1
This commit is contained in:
parent
65d314871c
commit
dad14b9c6e
79
pdm.lock
generated
79
pdm.lock
generated
@ -5,7 +5,7 @@
|
||||
groups = ["default"]
|
||||
strategy = ["inherit_metadata"]
|
||||
lock_version = "4.5.0"
|
||||
content_hash = "sha256:8a7c96389b96f926415381c0c955adbb57cb13259926b55a6709aec61f187158"
|
||||
content_hash = "sha256:6792f83567bd7cc25d7f3b7ac32b5348bd2bec72139884aa87acaa3a6167de9f"
|
||||
|
||||
[[metadata.targets]]
|
||||
requires_python = ">=3.12"
|
||||
@ -36,3 +36,80 @@ files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Python port of markdown-it. Markdown parsing, done right!"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"mdurl~=0.1",
|
||||
]
|
||||
files = [
|
||||
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
|
||||
{file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Markdown URL utilities"
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
|
||||
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.18.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Pygments is a syntax highlighting package written in Python."
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
|
||||
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
summary = "Extensions to the standard Python datetime module"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"six>=1.5",
|
||||
]
|
||||
files = [
|
||||
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
|
||||
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.7.1"
|
||||
requires_python = ">=3.7.0"
|
||||
summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"markdown-it-py>=2.2.0",
|
||||
"pygments<3.0.0,>=2.13.0",
|
||||
"typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"",
|
||||
]
|
||||
files = [
|
||||
{file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"},
|
||||
{file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
summary = "Python 2 and 3 compatibility utilities"
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
|
@ -3,11 +3,14 @@ name = "maddog"
|
||||
version = "0.1.0"
|
||||
description = "markdown tools"
|
||||
authors = [{ name = "James Turk", email = "dev@jpt.sh" }]
|
||||
dependencies = ["click>=8.1.7"]
|
||||
dependencies = ["click>=8.1.7", "rich>=13.7.1", "python-dateutil>=2.9.0.post0"]
|
||||
requires-python = ">=3.12"
|
||||
readme = "README.md"
|
||||
license = { text = "MIT" }
|
||||
|
||||
[project.scripts]
|
||||
maddog = "maddog.app:cli"
|
||||
|
||||
[build-system]
|
||||
requires = ["pdm-backend"]
|
||||
build-backend = "pdm.backend"
|
||||
|
110
src/maddog/app.py
Normal file
110
src/maddog/app.py
Normal file
@ -0,0 +1,110 @@
|
||||
import click
|
||||
import pathlib
|
||||
import datetime
|
||||
import re
|
||||
from dateutil import parser
|
||||
from rich.table import Table
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
now = datetime.datetime.now()
|
||||
|
||||
ALL_TODO_RE = re.compile(r"^(TODO|IDEA|DONE):?\s*([^\{\n]+)(\{.*\})?", re.MULTILINE)
|
||||
TODO_TODO_RE = re.compile(r"(TODO):?\s*")
|
||||
|
||||
|
||||
def parse_todo_tag(tag):
|
||||
if not tag:
|
||||
return ""
|
||||
name, val = tag.strip("{}").split(":", 1)
|
||||
if name == "by":
|
||||
dval = parser.parse(val)
|
||||
days_left = dval - now
|
||||
return f"by {dval.date()} ({days_left.days})"
|
||||
else:
|
||||
return f"{name}:{val}"
|
||||
|
||||
|
||||
def scan_contents(file: pathlib.Path) -> dict:
|
||||
text = file.read_text()
|
||||
words = text.split()
|
||||
return {"words": len(words), "todos": len(TODO_TODO_RE.findall(text))}
|
||||
|
||||
|
||||
def pull_todos(file: pathlib.Path):
|
||||
text = file.read_text()
|
||||
todos = ALL_TODO_RE.findall(text)
|
||||
for t in todos:
|
||||
yield {
|
||||
"file": file.name,
|
||||
"status": t[0],
|
||||
"description": t[1],
|
||||
"tags": parse_todo_tag(t[2]),
|
||||
}
|
||||
|
||||
|
||||
def human_readable_date(dt: datetime.datetime) -> str:
|
||||
delta = now - dt
|
||||
if delta < datetime.timedelta(hours=1):
|
||||
return f"{int(delta.total_seconds() / 60)}m ago"
|
||||
elif delta < datetime.timedelta(days=1):
|
||||
return f"{int(delta.total_seconds() / 3600)}h ago"
|
||||
else:
|
||||
return f"{int(delta.total_seconds() / 3600 / 24)}d ago"
|
||||
|
||||
|
||||
def lod_table(data: list[dict]) -> Table:
|
||||
"""list of dicts to Table"""
|
||||
if not data:
|
||||
return "no results"
|
||||
|
||||
table = Table()
|
||||
for key in data[0].keys():
|
||||
table.add_column(key)
|
||||
|
||||
for row in data:
|
||||
table.add_row(*(str(x) for x in row.values()))
|
||||
|
||||
return table
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
pass
|
||||
|
||||
|
||||
def get_files(dirname):
|
||||
if not dirname:
|
||||
dirname = "~/wiki/"
|
||||
p = pathlib.Path(dirname).expanduser()
|
||||
return p.rglob("*.md")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("dirname", nargs=-1)
|
||||
def todos(dirname):
|
||||
output = [] # list of data
|
||||
for file in get_files(dirname):
|
||||
output += pull_todos(file)
|
||||
table = lod_table(output)
|
||||
console.print(table)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("dirname", nargs=-1)
|
||||
def ls(dirname):
|
||||
table = Table()
|
||||
output = []
|
||||
for file in get_files(dirname):
|
||||
st = file.stat()
|
||||
modified = datetime.datetime.fromtimestamp(st.st_mtime)
|
||||
scan = scan_contents(file)
|
||||
output.append(
|
||||
{"file": file.name, "modified": human_readable_date(modified), **scan}
|
||||
)
|
||||
table = lod_table(output)
|
||||
console.print(table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
Loading…
Reference in New Issue
Block a user