makea-cli 0.2.26__py3-none-any.whl → 0.2.28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- makea_cli/__init__.py +1 -1
- makea_cli/api_client.py +7 -2
- makea_cli/commands/__init__.py +2 -0
- makea_cli/commands/files/__init__.py +0 -0
- makea_cli/commands/files/cmd.py +84 -0
- makea_cli/commands/tasks/cmd.py +53 -15
- makea_cli/file_reader.py +1236 -0
- {makea_cli-0.2.26.dist-info → makea_cli-0.2.28.dist-info}/METADATA +10 -3
- {makea_cli-0.2.26.dist-info → makea_cli-0.2.28.dist-info}/RECORD +11 -8
- {makea_cli-0.2.26.dist-info → makea_cli-0.2.28.dist-info}/WHEEL +0 -0
- {makea_cli-0.2.26.dist-info → makea_cli-0.2.28.dist-info}/entry_points.txt +0 -0
makea_cli/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.2.
|
|
1
|
+
__version__ = "0.2.28"
|
makea_cli/api_client.py
CHANGED
|
@@ -1195,8 +1195,12 @@ def admin_list_tasks(
|
|
|
1195
1195
|
type_: str | None = None,
|
|
1196
1196
|
user: str | None = None,
|
|
1197
1197
|
product: str | None = None,
|
|
1198
|
+
wrong: bool = False,
|
|
1198
1199
|
) -> Any:
|
|
1199
|
-
"""GET /admin/tasks — the team's open tasks; filters are comma-separated.
|
|
1200
|
+
"""GET /admin/tasks — the team's open tasks; filters are comma-separated.
|
|
1201
|
+
``wrong``: the tasks the team marked wrong instead, newest first."""
|
|
1202
|
+
if wrong:
|
|
1203
|
+
return get_json("/admin/tasks", params={"wrong": "1"})
|
|
1200
1204
|
params = {
|
|
1201
1205
|
k: v
|
|
1202
1206
|
for k, v in (("kind", kind), ("type", type_), ("user", user), ("product", product))
|
|
@@ -1211,7 +1215,8 @@ def admin_get_task(task_id: str) -> Any:
|
|
|
1211
1215
|
|
|
1212
1216
|
|
|
1213
1217
|
def admin_close_task(task_id: str, verb: str, body: dict[str, Any]) -> Any:
|
|
1214
|
-
"""POST /admin/tasks/{task_id}/{done|dismiss} — {note, reason?, caused_by?,
|
|
1218
|
+
"""POST /admin/tasks/{task_id}/{done|dismiss|note} — {note, reason?, caused_by?,
|
|
1219
|
+
recurs?, wrong?}. ``note`` adds a note and leaves the task open."""
|
|
1215
1220
|
return post_json(f"/admin/tasks/{quote(task_id, safe='')}/{verb}", body)
|
|
1216
1221
|
|
|
1217
1222
|
|
makea_cli/commands/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ from makea_cli.commands.directory.cmd import register as register_directory
|
|
|
11
11
|
from makea_cli.commands.document.cmd import register as register_document
|
|
12
12
|
from makea_cli.commands.email.cmd import register as register_email
|
|
13
13
|
from makea_cli.commands.factory.cmd import register as register_factory
|
|
14
|
+
from makea_cli.commands.files.cmd import register as register_files
|
|
14
15
|
from makea_cli.commands.flags.cmd import register as register_flags
|
|
15
16
|
from makea_cli.commands.library.cmd import register as register_library
|
|
16
17
|
from makea_cli.commands.metrics.cmd import register as register_metrics
|
|
@@ -38,6 +39,7 @@ def register_all(app: typer.Typer) -> None:
|
|
|
38
39
|
register_email(app)
|
|
39
40
|
register_library(app)
|
|
40
41
|
register_factory(app)
|
|
42
|
+
register_files(app)
|
|
41
43
|
register_brand(app)
|
|
42
44
|
register_playbook(app)
|
|
43
45
|
register_about(app)
|
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""``makea-cli <persona> get file PATH``: read a file a brand sent, locally.
|
|
2
|
+
|
|
3
|
+
Pattern DXF/RUL, Illustrator, EPS, Photoshop, embroidery (PES/DST/EMB), ZIP,
|
|
4
|
+
AVIF/TIFF/SVG. No API call: the file never leaves the machine. Pictures of what
|
|
5
|
+
the file draws are saved next to it so the caller can look at them (an agent
|
|
6
|
+
reads them with its image tool).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import typer
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
from makea_cli.commands._util import print_json_or_exit
|
|
18
|
+
from makea_cli.file_reader import read_path
|
|
19
|
+
from makea_cli.registry import command
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
_HELP = (
|
|
24
|
+
"Read a pattern (DXF/RUL), artwork (AI/EPS/PSD/SVG), embroidery (PES/DST/EMB), ZIP or "
|
|
25
|
+
"AVIF/TIFF file on this machine: what it states plus PNG pictures of what it draws."
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _slug(text: str) -> str:
|
|
30
|
+
return re.sub(r"[^A-Za-z0-9]+", "-", text).strip("-").lower()[:40] or "picture"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def read_file_payload(path: Path, pictures_dir: Path | None) -> dict:
|
|
34
|
+
if not path.is_file():
|
|
35
|
+
raise FileNotFoundError(f"{path} is not a file")
|
|
36
|
+
reading = read_path(str(path))
|
|
37
|
+
out_dir = pictures_dir or path.parent
|
|
38
|
+
saved = []
|
|
39
|
+
pictures = reading.all_pictures()
|
|
40
|
+
if pictures:
|
|
41
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
for i, pic in enumerate(pictures, 1):
|
|
43
|
+
target = out_dir / f"{path.stem}.{i}-{_slug(pic.label)}.png"
|
|
44
|
+
target.write_bytes(pic.png)
|
|
45
|
+
saved.append({"label": pic.label, "path": str(target)})
|
|
46
|
+
payload = {
|
|
47
|
+
"file": str(path),
|
|
48
|
+
"kind": reading.kind,
|
|
49
|
+
"format": reading.format,
|
|
50
|
+
"readable": reading.readable,
|
|
51
|
+
"readout": reading.markdown(),
|
|
52
|
+
"pictures": saved,
|
|
53
|
+
}
|
|
54
|
+
if saved:
|
|
55
|
+
payload["next"] = (
|
|
56
|
+
"Look at each picture to read what it shows; the readout lists only stated facts."
|
|
57
|
+
)
|
|
58
|
+
return payload
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def register(app: typer.Typer) -> None:
|
|
62
|
+
for persona in ("designer", "supplier", "admin"):
|
|
63
|
+
_register_for(app, persona)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _register_for(app: typer.Typer, persona: str) -> None:
|
|
67
|
+
@command(
|
|
68
|
+
app,
|
|
69
|
+
persona,
|
|
70
|
+
"get",
|
|
71
|
+
"file",
|
|
72
|
+
api="LOCAL file_reader",
|
|
73
|
+
external="local",
|
|
74
|
+
help=_HELP,
|
|
75
|
+
)
|
|
76
|
+
def get_file(
|
|
77
|
+
path: Path = typer.Argument(..., help="The file to read."),
|
|
78
|
+
pictures_dir: Path | None = typer.Option(
|
|
79
|
+
None,
|
|
80
|
+
"--pictures-dir",
|
|
81
|
+
help="Where to save the PNG pictures (default: next to the file).",
|
|
82
|
+
),
|
|
83
|
+
) -> None:
|
|
84
|
+
print_json_or_exit(console, lambda: read_file_payload(path, pictures_dir))
|
makea_cli/commands/tasks/cmd.py
CHANGED
|
@@ -12,7 +12,10 @@ read as one order's problem), ``note`` (a slip of Makea's or the platform's),
|
|
|
12
12
|
|
|
13
13
|
The change a task asks for is made with the command that owns it (``admin
|
|
14
14
|
create rule``, ``admin update user``, ``admin update about`` …); ``update
|
|
15
|
-
task --done`` then records that it was handled.
|
|
15
|
+
task --done`` then records that it was handled. ``update task --note`` alone
|
|
16
|
+
adds a note and leaves the task open; ``--dismiss --wrong`` says the task
|
|
17
|
+
should never have been raised (Beeboo's next reading is told, and ``list
|
|
18
|
+
task --wrong`` lists every such task). Only notes the team handles
|
|
16
19
|
off the platform can be marked done; the others close by themselves when the
|
|
17
20
|
fact behind them lands, and ``get task`` says which fact.
|
|
18
21
|
"""
|
|
@@ -44,6 +47,10 @@ Examples
|
|
|
44
47
|
makea-cli admin update task <task_id> --done --note "Asked the brand on WhatsApp" \\
|
|
45
48
|
--reason "The brand changed the cuff after approval" --caused-by brand --once
|
|
46
49
|
makea-cli admin update task <task_id> --dismiss --note "Brand keeps GBP"
|
|
50
|
+
makea-cli admin update task <task_id> --note "Asked the brand, waiting"
|
|
51
|
+
makea-cli admin update task <task_id> --dismiss --wrong \
|
|
52
|
+
--note "The factory was confirming the file, not reporting a mistake"
|
|
53
|
+
makea-cli admin list task --wrong
|
|
47
54
|
""".strip()
|
|
48
55
|
|
|
49
56
|
|
|
@@ -56,8 +63,9 @@ def register(app: typer.Typer) -> None:
|
|
|
56
63
|
api="GET /admin/tasks",
|
|
57
64
|
help=(
|
|
58
65
|
"GET /admin/tasks — the Makea team's open tasks: task_id, type, kind, title, "
|
|
59
|
-
"owner (the account), product, due date, can_mark_done; counts by kind. "
|
|
60
|
-
"--user adds that account's own cards the team may settle (account.*)."
|
|
66
|
+
"owner (the account), product, due date, can_mark_done, notes; counts by kind. "
|
|
67
|
+
"--user adds that account's own cards the team may settle (account.*). "
|
|
68
|
+
"--wrong lists the tasks the team marked wrong instead, newest first."
|
|
61
69
|
),
|
|
62
70
|
epilog=_TASKS_EPILOG,
|
|
63
71
|
)
|
|
@@ -72,11 +80,18 @@ def register(app: typer.Typer) -> None:
|
|
|
72
80
|
None, "--user", "-u", help="One account: user_id or email."
|
|
73
81
|
),
|
|
74
82
|
product: str | None = typer.Option(None, "--product", "-p", help="One product id."),
|
|
83
|
+
wrong: bool = typer.Option(
|
|
84
|
+
False, "--wrong", help="The tasks marked wrong (closed), with why, newest first."
|
|
85
|
+
),
|
|
75
86
|
) -> None:
|
|
76
|
-
"""List the Makea team's open tasks."""
|
|
87
|
+
"""List the Makea team's open tasks, or the ones marked wrong."""
|
|
88
|
+
if wrong and (kind or type_ or user or product):
|
|
89
|
+
raise typer.BadParameter("--wrong takes no filters")
|
|
77
90
|
print_json_or_exit(
|
|
78
91
|
console,
|
|
79
|
-
lambda: api_client.admin_list_tasks(
|
|
92
|
+
lambda: api_client.admin_list_tasks(
|
|
93
|
+
kind=kind, type_=type_, user=user, product=product, wrong=wrong
|
|
94
|
+
),
|
|
80
95
|
)
|
|
81
96
|
|
|
82
97
|
@command(
|
|
@@ -101,19 +116,23 @@ def register(app: typer.Typer) -> None:
|
|
|
101
116
|
"admin",
|
|
102
117
|
"update",
|
|
103
118
|
"task",
|
|
104
|
-
api="POST /admin/tasks/{task_id}/{done|dismiss}",
|
|
119
|
+
api="POST /admin/tasks/{task_id}/{done|dismiss|note}",
|
|
105
120
|
side_effects=[
|
|
106
|
-
"closes the task for the whole Makea team",
|
|
121
|
+
"with --done or --dismiss, closes the task for the whole Makea team",
|
|
122
|
+
"with --note alone, adds a note the team sees; the task stays open",
|
|
107
123
|
"with --reason, Beeboo's next reading of the chats uses that reason",
|
|
124
|
+
"with --wrong, Beeboo's next reading is told the finding was wrong",
|
|
108
125
|
],
|
|
109
126
|
approval="ask",
|
|
110
127
|
reversible=False,
|
|
111
128
|
idempotent=False,
|
|
112
129
|
help=(
|
|
113
|
-
"POST /admin/tasks/{task_id}/{done|dismiss} — close a task: --done once
|
|
114
|
-
"handled (notes the team handles off the platform only), --dismiss when
|
|
115
|
-
"will not be done
|
|
116
|
-
"
|
|
130
|
+
"POST /admin/tasks/{task_id}/{done|dismiss|note} — close a task: --done once "
|
|
131
|
+
"it is handled (notes the team handles off the platform only), --dismiss when "
|
|
132
|
+
"it will not be done, --dismiss --wrong when it should never have been raised "
|
|
133
|
+
"(the note says why). --note required; --note alone adds a note and leaves the "
|
|
134
|
+
"task open. On a held lesson, --reason records why it happened for Beeboo's "
|
|
135
|
+
"next reading."
|
|
117
136
|
),
|
|
118
137
|
epilog=_TASKS_EPILOG,
|
|
119
138
|
)
|
|
@@ -121,7 +140,17 @@ def register(app: typer.Typer) -> None:
|
|
|
121
140
|
task_id: str = typer.Argument(..., help="Task id (from list task)."),
|
|
122
141
|
done: bool = typer.Option(False, "--done", help="It was handled."),
|
|
123
142
|
dismiss: bool = typer.Option(False, "--dismiss", help="It will not be done."),
|
|
124
|
-
|
|
143
|
+
wrong: bool = typer.Option(
|
|
144
|
+
False,
|
|
145
|
+
"--wrong",
|
|
146
|
+
help="With --dismiss: it should never have been raised; --note says why.",
|
|
147
|
+
),
|
|
148
|
+
note: str = typer.Option(
|
|
149
|
+
...,
|
|
150
|
+
"--note",
|
|
151
|
+
"-n",
|
|
152
|
+
help="What was done, or why not (required). Alone: a note; the task stays open.",
|
|
153
|
+
),
|
|
125
154
|
reason: str | None = typer.Option(
|
|
126
155
|
None,
|
|
127
156
|
"--reason",
|
|
@@ -136,13 +165,22 @@ def register(app: typer.Typer) -> None:
|
|
|
136
165
|
help="With --reason: it will happen again, or it was this order only.",
|
|
137
166
|
),
|
|
138
167
|
) -> None:
|
|
139
|
-
"""Mark a task done, or
|
|
140
|
-
if done
|
|
141
|
-
raise typer.BadParameter("pass
|
|
168
|
+
"""Mark a task done, dismiss it, or add a note."""
|
|
169
|
+
if done and dismiss:
|
|
170
|
+
raise typer.BadParameter("pass at most one of --done, --dismiss")
|
|
171
|
+
if wrong and not dismiss:
|
|
172
|
+
raise typer.BadParameter("--wrong goes with --dismiss")
|
|
142
173
|
clean_note = " ".join(note.split())
|
|
143
174
|
if not clean_note:
|
|
144
175
|
raise typer.BadParameter("--note must not be empty")
|
|
145
176
|
body: dict[str, Any] = {"note": clean_note}
|
|
177
|
+
if not (done or dismiss):
|
|
178
|
+
if reason is not None or caused_by is not None or recurs is not None:
|
|
179
|
+
raise typer.BadParameter("--reason, --caused-by, --recurs/--once go with --done")
|
|
180
|
+
print_json_or_exit(console, lambda: api_client.admin_close_task(task_id, "note", body))
|
|
181
|
+
return
|
|
182
|
+
if wrong:
|
|
183
|
+
body["wrong"] = True
|
|
146
184
|
if reason is not None:
|
|
147
185
|
if not done:
|
|
148
186
|
raise typer.BadParameter("--reason goes with --done")
|