From 16cab6edb3398a3d2f17c4e5f74160a284bedf85 Mon Sep 17 00:00:00 2001 From: luk Date: Wed, 31 Mar 2021 00:42:01 +0200 Subject: [PATCH 1/4] New: Script to create reoccuring events based on Eastersunday For the functionality see `-h` and the 'README.md'. I think this is useful since it fills the gap for creating events for holiday days which dates are based on quite complicated functions like Eastesunday. --- contrib/holiday/README.md | 60 +++++++++++++ contrib/holiday/calcurse-holiday.py | 125 +++++++++++++++++++++++++++ contrib/holiday/holiday.yaml | 10 +++ contrib/holiday/testing-functions.py | 33 +++++++ 4 files changed, 228 insertions(+) create mode 100644 contrib/holiday/README.md create mode 100755 contrib/holiday/calcurse-holiday.py create mode 100644 contrib/holiday/holiday.yaml create mode 100644 contrib/holiday/testing-functions.py diff --git a/contrib/holiday/README.md b/contrib/holiday/README.md new file mode 100644 index 00000000..60ea9455 --- /dev/null +++ b/contrib/holiday/README.md @@ -0,0 +1,60 @@ +calcurse-holiday +================ + +calcurse-holiday is a Python script that can create holiday entries for +calcurse for a desired list of years based on a configuration file. Please note +that this script is still in an alpha version, so please report bugs (I think +the best way for this will be github, you can mention @ #TODO) + +Why not simply use reocuring events? +------------------------------------ +Since some holiday days are not on the same date each year (e.g.some are +connected to the date Eastersunday is placed on), it is not possible to create +native reocuring events in calcurse for these events. + +Usage +----- +* Create a `yaml` file which will be the base of which holiday dates will be +inserted. +* Then run `calcurse-holiday` with the path to the yaml-file you just created + and a list of years you'd like to create the holiday-events for. + +To check if the dates are correct before inserting them, just run the script +with the `-s` option. + +**Tipp:** On failiure the new events should simple by at the end of your `app` file +of calcurse and it should be easy to remove them again + +Examples: +-------- +``` +holiday.yaml +---------------------- +# specify the description/name of the events that will be created +holiday_msg: "%s - free day" # %s will be replaced with the name/key of the +holiday date + +# current valid date specs: +# "easter [+-]x day" => Date will be x days before/after the day of Eastersunday +# "MM-DD [+-]x weekday" => Date will be the x.th mon/thu/... before/after +# YYYY-MM-DD (with given year). +# The weekday is specified as 1->Monday 2->Thuesday ... +irregular holiday: + - Eastersunday: "easter +0 day" + - Eastermonday: "easter +1 day" + - Thanksgiving: 11-01 +4 4 +``` + +`calcurse-holiday holiday.yaml 2021 2022` this will read the holiday +template from the `holiday.yaml` File and create the holiday events for +the years `2021` and `2022` + +Notes +----- +Currently the purpose of this script is ONLY for events that are weekday offset +based or on eastern. More base dates are possible, just suggest them ;) + +Maybe one day this will support "normal" reoccuring Events too. But since this +can much better be done via calcurses native reoccuring events (which is much +better, since one doesn't need to remember to recreate the events), this is not +the main purpose of this script. diff --git a/contrib/holiday/calcurse-holiday.py b/contrib/holiday/calcurse-holiday.py new file mode 100755 index 00000000..7ea555cb --- /dev/null +++ b/contrib/holiday/calcurse-holiday.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 + +import sys +import os +import subprocess +from datetime import datetime, timedelta +import argparse + +import yaml +import re + +# to calculate the date of the Eastersunday +def spencerFormula(y:int) -> datetime: + a = y % 19 + b = y // 100 + c = y % 100 + d = b // 4 + e = b % 4 + f = (b+8) // 25 + g = (b-f+1) // 3 + h = (19*a+b-d-g+15) % 30 + i = c // 4 + k = c % 4 + l = (32+2*e+2*i-h-k) % 7 + m = (a+11*h+22*l) // 451 + n = (h+l-7*m+114) // 31 + p = (h+l-7*m+114) % 31 + + return datetime(year=y, month=n, day=p+1) + + +def fullDayAppointmentIcal(date:datetime, offset:int, desc:str, noDup:bool = True, simu:bool = False) -> str: + ret = [] + ret.append("BEGIN:VEVENT") + ret.append("DTSTART;VALUE=DATE:" + (date + timedelta(days=offset)).strftime("%Y%m%d")) + ret.append("SUMMARY:" + desc) + if simu: + print("Event", desc, "on ", (date + timedelta(days=offset)).strftime("%Y-%m-%d"), "would be created") + ret.append("END:VEVENT") + if noDup: + command = [ + "/usr/bin/calcurse", + "-Q", + "-d", "%s" % (date + timedelta(days=offset)).strftime("%d/%m/%Y"), + "--filter-pattern",'"^%s$"' % re.escape(desc).replace("\\\\", "\\")] + p = subprocess.Popen(" ".join(command), stderr=None, stdin=None, stdout=subprocess.PIPE, encoding="utf-8", shell=True) + stdout, stderr = p.communicate() + if stdout != "": + print(ret, "is already present -> skip") + return "" + + return "\n".join(ret) + + +def toCalcurse(data:list[str]): + if "".join(data) != "": + data = \ + ["BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//calcurse-holiday//NONSGML calcurse-holiday//EN"] \ + + data \ + + ["END:VCALENDAR\n"] + + command = ["calcurse"] + [ + '-i', '-', #read from stdin + '--dump-imported', + '-q' # quiet + ] + p = subprocess.Popen(command, stderr=None, stdin=subprocess.PIPE, stdout=None, encoding="utf-8") + p.communicate("\n".join(data)) + + +def createForYears(file:str, years:list[int], simu:bool): + with open(file, 'r') as f: + c = yaml.load(f, Loader=yaml.FullLoader) + + msg = c["holiday_msg"] if "holiday_msg" in c else "%s" + + if "irregular holiday" in c: + ical = [] + for d in c["irregular holiday"]: + d = list(d.items()) + if len(d) > 1: + print("Error while parsing the config", file=sys.stderr) + continue + + name,dateDesc = d[0] + + r1 = re.match(r'^easter ([+-]\d+) day$', dateDesc) + r2 = re.match(r'^(\d{2}\-\d{2}) ([+-]\d+) ([1-7])$', dateDesc) + + for year in years: + if r1: + offset = r1.groups()[0] + dtStart = spencerFormula(year) + ical.append(fullDayAppointmentIcal(dtStart, int(offset), msg % name, simu=simu)) + elif r2: + start,scale,weekday = r2.groups() + dtStart = datetime.strptime(str(year)+"-"+start, "%Y-%m-%d") + # print(int(weekday), "-", dtStart.weekday(),"- 1", "% 7 + ", int(scale), "* 7") + offset = (int(weekday) - dtStart.weekday()-1) % 7 + int(scale)*7 + ical.append(fullDayAppointmentIcal(dtStart, offset, msg % name, simu=simu)) + else: + print("Failed to read entry", dateDesc, file=sys.stderr) + if simu: + print("This ical would have been imported") + print(ical) + else: + toCalcurse(ical) + + +parser = argparse.ArgumentParser("calcurse-holiday", + description="Add holiday entries based on given holiday.yaml file to calcurse") + +parser.add_argument("file", nargs='?', + default="./holiday.yaml", + help="The yaml file to read the holidays from [defaults to './holiday.yaml']") +parser.add_argument("year", nargs='+', + type=int, + help="Specify a list of years to generate the holidays for") +parser.add_argument("-s", "--simulate", + action='store_true', + help="Only simulate the insertion of the events") + +args = parser.parse_args() + +createForYears(args.file, args.year, args.simulate) diff --git a/contrib/holiday/holiday.yaml b/contrib/holiday/holiday.yaml new file mode 100644 index 00000000..f5aed5a0 --- /dev/null +++ b/contrib/holiday/holiday.yaml @@ -0,0 +1,10 @@ +# holiday_msg: "Frei %s" + +regular holiday: + - Tag der Arbeit: 1.5. + +# valid specifications are "easter +- x day" or MM.DD. +- weekday (given as 1-7 starting at monday) +irregular holiday: + - Ostersonntag: "easter +0 day" + - Ostermontag: "easter +1 day" + - Buß- und Bettag: "11-23 -1 3" diff --git a/contrib/holiday/testing-functions.py b/contrib/holiday/testing-functions.py new file mode 100644 index 00000000..56feebd8 --- /dev/null +++ b/contrib/holiday/testing-functions.py @@ -0,0 +1,33 @@ +#!/bin/python +import math + +# According to wikipedia these are critical years for Eastersunday date calculations +l = [(1590,"22.4") ,(1666,"25.4") ,(1685,"22.4") ,(1924,"20.4") ,(1943,"25.4") ,(1962,"22.4") ,(2019,"21.4") ,(2038,"25.4") ,(2057,"22.4") ,(2076,"19.4") ,(2095,"24.4") ,(2133,"19.4") ,(2152,"23.4") ,(2171,"21.4") ,(2190,"25.4") ,(1876,"16.4") ,(1974,"14.4") ,(2045,"9.4") ,(2069,"14.4") ,(2089,"3.4") ,(2096,"15.4") ,(1802,"18.4") ,(1805,"14.4") ,(1818,"22.3") ,(1825,"3.4") ,(1829,"19.4") ,(1845,"23.3") ,(1900,"15.4") ,(1903,"12.4") ,(1923,"1.4") ,(1927,"17.4") ,(1954,"18.4") ,(1967,"26.3") ,(1981,"19.4") ,(2049,"18.4") ,(2076,"19.4") ,(2106,"18.4") ,(2119,"26.3") ,(2133,"19.4") ,(2147,"16.4") ,(2150,"12.4") ,(2170,"1.4") ,(2174,"17.4")] + +def spencerFormula(y:int): + a = y % 19 + b = y // 100 + c = y % 100 + d = b // 4 + e = b % 4 + f = (b+8) // 25 + g = (b-f+1) // 3 + h = (19*a+b-d-g+15) % 30 + i = c // 4 + k = c % 4 + l = (32+2*e+2*i-h-k) % 7 + m = (a+11*h+22*l) // 451 + n = (h+l-7*m+114) // 31 + p = (h+l-7*m+114) % 31 + + return str(p+1) + "." + str(n) + +def checkEastern(foo, verbose:bool=False): + for y,e in l: + x = foo(y) + if x != e: + print(y, "failed, expected", e, "but was", x) + elif verbose: + print("success") + +checkEastern(spencerFormula) From db0348a93aff39e3239d96f55748b275554f570b Mon Sep 17 00:00:00 2001 From: luk Date: Wed, 31 Mar 2021 00:54:38 +0200 Subject: [PATCH 2/4] formatting, todos and additional info --- contrib/holiday/README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contrib/holiday/README.md b/contrib/holiday/README.md index 60ea9455..8ff952fb 100644 --- a/contrib/holiday/README.md +++ b/contrib/holiday/README.md @@ -1,23 +1,24 @@ calcurse-holiday ================ -calcurse-holiday is a Python script that can create holiday entries for +`calcurse-holiday` is a Python script that can create holiday entries for calcurse for a desired list of years based on a configuration file. Please note that this script is still in an alpha version, so please report bugs (I think -the best way for this will be github, you can mention @ #TODO) +the best way for this will be github, you can mention @atticus-sullivan) Why not simply use reocuring events? ------------------------------------ Since some holiday days are not on the same date each year (e.g.some are connected to the date Eastersunday is placed on), it is not possible to create -native reocuring events in calcurse for these events. +calcurse native reocuring events in calcurse for these events. Usage ----- * Create a `yaml` file which will be the base of which holiday dates will be inserted. * Then run `calcurse-holiday` with the path to the yaml-file you just created - and a list of years you'd like to create the holiday-events for. + and a list of years you'd like to create the holiday-events for. (Duplicates + will be omitted) To check if the dates are correct before inserting them, just run the script with the `-s` option. @@ -27,7 +28,7 @@ of calcurse and it should be easy to remove them again Examples: -------- -``` +```yaml holiday.yaml ---------------------- # specify the description/name of the events that will be created @@ -51,10 +52,14 @@ the years `2021` and `2022` Notes ----- -Currently the purpose of this script is ONLY for events that are weekday offset +* Currently the purpose of this script is ONLY for events that are weekday offset based or on eastern. More base dates are possible, just suggest them ;) -Maybe one day this will support "normal" reoccuring Events too. But since this +* Maybe one day this will support "normal" reoccuring Events too. But since this can much better be done via calcurses native reoccuring events (which is much better, since one doesn't need to remember to recreate the events), this is not the main purpose of this script. + +* The functionallity of avoiding duplicate entries might be added into native + calcurse via an commandline option. If this is complete one can remove the + calls to calcurse to check if an event is already added to calcurse From ece3011403d25da9e2adda5e4fe463ba9d4bc6ad Mon Sep 17 00:00:00 2001 From: luk Date: Wed, 31 Mar 2021 00:56:41 +0200 Subject: [PATCH 3/4] formatting --- contrib/holiday/README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contrib/holiday/README.md b/contrib/holiday/README.md index 8ff952fb..982f4b2b 100644 --- a/contrib/holiday/README.md +++ b/contrib/holiday/README.md @@ -26,7 +26,7 @@ with the `-s` option. **Tipp:** On failiure the new events should simple by at the end of your `app` file of calcurse and it should be easy to remove them again -Examples: +Example: -------- ```yaml holiday.yaml @@ -42,13 +42,12 @@ holiday date # The weekday is specified as 1->Monday 2->Thuesday ... irregular holiday: - Eastersunday: "easter +0 day" - - Eastermonday: "easter +1 day" - - Thanksgiving: 11-01 +4 4 + - Eastermonday: "easter +1 day" + - Thanksgiving: 11-01 +4 4 ``` -`calcurse-holiday holiday.yaml 2021 2022` this will read the holiday -template from the `holiday.yaml` File and create the holiday events for -the years `2021` and `2022` +`calcurse-holiday holiday.yaml 2021 2022` will read the holiday template from the +`holiday.yaml` file and create the holiday events for the years `2021` and `2022` Notes ----- From 800a60700c737d60cdedbebfbf9141d6a9e45528 Mon Sep 17 00:00:00 2001 From: luk Date: Wed, 31 Mar 2021 01:03:14 +0200 Subject: [PATCH 4/4] formatting and fixes in thanksgiving rule --- contrib/holiday/README.md | 2 +- contrib/holiday/calcurse-holiday.py | 4 ++-- contrib/holiday/holiday.yaml | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/holiday/README.md b/contrib/holiday/README.md index 982f4b2b..1cdf36fe 100644 --- a/contrib/holiday/README.md +++ b/contrib/holiday/README.md @@ -43,7 +43,7 @@ holiday date irregular holiday: - Eastersunday: "easter +0 day" - Eastermonday: "easter +1 day" - - Thanksgiving: 11-01 +4 4 + - Thanksgiving: "11-01 +3 4" ``` `calcurse-holiday holiday.yaml 2021 2022` will read the holiday template from the diff --git a/contrib/holiday/calcurse-holiday.py b/contrib/holiday/calcurse-holiday.py index 7ea555cb..35b6a095 100755 --- a/contrib/holiday/calcurse-holiday.py +++ b/contrib/holiday/calcurse-holiday.py @@ -101,8 +101,8 @@ def createForYears(file:str, years:list[int], simu:bool): else: print("Failed to read entry", dateDesc, file=sys.stderr) if simu: - print("This ical would have been imported") - print(ical) + print("\nThis ical (+header) would have been imported") + print("\n".join(ical)) else: toCalcurse(ical) diff --git a/contrib/holiday/holiday.yaml b/contrib/holiday/holiday.yaml index f5aed5a0..7e624a32 100644 --- a/contrib/holiday/holiday.yaml +++ b/contrib/holiday/holiday.yaml @@ -8,3 +8,4 @@ irregular holiday: - Ostersonntag: "easter +0 day" - Ostermontag: "easter +1 day" - Buß- und Bettag: "11-23 -1 3" + - Thanksgiving: "11-01 +3 4"