├── .github
└── workflows
│ └── update_readme.yml
├── .gitignore
├── LICENSE
├── README.md
├── Usage.md
├── backup
├── .gitkeep
├── 11#543. 二叉树的直径.md
├── 2#110. 平衡二叉树.md
├── 3#100. 相同的树.md
└── 6#101. 对称二叉树.md
├── main.py
└── mdcal.py
/.github/workflows/update_readme.yml:
--------------------------------------------------------------------------------
1 | name: update_readme
2 |
3 | on:
4 | workflow_dispatch:
5 | issues:
6 | types: [opened, edited, labeled, unlabeled]
7 | issue_comment:
8 | types: [created, edited]
9 | push:
10 | branches:
11 | - master
12 | paths:
13 | - main.py
14 |
15 | jobs:
16 | update-readme:
17 | runs-on: ubuntu-latest
18 | if: github.repository_owner_id == github.event.issue.user.id || github.event_name == 'push'
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v3
22 |
23 | - name: Set up Python
24 | uses: actions/setup-python@v4
25 | with:
26 | python-version: 3.8
27 |
28 | - name: Install dependencies
29 | run: |
30 | python -m pip install --upgrade pip
31 | pip install requests PyGithub
32 |
33 | - name: Get issue data and update README.md
34 | run: python main.py --issue_number '${{ github.event.issue.number }}'
35 | env:
36 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 |
38 | - name: Push readme
39 | run: |
40 | git config --local user.email "action@github.com"
41 | git config --local user.name "GitHub Action"
42 | git add backup/*.md
43 | git commit -a -m 'update new record' || echo "nothing to commit"
44 | git push || echo "nothing to push"
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Gordon Lee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📝 Algorithm
2 | 算法题练习记录博客(受到[yihong06018/gitblog](https://github.com/yihong0618/gitblog)启发),基于GitHub Action和GitHub Issue功能记录
3 |
4 | 刷题进度:[](https://github.com/doragd/algorithm)
6 |
7 | ## 🎄 How to Use
8 |
9 | ❗ 请注意,不要在本项目下提交刷题记录,正确方法如下:
10 |
11 | 查看[Usage](Usage.md)文档,创建并使用一个个人专属的记录博客。
12 |
13 |
14 | ## 🎯 Calendar
15 |
16 | * 2024/1
17 |
18 | |Mon|Tue|Wed|Thu|Fri|Sat|Sun|
19 | |:-:|:-:|:-:|:-:|:-:|:-:|:-:|
20 | |1🌟|2|3|4|5|6|7|
21 | |8|9|10|11|12|13|14|
22 | |15|16|17|18|19|20|21|
23 | |22|23|24|25|26|27|28|
24 | |29|30|31|1🌟|2|3|4|
25 |
26 |
27 | ## 🍃 Records
28 |
29 | |#|Title|Tag|Date|
30 | |:-:|:-:|:-:|:-:|
31 | |11|[543. 二叉树的直径](https://github.com/Doragd/Algorithm/issues/11)|`二叉树` `递归`|2024-01-01T09:05:12Z|
32 | |6|[101. 对称二叉树](https://github.com/Doragd/Algorithm/issues/6)|`二叉树` `递归`|2023-12-16T02:01:21Z|
33 | |3|[100. 相同的树](https://github.com/Doragd/Algorithm/issues/3)|`二叉树` `递归`|2023-12-10T12:14:09Z|
34 | |2|[110. 平衡二叉树](https://github.com/Doragd/Algorithm/issues/2)|`二叉树` `递归`|2023-12-10T10:56:53Z|
35 |
--------------------------------------------------------------------------------
/Usage.md:
--------------------------------------------------------------------------------
1 | # 🎯 Usage
2 |
3 | 算法题练习记录博客(基于GitHub Action和GitHub Issue实现)。
4 |
5 | 可通过提交issue,自动将刷题记录更新在项目的README.md中,并将issue内容备份在项目backup文件夹下。
6 |
7 | ## Step 1: fork该项目
8 |
9 | 激活GitHub Actions功能
10 |
11 |
12 | 打开fork项目的issues功能
13 | * 点击下图所示按钮
14 |
15 | * 进入到setting页面,在这一页往下翻
16 |
17 | * 找到Features一节,然后勾选Issues
18 |
19 |
20 |
21 | ## Step 2: 提交issue
22 |
23 | 每当你刷完一道算法题,可在你的项目上提交一个issue:
24 | * issue title可以是所刷题目的标题,也可以是其他任意你想记录的笔记标题。
25 | * 同时,你也可以为这个issue打上label,例如二叉树、搜索。
26 | * 将你想记录的对这道题的思考作为issue的description。
27 | [示例issue](https://github.com/Doragd/Algorithm/issues/3)
28 |
29 |
30 |
31 | ## Step 3: 查看效果
32 |
33 | 此时[README.md](README.md) 中
34 |
35 | 1) Calendar部分会基于issue创建日期在日历上标记一个star
36 |
37 | 2) Records部分会新增一行记录, 各列分别记录:
38 |
39 | #: issue号
40 | Title: issue title(点击可跳转至你所创建的issue)
41 | Tag: issue Labels
42 | Date: issue创建时间
43 |
44 | 同时,backup文件夹下会以“issue号+#+的issue title"作为文件名,生成一个对issue的备份文件,备份issue的description。该issue下所有后续的comments也会被记录下来。[示例备份文件](backup/2#110.%20平衡二叉树.md)
45 |
46 | ## Step 4: 编辑和更新
47 |
48 | 当你编辑issue的title或description、修改issue的Labels、issue有新comment或者issue的comment被编辑时, [README.md](README.md)和backup文件夹下issue的备份文件都会进行相应更新
49 |
50 |
--------------------------------------------------------------------------------
/backup/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Doragd/Algorithm/403c55a79a5076ddc2ed75d33eba5e90cfa3f23a/backup/.gitkeep
--------------------------------------------------------------------------------
/backup/11#543. 二叉树的直径.md:
--------------------------------------------------------------------------------
1 | # 543. 二叉树的直径
2 |
3 | [543. 二叉树的直径](https://leetcode.cn/problems/diameter-of-binary-tree/)
4 | - 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。**注意:**两结点之间的路径长度是以它们之间边的数目表示。
5 | ```C++
6 | class Solution {
7 | public:
8 | int res = INT_MIN;
9 | int dfs(TreeNode *root){
10 | if(!root) return 0;
11 | int left_val = dfs(root->left);
12 | int right_val = dfs(root->right);
13 | int cur = left_val + right_val; //以当前结点为最高点的路径长度: 边的数目
14 | res = max(res, cur); //最大值
15 | return max(left_val, right_val) + 1; //单边的最大路径和: 点的数目
16 | }
17 | int diameterOfBinaryTree(TreeNode* root) {
18 | dfs(root);
19 | return res;
20 | }
21 | };
22 | ```
23 |
24 | ---
25 |
26 | * Link: https://github.com/Doragd/Algorithm/issues/11
27 | * Labels: `二叉树`, `递归`
28 | * Creation Date: 2024-01-01T09:05:12Z
29 |
--------------------------------------------------------------------------------
/backup/2#110. 平衡二叉树.md:
--------------------------------------------------------------------------------
1 | # 110. 平衡二叉树
2 |
3 | [110. 平衡二叉树](https://leetcode.cn/problems/balanced-binary-tree/)
4 |
5 | * 给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
6 | * 递归定义法:按照定义法来做
7 | * 问题分解: 求高度, 然后求是否平衡
8 | * 递归定义: 一颗高度平衡二叉树的左子树是平衡的;右子树是平衡的; 再判断这个子结构是否是平衡的
9 |
10 | ```C++
11 | class Solution {
12 | public:
13 | //问题分解: 求高度, 然后求是否平衡
14 | //递归定义: 一颗高度平衡二叉树的左子树是平衡的;右子树是平衡的; 再判断这个子结构是否是平衡的
15 | int getMaxDepth(TreeNode* root){
16 | if(!root) return 0;
17 | return max(
18 | getMaxDepth(root->left), getMaxDepth(root->right)
19 | ) + 1;
20 | }
21 | bool isBalanced(TreeNode* root) {
22 | if(!root) return true;
23 | bool flag = isBalanced(root->left) & isBalanced(root->right);
24 | if(!flag) return false;
25 | return abs(getMaxDepth(root->left)-getMaxDepth(root->right)) <= 1;
26 | }
27 | };
28 | ```
29 |
30 |
31 | ---
32 |
33 | * Link: https://github.com/Doragd/Algorithm/issues/2
34 | * Labels: `二叉树`, `递归`
35 | * Creation Date: 2023-12-10T10:56:53Z
36 |
--------------------------------------------------------------------------------
/backup/3#100. 相同的树.md:
--------------------------------------------------------------------------------
1 | # 100. 相同的树
2 |
3 | * [100. 相同的树](https://leetcode-cn.com/problems/same-tree/)
4 | * 给你两棵二叉树的根节点 `p` 和 `q` ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
5 | * 递归定义法:按照定义法来做
6 | * 定义: 如果两棵树相等则左子树和左子树相同, 右子树和右子树相同, 且根的值相同
7 | * 分类讨论终止: pq其中一个为空, 直接返回false; pq均为空, 则返回true
8 |
9 | ```c++
10 | class Solution {
11 | public:
12 | //递归定义+分类讨论
13 | //定义: 如果两棵树相等则左子树和左子树相同, 右子树和右子树相同, 且根的值相同
14 | //分类讨论终止: pq其中一个为空, 直接返回false; pq均为空, 则返回true
15 | bool isSameTree(TreeNode* p, TreeNode* q) {
16 | if(!q && !p) return true;
17 | if(p && q && p->val == q->val) return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
18 | return false;
19 | }
20 | };
21 |
22 | ```
23 |
24 | ---
25 |
26 | * Link: https://github.com/Doragd/Algorithm/issues/3
27 | * Labels: `二叉树`, `递归`
28 | * Creation Date: 2023-12-10T12:14:09Z
29 |
30 | ---
31 |
32 | comment测试
33 |
34 | *
35 |
--------------------------------------------------------------------------------
/backup/6#101. 对称二叉树.md:
--------------------------------------------------------------------------------
1 | # 101. 对称二叉树
2 |
3 | * [101. 对称二叉树](https://leetcode-cn.com/problems/symmetric-tree/)
4 | - 给你一个二叉树的根节点 `root`, 检查它是否轴对称。
5 | - 递归遍历+问题分解+分类讨论
6 | - 子结构: 两棵小树p和q, 同步遍历两棵树
7 | - 终止条件:p和q其中一方为空 或者 p 和 q不相等 return false
8 | - 否则: 递归判定: (左子树,右子树) (右子树,左子树)
9 | ```C++
10 | class Solution {
11 | public:
12 | //递归遍历+分类讨论:
13 | //子结构: 两棵小树p和q, 同步遍历两棵树
14 | //p和q其中一方为空 或者 p 和 q不相等 return false
15 | //否则: 递归判定: (左子树,右子树) (右子树,左子树)
16 | bool dfs(TreeNode *p, TreeNode *q){
17 | if(!p && !q) return true;
18 | if(p && q && p->val == q->val) return dfs(p->left, q->right) && dfs(p->right, q->left);
19 | return false;
20 | }
21 | bool isSymmetric(TreeNode* root) {
22 | if(!root) return true;
23 | return dfs(root->left, root->right);
24 | }
25 | };
26 | ```
27 |
28 | ---
29 |
30 | * Link: https://github.com/Doragd/Algorithm/issues/6
31 | * Labels: `二叉树`, `递归`
32 | * Creation Date: 2023-12-16T02:01:21Z
33 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import requests
3 | import argparse
4 | from mdcal import update_calendar
5 |
6 |
7 | def get_issue(issue_number=None):
8 | try:
9 | if issue_number is None:
10 | issue_number = os.getenv("ISSUE_NUMBER")
11 | repo_name = os.getenv("GITHUB_REPOSITORY")
12 | gh_token = os.getenv("GH_TOKEN")
13 |
14 | headers = {"Authorization": f"token {gh_token}"}
15 | api_url = f"https://api.github.com/repos/{repo_name}/issues/{issue_number}"
16 |
17 | response = requests.get(api_url, headers=headers)
18 | issue = response.json()
19 |
20 | print(api_url)
21 | print(headers)
22 | print(issue)
23 |
24 | except Exception as e:
25 | print(f"Error in get_issue: {e}")
26 | return None
27 |
28 | return issue
29 |
30 |
31 | def update_records(issue, issue_number=None):
32 | if issue_number is None:
33 | issue_number = os.getenv("ISSUE_NUMBER")
34 |
35 | issue_title = issue["title"]
36 | issue_labels = ["`" + label["name"] + "`" for label in issue["labels"]]
37 | issue_link = issue["html_url"]
38 |
39 | with open("README.md", "r") as file:
40 | lines = file.readlines()
41 |
42 | table_start_index = None
43 | existing_issue_index = None
44 |
45 | for i in range(len(lines)):
46 | if lines[i].strip() == "|#|Title|Tag|Date|":
47 | table_start_index = i + 2
48 | if lines[i].strip().startswith(f"|{issue_number}|") and table_start_index:
49 | existing_issue_index = i
50 | if existing_issue_index:
51 | break
52 |
53 | new_line = f"|{issue_number}|[{issue_title}]({issue_link})|{' '.join(issue_labels)}|{issue['created_at']}|\n"
54 | if existing_issue_index is not None:
55 | lines[existing_issue_index] = new_line
56 | else:
57 | lines.insert(table_start_index, new_line)
58 | with open("README.md", "w") as file:
59 | file.writelines(lines)
60 |
61 | return "Successfully updated Records of README.md"
62 |
63 | def update_star(issue):
64 | created_at_str = issue['created_at']
65 | date_str = created_at_str.split("T")[0]
66 | year, month, day = map(int, date_str.split("-"))
67 | return update_calendar(year, month, day)
68 |
69 | def get_comments(issue_number=None):
70 | try:
71 | if issue_number is None:
72 | issue_number = os.getenv("ISSUE_NUMBER")
73 |
74 | repo_name = os.getenv("GITHUB_REPOSITORY")
75 | gh_token = os.getenv("GH_TOKEN")
76 |
77 | headers = {"Authorization": f"token {gh_token}"}
78 | comments_api_url = f"https://api.github.com/repos/{repo_name}/issues/{issue_number}/comments"
79 |
80 | comments_response = requests.get(comments_api_url, headers=headers)
81 | comments = comments_response.json()
82 |
83 | except Exception as e:
84 | print(f"Error in get_comments: {e}")
85 | return []
86 |
87 | return comments
88 |
89 | def backup_issue_as_md(issue, issue_number):
90 | try:
91 | if issue_number is None:
92 | issue_number = os.getenv("ISSUE_NUMBER")
93 |
94 | issue_title = issue["title"]
95 | issue_body = issue['body']
96 | issue_labels = ["`" + label['name'] + "`" for label in issue['labels']]
97 | issue_link = issue['html_url']
98 | issue_date = issue['created_at']
99 |
100 | comments = get_comments(issue_number)
101 |
102 | if not os.path.exists("backup"):
103 | os.mkdir("backup")
104 |
105 | with open(f"backup/{issue_number}#{issue_title}.md", "w") as file:
106 | file.write("# " + issue_title + "\n\n")
107 | file.write(issue_body + "\n\n")
108 | file.write("---\n\n")
109 | file.write("* Link: " + issue_link + "\n")
110 | file.write("* Labels: " + ', '.join(issue_labels) + "\n")
111 | file.write("* Creation Date: " + issue_date + "\n")
112 | for i, comment in enumerate(comments, start=1):
113 | file.write(f"\n---\n\n")
114 | file.write(comment['body'])
115 | file.write("\n\n*\n")
116 |
117 | except Exception as e:
118 | print(f"Error in backup_issue_as_md: {e}")
119 | return "Backup failed"
120 |
121 | return "Successfully backup records"
122 |
123 | def main(issue_number):
124 | try:
125 | issue = get_issue(issue_number)
126 | if issue is not None:
127 | print(update_records(issue, issue_number))
128 | print(update_star(issue))
129 | print(backup_issue_as_md(issue, issue_number))
130 | else:
131 | print("Issue could not be retrieved.")
132 | except Exception as e:
133 | print(f"Error in main: {e}")
134 |
135 |
136 | if __name__ == "__main__":
137 | try:
138 | parser = argparse.ArgumentParser()
139 | parser.add_argument(
140 | "--issue_number", help="issue_number", default=None, required=False
141 | )
142 | args = parser.parse_args()
143 | main(args.issue_number)
144 | except Exception as e:
145 | print(f"Error: {e}")
146 |
--------------------------------------------------------------------------------
/mdcal.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import calendar
4 | from datetime import datetime
5 | import sys
6 |
7 | def create_calendar(year, month, with_isoweek=False, start_from_Sun=False, lang="en"):
8 | firstweekday = 6 if start_from_Sun else 0
9 |
10 | cal = calendar.Calendar(firstweekday=firstweekday)
11 |
12 | mdstr = ""
13 | dic = get_dict(lang)
14 |
15 | colnames = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
16 | if start_from_Sun:
17 | colnames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
18 | if with_isoweek:
19 | colnames.insert(0, "Week")
20 | colnames = [dic[col] for col in colnames]
21 |
22 | mdstr += '|' + '|'.join(colnames) + '|' + '\n'
23 | mdstr += '|' + '|'.join([':-:' for _ in range(len(colnames))]) + '|' + '\n'
24 |
25 | for days in cal.monthdatescalendar(year, month):
26 | if with_isoweek:
27 | isoweek = days[0].isocalendar()[1]
28 | mdstr += '|' + str(isoweek) + '|' + \
29 | '|'.join([str(d.day) for d in days]) + '|' + '\n'
30 | else:
31 | mdstr += '|' + '|'.join([str(d.day) for d in days]) + '|' + '\n'
32 |
33 | return mdstr
34 |
35 |
36 | def print_calendar(year, month, with_isoweek=False, start_from_Sun=False, lang="en"):
37 | print('{}/{}\n'.format(year, month))
38 | print(create_calendar(year, month, with_isoweek, start_from_Sun, lang))
39 |
40 |
41 | def get_dict(lang='en'):
42 | dic = {}
43 | colnames = ['Week', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
44 | colnames_ja = ['週', '月', '火', '水', '木', '金', '土', '日']
45 | if lang == 'en':
46 | for col in colnames:
47 | dic[col] = col
48 | elif lang == 'ja':
49 | for col, colja in zip(colnames, colnames_ja):
50 | dic[col] = colja
51 | else:
52 | for col in colnames:
53 | dic[col] = col
54 | return dic
55 |
56 | def update_calendar(year, month, day, with_isoweek=False, start_from_Sun=False, lang="en"):
57 | # Check if README.md exists, if not, create one
58 | if not os.path.exists('README.md'):
59 | with open('README.md', 'w') as f:
60 | print("The file README.md does not exist. Creating a new one...")
61 | f.write('## 🎯 Calendar\n\n## Records\n\n')
62 |
63 | # Read the content of README.md
64 | with open('README.md', 'r') as file:
65 | content = file.read()
66 |
67 | # Extract the part between "## 🎯 Calendar" to the start of the next section "## Records".
68 | calendar_section_match = re.search("## 🎯 Calendar(.*)(?=## 🍃 Records)", content, re.DOTALL)
69 |
70 | # If "## 🎯 Calendar" section doesn't exist or there is no calendar data
71 | if calendar_section_match is None:
72 | return "The 'Calendar' section does not exist in README.md or there is no calendar data."
73 |
74 | calendar_section = calendar_section_match.group(1)
75 |
76 | # Check if the current month/year already exists in the calendar
77 | current_month_exists = "* {}/{}\n".format(year, month) in calendar_section
78 | calendar_section_lines = ["## 🎯 Calendar\n"]
79 |
80 | if not current_month_exists:
81 | # Create the calendar for the current month/year and append it
82 | cal = create_calendar(year, month, with_isoweek, start_from_Sun, lang)
83 | calendar_section_lines.append('* {}/{}\n'.format(year, month))
84 | calendar_section_lines += cal.split("\n")
85 | calendar_section_lines.append('\n')
86 | else:
87 | # Append the existing calendar for the current month/year
88 | calendar_section_lines += calendar_section.split("\n")[2:]
89 |
90 | star_flag = True
91 | month_start_flag = False
92 | for i in range(4, len(calendar_section_lines)):
93 | if re.match("^\\|([ ]*.*[ ]*\|)+$", calendar_section_lines[i]):
94 | day_cells = calendar_section_lines[i].split("|")
95 | for j in range(1, len(day_cells) - 1):
96 | digit = re.findall(r'\d+', day_cells[j].strip())
97 | if len(digit) == 0:
98 | continue
99 | if digit[0] == "1":
100 | month_start_flag = True
101 | if digit[0] == str(day) and "🌟" not in day_cells[j] and star_flag and month_start_flag:
102 | day_cells[j] = day_cells[j].strip() + "🌟"
103 | star_flag = False
104 | calendar_section_lines[i] = "|".join(day_cells)
105 |
106 | # Replace 'Calendar' section in README.md with the updated section
107 | new_content = re.sub(r"## 🎯 Calendar(.*)(?=## 🍃 Records)", "\n".join(calendar_section_lines), content, flags=re.DOTALL)
108 |
109 | with open('README.md', 'w') as file:
110 | file.write(new_content)
111 |
112 | return "Successfully updated Calendar of README.md"
113 |
114 | if __name__ == "__main__":
115 | argv = sys.argv
116 | if len(argv) == 3:
117 | year, month = [int(a) for a in argv[1:3]]
118 | print(update_calendar(year, month, datetime.now().day))
119 | elif len(argv) == 4:
120 | year, month, day = [int(a) for a in argv[1:4]]
121 | print(update_calendar(year, month, day))
122 | else:
123 | print('Usage: python mdcal.py [year] [month] [day]')
--------------------------------------------------------------------------------