diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 747d0b62..50b9f222 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,17 @@ jobs: black: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: psf/black@stable with: options: "--check" + jsonlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: validate JSON + env: + REPORT_ONLY: true + SHOW_ERRORS: true + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/json-lint/master/pipeline.sh) + diff --git a/README.md b/README.md index 681d910b..06cb4bc5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`battery`**: print the percentage of the battery; - **`between`**: check if the given values are ordered, usually used for three items to check membership of a range; - **`biblespeak`**: speak the next verse of the *Bible*; + - **`blackcp`**: run the *Black* formatter, and if Black made changes, commit and push these; - **`blooddonation`**: register a blood donation together with blood metrics; - **`bloodresults`**: store blood result measurements; - **`bloodpressure`**: register a blood pressure measurement; @@ -42,6 +43,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`dockerprune`**: prune unused docker images; - **`dockersave`**: save a docker image as a compressed tarball; - **`dockersavescp`**: save a docker image as a compressed tarball and copy it with `scp`; + - **`dockerscpload`**: transfer docker images over ssh to a remote directly loading these in docker at the remote side; ### E @@ -52,6 +54,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i ### F + - **`facialmask`**: register the use of a facial mask; - **`fill`**: fill the entire screen with a certain color; - **`fish_home`**: get the directory where the fish functions are stored; - **`frmclip`**: copy from the clipboard to the stdout; @@ -69,7 +72,10 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`gitacp`**: add items to the repository, commit, and push; - **`gitb`**: make a new git branch; - **`gitc`**: make a git commit (short for `git commit -am`); + - **`gitci`**: list the last CI/CD runs on *GitHub*; + - **`gitclonec`**: clone a repository and chance the directory to that of the cloned repository; - **`gitcp`**: commit with the given parameters, and push the changes; + - **`gitcpr`**: commit with the given parameters, push the changes and open a pull request on *GitHub*; - **`gitd`**: calculate the git difference and work with a pager to read the full response; - **`gitf`**: alias for `git fetch`; - **`gith`**: checkout a given branch; @@ -77,6 +83,8 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`gitmaster`**: checkout the master branch (and stash work in progress if any); - **`gitmastertag`**: checkout the master branch, pull from remote, tag the commit and push the tag to the repository; - **`gitp`**: pushes the changes to the remote repository (short for `git push`); + - **`gitpr`**: open a pull request on *GitHub*; + - **`gitprv`**: show comments of the active pull request; - **`gitremote`**: add the remote as origin with the given `GIT_PREFIX` and the name of the directory as git project; - **`gits`**: prints the status of the current Git repository (short for `git status`); - **`gitt`**: make a git tag and push the tag; @@ -90,6 +98,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`here_is_the_news`**: beeps a few times to mark a certain event; - **`hlint`**: run Haskell lint and use a pager for the results; - **`homebattery`**: determine how long it will take to load the battery full; + - **`hourcolor`**: change the keyboard to the hour color, can be scheduled as cronjob; - **`household`**: wait until a household device has finished with info; - **`hydrate`**: keep track of the amount of water we drink; @@ -97,6 +106,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`igrep`**: run grep in a case insensitive way; - **`import`**: initializes the python shell with `import …`, so `import datetime` for example can be written in the shell as first Python command; + - **`isortcp`**: run the isort formatter, and if isort made changes, commit and push these; ### J @@ -122,15 +132,23 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`measureall`**: start asking for many measures in bulk, used as helper function; - **`measurelog`**: add a given measurement at a given time; - **`mkdircd`**: make a directory, if it does not yet exist, and move the cd to that directory; + - **`mkdircdgit`**: make a directory, if it does not yet exist, cd into that directory, and start a git repository; - **`mkvim`**: make the directories needed to edit a file; - **`mkvimgit`**: make the directories needed to edit a file and add the file to git; + - **`mynote`**: add a notebook entry to a specific topic; ### N - **`natrix`**: the Python interpreter of the fish environment; - **`natrix-env`**: run a Python program in the natrix environment; + - **`note`**: add a notebook entry; + - **`notes`**: notebook in a loop to log entries; - **`numpy`**: start a Python shell with `numpy` imported; +### O + + - **`otp`**: generate/configure One Time Passwords (OTPs) with pass-extension-otp; + ### P - **`pandas`**: start a Python shell with `numpy` and `pandas` imported; @@ -143,6 +161,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`proofreadedit`**: let vim open the file along with the proofread file; - **`proofreadmd`**: proofread Markdown files with Groq to find spelling and grammatical errors; - **`pwd`**: list the absolute path for the given files listed or the `pwd` if no arguments were provided; + - **`pydoc`**: add doc strings to all elements of a Python file; - **`python3`**: overrides normal Python prompt with an IPython shell without banner, no confirmations and matplotlib; ### Q @@ -151,6 +170,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i ### R + - **`rabbitdoc`**: ask [*CodeRabbit*](https://github.com/coderabbitai) to generate docstrings for the latest pull request; - **`redo`**: redo a command until the exit code is 0; - **`remake`**: a loop to remake certain products with a Makefile; - **`rmake`**: walk up the filetree until it finds a Makefile; @@ -159,6 +179,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i ### S + - **`season`**: determine the season for a given day; - **`semver`**: convert a given requirements file to its sem-ver equivalent; - **`sensors`**: show the temperature measured by sensors updated every second; - **`setvar`**: check if a variable with the name exists; if not, query for a value; @@ -185,9 +206,11 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`taskd`**: set the task with the given id as done; - **`taskflush`**: set all expired tasks to done; - **`teeth`**: help cleaning teeth; + - **`timeprompt`**: wait for a given amount of time, or until the person hits a key exits with 1 if the user interrupted; - **`timestamp`**: get a string that specifies date and time, used for filenames mainly; - **`todo`**: add or inspect to a todo list; - **`truthful`**: check if at least one of the given Python literals or JSON values has truthiness `true`; + - **`typehintcheck`**: check if all defined functions have type hints; ### U @@ -198,6 +221,7 @@ A set of functions for the **F**riendly **I**nteractive **Sh**ell (fish). This i - **`videos`**: see random videos; - **`vimc`**: create a directory if the directory does not yet exist before running `vim`; + - **`vimgit`**: edit the given files in the parameters, and add these to git; - **`vimo`**: open two or more files in vim next to each other; - **`vimp`**: use vim with different tabs, a shortcut for `vim -p`; - **`vimpsrc`**: open all the Haskell files in the `src` directory with `vim`; @@ -251,6 +275,10 @@ The following emoji aliases can be used as commands: - **`GROQ_API_TOKEN`**: the API token for *Groq* to make chatbot requests, can be found [here](https://console.groq.com/keys); and - **`USER_SLUG`**: the slug used to prepend to file names when adding an author to it, for example `Elon_Musk`. +## Measurements + +One of the core features is that one can measure all sorts of parameters, like health, activities, notes, etc. These are stored in `assets/Measurements.json`, a JSON file that allows cascading measurements. + ## Installation One can install this by downloading (or cloning) and put the files in the `~/.config/fish/functions/` diff --git a/add_docstrings.py b/add_docstrings.py new file mode 100755 index 00000000..5d11e114 --- /dev/null +++ b/add_docstrings.py @@ -0,0 +1,99 @@ +#!/usr/bin/python3 + +import ast +import os +import os.path +import sys + +import astor + + +class AddDocString(ast.NodeTransformer): + def __init__(self, *args, docstring: str, **kwargs): + """ + Initialize the AddDocString transformer with the docstring text to insert. + + The provided `docstring` value is stored on the instance as `self.docstring` and will be inserted into any Module, ClassDef, FunctionDef, or AsyncFunctionDef that lacks a docstring. + Any additional positional and keyword arguments are forwarded to the superclass initializer. + """ + super().__init__(*args, **kwargs) + self.docstring = docstring + + def add_docstring(self, node): + """ + Insert the configured docstring into the given AST node if it has no existing docstring. + + If the node (Module, ClassDef, FunctionDef, or AsyncFunctionDef) has no docstring, this function inserts the value of self.docstring as the first statement of node.body. If a docstring already exists, the node is left unchanged. The node is then traversed with generic_visit and returned. + + Parameters: + node (ast.AST): An AST node that has a `body` sequence (module, class, or function node). + + Returns: + ast.AST: The (possibly modified) node after applying generic visitation. + """ + if ast.get_docstring(node) is None: + node.body.insert(0, ast.Expr(value=ast.Constant(self.docstring))) + return self.generic_visit(node) + + def visit_FunctionDef(self, node): + """ + Ensure an ast.FunctionDef node has a docstring, inserting the transformer's configured docstring if one is missing, then continue generic traversal. + + Delegates to add_docstring(node) and returns the possibly modified ast.FunctionDef node. + """ + return self.add_docstring(node) + + def visit_AsyncFunctionDef(self, node): + """ + Visit an async function definition node and ensure it has a docstring. + + If the async function node has no existing docstring, inserts self.docstring + as the first statement in the node's body. Continues generic AST traversal + and returns the (possibly modified) node. + + Parameters: + node (ast.AsyncFunctionDef): The async function AST node to visit. + + Returns: + ast.AST: The visited (and possibly modified) node. + """ + return self.add_docstring(node) + + def visit_ClassDef(self, node): + """ + Visit a class definition node and ensure it has a docstring. + + If the class has no docstring, insert the transformer's configured docstring as the first statement. Returns the (possibly modified) AST ClassDef node for further traversal. + + Parameters: + node (ast.ClassDef): The class definition AST node to visit and potentially modify. + + Returns: + ast.AST: The transformed ClassDef node. + """ + return self.add_docstring(node) + + def visit_Module(self, node): + """ + Ensure the given module AST node has a docstring. If the module lacks a docstring, insert the transformer's configured docstring as the first statement, then continue a generic visit of the node's children. + + Parameters: + node (ast.Module): The module AST node to process. + + Returns: + ast.AST: The module node after potential modification and traversal. + """ + return self.add_docstring(node) + + +if __name__ == "__main__": + converter = AddDocString( + docstring=os.environ.get("PY_DOCSTRING", "@coderabbitai generate docstrings") + ) + for file in sys.argv[1:]: + with open(file, "rb") as f: + tree = ast.parse(f.read()) + result = astor.to_source(converter.visit(tree)) + file, ext = os.path.splitext(file) + with open(f"{file}_modified{ext}", "w") as f: + f.write(result) diff --git a/add_measure.py b/add_measure.py index 61965cf4..5131d9cf 100755 --- a/add_measure.py +++ b/add_measure.py @@ -6,31 +6,25 @@ from ast import literal_eval from datetime import datetime from shutil import move +import sqlite3 +from peewee import DateTimeField, Model, SqliteDatabase, TextField from color_pprint import cprint from dateparser import parse from filelock import FileLock DURATION = re.compile(r"\d{1,2}([:]\d{2})+") -ASSET_PATH = "assets/measurements.json" +ASSET_PATH = "assets/measurements.db" +db = SqliteDatabase(ASSET_PATH) -def filter_dict(data, dt): - if isinstance(data, dict): - result = {} - for k, v in data.items(): - v = filter_dict(v, dt) - if v: - try: - dt2 = datetime.fromisoformat(k) - except ValueError: - result[k] = v - else: - if dt2 >= dt: - result[k] = v - return result - return data +class Measurement(Model): + key = TextField() + value = TextField() + created_date = DateTimeField(default=datetime.now) + class Meta: + database = db if __name__ == "__main__": n = len(sys.argv) @@ -38,34 +32,21 @@ def filter_dict(data, dt): n > 1 ), "You should provide the name of the measurement and the corresponding value" with FileLock(f"{ASSET_PATH}.lock"): - try: - with open(ASSET_PATH, "r") as f: - data = json.load(f) - except IOError: - data = {} - timestamp = datetime.now().replace(microsecond=0) + db.connect() + db.create_tables([Measurement]) + key_dt = timestamp = datetime.now() dt = timestamp.isoformat() - to_sort = {} - _def = {} for i in range(1, n, 2): key = sys.argv[i] try: key, dt2 = key.rsplit("@", 1) - timefilter = dt2 = parse(dt2) + timefilter = dt2 = parse(dt2).replace(tzinfo=None) if dt2 is not None: - key_dt = dt2.replace(microsecond=0, tzinfo=None).isoformat() + key_dt = dt2.replace(tzinfo=None) except ValueError: # key is already fine timefilter = datetime.min key_dt = dt - datum = data - for ky in key.split("."): - _datum = datum.setdefault(ky, _def) - if _datum is _def: - _def = {} # new one - # need to resort datum, new entry - to_sort[id(datum)] = datum - datum = _datum if n > i + 1: val = sys.argv[i + 1].strip() if DURATION.fullmatch(val.strip()): @@ -83,16 +64,9 @@ def filter_dict(data, dt): except ValueError: pass # keep it a string if val: # None, False, etc. are all omitted - datum[key_dt] = val - if key_dt is not dt: - # we add a key from the past - to_sort[id(datum)] = datum + Measurement.create(created_date=key_dt, key=key, value=json.dumps(val)) else: - cprint(filter_dict(datum, timefilter)) - for datum in to_sort.values(): - datum_sort = {k: v for k, v in sorted(datum.items())} - datum.clear() - datum.update(datum_sort) - with open(f"{ASSET_PATH}.tmp", "w") as f: - json.dump(data, f, indent=4) - move(f"{ASSET_PATH}.tmp", ASSET_PATH) + datum = {} + for entry in Measurement.select().where(Measurement.key == key, Measurement.created_date >= timefilter.isoformat()): + datum[parse(entry.created_date).replace(microsecond=0)] = json.loads(entry.value) + cprint(datum) diff --git a/assets/.gitignore b/assets/.gitignore index 71a58f35..cdd496e4 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -1,6 +1,12 @@ measurements*.json *_measurements.json *_measurements.json.gz +measurements*.sqlite3 +*_measurements.sqlite3 +*_measurements.slite3.gz +measurements*.db +*_measurements.db +*_measurements.db.gz list.json *_list.json *.lock diff --git a/assets/alarm_colors.json b/assets/colors/alarm.json similarity index 100% rename from assets/alarm_colors.json rename to assets/colors/alarm.json diff --git a/assets/body_colors.json b/assets/colors/body.json similarity index 100% rename from assets/body_colors.json rename to assets/colors/body.json diff --git a/assets/colors/coffee.json b/assets/colors/coffee.json new file mode 100644 index 00000000..40b79833 --- /dev/null +++ b/assets/colors/coffee.json @@ -0,0 +1 @@ +{"0": {"hex": "#f9f8f6", "rgb": [249, 248, 246]}, "1": {"hex": "#efe9e3", "rgb": [239, 233, 227]}, "2": {"hex": "#d9cfc7", "rgb": [217, 207, 199]}, "3": {"hex": "#c9b59c", "rgb": [201, 181, 156]}} \ No newline at end of file diff --git a/assets/fix_colors.py b/assets/colors/fix.py similarity index 93% rename from assets/fix_colors.py rename to assets/colors/fix.py index 2193414b..05ecbc96 100755 --- a/assets/fix_colors.py +++ b/assets/colors/fix.py @@ -18,6 +18,7 @@ elif "hex" not in rec: r, g, b = rec["rgb"] rec["hex"] = f"#{hex((r << 16) | (g << 8) | b)[2:]}" + rec["hex"] = rec["hex"].casefold() with open(f, "w") as fh: json.dump(data, fh) diff --git a/assets/focus_colors.json b/assets/colors/focus.json similarity index 100% rename from assets/focus_colors.json rename to assets/colors/focus.json diff --git a/assets/heal_colors.json b/assets/colors/heal.json similarity index 100% rename from assets/heal_colors.json rename to assets/colors/heal.json diff --git a/assets/colors/hour.json b/assets/colors/hour.json new file mode 100644 index 00000000..1bd40aa7 --- /dev/null +++ b/assets/colors/hour.json @@ -0,0 +1,194 @@ +{ + "Midnight 00": { + "rgb": [ + 0, + 0, + 30 + ], + "hex": "#00001E" + }, + "Deep Night 01": { + "rgb": [ + 15, + 0, + 50 + ], + "hex": "#0F0032" + }, + "Late Night 02": { + "rgb": [ + 30, + 10, + 75 + ], + "hex": "#1E0A4B" + }, + "Pre-Dawn Dark 03": { + "rgb": [ + 50, + 40, + 95 + ], + "hex": "#32285F" + }, + "Astronomical Dawn 04": { + "rgb": [ + 75, + 75, + 120 + ], + "hex": "#4B4B78" + }, + "Nautical Twilight 05": { + "rgb": [ + 100, + 120, + 160 + ], + "hex": "#6478A0" + }, + "Civil Twilight/Sunrise 06": { + "rgb": [ + 220, + 180, + 200 + ], + "hex": "#DCB4C8" + }, + "Early Morning 07": { + "rgb": [ + 255, + 230, + 180 + ], + "hex": "#FFE6B4" + }, + "Morning Clarity 08": { + "rgb": [ + 130, + 200, + 255 + ], + "hex": "#82C8FF" + }, + "Mid-Morning 09": { + "rgb": [ + 180, + 230, + 255 + ], + "hex": "#B4E6FF" + }, + "Late Morning 10": { + "rgb": [ + 220, + 245, + 255 + ], + "hex": "#DCF5FF" + }, + "Pre-Noon 11": { + "rgb": [ + 250, + 250, + 255 + ], + "hex": "#FAFAFF" + }, + "Noon 12": { + "rgb": [ + 255, + 255, + 255 + ], + "hex": "#FFFFFF" + }, + "Post-Noon 13": { + "rgb": [ + 255, + 255, + 245 + ], + "hex": "#FFFFF5" + }, + "Early Afternoon 14": { + "rgb": [ + 255, + 245, + 200 + ], + "hex": "#FFF5C8" + }, + "Mid-Afternoon 15": { + "rgb": [ + 255, + 230, + 150 + ], + "hex": "#FFE696" + }, + "Late Afternoon 16": { + "rgb": [ + 255, + 200, + 100 + ], + "hex": "#FFC864" + }, + "Golden Hour 17": { + "rgb": [ + 255, + 160, + 0 + ], + "hex": "#FFA000" + }, + "Sunset 18": { + "rgb": [ + 220, + 90, + 40 + ], + "hex": "#DC5A28" + }, + "Civil Twilight 19": { + "rgb": [ + 180, + 40, + 100 + ], + "hex": "#B42864" + }, + "Nautical Twilight 20": { + "rgb": [ + 100, + 20, + 120 + ], + "hex": "#641478" + }, + "Early Night 21": { + "rgb": [ + 50, + 20, + 100 + ], + "hex": "#321464" + }, + "Mid Night 22": { + "rgb": [ + 30, + 10, + 75 + ], + "hex": "#1E0A4B" + }, + "Late Night Dark 23": { + "rgb": [ + 15, + 0, + 50 + ], + "hex": "#0F0032" + } +} \ No newline at end of file diff --git a/assets/colors/season.json b/assets/colors/season.json new file mode 100644 index 00000000..eb4e4fe5 --- /dev/null +++ b/assets/colors/season.json @@ -0,0 +1 @@ +{"winter": {"hex": "#01BFFF", "rgb": [1, 191, 255]}, "spring": {"hex": "#A7FC01", "rgb": [167, 252, 1]}, "summer": {"hex": "#FFFE00", "rgb": [255, 254, 0]}, "autumn": {"hex": "#FF7F00", "rgb": [255, 127, 0]}} diff --git a/assets/sleep_colors.json b/assets/colors/sleep.json similarity index 100% rename from assets/sleep_colors.json rename to assets/colors/sleep.json diff --git a/assets/stripe_colors.json b/assets/colors/stripe.json similarity index 100% rename from assets/stripe_colors.json rename to assets/colors/stripe.json diff --git a/assets/trend_colors.json b/assets/colors/trend.json similarity index 100% rename from assets/trend_colors.json rename to assets/colors/trend.json diff --git a/assets/wakeup_colors.json b/assets/colors/wakeup.json similarity index 100% rename from assets/wakeup_colors.json rename to assets/colors/wakeup.json diff --git a/biblespeak.fish b/biblespeak.fish index 73620b85..7ce591bd 100644 --- a/biblespeak.fish +++ b/biblespeak.fish @@ -1,6 +1,7 @@ function biblespeak --description 'speak the next verse of the *Bible*' set bibleend (math (setvar BIBLE_SPEAK) "+1") set chap (string split , -- (sed "$BIBLE_SPEAK!d" (assets)/bibleverses.csv)) + measurelog "study.bible" "$chap[1]:1-$chap[1]:$chap[2]" bible "$chap[1]:1-$chap[1]:$chap[2]" | sed -E '/^ [0-9]+ /d' | piper set -Ux BIBLE_SPEAK $bibleend end diff --git a/blackcp.fish b/blackcp.fish new file mode 100644 index 00000000..b2f31689 --- /dev/null +++ b/blackcp.fish @@ -0,0 +1,11 @@ +function blackcp --description 'run the Black formatter, and if Black made changes, commit and push these' + if set -q argv[1] + set args $argv + else + set args . + end + if ! black --check --quiet $args + black $args + gitcp 'ℬ𝓁𝒢𝒸𝓀 reformatting' + end +end diff --git a/blooddonation.fish b/blooddonation.fish index e3b36c6c..9f4713eb 100644 --- a/blooddonation.fish +++ b/blooddonation.fish @@ -5,6 +5,9 @@ function blooddonation --description 'register a blood donation together with bl test -n "$bpm" || read -P (string unescape '\e[31mBPM\e[0m> ') bpm bloodpressure "$systolic" "$diastolic" & measurelog "blooddonation.$kind" true health.bodilyfunction.bpm "$bpm" & - waitfor 2700 '🩸 donation' '🩸 ' (getcolor blood body_colors ,) - here_is_the_news + if ! waitfor 2700 '' '🩸 donation' '🩸 ' (getcolor blood body ,) + measurelog 'blooddonation.end' true + else + here_is_the_news + end end diff --git a/check_typehints.py b/check_typehints.py new file mode 100755 index 00000000..76f102df --- /dev/null +++ b/check_typehints.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 + +import ast +import os +import os.path +import sys + +import astor + + +class CheckTypeHints(ast.NodeTransformer): + IGNORE_PARAMETER_NAMES = {"self", "cls", "klass"} + ARG_COLLECTIONS = { + "posonlyargs": False, + "args": False, + "kwonlyargs": False, + "vararg": True, + "kwarg": True, + } + + def __init__(self, *args, filename="", **kwargs) -> None: + super().__init__(*args, **kwargs) + self.filename = filename + self.errors = 0 + + def report(self, node, message): + location = self.get_qualfied_name(node) + print(f"{location}: {message} has no typehint") + self.errors += 1 + + def get_qualfied_name(self, node: ast.AST): + return f"{self.filename}:{node.lineno}" + + def check_types(self, node) -> ast.AST: + location = self.get_qualfied_name(node) + args = node.args + for arg_col, wrap in self.ARG_COLLECTIONS.items(): + col = getattr(args, arg_col) + if col is not None: + if wrap: + col = (col,) + for argi in col: + if ( + not argi.annotation + and argi.arg not in self.IGNORE_PARAMETER_NAMES + ): + self.report(node, f"argument \x1b[1m{argi.arg}\x1b[0m") + if not node.returns: + self.report(node, f"the return type") + return self.generic_visit(node) + + def visit_FunctionDef(self, node): + return self.check_types(node) + + def visit_AsyncFunctionDef(self, node): + return self.check_types(node) + + +if __name__ == "__main__": + errors = 0 + for file in sys.argv[1:]: + converter = CheckTypeHints(filename=file) + with open(file, "rb") as f: + tree = ast.parse(f.read(), filename=file) + converter.visit(tree) + errors += converter.errors + if errors: + exit(min(255, errors)) diff --git a/coffee.fish b/coffee.fish index 44323cd1..628ddbfe 100644 --- a/coffee.fish +++ b/coffee.fish @@ -2,7 +2,7 @@ function coffee --description 'wait until the coffee machine has finished two cu set hr (date +%H%M) if test \( (setvar COFFEE_MIN) -le "$hr" \) -a \( "$hr" -le (setvar COFFEE_MAX) \) measurelog home.coffeemachine.brew true & - household 270 'β˜•β˜•' 'coffee' 3 0.5 + household 270 'β˜•β˜•' 'coffee' 3 0.5 coffee else echo >&2 'no coffee anymore' return 1 diff --git a/correspondence/.gitignore b/correspondence/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/correspondence/.gitignore @@ -0,0 +1 @@ +* diff --git a/correspondence/letters/.gitignore b/correspondence/letters/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/correspondence/letters/.gitignore @@ -0,0 +1 @@ +* diff --git a/cpgit.fish b/cpgit.fish index cda76145..fe773b9b 100644 --- a/cpgit.fish +++ b/cpgit.fish @@ -1,6 +1,6 @@ function cpgit --description 'Copy the given file to another path, and add the file to git' -a src for item in $argv[2..] - mkdir -p (dirname -- "$item") + mkdir -p -- (dirname -- "$item") cp -- "$src" "$item" git add -- "$item" end diff --git a/cpvim.fish b/cpvim.fish index 7602bb6a..442e1639 100644 --- a/cpvim.fish +++ b/cpvim.fish @@ -1,6 +1,6 @@ function cpvim --description 'Copy the given file to another path, and start the editor' -a src for item in $argv[2..] - mkdir -p (dirname -- "$item") + mkdir -p -- (dirname -- "$item") cp -- "$src" "$item" editor -- "$item" end diff --git a/cpvimgit.fish b/cpvimgit.fish index 0716e5fc..865629e3 100644 --- a/cpvimgit.fish +++ b/cpvimgit.fish @@ -1,6 +1,6 @@ function cpvimgit --description 'Copy the given file to another path, add the file to git, and start the editor' -a src for item in $argv[2..] - mkdir -p (dirname -- "$item") + mkdir -p -- (dirname -- "$item") cp -- "$src" "$item" git add -- "$item" editor -- "$item" diff --git a/dockersavescp.fish b/dockersavescp.fish index d43bf2d7..c814cbd1 100644 --- a/dockersavescp.fish +++ b/dockersavescp.fish @@ -7,7 +7,7 @@ function dockersavescp --description 'save a docker image as a compressed tarbal set target "$nms[1]" echo -en "\033]0;🐳 $i/$n dockersave $name\7" set sz (docker image inspect -f '{{ .Size }}' "$name") - if [ -n "$host "] + if [ -n "$host" ] docker save "$name" | pv -s "$sz" | ssh "$host" "cat - > \"$target.tar.gz\"" else docker save "$name" | pv -s "$sz" > "$target.tar.gz" diff --git a/dockerscpload.fish b/dockerscpload.fish new file mode 100644 index 00000000..0ef83eb4 --- /dev/null +++ b/dockerscpload.fish @@ -0,0 +1,18 @@ +function dockerscpload --description 'transfer docker images over ssh to a remote directly loading these in docker at the remote side' -a host + set i 1 + set n (count $argv[2..]) + echo "$name" + for name in $argv[2..] + set nms (string split ':' -m 1 "$name") + set target "$nms[1]" + echo -en "\033]0;🐳 $i/$n dockersave $name\7" + set sz (docker image inspect -f '{{ .Size }}' "$name") + if [ -n "$host" ] + docker save "$name" | pv -s "$sz" | gzip | ssh "$host" "gzip -d | docker image load" + else + docker save "$name" | pv -s "$sz" > "$target.tar.gz" + end + set i (math "$i+1") + end + wait +end diff --git a/eggs.fish b/eggs.fish index 88e1e015..6dc89e5d 100644 --- a/eggs.fish +++ b/eggs.fish @@ -1,6 +1,6 @@ function eggs --description 'wait until the eggs are fully cooked in the water boiler' measurelog cooking.eggs.boiled true & echo -e 'takes \e[1m7%\e[0m of the home battery' - waitfor 1500 'πŸ₯š eggs' 'πŸ₯š ' + waitfor 1500 '' 'πŸ₯š eggs' 'πŸ₯š ' here_is_the_news end diff --git a/facialmask.fish b/facialmask.fish new file mode 100644 index 00000000..574a247e --- /dev/null +++ b/facialmask.fish @@ -0,0 +1,6 @@ +function facialmask --description 'register the use of a facial mask' -a kind + measurelog health.facialmask "$kind" & + timeout 1200 play -q -n synth 1200 brownnoise synth pinknoise mix synth sine amod 0.1 10 vol 0.2 >/dev/null 2>/dev/null & + waitfor 1200 '' 'πŸ§– facial mask' 'πŸ§– ' (getcolor random heal ,) + here_is_the_news +end diff --git a/fromtemplate.fish b/fromtemplate.fish index 3549fe04..220478bb 100644 --- a/fromtemplate.fish +++ b/fromtemplate.fish @@ -3,6 +3,7 @@ function fromtemplate -a tmpn -a to test -n "$to" || set to "$tmpn" natrix-env jinja2 -o "$to" -D "dirname="(basename (/usr/bin/pwd)) -D "basename="(basename "$to") -- "$src" || cp "$src" "$to" cp --attributes-only -- "$src" "$to" + chmod (stat --format '%a' -- "$src") -- "$to" git rev-parse --is-inside-work-tree >/dev/null 2>/dev/null && git add "$to" editor "$to" end diff --git a/getcolor.fish b/getcolor.fish index df16b8c0..976dd8c3 100644 --- a/getcolor.fish +++ b/getcolor.fish @@ -1,7 +1,7 @@ function getcolor --description 'get the color for a given name' -a name -a palette -a join function inner -a name -a palette test -n "$palette" || set palette 'colors' - set fl (assets)"$palette.json" + set fl (assets)"colors/$palette.json" if [ "$name" = 'random' ] set n (jq '.|length-1' "$fl") jq -r "to_entries["(random 0 $n)"].value.rgb|join(\"\n\")" "$fl" diff --git a/gitb.fish b/gitb.fish index ac27d5f7..3009355a 100644 --- a/gitb.fish +++ b/gitb.fish @@ -1,3 +1,7 @@ function gitb --wraps='git checkout -b' --description 'make a new git branch' - git checkout -b $argv + if set -q argv[1] + git checkout -b $argv + else + git branch + end end diff --git a/gitci.fish b/gitci.fish new file mode 100644 index 00000000..be08ad78 --- /dev/null +++ b/gitci.fish @@ -0,0 +1,3 @@ +function gitci --description 'list the last CI/CD runs on GitHub' + gh run list +end diff --git a/gitclonec.fish b/gitclonec.fish new file mode 100644 index 00000000..172d8d9f --- /dev/null +++ b/gitclonec.fish @@ -0,0 +1,5 @@ +function gitclonec --description 'clone a repository and chance the directory to that of the cloned repository' + set bn (basename "$argv") + git clone $argv + cd "$bn" +end diff --git a/gitcp.fish b/gitcp.fish index 4264751d..62beb814 100644 --- a/gitcp.fish +++ b/gitcp.fish @@ -1,4 +1,3 @@ function gitcp --wraps='git commit -am' --description 'commit with the given parameters, and push the changes' - git commit -am $argv; - gitp + git commit -am $argv && gitp end diff --git a/gitcpr.fish b/gitcpr.fish new file mode 100644 index 00000000..2c300838 --- /dev/null +++ b/gitcpr.fish @@ -0,0 +1,6 @@ +function gitcpr --description 'commit with the given parameters, push the changes and open a pull request on GitHub' + set message (echo "$argv") + git commit -am $argv; + gitp + gitpr $argv +end diff --git a/gitignores/.gitignore b/gitignores/.gitignore new file mode 100644 index 00000000..aca24175 --- /dev/null +++ b/gitignores/.gitignore @@ -0,0 +1,2 @@ +* +!*.gitignore diff --git a/gitignores/python.gitignore b/gitignores/python.gitignore new file mode 100644 index 00000000..44337e79 --- /dev/null +++ b/gitignores/python.gitignore @@ -0,0 +1,217 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +# poetry.lock +# poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml + diff --git a/gitpr.fish b/gitpr.fish new file mode 100644 index 00000000..fadf09e2 --- /dev/null +++ b/gitpr.fish @@ -0,0 +1,4 @@ +function gitpr --description 'open a pull request on GitHub' + set title (echo "$argv") + firefox (gh pr create --title "$title" --body "$title" 2>&1 | tail -n 1) +end diff --git a/gitprv.fish b/gitprv.fish new file mode 100644 index 00000000..00ad3604 --- /dev/null +++ b/gitprv.fish @@ -0,0 +1,3 @@ +function gitprv --wraps='gh pr view' --description 'show comments of the active pull request' + gh pr view $argv +end diff --git a/homebattery.fish b/homebattery.fish index 91fc9cdd..ffe9693d 100644 --- a/homebattery.fish +++ b/homebattery.fish @@ -7,5 +7,5 @@ function homebattery --description 'determine how long it will take to load the # 80% -> 100% @150Watts set s1 (math "max(80, $upto) - max(80, $cur)") set tot (math "round(73728*$s0/300 + 73728*$s1/150)") - waitfor "$tot" '⚑ battery' '⚑ ' + waitfor "$tot" '' '⚑ battery' '⚑ ' end diff --git a/hourcolor.fish b/hourcolor.fish new file mode 100644 index 00000000..2fd6feca --- /dev/null +++ b/hourcolor.fish @@ -0,0 +1,3 @@ +function hourcolor --description 'change the keyboard to the hour color, can be scheduled as cronjob' + keycolor (getcolor (date +%H) hour) 255 +end diff --git a/household.fish b/household.fish index 7c895b35..d7979ec2 100644 --- a/household.fish +++ b/household.fish @@ -1,5 +1,8 @@ -function household --description 'wait until a household device has finished with info' -a time -a emoji -a name -a energy -a water +function household --description 'wait until a household device has finished with info' -a time -a emoji -a name -a energy -a water -a keycol + if [ -n "$keycol" ] + keycolor (getcolor random "$keycol") 255 + end echo -e "takes \e[1m$energy%\e[0m of the home battery and \e[1m"$water"β„“\e[0m water" - waitfor $time "$emoji $name" "$emoji " + waitfor $time '' "$emoji $name" "$emoji " here_is_the_news end diff --git a/install.sh b/install.sh index 8fca702f..aad69ea0 100755 --- a/install.sh +++ b/install.sh @@ -1,13 +1,16 @@ #!/bin/bash curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg + echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list sudo add-apt-repository ppa:tatokis/alarm-clock-applet sudo apt-get update -sudo apt-get install -y alarm-clock-applet apt-file agrep certbot cloc curl cvc4 djinn fish git git-filter-repo git-lfs gnuplot-nox haskell-stack hlint hoogle ipython3 jq lambdabot ledger lynx nodeenv nodejs npm pandoc pdfsam powerline pv pylint python-wxtools python3-pytest rename sdate sox supervisor swi-prolog-core tig tox xdotool yarn zbar-tools z3 -sudo snap install upscayl +sudo apt-get install -y alarm-clock-applet apt-file agrep certbot cloc curl cvc4 djinn fish gh git git-filter-repo git-lfs gnuplot-nox haskell-stack hlint hoogle ipython3 jq lambdabot ledger lynx nodeenv nodejs npm pandoc pass-extension-otp pdfsam powerline pv pylint python-wxtools python3-pytest rename sdate sox supervisor swi-prolog-core tig visidata tox xdotool yarn zbar-tools z3 +sudo snap install pyright upscayl git lfs install git config --global push.default current @@ -55,8 +58,12 @@ rm -rf fonts abbr -a --regex '[.][.][.]([/].*)?' --position anywhere --function ... -- git_root abbr -a --regex '.*[{]-?[0-9]+[:]-?[0-9]+([:]-?[0-9]+)?[}].*' --position anywhere --function expand -- expand +# completions +gh completion -s fish > ~/.config/fish/completions/gh.fish + sudo groupadd light -usermod -aG light "$USER" +sudo groupadd docker +usermod -aG light,docker "$USER" sudo chgrp light /sys/class/leds/rgb:kbd_backlight/ sudo chmod g+w -R /sys/class/leds/rgb:kbd_backlight/ diff --git a/isortcp.fish b/isortcp.fish new file mode 100644 index 00000000..a3a1b8dd --- /dev/null +++ b/isortcp.fish @@ -0,0 +1,11 @@ +function isortcp --description 'run the isort formatter, and if isort made changes, commit and push these' + if set -q argv[1] + set args $argv + else + set args . + end + if ! isort --check --quiet $args + isort $args + gitcp 'isort reformatting' + end +end diff --git a/lunar.fish b/lunar.fish index 277504da..de472582 100644 --- a/lunar.fish +++ b/lunar.fish @@ -1,11 +1,11 @@ function lunar --description 'determine the phase of the moon for a given date' + set phases πŸŒ‘ πŸŒ’ πŸŒ“ πŸŒ” πŸŒ• πŸŒ– πŸŒ— 🌘 if set -q argv[1] set all $argv else set all 'now' end for when in $all - set phases πŸŒ‘ πŸŒ’ πŸŒ“ πŸŒ” πŸŒ• πŸŒ– πŸŒ— 🌘 set dy (date -d "$when" +%s) set dy (math "floor(8 * ((floor($dy/86400) - 25.5) % 29.5) / 29.5)+1") echo "$phases[$dy]" diff --git a/mkdircd.fish b/mkdircd.fish index 1389f2c1..6d287350 100644 --- a/mkdircd.fish +++ b/mkdircd.fish @@ -1,3 +1,3 @@ function mkdircd --description 'make a directory, if it does not yet exist, and cd into that directory' -a dir - mkdir -p "$dir" && cd "$dir" + mkdir -p -- "$dir" && cd "$dir" end diff --git a/mkdircdgit.fish b/mkdircdgit.fish new file mode 100644 index 00000000..536978c6 --- /dev/null +++ b/mkdircdgit.fish @@ -0,0 +1,10 @@ +function mkdircdgit --description 'make a directory, if it does not yet exist, cd into that directory, and start a git repository' -a pth -a lang + mkdircd "$pth" $argv && git init . + for lang in $argv[2..] + set gt (fish_home)/gitignores/$lang.gitignore + if [ -e "$gt" -a ! -e .gitignore ] + cat "$gt" >> .gitignore + git add .gitignore + end + end +end diff --git a/mkvim.fish b/mkvim.fish index c14d38f4..1cb0fd0a 100644 --- a/mkvim.fish +++ b/mkvim.fish @@ -1,6 +1,6 @@ function mkvim --description 'Make the directories needed to edit a file' for item in $argv - mkdir -p (dirname -- "$item") + mkdir -p -- (dirname -- "$item") editor -- "$item" end end diff --git a/mkvimgit.fish b/mkvimgit.fish index f10bdc17..e2d2296f 100644 --- a/mkvimgit.fish +++ b/mkvimgit.fish @@ -1,6 +1,6 @@ function mkvimgit --description 'Make the directories needed to edit a file and add the file to git' for item in $argv - mkdir -p (dirname -- "$item") + mkdir -p -- (dirname -- "$item") editor -- "$item" git add -- "$item" end diff --git a/movie.fish b/movie.fish index 87a14395..c02b2f6b 100644 --- a/movie.fish +++ b/movie.fish @@ -2,7 +2,7 @@ function movie --description 'register going to a movie' -a name -a duration test -n "$name" || read -P (string unescape '\e[31name of the movie\e[0m> ') name measurelog entertainment.movie "$name" if test -n "$duration" - waitfor "$duration" 'πŸŽ₯ movie' 'πŸŽ₯ ' + waitfor "$duration" '' 'πŸŽ₯ movie' 'πŸŽ₯ ' here_is_the_news end end diff --git a/mynote.fish b/mynote.fish new file mode 100644 index 00000000..a52470dc --- /dev/null +++ b/mynote.fish @@ -0,0 +1,21 @@ +function mynote --description 'Add a notebook entry to a specific topic' -a topic + test -n "$topic" || set topic general + if set -q argv[2] + set nt (echo $argv[2..]) + else + set hhmm (date +%H%M) + set nt (read -P (echo -e "\033]0;πŸ““ $hhmm $topic notebook\a \e[93m$hhmm>\e[0m ")) + set hhmm (date +%H%M) + if [ ! -n "$nt" ] + echo -e "\e[1A\r\e[31mβœ— $hhmm>\e[0m" + end + end + if [ -n "$nt" ] + measurelog "notebook.$topic" "$nt" + if ! set -q argv[2] + echo -e "\e[1A\r\e[32mβœ“ $hhmm>\e[0m" + end + else + return 1 + end +end diff --git a/note.fish b/note.fish new file mode 100644 index 00000000..c2a27d33 --- /dev/null +++ b/note.fish @@ -0,0 +1,3 @@ +function note --description 'Add a notebook entry' + mynote general +end diff --git a/notes.fish b/notes.fish new file mode 100644 index 00000000..aff754d9 --- /dev/null +++ b/notes.fish @@ -0,0 +1,5 @@ +function notes --description 'Notebook in a loop to log entries' -a topic + test -n topic || set topic general + while mynote "$topic" + end +end diff --git a/otp.fish b/otp.fish new file mode 100644 index 00000000..dbb8c0c8 --- /dev/null +++ b/otp.fish @@ -0,0 +1,3 @@ +function otp --wraps='pass otp' --description 'generate/configure One Time Passwords (OTPs) with pass-extension-otp' + pass otp $argv +end diff --git a/piper.fish b/piper.fish index 49ba9301..1a2fcbde 100644 --- a/piper.fish +++ b/piper.fish @@ -1,3 +1,4 @@ -function piper - natrix -m piper -m en_US-lessac-medium --output-raw 2>/dev/null | aplay -r 22050 -f S16_LE -t raw - 2>/dev/null +function piper -a lang + test -n "$lang" || set lang en_US-lessac-high + natrix -m piper -m "$lang" --output-raw 2>/dev/null | aplay -r 22050 -f S16_LE -t raw - 2>/dev/null end diff --git a/pomodoro.fish b/pomodoro.fish index 3b968f2e..6395d25b 100644 --- a/pomodoro.fish +++ b/pomodoro.fish @@ -15,39 +15,44 @@ function pomodoro --description 'the pomodoro technique to stay focussed' end echo "$last_pid" end - while true + set ending 0 + while [ "$ending" -eq 0 ] set pause 300 set pausetype '\e[10D\e[32m[short pause]\e[0m' set pausetitle '⏸️ ' for i in (seq 4) - xdotool key XF86AudioPlay & - notify-send -i /usr/share/icons/hicolor/64x64/apps/io.github.alarm-clock-applet.clock.png -c productivity -u low 'pomodoro ⏰' '▢️ start working' - gsettings set org.gnome.desktop.notifications show-banners false - set gamma (noising) - trap "endpom $gamma" EXIT KILL INT QUIT STOP - gh_status 'Focusing' 'arrow_forward' '25 minutes' true - set end (date '+%Y-%m-%d %T%z' -d "+40 minutes") - echo "$end" > "$HOME/block_sleep" - measurelog pomodoro.work true & - waitfor 1500 '\e[100D \e[100D\e[31m[working]\e[0m' '▢️ ' (getcolor random focus_colors ,) - kill "$gamma" - if [ "$i" -gt 3 ] - set waitcolor '255,3,32' - set pause 900 - set pausetype '\e[10D\e[33m[long pause]\e[0m' - set pausetitle '⏹️ ' - gh_status 'Long pause' 'stop_button' '15 minutes' false - else - set waitcolor (getcolor random heal_colors ,) - gh_status 'Short pause' 'pause_button' '5 minutes' false + if [ "$ending" -eq 0 ] + xdotool key XF86AudioPlay & + notify-send -i /usr/share/icons/hicolor/64x64/apps/io.github.alarm-clock-applet.clock.png -c productivity -u low 'pomodoro ⏰' '▢️ start working' + gsettings set org.gnome.desktop.notifications show-banners false + set gamma (noising) + trap "endpom $gamma" EXIT KILL INT QUIT STOP + gh_status 'Focusing' 'arrow_forward' '25 minutes' true + set end (date '+%Y-%m-%d %T%z' -d "+40 minutes") + echo "$end" > "$HOME/block_sleep" + measurelog pomodoro.work true & + waitfor 1500 '' '\e[100D \e[100D\e[31m[working]\e[0m' '▢️ ' (getcolor random focus ,) || set ending 1 + kill "$gamma" + if [ "$ending" -eq 0 ] + if [ "$i" -gt 3 ] + set waitcolor '255,3,32' + set pause 900 + set pausetype '\e[10D\e[33m[long pause]\e[0m' + set pausetitle '⏹️ ' + gh_status 'Long pause' 'stop_button' '15 minutes' false + else + set waitcolor (getcolor random heal ,) + gh_status 'Short pause' 'pause_button' '5 minutes' false + end + xdotool key XF86AudioPlay & + here_is_the_news & + gsettings set org.gnome.desktop.notifications show-banners "$banners" + notify-send -i /usr/share/icons/hicolor/64x64/apps/io.github.alarm-clock-applet.clock.png -c productivity -u low 'pomodoro ⏰' '⏸️ take a break' + measurelog pomodoro.pause true & + waitfor "$pause" '' "$pausetype" "$pausetitle" "$waitcolor" || set ending 1 + here_is_the_news & + end end - xdotool key XF86AudioPlay & - here_is_the_news & - gsettings set org.gnome.desktop.notifications show-banners "$banners" - notify-send -i /usr/share/icons/hicolor/64x64/apps/io.github.alarm-clock-applet.clock.png -c productivity -u low 'pomodoro ⏰' '⏸️ take a break' - measurelog pomodoro.pause true & - waitfor "$pause" "$pausetype" "$pausetitle" "$waitcolor" - here_is_the_news & end end end diff --git a/presleep.fish b/presleep.fish index 1ecd8b51..0ab7cd1b 100644 --- a/presleep.fish +++ b/presleep.fish @@ -7,7 +7,7 @@ function presleep --description 'first gradually make a person sleepy before the play -q -n synth 1500 sine 409/370 sine 369 fade 0 0 750 vol 0.2 & end for i in (seq 4 -1 0) - set cols (getcolor $i wakeup_colors) + set cols (getcolor $i wakeup) keycolor $cols (math "50*$i+55") sleep 300 end diff --git a/pydoc.fish b/pydoc.fish new file mode 100644 index 00000000..69ae5880 --- /dev/null +++ b/pydoc.fish @@ -0,0 +1,4 @@ +function pydoc --description 'Add doc strings to all elements of a Python file' + set h (fish_home) + natrix "$h/add_docstrings.py" $argv +end diff --git a/rabbitdoc.fish b/rabbitdoc.fish new file mode 100644 index 00000000..10883848 --- /dev/null +++ b/rabbitdoc.fish @@ -0,0 +1,3 @@ +function rabbitdoc --description 'ask CodeRabbit.ai to generate docstrings for the latest pull request' + firefox (gh pr comment (gh pr list --json number --jq '.[0].number') -b '@coderabbitai generate docstrings') +end diff --git a/requirements.txt b/requirements.txt index 216598bd..1c3ef267 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +astor autopep8 black Click @@ -16,6 +17,7 @@ opencv-python pandas pip piper-tts +pre-commit prefect pycountry pygments diff --git a/season.fish b/season.fish new file mode 100644 index 00000000..bdc46a0c --- /dev/null +++ b/season.fish @@ -0,0 +1,17 @@ +function season --description 'determine the season for a given day' + set seasons '0100 0299 ❄️ ' '0300 0599 🌱' '0600 0899 🌻' '0900 1199 πŸ‚' '1200 1299 ❄️ ' + if set -q argv[1] + set all $argv + else + set all 'now' + end + for when in $all + set dy (date -d "$when" +%m%d) + for season in $seasons + set s (string split ' ' $season) + if test "$s[1]" -le "$dy" -a "$dy" -le "$s[2]" + echo -e "$s[3]" + end + end + end +end diff --git a/setvar.fish b/setvar.fish index 3fde2469..f72d7d5d 100644 --- a/setvar.fish +++ b/setvar.fish @@ -1,5 +1,7 @@ function setvar --description 'check if a variable exists, and otherwise prompt for a value' -a name -a helptext - test -n "$helptext" && set helptext " [$helptext]" - set -q "$name" || read -Ux "$name" -P (string unescape '$\e[31m')"$name$helptext"(string unescape '\e[0m> ') + if ! set -q name + test -n "$helptext" && set helptext " [$helptext]" + read -Ux "$name" -P (string unescape '$\e[31m')"$name$helptext"(string unescape '\e[0m> ') + end eval "echo \"\$$name\"" end diff --git a/sleep_for.fish b/sleep_for.fish index 24cc3b50..1ccbb5a8 100644 --- a/sleep_for.fish +++ b/sleep_for.fish @@ -1,4 +1,4 @@ -function sleep_for --description 'Sleep a given number of hours' -a n +function sleep_for --description 'Sleep a given number of hours' -a n -a noair test -n "$n" || set n '7' if test "$n" -gt 12 -o "$n" -lt -12 echo "Too long" @@ -12,15 +12,17 @@ function sleep_for --description 'Sleep a given number of hours' -a n xinput -enable 11 gh_status '' 'alarm_clock' '25 minutes' false end - airplane & + if [ ! -n "$noair" ] + airplane & + end gsettings set org.gnome.desktop.peripherals.touchpad send-events disabled & xinput -disable 11 # kill noisy apps killall element-desktop thunderbird-bin >/dev/null 2>/dev/null & set banners (gsettings get org.gnome.desktop.notifications show-banners) gsettings set org.gnome.desktop.notifications show-banners false - fill (getcolor 0 wakeup_colors) - keycolor (getcolor random sleep_colors) 64 + fill (getcolor 0 wakeup) + keycolor (getcolor random sleep) 64 if [ "$n" -lt 0 ] set n (math "-$n") set pth (/usr/bin/pwd) @@ -48,7 +50,7 @@ function sleep_for --description 'Sleep a given number of hours' -a n set pct (math "round(100*($eps-$f)/$eps)") echo -en "\033]0;πŸ’€ $tf\007\033]9;4;1;$pct\033\0134$tf\e[0m\e["$ntf"D" sleep 600 - keycolor (getcolor random sleep_colors) 32 + keycolor (getcolor random sleep) 32 xset dpms force off end sleep 300 diff --git a/smartwatch.fish b/smartwatch.fish index 383b3301..5cbadd81 100644 --- a/smartwatch.fish +++ b/smartwatch.fish @@ -3,6 +3,6 @@ function smartwatch --description 'determine how long it will take to load the s test -n "$upto" || set upto 100 measurelog home.smartwatch (jsonlist "$cur" "$upto") & set tot (math "round(51.724137931*($upto-$cur))") - waitfor "$tot" '⌚ battery' '⌚ ' + waitfor "$tot" '' '⌚ battery' '⌚ ' here_is_the_news end diff --git a/taskflush.fish b/taskflush.fish index 72a48741..6778a527 100644 --- a/taskflush.fish +++ b/taskflush.fish @@ -1,3 +1,3 @@ function taskflush --description 'set all expired tasks to done' - taskd (taskd | grep -P 'P\d+D\s*[-]' | cut -d' ' -f 1) + taskd (taskd | grep -P 'P\d+D\s*[-]' | sed -E 's/^\s+//' | cut -d' ' -f 1) end diff --git a/teeth.fish b/teeth.fish index 5807101f..4024eca8 100644 --- a/teeth.fish +++ b/teeth.fish @@ -1,6 +1,6 @@ function teeth --description 'help cleaning teeth' function draw_teeth -a cc -a tt -a kin - set teeth_map 'o7β–•u7β–ˆi7▏ Tβ–ˆβ–ˆβ–ˆβ–ˆ i0β–•u0β–ˆo0▏\no7β–•u7β–ˆi7▏ Tβ–ˆβ–ˆβ–ˆβ–ˆ i0β–•u0β–ˆo0▏\no6β–•u6β–ˆi6▏ Tβ–ˆβ–ˆβ–ˆβ–ˆ i1β–•u1β–ˆo1▏\no6β–•u6β–ˆi6▏ Tβ–œβ–ˆβ–ˆβ–› i1β–•u1β–ˆo1▏\no5β–•u5β–ˆi5▏ i2 β–•u2β–ˆo2▏\n i5▁ i4▁ i4▁ i3▁ i3▁ i2▁ \n u5β–ˆ u4β–ˆ u4β–ˆ u3β–ˆ u3β–ˆ u2β–ˆ \n o5β–” o4β–” o4β–” o3β–” o3β–” o2β–” ' ' o5▁ o4▁ o4▁ o3▁ o3▁ o2▁\n u5β–ˆ u4β–ˆ u4β–ˆ u3β–ˆ u3β–ˆ u2β–ˆ\n i5β–” i4β–” i4β–” i3β–” i3β–” i2β–”\no5β–•u5β–ˆi5▏ i2β–•u2β–ˆo2▏\no6β–•u6β–ˆi6▏ i1β–•u1β–ˆo1▏\no6β–•u6β–ˆi6▏ i1β–•u1β–ˆo1▏\no7β–•u7β–ˆi7▏ i0β–•u0β–ˆo0▏\no7β–•u7β–ˆi7▏ i0β–•u0β–ˆo0▏' + set teeth_map 'o7β–•u7β–ˆi7▏ Tβ–ˆβ–ˆβ–ˆβ–ˆ i0β–•u0β–ˆo0▏\no7β–•u7β–ˆi7▏ Tβ–ˆβ–ˆβ–ˆβ–ˆ i0β–•u0β–ˆo0▏\no6β–•u6β–ˆi6▏ Tβ–ˆβ–ˆβ–ˆβ–ˆ i1β–•u1β–ˆo1▏\no6β–•u6β–ˆi6▏ Tβ–œβ–ˆβ–ˆβ–› i1β–•u1β–ˆo1▏\no5β–•u5β–ˆi5▏ i2β–•u2β–ˆo2▏\n i5▁ i4▁ i4▁ i3▁ i3▁ i2▁ \n u5β–ˆ u4β–ˆ u4β–ˆ u3β–ˆ u3β–ˆ u2β–ˆ \n o5β–” o4β–” o4β–” o3β–” o3β–” o2β–” ' ' o5▁ o4▁ o4▁ o3▁ o3▁ o2▁\n u5β–ˆ u4β–ˆ u4β–ˆ u3β–ˆ u3β–ˆ u2β–ˆ\n i5β–” i4β–” i4β–” i3β–” i3β–” i2β–”\no5β–•u5β–ˆi5▏ i2β–•u2β–ˆo2▏\no6β–•u6β–ˆi6▏ i1β–•u1β–ˆo1▏\no6β–•u6β–ˆi6▏ i1β–•u1β–ˆo1▏\no7β–•u7β–ˆi7▏ i0β–•u0β–ˆo0▏\no7β–•u7β–ˆi7▏ i0β–•u0β–ˆo0▏' set tooth_map $teeth_map[$cc] set col (echo -e '\e[97m') set tongue (echo -e '\e[31m') @@ -23,7 +23,7 @@ function teeth --description 'help cleaning teeth' for tooth in $teeth set kin (string sub -l 1 "$move") draw_teeth "$cc" "$tt" "$kin" - waitfor 3 "$cheek - $move - $tooth" "clean teeth $cheek - $move - $tooth " + waitfor 3 '' "$cheek - $move - $tooth" "clean teeth $cheek - $move - $tooth " echo -en ' \e[100D\e[8A' set tt (math $tt+1) end @@ -34,6 +34,6 @@ function teeth --description 'help cleaning teeth' echo ' ' end echo -en ' \e[100D\e[8A' - waitfor 5 'brush tongue' 'brush tongue ' + waitfor 5 '' 'brush tongue' 'brush tongue ' echo 'do NOT rinse with water ' end diff --git a/templates/install.sh b/templates/install.sh index 7a693aac..45ae3c42 100755 --- a/templates/install.sh +++ b/templates/install.sh @@ -1,3 +1,4 @@ #!/bin/bash - +sudo apt-get update -y +sudo apt-get install -y diff --git a/timeprompt.fish b/timeprompt.fish new file mode 100644 index 00000000..1a61bb11 --- /dev/null +++ b/timeprompt.fish @@ -0,0 +1,3 @@ +function timeprompt --description 'wait for a given amount of time, or until the person hits a key exits with 1 if the user interrupted' -a time + ! bash -c "read -n 1 -t '$time'" +end diff --git a/typehintcheck.fish b/typehintcheck.fish new file mode 100644 index 00000000..ea9482b9 --- /dev/null +++ b/typehintcheck.fish @@ -0,0 +1,4 @@ +function typehintcheck --description 'Check if all defined functions have type hints' + set h (fish_home) + natrix "$h/check_typehints.py" $argv +end diff --git a/variables.json b/variables.json index f03bfb4d..be48a222 100644 --- a/variables.json +++ b/variables.json @@ -1,3 +1,9 @@ { - "COFFEE_MIN": "start time in decimal notation to drink coffee, for example `0800`" + "COFFEE_MAX": ["end time of the day when coffee is no longer allowed, for example `1900`", null], + "COFFEE_MIN": ["start time of the day when coffee is allowed in decimal notation, for example `0800`", null], + "GIT_BRANCH": ["the default branch, used to automatically map the remote to the local default branch in `gitremote`", null], + "GIT_REPO_PREFIX": ["the prefix of (most) git repositories, for example `github:hapytex`", null], + "GROQ_API_TOKEN": ["the API token for *Groq* to make chatbot requests, can be found [here](https://console.groq.com/keys)", null], + "USER_SLUG": ["the slug used to prepend to file names when adding an author to it, for example `Elon_Musk`", null], + "DEFAULT_CURRENCY": ["The default currency used in transactions", "EUR"] } diff --git a/vimc.fish b/vimc.fish index 56220fe9..35fa8770 100644 --- a/vimc.fish +++ b/vimc.fish @@ -1,4 +1,4 @@ function vimc --description 'create a directory if the directory does not yet exist before running vim' - mkdir -p (dirname $argv); + mkdir -p -- (dirname $argv); vim -p $argv; end diff --git a/vimgit.fish b/vimgit.fish new file mode 100644 index 00000000..c5ce8660 --- /dev/null +++ b/vimgit.fish @@ -0,0 +1,7 @@ +function vimgit --description 'Edit the given files in the parameters, and add these to git' + for item in $argv + mkdir -p -- (dirname -- "$item") + editor -- "$item" + git add -- "$item" + end +end diff --git a/vitamins.fish b/vitamins.fish index 12887cf8..c267ad62 100644 --- a/vitamins.fish +++ b/vitamins.fish @@ -1,5 +1,5 @@ function vitamins --description 'take our daily vitamins and magnesium' - waitfor 90 'πŸ’Š vitamins' 'πŸ’Š ' + waitfor 90 '' 'πŸ’Š vitamins' 'πŸ’Š ' measurelog health.vitamins true hydrate 33cl end diff --git a/waitfor.fish b/waitfor.fish index df66c2a9..00c678ff 100644 --- a/waitfor.fish +++ b/waitfor.fish @@ -1,4 +1,5 @@ -function waitfor --description 'wait a certain amount of time, or until the user hits ENTER' -a time -a label -a desc -a keydown -a until -a step +function waitfor --description 'wait a certain amount of time, or until the user hits ENTER' -a time -a allow -a label -a desc -a keydown -a until -a step + set es 0 if [ -n "$keydown" ] keycolor (string split , "$keydown") 255 & end @@ -11,32 +12,56 @@ function waitfor --description 'wait a certain amount of time, or until the user else set label '' end + set clems 'β—‹' 'β—”' 'β—‘' 'β—•' '●' + set c (tput cols) + set ll (string length "$label") + set c (math "$c - $ll") test -n "$until" || set until 0 test -n "$step" || set step 1 set oldval 256 set oldn 8 set time (math "round($time)") - for i in (seq "$time" "-$step" "$until") - set txt (timeformat "$i") + set cur (date '+%s') + set tar (math "$cur + $time") + while test "$cur" -lt "$tar" + set rmd (math "$tar - $cur") + set txt (timeformat "$rmd") set n (string length "$txt") if [ "$n" -lt "$oldn" ] # erase previous one if length differs echo -en ' \e[8D' end - set cl (math "min(255, 4*$i)") - echo -en "\e[1m\e[38;2;255;$cl;"$cl"m$txt\e[0m\e["$n"D" - set pct (math "round(100*($time-$i)/$time)") - echo -en "\033]0;$desc$txt\007\033]9;4;1;$pct\033\0134" + set ll (string length "$desc$txt") + set rm (math "$c - $ll - 1") + set nn (math "round($rm*($time-$rmd)/$time)") + set fl (string repeat -n$nn 'β–ˆ') + set nnn (math "$n+$nn+1") + set cl (math "min(255, 4*$rmd)") + echo -en "\e[1m\e[38;2;255;$cl;"$cl"m$txt $fl\e[0m\e["$nnn"D" + set pct (math "round(100*($time-$rmd)/$time)") + set pcf (math "max(1, min(5, floor($pct/20)+1))") + echo -en "\033]0;$clems[$pcf]$desc$txt\007\033]9;4;1;$pct\033\0134" if [ -n "$keydown" ] - set newval (math "round(235*$i/$time)+20") + set newval (math "round(235*$rmd/$time)+20") if [ "$oldval" -ne "$newval" ] echo "$newval" > /sys/class/leds/rgb:kbd_backlight/brightness & set oldval $newval end end - sleep "$step" + if [ -z "$allow" ] + if ! timeprompt "$step" + set cur "$tar" + set es 1 + else + set cur (date '+%s') + end + else + sleep "$step" + set cur (date '+%s') + end set oldn "$n" end # clear progress echo -en "\033]9;4;0\007" + return "$es" end diff --git a/wakeup.fish b/wakeup.fish index 8c74fef0..83ada668 100644 --- a/wakeup.fish +++ b/wakeup.fish @@ -11,7 +11,7 @@ function wakeup --description 'Make some noise to wake someone up' -a tty -a l - for i in (seq 0 4) xrandr --output eDP-1 --brightness (math "($i+1)/5") & xset dpms force on & - set cols (getcolor $i wakeup_colors) + set cols (getcolor $i wakeup) keycolor $cols (math "50*$i+55") fill $cols $l $c > "$tty" sleep 300 diff --git a/walking.fish b/walking.fish index 7c10b8ce..4565ac3a 100644 --- a/walking.fish +++ b/walking.fish @@ -1,4 +1,4 @@ function walking --description 'register a walk' - waitfor 2100 '🚢 walking' '🚢 ' + waitfor 2100 '' '🚢 walking' '🚢 ' measurelog activity.walking (activity_survey) end diff --git a/whatday.fish b/whatday.fish index 198538ed..43933da1 100644 --- a/whatday.fish +++ b/whatday.fish @@ -2,17 +2,18 @@ function whatday --description 'specifies the day with some counters' set dt (date +%s -d '0:00 UTC') set hr (date +%H%M) set colors '232;20;22' '255;165;0' '250;235;54' '121;195;20' '72;125;231' '75;54;157' '112;54;157' - set tcol 97 97 30 30 30 97 97 + set tcol 97 30 30 30 97 97 97 set planets '☽ ' 'β™‚' '☿' '♃' '♀' 'β™„' 'β˜‰' - set polars '↙' 'β†—' + set polars '↙' 'β†˜' 'β†–' 'β†—' '⇙' 'β‡˜' 'β‡–' 'β‡—' + set npol (count $polars) set caf 'πŸš«β˜•' if test \( (setvar COFFEE_MIN) -le "$hr" \) -a \( "$hr" -le (setvar COFFEE_MAX) \) set caf 'β˜•' end set ds (math --scale 0 "$dt/86400") set dow (math "(($ds + 3) % 7) + 1") - set dp (math "($ds % 2) + 1") + set dp (math "($ds % $npol) + 1") set bat (date +%s) set bat (math "round(((((39600 - $bat) % 1209600) + 1209600) % 1209600) / 12096)") - echo -e "\e[48;2;$colors[$dow];$tcol[$dow]m$polars[$dp] "(lunar)" $planets[$dow] "(date '+%d %b')" $caf "(sdate)" "(advent)" "(battery $bat)" "(battery)" \e[0m" + echo -e "\e[48;2;$colors[$dow];$tcol[$dow]m$polars[$dp] "(lunar)(season)" $planets[$dow] "(date '+%d %b')" $caf "(sdate)" "(advent)" "(battery $bat)" "(battery)" \e[0m" end