stooge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stooge-0.1.0/LICENSE +21 -0
- stooge-0.1.0/PKG-INFO +152 -0
- stooge-0.1.0/README.md +99 -0
- stooge-0.1.0/pyproject.toml +76 -0
- stooge-0.1.0/setup.cfg +4 -0
- stooge-0.1.0/src/port/__init__.py +11 -0
- stooge-0.1.0/src/port/cli.py +46 -0
- stooge-0.1.0/src/port/console.py +6 -0
- stooge-0.1.0/src/port/constants.py +12 -0
- stooge-0.1.0/src/port/data/project_template/a010_first_script.py +6 -0
- stooge-0.1.0/src/port/data/project_template/local.py +38 -0
- stooge-0.1.0/src/port/data/project_template/utils.py +3 -0
- stooge-0.1.0/src/port/data/spf_example/a010_clean_data.py +21 -0
- stooge-0.1.0/src/port/data/spf_example/a020_calculate.py +25 -0
- stooge-0.1.0/src/port/data/spf_example/a030_visualize.py +38 -0
- stooge-0.1.0/src/port/data/spf_example/local.py +36 -0
- stooge-0.1.0/src/port/data/spf_example/utils.py +20 -0
- stooge-0.1.0/src/port/exceptions.py +11 -0
- stooge-0.1.0/src/port/info.py +24 -0
- stooge-0.1.0/src/port/init.py +53 -0
- stooge-0.1.0/src/port/meta.py +42 -0
- stooge-0.1.0/src/port/project.py +73 -0
- stooge-0.1.0/src/port/utils/__init__.py +213 -0
- stooge-0.1.0/src/port/utils/dag.py +61 -0
- stooge-0.1.0/src/port/utils/parse.py +237 -0
- stooge-0.1.0/src/port/version.py +4 -0
- stooge-0.1.0/src/stooge.egg-info/PKG-INFO +152 -0
- stooge-0.1.0/src/stooge.egg-info/SOURCES.txt +34 -0
- stooge-0.1.0/src/stooge.egg-info/dependency_links.txt +1 -0
- stooge-0.1.0/src/stooge.egg-info/entry_points.txt +2 -0
- stooge-0.1.0/src/stooge.egg-info/requires.txt +14 -0
- stooge-0.1.0/src/stooge.egg-info/top_level.txt +1 -0
- stooge-0.1.0/tests/test_cli.py +36 -0
- stooge-0.1.0/tests/test_info.py +23 -0
- stooge-0.1.0/tests/test_init.py +57 -0
- stooge-0.1.0/tests/test_utils.py +59 -0
stooge-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Derrick Chambers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
stooge-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stooge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple project framework to keep you from getting burned
|
|
5
|
+
Author-email: Derrick Chambers <chambers.ja.derrick@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2023 Derrick Chambers
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Bug Tracker, https://github.com/d-chambers/port/issues
|
|
29
|
+
Project-URL: Documentation, https://github.com/d-chambers/port
|
|
30
|
+
Project-URL: Homepage, https://github.com/d-chambers/port
|
|
31
|
+
Keywords: workflow,projects
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
36
|
+
Classifier: Topic :: Scientific/Engineering
|
|
37
|
+
Requires-Python: >=3.13
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
License-File: LICENSE
|
|
40
|
+
Requires-Dist: typer
|
|
41
|
+
Requires-Dist: rich
|
|
42
|
+
Requires-Dist: jinja2
|
|
43
|
+
Requires-Dist: toml
|
|
44
|
+
Requires-Dist: pdbpp>=0.12.0.post1
|
|
45
|
+
Provides-Extra: test
|
|
46
|
+
Requires-Dist: pytest; extra == "test"
|
|
47
|
+
Requires-Dist: pre-commit; extra == "test"
|
|
48
|
+
Requires-Dist: ruff; extra == "test"
|
|
49
|
+
Requires-Dist: scikit-learn; extra == "test"
|
|
50
|
+
Provides-Extra: dev
|
|
51
|
+
Requires-Dist: stooge[test]; extra == "dev"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# port
|
|
55
|
+
|
|
56
|
+
A simple project framework.
|
|
57
|
+
|
|
58
|
+
`port` facilitates rapid, reproducible development for local research projects.
|
|
59
|
+
|
|
60
|
+
## Project structure
|
|
61
|
+
|
|
62
|
+
port requires a specific project structure.
|
|
63
|
+
|
|
64
|
+
An example project directory looks like this:
|
|
65
|
+
|
|
66
|
+
my_project
|
|
67
|
+
-> inputs/
|
|
68
|
+
---> some_file.csv
|
|
69
|
+
-> outputs/
|
|
70
|
+
-> local.py
|
|
71
|
+
-> environment.yml
|
|
72
|
+
-> a010_clean_data.py
|
|
73
|
+
-> a020_calculate.py
|
|
74
|
+
-> a030_visualize.py
|
|
75
|
+
|
|
76
|
+
Each script has an ID (the part of the name before the first `_`).
|
|
77
|
+
|
|
78
|
+
The file called `local.py` defines the inputs and outputs of the scripts. In this file, outputs are also given the same prefix.
|
|
79
|
+
|
|
80
|
+
For example:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from pathlib import Path
|
|
84
|
+
|
|
85
|
+
input_path = Path("inputs")
|
|
86
|
+
output_path = Path("outputs")
|
|
87
|
+
|
|
88
|
+
raw_data_path = input_path / "raw data.csv"
|
|
89
|
+
|
|
90
|
+
a010_clean_data_path = output_path / "a010_cleaned_outputs.csv"
|
|
91
|
+
a020_calculated_data_path = output_path / "a020_calculated_results.csv"
|
|
92
|
+
a030_result_plot = output_path / "a030_result_plot.png"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This provides port with all the required information to figure out the relationships between scripts and their outputs.
|
|
96
|
+
|
|
97
|
+
## Init
|
|
98
|
+
Creates a port project skeleton.
|
|
99
|
+
|
|
100
|
+
This provides a small Rich interface to get information about the project name and backend, or they can be provided via command-line flags.
|
|
101
|
+
|
|
102
|
+
Supported backends are:
|
|
103
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
104
|
+
- [miniforge](https://github.com/conda-forge/miniforge)
|
|
105
|
+
- [marimo](https://marimo.io/)
|
|
106
|
+
- python (use system python or provided interpreter path)
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
port init test_project --backend uv
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Run
|
|
113
|
+
|
|
114
|
+
`port run` runs the entire project up to and including a target ID. It automatically calculates stale or missing results that `a030` depends on.
|
|
115
|
+
|
|
116
|
+
For example:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
port run a030
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This first gets the directed acyclic graph (DAG) that defines `a030`'s dependencies. It then uses file output mtime values to determine if any results need to be re-run. This includes:
|
|
123
|
+
|
|
124
|
+
- Requiring that, for a given output, all dependency outputs have an mtime less than or equal to the current output.
|
|
125
|
+
- Requiring all outputs to have an mtime greater than or equal to the script that creates them.
|
|
126
|
+
|
|
127
|
+
Flags include:
|
|
128
|
+
- `--force`: rerun the specified task.
|
|
129
|
+
- `--force-all`: rerun all affected tasks.
|
|
130
|
+
- `--debug`: If true, drop into a pdb debugger on failure.
|
|
131
|
+
|
|
132
|
+
## Remove
|
|
133
|
+
|
|
134
|
+
Remove results from specified tasks.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
port remove a020 # removes the results for a020
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Parse
|
|
141
|
+
|
|
142
|
+
Although typically called automatically by other commands, it can be used manually to parse script structure/dependencies and update timestamps.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
port parse # parse current directory, or pass a path
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The flag `--dryrun` can be used to simply validate the project structure.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# Guiding principles
|
|
152
|
+
- `port` is always optional. A project can be fully executed by running the scripts in order. It is there when you want it, and it gets out of your way when you don't.
|
stooge-0.1.0/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# port
|
|
2
|
+
|
|
3
|
+
A simple project framework.
|
|
4
|
+
|
|
5
|
+
`port` facilitates rapid, reproducible development for local research projects.
|
|
6
|
+
|
|
7
|
+
## Project structure
|
|
8
|
+
|
|
9
|
+
port requires a specific project structure.
|
|
10
|
+
|
|
11
|
+
An example project directory looks like this:
|
|
12
|
+
|
|
13
|
+
my_project
|
|
14
|
+
-> inputs/
|
|
15
|
+
---> some_file.csv
|
|
16
|
+
-> outputs/
|
|
17
|
+
-> local.py
|
|
18
|
+
-> environment.yml
|
|
19
|
+
-> a010_clean_data.py
|
|
20
|
+
-> a020_calculate.py
|
|
21
|
+
-> a030_visualize.py
|
|
22
|
+
|
|
23
|
+
Each script has an ID (the part of the name before the first `_`).
|
|
24
|
+
|
|
25
|
+
The file called `local.py` defines the inputs and outputs of the scripts. In this file, outputs are also given the same prefix.
|
|
26
|
+
|
|
27
|
+
For example:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
input_path = Path("inputs")
|
|
33
|
+
output_path = Path("outputs")
|
|
34
|
+
|
|
35
|
+
raw_data_path = input_path / "raw data.csv"
|
|
36
|
+
|
|
37
|
+
a010_clean_data_path = output_path / "a010_cleaned_outputs.csv"
|
|
38
|
+
a020_calculated_data_path = output_path / "a020_calculated_results.csv"
|
|
39
|
+
a030_result_plot = output_path / "a030_result_plot.png"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This provides port with all the required information to figure out the relationships between scripts and their outputs.
|
|
43
|
+
|
|
44
|
+
## Init
|
|
45
|
+
Creates a port project skeleton.
|
|
46
|
+
|
|
47
|
+
This provides a small Rich interface to get information about the project name and backend, or they can be provided via command-line flags.
|
|
48
|
+
|
|
49
|
+
Supported backends are:
|
|
50
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
51
|
+
- [miniforge](https://github.com/conda-forge/miniforge)
|
|
52
|
+
- [marimo](https://marimo.io/)
|
|
53
|
+
- python (use system python or provided interpreter path)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
port init test_project --backend uv
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Run
|
|
60
|
+
|
|
61
|
+
`port run` runs the entire project up to and including a target ID. It automatically calculates stale or missing results that `a030` depends on.
|
|
62
|
+
|
|
63
|
+
For example:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
port run a030
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This first gets the directed acyclic graph (DAG) that defines `a030`'s dependencies. It then uses file output mtime values to determine if any results need to be re-run. This includes:
|
|
70
|
+
|
|
71
|
+
- Requiring that, for a given output, all dependency outputs have an mtime less than or equal to the current output.
|
|
72
|
+
- Requiring all outputs to have an mtime greater than or equal to the script that creates them.
|
|
73
|
+
|
|
74
|
+
Flags include:
|
|
75
|
+
- `--force`: rerun the specified task.
|
|
76
|
+
- `--force-all`: rerun all affected tasks.
|
|
77
|
+
- `--debug`: If true, drop into a pdb debugger on failure.
|
|
78
|
+
|
|
79
|
+
## Remove
|
|
80
|
+
|
|
81
|
+
Remove results from specified tasks.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
port remove a020 # removes the results for a020
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Parse
|
|
88
|
+
|
|
89
|
+
Although typically called automatically by other commands, it can be used manually to parse script structure/dependencies and update timestamps.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
port parse # parse current directory, or pass a path
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The flag `--dryrun` can be used to simply validate the project structure.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Guiding principles
|
|
99
|
+
- `port` is always optional. A project can be fully executed by running the scripts in order. It is there when you want it, and it gets out of your way when you don't.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
# --- Build system configuration
|
|
3
|
+
|
|
4
|
+
[build-system]
|
|
5
|
+
requires = [ "setuptools>=41", ]
|
|
6
|
+
build-backend = "setuptools.build_meta"
|
|
7
|
+
|
|
8
|
+
[tool.setuptools]
|
|
9
|
+
include-package-data = true
|
|
10
|
+
|
|
11
|
+
[tool.setuptools.packages.find]
|
|
12
|
+
where = ["src"]
|
|
13
|
+
|
|
14
|
+
# --- Project Metadata
|
|
15
|
+
|
|
16
|
+
[project]
|
|
17
|
+
name = "stooge"
|
|
18
|
+
version = "0.1.0"
|
|
19
|
+
|
|
20
|
+
authors = [
|
|
21
|
+
{ name="Derrick Chambers", email="chambers.ja.derrick@gmail.com" },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
description = "A simple project framework to keep you from getting burned"
|
|
25
|
+
readme = "README.md"
|
|
26
|
+
license = { file="LICENSE" }
|
|
27
|
+
requires-python = ">=3.13"
|
|
28
|
+
classifiers = [
|
|
29
|
+
"Development Status :: 4 - Beta",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Programming Language :: Python :: 3.14",
|
|
33
|
+
"Topic :: Scientific/Engineering",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
keywords = ["workflow", "projects"]
|
|
37
|
+
|
|
38
|
+
# --- Dependencies
|
|
39
|
+
|
|
40
|
+
dependencies = [
|
|
41
|
+
"typer",
|
|
42
|
+
"rich",
|
|
43
|
+
"jinja2",
|
|
44
|
+
"toml",
|
|
45
|
+
"pdbpp>=0.12.0.post1",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
|
|
50
|
+
test = [
|
|
51
|
+
"pytest",
|
|
52
|
+
"pre-commit",
|
|
53
|
+
"ruff",
|
|
54
|
+
"scikit-learn",
|
|
55
|
+
]
|
|
56
|
+
dev = ["stooge[test]"]
|
|
57
|
+
|
|
58
|
+
[project.scripts]
|
|
59
|
+
port = "port.cli:app"
|
|
60
|
+
|
|
61
|
+
# --- URLs for project
|
|
62
|
+
|
|
63
|
+
[project.urls]
|
|
64
|
+
"Bug Tracker" = "https://github.com/d-chambers/port/issues"
|
|
65
|
+
"Documentation" = "https://github.com/d-chambers/port"
|
|
66
|
+
"Homepage" = "https://github.com/d-chambers/port"
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
testpaths = ["tests"]
|
|
70
|
+
markers = [
|
|
71
|
+
"slow: tests that run very slowly, skipped by default",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.ruff]
|
|
75
|
+
line-length = 88
|
|
76
|
+
target-version = "py313"
|
stooge-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""port package exports."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from port.info import info
|
|
6
|
+
from port.init import init
|
|
7
|
+
from port.version import __version__
|
|
8
|
+
|
|
9
|
+
_template_path = Path(__file__).parent / "data" / "project_template"
|
|
10
|
+
|
|
11
|
+
__all__ = ["__version__", "_template_path", "info", "init"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Port's CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from port.info import info as info_project
|
|
14
|
+
from port.init import init as init_project
|
|
15
|
+
from port.version import __version__
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(help="CLI for port project scaffolding.")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@app.command("init")
|
|
21
|
+
def init(path: Optional[Path] = typer.Argument(None)):
|
|
22
|
+
"""Create or initialize a port project directory."""
|
|
23
|
+
project_path = init_project(path)
|
|
24
|
+
typer.echo(str(project_path))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.command("info")
|
|
28
|
+
def info(path: Optional[Path] = typer.Argument(None)):
|
|
29
|
+
"""Show metadata for a port project."""
|
|
30
|
+
data = info_project(path)
|
|
31
|
+
typer.echo(json.dumps(data))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.command("version")
|
|
35
|
+
def version():
|
|
36
|
+
"""Print the installed port version."""
|
|
37
|
+
typer.echo(__version__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main():
|
|
41
|
+
"""Entrypoint used by executable wrappers."""
|
|
42
|
+
app()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# {{ project_name }}
|
|
3
|
+
|
|
4
|
+
#### {Insert description of project} ####
|
|
5
|
+
"""
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# --- Project functions
|
|
9
|
+
# These are used in more than one script. Consider creating a separate utils
|
|
10
|
+
# module if this section gets too large. However, never import any project
|
|
11
|
+
# modules in local.py.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ensure_folder_exists(path: Path) -> Path:
|
|
15
|
+
"""
|
|
16
|
+
Given a path to a directory, ensure the directory exists.
|
|
17
|
+
|
|
18
|
+
Returns a path to said directory.
|
|
19
|
+
"""
|
|
20
|
+
path = Path(path)
|
|
21
|
+
path.mkdir(exist_ok=True, parents=True)
|
|
22
|
+
return path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# --- Project Parameters
|
|
26
|
+
|
|
27
|
+
# The base path for this project
|
|
28
|
+
project_path = Path(__file__).parent
|
|
29
|
+
|
|
30
|
+
# --- Inputs to project
|
|
31
|
+
|
|
32
|
+
input_path = ensure_folder_exists(project_path / "inputs")
|
|
33
|
+
|
|
34
|
+
# --- Outputs produced by project
|
|
35
|
+
|
|
36
|
+
# outputs should have the same prefix as the script which creates them (eg a010)
|
|
37
|
+
|
|
38
|
+
output_path = ensure_folder_exists(project_path / "outputs")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The first script to run
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from local import cleaned_csv, earthquake_csv
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def clean_earthquake_df(df):
|
|
10
|
+
"""Clean the earthquake dataframe."""
|
|
11
|
+
# drop any rows with lat/long out of range or invalid event_id
|
|
12
|
+
bad_lat = abs(df["longitude"]) > 180
|
|
13
|
+
bad_long = abs(df["latitude"]) > 90
|
|
14
|
+
out = df[~(bad_long | bad_lat)].dropna(axis="rows", subset=["event_id"])
|
|
15
|
+
return out
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
df = pd.read_csv(earthquake_csv)
|
|
20
|
+
out = clean_earthquake_df(df)
|
|
21
|
+
out.to_csv(cleaned_csv, index=False)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This script simply adds a column to the dataframe.
|
|
3
|
+
"""
|
|
4
|
+
import local
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def add_summary_string(df):
|
|
9
|
+
"""Add a column to the df which is useful for displaying."""
|
|
10
|
+
|
|
11
|
+
def _get_summary_str(ser):
|
|
12
|
+
time_str = str(ser["time"])[:19]
|
|
13
|
+
depth_str = f"Depth={ser['depth']/1_000}km"
|
|
14
|
+
mag_str = f"ML={ser['magnitude']:02f}"
|
|
15
|
+
out = f"{time_str} {depth_str} {mag_str}"
|
|
16
|
+
return out
|
|
17
|
+
|
|
18
|
+
df["summary_string"] = df.apply(_get_summary_str, axis=1)
|
|
19
|
+
return df
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
df = pd.read_csv(local.cleaned_csv)
|
|
24
|
+
out = add_summary_string(df)
|
|
25
|
+
out.to_csv(local.calc_csv)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Script to create simple visualizations of earthquake data.
|
|
3
|
+
"""
|
|
4
|
+
import local
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from local import magnitude_time_plot
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def plot_magnitude_histogram(df):
|
|
11
|
+
"""Plot a histogram of magnitudes, return mpl figure."""
|
|
12
|
+
fig, ax = plt.subplots(1, 1)
|
|
13
|
+
ax.hist(df["magnitude"])
|
|
14
|
+
ax.set_xlabel("Local Magnitude")
|
|
15
|
+
ax.set_ylabel("Count")
|
|
16
|
+
plt.tight_layout()
|
|
17
|
+
return fig
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def time_mag_dot_plot(df):
|
|
21
|
+
"""Create a dot plot of magnitude and time."""
|
|
22
|
+
time = pd.to_datetime(df["time"])
|
|
23
|
+
fig, ax = plt.subplots(1, 1)
|
|
24
|
+
ax.plot(time, df["magnitude"], ".")
|
|
25
|
+
ax.set_xlabel("Time")
|
|
26
|
+
ax.set_ylabel("Local Magnitude")
|
|
27
|
+
plt.tight_layout()
|
|
28
|
+
return fig
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
df = pd.read_csv(local.calc_csv)
|
|
33
|
+
# plot magnitude histogram
|
|
34
|
+
fig = plot_magnitude_histogram(df)
|
|
35
|
+
fig.savefig(local.magnitude_histogram)
|
|
36
|
+
# plot magnitude vs time
|
|
37
|
+
fig = time_mag_dot_plot(df)
|
|
38
|
+
fig.savefig(magnitude_time_plot)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project variables used by the example project.
|
|
3
|
+
"""
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
# --- Project functions
|
|
7
|
+
# These are used in more than one script. Consider creating a separate utils
|
|
8
|
+
# module if this section gets too large.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def ensure_folder_exists(path: Path) -> Path:
|
|
12
|
+
"""
|
|
13
|
+
Given a path to a directory, ensure the directory exists.
|
|
14
|
+
|
|
15
|
+
Returns a path to said directory.
|
|
16
|
+
"""
|
|
17
|
+
path = Path(path)
|
|
18
|
+
path.mkdir(exist_ok=True, parents=True)
|
|
19
|
+
return path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# The base path for this project
|
|
23
|
+
project_path = Path(__file__).parent
|
|
24
|
+
|
|
25
|
+
# --- Inputs to project
|
|
26
|
+
|
|
27
|
+
input_path = ensure_folder_exists(project_path / "inputs")
|
|
28
|
+
earthquake_csv = input_path / "bingham_earthquakes.csv"
|
|
29
|
+
|
|
30
|
+
# --- Outputs produced by project
|
|
31
|
+
|
|
32
|
+
output_path = ensure_folder_exists(project_path / "outputs")
|
|
33
|
+
cleaned_csv = output_path / "a010_cleaned_earthquakes.csv"
|
|
34
|
+
calc_csv = output_path / "a020_calculated_earthquakes.csv"
|
|
35
|
+
magnitude_histogram = output_path / "a030_magnitude_hist.png"
|
|
36
|
+
magnitude_time_plot = output_path / "a030_magnitude_time_plot"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example utilites
|
|
3
|
+
"""
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def ensure_folder_exists(path: Union[Path, str]) -> Path:
|
|
9
|
+
"""
|
|
10
|
+
Given a path to a directory, ensure the directory exists.
|
|
11
|
+
|
|
12
|
+
Returns a path to said directory.
|
|
13
|
+
"""
|
|
14
|
+
path = Path(path)
|
|
15
|
+
path.mkdir(exist_ok=True, parents=True)
|
|
16
|
+
return path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def some_function():
|
|
20
|
+
"""Another function which might be used by multiple files."""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Helpers for getting metadata from a port project."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, Union
|
|
5
|
+
|
|
6
|
+
from port.meta import read_metadata
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def info(project_path: Optional[Union[Path, str]] = None) -> dict:
|
|
10
|
+
"""
|
|
11
|
+
Return metadata for a project.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
project_path
|
|
16
|
+
Path to project directory. Defaults to current working directory.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
dict
|
|
21
|
+
Metadata dictionary for the project.
|
|
22
|
+
"""
|
|
23
|
+
data = read_metadata(project_path)
|
|
24
|
+
return data
|