diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index d8f61f0..8fce86b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -32,6 +32,14 @@ jobs: shell: bash run: python crawler.py + - name: Run global scripts + shell: bash + run: | + for script in global/*/main.py; do + cd $(dirname $script) + python $(basename $script) + done + - name: Setup Haskell and Cabal uses: haskell-actions/setup@v2 with: @@ -44,10 +52,18 @@ jobs: - name: Generate ICS files run: cabal run + - name: Generate global data + shell: bash + run: | + for script in global/*/deploy.sh; do + cd $(dirname $script) + bash $(basename $script) 2>/dev/null + done + - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v5 with: - file_pattern: README.md docs/* data/* + file_pattern: README.md data/* global/*/data/* docs/* docs/global/*/* commit_message: update calendar data automatically commit_user_name: Muhan Li commit_user_email: 77625954+muhav@users.noreply.github.com diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 686fb4c..f38b326 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,14 @@ jobs: shell: bash run: python crawler.py + - name: Run global scripts + shell: bash + run: | + for script in global/*/main.py; do + cd $(dirname $script) + python $(basename $script) + done + parser: name: Parser Dry Run runs-on: ubuntu-latest diff --git a/global/ca_on/data/2022.txt b/global/ca_on/data/2022.txt new file mode 100644 index 0000000..0d92de0 --- /dev/null +++ b/global/ca_on/data/2022.txt @@ -0,0 +1,12 @@ +// Ontario Public Holidays in 2022 +// I hope ChatGPT gets the dates correct + +元旦_New_Year's_Day;2022.1.3;2022.1.1-2022.1.3 +家庭日_Family_Day;2022.2.21;2022.2.19-2022.2.21 +耶稣受难日_Good_Friday;2022.4.15;2022.4.15-2022.4.17 +维多利亚日_Victoria_Day;2022.5.23;2022.5.21-2022.5.23 +国庆节_Canada_Day;2022.7.1;2022.7.1-2022.7.3 +劳动节_Labour_Day;2022.9.5;2022.9.3-2022.9.5 +感恩节_Thanksgiving;2022.10.10;2022.10.8-2022.10.10 +圣诞节_Christmas_Day;2022.12.26;2022.12.24-2022.12.27 +节礼日_Boxing_Day;2022.12.27; diff --git a/global/ca_on/data/2023.txt b/global/ca_on/data/2023.txt new file mode 100644 index 0000000..b9979d6 --- /dev/null +++ b/global/ca_on/data/2023.txt @@ -0,0 +1,12 @@ +// Ontario Public Holidays in 2023 +// I hope ChatGPT gets the dates correct + +元旦_New_Year's_Day;2023.1.2;2022.12.31-2023.1.2 +家庭日_Family_Day;2023.2.20;2023.2.18-2023.2.20 +耶稣受难日_Good_Friday;2023.4.7;2023.4.7-2023.4.9 +维多利亚日_Victoria_Day;2023.5.22;2023.5.20-2023.5.22 +国庆节_Canada_Day;2023.7.3;2023.7.1-2023.7.3 +劳动节_Labour_Day;2023.9.4;2023.9.2-2023.9.4 +感恩节_Thanksgiving;2023.10.9;2023.10.7-2023.10.9 +圣诞节_Christmas_Day;2023.12.25;2023.12.23-2023.12.26 +节礼日_Boxing_Day;2023.12.26; diff --git a/global/ca_on/data/2024.txt b/global/ca_on/data/2024.txt new file mode 100644 index 0000000..988e9af --- /dev/null +++ b/global/ca_on/data/2024.txt @@ -0,0 +1,12 @@ +// Ontario Public Holidays in 2024 +// I hope ChatGPT gets the dates correct + +元旦_New_Year's_Day;2024.1.1;2023.12.30-2024.1.1 +家庭日_Family_Day;2024.2.19;2024.2.17-2024.2.19 +耶稣受难日_Good_Friday;2024.3.29;2024.3.29-2024.3.31 +维多利亚日_Victoria_Day;2024.5.20;2024.5.18-2024.5.20 +国庆节_Canada_Day;2024.7.1;2024.6.29-2024.7.1 +劳动节_Labour_Day;2024.9.2;2024.8.31-2024.9.2 +感恩节_Thanksgiving;2024.10.14;2024.10.12-2024.10.14 +圣诞节_Christmas_Day;2024.12.25;2024.12.25-2024.12.26 +节礼日_Boxing_Day;2024.12.26; diff --git a/global/ca_on/deploy.sh b/global/ca_on/deploy.sh new file mode 100644 index 0000000..48b7ceb --- /dev/null +++ b/global/ca_on/deploy.sh @@ -0,0 +1,23 @@ +# To make it simple, copy the parser script +# to the current directory, and I only need +# to modify the titles of the output files. +mkdir docs +mkdir -p ../../docs/global/ca_on + +cp -r ../../parser.cabal . +cp -r ../../parser . + +sed -i \ + -e 's/Chinese Holidays/Ontario Statutory Holidays/' \ + -e 's/titleStatus status/"加拿大安大略省公共假日"/' \ + -e 's/name ++ show status/unwords name_en/' \ + -e 's/show status/name_cn ++ " "/' \ + -e 's/0xa95511fe/0xca4ada04;name_cn:name_en=splitOn "_" name/' \ + -e 's/import/import Data.List.Split (splitOn);import/' \ + ./parser/Main/Output.hs + +cabal build +cabal run + +mv docs/rest.ics ../../docs/global/ca_on/main.ics +mv docs/work.ics ../../docs/global/ca_on/rest.ics diff --git a/global/ca_on/main.py b/global/ca_on/main.py new file mode 100644 index 0000000..478c021 --- /dev/null +++ b/global/ca_on/main.py @@ -0,0 +1,166 @@ +""" +Generate the public holidays in Ontario for the next year +Thanks to ChatGPT and Copilot for the code +""" + +import datetime +from dateutil.easter import easter + + +class Holiday: + """节假日""" + + def __init__(self, name_cn, name_en, date): + self.name_cn = name_cn + self.name_en = name_en + self.date = date + self.long_weekend_dates = None + + def __str__(self): + """格式化 debug 输出""" + return f"{self.name_cn}: {self.format_date(self.date)}" + + @staticmethod + def format_date(date): + """格式化日期为 data.txt 的格式""" + return date.strftime('%Y.%-m.%-d') + + +def get_holidays(year: int) -> list[Holiday]: + """获取给定年份的节假日""" + holidays = [ + # 固定假日 + # 元旦 (1月1日) + Holiday("元旦", "New Year's Day", datetime.date(year, 1, 1)), + # 劳动节 (5月1日) + Holiday("国庆节", "Canada Day", datetime.date(year, 7, 1)), + # 圣诞节 (12月25日) + Holiday("圣诞节", "Christmas Day", datetime.date(year, 12, 25)), + # 节礼日 (12月26日) + Holiday("节礼日", "Boxing Day", datetime.date(year, 12, 26)), + + # 变动假日 + # 耶稣受难日(复活节前2天) + Holiday("耶稣受难日", "Good Friday", easter(year) - datetime.timedelta(days=2)), + # 维多利亚日(5月最后一个周一) + Holiday("维多利亚日", "Victoria Day", get_last_monday(datetime.date(year, 5, 25))), + # 劳动节(9月的第一个周一) + Holiday("劳动节", "Labour Day", get_first_monday(datetime.date(year, 9, 1))), + # 感恩节(10月的第二个周一) + Holiday("感恩节", "Thanksgiving", get_second_monday(datetime.date(year, 10, 1))), + # 家庭日(2月的第三个周一) + Holiday("家庭日", "Family Day", get_third_monday(datetime.date(year, 2, 1))), + ] + + # 处理假日如果落在周末的情况 + adjust_for_weekends(holidays) + connect_long_weekends(holidays) + + return holidays + + +def get_first_monday(date): + """获取给定日期所在月的第一个星期一""" + while date.weekday() != 0: + date += datetime.timedelta(days=1) + return date + + +def get_second_monday(date): + """获取给定日期所在月的第二个星期一""" + first_monday = get_first_monday(date) + return first_monday + datetime.timedelta(days=7) + + +def get_third_monday(date): + """获取给定日期所在月的第三个星期一""" + first_monday = get_first_monday(date) + return first_monday + datetime.timedelta(days=14) + + +def get_last_monday(date): + """获取给定日期所在月的最后一个星期一""" + while date.weekday() != 0: + date -= datetime.timedelta(days=1) + return date + + +def adjust_for_weekends(holidays: list[Holiday]): + """如果假日落在周末,将假日顺延到下一个工作日,特殊处理圣诞节和节礼日""" + boxing_day_delta = 0 + for holiday in holidays: + # 特殊处理圣诞节和节礼日 + if holiday.name_cn == "圣诞节" and holiday.date.weekday() == 5: + # 圣诞节在周六, 节礼日在周日 + holiday.date += datetime.timedelta(days=2) + boxing_day_delta = 2 + elif holiday.name_cn == "圣诞节" and holiday.date.weekday() == 6: + # 圣诞节在周日, 节礼日在周一 + holiday.date += datetime.timedelta(days=1) + boxing_day_delta = 1 + elif holiday.name_cn == "节礼日" and holiday.date.weekday() == 5: + # 节礼日在周六 + holiday.date += datetime.timedelta(days=2) + elif holiday.name_cn == "节礼日": + holiday.date += datetime.timedelta(days=boxing_day_delta) + else: + # 其他假期处理 + if holiday.date.weekday() == 5: # 如果是假日落在周六 + holiday.date += datetime.timedelta(days=2) + elif holiday.date.weekday() == 6: # 如果是假日落在周日 + holiday.date += datetime.timedelta(days=1) + + +def connect_long_weekends(holidays: list[Holiday]): + """将连续的假期连接起来""" + boxing_day = next(h.date for h in holidays if h.name_cn == "节礼日") + + for holiday in holidays: + dates = [holiday.date] + if holiday.name_cn == "节礼日": + continue + if holiday.name_cn == "圣诞节": + dates.append(boxing_day) + + long_weekend_start = holiday.date + while is_weekend(prev_day := long_weekend_start - datetime.timedelta(days=1), dates): + long_weekend_start = prev_day + + long_weekend_end = holiday.date + while is_weekend(next_day := long_weekend_end + datetime.timedelta(days=1), dates): + long_weekend_end = next_day + + holiday.long_weekend_dates = ( + Holiday.format_date(long_weekend_start), + Holiday.format_date(long_weekend_end), + ) + + +def is_weekend(date, extra_holidays=None): + """判断给定日期是否是周末或者公共假日""" + return date.weekday() >= 5 or (extra_holidays and date in extra_holidays) + + +def main(year=None): + """生成下一年的节假日数据""" + next_year = year if year else datetime.datetime.now().year + 1 + print(f"Generating Ontario holidays for {next_year}") + + holidays = get_holidays(next_year) + holidays.sort(key=lambda x: x.date) + + data = (f"// Ontario Public Holidays in {next_year}\n" + f"// I hope ChatGPT gets the dates correct\n\n") + + for holiday in holidays: + print(holiday) + title = f"{holiday.name_cn} {holiday.name_en}".replace(' ', '_') + data += (f"{title};{Holiday.format_date(holiday.date)};" + f"{f'{d[0]}-{d[1]}' if (d := holiday.long_weekend_dates) else ''}\n") # pylint: disable=unsubscriptable-object + + with open(f"./data/{next_year}.txt", "w", encoding='utf-8') as f: + f.write(data) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt index 1d769b7..812c9d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -requests~=2.28.1 +requests~=2.32.3 +python-dateutil~=2.9.0