├── .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 | 刷题进度:[![LeetCode](https://img.shields.io/github/issues/doragd/algorithm?style=flat&label=%F0%9F%8C%B8%20LeetCode%20Record&labelColor=%20%236DB9EF&color=%23FF90BC&link=https%3A%2F%2Fgithub.com%2Fdoragd%2Falgorithm 5 | )](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 | image 11 | 12 | 打开fork项目的issues功能 13 | * 点击下图所示按钮 14 | image 15 | * 进入到setting页面,在这一页往下翻 16 | image 17 | * 找到Features一节,然后勾选Issues 18 | image 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 | image 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]') --------------------------------------------------------------------------------