Skip to content

Commit c4194a7

Browse files
author
Yang
authored
Merge pull request #8 from EcoExtreML/handle_working_dir
Handle input directory
2 parents 1c44596 + e427e0d commit c4194a7

22 files changed

Lines changed: 351 additions & 353 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
fail-fast: false
1818
matrix:
1919
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
20-
python-version: ['3.7', '3.8', '3.9']
20+
python-version: ['3.8', '3.9']
2121
steps:
2222
- uses: actions/checkout@v3
2323
- name: Set up Python ${{ matrix.python-version }}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ __pycache__/
33
*.py[cod]
44
*$py.class
55

6+
# dummy test product
7+
tests/test_data/directories/input/
8+
tests/test_data/directories/output/
9+
610
# C extensions
711
*.so
812

.prospector.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ pydocstyle:
2727
D213, # Multi-line docstring summary should start at the second line
2828
D404, # First word of the docstring should not be This
2929
]
30+
31+
pyflakes:
32+
disable: [
33+
F401, # unused import: already checked by pylint
34+
]

PyStemmusScope/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Documentation about PyStemmusScope"""
22
import logging
3+
from .iostreamer import create_io_dir
4+
from .iostreamer import read_config
35

46

57
logging.getLogger(__name__).addHandler(logging.NullHandler())

PyStemmusScope/iostreamer.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""PyStemmusScope directories utilities.
2+
3+
Module designed to manage input directories and data for running the model
4+
and storing outputs.
5+
"""
6+
import logging
7+
import os
8+
import shutil
9+
import time
10+
from pathlib import Path
11+
12+
13+
logger = logging.getLogger(__name__)
14+
15+
def read_config(path_to_config_file):
16+
"""Read config from given config file.
17+
18+
Load paths from config file and save them into dict.
19+
20+
Args:
21+
path_to_config_file: Path to the config file.
22+
23+
Returns:
24+
Dictionary containing paths to work directory and all sub-directories.
25+
"""
26+
config = {}
27+
with open(path_to_config_file, "r", encoding="utf8") as f:
28+
for line in f:
29+
(key, val) = line.split("=")
30+
config[key] = val.rstrip('\n')
31+
32+
return config
33+
34+
def create_io_dir(forcing_filename, config):
35+
"""Create input directory and copy required files.
36+
37+
Work flow executor to create work directory and all sub-directories.
38+
39+
Returns:
40+
Path (string) to input, output directory and config file for every station/forcing.
41+
"""
42+
# get start time with the format Y-M-D-HM
43+
timestamp = time.strftime('%Y-%m-%d-%H%M')
44+
station_name = forcing_filename.split('_')[0]
45+
# create input directory
46+
input_dir = Path(f"{config['WorkDir']}/input/{station_name}_{timestamp}")
47+
Path(input_dir).mkdir(parents=True, exist_ok=True)
48+
message = f"Prepare work directory {input_dir} for the station: {station_name}"
49+
logger.info("%s", message)
50+
# copy model parameters to work directory
51+
_copy_data(input_dir, config)
52+
input_dir = str(input_dir)
53+
54+
# create output directory
55+
output_dir = Path(f"{config['WorkDir']}/output/{station_name}_{timestamp}")
56+
output_dir.mkdir(parents=True, exist_ok=True)
57+
message = f"Prepare work directory {output_dir} for the station: {station_name}"
58+
logger.info("%s", message)
59+
output_dir = str(output_dir)
60+
61+
# update config file for ForcingFileName and InputPath
62+
config_file_path = _update_config_file(forcing_filename, input_dir, output_dir,
63+
config, station_name, timestamp)
64+
65+
return input_dir, output_dir, config_file_path
66+
67+
def _copy_data(input_dir, config):
68+
"""Copy required data to the work directory.
69+
70+
Create sub-directories inside the work directory and copy data.
71+
72+
Args:
73+
input_dir: Path to the input directory.
74+
config: Dictionary containing all the paths.
75+
"""
76+
folder_list_vegetation = ["directional", "fluspect_parameters", "leafangles",
77+
"radiationdata", "soil_spectrum"]
78+
for folder in folder_list_vegetation:
79+
os.makedirs(input_dir / folder, exist_ok=True)
80+
shutil.copytree(str(config[folder]), str(input_dir / folder), dirs_exist_ok=True)
81+
82+
# copy input_data.xlsx
83+
shutil.copy(str(config["input_data"]), str(input_dir))
84+
85+
def _update_config_file(nc_file, input_dir, output_dir, config, station_name, timestamp): #pylint: disable=too-many-arguments
86+
"""Update config file for each station.
87+
88+
Create config file for each forcing/station under the work directory.
89+
90+
Args:
91+
ncfile: Name of forcing file.
92+
input_dir: Path to the input directory.
93+
output_dir: Path to the output directory.
94+
config: Dictionary containing all the paths.
95+
station_name: Station name inferred from forcing file.
96+
timestamp: Timestamp when creating the config file.
97+
98+
Returns:
99+
Path to updated config file.
100+
"""
101+
config_file_path = Path(input_dir, f"{station_name}_{timestamp}_config.txt")
102+
with open(config_file_path, 'w', encoding="utf8") as f:
103+
for key, value in config.items():
104+
if key == "ForcingFileName":
105+
update_entry = f"{key}={nc_file}\n"
106+
elif key == "InputPath":
107+
update_entry = f"{key}={str(input_dir)}/\n"
108+
elif key == "OutputPath":
109+
update_entry = f"{key}={str(output_dir)}/\n"
110+
else:
111+
update_entry = f"{key}={value}\n"
112+
113+
f.write(update_entry)
114+
115+
return str(config_file_path)

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ For more information on how to download and install it, see the links below:
8282
[Snellius](https://servicedesk.surfsara.nl/wiki/display/WIKI/Snellius) is the
8383
Dutch National supercomputer hosted at SURF. MATLAB Runtime is installed on
8484
Snellius, see the script
85-
[`run_jupyter_lab_on_compute_node.sh`](https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/blob/main/run_jupyter_lab_on_compute_node.sh)
85+
[`run_jupyter_lab_snellius.sh`](https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/blob/main/run_jupyter_lab_snellius.sh)
8686
on how to load the module.
8787
</details>
8888

@@ -99,7 +99,7 @@ JupyterLab will open automatically in your browser.
9999
**On Snellius:**
100100

101101
Use the script
102-
[`run_jupyter_lab_snellius.sh`](https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/blob/main/run_jupyter_lab_on_compute_node.sh)
102+
[`run_jupyter_lab_snellius.sh`](https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/blob/main/run_jupyter_lab_snellius.sh)
103103
to create a jupyter lab server on Snellius for running the notebook
104104
interactively.
105105

@@ -118,10 +118,21 @@ The execution of the model includes following steps:
118118
- Run the model
119119
- Create output directories, prepare output files
120120

121+
## Configure the package for development and testing
122+
The testing framework used here is [PyTest](https://pytest.org). Before running the test, the package need to be installed and configured as via the command:
123+
124+
```py
125+
pip install -e .
126+
```
127+
or
128+
```py
129+
python setup.py develop
130+
```
131+
121132
## Contributing
122133

123134
If you want to contribute to the development of PyStemmusScope,
124-
have a look at the [contribution guidelines](docs/CONTRIBUTING.md).
135+
have a look at the [contribution guidelines](CONTRIBUTING.md).
125136

126137
## Credits
127138

docs/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414

1515
### Changed
1616

17-
[Unreleased]: https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/compare/v1.0.0...HEAD
18-
[0.0.1]: https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/releases/tag/v0.0.1
17+
<!-- [Unreleased]: https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/compare/v1.0.0...HEAD
18+
[0.0.1]: https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/releases/tag/v0.0.1 -->

docs/CONTRIBUTING.md

Lines changed: 0 additions & 78 deletions
This file was deleted.

docs/project_setup.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ checklist](https://guide.esciencecenter.nl/#/best_practices/checklist).
1111

1212
This repository is set up with Python versions:
1313

14-
- 3.7
1514
- 3.8
1615
- 3.9
1716

@@ -44,7 +43,7 @@ help you decide which tool to use for packaging.
4443
- [PyTest introduction](https://pythontest.com/pytest-book/)
4544
- PyTest is listed as a development dependency
4645
- This is configured in `setup.cfg`
47-
- The project uses [GitHub action workflows](https://docs.github.com/en/actions) to automatically run tests on GitHub infrastructure against multiple Python versions
46+
- The project uses GitHub action workflows to automatically run tests on GitHub infrastructure against multiple Python versions
4847
- Workflows can be found in [`.github/workflows`](../.github/workflows/)
4948
- [Relevant section in the guide](https://guide.esciencecenter.nl/#/best_practices/language_guides/python?id=testing)
5049

matlab_scripts/example_python_modules.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
def read_config(config_file_path):
99
config = {}
10-
with open(config_file_path, "r") as f:
10+
with open(config_file_path, "r", encoding="utf8") as f:
1111
for line in f:
1212
(key, val) = line.split("=")
1313
config[key] = val.rstrip('\n')
@@ -28,7 +28,7 @@ def input_dir(ncfile, config):
2828
shutil.copytree(config["VegetationPropertyPath"], work_dir, dirs_exist_ok=True)
2929
# update config file for ForcingFileName and InputPath
3030
config_file_path = Path(work_dir, f"{station_name}_{timestamp}_config.txt")
31-
with open(config_file_path, 'w') as f:
31+
with open(config_file_path, 'w', encoding="utf8") as f:
3232
for i in config.keys():
3333
if i == "ForcingFileName":
3434
f.write(i + "=" + ncfile + "\n")

0 commit comments

Comments
 (0)