bocai-cli 0.1.0__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.
- bocai_cli/__init__.py +3 -0
- bocai_cli/api/__init__.py +19 -0
- bocai_cli/api/auth_client.py +26 -0
- bocai_cli/api/card_client.py +85 -0
- bocai_cli/api/checkin_client.py +29 -0
- bocai_cli/api/course_client.py +85 -0
- bocai_cli/api/facade.py +30 -0
- bocai_cli/api/report_client.py +41 -0
- bocai_cli/api/reservation_client.py +29 -0
- bocai_cli/api/transport.py +153 -0
- bocai_cli/client.py +69 -0
- bocai_cli/command_utils.py +28 -0
- bocai_cli/commands/__init__.py +7 -0
- bocai_cli/commands/auth.py +66 -0
- bocai_cli/commands/card.py +260 -0
- bocai_cli/commands/checkin.py +71 -0
- bocai_cli/commands/course.py +260 -0
- bocai_cli/commands/registry.py +36 -0
- bocai_cli/commands/report.py +93 -0
- bocai_cli/commands/reservation.py +71 -0
- bocai_cli/config.py +116 -0
- bocai_cli/date_range_validation.py +48 -0
- bocai_cli/errors.py +22 -0
- bocai_cli/main.py +11 -0
- bocai_cli/output.py +28 -0
- bocai_cli-0.1.0.dist-info/METADATA +83 -0
- bocai_cli-0.1.0.dist-info/RECORD +29 -0
- bocai_cli-0.1.0.dist-info/WHEEL +4 -0
- bocai_cli-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from bocai_cli.api import ApiFacade
|
|
6
|
+
from bocai_cli.command_utils import command_handler
|
|
7
|
+
from bocai_cli.config import load_config
|
|
8
|
+
from bocai_cli.date_range_validation import validate_optional_date_range
|
|
9
|
+
from bocai_cli.output import print_json
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(help="report相关命令。", invoke_without_command=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.callback()
|
|
15
|
+
def report_root(ctx: typer.Context) -> None:
|
|
16
|
+
if ctx.invoked_subcommand is None:
|
|
17
|
+
typer.echo(ctx.get_help())
|
|
18
|
+
raise typer.Exit(code=0)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _build_api() -> ApiFacade:
|
|
22
|
+
config = load_config()
|
|
23
|
+
return ApiFacade(
|
|
24
|
+
base_url=config.base_url,
|
|
25
|
+
token=config.token,
|
|
26
|
+
timeout_seconds=config.timeout_seconds,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.command("revenue", help="获取营收数据")
|
|
31
|
+
@command_handler
|
|
32
|
+
def revenue(
|
|
33
|
+
start_date: str | None = typer.Option(
|
|
34
|
+
None,
|
|
35
|
+
"--start-date",
|
|
36
|
+
help="开始日期,格式 YYYY-MM-DD。",
|
|
37
|
+
),
|
|
38
|
+
end_date: str | None = typer.Option(
|
|
39
|
+
None,
|
|
40
|
+
"--end-date",
|
|
41
|
+
help="结束日期,格式 YYYY-MM-DD。",
|
|
42
|
+
),
|
|
43
|
+
center_id: str | None = typer.Option(
|
|
44
|
+
None,
|
|
45
|
+
"--center-id",
|
|
46
|
+
help="门店ID。",
|
|
47
|
+
),
|
|
48
|
+
) -> None:
|
|
49
|
+
validate_optional_date_range(start_date, end_date)
|
|
50
|
+
|
|
51
|
+
api = _build_api()
|
|
52
|
+
data = api.report.get_revenue(
|
|
53
|
+
start_date=start_date,
|
|
54
|
+
end_date=end_date,
|
|
55
|
+
center_id=center_id,
|
|
56
|
+
)
|
|
57
|
+
print_json({"status": "ok", "data": data})
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@app.command("revenue-detail", help="获取营收明细数据")
|
|
61
|
+
@command_handler
|
|
62
|
+
def revenue_detail(
|
|
63
|
+
detail_type: str = typer.Option(
|
|
64
|
+
...,
|
|
65
|
+
"--type",
|
|
66
|
+
help="营收明细类型。",
|
|
67
|
+
),
|
|
68
|
+
start_date: str | None = typer.Option(
|
|
69
|
+
None,
|
|
70
|
+
"--start-date",
|
|
71
|
+
help="开始日期,格式 YYYY-MM-DD。",
|
|
72
|
+
),
|
|
73
|
+
end_date: str | None = typer.Option(
|
|
74
|
+
None,
|
|
75
|
+
"--end-date",
|
|
76
|
+
help="结束日期,格式 YYYY-MM-DD。",
|
|
77
|
+
),
|
|
78
|
+
center_id: str | None = typer.Option(
|
|
79
|
+
None,
|
|
80
|
+
"--center-id",
|
|
81
|
+
help="门店ID。",
|
|
82
|
+
),
|
|
83
|
+
) -> None:
|
|
84
|
+
validate_optional_date_range(start_date, end_date)
|
|
85
|
+
|
|
86
|
+
api = _build_api()
|
|
87
|
+
data = api.report.get_revenue_detail(
|
|
88
|
+
type_=detail_type,
|
|
89
|
+
start_date=start_date,
|
|
90
|
+
end_date=end_date,
|
|
91
|
+
center_id=center_id,
|
|
92
|
+
)
|
|
93
|
+
print_json({"status": "ok", "data": data})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from bocai_cli.api import ApiFacade
|
|
6
|
+
from bocai_cli.command_utils import command_handler
|
|
7
|
+
from bocai_cli.config import load_config
|
|
8
|
+
from bocai_cli.date_range_validation import validate_date_range
|
|
9
|
+
from bocai_cli.output import print_json
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(help="预约相关命令。", invoke_without_command=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.callback()
|
|
15
|
+
def reservation_root(ctx: typer.Context) -> None:
|
|
16
|
+
if ctx.invoked_subcommand is None:
|
|
17
|
+
typer.echo(ctx.get_help())
|
|
18
|
+
raise typer.Exit(code=0)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _build_api() -> ApiFacade:
|
|
22
|
+
config = load_config()
|
|
23
|
+
return ApiFacade(
|
|
24
|
+
base_url=config.base_url,
|
|
25
|
+
token=config.token,
|
|
26
|
+
timeout_seconds=config.timeout_seconds,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.command("list", help="获取预约记录数据")
|
|
31
|
+
@command_handler
|
|
32
|
+
def list_reservation(
|
|
33
|
+
start_date: str = typer.Option(
|
|
34
|
+
...,
|
|
35
|
+
"--start-date",
|
|
36
|
+
help="开始日期,格式 YYYY-MM-DD。",
|
|
37
|
+
),
|
|
38
|
+
end_date: str = typer.Option(
|
|
39
|
+
...,
|
|
40
|
+
"--end-date",
|
|
41
|
+
help="结束日期,格式 YYYY-MM-DD。",
|
|
42
|
+
),
|
|
43
|
+
page_num: int = typer.Option(
|
|
44
|
+
...,
|
|
45
|
+
"--page-num",
|
|
46
|
+
min=0,
|
|
47
|
+
help="页码,必须大于等于 0。",
|
|
48
|
+
),
|
|
49
|
+
page_size: int = typer.Option(
|
|
50
|
+
...,
|
|
51
|
+
"--page-size",
|
|
52
|
+
min=1,
|
|
53
|
+
help="每页条数,必须大于等于 1。",
|
|
54
|
+
),
|
|
55
|
+
center_id: str | None = typer.Option(
|
|
56
|
+
None,
|
|
57
|
+
"--center-id",
|
|
58
|
+
help="门店ID。",
|
|
59
|
+
),
|
|
60
|
+
) -> None:
|
|
61
|
+
validate_date_range(start_date, end_date)
|
|
62
|
+
|
|
63
|
+
api = _build_api()
|
|
64
|
+
data = api.reservation.list(
|
|
65
|
+
start_date=start_date,
|
|
66
|
+
end_date=end_date,
|
|
67
|
+
page_num=page_num,
|
|
68
|
+
page_size=page_size,
|
|
69
|
+
center_id=center_id,
|
|
70
|
+
)
|
|
71
|
+
print_json({"status": "ok", "data": data})
|
bocai_cli/config.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from dataclasses import asdict, dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import tomli_w
|
|
10
|
+
|
|
11
|
+
from bocai_cli.errors import ConfigValidationError
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import tomllib
|
|
15
|
+
except ModuleNotFoundError: # pragma: no cover
|
|
16
|
+
import tomli as tomllib # type: ignore[no-redef]
|
|
17
|
+
|
|
18
|
+
DEFAULT_CONFIG_PATH = Path.home() / ".config" / "bocai-cli" / "config.toml"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class AppConfig:
|
|
23
|
+
base_url: str | None = None
|
|
24
|
+
token: str | None = None
|
|
25
|
+
timeout_seconds: float = 30.0
|
|
26
|
+
app_id: str | None = None
|
|
27
|
+
secret: str | None = None
|
|
28
|
+
token_url: str | None = None
|
|
29
|
+
token_backup: str | None = None
|
|
30
|
+
token_updated_at: str | None = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_config_path() -> Path:
|
|
34
|
+
raw_path = os.getenv("BOCAI_CLI_CONFIG_PATH")
|
|
35
|
+
if raw_path:
|
|
36
|
+
return Path(raw_path).expanduser()
|
|
37
|
+
return DEFAULT_CONFIG_PATH
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_config(path: Path | None = None) -> AppConfig:
|
|
41
|
+
config_path = path or get_config_path()
|
|
42
|
+
if not config_path.exists():
|
|
43
|
+
return AppConfig()
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
with config_path.open("rb") as f:
|
|
47
|
+
data = tomllib.load(f)
|
|
48
|
+
except tomllib.TOMLDecodeError as exc:
|
|
49
|
+
raise ConfigValidationError(
|
|
50
|
+
"配置文件格式错误,请检查 TOML 语法。",
|
|
51
|
+
details={"config_path": str(config_path)},
|
|
52
|
+
) from exc
|
|
53
|
+
except OSError as exc:
|
|
54
|
+
raise ConfigValidationError(
|
|
55
|
+
"配置文件读取失败,请检查文件权限或路径。",
|
|
56
|
+
details={"config_path": str(config_path)},
|
|
57
|
+
) from exc
|
|
58
|
+
|
|
59
|
+
return AppConfig(
|
|
60
|
+
base_url=_optional_string(data.get("base_url")),
|
|
61
|
+
token=_optional_string(data.get("token")),
|
|
62
|
+
timeout_seconds=_optional_float(data.get("timeout_seconds"), default=30.0),
|
|
63
|
+
app_id=_optional_string(data.get("app_id")),
|
|
64
|
+
secret=_optional_string(data.get("secret")),
|
|
65
|
+
token_url=_optional_string(data.get("token_url")),
|
|
66
|
+
token_backup=_optional_string(data.get("token_backup")),
|
|
67
|
+
token_updated_at=_optional_string(data.get("token_updated_at")),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def save_config(config: AppConfig, path: Path | None = None) -> Path:
|
|
72
|
+
config_path = path or get_config_path()
|
|
73
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
|
|
75
|
+
payload = _to_toml_payload(config)
|
|
76
|
+
with config_path.open("wb") as f:
|
|
77
|
+
f.write(tomli_w.dumps(payload).encode("utf-8"))
|
|
78
|
+
|
|
79
|
+
_enforce_permissions_best_effort(config_path)
|
|
80
|
+
return config_path
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _to_toml_payload(config: AppConfig) -> dict[str, Any]:
|
|
84
|
+
values = asdict(config)
|
|
85
|
+
payload: dict[str, Any] = {}
|
|
86
|
+
for key, value in values.items():
|
|
87
|
+
if value is None:
|
|
88
|
+
continue
|
|
89
|
+
payload[key] = value
|
|
90
|
+
return payload
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _optional_string(raw: Any) -> str | None:
|
|
94
|
+
if raw is None:
|
|
95
|
+
return None
|
|
96
|
+
value = str(raw).strip()
|
|
97
|
+
return value or None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _optional_float(raw: Any, default: float) -> float:
|
|
101
|
+
if raw is None:
|
|
102
|
+
return default
|
|
103
|
+
try:
|
|
104
|
+
return float(raw)
|
|
105
|
+
except (TypeError, ValueError):
|
|
106
|
+
return default
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _enforce_permissions_best_effort(path: Path) -> None:
|
|
110
|
+
if sys.platform.startswith("win"):
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
path.chmod(0o600)
|
|
115
|
+
except OSError:
|
|
116
|
+
return
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import date, datetime
|
|
4
|
+
|
|
5
|
+
from bocai_cli.errors import ConfigValidationError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _parse_date(value: str, *, option_name: str) -> date:
|
|
9
|
+
try:
|
|
10
|
+
return datetime.strptime(value, "%Y-%m-%d").date()
|
|
11
|
+
except ValueError as exc:
|
|
12
|
+
raise ConfigValidationError(
|
|
13
|
+
f"--{option_name} 格式必须为 YYYY-MM-DD。",
|
|
14
|
+
details={"invalid": [option_name]},
|
|
15
|
+
) from exc
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_date_range(start_date: str, end_date: str) -> None:
|
|
19
|
+
start = _parse_date(start_date, option_name="start-date")
|
|
20
|
+
end = _parse_date(end_date, option_name="end-date")
|
|
21
|
+
if start > end:
|
|
22
|
+
raise ConfigValidationError(
|
|
23
|
+
"--start-date 不能晚于 --end-date。",
|
|
24
|
+
details={"start_date": start_date, "end_date": end_date},
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
range_days = (end - start).days + 1
|
|
28
|
+
if range_days > 31:
|
|
29
|
+
raise ConfigValidationError(
|
|
30
|
+
"--start-date 到 --end-date 的时间范围不能超过 31 天。",
|
|
31
|
+
details={
|
|
32
|
+
"start_date": start_date,
|
|
33
|
+
"end_date": end_date,
|
|
34
|
+
"range_days": range_days,
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def validate_optional_date_range(start_date: str | None, end_date: str | None) -> None:
|
|
40
|
+
if (start_date is None) != (end_date is None):
|
|
41
|
+
raise ConfigValidationError("--start-date 和 --end-date 必须同时提供。")
|
|
42
|
+
|
|
43
|
+
if start_date is None and end_date is None:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
assert start_date is not None
|
|
47
|
+
assert end_date is not None
|
|
48
|
+
validate_date_range(start_date, end_date)
|
bocai_cli/errors.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class BocaiCliError(Exception):
|
|
9
|
+
message: str
|
|
10
|
+
status_code: int | None = None
|
|
11
|
+
details: dict[str, Any] = field(default_factory=dict)
|
|
12
|
+
|
|
13
|
+
def __str__(self) -> str:
|
|
14
|
+
return self.message
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ConfigValidationError(BocaiCliError):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ApiRequestError(BocaiCliError):
|
|
22
|
+
pass
|
bocai_cli/main.py
ADDED
bocai_cli/output.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def print_json(payload: dict[str, Any]) -> None:
|
|
10
|
+
typer.echo(json.dumps(payload, ensure_ascii=False))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def print_error(
|
|
14
|
+
message: str,
|
|
15
|
+
*,
|
|
16
|
+
status_code: int | None = None,
|
|
17
|
+
details: dict[str, Any] | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
error_payload: dict[str, Any] = {
|
|
20
|
+
"status": "error",
|
|
21
|
+
"message": message,
|
|
22
|
+
}
|
|
23
|
+
if status_code is not None:
|
|
24
|
+
error_payload["status_code"] = status_code
|
|
25
|
+
if details:
|
|
26
|
+
error_payload["details"] = details
|
|
27
|
+
|
|
28
|
+
print_json(error_payload)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bocai-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI wrapper for SaaS APIs
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: httpx>=0.27.0
|
|
7
|
+
Requires-Dist: tenacity>=9.0.0
|
|
8
|
+
Requires-Dist: tomli-w>=1.0.0
|
|
9
|
+
Requires-Dist: typer>=0.12.3
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=8.2.0; extra == 'dev'
|
|
12
|
+
Requires-Dist: respx>=0.21.1; extra == 'dev'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# bocai-cli
|
|
16
|
+
|
|
17
|
+
使用 Python + Typer 封装 SaaS API 的命令行工具。
|
|
18
|
+
|
|
19
|
+
## 安装
|
|
20
|
+
|
|
21
|
+
### 1) 本地路径安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m pip install .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2) 可编辑安装(开发)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
python -m pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3) 私有 Git 安装
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install "git+<repo-url>@<tag-or-commit>"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 4)使用构建生成的wheel安装
|
|
40
|
+
```bash
|
|
41
|
+
pip install xxx.whl(e.g. bocai_cli-0.1.0-py3-none-any.whl)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 构建 wheel(私仓分发),同时打包依赖
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python -m pip wheel . -w dist
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
构建后会在 `dist/` 目录下生成类似 `bocai_cli-0.1.0-py3-none-any.whl` 的文件。
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## 构建 wheel(私仓分发),不打包依赖
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python -m pip wheel . --no-deps -w dist
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
构建后会在 `dist/` 目录下生成类似 `bocai_cli-0.1.0-py3-none-any.whl` 的文件。
|
|
60
|
+
|
|
61
|
+
## 安装后验证
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bocai-cli --help
|
|
65
|
+
bocai-cli auth --help
|
|
66
|
+
bocai-cli report --help
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 配置文件
|
|
70
|
+
### Windows系统
|
|
71
|
+
C:\Users\{用户名}\.config\bocai-cli\config.toml
|
|
72
|
+
### Linux系统
|
|
73
|
+
#### 先读环境变量 BOCAI_CLI_CONFIG_PATH
|
|
74
|
+
#### 没有就读默认路径:~/.config/bocai-cli/config.toml
|
|
75
|
+
/home/{用户名}/.config/bocai-cli\config.toml (非root用户)
|
|
76
|
+
|
|
77
|
+
/root/.config/bocai-cli\config.toml (root用户)
|
|
78
|
+
### config.toml配置内容(base_url,app_id,secret按实际替换)
|
|
79
|
+
base_url = "http://10.0.0.227:10086"
|
|
80
|
+
|
|
81
|
+
app_id = "test_app_001"
|
|
82
|
+
|
|
83
|
+
secret = "test_secret_123456"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
bocai_cli/__init__.py,sha256=4t_crzhrLum--oyowUMxtjBTzUtWp7oRTF22ewEvJG4,49
|
|
2
|
+
bocai_cli/client.py,sha256=xjtirUDW9ip3kHAhKaTnaXiqtZaFMqx81Yx5KFQogzs,1754
|
|
3
|
+
bocai_cli/command_utils.py,sha256=JlE_fh_cWRpEnoUamNLrZSEm-GOWy-QdiXHDW3Xgt9c,764
|
|
4
|
+
bocai_cli/config.py,sha256=f5DZ_zd7hkLZfNjzqrdZvg2HVO94hZZJ3GwH4TXVWyI,3254
|
|
5
|
+
bocai_cli/date_range_validation.py,sha256=5mKA3I13-VmdNrHJTz4tpf902tqrwVumS6tWEUvadqM,1584
|
|
6
|
+
bocai_cli/errors.py,sha256=1r9104aIibAmGIIGOBwwggZNPl-PpT-6mZo-wMi5qa0,418
|
|
7
|
+
bocai_cli/main.py,sha256=VjiHlam7LeX9OKbM8H3_Wye_iqcu-XO2n2sMexhUWW8,234
|
|
8
|
+
bocai_cli/output.py,sha256=c-kvmVpuTxY-CEVuRv93nyEGy5Tu-jPGsrfuHKd3F4o,598
|
|
9
|
+
bocai_cli/api/__init__.py,sha256=72HnDON6aRw-hcNCDkeUYvCtcKG_VAiAR82h39kd-dE,588
|
|
10
|
+
bocai_cli/api/auth_client.py,sha256=Oec2H4C1ofQKS9ePFVy-M9nYm7ZBzjuoQmAYy2em0U0,830
|
|
11
|
+
bocai_cli/api/card_client.py,sha256=HqBvpBop9T2phJtglR0Z5RPOeHxaGltRU16LPBit7r0,2294
|
|
12
|
+
bocai_cli/api/checkin_client.py,sha256=H620MXbPtc_06ncVuQgj7ksh2ZkiTUEntxWZeb0J_ts,755
|
|
13
|
+
bocai_cli/api/course_client.py,sha256=dDx-a-SBx2ilUcDudqqh4BTmCUa0oFkjlokj0_l3dCo,2300
|
|
14
|
+
bocai_cli/api/facade.py,sha256=-ok7YPlOnDMAYZJCw5svOjqS6RJANeoi0z6giJGvWPs,1040
|
|
15
|
+
bocai_cli/api/report_client.py,sha256=pQQ71_gIowMH3AX9cVnOd3UZWpgf5gpoNKhkx-2qZCY,1343
|
|
16
|
+
bocai_cli/api/reservation_client.py,sha256=4DmgazRQIPLtuvxNR_p-QUyzexk6NMemtuXz3uX4wDw,763
|
|
17
|
+
bocai_cli/api/transport.py,sha256=BAJWm5dlPpl7lOPaxj2V1ebXlj_VX2ostJMiPn13Ss0,5368
|
|
18
|
+
bocai_cli/commands/__init__.py,sha256=gAOyyeRGRJiAV0sL3in9-ETCvJCDVUIvXbgsGJ3RsEE,374
|
|
19
|
+
bocai_cli/commands/auth.py,sha256=EL9KKZ15Lw9Fa5IEFiEJB4fqcjwlksRCXmGqt6kDCDs,1877
|
|
20
|
+
bocai_cli/commands/card.py,sha256=vzELiduvzmh8zT25qDvwuPMsr6eZNsgwaCcjGMev-ac,7764
|
|
21
|
+
bocai_cli/commands/checkin.py,sha256=Okj76j0BUv1sK-ygTpsprXxeIogqjMbCMUNFSsEQS-A,1789
|
|
22
|
+
bocai_cli/commands/course.py,sha256=72Cor9pwa4DRdKGL0NR9qwij0PWp6T5rtoN4Ld6TQ1M,7810
|
|
23
|
+
bocai_cli/commands/registry.py,sha256=BpKsCkn3cHXgmULGFuksu8MfQZfTAIJePKK0Cg179cA,1230
|
|
24
|
+
bocai_cli/commands/report.py,sha256=_weEShAsRO8PT3LewHQs2-xATMnOgWlpQW-Mxe3FsWM,2381
|
|
25
|
+
bocai_cli/commands/reservation.py,sha256=O7a9o2Obci1RZdlh_2X2i6vqQICwGfahAJOlxRRSZu0,1801
|
|
26
|
+
bocai_cli-0.1.0.dist-info/METADATA,sha256=0ojRQBqUmmqQtsVculD5IL1L5iHd2G8FXuKiqiY33AE,1798
|
|
27
|
+
bocai_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
28
|
+
bocai_cli-0.1.0.dist-info/entry_points.txt,sha256=iaKWpXFc8niVX-4Eimhpzwq3R11pfFxjpZS4RFJLpEU,49
|
|
29
|
+
bocai_cli-0.1.0.dist-info/RECORD,,
|