44from datetime import timedelta , datetime , timezone
55from pprint import pprint
66
7+ import click
8+
79import aw_client
810from aw_core import Event
911
12+ now = datetime .now (timezone .utc )
13+ td1day = timedelta (days = 1 )
14+ td1yr = timedelta (days = 365 )
15+
1016
1117def _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: {} \t Data: {}" .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: {} \t Data: {}" .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
126126if __name__ == "__main__" :
0 commit comments