diff --git a/CHANGELOG.md b/CHANGELOG.md index 02d8d86..0b2f7ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,71 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ### Changed +- point readme towards govcookiecutter-lite, added section on original govcookiecutter ### Removed - -## [2.1.0] - 2nd June 2025 - -### Added - -- Example test and module -- Example pipeline -- Version to config -- Dev dependencies to config -- New requirements: `bandit` and `jinja2-time` -- `bandit` to user pre-commit hooks -- Including common Excel file times in the default .gitignore -- `bandit` to dev pre-commit hooks -- R install guidance to template README -- Setup question for locked down environments, uses local installed packages for commit hooks - -### Fixed - -- Broken links - -### Changed - -- Updated instructions on running the tests -- Updated imports -- Exclude certain files from pre-commit hooks -- Updated README to use `python -m` -- Minor contributing guidance changes -- Updated Python versions to 3.9-3.12 -- Updated Python versions in workflows to 3.9-3.12 -- Updated dev pre-commit hook versions via `pre-commit autoupdate` -- Template code of conduct updated -- Template contributing guidance updated -- Updated README -- Updated documentation website for `govcookiecutter` and created repo structures - -### Removed - -- Example data folder -- Removed the Email Address prompts from the installation. - -## [2.0.0] - 6th March 2023 - -### Added - -- Workflow to check external links on pull request - -### Fixed - -- Broken links - -### Changed - -- Updated folder structure -- Relative links have been replaced with complete paths -- Workflow actions now use later versions -- Updated CRAN link being installed from in startup.R -- Updates to the Contributing Guidance - -### Removed - -- Workflow support for Python 3.6 - - -[unreleased]: https://github.com/best-practice-and-impact/govcookiecutter -[2.1.0]: https://github.com/best-practice-and-impact/govcookiecutter/tree/2.1.0 -[2.0.0]: https://github.com/best-practice-and-impact/govcookiecutter/tree/2.0.0 +- Project version from setup, default is 0.0.1 +- locked down setup question, not needed as commit hooks have been removed +- removed all R files, not included in lite version +- removed overview setup question, a placeholder has been inserted into readme and project toml +- removed repo name question, instead derived from project name question +- hosting platform setup question, PR templates not included in lite version diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a499cba..7526038 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,7 +78,7 @@ pytest Code coverage of Python scripts is measured using the [`coverage` Python package][coverage]; its configuration can be found in `pyproject.toml`. Note coverage only extends to Python scripts in the `hooks`, and -`{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}` folders. +`{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folders. To run code coverage, and view it as an HTML report, enter the following command in your terminal: @@ -114,13 +114,6 @@ make docs This should create an HTML version of your documentation accessible from `docs/_build/index.html`. -## Organisational frameworks - -Organisational frameworks are stored in the -`.govcookiecutter/organisational_frameworks` folder. [If you would like to add your own -organisation's framework, follow the instructions][docs-govcookiecutter-frameworks] in -the `README.md` file in that folder. - [code-of-conduct]: https://github.com/best-practice-and-impact/govcookiecutter/blob/main/CODE_OF_CONDUCT.md [coverage]: https://coverage.readthedocs.io/ [detect-secrets-repo]: https://github.com/Yelp/detect-secrets/tree/master diff --git a/README.md b/README.md index 0fb5977..c1139aa 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,23 @@ # `govcookiecutter` -## What is govcookiecutter? +## What is govcookiecutter-lite? -A cookiecutter template for analytical, Python-, or Python and R-based projects within +A lightweight cookiecutter template for analytical Python-based projects within His Majesty's Government, and wider public sector. ## How is the template used? -This template helps to set up standardised project structures, and [includes security -features using pre-commit hooks][docs-pre-commit]. This cookiecutter template also acts +This template helps to set up standardised project structures. This cookiecutter template also acts as an installable template (python projects only). It also provides an Agile, centralised, and lightweight analytical quality assurance -(AQA) process. Pull or merge request templates are used to nudge users to complete this -process. [This helps meet HM Government best practice on producing quality analysis, as +(AQA) process. [This helps meet HM Government best practice on producing quality analysis, as defined in the Aqua Book][aqua-book]. +govcookiecutter-lite was developed from the original [govcookiecutter][govcookiecutter] but has +minimised the project creation to include only the minimum files and structure to quickly setup +python packages. + [For reasons why we developed `govcookiecutter`, read the blog post][blog-post], and [watch the live demonstration from March 2021 on version 0.5.3][youtube]. @@ -26,8 +28,8 @@ The cookiecutter template generated for each project will follow this folder str ```shell . └── govcookiecutter/ - ├── {{ cookiecutter.repo_name }}/ - │ └── {{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/ + ├── {{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/ + │ └── {{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/ │ ├── example_modules/ │ │ ├── __init__.py │ │ └── example_module.py @@ -85,13 +87,13 @@ Next, open your terminal, navigate to the directory where you want your new repository to exist. Then run the following command for the latest stable release: ```shell -python -m cookiecutter https://github.com/best-practice-and-impact/govcookiecutter.git +python -m cookiecutter https://github.com/best-practice-and-impact/govcookiecutter-lite.git ``` or for a specific branch, tag, or commit SHA `{SPECIFIC}`, run: ```shell -python -m cookiecutter https://github.com/best-practice-and-impact/govcookiecutter.git --checkout {SPECIFIC} +python -m cookiecutter https://github.com/best-practice-and-impact/govcookiecutter-lite.git --checkout {SPECIFIC} ``` Follow the prompts; if you are asked to re-download `govcookiecutter`, input `yes`. @@ -168,6 +170,7 @@ project][drivendata]. Specifically, it uses a modified version of the `help` com [cruft]: https://github.com/cruft/cruft [docs-pre-commit]: ./CONTRIBUTING.md#getting-started [drivendata]: http://drivendata.github.io/cookiecutter-data-science/ +[govcookiecutter]: https://github.com/best-practice-and-impact/govcookiecutter.git [homebrew]: https://brew.sh/ [issue-windows-os]: https://github.com/best-practice-and-impact/govcookiecutter/issues/20 [pluralsight]: https://www.pluralsight.com/tech-blog/managing-python-environments/ diff --git a/cookiecutter.json b/cookiecutter.json index 97a739e..5a320bd 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -1,12 +1,5 @@ { "organisation_name": "Your public sector organisation name, for example Government Digital Service", - "repository_hosting_platform": ["GitHub", "GitLab"], "organisation_handle": "Your GitHub/GitLab organisation name, for example ukgovdatascience", - "organisational_framework": ["GDS", "N/A"], - "project_name": "Your new project name", - "repo_name": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}", - "overview": "Brief overview of your project.", - "project_version": "0.0.1", - "locked_down_environment": ["No", "Yes"], - "using_R": ["No", "Yes"] + "project_name": "your_new_project_name" } diff --git a/docs/contributing_guide/docs_contributor_README.md b/docs/contributing_guide/docs_contributor_README.md index 1bc04b2..2791a82 100644 --- a/docs/contributing_guide/docs_contributor_README.md +++ b/docs/contributing_guide/docs_contributor_README.md @@ -7,6 +7,5 @@ This is the contributor guide for the `govcookiecutter` project. ../CODE_OF_CONDUCT.md ../CONTRIBUTING.md ./modify_govcookiecutter.md -../{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/docs_repo_frameworks_README.md ``` diff --git a/docs/contributing_guide/modify_govcookiecutter.md b/docs/contributing_guide/modify_govcookiecutter.md index 9077a71..dcd8b09 100644 --- a/docs/contributing_guide/modify_govcookiecutter.md +++ b/docs/contributing_guide/modify_govcookiecutter.md @@ -17,13 +17,13 @@ When you open your terminal and run: cookiecutter https://github.com/best-practice-and-impact/govcookiecutter.git ``` -you'll see a list of prompts to answer; one of them is `repo_name`. +you'll see a list of prompts to answer; one of them is `project_name`. -Your answer for `repo_name` is used to overwrite every instance of -`{{ cookiecutter.repo_name }}`. The first instance is the `govcookiecutter` folder -`{{ cookiecutter.repo_name }}`, which becomes your outputted project! +Your answer for `project_name` is used to overwrite every instance of +`{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}`. The first instance is the `govcookiecutter` folder +`{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}`, which becomes your outputted project! -This means every folder and file contained within the `{{ cookiecutter.repo_name }}` +This means every folder and file contained within the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder becomes part of your output project, including their content. Anything else outside of this folder in `govcookiecutter` will not exist in the outputted project. @@ -33,7 +33,7 @@ The prompts, and their default responses are defined in `cookiecutter.json`. Her keys starting with `_` are not shown to the user, but provide template extensions. One such extension is `jinja2_time.TimeExtension`, which is used to add the correct -year in the `{{ cookiecutter.repo_name }}/LICENSE` file. +year in the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/LICENSE` file. All other keys are used to inject the user responses throughout the template. This happens wherever you see `{{ cookiecutter.{KEY} }}`, where `{KEY}` is the key in @@ -44,9 +44,7 @@ to the user. If the user does not enter a response, these default values are use Values that are lists are shown as numerical options to the user, with the first list element as the default value. -Note that these default values can also contain Jinja templating! For example, the -default response for `repo_name` is actually based on `project_name`, but with all -characters in lowercase, and any spaces replaced with hyphens. +Note that these default values can also contain Jinja templating! ## Validating user entries @@ -75,7 +73,7 @@ defined in `hooks/post_gen_project.py`. These hooks only run after a project has generated and, if they fail, will rollback the entire project. Conditional files and folders are defined as `features` in the -`{{ cookiecutter.repo_name }}/manifest.json` file, which looks like: +`{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/manifest.json` file, which looks like: ``` { @@ -118,14 +116,6 @@ For example, for the following Markdown: The contributing guidelines for this project. -{% if cookiecutter.using_R == "Yes" -%} -### `DESCRIPTION` - -R-specific information related to the project including the name, authors and packages -necessary for the project. - -{% endif -%} - ### `LICENSE` The licence for this project... @@ -146,11 +136,11 @@ These are performed in the `hooks/post_gen_project.py`file. ## Tests, coverage, and continuous integration All pre- and post-generation hooks should be fully tested, alongside any generic -functions that we want to supply to users within the `{{ cookiecutter.repo_name }}` +functions that we want to supply to users within the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` package. These tests should be written in `tests` or -`{{ cookiecutter.repo_name }}/tests` as appropriate. +`{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/tests` as appropriate. -Coverage also only covers the `hooks` and `{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}` folders. +Coverage also only covers the `hooks` and `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folders. ### Testing Jinja templating @@ -158,7 +148,7 @@ Most of the tests are straightforward, and comprehensive. However, to test the J injection of user responses, the `test_govcookiecutter_injected_variables.py` script adopts a test-driven development approach to completeness. -This test parses all the content of the `{{ cookiecutter.repo_name }}` folder, and +This test parses all the content of the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder, and counts the number of times the replacement variable and its variations appear. The constant dictionary variables at the top of the test script define the different @@ -207,7 +197,6 @@ then use semantic versioning to number our releases][semver]. This helps our use select a different version of `govcookiecutter` to use based on their individual needs. [cookiecutter]: https://cookiecutter.readthedocs.io -[docs-organisational-frameworks]: https://github.com/best-practice-and-impact/govcookiecutter/blob/main/docs/%7B%7B%20cookiecutter.repo_name%20%7D%7D/.govcookiecutter/organisational_frameworks/docs_repo_frameworks_README.md [github-issues]: https://github.com/best-practice-and-impact/govcookiecutter/issues [html5-email-format]: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address [jinja]: https://jinja.palletsprojects.com diff --git a/docs/docs_README.md b/docs/docs_README.md index 71a8107..aa5bb2e 100644 --- a/docs/docs_README.md +++ b/docs/docs_README.md @@ -6,8 +6,8 @@ This folder contains documentation for `govcookiecutter`. These are written in M Further details to consider when modifying these files are supplied in the [contributing guidance][contributing-guidance]. ``` -To include documentation from the `{{ cookiecutter.repo_name }}` -folder without duplicating it, refer to it in a file within the `docs/{{ cookiecutter.repo_name }}` folder. +To include documentation from the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` +folder without duplicating it, refer to it in a file within the `docs/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder. To build the documentation, run: diff --git a/docs/govcookiecutter_structure/docs_structure_README.md b/docs/govcookiecutter_structure/docs_structure_README.md index 825d66d..2e873be 100644 --- a/docs/govcookiecutter_structure/docs_structure_README.md +++ b/docs/govcookiecutter_structure/docs_structure_README.md @@ -9,7 +9,7 @@ Further detail on folder contents is available: ./example.md ./hooks.md ./tests.md -../{{ cookiecutter.repo_name }}/docs_repo_README.md +../{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/docs_repo_README.md ``` ## Top-level files @@ -101,7 +101,7 @@ Python imports are arranged according to the [specification defined by `black`][ #### `pytest` -To run the tests within the `tests`, and `{{ cookiecutter.repo_name }}/tests` folders +To run the tests within the `tests`, and `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/tests` folders using the `pytest` Python package, enter the following command: ```shell @@ -125,7 +125,7 @@ make coverage_html ``` A code coverage report in HTML will be produced on the code in the `hooks` and -`{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}` folders. +`{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folders. This HTML report can be accessed at `htmlcov/index.html`. ### `README.md` diff --git a/docs/index.md b/docs/index.md index 263bd64..dcde541 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,6 +8,6 @@ self ./contributing_guide/docs_contributor_README.md ./govcookiecutter_structure/docs_structure_README.md -./{{ cookiecutter.repo_name }}/docs_repo_README.md +./{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/docs_repo_README.md ./reference/docs_ref_README.md ``` diff --git a/docs/reference/docs_ref_README.md b/docs/reference/docs_ref_README.md index 4d1049c..bbcbd84 100644 --- a/docs/reference/docs_ref_README.md +++ b/docs/reference/docs_ref_README.md @@ -26,7 +26,6 @@ hooks fail, the generated project will be rolled-back, and deleted. :toctree: api/ set_aqa_framework - set_request_template ``` diff --git a/docs/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/docs_repo_frameworks_README.md b/docs/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/docs_repo_frameworks_README.md deleted file mode 100644 index 3066c3d..0000000 --- a/docs/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/docs_repo_frameworks_README.md +++ /dev/null @@ -1,2 +0,0 @@ -```{include} ../../../../{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/framework_README.md -``` diff --git a/docs/{{ cookiecutter.repo_name }}/docs.md b/docs/{{ cookiecutter.repo_name }}/docs.md index e8799ba..d42c3ea 100644 --- a/docs/{{ cookiecutter.repo_name }}/docs.md +++ b/docs/{{ cookiecutter.repo_name }}/docs.md @@ -1,2 +1,2 @@ -```{include} ../../{{ cookiecutter.repo_name }}/docs/repo_docs_README.md +```{include} ../../{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/docs/repo_docs_README.md ``` diff --git a/docs/{{ cookiecutter.repo_name }}/docs_repo_README.md b/docs/{{ cookiecutter.repo_name }}/docs_repo_README.md index b858ef8..9ab279f 100644 --- a/docs/{{ cookiecutter.repo_name }}/docs_repo_README.md +++ b/docs/{{ cookiecutter.repo_name }}/docs_repo_README.md @@ -1,7 +1,7 @@ -# `{{ cookiecutter.repo_name }}` folder structure +# `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder structure This is information about the structure of the created repository contained -in the `{{ cookiecutter.repo_name }}` folder within the `govcookiecutter` repository. +in the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder within the `govcookiecutter` repository. Some of these files may not be present in the actual created repository, as this is customised based on the user's answers to the prompts on set-ups. diff --git a/docs/{{ cookiecutter.repo_name }}/dot_govcookiecutter.md b/docs/{{ cookiecutter.repo_name }}/dot_govcookiecutter.md index 52ef5b5..72fe5b6 100644 --- a/docs/{{ cookiecutter.repo_name }}/dot_govcookiecutter.md +++ b/docs/{{ cookiecutter.repo_name }}/dot_govcookiecutter.md @@ -4,16 +4,3 @@ Used in cookiecutter configuation for removing files in the created repository that are not required for users not using R. - -## organisational_frameworks/ - -Contains folders for organisation-specific frameworks. These allow for customisation of the created repository. -The folders require: - -- A template Analytical Quality Assurance (AQA) plan -- A template data log -- A template assumptions and caveats log -- A `pull_request_template.md` -- A `README.md`. - -`govcookiecutter` currently contains a Government Digital Service framework. diff --git a/docs/{{ cookiecutter.repo_name }}/source_code.md b/docs/{{ cookiecutter.repo_name }}/source_code.md index 1591930..7c5ad92 100644 --- a/docs/{{ cookiecutter.repo_name }}/source_code.md +++ b/docs/{{ cookiecutter.repo_name }}/source_code.md @@ -1,4 +1,4 @@ -# `{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}` folder overview +# `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder overview This is where source code for the created project is stored. We recommend following a standard Python project structure, with functions grouped into modules inside a descriptively named folder. The created repository contains an `example_module.py` diff --git a/docs/{{ cookiecutter.repo_name }}/tests.md b/docs/{{ cookiecutter.repo_name }}/tests.md index 5d230f7..678fae4 100644 --- a/docs/{{ cookiecutter.repo_name }}/tests.md +++ b/docs/{{ cookiecutter.repo_name }}/tests.md @@ -1,2 +1,2 @@ -```{include} ../../{{ cookiecutter.repo_name }}/tests/repo_tests_README.md +```{include} ../../{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/tests/repo_tests_README.md ``` diff --git a/hooks/__init__.py b/hooks/__init__.py index 06bc4bd..d4d5baf 100644 --- a/hooks/__init__.py +++ b/hooks/__init__.py @@ -1,13 +1,11 @@ from hooks.post_gen_project import ( delete_files_and_folders, parse_features_json, - set_aqa_framework, - set_request_template, ) +from hooks.pre_gen_project import check_repo_name_structure __all__ = ( "delete_files_and_folders", + "check_repo_name_structure", "parse_features_json", - "set_aqa_framework", - "set_request_template", ) diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index ed7f3c9..3bbfd9f 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -24,88 +24,6 @@ def delete_files_and_folders(paths: Union[Path, str, List[Path], List[str]]) -> _ = [f.unlink() for f in paths if f.is_file() and f.exists()] -def set_aqa_framework( - dir_organisational_framework_aqa: Union[Path, str], - dir_cookiecutter_docs_aqa: Union[Path, str], -) -> None: - """Set a specific organisational analytical quality assurance (AQA) framework. - - Args: - dir_organisational_framework_aqa: A folder path that contains a specific - organisational AQA framework. - dir_cookiecutter_docs_aqa: A folder path within the outputted project - structure, where the contents of `dir_organisational_framework_aqa` will - reside. - - Returns: - The organisation-specific AQA framework in the outputted project structure's - `dir_cookiecutter_docs_aqa` folder. - - """ - - # Remove the default `docs/aqa` folder, and its contents - delete_files_and_folders(dir_cookiecutter_docs_aqa) - - # Copy the relevant organisational AQA framework to the `docs/aqa` folder - _ = Path(dir_organisational_framework_aqa).rename(dir_cookiecutter_docs_aqa) - - -def set_request_template( - path_organisational_framework_request_template: Union[Path, str], - dir_govcookiecutter: Union[Path, str], - repository_hosting_platform: str, -) -> None: - """Set a pull or merge request template in the outputted project structure for a - specific organisation. - - A pull request template is created if the user chooses GitHub as their repository - hosting platform. A merge request template is created if they choose GitLab instead. - - Args: - path_organisational_framework_request_template: A file path to the specific - organisation pull or merge request template. - dir_govcookiecutter: A folder path to the outputted `govcookiecutter` template. - repository_hosting_platform: The repository hosting platform. Must be one of - "GitHub" or "GitLab" (case insensitive). - - Returns: - A organisation-specific pull or merge request template in the correct location, - depending on their choice of GitHub or GitLab as the repository hosting - platform. If neither GitHub or GitLab are chosen, a `ValueError` is raised. - - """ - - # Define the file path where the request template will be moved, which is dependent - # on the repository hosting platform. If the `dir_request_template` is not - # initially one of `.github` or `.gitlab`, raise a `ValueError` - if repository_hosting_platform.lower() == "github": - path_request_template = Path(dir_govcookiecutter).joinpath( - f".{repository_hosting_platform.lower()}", "pull_request_template.md" - ) - elif repository_hosting_platform.lower() == "gitlab": - path_request_template = Path(dir_govcookiecutter).joinpath( - f".{repository_hosting_platform.lower()}", - "merge_request_templates", - "{{ cookiecutter.project_name }}.md", - ) - else: - raise ValueError( - "`repository_hosting_platform` must be one of `GitHub` or `GitLab`: " - f"{repository_hosting_platform}" - ) - - # Recursively create all the directories to the parent directory of - # `path_request_template` if they do not already exist - if not path_request_template.parent.is_dir(): - path_request_template.parent.mkdir(parents=True, exist_ok=True) - - # Move the `path_organisational_framework_request_template` to - # `path_request_template` - _ = Path(path_organisational_framework_request_template).rename( - path_request_template - ) - - def parse_features_json(file: Union[Path, str]) -> List[Path]: """Parse a JSON file containing filepaths. @@ -138,31 +56,5 @@ def parse_features_json(file: Union[Path, str]) -> List[Path]: # Define the folder path to `.govcookiecutter` DIR_GOVCOOKIECUTTER = Path(".govcookiecutter") - # Check `{{ cookiecutter.organisational_framework }}` is not `N/A` - if "{{ cookiecutter.organisational_framework }}" != "N/A": - - # Define the folder path to the specific organisation framework of interest in - # the `organisational_frameworks` - # folder - DIR_ORGANISATIONAL_FRAMEWORKS = DIR_GOVCOOKIECUTTER.joinpath( - "organisational_frameworks", "{{ cookiecutter.organisational_framework }}" - ) - - # Transfer the `aqa` folder, and the pull/merge request templates to the - # correct folder paths - set_aqa_framework( - DIR_ORGANISATIONAL_FRAMEWORKS.joinpath("aqa"), Path("docs").joinpath("aqa") - ) - set_request_template( - DIR_ORGANISATIONAL_FRAMEWORKS.joinpath("request_template.md"), - Path.cwd(), - "{{ cookiecutter.repository_hosting_platform }}", - ) - - # Delete files defined in the `manifest.json` file - delete_files_and_folders( - parse_features_json(Path(".govcookiecutter", "manifest.json")) - ) - # Remove `DIR_GOVCOOKIECUTTER` delete_files_and_folders(DIR_GOVCOOKIECUTTER) diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py new file mode 100644 index 0000000..5f29cbe --- /dev/null +++ b/hooks/pre_gen_project.py @@ -0,0 +1,20 @@ +def check_repo_name_structure(repo_name: str) -> None: + """Check if the repository name follows the correct structure. + + Args: + repo_name: The name of the repository to check. + + Raises: + ValueError: If the repository name does not follow the correct structure. + """ + print("checking repo name length", repo_name) + + if len(repo_name) > 88: + print("checking repo name length") + raise ValueError("Repository name must not exceed 100 characters.") + + +if __name__ == "__main__": + + # Check the format of the contact email address supplied is a valid one + check_repo_name_structure("{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}") diff --git a/pyproject.toml b/pyproject.toml index dece2fa..71141f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,10 +2,10 @@ [tool.coverage.run] source = [ "./hooks", - "./{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}" + "./{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}" ] omit = [ - "./{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/run_pipeline.py" + "./{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/run_pipeline.py" ] [tool.coverage.omit] @@ -26,7 +26,7 @@ addopts = [ "--doctest-modules", "--ignore='./docs/'", "--ignore='./example/'", - "--ignore='./{{ cookiecutter.repo_name }}/docs/'" + "--ignore='./{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/docs/'" ] doctest_optionflags = "NORMALIZE_WHITESPACE" testpaths = [ diff --git a/tests/test_env.py b/tests/test_env.py index 4eeb8ec..54b893b 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -4,7 +4,7 @@ from dotenv import dotenv_values # Define a path to the `govcookiecutter` template directory, and its `.env` file -DIR_TEMPLATE = Path("{{ cookiecutter.repo_name }}") +DIR_TEMPLATE = Path("{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}") PATH_TEMPLATE_ENV = DIR_TEMPLATE.joinpath(".env") # Define a list of directory names to recursively ignore, as well as a list of @@ -119,7 +119,7 @@ def define_expected_env_variables( ): if ( d.name.upper() - == "{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}".upper() # noqa: E501 + == "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}".upper() # noqa: E501 ): env_expected_dir_variable = loop_directories_children( d, env_expected_dir_variable diff --git a/tests/test_govcookiecutter_creation.py b/tests/test_govcookiecutter_creation.py index c348b8f..d697827 100644 --- a/tests/test_govcookiecutter_creation.py +++ b/tests/test_govcookiecutter_creation.py @@ -5,73 +5,17 @@ from sphinx.cmd.build import main -@pytest.mark.parametrize("test_input_repository_hosting_platform", ["GitHub", "GitLab"]) -@pytest.mark.parametrize("test_input_project_name", ["A project", "Another project"]) -@pytest.mark.parametrize("test_input_using_r", ["Yes", "No"]) -@pytest.mark.parametrize("test_input_locked_down_environment", ["Yes", "No"]) -def test_request_template_generated_correctly( - cookies, - test_input_repository_hosting_platform: str, - test_input_project_name: str, - test_input_using_r: str, - test_input_locked_down_environment: str, -) -> None: - """Test the pull or merge request templates are created correctly.""" - - # Create a new project adding extra context; return it's `project_path` attribute - test_output_project = cookies.bake( - extra_context={ - "repository_hosting_platform": test_input_repository_hosting_platform, - "project_name": test_input_project_name, - "using_R": test_input_using_r, - "locked_down_environment": test_input_locked_down_environment, - } - ) - - # Check that the build passes - assert test_output_project.exit_code == 0 - assert test_output_project.exception is None - - # Define the path to the pull or merge request template - test_output = test_output_project.project_path - if test_input_repository_hosting_platform == "GitHub": - assert test_output.joinpath(".github", "pull_request_template.md").is_file() - elif test_input_repository_hosting_platform == "GitLab": - assert test_output.joinpath( - ".gitlab", "merge_request_templates", f"{test_input_project_name}.md" - ).is_file() - else: - pytest.fail( - "Unknown `repository_hosting_platform` value: " - f"{test_input_repository_hosting_platform}" - ) - - -@pytest.mark.skip( - reason="Unclear how to test this, unless there is a title in each " "framework" -) -def test_organisational_framework_correct() -> None: - """Test that the correct organisational framework is built.""" - pass - - @pytest.mark.parametrize("test_input_repository_name", ["a", "b"]) -@pytest.mark.parametrize("test_input_using_r", ["Yes", "No"]) -@pytest.mark.parametrize("test_input_locked_down_environment", ["Yes", "No"]) def test_repo_name_directory_correct( cookies, test_input_repository_name: str, - test_input_using_r: str, - test_input_locked_down_environment: str, ) -> None: """Check the project repository is generated with the correct name.""" # Create a new project adding extra context test_output_project = cookies.bake( extra_context={ - "repo_name": test_input_repository_name, - "using_R": test_input_using_r, - "locked_down_environment": test_input_locked_down_environment, + "project_name": test_input_repository_name, } ) @@ -91,8 +35,6 @@ def test_repo_name_directory_correct( "organisation_handle": "handle_1", "contact_email": "email@1", "project_name": "Project_1", - "repo_name": "repo_1", - "overview": "overview_1", "project_version": "version_1", }, { @@ -100,25 +42,15 @@ def test_repo_name_directory_correct( "organisation_handle": "handle_2", "contact_email": "email@2", "project_name": "Project_2", - "repo_name": "repo_2", - "overview": "overview_2", "project_version": "version_2", }, ] @pytest.mark.parametrize("test_input_context", args_builds_correctly) -@pytest.mark.parametrize("test_input_repository_hosting_platform", ["GitHub", "GitLab"]) -@pytest.mark.parametrize("test_input_organisational_framework", ["GDS", "N/A"]) -@pytest.mark.parametrize("test_input_using_r", ["No", "Yes"]) -@pytest.mark.parametrize("test_input_locked_down_environment", ["Yes", "No"]) def test_builds_correctly( cookies, test_input_context: Dict[str, str], - test_input_repository_hosting_platform: str, - test_input_organisational_framework: str, - test_input_using_r: str, - test_input_locked_down_environment: str, ) -> None: """Test that the projects are built correctly with no errors.""" @@ -126,10 +58,6 @@ def test_builds_correctly( test_output_project = cookies.bake( extra_context={ **test_input_context, - "repository_hosting_platform": test_input_repository_hosting_platform, - "organisational_framework": test_input_organisational_framework, - "using_R": test_input_using_r, - "locked_down_environment": test_input_locked_down_environment, } ) diff --git a/tests/test_govcookiecutter_injected_variables.py b/tests/test_govcookiecutter_injected_variables.py index dd1afc1..7b14d5c 100644 --- a/tests/test_govcookiecutter_injected_variables.py +++ b/tests/test_govcookiecutter_injected_variables.py @@ -17,47 +17,12 @@ '[u"{{ cookiecutter.organisation_handle }}"],': 0, '"{{ cookiecutter.organisation_handle }}",': 0, } -# CONTACT_EMAIL_COUNT = { -# "mailto:{{ cookiecutter.contact_email }}": 2, -# "[{{ cookiecutter.contact_email }}][email-address].": 1, -# '"{{ cookiecutter.contact_email }}")': 0, -# } PROJECT_NAME_COUNT = { '"{{ cookiecutter.project_name }}"': 1, '"{{ cookiecutter.project_name }}",': 0, 'u"{{ cookiecutter.project_name }}': 0, "{{ cookiecutter.project_name }}": 2, } -REPO_NAME_COUNT = { - '"{{ cookiecutter.repo_name }}",': 0, - "`{{ cookiecutter.repo_name }}`": 8, - "`{{ cookiecutter.repo_name }}`,": 1, - '"{{ cookiecutter.repo_name }}.tex",': 0, - '"{{ cookiecutter.repo_name }}doc"': 1, - "{{ cookiecutter.repo_name }}": 2, -} -OVERVIEW_COUNT = { - '"{{ cookiecutter.overview }}",': 0, - "{{ cookiecutter.overview }}": 2, -} -PROJECT_VERSION_COUNT = { - '"{{ cookiecutter.project_version }}"': 2, - "{{ cookiecutter.project_version }}": 1, -} -USING_R_NO_COUNT = { - "https://github.com/lorenzwalthert/precommit": 0, - "`.lintr`": 0, - "`.Rprofile`": 0, - "`DESCRIPTION`": 0, - "`startup.R`": 0, -} -USING_R_YES_COUNT = { - "https://github.com/lorenzwalthert/precommit": 2, - "`.lintr`": 0, - "`.Rprofile`": 0, - "`DESCRIPTION`": 0, - "`startup.R`": 0, -} def replace_cookiecutter_jinja2_counts( @@ -138,58 +103,25 @@ def recursive_open_and_count_search_terms( # Define the test cases for the `test_injected_counts_correct` test args_injected_counts_correct = [ - ("organisation_name", "org_1", ORGANISATION_NAME_COUNT, {"using_R": "No"}), - ("organisation_name", "org_2", ORGANISATION_NAME_COUNT, {"using_R": "Yes"}), - ("organisation_handle", "handle_1", ORGANISATION_HANDLE_COUNT, {"using_R": "No"}), + ("organisation_name", "org_1", ORGANISATION_NAME_COUNT), + ("organisation_name", "org_2", ORGANISATION_NAME_COUNT), + ("organisation_handle", "handle_1", ORGANISATION_HANDLE_COUNT), ( "organisation_handle", "handle_2", {**ORGANISATION_HANDLE_COUNT, '"{{ cookiecutter.organisation_handle }}",': 0}, - {"using_R": "Yes"}, ), - # ("contact_email", "email@1", CONTACT_EMAIL_COUNT, {"using_R": "No"}), - # ( - # "contact_email", - # "email@2", - # {**CONTACT_EMAIL_COUNT, '"{{ cookiecutter.contact_email }}")': 0}, - # {"using_R": "Yes"}, - # ), - ("project_name", "Project_1", PROJECT_NAME_COUNT, {"using_R": "No"}), + ("project_name", "Project_1", PROJECT_NAME_COUNT), ( "project_name", "Project_2", - {**PROJECT_NAME_COUNT, "{{ cookiecutter.project_name }}": 3}, - {"using_R": "Yes"}, - ), - ("repo_name", "repo_1", REPO_NAME_COUNT, {"using_R": "No"}), - ( - "repo_name", - "repo_2", - {**REPO_NAME_COUNT, "{{ cookiecutter.repo_name }}": 3}, - {"using_R": "Yes"}, - ), - ("overview", "overview_1", OVERVIEW_COUNT, {"using_R": "No"}), - ( - "overview", - "overview_1", - {**OVERVIEW_COUNT, "{{ cookiecutter.overview }}": 3}, - {"using_R": "Yes"}, - ), - ("project_version", "project_version_1", PROJECT_VERSION_COUNT, {"using_R": "No"}), - ( - "project_version", - "project_version_2", - {**PROJECT_VERSION_COUNT, "{{ cookiecutter.project_version }}": 2}, - {"using_R": "Yes"}, + {**PROJECT_NAME_COUNT, "{{ cookiecutter.project_name }}": 2}, ), - ("using_R", "No", USING_R_NO_COUNT, {}), - ("using_R", "Yes", USING_R_YES_COUNT, {}), ] @pytest.mark.parametrize( - "test_input_variable, test_input_value, " - "test_input_variable_counts, test_input_other_context", + "test_input_variable, test_input_value, " "test_input_variable_counts", args_injected_counts_correct, ) def test_injected_counts_correct( @@ -197,7 +129,6 @@ def test_injected_counts_correct( test_input_variable: str, test_input_value: str, test_input_variable_counts: Dict[str, int], - test_input_other_context: Dict[str, str], ) -> None: # Generate the expected counts @@ -211,7 +142,6 @@ def test_injected_counts_correct( test_output_project = cookies.bake( extra_context={ test_input_variable: test_input_value, - **test_input_other_context, } ) diff --git a/tests/test_post_gen_project/test_set_aqa_framework.py b/tests/test_post_gen_project/test_set_aqa_framework.py deleted file mode 100644 index b434c96..0000000 --- a/tests/test_post_gen_project/test_set_aqa_framework.py +++ /dev/null @@ -1,91 +0,0 @@ -from pathlib import Path -from unittest.mock import MagicMock - -import pytest - -from hooks.post_gen_project import delete_files_and_folders, set_aqa_framework - - -@pytest.fixture -def patch_delete_files_and_folders(mocker) -> MagicMock: - """Patch the ``delete_files_and_folders`` function.""" - return mocker.patch( - "hooks.post_gen_project.delete_files_and_folders", - side_effect=delete_files_and_folders, - ) - - -# Define test cases for the `TestSetAqaFramework` test class -args_test_set_aqa_framework = ["hello", "world"] -args_test_set_aqa_framework_temporary_frameworks = [ - ("This is the request template.", "This is the AQA file."), - ("Here is the request template.", "Here is the AQA file."), -] - - -@pytest.mark.parametrize( - "test_input_dir_cookiecutter_docs_aqa", args_test_set_aqa_framework -) -@pytest.mark.parametrize( - "test_input_request_template, test_input_aqa", - args_test_set_aqa_framework_temporary_frameworks, -) -class TestSetAqaFramework: - def test_delete_files_and_folders_called_once_correctly( - self, - temporary_frameworks: Path, - patch_delete_files_and_folders: MagicMock, - test_input_dir_cookiecutter_docs_aqa: str, - ) -> None: - """Test the ``delete_files_and_folders`` function is called once correctly.""" - - # Define a path to the example output folder, which is the parent of - # `temporary_frameworks`, and create a directory with an example text file - dir_docs_aqa = temporary_frameworks.parent.joinpath( - test_input_dir_cookiecutter_docs_aqa - ) - dir_docs_aqa.mkdir() - dir_docs_aqa.joinpath("example.txt").write_text( - f"New file in: {repr(test_input_dir_cookiecutter_docs_aqa)}" - ) - - # Execute the `set_aqa_framework` function - set_aqa_framework(temporary_frameworks, dir_docs_aqa) - - # Assert `delete_files_and_folders` is called once correctly, then delete the - # output folder for the next test - # execution - patch_delete_files_and_folders.assert_called_once_with(dir_docs_aqa) - - def test_aqa_framework_folder_moved_correctly( - self, - temporary_frameworks: Path, - test_input_request_template: str, - test_input_aqa: str, - test_input_dir_cookiecutter_docs_aqa: str, - ) -> None: - """Test the right AQA framework folder is moved to the correct location.""" - - # Define a path to the example output folder, which is the parent of - # `temporary_frameworks`, and create a - # directory with an example text file - dir_docs_aqa = temporary_frameworks.parent.joinpath( - test_input_dir_cookiecutter_docs_aqa - ) - dir_docs_aqa.mkdir() - dir_docs_aqa.joinpath("example.txt").write_text( - f"New file in: {repr(test_input_dir_cookiecutter_docs_aqa)}" - ) - - # Execute the `set_aqa_framework` function - set_aqa_framework(temporary_frameworks, dir_docs_aqa) - - # Assert the correct text has been written - assert ( - dir_docs_aqa.joinpath("request_template.md").read_text() - == test_input_request_template - ) - assert dir_docs_aqa.joinpath("aqa", "aqa.md").read_text() == test_input_aqa - - # Assert the `dir_framework` has been removed - assert not temporary_frameworks.exists() diff --git a/tests/test_post_gen_project/test_set_request_template.py b/tests/test_post_gen_project/test_set_request_template.py deleted file mode 100644 index fd0fb6f..0000000 --- a/tests/test_post_gen_project/test_set_request_template.py +++ /dev/null @@ -1,75 +0,0 @@ -from pathlib import Path - -import pytest - -from hooks.post_gen_project import set_request_template - -# Define test cases for the `TestSetRequestTemplate` test class -args_test_set_request_template_valueerror_for_bad_repository_hosting_platform = [ - "hello", - "world", -] -args_test_set_request_template_request_template_moved_correctly = [ - "Github", - "gitlab", - "GITHUB", - "gitLab", -] -args_test_set_request_template_temporary_frameworks = [ - ("This is the request template.", "This is the AQA file."), - ("Here is the request template.", "Here is the AQA file."), -] - - -@pytest.mark.parametrize( - "test_input_request_template, test_input_aqa", - args_test_set_request_template_temporary_frameworks, -) -class TestSetRequestTemplate: - @pytest.mark.parametrize( - "test_input_repository_hosting_platform", - args_test_set_request_template_valueerror_for_bad_repository_hosting_platform, - ) - def test_valueerror_for_bad_repository_hosting_platform( - self, temporary_frameworks: Path, test_input_repository_hosting_platform: str - ) -> None: - """Test a ``ValueError`` is raised if the repository hosting platform is not - Github or GitLab.""" - with pytest.raises(ValueError): - set_request_template( - temporary_frameworks.joinpath("request_template.md"), - temporary_frameworks.parent, - test_input_repository_hosting_platform, - ) - - @pytest.mark.parametrize( - "test_input_repository_hosting_platform", - args_test_set_request_template_request_template_moved_correctly, - ) - def test_request_template_moved_correctly( - self, - temporary_frameworks: Path, - test_input_request_template: str, - test_input_repository_hosting_platform: str, - ) -> None: - """Test the right AQA framework folder is moved to the correct location.""" - - # Execute the `set_request_template` function - set_request_template( - temporary_frameworks.joinpath("request_template.md"), - temporary_frameworks.parent, - test_input_repository_hosting_platform, - ) - - # Assert the correct text has been written to the correct location - if test_input_repository_hosting_platform.lower() == "github": - test_output = temporary_frameworks.parent.joinpath( - ".github", "pull_request_template.md" - ).read_text() - else: - test_output = temporary_frameworks.parent.joinpath( - ".gitlab", - "merge_request_templates", - "{{ cookiecutter.project_name }}.md", - ).read_text() - assert test_output == test_input_request_template diff --git a/{{ cookiecutter.repo_name }}/.gitignore b/{{ cookiecutter.project_name }}/.gitignore similarity index 100% rename from {{ cookiecutter.repo_name }}/.gitignore rename to {{ cookiecutter.project_name }}/.gitignore diff --git a/{{ cookiecutter.project_name }}/CHANGELOG.md b/{{ cookiecutter.project_name }}/CHANGELOG.md new file mode 100644 index 0000000..17880fb --- /dev/null +++ b/{{ cookiecutter.project_name }}/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +All notable changes to this project will be documented in this file, grouped by Added, Fixed, Changed, and Removed. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] - + +### Added + +### Fixed + +### Changed + +### Removed diff --git a/{{ cookiecutter.repo_name }}/CODE_OF_CONDUCT.md b/{{ cookiecutter.project_name }}/CODE_OF_CONDUCT.md similarity index 73% rename from {{ cookiecutter.repo_name }}/CODE_OF_CONDUCT.md rename to {{ cookiecutter.project_name }}/CODE_OF_CONDUCT.md index 6a8f475..2a4d6bc 100644 --- a/{{ cookiecutter.repo_name }}/CODE_OF_CONDUCT.md +++ b/{{ cookiecutter.project_name }}/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# Code of conduct for `{{ cookiecutter.repo_name }}` +# Code of conduct for `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` [Our code of conduct can be found at `docs/contributor_guide/CODE_OF_CONDUCT.md`][code-of-conduct]. diff --git a/{{ cookiecutter.repo_name }}/LICENSE b/{{ cookiecutter.project_name }}/LICENSE similarity index 100% rename from {{ cookiecutter.repo_name }}/LICENSE rename to {{ cookiecutter.project_name }}/LICENSE diff --git a/{{ cookiecutter.repo_name }}/README.md b/{{ cookiecutter.project_name }}/README.md similarity index 81% rename from {{ cookiecutter.repo_name }}/README.md rename to {{ cookiecutter.project_name }}/README.md index 015b0da..a34e061 100644 --- a/{{ cookiecutter.repo_name }}/README.md +++ b/{{ cookiecutter.project_name }}/README.md @@ -1,12 +1,12 @@ -# `{{ cookiecutter.repo_name }}` +# `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` -{{ cookiecutter.overview }} +A placeholder description for your python project ```{warning} Where this documentation refers to the root folder we mean where this README.md is located. ``` -## What is `{{ cookiecutter.repo_name }}`? +## What is `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}`? Add a summary of your project here. @@ -21,9 +21,6 @@ a virtual environment. ## Requirements - Python 3.9+ installed -{% if cookiecutter.using_R == "Yes" -%} -- R 4.2.3+ installed -{% endif -%} - a `.secrets` file with the required [secrets and credentials](#required-secrets-and-credentials) - to have [loaded environment variables][docs-loading-environment-variables] from `.env` @@ -39,14 +36,6 @@ python -m pip install -U pip setuptools pip install -e . ``` -{% if cookiecutter.using_R == "Yes" -%} -While in the root folder in an R console, such as that in Rstudio, -install the R components with: -```R -install.packages(".") -``` -{% endif -%} - ### Install for contributors (Python only) To install the contributing requirements, use: @@ -60,15 +49,6 @@ This installs an editable version of the package. This means that when you updat package code you do not have to reinstall it for the changes to take effect. This saves a lot of time when you test your code. -{% if cookiecutter.using_R == "Yes" -%} -While in the root folder in an R console, such as that in Rstudio, load the package -using `devtools`: -```R -#install.packages("devtools") -devtools::load_all() -``` -{% endif -%} - Remember to update the setup and requirement files inline with any changes to your package. @@ -79,7 +59,7 @@ To run the pipeline, run the following code in the terminal (either in the root project, or by specifying the path to `run_pipeline.py` from elsewhere). ```shell -python src/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/run_pipeline.py +python src/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/run_pipeline.py ``` Alternatively, most Python IDEs allow you to run the code directly using a `run` button. @@ -104,8 +84,8 @@ The cookiecutter template generated for each project will follow this folder str ```shell . -├── {{ cookiecutter.repo_name }}/ -│ └── {{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/ +├── {{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/ +│ └── {{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/ │ ├── example_modules/ │ │ ├── __init__.py │ │ └── example_module.py @@ -123,7 +103,7 @@ Crown copyright and available under the terms of the Open Government 3.0 licence ## Contributing -If you want to help us build and improve `{{ cookiecutter.repo_name }}`, please take a look at our +If you want to help us build and improve `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}`, please take a look at our [contributing guidelines][contributing]. ## Acknowledgements diff --git a/{{ cookiecutter.repo_name }}/docs/Quality Assurance/QA_README.md b/{{ cookiecutter.project_name }}/docs/Quality Assurance/QA_README.md similarity index 100% rename from {{ cookiecutter.repo_name }}/docs/Quality Assurance/QA_README.md rename to {{ cookiecutter.project_name }}/docs/Quality Assurance/QA_README.md diff --git a/{{ cookiecutter.repo_name }}/docs/_static/.gitkeep b/{{ cookiecutter.project_name }}/docs/_static/.gitkeep similarity index 100% rename from {{ cookiecutter.repo_name }}/docs/_static/.gitkeep rename to {{ cookiecutter.project_name }}/docs/_static/.gitkeep diff --git a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/aqa_plan.md b/{{ cookiecutter.project_name }}/docs/aqa/aqa_plan.md similarity index 100% rename from {{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/aqa_plan.md rename to {{ cookiecutter.project_name }}/docs/aqa/aqa_plan.md diff --git a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/assumptions_caveats.md b/{{ cookiecutter.project_name }}/docs/aqa/assumptions_caveats.md similarity index 100% rename from {{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/assumptions_caveats.md rename to {{ cookiecutter.project_name }}/docs/aqa/assumptions_caveats.md diff --git a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/data_log.md b/{{ cookiecutter.project_name }}/docs/aqa/data_log.md similarity index 100% rename from {{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/data_log.md rename to {{ cookiecutter.project_name }}/docs/aqa/data_log.md diff --git a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/gds_aqa_README.md b/{{ cookiecutter.project_name }}/docs/aqa/gds_aqa_README.md similarity index 100% rename from {{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/aqa/gds_aqa_README.md rename to {{ cookiecutter.project_name }}/docs/aqa/gds_aqa_README.md diff --git a/{{ cookiecutter.repo_name }}/docs/conf.py b/{{ cookiecutter.project_name }}/docs/conf.py similarity index 97% rename from {{ cookiecutter.repo_name }}/docs/conf.py rename to {{ cookiecutter.project_name }}/docs/conf.py index e7d3fdf..639624a 100644 --- a/{{ cookiecutter.repo_name }}/docs/conf.py +++ b/{{ cookiecutter.project_name }}/docs/conf.py @@ -55,9 +55,9 @@ # |version| and |release|, also used in # various other places throughout the built documents. # The short X.Y.Z version. -version = "{{ cookiecutter.project_version }}" +version = "0.0.1" # The full version, including alpha/beta/rc tags. -release = "{{ cookiecutter.project_version }}" +release = "0.0.1" # List of patterns, relative to source directory, # that match files and directories to @@ -165,7 +165,7 @@ # html_search_scorer = "scorer.js" # Output file base name for HTML help builder. -htmlhelp_basename = "{{ cookiecutter.repo_name }}doc" +htmlhelp_basename = "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}doc" # -- Options for autosection output ---------------- diff --git a/{{ cookiecutter.repo_name }}/docs/contributor_guide/CODE_OF_CONDUCT.md b/{{ cookiecutter.project_name }}/docs/contributor_guide/CODE_OF_CONDUCT.md similarity index 95% rename from {{ cookiecutter.repo_name }}/docs/contributor_guide/CODE_OF_CONDUCT.md rename to {{ cookiecutter.project_name }}/docs/contributor_guide/CODE_OF_CONDUCT.md index d757174..6c1a97e 100644 --- a/{{ cookiecutter.repo_name }}/docs/contributor_guide/CODE_OF_CONDUCT.md +++ b/{{ cookiecutter.project_name }}/docs/contributor_guide/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# Code of conduct for `{{ cookiecutter.repo_name }}` +# Code of conduct for `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` All contributors to this repository hosted by `{{ cookiecutter.organisation_handle }}` are expected to follow the Contributor Covenant Code of Conduct. Those working within HM Government are also expected to follow the [Civil Service @@ -10,7 +10,7 @@ Code][civil-service-code]. Where this Code of Conduct says: -- "Project", we mean this {{ cookiecutter.repository_hosting_platform }} repository, `{{ cookiecutter.repo_name }}` ; +- "Project", we mean this repository `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` ; - "Maintainer", we mean active developers of the primary project team(s) behind `{{ cookiecutter.organisation_handle }}`; and - "Leadership", we mean `{{ cookiecutter.organisation_handle }}` organisation owners, line managers, and other leadership within the {{ cookiecutter.organisation_name }}. diff --git a/{{ cookiecutter.repo_name }}/docs/contributor_guide/CONTRIBUTING.md b/{{ cookiecutter.project_name }}/docs/contributor_guide/CONTRIBUTING.md similarity index 97% rename from {{ cookiecutter.repo_name }}/docs/contributor_guide/CONTRIBUTING.md rename to {{ cookiecutter.project_name }}/docs/contributor_guide/CONTRIBUTING.md index 0d90efb..ee541b9 100644 --- a/{{ cookiecutter.repo_name }}/docs/contributor_guide/CONTRIBUTING.md +++ b/{{ cookiecutter.project_name }}/docs/contributor_guide/CONTRIBUTING.md @@ -69,7 +69,7 @@ pytest Code coverage of Python scripts is measured using the [`coverage` Python package][coverage]; its configuration can be found in `pyproject.toml`. Note coverage -only extends to Python scripts in the `{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}` folder. +only extends to Python scripts in the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder. To run code coverage, and view it as an HTML report, enter the following command in your terminal: diff --git a/{{ cookiecutter.project_name }}/docs/contributor_guide/contributor_README.md b/{{ cookiecutter.project_name }}/docs/contributor_guide/contributor_README.md new file mode 100644 index 0000000..410b80e --- /dev/null +++ b/{{ cookiecutter.project_name }}/docs/contributor_guide/contributor_README.md @@ -0,0 +1,9 @@ +# Contributing guide + +This is the contributor guide for the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` project. + +```{toctree} +:maxdepth: 2 +./CODE_OF_CONDUCT.md +./CONTRIBUTING.md +``` diff --git a/{{ cookiecutter.repo_name }}/docs/index.md b/{{ cookiecutter.project_name }}/docs/index.md similarity index 100% rename from {{ cookiecutter.repo_name }}/docs/index.md rename to {{ cookiecutter.project_name }}/docs/index.md diff --git a/{{ cookiecutter.repo_name }}/docs/repo_docs_README.md b/{{ cookiecutter.project_name }}/docs/repo_docs_README.md similarity index 100% rename from {{ cookiecutter.repo_name }}/docs/repo_docs_README.md rename to {{ cookiecutter.project_name }}/docs/repo_docs_README.md diff --git a/{{ cookiecutter.repo_name }}/docs/user_guide/loading_environment_variables.md b/{{ cookiecutter.project_name }}/docs/user_guide/loading_environment_variables.md similarity index 100% rename from {{ cookiecutter.repo_name }}/docs/user_guide/loading_environment_variables.md rename to {{ cookiecutter.project_name }}/docs/user_guide/loading_environment_variables.md diff --git a/{{ cookiecutter.project_name }}/docs/user_guide/user_guide_README.md b/{{ cookiecutter.project_name }}/docs/user_guide/user_guide_README.md new file mode 100644 index 0000000..112198f --- /dev/null +++ b/{{ cookiecutter.project_name }}/docs/user_guide/user_guide_README.md @@ -0,0 +1,8 @@ +# User guide + +This is the user guide for the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` project. + +```{toctree} +:maxdepth: 2 +./loading_environment_variables.md +``` diff --git a/{{ cookiecutter.repo_name }}/docs/user_guide/using_pytest.md b/{{ cookiecutter.project_name }}/docs/user_guide/using_pytest.md similarity index 100% rename from {{ cookiecutter.repo_name }}/docs/user_guide/using_pytest.md rename to {{ cookiecutter.project_name }}/docs/user_guide/using_pytest.md diff --git a/{{ cookiecutter.project_name }}/pyproject.toml b/{{ cookiecutter.project_name }}/pyproject.toml new file mode 100644 index 0000000..5b63ddb --- /dev/null +++ b/{{ cookiecutter.project_name }}/pyproject.toml @@ -0,0 +1,30 @@ +[project] +name = "{{ cookiecutter.project_name.lower().replace(' ', '-').replace('_', '-') }}" +description = "A placeholder description for your python project" +version = "0.0.1" +authors = [ + {"name" = "{{ cookiecutter.organisation_handle }}"} +] +requires-python = ">=3.9" +classifiers = [ + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" +] +dependencies = [ + "pyyaml" +] + +[project.optional-dependencies] +dev = [ + "coverage", + "myst-parser", + "pytest", + "python-dotenv", + "Sphinx", + "toml" +] + +[tool.setuptools] +zip-safe = false diff --git a/{{ cookiecutter.project_name }}/tests/repo_tests_README.md b/{{ cookiecutter.project_name }}/tests/repo_tests_README.md new file mode 100644 index 0000000..e6651fe --- /dev/null +++ b/{{ cookiecutter.project_name }}/tests/repo_tests_README.md @@ -0,0 +1,3 @@ +# `tests` folder overview + +All tests for the functions defined in the `{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}` folder should be stored here. diff --git a/{{ cookiecutter.repo_name }}/tests/__init__.py b/{{ cookiecutter.project_name }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/__init__.py similarity index 100% rename from {{ cookiecutter.repo_name }}/tests/__init__.py rename to {{ cookiecutter.project_name }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/__init__.py diff --git a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/__init__.py b/{{ cookiecutter.project_name }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/example_module/__init__.py similarity index 100% rename from {{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/__init__.py rename to {{ cookiecutter.project_name }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/example_module/__init__.py diff --git a/{{ cookiecutter.repo_name }}/conftest.py b/{{ cookiecutter.project_name }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/main.py similarity index 100% rename from {{ cookiecutter.repo_name }}/conftest.py rename to {{ cookiecutter.project_name }}/{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}/main.py diff --git a/{{ cookiecutter.repo_name }}/.Rprofile b/{{ cookiecutter.repo_name }}/.Rprofile deleted file mode 100644 index 10c26cf..0000000 --- a/{{ cookiecutter.repo_name }}/.Rprofile +++ /dev/null @@ -1 +0,0 @@ -source('startup.R') diff --git a/{{ cookiecutter.repo_name }}/.env b/{{ cookiecutter.repo_name }}/.env deleted file mode 100644 index d886609..0000000 --- a/{{ cookiecutter.repo_name }}/.env +++ /dev/null @@ -1,33 +0,0 @@ -# Environment variables go here, and can be read in by Python using the `python-dotenv` -# package, and `os.getenv`: -# -# ------------------------------------------------------------------------------------ -# from dotenv import load_dotenv -# import os -# -# # Load the environment variables from the `.env` file, overriding any system -# # environment variables -# load_dotenv(override=True) -# -# # Load secrets from the `.secrets` file, overriding any system environment variables -# load_dotenv(".secrets", override=True) -# -# # Example variable -# EXAMPLE_VARIABLE = os.getenv("EXAMPLE_VARIABLE") -# ------------------------------------------------------------------------------------ -# -# For folder/file path environment variables, use relative paths. -# -# DO NOT STORE SECRETS HERE - this file is version-controlled! You should store secrets -# in the untracked `.secrets` file. - -# Add environment variables for the `docs` directory -DIR_DOCS=./docs - -# Add environment variable for package directory - -# Add environment variables for the `src` directories -DIR_COOKIECUTTER.REPO_NAME.LOWER().REPLACE('','_').REPLACE('-','_') =./{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }} - -# Add environment variables for the `tests` directory -DIR_TESTS=./tests diff --git a/{{ cookiecutter.repo_name }}/.envrc b/{{ cookiecutter.repo_name }}/.envrc deleted file mode 100644 index 10ddb6e..0000000 --- a/{{ cookiecutter.repo_name }}/.envrc +++ /dev/null @@ -1,23 +0,0 @@ -# Orchestration file to load environment variables from the `.env` and `.secrets` files. -# -# Only used by systems with `direnv` (https://direnv.net/) installed. Environment -# variables can be read in by Python using `os.getenv` _without_ using `python-dotenv`: -# -# ------------------------------------------------------------------------------------ -# import os -# -# # Example variable -# EXAMPLE_VARIABLE = os.getenv("EXAMPLE_VARIABLE") -# ------------------------------------------------------------------------------------ -# -# DO NOT STORE SECRETS HERE - this file is version-controlled! You should store secrets -# in the untracked `.secrets` file. This is loaded here using the `dotenv_if_exists` -# command. - -# Add the working directory to `PYTHONPATH`; allows Jupyter notebooks in the -# `notebooks` folder to import `src` -export PYTHONPATH="$PYTHONPATH:$(pwd)" - -# Load the `.env` file, and `.secrets` (if it exists) -dotenv .env -dotenv_if_exists .secrets diff --git a/{{ cookiecutter.repo_name }}/.flake8 b/{{ cookiecutter.repo_name }}/.flake8 deleted file mode 100644 index e9bfa57..0000000 --- a/{{ cookiecutter.repo_name }}/.flake8 +++ /dev/null @@ -1,9 +0,0 @@ -[flake8] -# Rule definitions: http://flake8.pycqa.org/en/latest/user/error-codes.html -# D203: 1 blank line required before class docstring -# W503: line break before binary operator -exclude = venv*,__pycache__,node_modules,bower_components,migrations -ignore = D203,W503 -max-complexity = 9 -max-line-length = 88 -extend-ignore = E203 diff --git a/{{ cookiecutter.repo_name }}/.govcookiecutter/manifest.json b/{{ cookiecutter.repo_name }}/.govcookiecutter/manifest.json deleted file mode 100644 index 33a087c..0000000 --- a/{{ cookiecutter.repo_name }}/.govcookiecutter/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "features": [ - { - "name": "R files", - "description": "R files that are removed if not enabled.", - "remove": {% if cookiecutter.using_R == "No" %}true{% else %}false{% endif %}, - "resources": [".Rprofile", ".lintr", "DESCRIPTION", "startup.R"] - } - ] -} diff --git a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/request_template.md b/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/request_template.md deleted file mode 100644 index a6fc3c0..0000000 --- a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/GDS/request_template.md +++ /dev/null @@ -1,34 +0,0 @@ -# Summary - -Add your summary here - keep it brief, to the point, and in plain English. [For further -information about pull requests, check out the GDS -Way](https://gds-way.cloudapps.digital/standards/pull-requests.html). - -# Checklists - - - -This pull/merge request meets the following requirements: - -- [ ] code runs -- [ ] [developments are ethical][data-ethics-framework] and secure -- [ ] you have made proportionate checks that the code works correctly -- [ ] test suite passes -- [ ] developments adhere to AQA plan (see `docs/aqa/aqa_plan.md`) -- [ ] data log updated (see `docs/aqa/data_log.md`), if necessary -- [ ] assumptions, and caveats log updated (see `docs/aqa/assumptions_caveats.md`), if - necessary -- [ ] [minimum usable documentation][agilemodeling] written in the `docs` folder - -Comments have been added below around the incomplete checks. - -[agilemodeling]: http://agilemodeling.com/essays/documentLate.htm -[data-ethics-framework]: https://www.gov.uk/government/publications/data-ethics-framework diff --git a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/framework_README.md b/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/framework_README.md deleted file mode 100644 index 4e3725b..0000000 --- a/{{ cookiecutter.repo_name }}/.govcookiecutter/organisational_frameworks/framework_README.md +++ /dev/null @@ -1,31 +0,0 @@ -# `organisation_frameworks` folder - -This folder contains all organisation-specific frameworks. Each sub-folder is named for -an organisation, and the name must be listed in the `organisational_framework` variable -in the root-level `cookiecutter.json` file to be used; see the `GDS` folder for an -example framework structure. - -## Analytical quality assurance (AQA) framework - -Within each organisation folder, there must be a `aqa` folder that contains templates -for the: - -- analytical quality assurance (AQA) plan; -- data log; and -- assumptions and caveats log. - -It must also include a `README.md` to link the AQA templates together using in -[MyST syntax][myst-parser]. - -## Pull or merge request template - -A `request_template.md` file must be included in each organisation folder. This will be -used to create a pull (GitHub) or merge (GitLab) request template in the project -structure created by `govcookiecutter`. - -You should include hints, and nudges to make sure contributors to your organisation's -projects follow its AQA framework. - -[aqua-book]: https://www.gov.uk/government/publications/the-aqua-book-guidance-on-producing-quality-analysis-for-government -[aqua-book-resources]: https://www.gov.uk/government/collections/aqua-book-resources -[myst-parser]: https://myst-parser.readthedocs.io/ diff --git a/{{ cookiecutter.repo_name }}/.lintr b/{{ cookiecutter.repo_name }}/.lintr deleted file mode 100644 index 86fcfe6..0000000 --- a/{{ cookiecutter.repo_name }}/.lintr +++ /dev/null @@ -1,4 +0,0 @@ -linters: linters_with_defaults( - line_length_linter = line_length_linter(120), - commented_code_linter = NULL - ) diff --git a/{{ cookiecutter.repo_name }}/.pre-commit-config.yaml b/{{ cookiecutter.repo_name }}/.pre-commit-config.yaml deleted file mode 100644 index 5adcfa8..0000000 --- a/{{ cookiecutter.repo_name }}/.pre-commit-config.yaml +++ /dev/null @@ -1,184 +0,0 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks -repos: -{% if cookiecutter.locked_down_environment == "No" %} - - repo: https://github.com/kynan/nbstripout - rev: 0.8.1 - hooks: - - id: nbstripout - name: nbstripout - Strip outputs from notebooks (auto-fixes) - args: - - --extra-keys - - "metadata.colab metadata.kernelspec cell.metadata.colab cell.metadata.executionInfo cell.metadata.id cell.metadata.outputId" - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: check-added-large-files - name: Check for files larger than 5 MB - args: [ "--maxkb=5120" ] - - id: end-of-file-fixer - name: Check for a blank line at the end of scripts (auto-fixes) - exclude: '\.Rd' - - id: trailing-whitespace - name: Check for trailing whitespaces (auto-fixes) - - repo: https://github.com/pycqa/isort - rev: 6.0.1 - hooks: - - id: isort - name: isort - Sort python imports (auto-fixes) - args: [ "--profile", "black", "--filter-files" ] - - id: isort - name: isort - Sort cython imports (auto-fixes) - types: [cython] - args: [ "--profile", "black", "--filter-files" ] - - id: isort - name: isort - Sort pyi imports (auto-fixes) - types: [pyi] - args: [ "--profile", "black", "--filter-files" ] - - repo: https://github.com/psf/black - rev: 25.1.0 # Replace by any tag/version: https://github.com/psf/black/tags - hooks: - - id: black - name: black - consistent Python code formatting (auto-fixes) - language_version: python # Should be a command that runs python3.6+ - - repo: https://github.com/PyCQA/flake8 - rev: 7.1.2 - hooks: - - id: flake8 - name: flake8 - Python linting - - repo: https://github.com/nbQA-dev/nbQA - rev: 0.12.0 - hooks: - - id: nbqa-isort - name: nbqa-isort - Sort Python imports (notebooks; auto-fixes) - args: [ --nbqa-mutate ] - additional_dependencies: [ isort==5.8.0 ] - - id: nbqa-black - name: nbqa-black - consistent Python code formatting (notebooks; auto-fixes) - args: [ --nbqa-mutate ] - additional_dependencies: [ black==21.5b2 ] - # TODO: Disabled for now until it's clear how to add noqa to specific cells of a Jupyter notebook - #- id: nbqa-flake8 - # name: nbqa-flake8 - Python linting (notebooks) - # additional_dependencies: [ flake8==3.9.2 ] - - repo: https://github.com/Yelp/detect-secrets - rev: v1.5.0 - hooks: - - id: detect-secrets - name: detect-secrets - Detect secrets in staged code - args: [ "--baseline", ".secrets.baseline" ] - exclude: .*/tests/.*|^\.cruft\.json$ - - repo: https://github.com/PyCQA/bandit - rev: 1.8.3 - hooks: - - id: bandit - name: bandit - Checks for vulnerabilities - args: ["-c", "pyproject.toml"] - additional_dependencies: ["bandit[toml]"] -{% endif -%} -{% if cookiecutter.using_R == "Yes" %} - # R specific hooks: https://github.com/lorenzwalthert/precommit - - repo: https://github.com/lorenzwalthert/precommit - rev: v0.1.3 - hooks: - - id: style-files - name: Style files using styler - args: [ --style_pkg=styler, --style_fun=tidyverse_style ] - - id: roxygenize - name: Run roxygen2::roxygenize() - # codemeta must be above use-tidy-description when both are used - # - id: codemeta-description-updated - - id: use-tidy-description - name: Run sethis::use_tidy_description() - - id: lintr - name: Run lintr::linters_with_defaults() - exclude: renv/activate.R - - id: readme-rmd-rendered - name: Check README.Rmd has been rendered to README.md - - id: parsable-R - name: Check for valid R code using the parse() function - - id: no-browser-statement - name: Check for accidential browser() statements -{% endif -%} -{% if cookiecutter.locked_down_environment == "Yes" %} -- repo: local - hooks: - - id: nbstripout - name: nbstripout - Strip outputs from notebooks (auto-fixes) - entry: nbstripout - language: system - args: - - --extra-keys - - "metadata.colab metadata.kernelspec cell.metadata.colab cell.metadata.executionInfo cell.metadata.id cell.metadata.outputId" - - - id: check-added-large-files - name: Check for files larger than 5 MB - entry: check-added-large-files - language: system - args: - - "--maxkb=5120" - - - id: end-of-file-fixer - name: Check for a blank line at the end of scripts (auto-fixes) - entry: end-of-file-fixer - language: system - exclude: '\.Rd' - - - id: trailing-whitespace - name: Check for trailing whitespaces (auto-fixes) - entry: trailing-whitespace-fixer - language: system - - - id: isort - name: isort - Sort python imports (auto-fixes) - entry: isort - language: system - args: - - "--profile" - - "black" - - "--filter-files" - - - id: black - name: black - consistent Python code formatting (auto-fixes) - entry: black - language: system - types: [python] - - - id: flake8 - name: flake8 - Python linting - entry: flake8 - language: system - types: [python] - - - id: nbqa-isort - name: nbqa-isort - Sort Python imports (notebooks; auto-fixes) - entry: nbqa - language: system - args: - - isort - - --nbqa-mutate - - - id: nbqa-black - name: nbqa-black - consistent Python code formatting (notebooks; auto-fixes) - entry: nbqa - language: system - types: [python] - args: - - black - - --nbqa-mutate - - - id: detect-secrets - name: detect-secrets - Detect secrets in staged code - entry: detect-secrets-hook - language: system - exclude: .*/tests/.*|^\.cruft\.json$ - - - id: bandit - name: bandit - Checks for vulnerabilities - entry: bandit - language: system - args: - - "-c" - - "pyproject.toml" - -{% endif -%} diff --git a/{{ cookiecutter.repo_name }}/.secrets b/{{ cookiecutter.repo_name }}/.secrets deleted file mode 100644 index 60bc85d..0000000 --- a/{{ cookiecutter.repo_name }}/.secrets +++ /dev/null @@ -1,20 +0,0 @@ -# Secrets and credentials should be stored here as environmental variables. For example: -# -# # Google Cloud authentication credentials -# GOOGLE_APPLICATION_CREDENTIALS=path/to/credentials.json -# -# These environment variables can then be read in by Python using the `python-dotenv` -# package, and `os.getenv`: -# -# ------------------------------------------------------------------------------------ -# from dotenv import load_dotenv -# import os -# -# # Load secrets from the `.secrets` file, overriding any system environment variables -# load_dotenv(".secrets", override=True) -# -# # Google Cloud authentication credentials -# GOOGLE_APPLICATION_CREDENTIALS = os.getenv("GOOGLE_APPLICATION_CREDENTIALS") -# ------------------------------------------------------------------------------------ -# -# This file is NOT version-controlled! diff --git a/{{ cookiecutter.repo_name }}/.secrets.baseline b/{{ cookiecutter.repo_name }}/.secrets.baseline deleted file mode 100644 index 58012f6..0000000 --- a/{{ cookiecutter.repo_name }}/.secrets.baseline +++ /dev/null @@ -1,94 +0,0 @@ -{ - "version": "1.0.3", - "plugins_used": [ - { - "name": "ArtifactoryDetector" - }, - { - "name": "AWSKeyDetector" - }, - { - "name": "AzureStorageKeyDetector" - }, - { - "name": "Base64HighEntropyString", - "limit": 4.5 - }, - { - "name": "BasicAuthDetector" - }, - { - "name": "CloudantDetector" - }, - { - "name": "HexHighEntropyString", - "limit": 3.0 - }, - { - "name": "IbmCloudIamDetector" - }, - { - "name": "IbmCosHmacDetector" - }, - { - "name": "JwtTokenDetector" - }, - { - "name": "KeywordDetector", - "keyword_exclude": "" - }, - { - "name": "MailchimpDetector" - }, - { - "name": "NpmDetector" - }, - { - "name": "PrivateKeyDetector" - }, - { - "name": "SlackDetector" - }, - { - "name": "SoftlayerDetector" - }, - { - "name": "SquareOAuthDetector" - }, - { - "name": "StripeDetector" - }, - { - "name": "TwilioKeyDetector" - } - ], - "filters_used": [ - { - "path": "detect_secrets.filters.allowlist.is_line_allowlisted" - }, - { - "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", - "min_level": 2 - }, - { - "path": "detect_secrets.filters.heuristic.is_indirect_reference" - }, - { - "path": "detect_secrets.filters.heuristic.is_likely_id_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_potential_uuid" - }, - { - "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" - }, - { - "path": "detect_secrets.filters.heuristic.is_sequential_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_templated_secret" - } - ], - "results": {}, - "generated_at": "2021-06-14T10:43:14Z" -} diff --git a/{{ cookiecutter.repo_name }}/CONTRIBUTING.md b/{{ cookiecutter.repo_name }}/CONTRIBUTING.md deleted file mode 100644 index 46ca528..0000000 --- a/{{ cookiecutter.repo_name }}/CONTRIBUTING.md +++ /dev/null @@ -1,6 +0,0 @@ -# Contributing - -[Our contributing guidelines can be found at -`docs/contributor_guide/CONTRIBUTING.md`][contributing]. - -[contributing]: ./docs/contributor_guide/CONTRIBUTING.md diff --git a/{{ cookiecutter.repo_name }}/DESCRIPTION b/{{ cookiecutter.repo_name }}/DESCRIPTION deleted file mode 100644 index c6d3568..0000000 --- a/{{ cookiecutter.repo_name }}/DESCRIPTION +++ /dev/null @@ -1,18 +0,0 @@ -Package: {{ cookiecutter.repo_name }} -Title: {{ cookiecutter.project_name }} -Version: {{ cookiecutter.project_version }} -Authors@R: - person("{{ cookiecutter.organisation_handle }}", , , "organisation@email.address", role = c("aut", "cre")) -Description: {{ cookiecutter.overview }} -License: MIT + file LICENSE -Imports: - docopt, - git2r, - lintr, - styler, - usethis -Encoding: UTF-8 -Language: en-GB -LazyData: true -Roxygen: list(markdown = TRUE) -RoxygenNote: 7.0.0 diff --git a/{{ cookiecutter.repo_name }}/Makefile b/{{ cookiecutter.repo_name }}/Makefile deleted file mode 100644 index 24203d8..0000000 --- a/{{ cookiecutter.repo_name }}/Makefile +++ /dev/null @@ -1,88 +0,0 @@ -.PHONY: - coverage - coverage_html - coverage_xml - docs - docs_check_external_links - help - install - install_dev - prepare_docs_folder - -.DEFAULT_GOAL := help - -## Install the Python package for contributors, and install pre-commit hooks -install_dev: - python -m pip install -U pip setuptools - python -m pip install -e .[dev] - pre-commit install - -## Install the Python package for users -install: - python -m pip install -U pip setuptools - python -m pip install -e . - -## Create a `docs/_build` folder, if it does not exist. Otherwise delete any sub-folders and their contents within it -prepare_docs_folder: - if [ ! -d "./docs/_build" ]; then mkdir ./docs/_build; fi - find ./docs/_build -mindepth 1 -maxdepth 1 -type d -exec rm -rf {} \; - -## Compile the Sphinx documentation in HTML format in the docs/_build folder from a clean build -docs: prepare_docs_folder install_dev - sphinx-build -b html ./docs ./docs/_build - -## Check external links in the Sphinx documentation using linkcheck in the docs/_build folder from a clean build -docs_check_external_links: prepare_docs_folder install_dev - sphinx-build -b linkcheck ./docs ./docs/_build - -## Run code coverage -coverage: install_dev - coverage run -m pytest - -## Run code coverage, and produce a HTML output -coverage_html: coverage - coverage html - -## Run code coverage, and produce an XML output -coverage_xml: coverage - coverage xml - -## Get help on all make commands; referenced from https://github.com/drivendata/cookiecutter-data-science -help: - @echo "$$(tput bold)Available rules:$$(tput sgr0)" - @echo - @sed -n -e "/^## / { \ - h; \ - s/.*//; \ - :doc" \ - -e "H; \ - n; \ - s/^## //; \ - t doc" \ - -e "s/:.*//; \ - G; \ - s/\\n## /---/; \ - s/\\n/ /g; \ - p; \ - }" ${MAKEFILE_LIST} \ - | LC_ALL='C' sort --ignore-case \ - | awk -F '---' \ - -v ncol=$$(tput cols) \ - -v indent=25 \ - -v col_on="$$(tput setaf 6)" \ - -v col_off="$$(tput sgr0)" \ - '{ \ - printf "%s%*s%s ", col_on, -indent, $$1, col_off; \ - n = split($$2, words, " "); \ - line_length = ncol - indent; \ - for (i = 1; i <= n; i++) { \ - line_length -= length(words[i]) + 1; \ - if (line_length <= 0) { \ - line_length = ncol - indent - length(words[i]) - 1; \ - printf "\n%*s ", -indent, " "; \ - } \ - printf "%s ", words[i]; \ - } \ - printf "\n"; \ - }' \ - | more $(shell test $(shell uname) = Darwin && echo '--no-init --raw-control-chars') diff --git a/{{ cookiecutter.repo_name }}/docs/aqa/aqa_plan.md b/{{ cookiecutter.repo_name }}/docs/aqa/aqa_plan.md deleted file mode 100644 index 3fa183d..0000000 --- a/{{ cookiecutter.repo_name }}/docs/aqa/aqa_plan.md +++ /dev/null @@ -1,12 +0,0 @@ -# Analytical quality assurance plan - -This analytical quality assurance (AQA) plan outlines [our implementation of the -Aqua Book][aqua-book] for this project. [Further resources related to the Aqua Book -are also available on GOV.UK][aqua-book-resources]. - -This is a living document, and should be updated and/or modified as necessary. For -example if new tasks not listed here become relevant to project success, please add -them to this plan. - -[aqua-book]: https://www.gov.uk/government/publications/the-aqua-book-guidance-on-producing-quality-analysis-for-government -[aqua-book-resources]: https://www.gov.uk/government/collections/aqua-book-resources diff --git a/{{ cookiecutter.repo_name }}/docs/aqa/assumptions_caveats.md b/{{ cookiecutter.repo_name }}/docs/aqa/assumptions_caveats.md deleted file mode 100644 index 2ec9d5c..0000000 --- a/{{ cookiecutter.repo_name }}/docs/aqa/assumptions_caveats.md +++ /dev/null @@ -1,3 +0,0 @@ -# Assumptions and caveats log - -This log contains a list of assumptions and caveats used in this analysis. diff --git a/{{ cookiecutter.repo_name }}/docs/aqa/data_log.md b/{{ cookiecutter.repo_name }}/docs/aqa/data_log.md deleted file mode 100644 index a73f169..0000000 --- a/{{ cookiecutter.repo_name }}/docs/aqa/data_log.md +++ /dev/null @@ -1,3 +0,0 @@ -# Data log - -This log contains a list of data sources used in this analysis. diff --git a/{{ cookiecutter.repo_name }}/docs/contributor_guide/contributor_README.md b/{{ cookiecutter.repo_name }}/docs/contributor_guide/contributor_README.md deleted file mode 100644 index 2583bac..0000000 --- a/{{ cookiecutter.repo_name }}/docs/contributor_guide/contributor_README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Contributing guide - -This is the contributor guide for the `{{ cookiecutter.repo_name }}` project. - -```{toctree} -:maxdepth: 2 -./CODE_OF_CONDUCT.md -./CONTRIBUTING.md -``` diff --git a/{{ cookiecutter.repo_name }}/docs/user_guide/user_guide_README.md b/{{ cookiecutter.repo_name }}/docs/user_guide/user_guide_README.md deleted file mode 100644 index bb0f140..0000000 --- a/{{ cookiecutter.repo_name }}/docs/user_guide/user_guide_README.md +++ /dev/null @@ -1,8 +0,0 @@ -# User guide - -This is the user guide for the `{{ cookiecutter.repo_name }}` project. - -```{toctree} -:maxdepth: 2 -./loading_environment_variables.md -``` diff --git a/{{ cookiecutter.repo_name }}/make.bat b/{{ cookiecutter.repo_name }}/make.bat deleted file mode 100644 index 6f79204..0000000 --- a/{{ cookiecutter.repo_name }}/make.bat +++ /dev/null @@ -1,72 +0,0 @@ -@echo off - - -IF /I "%1"=="" GOTO .DEFAULT_GOAL -IF /I "%1"=="install" GOTO install -IF /I "%1"=="install_dev" GOTO install_dev -IF /I "%1"=="prepare_docs_folder" GOTO prepare_docs_folder -IF /I "%1"=="docs" GOTO docs -IF /I "%1"=="docs_check_external_links" GOTO docs_check_external_links -IF /I "%1"=="coverage" GOTO coverage -IF /I "%1"=="coverage_html" GOTO coverage_html -IF /I "%1"=="coverage_xml" GOTO coverage_xml -IF /I "%1"=="help" GOTO help -GOTO error - -:.DEFAULT_GOAL - GOTO help - -:install_dev - python -m pip install -U pip setuptools - python -m pip install -e .[dev] - pre-commit install - GOTO :EOF - -:install - python -m pip install -U pip setuptools - python -m pip install -e . - GOTO :EOF - -:prepare_docs_folder - IF exist "./docs/_build" ( rmdir /s /q "./docs/_build/" ) - mkdir ".\docs\_build" - GOTO :EOF - -:docs - CALL make.bat prepare_docs_folder - CALL make.bat install_dev - sphinx-build -b html ./docs ./docs/_build - GOTO :EOF - -:docs_check_external_links - CALL make.bat prepare_docs_folder - CALL make.bat install_dev - sphinx-build -b linkcheck ./docs ./docs/_build - GOTO :EOF - -:coverage - CALL make.bat install_dev - coverage run -m pytest - GOTO :EOF - -:coverage_html - CALL make.bat coverage - coverage html - GOTO :EOF - -:coverage_xml - CALL make.bat coverage - coverage xml - GOTO :EOF - -:help - ECHO make: Use one of the following commands: install, install_dev, docs, docs_check_external_links, coverage, coverage_html, coverage_xml. - GOTO :EOF - -:error - IF "%1"=="" ( - ECHO make: *** No targets specified and no makefile found. Stop. - ) ELSE ( - ECHO make: *** No rule to make target '%1%'. Stop. - ) - GOTO :EOF diff --git a/{{ cookiecutter.repo_name }}/pyproject.toml b/{{ cookiecutter.repo_name }}/pyproject.toml deleted file mode 100644 index 64b919e..0000000 --- a/{{ cookiecutter.repo_name }}/pyproject.toml +++ /dev/null @@ -1,30 +0,0 @@ -# `coverage` configurations -[tool.coverage.run] -source = [ - "./" -] - -[tool.coverage.report] -exclude_lines = [ - "if __name__ == .__main__.:" -] - -# `isort` configurations -[tool.isort] -profile = "black" - -# `pytest` configurations -[tool.pytest.ini_options] -addopts = [ - "-vv", - "--doctest-modules" -] -doctest_optionflags = "NORMALIZE_WHITESPACE" -testpaths = [ - "./tests" -] - -# `bandit' configurations -[tool.bandit] -exclude_dirs = ["tests", "docs"] -skips = [] diff --git a/{{ cookiecutter.repo_name }}/setup.cfg b/{{ cookiecutter.repo_name }}/setup.cfg deleted file mode 100644 index 22a8a56..0000000 --- a/{{ cookiecutter.repo_name }}/setup.cfg +++ /dev/null @@ -1,38 +0,0 @@ -[metadata] -name = {{ cookiecutter.repo_name }} -description = {{ cookiecutter.overview }} -version = {{ cookiecutter.project_version }} -author = {{ cookiecutter.organisation_handle }} -platforms = win32 -classifiers = - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - -[options] -packages = - {{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }} -python_requires = >=3.9 -zip_safe = no -install_requires = - pyyaml - -[options.extras_require] -dev = - coverage - detect-secrets == 1.0.3 - myst-parser - pre-commit - pytest - detect-secrets - python-dotenv - Sphinx - toml {% if cookiecutter.locked_down_environment == "Yes" %} - pre-commit-hooks - nbstripout - isort - black - flake8 - nbqa - bandit {% endif -%} diff --git a/{{ cookiecutter.repo_name }}/setup.py b/{{ cookiecutter.repo_name }}/setup.py deleted file mode 100644 index 7f1a176..0000000 --- a/{{ cookiecutter.repo_name }}/setup.py +++ /dev/null @@ -1,4 +0,0 @@ -from setuptools import setup - -if __name__ == "__main__": - setup() diff --git a/{{ cookiecutter.repo_name }}/startup.R b/{{ cookiecutter.repo_name }}/startup.R deleted file mode 100644 index c4a19de..0000000 --- a/{{ cookiecutter.repo_name }}/startup.R +++ /dev/null @@ -1,7 +0,0 @@ -if (length(grep(pattern = "devtools", x = utils::installed.packages()[, 1])) == 0) { - Sys.setenv(R_PROFILE_USER = "/dev/null") - utils::install.packages(pkgs = "devtools", Ncpus = 1, repos = "https://CRAN.R-project.org") - # install packages from DESCRIPTION file - devtools::install() - Sys.unsetenv("R_PROFILE_USER") -} diff --git a/{{ cookiecutter.repo_name }}/tests/repo_tests_README.md b/{{ cookiecutter.repo_name }}/tests/repo_tests_README.md deleted file mode 100644 index 6ec5b07..0000000 --- a/{{ cookiecutter.repo_name }}/tests/repo_tests_README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `tests` folder overview - -All tests for the functions defined in the `{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}` folder should be stored here. diff --git a/{{ cookiecutter.repo_name }}/tests/test_example_module.py b/{{ cookiecutter.repo_name }}/tests/test_example_module.py deleted file mode 100644 index a480ae6..0000000 --- a/{{ cookiecutter.repo_name }}/tests/test_example_module.py +++ /dev/null @@ -1,54 +0,0 @@ -import pytest - -from {{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}.example_modules.example_module import ( - hello_world, - print_favourite_number, - print_string, -) - - -class TestHelloWorld: - def test_string_concat(self): - assert hello_world("Bob", "ONS") == "Hello Bob and hello everyone at ONS" - - def test_name_type_error(self): - with pytest.raises(TypeError): - hello_world(1, "ONS") - - def test_company_type_error(self): - with pytest.raises(TypeError): - hello_world("Bob", 1) - - def test_name_and_company_type_error(self): - with pytest.raises(TypeError): - hello_world(1, 2) - - -class TestPrintString: - def test_print_statements(self, capsys): - print_string("Hello world and hello ONS") - captured = capsys.readouterr() - assert captured.out == "Hello world and hello ONS\n" - - def test_type_error(self): - with pytest.raises(TypeError): - print_string(1) - - -class TestPrintFavouriteNumber: - def test_string_concat(self, capsys): - print_favourite_number(7, "Bob") - captured = capsys.readouterr() - assert captured.out == "Bob's favourite number is 7!\n" - - def test_name_type_error(self): - with pytest.raises(TypeError): - print_favourite_number("7", "Bob") - - def test_company_type_error(self): - with pytest.raises(TypeError): - print_favourite_number(7, 1) - - def test_name_and_company_type_error(self): - with pytest.raises(TypeError): - print_favourite_number("1", 2) diff --git a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_config.yml b/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_config.yml deleted file mode 100644 index 7acc3b8..0000000 --- a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_config.yml +++ /dev/null @@ -1,3 +0,0 @@ -user_name: "John Stat" -company: "ONS" -favourite_number: 42 diff --git a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_modules/__init__.py b/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_modules/example_module.py b/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_modules/example_module.py deleted file mode 100644 index 7ec4165..0000000 --- a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_modules/example_module.py +++ /dev/null @@ -1,92 +0,0 @@ -def hello_world(name, company): - """This function returns a welcoming string given and company - and a persons name. - - Parameters - ---------- - name : str - The name you would like to say hello to - company : str - The company name to say hello to - - Returns - ------- - str - Complete string greating a person and the company - - Raises - ------ - TypeError - name is not a str - TypeError - company is not a str - TypeError - neither company or name are str variables - """ - if isinstance(name, str) and isinstance(company, str): - string = "Hello " + name + " and hello everyone at " + company - return string - - elif not isinstance(name, str): - raise TypeError("The 'name' variable entered was not a string") - - elif not isinstance(company, str): - raise TypeError("The 'company' variable entered was not a string") - - else: - raise TypeError("The 'name' and 'company' variables entered were not strings") - - -def print_string(string): - """function that prints a string after validating the input - - Parameters - ---------- - string : str - string to print ouy - - Raises - ------ - TypeError - If 'string' variable is not a str - """ - if isinstance(string, str): - print(string) - else: - raise TypeError("The 'string' variable entered was not a string") - - -def print_favourite_number(favourite_number, name): - """Function to print sentence of a name and a person favourite number - - Parameters - ---------- - favourite_number : int - Integer number to print - name : str - Person name to print - - Raises - ------ - TypeError - The name variable is not a str - TypeError - The favourite_number variable is not an int - TypeError - Both the name and favourite_number varibles are not str and int - respectively. - """ - if isinstance(name, str) and isinstance(favourite_number, int): - string = name + "'s favourite number is " + str(favourite_number) + "!" - print(string) - - elif not isinstance(name, str): - raise TypeError("The 'name' variable entered was not a string") - - elif not isinstance(favourite_number, int): - raise TypeError("The 'favourite_number' variable entered was not an integer") - - else: - raise TypeError( - "The 'name' and 'favourite_number' variables have incorrect types" - ) diff --git a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/run_pipeline.py b/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/run_pipeline.py deleted file mode 100644 index 7b86d55..0000000 --- a/{{ cookiecutter.repo_name }}/{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/run_pipeline.py +++ /dev/null @@ -1,25 +0,0 @@ -import yaml - -from {{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}.example_modules.example_module import ( - hello_world, - print_favourite_number, - print_string, -) - - -def run_pipeline(): - """This is the main function that runs the pipeline""" - with open("{{ cookiecutter.repo_name.lower().replace(' ', '_').replace('-', '_') }}/example_config.yml", "r") as file: - example_config = yaml.safe_load(file) - - name = example_config["user_name"] - company = example_config["company"] - favourite_number = example_config["favourite_number"] - - hello_string = hello_world(name, company) - print_string(hello_string) - print_favourite_number(favourite_number, name) - - -if __name__ == "__main__": - run_pipeline()