Skip to content

Commit b2e7985

Browse files
committed
feat: rewrote CLI to use click
1 parent ea6de3a commit b2e7985

File tree

4 files changed

+313
-295
lines changed

4 files changed

+313
-295
lines changed

aw_client/cli.py

Lines changed: 97 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@
44
from datetime import timedelta, datetime, timezone
55
from pprint import pprint
66

7+
import click
8+
79
import aw_client
810
from aw_core import Event
911

12+
now = datetime.now(timezone.utc)
13+
td1day = timedelta(days=1)
14+
td1yr = timedelta(days=365)
15+
1016

1117
def _valid_date(s):
1218
# https://stackoverflow.com/questions/25470844/specify-format-for-input-arguments-argparse-python
@@ -17,110 +23,104 @@ def _valid_date(s):
1723
raise argparse.ArgumentTypeError(msg)
1824

1925

20-
def main():
21-
now = datetime.now(timezone.utc)
22-
td1day = timedelta(days=1)
23-
td1yr = timedelta(days=365)
24-
25-
parser = argparse.ArgumentParser(
26-
prog="aw-cli", description="A CLI utility for interacting with ActivityWatch."
27-
)
28-
parser.set_defaults(which="none")
29-
parser.add_argument(
30-
"--host",
31-
default="127.0.0.1:5600",
32-
help="Host to use, in the format HOSTNAME[:PORT]",
33-
)
34-
parser.add_argument(
35-
"--testing", action="store_true", help="Set to use testing ports by default"
36-
)
37-
38-
subparsers = parser.add_subparsers(help="sub-command help")
39-
40-
parser_heartbeat = subparsers.add_parser(
41-
"heartbeat", help="Send a heartbeat to the server"
42-
)
43-
parser_heartbeat.set_defaults(which="heartbeat")
44-
parser_heartbeat.add_argument("--pulsetime", default=60, help="Pulsetime to use")
45-
parser_heartbeat.add_argument("bucket", help="bucketname to send heartbeat to")
46-
parser_heartbeat.add_argument(
47-
"data", default="{}", help="JSON data to send in heartbeat"
48-
)
49-
50-
parser_buckets = subparsers.add_parser("buckets", help="List all buckets")
51-
parser_buckets.set_defaults(which="buckets")
52-
53-
parser_buckets = subparsers.add_parser("events", help="Query events from bucket")
54-
parser_buckets.set_defaults(which="events")
55-
parser_buckets.add_argument("bucket")
56-
57-
parser_query = subparsers.add_parser("query", help="Query events from bucket")
58-
parser_query.set_defaults(which="query")
59-
parser_query.add_argument("path")
60-
parser_query.add_argument("--name")
61-
parser_query.add_argument("--cache", action="store_true")
62-
parser_query.add_argument(
63-
"--json", action="store_true", help="Output resulting JSON"
26+
@click.group(
27+
help="CLI utility for aw-client to aid in interacting with the ActivityWatch server"
28+
)
29+
@click.option("--testing", is_flag=True, help="Set to use testing ports by default")
30+
@click.option(
31+
"--host",
32+
default="127.0.0.1",
33+
help="Address of host",
34+
)
35+
@click.option(
36+
"--port",
37+
default=5600,
38+
help="Port to use",
39+
)
40+
@click.pass_context
41+
def main(ctx, testing: bool, host: str, port: int):
42+
ctx.testing = testing
43+
ctx.client = aw_client.ActivityWatchClient(
44+
host=host,
45+
port=port if port != 5600 else (5666 if testing else 5600),
6446
)
65-
parser_query.add_argument("--start", default=now - td1day, type=_valid_date)
66-
parser_query.add_argument("--end", default=now + 10 * td1yr, type=_valid_date)
6747

68-
args = parser.parse_args()
69-
# print("Args: {}".format(args))
7048

71-
client = aw_client.ActivityWatchClient(
72-
host=args.host.split(":")[0],
73-
port=int(
74-
(args.host.split(":")[1:] + [5600 if not args.testing else 5666]).pop()
75-
),
76-
)
77-
78-
if args.which == "heartbeat":
79-
e = Event(duration=0, data=json.loads(args.data), timestamp=now)
80-
print(e)
81-
client.heartbeat(args.bucket, e, args.pulsetime)
82-
elif args.which == "buckets":
83-
buckets = client.get_buckets()
84-
print("Buckets:")
85-
for bucket in buckets:
86-
print(" - {}".format(bucket))
87-
elif args.which == "events":
88-
events = client.get_events(args.bucket)
89-
print("events:")
90-
for e in events:
91-
print(
92-
" - {} ({}) {}".format(
93-
e.timestamp.replace(tzinfo=None, microsecond=0),
94-
str(e.duration).split(".")[0],
95-
e.data,
96-
)
49+
@main.command(help="Send a heartbeat to bucket with ID `bucket_id` with JSON `data`")
50+
@click.argument("bucket_id")
51+
@click.argument("data")
52+
@click.option("--pulsetime", default=60, help="pulsetime to use for merging heartbeats")
53+
@click.pass_context
54+
def heartbeat(ctx, bucket_id: str, data: str, pulsetime: int):
55+
now = datetime.now(timezone.utc)
56+
e = Event(duration=0, data=json.loads(data), timestamp=now)
57+
print(e)
58+
ctx.client.heartbeat(bucket_id, e, pulsetime)
59+
60+
61+
@main.command(help="List all buckets")
62+
@click.pass_context
63+
def buckets(ctx):
64+
buckets = ctx.client.get_buckets()
65+
print("Buckets:")
66+
for bucket in buckets:
67+
print(" - {}".format(bucket))
68+
69+
70+
@main.command(help="Query events from bucket with ID `bucket_id`")
71+
@click.argument("bucket_id")
72+
@click.pass_context
73+
def events(ctx, bucket_id: str):
74+
events = ctx.client.get_events(bucket_id)
75+
print("events:")
76+
for e in events:
77+
print(
78+
" - {} ({}) {}".format(
79+
e.timestamp.replace(tzinfo=None, microsecond=0),
80+
str(e.duration).split(".")[0],
81+
e.data,
9782
)
98-
elif args.which == "query":
99-
with open(args.path) as f:
100-
query = f.read()
101-
result = client.query(
102-
query, args.start, args.end, cache=args.cache, name=args.name
10383
)
104-
if args.json:
105-
print(json.dumps(result))
106-
else:
107-
for period in result:
108-
print("Showing 10 out of {} events:".format(len(period)))
109-
for event in period[:10]:
110-
event.pop("id")
111-
event.pop("timestamp")
112-
print(
113-
" - Duration: {} \tData: {}".format(
114-
str(timedelta(seconds=event["duration"])).split(".")[0],
115-
event["data"],
116-
)
117-
)
84+
85+
86+
@main.command(help="Run a query in file at `path` on the server")
87+
@click.argument("path")
88+
@click.option("--name")
89+
@click.option("--cache", is_flag=True)
90+
@click.option("--json", is_flag=True)
91+
@click.option("--start", default=now - td1day, type=click.DateTime())
92+
@click.option("--stop", default=now + td1yr, type=click.DateTime())
93+
@click.pass_context
94+
def query(
95+
ctx,
96+
path: str,
97+
cache: bool,
98+
_json: bool,
99+
start: datetime,
100+
stop: datetime,
101+
name: str = None,
102+
):
103+
with open(path) as f:
104+
query = f.read()
105+
result = ctx.client.query(query, [(start, stop)], cache=cache, name=name)
106+
if _json:
107+
print(json.dumps(result))
108+
else:
109+
for period in result:
110+
print("Showing 10 out of {} events:".format(len(period)))
111+
for event in period[:10]:
112+
event.pop("id")
113+
event.pop("timestamp")
118114
print(
119-
"Total duration:\t",
120-
timedelta(seconds=sum(e["duration"] for e in period)),
115+
" - Duration: {} \tData: {}".format(
116+
str(timedelta(seconds=event["duration"])).split(".")[0],
117+
event["data"],
118+
)
121119
)
122-
else:
123-
parser.print_help()
120+
print(
121+
"Total duration:\t",
122+
timedelta(seconds=sum(e["duration"] for e in period)),
123+
)
124124

125125

126126
if __name__ == "__main__":

aw_client/client.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import functools
77
from datetime import datetime
88
from collections import namedtuple
9-
from typing import Optional, List, Any, Union, Dict, Callable
9+
from typing import Optional, List, Any, Union, Dict, Callable, Tuple
1010

1111
import requests as req
1212
import persistqueue
@@ -287,11 +287,10 @@ def import_bucket(self, bucket: dict) -> None:
287287
def query(
288288
self,
289289
query: str,
290-
start: datetime,
291-
end: datetime,
290+
timeperiods: List[Tuple[datetime, datetime]],
292291
name: str = None,
293292
cache: bool = False,
294-
) -> Union[int, dict]:
293+
) -> Any:
295294
endpoint = "query/"
296295
params = {} # type: Dict[str, Any]
297296
if cache:
@@ -301,8 +300,12 @@ def query(
301300
)
302301
params["name"] = name
303302
params["cache"] = int(cache)
303+
304304
data = {
305-
"timeperiods": ["/".join([start.isoformat(), end.isoformat()])],
305+
"timeperiods": [
306+
"/".join([start.isoformat(), end.isoformat()])
307+
for start, end in timeperiods
308+
],
306309
"query": query.split("\n"),
307310
}
308311
response = self._post(endpoint, data, params=params)

0 commit comments

Comments
 (0)