emod-api 3.1.1__tar.gz → 3.2.2__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.
- {emod_api-3.1.1 → emod_api-3.2.2}/LICENSE +1 -1
- {emod_api-3.1.1/emod_api.egg-info → emod_api-3.2.2}/PKG-INFO +36 -21
- {emod_api-3.1.1 → emod_api-3.2.2}/README.md +32 -19
- emod_api-3.2.2/emod_api/__init__.py +1 -0
- emod_api-3.2.2/emod_api/campaign.py +371 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/demographics.py +9 -7
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/demographics_base.py +135 -63
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/implicit_functions.py +7 -7
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/node.py +9 -51
- emod_api-3.2.2/emod_api/demographics/overlay_node.py +42 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/properties_and_attributes.py +132 -86
- emod_api-3.2.2/emod_api/utils/emod_enum.py +14 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/str_enum.py +3 -0
- {emod_api-3.1.1 → emod_api-3.2.2/emod_api.egg-info}/PKG-INFO +36 -21
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api.egg-info/SOURCES.txt +1 -4
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api.egg-info/requires.txt +1 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/pyproject.toml +5 -3
- emod_api-3.2.2/tests/test_campaign_module.py +440 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_demographics.py +240 -38
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_node.py +31 -1
- emod_api-3.1.1/emod_api/__init__.py +0 -1
- emod_api-3.1.1/emod_api/campaign.py +0 -170
- emod_api-3.1.1/emod_api/demographics/overlay_node.py +0 -16
- emod_api-3.1.1/emod_api/migration/__main__.py +0 -22
- emod_api-3.1.1/emod_api/migration/migration.py +0 -782
- emod_api-3.1.1/emod_api/weather/__init__.py +0 -0
- emod_api-3.1.1/tests/test_campaign_module.py +0 -103
- emod_api-3.1.1/tests/test_migration.py +0 -875
- {emod_api-3.1.1 → emod_api-3.2.2}/MANIFEST.in +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/channelreports/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/channelreports/channels.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/channelreports/plot_icj_means.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/channelreports/plot_prop_report.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/channelreports/utils.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/config/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/config/default_from_schema.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/config/default_from_schema_no_validation.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/config/from_overrides.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/age_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/base_input_file.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/calculators.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/demographic_exceptions.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/demographics_overlay.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/fertility_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/mortality_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/service/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/service/grid_construction.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/service/service.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/susceptibility_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/demographics/updateable.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/legacy/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/legacy/plotAllCharts.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/multidim_plotter.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/schema_to_class.py +0 -0
- {emod_api-3.1.1/emod_api/migration → emod_api-3.2.2/emod_api/serialization}/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/serialization/census_and_mod_pop.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/serialization/dtk_file_support.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/serialization/dtk_file_tools.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/serialization/dtk_file_utility.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/serialization/serialized_population.py +0 -0
- {emod_api-3.1.1/emod_api/serialization → emod_api-3.2.2/emod_api/spatialreports}/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/spatialreports/__main__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/spatialreports/plot_spat_means.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/spatialreports/spatial.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/__init__.py +0 -0
- {emod_api-3.1.1/emod_api/spatialreports → emod_api-3.2.2/emod_api/utils/distributions}/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/base_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/bimodal_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/constant_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/demographic_distribution_flag.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/distribution_type.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/dual_constant_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/dual_exponential_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/exponential_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/gaussian_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/log_normal_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/poisson_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/uniform_distribution.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/utils/distributions/weibull_distribution.py +0 -0
- {emod_api-3.1.1/emod_api/utils/distributions → emod_api-3.2.2/emod_api/weather}/__init__.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api/weather/weather.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api.egg-info/dependency_links.txt +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/emod_api.egg-info/top_level.txt +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/setup.cfg +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_channel_reports.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_config.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_config_demog.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_demog_from_pop.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_demographics_calculators.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_distributions.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_property_reports.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_schema.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_serialization.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_spatial_reports.py +0 -0
- {emod_api-3.1.1 → emod_api-3.2.2}/tests/test_weather_files.py +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emod-api
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: Core tools for modeling using EMOD
|
|
5
5
|
Author-email: Sharon Chen <sharon.chen@gatesfoundation.org>, Zhaowei Du <zhaowei.du@gatesfoundation.org>, Clark Kirkman IV <clark.kirkmand@gatesfoundation.org>, Daniel Bridenbecker <daniel.bridenbecker@gatesfoundation.org>, Svetlana Titova <svetlana.titova@gatesfoundation.org>, Ye Chen <ye.chen@gatesfoundation.org>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Repository, https://github.com/EMOD-Hub/emod-api
|
|
8
|
-
Project-URL:
|
|
8
|
+
Project-URL: Documentation, https://emod.idmod.org/emod-api/
|
|
9
|
+
Project-URL: Issues, https://github.com/EMOD-Hub/issues-and-discussions/issues
|
|
9
10
|
Keywords: modeling,IDM
|
|
10
11
|
Classifier: Intended Audience :: Science/Research
|
|
11
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -30,6 +31,7 @@ Requires-Dist: mkdocs-include-markdown-plugin; extra == "docs"
|
|
|
30
31
|
Requires-Dist: mkdocstrings-python; extra == "docs"
|
|
31
32
|
Requires-Dist: mkdocs-autoapi; extra == "docs"
|
|
32
33
|
Requires-Dist: mkdocs-glightbox; extra == "docs"
|
|
34
|
+
Requires-Dist: mkdocs-table-reader-plugin; extra == "docs"
|
|
33
35
|
Provides-Extra: lint
|
|
34
36
|
Requires-Dist: flake8; extra == "lint"
|
|
35
37
|
Provides-Extra: test
|
|
@@ -44,17 +46,31 @@ Dynamic: license-file
|
|
|
44
46
|
|
|
45
47
|
# emod-api
|
|
46
48
|
|
|
49
|
+
**emod-api** is a set of Python tools for working with EMOD configuration, campaign, and output files.
|
|
50
|
+
|
|
47
51
|
[](https://github.com/EMOD-Hub/emod-api/actions/workflows/mkdocs_deploy.yml)
|
|
48
52
|
[](https://github.com/EMOD-Hub/emod-api/actions/workflows/lint.yml)
|
|
49
53
|
[](https://github.com/EMOD-Hub/emod-api/actions/workflows/test_and_bump_version.yml)
|
|
50
54
|
|
|
55
|
+
## Project status
|
|
56
|
+
|
|
57
|
+
EMOD-Hub projects are provided as open source software under the MIT License for
|
|
58
|
+
community use, research, and development.
|
|
59
|
+
|
|
60
|
+
**Unless otherwise noted, these projects are no longer actively maintained or supported
|
|
61
|
+
by IDM or the Gates Foundation.**
|
|
62
|
+
|
|
63
|
+
Community contributions are welcome, and trusted collaborators may review and
|
|
64
|
+
merge pull requests, but no guarantees are made regarding support, pull request
|
|
65
|
+
review, security response, maintenance, or release timelines.
|
|
66
|
+
|
|
51
67
|
## Python Version
|
|
52
68
|
|
|
53
69
|
Python 3.13 is the recommended and supported version.
|
|
54
70
|
|
|
55
71
|
## Documentation
|
|
56
72
|
|
|
57
|
-
Documentation available at https://emod
|
|
73
|
+
Documentation available at https://emod.idmod.org/emod-api/
|
|
58
74
|
|
|
59
75
|
To build the documentation locally, do the following:
|
|
60
76
|
|
|
@@ -62,17 +78,16 @@ To build the documentation locally, do the following:
|
|
|
62
78
|
2. Navigate to the root directory of the repo.
|
|
63
79
|
```
|
|
64
80
|
python -m pip install .[docs]
|
|
81
|
+
mkdocs serve
|
|
65
82
|
```
|
|
66
83
|
|
|
67
84
|
## Dependencies
|
|
68
85
|
|
|
69
86
|
### Linux
|
|
70
87
|
|
|
71
|
-
emod-api can use Snappy [de]compression (python-snappy) as necessary if it is installed which requires
|
|
88
|
+
emod-api can use Snappy [de]compression (python-snappy) as necessary if it is installed, which requires libsnappy-dev on Ubuntu 22.04 (Jammy Jellyfish).
|
|
72
89
|
|
|
73
|
-
Ubuntu: ```[sudo] apt install
|
|
74
|
-
|
|
75
|
-
CentOS: ```[sudo] yum install snappy-devel``` (not yet tested)
|
|
90
|
+
Ubuntu: ```[sudo] apt install libsnappy-dev```
|
|
76
91
|
|
|
77
92
|
NOTE: The python-snappy version needs to be 0.6.1. Newer versions have problems
|
|
78
93
|
working correctly with emod-api.
|
|
@@ -94,14 +109,6 @@ Output
|
|
|
94
109
|
- User wants to be able to work easily with serialization files.
|
|
95
110
|
- User wants to be able to work easily with (pending) events.sql file.
|
|
96
111
|
|
|
97
|
-
## Dev Tips
|
|
98
|
-
|
|
99
|
-
- To build package:
|
|
100
|
-
`python -m build --wheel`
|
|
101
|
-
|
|
102
|
-
- To install package (fill in actual version number in filename):
|
|
103
|
-
`python -m pip install dist/emod_api...whl`
|
|
104
|
-
|
|
105
112
|
## Capability Wishlist
|
|
106
113
|
|
|
107
114
|
- Migration files: users should never have to edit migration binary or header files.
|
|
@@ -116,13 +123,21 @@ Please see the documentation for [testing](/tests/README.md).
|
|
|
116
123
|
|
|
117
124
|
## Community
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
Have a question or a comment? Check out our
|
|
127
|
+
[Discussions](https://github.com/orgs/EMOD-Hub/discussions) space.
|
|
128
|
+
|
|
129
|
+
## Contributing
|
|
121
130
|
|
|
122
|
-
|
|
131
|
+
If you have feature requests, issues, or new code, please see our
|
|
132
|
+
[CONTRIBUTING](https://github.com/EMOD-Hub/.github/blob/main/CONTRIBUTING.md)
|
|
133
|
+
page for how to provide your feedback.
|
|
123
134
|
|
|
124
135
|
## Disclaimer
|
|
125
136
|
|
|
126
|
-
The code in this repository was developed by IDM and other collaborators to support our
|
|
127
|
-
|
|
128
|
-
|
|
137
|
+
The code in this repository was developed by IDM and other collaborators to support our
|
|
138
|
+
joint research on flexible agent-based modeling. We've made it publicly available under
|
|
139
|
+
the MIT License to provide others with a better understanding of our research and an
|
|
140
|
+
opportunity to build upon it for their own work. We make no representations that the code
|
|
141
|
+
works as intended or that we will provide support, address issues that are found, or accept
|
|
142
|
+
pull requests. You are welcome to create your own fork and modify the code to suit your own
|
|
143
|
+
modeling needs as permitted under the MIT License.
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
# emod-api
|
|
2
2
|
|
|
3
|
+
**emod-api** is a set of Python tools for working with EMOD configuration, campaign, and output files.
|
|
4
|
+
|
|
3
5
|
[](https://github.com/EMOD-Hub/emod-api/actions/workflows/mkdocs_deploy.yml)
|
|
4
6
|
[](https://github.com/EMOD-Hub/emod-api/actions/workflows/lint.yml)
|
|
5
7
|
[](https://github.com/EMOD-Hub/emod-api/actions/workflows/test_and_bump_version.yml)
|
|
6
8
|
|
|
9
|
+
## Project status
|
|
10
|
+
|
|
11
|
+
EMOD-Hub projects are provided as open source software under the MIT License for
|
|
12
|
+
community use, research, and development.
|
|
13
|
+
|
|
14
|
+
**Unless otherwise noted, these projects are no longer actively maintained or supported
|
|
15
|
+
by IDM or the Gates Foundation.**
|
|
16
|
+
|
|
17
|
+
Community contributions are welcome, and trusted collaborators may review and
|
|
18
|
+
merge pull requests, but no guarantees are made regarding support, pull request
|
|
19
|
+
review, security response, maintenance, or release timelines.
|
|
20
|
+
|
|
7
21
|
## Python Version
|
|
8
22
|
|
|
9
23
|
Python 3.13 is the recommended and supported version.
|
|
10
24
|
|
|
11
25
|
## Documentation
|
|
12
26
|
|
|
13
|
-
Documentation available at https://emod
|
|
27
|
+
Documentation available at https://emod.idmod.org/emod-api/
|
|
14
28
|
|
|
15
29
|
To build the documentation locally, do the following:
|
|
16
30
|
|
|
@@ -18,17 +32,16 @@ To build the documentation locally, do the following:
|
|
|
18
32
|
2. Navigate to the root directory of the repo.
|
|
19
33
|
```
|
|
20
34
|
python -m pip install .[docs]
|
|
35
|
+
mkdocs serve
|
|
21
36
|
```
|
|
22
37
|
|
|
23
38
|
## Dependencies
|
|
24
39
|
|
|
25
40
|
### Linux
|
|
26
41
|
|
|
27
|
-
emod-api can use Snappy [de]compression (python-snappy) as necessary if it is installed which requires
|
|
42
|
+
emod-api can use Snappy [de]compression (python-snappy) as necessary if it is installed, which requires libsnappy-dev on Ubuntu 22.04 (Jammy Jellyfish).
|
|
28
43
|
|
|
29
|
-
Ubuntu: ```[sudo] apt install
|
|
30
|
-
|
|
31
|
-
CentOS: ```[sudo] yum install snappy-devel``` (not yet tested)
|
|
44
|
+
Ubuntu: ```[sudo] apt install libsnappy-dev```
|
|
32
45
|
|
|
33
46
|
NOTE: The python-snappy version needs to be 0.6.1. Newer versions have problems
|
|
34
47
|
working correctly with emod-api.
|
|
@@ -50,14 +63,6 @@ Output
|
|
|
50
63
|
- User wants to be able to work easily with serialization files.
|
|
51
64
|
- User wants to be able to work easily with (pending) events.sql file.
|
|
52
65
|
|
|
53
|
-
## Dev Tips
|
|
54
|
-
|
|
55
|
-
- To build package:
|
|
56
|
-
`python -m build --wheel`
|
|
57
|
-
|
|
58
|
-
- To install package (fill in actual version number in filename):
|
|
59
|
-
`python -m pip install dist/emod_api...whl`
|
|
60
|
-
|
|
61
66
|
## Capability Wishlist
|
|
62
67
|
|
|
63
68
|
- Migration files: users should never have to edit migration binary or header files.
|
|
@@ -72,13 +77,21 @@ Please see the documentation for [testing](/tests/README.md).
|
|
|
72
77
|
|
|
73
78
|
## Community
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
Have a question or a comment? Check out our
|
|
81
|
+
[Discussions](https://github.com/orgs/EMOD-Hub/discussions) space.
|
|
82
|
+
|
|
83
|
+
## Contributing
|
|
77
84
|
|
|
78
|
-
|
|
85
|
+
If you have feature requests, issues, or new code, please see our
|
|
86
|
+
[CONTRIBUTING](https://github.com/EMOD-Hub/.github/blob/main/CONTRIBUTING.md)
|
|
87
|
+
page for how to provide your feedback.
|
|
79
88
|
|
|
80
89
|
## Disclaimer
|
|
81
90
|
|
|
82
|
-
The code in this repository was developed by IDM and other collaborators to support our
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
The code in this repository was developed by IDM and other collaborators to support our
|
|
92
|
+
joint research on flexible agent-based modeling. We've made it publicly available under
|
|
93
|
+
the MIT License to provide others with a better understanding of our research and an
|
|
94
|
+
opportunity to build upon it for their own work. We make no representations that the code
|
|
95
|
+
works as intended or that we will provide support, address issues that are found, or accept
|
|
96
|
+
pull requests. You are welcome to create your own fork and modify the code to suit your own
|
|
97
|
+
modeling needs as permitted under the MIT License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.2.2"
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Simple campaign builder for EMOD simulations.
|
|
3
|
+
|
|
4
|
+
Import this module, add valid campaign events via ``add``, and write the
|
|
5
|
+
campaign file with ``save``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
11
|
+
from emod_api import schema_to_class as s2c
|
|
12
|
+
|
|
13
|
+
schema_path = None
|
|
14
|
+
_schema_json = None
|
|
15
|
+
campaign_dict = {"Events": [], "Use_Defaults": 1}
|
|
16
|
+
individual_events_listened = []
|
|
17
|
+
individual_events_broadcast = []
|
|
18
|
+
node_events_broadcast = []
|
|
19
|
+
node_events_listened = []
|
|
20
|
+
coordinator_events_broadcast = []
|
|
21
|
+
coordinator_events_listened = []
|
|
22
|
+
use_old_adhoc_handling = False
|
|
23
|
+
unsafe = False
|
|
24
|
+
implicits = list()
|
|
25
|
+
individual_builtin_events = []
|
|
26
|
+
node_builtin_events = []
|
|
27
|
+
coordinator_builtin_events = []
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def reset():
|
|
31
|
+
"""Reset all campaign state to defaults.
|
|
32
|
+
|
|
33
|
+
Clears accumulated events, signal tracking lists, event mappings,
|
|
34
|
+
and the schema cache.
|
|
35
|
+
"""
|
|
36
|
+
campaign_dict["Events"].clear()
|
|
37
|
+
|
|
38
|
+
individual_events_listened.clear()
|
|
39
|
+
individual_events_broadcast.clear()
|
|
40
|
+
node_events_broadcast.clear()
|
|
41
|
+
node_events_listened.clear()
|
|
42
|
+
coordinator_events_broadcast.clear()
|
|
43
|
+
coordinator_events_listened.clear()
|
|
44
|
+
implicits.clear()
|
|
45
|
+
individual_builtin_events.clear()
|
|
46
|
+
node_builtin_events.clear()
|
|
47
|
+
coordinator_builtin_events.clear()
|
|
48
|
+
s2c.clear_schema_cache()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _find_builtin_events(schema, reporter_key, events_key):
|
|
52
|
+
"""Recursively find a builtin events using reporter entry and extract its event list.
|
|
53
|
+
|
|
54
|
+
Walks the schema looking for ``reporter_key`` as a dict key. When
|
|
55
|
+
found, looks up ``events_key`` inside it and returns the
|
|
56
|
+
``"Built-in"`` list if present, otherwise the ``"enum"`` list (used in EMOD-Generic)
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
schema: The schema JSON object (or sub-object) to search.
|
|
60
|
+
reporter_key: The reporter key to find (e.g.
|
|
61
|
+
``"ReportEventRecorder"``).
|
|
62
|
+
events_key: The events parameter inside the reporter (e.g.
|
|
63
|
+
``"Report_Event_Recorder_Events"``).
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
A list of event name strings, or ``None`` if the reporter
|
|
67
|
+
or its event list is not found.
|
|
68
|
+
"""
|
|
69
|
+
if isinstance(schema, dict):
|
|
70
|
+
if reporter_key in schema:
|
|
71
|
+
events_entry = schema[reporter_key]
|
|
72
|
+
if isinstance(events_entry, dict):
|
|
73
|
+
events_param = events_entry.get(events_key)
|
|
74
|
+
if isinstance(events_param, dict):
|
|
75
|
+
builtin = events_param.get("Built-in")
|
|
76
|
+
if isinstance(builtin, list):
|
|
77
|
+
return builtin
|
|
78
|
+
enum = events_param.get("enum")
|
|
79
|
+
if isinstance(enum, list):
|
|
80
|
+
return enum
|
|
81
|
+
return None
|
|
82
|
+
for value in schema.values():
|
|
83
|
+
result = _find_builtin_events(value, reporter_key, events_key)
|
|
84
|
+
if result is not None:
|
|
85
|
+
return result
|
|
86
|
+
elif isinstance(schema, list):
|
|
87
|
+
for item in schema:
|
|
88
|
+
result = _find_builtin_events(item, reporter_key, events_key)
|
|
89
|
+
if result is not None:
|
|
90
|
+
return result
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def set_schema(schema_path_in):
|
|
95
|
+
"""Set the schema file path and reset all campaign state.
|
|
96
|
+
|
|
97
|
+
This is essentially the "start building a campaign" entry point.
|
|
98
|
+
It clears any previously accumulated events and loads the new schema.
|
|
99
|
+
Also extracts built-in event lists for individual, node, and
|
|
100
|
+
coordinator levels by recursively searching for
|
|
101
|
+
``ReportEventRecorder``, ``ReportEventRecorderNode``, and
|
|
102
|
+
``ReportEventRecorderCoordinator`` in the schema.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
schema_path_in: Path to a ``schema.json`` file.
|
|
106
|
+
"""
|
|
107
|
+
reset()
|
|
108
|
+
global schema_path, _schema_json
|
|
109
|
+
|
|
110
|
+
schema_path = schema_path_in
|
|
111
|
+
with open(schema_path_in) as schema_file:
|
|
112
|
+
_schema_json = json.load(schema_file)
|
|
113
|
+
|
|
114
|
+
found = _find_builtin_events(_schema_json, "ReportEventRecorder", "Report_Event_Recorder_Events")
|
|
115
|
+
if found:
|
|
116
|
+
individual_builtin_events.extend(found)
|
|
117
|
+
|
|
118
|
+
found = _find_builtin_events(_schema_json, "ReportEventRecorderNode", "Report_Node_Event_Recorder_Events")
|
|
119
|
+
if found:
|
|
120
|
+
node_builtin_events.extend(found)
|
|
121
|
+
|
|
122
|
+
found = _find_builtin_events(_schema_json, "ReportEventRecorderCoordinator", "Report_Coordinator_Event_Recorder_Events")
|
|
123
|
+
if found:
|
|
124
|
+
coordinator_builtin_events.extend(found)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_schema():
|
|
128
|
+
"""Return the loaded schema JSON dictionary.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
The parsed schema dictionary, or ``None`` if ``set_schema`` has
|
|
132
|
+
not been called.
|
|
133
|
+
"""
|
|
134
|
+
return _schema_json
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def add(event, note: str = None):
|
|
138
|
+
"""Add a complete campaign event to the campaign builder.
|
|
139
|
+
|
|
140
|
+
The event is assumed to be valid and is not validated here.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
event: A complete campaign event object. It must support
|
|
144
|
+
``finalize()`` and dict-style key assignment.
|
|
145
|
+
note: An optional human-readable note added to the event
|
|
146
|
+
inside the output ``campaign.json`` file.
|
|
147
|
+
"""
|
|
148
|
+
event.finalize()
|
|
149
|
+
if note is not None:
|
|
150
|
+
event["Note"] = note
|
|
151
|
+
campaign_dict["Events"].append(event)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def save(filename: str = "campaign.json"):
|
|
155
|
+
"""Save the accumulated campaign events to a JSON file.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
filename: Output file path.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
The filename that was written.
|
|
162
|
+
"""
|
|
163
|
+
with open(filename, "w") as camp_file:
|
|
164
|
+
json.dump(campaign_dict, camp_file, sort_keys=True, indent=4)
|
|
165
|
+
|
|
166
|
+
return filename
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _validate_custom_events(listened_list, broadcast_list, builtin_list, level):
|
|
170
|
+
"""Validate that listened-to events are broadcast and vice versa.
|
|
171
|
+
|
|
172
|
+
Built-in events are excluded from validation since they are
|
|
173
|
+
handled by the simulation engine and do not need to be explicitly
|
|
174
|
+
broadcast or listened to in the campaign.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
listened_list: List of event names being listened to.
|
|
178
|
+
broadcast_list: List of event names being broadcast.
|
|
179
|
+
builtin_list: List of built-in event names to exclude from
|
|
180
|
+
validation.
|
|
181
|
+
level: Label for the event level (e.g. ``"coordinator"``
|
|
182
|
+
or ``"node"``) used in error/warning messages.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
A deduplicated list of custom (non-built-in) broadcast event
|
|
186
|
+
name strings.
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
ValueError: If any events are listened to but never broadcast.
|
|
190
|
+
"""
|
|
191
|
+
builtins = set(builtin_list)
|
|
192
|
+
|
|
193
|
+
broadcast_matching_builtins = set(broadcast_list) & builtins
|
|
194
|
+
if broadcast_matching_builtins:
|
|
195
|
+
warnings.warn(
|
|
196
|
+
f"The following {level}-level broadcast events mirror built-in {level}-level events, "
|
|
197
|
+
f"therefore these events will be broadcast by the simulation as well as the campaign: "
|
|
198
|
+
f"{sorted(broadcast_matching_builtins)}")
|
|
199
|
+
|
|
200
|
+
listened = set(listened_list) - builtins
|
|
201
|
+
broadcast = set(broadcast_list) - builtins
|
|
202
|
+
|
|
203
|
+
listened_not_broadcast = listened - broadcast
|
|
204
|
+
if listened_not_broadcast:
|
|
205
|
+
raise ValueError(
|
|
206
|
+
f"The following {level}-level events are listened to but never broadcast. This means that any campaign "
|
|
207
|
+
f"interventions that rely on listening to these events will never fire. Please fix the error by either "
|
|
208
|
+
f"broadcasting these events in the campaign or removing the interventions that are listening for them:\n"
|
|
209
|
+
f"{sorted(listened_not_broadcast)}")
|
|
210
|
+
|
|
211
|
+
broadcast_not_listened = broadcast - listened
|
|
212
|
+
if broadcast_not_listened:
|
|
213
|
+
warnings.warn(
|
|
214
|
+
f"The following {level} events are broadcast but nothing is listening to them within "
|
|
215
|
+
f"the campaign: {sorted(broadcast_not_listened)}")
|
|
216
|
+
|
|
217
|
+
return list(broadcast)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def validate_custom_coordinator_events():
|
|
221
|
+
"""Validate and return deduplicated custom coordinator-level events.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
A list of unique coordinator event name strings that are broadcast
|
|
225
|
+
in the campaign.
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
ValueError: If any coordinator events are listened to but
|
|
229
|
+
never broadcast.
|
|
230
|
+
"""
|
|
231
|
+
return _validate_custom_events(coordinator_events_listened, coordinator_events_broadcast, coordinator_builtin_events, "coordinator")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def validate_custom_node_events():
|
|
235
|
+
"""Validate and return deduplicated custom node-level events.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
A list of unique node event name strings that are broadcast
|
|
239
|
+
in the campaign.
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
ValueError: If any node events are listened to but
|
|
243
|
+
never broadcast.
|
|
244
|
+
"""
|
|
245
|
+
return _validate_custom_events(node_events_listened, node_events_broadcast, node_builtin_events, "node")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def validate_custom_individual_events():
|
|
249
|
+
"""Validate and return deduplicated custom individual-level events.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
A list of unique individual event name strings that are broadcast
|
|
253
|
+
in the campaign.
|
|
254
|
+
|
|
255
|
+
Raises:
|
|
256
|
+
ValueError: If any individual events are listened to but
|
|
257
|
+
never broadcast.
|
|
258
|
+
"""
|
|
259
|
+
return _validate_custom_events(individual_events_listened, individual_events_broadcast, individual_builtin_events, "individual")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def get_recv_trigger(trigger, old=use_old_adhoc_handling):
|
|
263
|
+
"""Register an individual-level event as listened to.
|
|
264
|
+
|
|
265
|
+
Tracks which individual events are used throughout the simulation
|
|
266
|
+
so that ``validate_custom_individual_events`` can validate that every
|
|
267
|
+
listened-to event has a corresponding broadcast.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
trigger: The individual event name string.
|
|
271
|
+
old: Unused. Kept for backwards compatibility.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
The event name, unchanged.
|
|
275
|
+
"""
|
|
276
|
+
if not trigger:
|
|
277
|
+
raise ValueError("Event name must not be None or empty.")
|
|
278
|
+
individual_events_listened.append(trigger)
|
|
279
|
+
return trigger
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def set_listened_node_event(event: str) -> str:
|
|
283
|
+
"""Register a node-level event as listened to.
|
|
284
|
+
|
|
285
|
+
Tracks which node events are used throughout the simulation so
|
|
286
|
+
that ``validate_custom_node_events`` can validate that every listened-to
|
|
287
|
+
event has a corresponding broadcast.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
event: The node event name string.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
The event name, unchanged.
|
|
294
|
+
"""
|
|
295
|
+
if not event:
|
|
296
|
+
raise ValueError("Event name must not be None or empty.")
|
|
297
|
+
node_events_listened.append(event)
|
|
298
|
+
return event
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def set_listened_coordinator_event(event: str) -> str:
|
|
302
|
+
"""Register a coordinator-level event as listened to.
|
|
303
|
+
|
|
304
|
+
Tracks which coordinator events are used throughout the simulation
|
|
305
|
+
so that ``validate_custom_coordinator_events`` can validate that every
|
|
306
|
+
listened-to event has a corresponding broadcast.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
event: The coordinator event name string.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
The event name, unchanged.
|
|
313
|
+
"""
|
|
314
|
+
if not event:
|
|
315
|
+
raise ValueError("Event name must not be None or empty.")
|
|
316
|
+
coordinator_events_listened.append(event)
|
|
317
|
+
return event
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def get_send_trigger(trigger, old=use_old_adhoc_handling):
|
|
321
|
+
"""Register an individual-level event as broadcast.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
trigger: The individual event name string.
|
|
325
|
+
old: Unused. Kept for backwards compatibility.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
The event name, unchanged.
|
|
329
|
+
"""
|
|
330
|
+
if not trigger:
|
|
331
|
+
raise ValueError("Event name must not be None or empty.")
|
|
332
|
+
individual_events_broadcast.append(trigger)
|
|
333
|
+
return trigger
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def set_broadcast_node_event(event: str) -> str:
|
|
337
|
+
"""Register a node-level event as broadcast.
|
|
338
|
+
|
|
339
|
+
Tracks which node events are used throughout the simulation so
|
|
340
|
+
that ``validate_custom_node_events`` can validate that every broadcast
|
|
341
|
+
event has something listening to it.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
event: The node event name string.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
The event name, unchanged.
|
|
348
|
+
"""
|
|
349
|
+
if not event:
|
|
350
|
+
raise ValueError("Event name must not be None or empty.")
|
|
351
|
+
node_events_broadcast.append(event)
|
|
352
|
+
return event
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def set_broadcast_coordinator_event(event: str) -> str:
|
|
356
|
+
"""Register a coordinator-level event as broadcast.
|
|
357
|
+
|
|
358
|
+
Tracks which coordinator events are used throughout the simulation
|
|
359
|
+
so that ``validate_custom_coordinator_events`` can validate that every
|
|
360
|
+
broadcast event has something listening to it.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
event: The coordinator event name string.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
The event name, unchanged.
|
|
367
|
+
"""
|
|
368
|
+
if not event:
|
|
369
|
+
raise ValueError("Event name must not be None or empty.")
|
|
370
|
+
coordinator_events_broadcast.append(event)
|
|
371
|
+
return event
|
|
@@ -7,7 +7,7 @@ from typing import Union
|
|
|
7
7
|
|
|
8
8
|
from emod_api.demographics.demographics_base import DemographicsBase
|
|
9
9
|
from emod_api.demographics.node import Node
|
|
10
|
-
from emod_api.demographics.properties_and_attributes import NodeAttributes
|
|
10
|
+
from emod_api.demographics.properties_and_attributes import NodeAttributes, NodeProperty, NodeProperties # noqa: F401
|
|
11
11
|
from emod_api.demographics.service import service
|
|
12
12
|
|
|
13
13
|
|
|
@@ -30,13 +30,9 @@ class Demographics(DemographicsBase):
|
|
|
30
30
|
"""
|
|
31
31
|
super().__init__(nodes=nodes, idref=idref, default_node=default_node)
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
# as False allows setting default_node.node_attributes exactly as they are in the file. Loading via
|
|
35
|
-
# Demographics.from_file() is deprecated, see below.
|
|
33
|
+
# No current default settings
|
|
36
34
|
if set_defaults:
|
|
37
|
-
|
|
38
|
-
self.default_node.node_attributes.seaport = 1
|
|
39
|
-
self.default_node.node_attributes.region = 1
|
|
35
|
+
pass
|
|
40
36
|
|
|
41
37
|
def to_file(self, path: Union[str, Path] = "demographics.json", indent: int = 4) -> None:
|
|
42
38
|
"""
|
|
@@ -96,6 +92,12 @@ class Demographics(DemographicsBase):
|
|
|
96
92
|
demographics = cls(nodes=nodes, default_node=default_node, idref=idref, set_defaults=False)
|
|
97
93
|
demographics.metadata = metadata
|
|
98
94
|
demographics.implicits.extend(implicit_functions)
|
|
95
|
+
|
|
96
|
+
node_properties_list = demographics_dict.get("NodeProperties")
|
|
97
|
+
if node_properties_list:
|
|
98
|
+
for np_dict in node_properties_list:
|
|
99
|
+
demographics.node_properties.add(NodeProperty.from_dict(np_dict))
|
|
100
|
+
|
|
99
101
|
return demographics
|
|
100
102
|
|
|
101
103
|
@classmethod
|