├── .github ├── CODEOWNERS └── workflows │ ├── directory-ignore-files.yml │ ├── directory.yml │ ├── formatter-ignore-files.yml │ └── formatter.yml ├── DIRECTORY.md ├── LICENSE ├── README.md ├── build_directory_md.py ├── directory_md ├── action.yml └── build_directory_md.py ├── filename_formatter.sh ├── formatter ├── action.yml └── filename_formatter.sh ├── license_copyright └── license.yml └── test ├── README.md └── SOME FILE.cpp /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Panquesito7 @tjgurwara99 2 | -------------------------------------------------------------------------------- /.github/workflows/directory-ignore-files.yml: -------------------------------------------------------------------------------- 1 | name: DIRECTORY.md workflow (ignore directories) 2 | on: [workflow_dispatch] 3 | jobs: 4 | directory_builder: 5 | runs-on: ubuntu-latest 6 | name: Verify the DIRECTORY.md workflow works 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: ./directory_md 10 | with: 11 | language: scripts 12 | working-directory: . 13 | filetypes: .cpp,.hpp 14 | ignored-directories: ./test 15 | branch-name: directory-update-ignore 16 | -------------------------------------------------------------------------------- /.github/workflows/directory.yml: -------------------------------------------------------------------------------- 1 | name: DIRECTORY.md workflow 2 | on: [workflow_dispatch] 3 | jobs: 4 | directory_builder: 5 | runs-on: ubuntu-latest 6 | name: Verify the DIRECTORY.md workflow works 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: ./directory_md 10 | with: 11 | language: scripts 12 | working-directory: ./test 13 | filetypes: .cpp,.hpp 14 | branch-name: directory-update 15 | -------------------------------------------------------------------------------- /.github/workflows/formatter-ignore-files.yml: -------------------------------------------------------------------------------- 1 | name: Test with ignore directory argument 2 | on: [workflow_dispatch] 3 | jobs: 4 | formatter: 5 | runs-on: ubuntu-latest 6 | name: Verify that the filename formatter works (ignoring directory is included) 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: ./formatter 10 | with: 11 | filetypes: .cpp,.hpp 12 | working-directory: . 13 | ignore-files: ./test 14 | -------------------------------------------------------------------------------- /.github/workflows/formatter.yml: -------------------------------------------------------------------------------- 1 | name: Test without ignore directory argument 2 | on: [workflow_dispatch] 3 | jobs: 4 | formatter: 5 | runs-on: ubuntu-latest 6 | name: Verify that the filename formatter works 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: ./formatter 10 | with: 11 | filetypes: .cpp,.hpp 12 | working-directory: ./test 13 | -------------------------------------------------------------------------------- /DIRECTORY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenniferlb63/Python-scripts/b480a8f559062904d0f4aa865c1b4a1babce329e/DIRECTORY.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 The Algorithms 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 | # The Algorithms Scripts 2 | 3 | Internal scripts used across all The Algorithms repositories 4 | 5 | ## build_directory_md.py 6 | This script should be run by a workflow on every push and pr to update the `DIRECTORY.md` file. It takes the following arguments: 7 | ``` 8 | [0] - Language 9 | [1] - Base path 10 | [2] - Allowed filenames 11 | [3] - Files or folders to ignore (optional) 12 | [4] - Folders to ignore, but include children (optional) 13 | ``` 14 | For example, the command for the C++ repo would be: 15 | ```bash 16 | python3 build_directory_md.py C-Plus-Plus . .cpp,.hpp,.h > DIRECTORY.md 17 | ``` 18 | Or more advanced, for the MATLAB / Octave repo: 19 | ```bash 20 | python3 build_directory_md.py MATLAB-Octave . .m - algorithms > DIRECTORY.md 21 | -------------------------------------------------------------------------------- /build_directory_md.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | from typing import Iterator 6 | 7 | if ".py" in sys.argv[0]: 8 | sys.argv.pop(0) 9 | 10 | 11 | if len(sys.argv) not in (3, 4, 5): 12 | print( 13 | "Arguments:\n" 14 | "[0] - Language\n" 15 | "[1] - Base path\n" 16 | "[2] - Allowed filenames\n" 17 | "[3] - Files or folders to ignore (optional)\n" 18 | "[4] - Folders to ignore, but include children (optional)" 19 | ) 20 | sys.exit() 21 | 22 | ignore = sys.argv[3].split(",") if len(sys.argv) >= 4 else [] 23 | skip = sys.argv[4].split(",") if len(sys.argv) == 5 else [] 24 | 25 | URL_BASE = f"https://github.com/TheAlgorithms/{sys.argv[0]}/blob/HEAD" 26 | 27 | 28 | def good_file_paths(top_dir: str = ".") -> Iterator[str]: 29 | for dir_path, dir_names, filenames in os.walk(top_dir): 30 | dir_names[:] = [d for d in dir_names if d != "scripts" and d[0] not in "._"] 31 | for filename in filenames: 32 | if filename == "__init__.py" or any( 33 | e.lower() in os.path.join(dir_path, filename).lower() for e in ignore 34 | ): 35 | continue 36 | if os.path.splitext(filename)[1] in sys.argv[2].split(","): 37 | path = os.path.join(dir_path, filename).lstrip(".").lstrip("/") 38 | for e in skip: 39 | path = path.replace(f"{e}/", "").replace(f"{e}\\", "") 40 | yield path 41 | 42 | 43 | def md_prefix(i): 44 | return f"{i * ' '}*" if i else "\n##" 45 | 46 | 47 | def print_path(old_path: str, new_path: str) -> str: 48 | old_parts = old_path.split(os.sep) 49 | for i, new_part in enumerate(new_path.split(os.sep)): 50 | if i + 1 > len(old_parts) or old_parts[i] != new_part: 51 | if new_part: 52 | print(f"{md_prefix(i)} {new_part.replace('_', ' ').title()}") 53 | return new_path 54 | 55 | 56 | def print_directory_md(top_dir: str = ".") -> None: 57 | old_path = "" 58 | for filepath in sorted(good_file_paths(top_dir)): 59 | filepath, filename = os.path.split(filepath) 60 | if filepath != old_path: 61 | old_path = print_path(old_path, filepath) 62 | indent = (filepath.count(os.sep) + 1) if filepath else 0 63 | url = "/".join((URL_BASE, filepath, filename)).replace(" ", "%20") 64 | filename = os.path.splitext(filename.replace("_", " ").title())[0] 65 | print(f"{md_prefix(indent)} [{filename}]({url})") 66 | 67 | 68 | if __name__ == "__main__": 69 | print_directory_md(sys.argv[1]) 70 | -------------------------------------------------------------------------------- /directory_md/action.yml: -------------------------------------------------------------------------------- 1 | name: "Build directory file" 2 | description: "Builds a DIRECTORY.md file with all the algorithms in one repository." 3 | author: "TheAlgorithms" 4 | inputs: 5 | language: 6 | description: The language used in your repository 7 | required: true 8 | working-directory: 9 | description: Working/base directory of the script 10 | required: true 11 | default: . 12 | filetypes: 13 | description: Allowed filenames to check in (comma separated values in a string). Can have unlimited filetypes. E.g. `.cpp,.hpp,.h` 14 | required: true 15 | ignored-directories: 16 | description: Files or folders to ignore, separated by commas. 17 | required: false 18 | ignore-folders-children: 19 | description: Folders to ignore, but include children. 20 | required: false 21 | branch-name: 22 | description: The branch that will be used to push changes. 23 | required: false 24 | default: directory-update 25 | runs: 26 | using: composite 27 | steps: 28 | - run: echo "${{ github.action_path }}" >> $GITHUB_PATH 29 | shell: bash 30 | - name: Set up Python 31 | uses: actions/setup-python@v4 32 | with: 33 | python-version: '3.x' 34 | - name: Setup Git configurations 35 | shell: bash 36 | run: | 37 | git config --global user.name github-actions[bot] 38 | git config --global user.email 'github-actions@users.noreply.github.com' 39 | - name: Running the directory builder 40 | shell: bash 41 | run: | 42 | # If branch exists, change to that branch to prevent multiple committing/PR creation. 43 | git checkout ${{ inputs.branch-name }} || true 44 | 45 | python ./build_directory_md.py ${{ inputs.language }} ${{ inputs.working-directory }} ${{ inputs.filetypes }} ${{ inputs.ignored-directories }} ${{ inputs.ignore-folders-children }} > DIRECTORY.md 46 | - name: Creating a branch 47 | shell: bash 48 | run: | 49 | git branch ${{ inputs.branch-name }} || true 50 | git checkout ${{ inputs.branch-name }} || true 51 | 52 | - name: Committing, pushing, and creating a PR 53 | shell: bash 54 | run: | 55 | if [[ `git status --porcelain` ]]; 56 | then 57 | 58 | git add DIRECTORY.md 59 | 60 | git commit -m "docs: update DIRECTORY.md" || true 61 | git push origin ${{ inputs.branch-name }}:${{ inputs.branch-name }} --force-with-lease 62 | 63 | gh pr create --base ${GITHUB_REF##*/} --head ${{ inputs.branch-name }} --title 'docs: updating `DIRECTORY.md`' --body 'Updated the `DIRECTORY.md` file (see the diff. for changes).' || true 64 | # Using `true` will make sure no errors are displayed even if there's a PR created. 65 | fi 66 | env: 67 | GH_TOKEN: ${{ github.token }} 68 | -------------------------------------------------------------------------------- /directory_md/build_directory_md.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | from typing import Iterator 6 | 7 | if ".py" in sys.argv[0]: 8 | sys.argv.pop(0) 9 | 10 | 11 | if len(sys.argv) not in (3, 4, 5): 12 | print( 13 | "Arguments:\n" 14 | "[0] - Language\n" 15 | "[1] - Base path\n" 16 | "[2] - Allowed filenames\n" 17 | "[3] - Files or folders to ignore (optional)\n" 18 | "[4] - Folders to ignore, but include children (optional)" 19 | ) 20 | sys.exit() 21 | 22 | ignore = [] 23 | skip = [] 24 | if len(sys.argv) == 4: 25 | ignore = sys.argv[3].split(",") 26 | if len(sys.argv) == 5: 27 | skip = sys.argv[4].split(",") 28 | 29 | URL_BASE = f"https://github.com/TheAlgorithms/{sys.argv[0]}/blob/HEAD" 30 | 31 | 32 | def good_file_paths(top_dir: str = ".") -> Iterator[str]: 33 | for dir_path, dir_names, filenames in os.walk(top_dir): 34 | dir_names[:] = [d for d in dir_names if d != "scripts" and d[0] not in "._"] 35 | for filename in filenames: 36 | if filename == "__init__.py": 37 | continue 38 | if any( 39 | e.lower() in os.path.join(dir_path, filename).lower() for e in ignore 40 | ): 41 | continue 42 | if os.path.splitext(filename)[1] in sys.argv[2].split(","): 43 | path = os.path.join(dir_path, filename).lstrip(".").lstrip("/") 44 | for e in skip: 45 | path = path.replace(e + "/", "") 46 | path = path.replace(e + "\\", "") 47 | yield path 48 | 49 | 50 | def md_prefix(i): 51 | return f"{i * ' '}*" if i else "\n##" 52 | 53 | 54 | def print_path(old_path: str, new_path: str) -> str: 55 | old_parts = old_path.split(os.sep) 56 | for i, new_part in enumerate(new_path.split(os.sep)): 57 | if i + 1 > len(old_parts) or old_parts[i] != new_part: 58 | if new_part: 59 | print(f"{md_prefix(i)} {new_part.replace('_', ' ').title()}") 60 | return new_path 61 | 62 | 63 | def print_directory_md(top_dir: str = ".") -> None: 64 | old_path = "" 65 | for filepath in sorted(good_file_paths(top_dir)): 66 | filepath, filename = os.path.split(filepath) 67 | if filepath != old_path: 68 | old_path = print_path(old_path, filepath) 69 | indent = (filepath.count(os.sep) + 1) if filepath else 0 70 | url = "/".join((URL_BASE, filepath, filename)).replace(" ", "%20") 71 | filename = os.path.splitext(filename.replace("_", " ").title())[0] 72 | print(f"{md_prefix(indent)} [{filename}]({url})") 73 | 74 | 75 | if __name__ == "__main__": 76 | print_directory_md(sys.argv[1]) 77 | -------------------------------------------------------------------------------- /filename_formatter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -e "Arguments: 4 | [0] - Used by Bash (script filename) 5 | [1] - Base directory 6 | [2] - Filename type (maximum two values) 7 | [3] - Ignored files or folders (optional; use "\""./"\"") 8 | " 9 | 10 | # Separate $2 value (filename types) if it has a comma 11 | if [[ "$2" == *","* ]]; 12 | then 13 | string="$2" 14 | 15 | str_value=${string#*,} 16 | str_value2=${string%%,*} 17 | else 18 | str_value="$2" 19 | str_value2="$2" 20 | fi 21 | 22 | # Do not run script if there are no given arguments. 23 | if [[ "$1" == "" ]] || [[ "$2" == "" ]]; 24 | then 25 | echo "No arguments given. Please specify minimum two arguments." 26 | exit 1 27 | fi 28 | 29 | echo "Changed files:" 30 | 31 | IFS=$'\n'; set -f 32 | for fname in $(find $1 -type f -name "*$str_value2" -or -name "*$str_value") 33 | do 34 | ignored_files="$(echo "$3" | tr "," "\n")" 35 | 36 | str="${fname}" 37 | value=${str%/*} # If the base directory is `.`, check in all directories for the ignored filenames 38 | 39 | for files in $ignored_files 40 | do 41 | if [ "${fname}" == "$value/$files" ] || [ "$value" == "$files" ]; 42 | then 43 | continue 2 44 | fi 45 | done 46 | 47 | #echo ${fname} 48 | new_fname=$(echo "${fname}" | tr ' ' '_') 49 | #echo " ${new_fname}" 50 | new_fname=$(echo "${new_fname}" | tr '[:upper:]' '[:lower:]') 51 | #echo " ${new_fname}" 52 | new_fname=$(echo "${new_fname}" | tr '-' '_') 53 | #echo " ${new_fname}" 54 | if [ "${fname}" != "${new_fname}" ] 55 | then 56 | echo " ${fname} --> ${new_fname}" 57 | git "mv" "${fname}" "${new_fname}" # Requires you to be in version control 58 | fi 59 | done 60 | unset IFS; set +f 61 | -------------------------------------------------------------------------------- /formatter/action.yml: -------------------------------------------------------------------------------- 1 | name: "Filename Formatter" 2 | description: "Format filenames into the acceptable format by TheAlgorithms opganization" 3 | author: "TheAlgorithms" 4 | inputs: 5 | filetypes: 6 | description: Filter files by specified file types (comma separated values in a string.) Maximum two values. E.g. `.cpp,.hpp` 7 | required: true 8 | working-directory: 9 | description: Working/base directory of the formatter 10 | required: false 11 | default: . 12 | ignore-files: 13 | description: Files/folders to ignored 14 | required: false 15 | runs: 16 | using: composite 17 | steps: 18 | - run: echo "${{ github.action_path }}" >> $GITHUB_PATH 19 | shell: bash 20 | - name: Setup Git configurations 21 | shell: bash 22 | run: | 23 | git config --global user.name github-actions[bot] 24 | git config --global user.email 'github-actions@users.noreply.github.com' 25 | - name: Running the formatter 26 | shell: bash 27 | run: | 28 | filename_formatter.sh ${{ inputs.working-directory }} ${{ inputs.filetypes }} ${{ inputs.ignore-files }} 29 | - name: Committing changes 30 | shell: bash 31 | run: | 32 | git add ${{ inputs.working-directory }} || true 33 | git commit -m "chore: formatting filenames" || true 34 | 35 | git push origin HEAD:$GITHUB_REF || true 36 | -------------------------------------------------------------------------------- /formatter/filename_formatter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -e "Arguments: 4 | [0] - Used by Bash (script filename) 5 | [1] - Base directory 6 | [2] - Filename type (maximum two values) 7 | [3] - Ignored files or folders (optional; use "\""./"\"") 8 | " 9 | 10 | # Separate $2 value (filename types) if it has a comma 11 | if [[ "$2" == *","* ]]; 12 | then 13 | string="$2" 14 | 15 | str_value=${string#*,} 16 | str_value2=${string%%,*} 17 | else 18 | str_value="$2" 19 | str_value2="$2" 20 | fi 21 | 22 | # Do not run script if there are no given arguments. 23 | if [[ "$1" == "" ]] || [[ "$2" == "" ]]; 24 | then 25 | echo "No arguments given. Please specify minimum two arguments." 26 | exit 1 27 | fi 28 | 29 | echo "Changed files:" 30 | 31 | IFS=$'\n'; set -f 32 | for fname in $(find $1 -type f -name "*$str_value2" -or -name "*$str_value") 33 | do 34 | ignored_files="$(echo "$3" | tr "," "\n")" 35 | 36 | str="${fname}" 37 | value=${str%/*} # If the base directory is `.`, check in all directories for the ignored filenames 38 | 39 | for files in $ignored_files 40 | do 41 | if [ "${fname}" == "$value/$files" ] || [ "$value" == "$files" ]; 42 | then 43 | continue 2 44 | fi 45 | done 46 | 47 | #echo ${fname} 48 | new_fname=$(echo "${fname}" | tr ' ' '_') 49 | #echo " ${new_fname}" 50 | new_fname=$(echo "${new_fname}" | tr '[:upper:]' '[:lower:]') 51 | #echo " ${new_fname}" 52 | new_fname=$(echo "${new_fname}" | tr '-' '_') 53 | #echo " ${new_fname}" 54 | if [ "${fname}" != "${new_fname}" ] 55 | then 56 | echo " ${fname} --> ${new_fname}" 57 | git "mv" "${fname}" "${new_fname}" # Requires you to be in version control 58 | fi 59 | done 60 | unset IFS; set +f 61 | -------------------------------------------------------------------------------- /license_copyright/license.yml: -------------------------------------------------------------------------------- 1 | name: 'Update copyright license' 2 | description: Updates copyright license year automatically 3 | author: "TheAlgorithms" 4 | inputs: 5 | filename: 6 | description: "License filename" 7 | required: true 8 | default: LICENSE 9 | initial_year: 10 | description: "Year of the repository's creation" 11 | required: true 12 | default: 2016 13 | runs: 14 | using: composite 15 | steps: 16 | - name: Update the License 17 | run: | 18 | (echo "Copyright (C) ${{ inputs.initial_year }}-$(date +"%Y") TheAlgorithms and contributors"; tail -n +2 ${{ inputs.filename }}) > License.tmp 19 | mv License.tmp ${{ inputs.filename }} 20 | - name: Setup Git configurations 21 | shell: bash 22 | run: | 23 | git config --global user.name github-actions[bot] 24 | git config --global user.email 'github-actions@users.noreply.github.com' 25 | - name: Commit to a new branch and push changes 26 | shell: bash 27 | run: | 28 | git checkout -b license-update-${{ github.sha }} 29 | git commit -m "docs: updating copyright license year" 30 | git push origin license-update-${{ github.sha }}:license-update-${{ github.sha }} 31 | - name: Creating a PR 32 | shell: bash 33 | run: | 34 | if [[ $(git log --branches --not --remotes) ]]; then 35 | gh pr create --base ${GITHUB_REF##*/} --head license-update-${{ github.sha }} --title 'docs: updating `License` copyright' --body 'Updated License copyright (see the diff. for changes).' 36 | fi 37 | env: 38 | GH_TOKEN: ${{ github.token }} 39 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | This folder is used only for testing purposes.\ 4 | This should not be touched or modified in any way. 5 | -------------------------------------------------------------------------------- /test/SOME FILE.cpp: -------------------------------------------------------------------------------- 1 | // Test file to make sure the filename formatter works as excepted 2 | --------------------------------------------------------------------------------