Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ requirements:

## Format the code using isort and black
format:
isort ccds hooks tests
black ccds hooks tests

## Lint using flake8 + black
isort --profile black ccds hooks tests "{{ cookiecutter.repo_name }}/{{ cookiecutter.module_name }}"
black ccds hooks tests "{{ cookiecutter.repo_name }}/{{ cookiecutter.module_name }}"

lint:
flake8 ccds hooks tests
black --check ccds hooks tests
flake8 ccds hooks tests "{{ cookiecutter.repo_name }}/{{ cookiecutter.module_name }}"
isort --check --profile black ccds hooks tests "{{ cookiecutter.repo_name }}/{{ cookiecutter.module_name }}"
black --check ccds hooks tests "{{ cookiecutter.repo_name }}/{{ cookiecutter.module_name }}"


### DOCS
Expand Down
3 changes: 2 additions & 1 deletion ccds.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@
"none",
"basic"
],
"open_source_license": ["No license file", "MIT", "BSD-3-Clause"]
"open_source_license": ["No license file", "MIT", "BSD-3-Clause"],
"include_code_scaffold": ["Yes", "No"]
}
19 changes: 19 additions & 0 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import shutil
from pathlib import Path

# https://github.com/cookiecutter/cookiecutter/issues/824
Expand All @@ -14,12 +15,21 @@
"isort",
"pip",
"python-dotenv",
"setuptools",
"wheel",
Comment on lines +18 to +19
Copy link
Member

@jayqi jayqi Oct 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrisjkuch why do we need setuptools and wheel? These should be unnecessary for a pyproject.toml-based build (this stuff gets specified in the build section pyproject.toml).

]

# {% if cookiecutter.dataset_storage.s3 %}
packages += ["awscli"]
# {% endif %} #

# {% if cookiecutter.include_code_scaffold == "Yes" %}
packages += [
"typer",
"loguru",
]
# {% endif %}

# {% if cookiecutter.pydata_packages == "basic" %}
packages += [
"ipython",
Expand Down Expand Up @@ -59,3 +69,12 @@
# Jinja tojson escapes single-quotes with \u0027 since it's meant for HTML/JS
pyproject_text = Path("pyproject.toml").read_text()
Path("pyproject.toml").write_text(pyproject_text.replace(r"\u0027", "'"))

# {% if cookiecutter.include_code_scaffold == "No" %}
# remove everything except __init__.py so result is an empty package
for generated_path in Path("{{ cookiecutter.module_name }}").iterdir():
if generated_path.is_dir():
shutil.rmtree(generated_path)
elif generated_path.name != "__init__.py":
generated_path.unlink()
# {% endif %}
6 changes: 5 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,19 @@ def _is_valid(config):

# remove invalid configs
configs = [c for c in configs if _is_valid(c)]
include_code_scaffold = True

for ind, c in enumerate(configs):
config = dict(c)
config.update(default_args)
# Alternate including the code scaffold
config["include_code_scaffold"] = "Yes" if include_code_scaffold else "No"
include_code_scaffold = not include_code_scaffold
config["repo_name"] += f"-{ind}"
yield config

# just do a single config if fast passed once or three times
if fast in [1, 3]:
if fast == 1 or fast >= 3:
break


Expand Down
35 changes: 21 additions & 14 deletions tests/test_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from conftest import bake_project


BASH_EXECUTABLE = os.getenv("BASH_EXECUTABLE", "bash")


Expand Down Expand Up @@ -51,12 +50,16 @@ def verify_folders(root, config):
"reports",
"reports/figures",
config["module_name"],
f"{config['module_name']}/data",
f"{config['module_name']}/features",
f"{config['module_name']}/models",
f"{config['module_name']}/visualization",
]

if config["include_code_scaffold"] == "Yes":
expected_dirs += [
f"{config['module_name']}/data",
f"{config['module_name']}/features",
f"{config['module_name']}/models",
f"{config['module_name']}/visualization",
]

