From 42e041ce03613c2176f3187ca26a13f1b276d4a5 Mon Sep 17 00:00:00 2001 From: Duarte Figueiredo Date: Sun, 16 Apr 2023 11:24:51 +0100 Subject: [PATCH] [modules/wakatime] - New module that connects to https://wakatime.com and displays coding duration stats --- bumblebee_status/modules/contrib/wakatime.py | 95 +++++++++++++++++++ screenshots/wakatime.png | Bin 0 -> 1820 bytes tests/modules/contrib/test_wakatime.py | 56 +++++++++++ themes/icons/awesome-fonts.json | 3 + 4 files changed, 154 insertions(+) create mode 100644 bumblebee_status/modules/contrib/wakatime.py create mode 100644 screenshots/wakatime.png create mode 100644 tests/modules/contrib/test_wakatime.py diff --git a/bumblebee_status/modules/contrib/wakatime.py b/bumblebee_status/modules/contrib/wakatime.py new file mode 100644 index 0000000..7e4e33e --- /dev/null +++ b/bumblebee_status/modules/contrib/wakatime.py @@ -0,0 +1,95 @@ +# pylint: disable=C0111,R0903 + +""" +Displays the WakaTime daily/weekly/monthly times: + + * https://wakatime.com/developers#stats + +Uses `xdg-open` or `x-www-browser` to open web-pages. + +Requires the following library: + * requests + +Errors: + if the Wakatime status query failed, the shown value is `n/a` + +Parameters: + * wakatime.token: Wakatime secret api key, you can get it in https://wakatime.com/settings/account. + * wakatime.range: Range of the output, default is "Today". Can be one of “Today”, “Yesterday”, “Last 7 Days”, “Last 7 Days from Yesterday”, “Last 14 Days”, “Last 30 Days”, “This Week”, “Last Week”, “This Month”, or “Last Month”. + * wakatime.format: Format of the output, default is "digital" + Valid inputs are: + * "decimal" -> 1.37 + * "digital" -> 1:22 + * "seconds" -> 4931.29 + * "text" -> 1 hr 22 mins + * "%H:%M:%S" -> 01:22:31 (or any other valid format) +""" + +import base64 +import shutil +import time +from typing import Final, List + +import requests + +import core.decorators +import core.input +import core.module +import core.widget + +HOST_API: Final[str] = "https://wakatime.com" +SUMMARIES_URL: Final[str] = f"{HOST_API}/api/v1/users/current/summaries" +UTF8: Final[str] = "utf-8" +FORMAT_PARAMETERS: Final[List[str]] = ["decimal", "digital", "seconds", "text"] + + +class Module(core.module.Module): + @core.decorators.every(minutes=5) + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.wakatime)) + + self.background = True + self.__label = "" + + self.__output_format = self.parameter("format", "digital") + self.__range = self.parameter("range", "Today") + + self.__requests = requests.Session() + + token = self.__encode_to_base_64(self.parameter("token", "")) + self.__requests.headers.update({"Authorization": f"Basic {token}"}) + + cmd = "xdg-open" + if not shutil.which(cmd): + cmd = "x-www-browser" + + core.input.register( + self, + button=core.input.LEFT_MOUSE, + cmd=f"{cmd} {HOST_API}/dashboard", + ) + + def wakatime(self, _): + return self.__label + + def update(self): + try: + self.__label = self.__get_waka_time(self.__range) + except Exception: + self.__label = "n/a" + + def __get_waka_time(self, since_date: str) -> str: + response = self.__requests.get(f"{SUMMARIES_URL}?range={since_date}") + + data = response.json() + grand_total = data["cumulative_total"] + + if self.__output_format in FORMAT_PARAMETERS: + return str(grand_total[self.__output_format]) + else: + total_seconds = int(grand_total["seconds"]) + return time.strftime(self.__output_format, time.gmtime(total_seconds)) + + @staticmethod + def __encode_to_base_64(s: str) -> str: + return base64.b64encode(s.encode(UTF8)).decode(UTF8) diff --git a/screenshots/wakatime.png b/screenshots/wakatime.png new file mode 100644 index 0000000000000000000000000000000000000000..82c8c797827652308f6ca4989b90f3d713817063 GIT binary patch literal 1820 zcmV+%2jlpOP)!O6|D$~OQJ#0KB299dD2AOMI=a-1W5=Y#61LYNg~80L4-!gMMNY~ zMNw2qQ9ZKIF6I-ACw6iK-=G7e_VD|P%Fj271=q3T+xWR(AJw^>Dgm#R-~^*cPn!ngm2*X# z)+J}JnOlBveOk&?&c`kI`T6<#ztg^bd%nReu%tm?URG~vkrLd2Ju4A7ZNC>Uh)3WC zgokl|F&lu=Y0OwVol}P{-bidQ`+<)N?nb{rRjOk1~IQ5V7DeAI~4wj)@w2;@Fl~6WqAy z;X8BFaSV8HyP_~}iMD$`-a2;V;JOjR1~k^>9p1l|wqlq?J2IPZl>PR&?p{+l zmY?_f_hRmrLNJOI88iQQS|1YJ74@LN07QdrH3YZd3%6va-nm|efne9(w`&D{tLEtD z&H?_^4O+K5cXPX|EtLdnYD~nzqSY9_%Rsp$4#3yfx2;oi)^_b|l7gENHyZuJ459wy zZuMV3HDoNBfjT;I``0JUMzLP@fwQ)1}G5R{qM@5{^3KzGt)oS z2FfjQAo%$Dw(;~rK}AY%Lk7K%4xyL*`i7!%ygaA0cn$0qPVcI%*ur}oArM@2vyEBF zc#?~EX2RjnK1V!dAcBL!P)9#e@82&J^=n`4+f%TVbJj~5NT7D*XHd(z`kic`+!6;C zi?_Gc+p()k2@Y#DetyZq>F6kCD{0n5(mB)%wl3zqjnESu>{y(z8ldY4-gJI*g&zJBfUaQ=O#`Xvof$iNn9Z51;v{Ep zIY7J=E(I6Xr>DIotKy0_j(SMO(NJ&{B%4U$kO1-xI>?M7fAd27i|>+f*ta`#MrsjY zrr;VXqY@ z*#kFJkVdhd0wm)o5FEO}z8yeUkxODO85a>Uqrejj%jWf|^sdI5d~}k$buz>p!L>3D zpsmI6zm~Shp5gHP*`3)qxg`HFXcIs&)2Bw^u)Jouc=Os=ieL#}L`$^++6l7OLeu`5+*(k8=aXxy;2oCv2RK#HVS_CU7 zxuhpc!$7$`4is6zAyL3jBIzTSy+V7~Uwi=}!?8ozcFkVHCY3BMr%H@r90>x!_2?di z1QTf{`Iqy2L!n;xByl+k}v|F46L;K+-5ZJ~LIVW8X|2a2rV z6biSX1g9_+B{+qtD8VUAMF~z}DoSt)Q}IwJOyz^5992{?t^5sN