1414from .wakatime import USER_AGENT , WAKATIME_CLI
1515
1616
17- class RequestData (TypedDict ):
17+ class BeatData (TypedDict ):
1818 filepath : str
1919 iswrite : bool
2020 timestamp : float
2121
2222
23- class RouteHandler (APIHandler ):
23+ class BeatHandler (APIHandler ):
2424 # The following decorator should be present on all verb methods (head, get, post,
2525 # patch, put, delete, options) to ensure only authorized user can request the
2626 # Jupyter server
2727 @tornado .web .authenticated
2828 async def post (self ):
29+ if not os .path .exists (WAKATIME_CLI ):
30+ self .log .error ("JupyterLab Wakatime plugin failed to find %s" , WAKATIME_CLI )
31+ return self .finish (json .dumps ({"code" : 127 }))
2932 try :
30- data : RequestData = tornado .escape .json_decode (self .request .body )
33+ data : BeatData = tornado .escape .json_decode (self .request .body )
3134 cmd_args : list [str ] = ["--plugin" , USER_AGENT ]
3235
3336 root_dir = os .path .expanduser (self .contents_manager .root_dir )
@@ -38,16 +41,16 @@ async def post(self):
3841 if data ["iswrite" ]:
3942 cmd_args .append ("--write" )
4043 except :
41- self .set_status ( 400 )
42- return self .finish ()
44+ self .log . info ( "wakatime-cli " + shlex . join ( cmd_args ) )
45+ return self .finish (json . dumps ({ "code" : 400 }) )
4346 self .log .info ("wakatime-cli " + shlex .join (cmd_args ))
4447
4548 # Async subprocess is required for non-blocking access to return code
4649 # However, it's not supported on Windows
4750 # As a workaround, create a Popen instance and leave it alone
4851 if platform .system () == "Windows" :
4952 subprocess .Popen ([WAKATIME_CLI , * cmd_args ])
50- return self .finish ()
53+ return self .finish (json . dumps ({ "code" : 0 }) )
5154
5255 proc = await asyncio .create_subprocess_exec (
5356 WAKATIME_CLI ,
@@ -77,17 +80,40 @@ async def post(self):
7780 if log .get ("level" ) != "error" :
7881 continue
7982 self .log .error ("WakaTime error: %s" , log .get ("message" , line ))
80- self .finish ()
83+ return self .finish (json . dumps ({ "code" : proc . returncode }) )
8184
8285
83- def setup_handlers (web_app ):
84- if not os .path .exists (WAKATIME_CLI ):
85- raise RuntimeWarning (
86- "JupyterLab Wakatime plugin failed to find " + WAKATIME_CLI
87- )
86+ class StatusHandler (APIHandler ):
87+ @tornado .web .authenticated
88+ async def get (self ):
89+ if not os .path .exists (WAKATIME_CLI ):
90+ self .log .error ("JupyterLab Wakatime plugin failed to find %s" , WAKATIME_CLI )
91+ return self .finish (json .dumps ({"time" : "" }))
8892
93+ cmd = [WAKATIME_CLI , "--today" , "--output=raw-json" ]
94+ if platform .system () == "Windows" :
95+ proc = subprocess .run (cmd , stdout = subprocess .PIPE , encoding = "utf8" )
96+ if proc .returncode :
97+ return self .finish (json .dumps ({"time" : "" }))
98+ stdout = proc .stdout .strip ()
99+ else :
100+ proc = await asyncio .create_subprocess_exec (
101+ * cmd , stdout = asyncio .subprocess .PIPE
102+ )
103+ stdout , _ = await proc .communicate ()
104+ if proc .returncode :
105+ return self .finish (json .dumps ({"time" : "" }))
106+ stdout = stdout .decode ().strip ()
107+ data = json .loads (stdout ).get ("data" , {})
108+ time = data .get ("grand_total" , {}).get ("digital" , "" )
109+ self .finish (json .dumps ({"time" : time }))
110+
111+
112+ def setup_handlers (web_app ):
89113 host_pattern = ".*$"
90- base_url = web_app .settings ["base_url" ]
91- route_pattern = url_path_join (base_url , "jupyterlab-wakatime" , "heartbeat" )
92- handlers = [(route_pattern , RouteHandler )]
114+ base_url = url_path_join (web_app .settings ["base_url" ], "jupyterlab-wakatime" )
115+ handlers = [
116+ (url_path_join (base_url , "heartbeat" ), BeatHandler ),
117+ (url_path_join (base_url , "status" ), StatusHandler ),
118+ ]
93119 web_app .add_handlers (host_pattern , handlers )
0 commit comments