expected_dirs = [
# (root / d).resolve().relative_to(root) for d in expected_dirs
Path(d)
Expand Down Expand Up @@ -95,21 +98,25 @@ def verify_files(root, config):
"reports/figures/.gitkeep",
"models/.gitkeep",
f"{config['module_name']}/__init__.py",
f"{config['module_name']}/data/__init__.py",
f"{config['module_name']}/data/make_dataset.py",
f"{config['module_name']}/features/__init__.py",
f"{config['module_name']}/features/build_features.py",
f"{config['module_name']}/models/__init__.py",
f"{config['module_name']}/models/train_model.py",
f"{config['module_name']}/models/predict_model.py",
f"{config['module_name']}/visualization/__init__.py",
f"{config['module_name']}/visualization/visualize.py",
]

# conditional files
if not config["open_source_license"].startswith("No license"):
expected_files.append("LICENSE")

if config["include_code_scaffold"] == "Yes":
expected_files += [
f"{config['module_name']}/data/__init__.py",
f"{config['module_name']}/data/make_dataset.py",
f"{config['module_name']}/features/__init__.py",
f"{config['module_name']}/features/build_features.py",
f"{config['module_name']}/models/__init__.py",
f"{config['module_name']}/models/train_model.py",
f"{config['module_name']}/models/predict_model.py",
f"{config['module_name']}/visualization/__init__.py",
f"{config['module_name']}/visualization/visualize.py",
]

expected_files.append(config["dependency_file"])

expected_files = [Path(f) for f in expected_files]
Expand Down
68 changes: 14 additions & 54 deletions {{ cookiecutter.repo_name }}/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# GLOBALS #
#################################################################################

PROJECT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
PROJECT_NAME = {{ cookiecutter.repo_name }}
PYTHON_VERSION = {{ cookiecutter.python_version_number }}
PYTHON_INTERPRETER = python
Expand Down Expand Up @@ -97,68 +96,29 @@ create_environment:
# PROJECT RULES #
#################################################################################

{% if cookiecutter.include_code_scaffold == 'Yes' %}
## Make Dataset
.PHONY: data
data: requirements
$(PYTHON_INTERPRETER) {{ cookiecutter.module_name }}/data/make_dataset.py
{% endif %}

#################################################################################
# Self Documenting Commands #
#################################################################################

.DEFAULT_GOAL := help

# Inspired by <http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html>
# sed script explained:
# /^##/:
# * save line in hold space
# * purge line
# * Loop:
# * append newline + line to hold space
# * go to next line
# * if line starts with doc comment, strip comment character off and loop
# * remove target prerequisites
# * append hold space (+ newline) to line
# * replace newline plus comments by `---`
# * print line
# Separate expressions are necessary because labels cannot be delimited by
# semicolon; see <http://stackoverflow.com/a/11799865/1968>
.PHONY: help
define PRINT_HELP_PYSCRIPT
import re, sys

for line in sys.stdin:
match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
if match:
target, help = match.groups()
print("%-20s %s" % (target, help))
endef
export PRINT_HELP_PYSCRIPT

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=19 \
-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')
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
# -*- coding: utf-8 -*-
import click
import logging
from pathlib import Path

import click
from dotenv import find_dotenv, load_dotenv


@click.command()
@click.argument('input_filepath', type=click.Path(exists=True))
@click.argument('output_filepath', type=click.Path())
@click.argument("input_filepath", type=click.Path(exists=True))
@click.argument("output_filepath", type=click.Path())
def main(input_filepath, output_filepath):
""" Runs data processing scripts to turn raw data from (../raw) into
cleaned data ready to be analyzed (saved in ../processed).
"""Runs data processing scripts to turn raw data from (../raw) into
cleaned data ready to be analyzed (saved in ../processed).
"""
logger = logging.getLogger(__name__)
logger.info('making final data set from raw data')
logger.info("making final data set from raw data")


if __name__ == '__main__':
log_fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
if __name__ == "__main__":
log_fmt = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
logging.basicConfig(level=logging.INFO, format=log_fmt)

# not used in this stub but often useful for finding various files
Expand Down