move from click cli -> pydantic-settings cli
This commit is contained in:
parent
5411e55c17
commit
c0c53dc5fc
|
|
@ -6,8 +6,6 @@ authors = [{ name = "gitmatt", email = "git@publicmatt.com" }]
|
|||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"click>=8.1.8",
|
||||
"click-default-group>=1.2.4",
|
||||
"jinja2>=3.1.5",
|
||||
"lxml>=5.3.1",
|
||||
"markdown>=3.7",
|
||||
|
|
@ -18,7 +16,7 @@ dependencies = [
|
|||
]
|
||||
|
||||
[project.scripts]
|
||||
plan = "plan:cli.cli"
|
||||
plan = "plan:__main__.cli"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||
|
||||
from .config import Config
|
||||
|
||||
config = Config()
|
||||
|
||||
|
||||
def get_template(template_name: str = "plan.html", plan_path: Path | None = None):
|
||||
""" """
|
||||
templates = Environment(
|
||||
loader=PackageLoader("plan"), autoescape=select_autoescape()
|
||||
)
|
||||
template = templates.get_template(template_name)
|
||||
today = datetime.today().strftime("%Y-%m-%d")
|
||||
return template.render(today=today, plan_path=plan_path)
|
||||
|
||||
|
||||
def plan_path() -> Path:
|
||||
"""
|
||||
returns: Path to current date's .plan.md
|
||||
"""
|
||||
today = datetime.today().strftime("%Y-%m-%d")
|
||||
path = config.app_path / f"{today}.md"
|
||||
return path
|
||||
|
||||
|
||||
def print_plan(plan_path: Path):
|
||||
"""
|
||||
send .plan.md to printer.
|
||||
|
||||
uses pandoc and lp shell tools.
|
||||
"""
|
||||
# preamble ="""
|
||||
# ---
|
||||
# documentclass: extarticle
|
||||
# fontsize: 20pt
|
||||
# ---
|
||||
# """
|
||||
cat_process = subprocess.Popen(["cat", str(plan_path)], stdout=subprocess.PIPE)
|
||||
pandoc_process = subprocess.Popen(
|
||||
["pandoc", "-t", "pdf", "-V", "geometry:margin=.5in", "-"],
|
||||
stdin=cat_process.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
subprocess.run(
|
||||
["lp", "-o", "sides=two-sided-long-edge", "-o", "number-up=2"],
|
||||
stdin=pandoc_process.stdout,
|
||||
)
|
||||
|
||||
|
||||
def create_and_open_template(plan: Path, name: str = "plan.html") -> int:
|
||||
plan.parent.mkdir(parents=True, exist_ok=True)
|
||||
plan.touch()
|
||||
|
||||
template = get_template(name, plan_path=plan)
|
||||
with plan.open("w") as f:
|
||||
f.write(template)
|
||||
|
||||
editor = os.getenv("EDITOR", "nvim")
|
||||
os.chdir(plan.parent)
|
||||
p = subprocess.Popen([editor, str(plan)])
|
||||
sys.exit(p.wait())
|
||||
|
||||
|
||||
def open_in_editor(plan: Path):
|
||||
plan.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not plan.exists():
|
||||
plan.touch()
|
||||
with plan.open("w") as f:
|
||||
f.write(default_template(plan_path=plan))
|
||||
editor = os.getenv("EDITOR", "nvim")
|
||||
os.chdir(Path().home() / ".plan")
|
||||
subprocess.run([editor, str(plan)])
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from pydantic_settings import (
|
||||
CliApp,
|
||||
)
|
||||
|
||||
from .cli import PlanCli
|
||||
|
||||
|
||||
def cli():
|
||||
app = CliApp()
|
||||
app.run(PlanCli)
|
||||
|
|
@ -1,54 +1,20 @@
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
from click_default_group import DefaultGroup
|
||||
|
||||
from .main import (
|
||||
create_plan,
|
||||
edit_plan,
|
||||
plan_path,
|
||||
print_plan,
|
||||
from pydantic import Field
|
||||
from pydantic_settings import (
|
||||
BaseSettings,
|
||||
CliApp,
|
||||
CliSubCommand,
|
||||
get_subcommand,
|
||||
)
|
||||
|
||||
|
||||
@click.group(cls=DefaultGroup, default="main", default_if_no_args=True)
|
||||
def cli():
|
||||
pass
|
||||
from .commands import main
|
||||
|
||||
|
||||
@cli.command("print")
|
||||
@click.option(
|
||||
"--plan_path",
|
||||
"-p",
|
||||
"plan_path",
|
||||
type=click.Path(exists=True, dir_okay=False, readable=True, path_type=Path),
|
||||
default=plan_path(),
|
||||
)
|
||||
def send_to_printer(plan_path: Path):
|
||||
print_plan(plan_path)
|
||||
class PlanCli(BaseSettings):
|
||||
main_: CliSubCommand[main.Main] = Field(alias="main")
|
||||
print_: CliSubCommand[main.Print] = Field(alias="print")
|
||||
|
||||
|
||||
@cli.command("main")
|
||||
@click.argument("template_name", required=True, default="plan.html")
|
||||
@click.option(
|
||||
"--print/--no-print",
|
||||
"is_print",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
)
|
||||
@click.option("--quiet", "quiet", type=bool, is_flag=True, default=True)
|
||||
def open_today(template_name, is_print: bool = False, quiet: bool = True):
|
||||
"""
|
||||
open today
|
||||
|
||||
plan {:template|plan.html} {--quiet} {--print}
|
||||
|
||||
:template can be one of: {plan|work}.html
|
||||
"""
|
||||
path = plan_path()
|
||||
content = create_plan(plan_path=path, name=template_name)
|
||||
if is_print:
|
||||
print(content)
|
||||
def cli_cmd(self) -> None:
|
||||
if get_subcommand(self, is_required=False) is None:
|
||||
CliApp.run(main.Main)
|
||||
else:
|
||||
sys.exit(edit_plan(path))
|
||||
CliApp.run_subcommand(self)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic_settings import (
|
||||
BaseSettings,
|
||||
)
|
||||
|
||||
|
||||
class Base(BaseSettings):
|
||||
model_config = {"env_file": ".env", "env_prefix": "PLAN_", "extra": "ignore"}
|
||||
base_path: Path = Field(
|
||||
default=Path().home() / ".plan", description="base path for .plan files"
|
||||
)
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import Field, computed_field
|
||||
from pydantic_settings import (
|
||||
CliImplicitFlag,
|
||||
CliPositionalArg,
|
||||
)
|
||||
|
||||
from plan.commands import Base
|
||||
from plan.repository import PlanRepository
|
||||
|
||||
|
||||
class Main(Base):
|
||||
"""
|
||||
open today's plan
|
||||
|
||||
:template can be one of: {plan|work}
|
||||
"""
|
||||
|
||||
template_name: CliPositionalArg[str] = Field(default="plan")
|
||||
|
||||
@computed_field()
|
||||
@property
|
||||
def template(self) -> str:
|
||||
return f"{self.template_name}.md"
|
||||
|
||||
print: CliImplicitFlag[bool] = Field(default=False)
|
||||
|
||||
def create(self, force=False) -> str:
|
||||
plan_path = PlanRepository.path_for_date(base_path=self.base_path)
|
||||
plan_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if not plan_path.exists() or force:
|
||||
content = PlanRepository.get_template(self.template)
|
||||
with plan_path.open("w") as f:
|
||||
f.write(content)
|
||||
else:
|
||||
content = self.get(plan_path)
|
||||
return content
|
||||
|
||||
def get(self, plan_path: Path) -> str:
|
||||
with plan_path.open("r") as f:
|
||||
content = f.read()
|
||||
return content
|
||||
|
||||
def edit(self) -> int:
|
||||
plan_path = PlanRepository.path_for_date(base_path=self.base_path)
|
||||
editor = os.getenv("EDITOR", "nvim")
|
||||
os.chdir(plan_path.parent)
|
||||
p = subprocess.Popen([editor, str(plan_path)])
|
||||
return p.wait()
|
||||
|
||||
def cli_cmd(self) -> None:
|
||||
content = self.create()
|
||||
if self.print:
|
||||
print(content)
|
||||
else:
|
||||
sys.exit(self.edit())
|
||||
|
||||
|
||||
class Print(Base):
|
||||
PREAMBLE: ClassVar[str] = """
|
||||
---
|
||||
documentclass: extarticle
|
||||
fontsize: 20pt
|
||||
---
|
||||
"""
|
||||
|
||||
def print_plan(self, plan_path: Path):
|
||||
"""
|
||||
send .plan.md to printer.
|
||||
|
||||
uses pandoc and lp shell tools.
|
||||
"""
|
||||
preamble = subprocess.Popen(
|
||||
["echo", "-n", self.PREAMBLE], stdout=subprocess.PIPE
|
||||
)
|
||||
cat = subprocess.Popen(
|
||||
["cat", "-", str(plan_path)],
|
||||
stdin=preamble.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
pandoc = subprocess.Popen(
|
||||
["pandoc", "-t", "pdf", "-V", "geometry:margin=.5in", "-"],
|
||||
stdin=cat.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
send = subprocess.Popen(
|
||||
["lp", "-o", "sides=two-sided-long-edge", "-o", "number-up=2"],
|
||||
stdin=pandoc.stdout,
|
||||
)
|
||||
sys.exit(send.wait())
|
||||
|
||||
def plan_path(self, date: datetime | None = None) -> Path:
|
||||
"""
|
||||
returns: Path to current date's .plan.md
|
||||
"""
|
||||
if date is None:
|
||||
date = datetime.today()
|
||||
today = date.strftime("%Y-%m-%d")
|
||||
path = self.base_path / f"{today}.md"
|
||||
return path
|
||||
|
||||
def cli_cmd(self):
|
||||
path = self.plan_path()
|
||||
self.print_plan(path)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
def default_app_path():
|
||||
path = Path().home() / ".plan"
|
||||
return path
|
||||
|
||||
|
||||
class Config(BaseSettings):
|
||||
model_config = {"env_file": ".env", "env_prefix": "PLAN_", "extra": "ignore"}
|
||||
app_path: Path = Field(default_factory=default_app_path)
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from .config import Config
|
||||
|
||||
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||
|
||||
config = Config()
|
||||
|
||||
|
||||
def get_template(template_name: str = "plan.html", plan_path: Path | None = None):
|
||||
""" """
|
||||
templates = Environment(
|
||||
loader=PackageLoader("plan"), autoescape=select_autoescape()
|
||||
)
|
||||
template = templates.get_template(template_name)
|
||||
today = datetime.today().strftime("%Y-%m-%d")
|
||||
return template.render(today=today, plan_path=plan_path)
|
||||
|
||||
|
||||
def plan_path(date: datetime | None = None) -> Path:
|
||||
"""
|
||||
returns: Path to current date's .plan.md
|
||||
"""
|
||||
if date is None:
|
||||
date = datetime.today()
|
||||
today = date.strftime("%Y-%m-%d")
|
||||
path = config.app_path / f"{today}.md"
|
||||
return path
|
||||
|
||||
|
||||
def print_plan(plan_path: Path):
|
||||
"""
|
||||
send .plan.md to printer.
|
||||
|
||||
uses pandoc and lp shell tools.
|
||||
"""
|
||||
preamble = """
|
||||
---
|
||||
documentclass: extarticle
|
||||
fontsize: 20pt
|
||||
---
|
||||
|
||||
"""
|
||||
preamble = subprocess.Popen(["echo", "-n", preamble], stdout=subprocess.PIPE)
|
||||
cat = subprocess.Popen(
|
||||
["cat", "-", str(plan_path)],
|
||||
stdin=preamble.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
pandoc = subprocess.Popen(
|
||||
["pandoc", "-t", "pdf", "-V", "geometry:margin=.5in", "-"],
|
||||
stdin=cat.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
send = subprocess.Popen(
|
||||
["lp", "-o", "sides=two-sided-long-edge", "-o", "number-up=2"],
|
||||
stdin=pandoc.stdout,
|
||||
)
|
||||
sys.exit(send.wait())
|
||||
|
||||
|
||||
def create_plan(plan_path: Path, name: str = "plan.html", force=False) -> str:
|
||||
plan_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if not plan_path.exists() or force:
|
||||
content = get_template(name, plan_path=plan_path)
|
||||
with plan_path.open("w") as f:
|
||||
f.write(content)
|
||||
else:
|
||||
content = get_plan(plan_path)
|
||||
return content
|
||||
|
||||
|
||||
def get_plan(plan_path: Path) -> str:
|
||||
with plan_path.open("r") as f:
|
||||
content = f.read()
|
||||
return content
|
||||
|
||||
|
||||
def edit_plan(plan_path: Path) -> int:
|
||||
editor = os.getenv("EDITOR", "nvim")
|
||||
os.chdir(plan_path.parent)
|
||||
p = subprocess.Popen([editor, str(plan_path)])
|
||||
return p.wait()
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||
|
||||
|
||||
class PlanRepository:
|
||||
@classmethod
|
||||
def path_for_date(cls, base_path: Path, date: datetime | None = None) -> Path:
|
||||
"""
|
||||
returns: Path to current date's .plan.md
|
||||
"""
|
||||
if date is None:
|
||||
date = datetime.today()
|
||||
today = date.strftime("%Y-%m-%d")
|
||||
path = base_path / f"{today}.md"
|
||||
return path
|
||||
|
||||
@classmethod
|
||||
def get_template(cls, template_name: str = "plan.html"):
|
||||
""" """
|
||||
templates = Environment(
|
||||
loader=PackageLoader("plan"), autoescape=select_autoescape()
|
||||
)
|
||||
template = templates.get_template(template_name)
|
||||
today = datetime.today().strftime("%Y-%m-%d")
|
||||
return template.render(today=today)
|
||||
28
uv.lock
28
uv.lock
|
|
@ -24,30 +24,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click-default-group"
|
||||
version = "1.2.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1d/ce/edb087fb53de63dad3b36408ca30368f438738098e668b78c87f93cd41df/click_default_group-1.2.4.tar.gz", hash = "sha256:eb3f3c99ec0d456ca6cd2a7f08f7d4e91771bef51b01bdd9580cc6450fe1251e", size = 3505 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/1a/aff8bb287a4b1400f69e09a53bd65de96aa5cee5691925b38731c67fc695/click_default_group-1.2.4-py2.py3-none-any.whl", hash = "sha256:9b60486923720e7fc61731bdb32b617039aba820e22e1c88766b1125592eaa5f", size = 4123 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
|
|
@ -215,8 +191,6 @@ name = "plan"
|
|||
version = "0.1.1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "click-default-group" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "lxml" },
|
||||
{ name = "markdown" },
|
||||
|
|
@ -234,8 +208,6 @@ dev = [
|
|||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "click", specifier = ">=8.1.8" },
|
||||
{ name = "click-default-group", specifier = ">=1.2.4" },
|
||||
{ name = "jinja2", specifier = ">=3.1.5" },
|
||||
{ name = "lxml", specifier = ">=5.3.1" },
|
||||
{ name = "markdown", specifier = ">=3.7" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue