ccrt 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.
- ccrt-0.1.0/CHANGELOG.md +44 -0
- ccrt-0.1.0/CONTRIBUTING.md +24 -0
- ccrt-0.1.0/LICENSE +21 -0
- ccrt-0.1.0/MANIFEST.in +5 -0
- ccrt-0.1.0/PKG-INFO +118 -0
- ccrt-0.1.0/README.md +82 -0
- ccrt-0.1.0/benchmarks/benchmark.py +57 -0
- ccrt-0.1.0/benchmarks/evaluate.py +42 -0
- ccrt-0.1.0/benchmarks/smoke_install.py +124 -0
- ccrt-0.1.0/ccrt/__init__.py +37 -0
- ccrt-0.1.0/ccrt/__main__.py +3 -0
- ccrt-0.1.0/ccrt/_version.py +1 -0
- ccrt-0.1.0/ccrt/cli.py +205 -0
- ccrt-0.1.0/ccrt/client.py +166 -0
- ccrt-0.1.0/ccrt/config.py +23 -0
- ccrt-0.1.0/ccrt/evaluation.py +30 -0
- ccrt-0.1.0/ccrt/export.py +152 -0
- ccrt-0.1.0/ccrt/interactive.py +160 -0
- ccrt-0.1.0/ccrt/loaders/__init__.py +5 -0
- ccrt-0.1.0/ccrt/loaders/acled.py +69 -0
- ccrt-0.1.0/ccrt/loaders/base.py +144 -0
- ccrt-0.1.0/ccrt/loaders/ucdp.py +75 -0
- ccrt-0.1.0/ccrt/merge.py +186 -0
- ccrt-0.1.0/ccrt/report.py +170 -0
- ccrt-0.1.0/ccrt/review.py +56 -0
- ccrt-0.1.0/ccrt/schema.py +162 -0
- ccrt-0.1.0/ccrt/storage.py +168 -0
- ccrt-0.1.0/ccrt/tui.py +349 -0
- ccrt-0.1.0/ccrt.egg-info/PKG-INFO +118 -0
- ccrt-0.1.0/ccrt.egg-info/SOURCES.txt +55 -0
- ccrt-0.1.0/ccrt.egg-info/dependency_links.txt +1 -0
- ccrt-0.1.0/ccrt.egg-info/entry_points.txt +2 -0
- ccrt-0.1.0/ccrt.egg-info/requires.txt +15 -0
- ccrt-0.1.0/ccrt.egg-info/top_level.txt +1 -0
- ccrt-0.1.0/docs/api.md +145 -0
- ccrt-0.1.0/docs/releasing.md +95 -0
- ccrt-0.1.0/docs/source-mappings.md +101 -0
- ccrt-0.1.0/docs/usage.md +135 -0
- ccrt-0.1.0/docs/validation.md +117 -0
- ccrt-0.1.0/examples/acled_synthetic.csv +6 -0
- ccrt-0.1.0/examples/reconciliation_walkthrough.ipynb +274 -0
- ccrt-0.1.0/examples/source_a.csv +3 -0
- ccrt-0.1.0/examples/source_b.csv +3 -0
- ccrt-0.1.0/examples/truth_synthetic.csv +4 -0
- ccrt-0.1.0/examples/ucdp_synthetic.csv +5 -0
- ccrt-0.1.0/pyproject.toml +53 -0
- ccrt-0.1.0/setup.cfg +4 -0
- ccrt-0.1.0/tests/conftest.py +33 -0
- ccrt-0.1.0/tests/test_cli.py +123 -0
- ccrt-0.1.0/tests/test_client.py +153 -0
- ccrt-0.1.0/tests/test_confidence.py +15 -0
- ccrt-0.1.0/tests/test_config.py +70 -0
- ccrt-0.1.0/tests/test_loaders.py +174 -0
- ccrt-0.1.0/tests/test_merge.py +131 -0
- ccrt-0.1.0/tests/test_report.py +71 -0
- ccrt-0.1.0/tests/test_storage_review.py +125 -0
- ccrt-0.1.0/tests/test_tui.py +33 -0
ccrt-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Added source-preserving UCDP GED CSV/API and ACLED event CSV adapters, validation
|
|
6
|
+
reports, explicit snapshot versions, and input fingerprints.
|
|
7
|
+
- Added bounded authenticated API paging/retries with mocked failure tests and
|
|
8
|
+
verified the live endpoint's zero-based page numbering.
|
|
9
|
+
- Preserved published UCDP fatality estimates that fall outside low/best/high
|
|
10
|
+
numerical order, with a warning for each affected record.
|
|
11
|
+
- Replaced automatic one-to-one assignment with all candidate links, date intervals,
|
|
12
|
+
precision warnings, coarse-location handling, and explicit review decisions.
|
|
13
|
+
- Added JSON run/review bundles, review CSV import/export, nullable pandas tables,
|
|
14
|
+
and metadata-preserving Parquet.
|
|
15
|
+
- Added a resumable terminal reviewer that groups competing candidates and displays
|
|
16
|
+
the original source accounts while saving each decision.
|
|
17
|
+
- Added an optional experimental full-screen review dashboard using terminal curses,
|
|
18
|
+
isolated from the review data model for easy removal.
|
|
19
|
+
- Added review reports with candidate coverage, unique-record counts, unlinked
|
|
20
|
+
reasons, and accepted-link diagnostics. Partial reviews never produce precision
|
|
21
|
+
or recall metrics.
|
|
22
|
+
- Added installed-wheel workflow checks for core and tables installations, and
|
|
23
|
+
extended package CI beyond the CLI help check.
|
|
24
|
+
- Added MIT licensing, CLI subcommands, tutorial notebook, synthetic evaluation,
|
|
25
|
+
benchmark tooling, CI, and package build checks.
|
|
26
|
+
|
|
27
|
+
### Migration from the unreleased initial prototype
|
|
28
|
+
|
|
29
|
+
`Incident` is replaced by `SourceRecord`; `load_csv` now returns a `Dataset`.
|
|
30
|
+
`reconcile(left, right, config=MatchConfig(...))` accepts datasets and returns
|
|
31
|
+
`.candidates` plus `.unlinked`, replacing greedy `.matches` and unmatched lists.
|
|
32
|
+
Use `total_deaths_low/best/high` and `civilian_deaths` explicitly. The CLI now uses
|
|
33
|
+
`import`, `fetch-ucdp`, `match`, `review`, and `export` subcommands. These changes
|
|
34
|
+
precede the first public release; no backward-compatibility shim is provided.
|
|
35
|
+
|
|
36
|
+
### Validation scope
|
|
37
|
+
|
|
38
|
+
`0.1.0` is an experimental library. Independent real-data linkage
|
|
39
|
+
evaluation is not a publication gate for the library, but remains necessary before
|
|
40
|
+
claiming empirical matching accuracy. A prior UCDP GED 26.1 Iraq 2020 query
|
|
41
|
+
retrieved 282 records with 10 validation warnings; a real ACLED export supplied
|
|
42
|
+
6,306 filtered Iraq 2020 records with no import issues. Candidate generation
|
|
43
|
+
produced 1,329 pairs. Partial manual reviews do not establish precision or recall.
|
|
44
|
+
See `docs/releasing.md` for the publication procedure.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Contributing and learning
|
|
2
|
+
|
|
3
|
+
Install `.[tables,dev]` into a virtual environment. Run `python -m pytest`,
|
|
4
|
+
`ruff check .`, and `ruff format --check .` before proposing a change. Tests use
|
|
5
|
+
invented examples and mocked HTTP; they must never require private data or API keys.
|
|
6
|
+
|
|
7
|
+
Work on one behaviour at a time: explain the requirement, inspect its source
|
|
8
|
+
contract, write a test that demonstrates the behaviour, implement it, and review
|
|
9
|
+
its effect on real research assumptions. Good first exercises are in the notebook.
|
|
10
|
+
|
|
11
|
+
New source adapters need a documented field mapping and source codebook reference,
|
|
12
|
+
fixtures that can be distributed, validation for malformed input, and a round-trip
|
|
13
|
+
provenance test. Do not infer civilian deaths from total deaths. Changes to date
|
|
14
|
+
uncertainty or candidate ranking need an updated algorithm ID and evaluation notes.
|
|
15
|
+
Reports of matching quality need independently adjudicated reference labels from a
|
|
16
|
+
declared sample; synthetic metrics and partially reviewed candidates are not
|
|
17
|
+
real-data accuracy estimates. Keep source records and evidence within their access
|
|
18
|
+
and redistribution terms.
|
|
19
|
+
|
|
20
|
+
Open a GitHub issue with the version, a minimal synthetic reproduction, expected
|
|
21
|
+
behaviour, and actual behaviour. Remove credentials and private source material.
|
|
22
|
+
We aim to review contributions as maintainer time allows; no support response SLA
|
|
23
|
+
is offered for the preview. Python 3.10–3.14 are tested in CI; optional pandas and
|
|
24
|
+
PyArrow support is tested separately from the core installation.
|
ccrt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fabian Figueroa
|
|
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.
|
ccrt-0.1.0/MANIFEST.in
ADDED
ccrt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ccrt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Experimental toolkit for reviewing candidate links between conflict incident records
|
|
5
|
+
Author: Fabian Figueroa
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/FabianFig/ccrt
|
|
8
|
+
Project-URL: Issues, https://github.com/FabianFig/ccrt/issues
|
|
9
|
+
Keywords: conflict,record-linkage,ucdp,acled,research
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: httpx<1,>=0.27
|
|
23
|
+
Requires-Dist: python-dotenv<2,>=1
|
|
24
|
+
Provides-Extra: tables
|
|
25
|
+
Requires-Dist: pandas>=2.2; extra == "tables"
|
|
26
|
+
Requires-Dist: pyarrow>=15; extra == "tables"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.9; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=6; extra == "dev"
|
|
32
|
+
Requires-Dist: nbformat>=5.10; extra == "dev"
|
|
33
|
+
Requires-Dist: nbclient>=0.10; extra == "dev"
|
|
34
|
+
Requires-Dist: ipykernel>=6; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# Conflict Casualty Reconciliation Toolkit (CCRT)
|
|
38
|
+
|
|
39
|
+
CCRT compares source accounts of conflict incidents. It imports UCDP GED and ACLED
|
|
40
|
+
records, proposes possible links, supports human review, and exports results while
|
|
41
|
+
preserving each source's original claims.
|
|
42
|
+
|
|
43
|
+
**Status:** `0.1.0` is an experimental research library.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
CCRT supports Python 3.10–3.14. To install a published release:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python -m pip install ccrt
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Install `ccrt[tables]` for pandas and Parquet support. From a checkout, use
|
|
54
|
+
`python -m pip install -e .`, or `python -m pip install -e '.[tables,dev]'` for the
|
|
55
|
+
project's development checks.
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
The example files contain invented incidents in Exampleland. From the repository
|
|
60
|
+
root, with an unused `work/` directory:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
mkdir -p work
|
|
64
|
+
ccrt import ucdp examples/ucdp_synthetic.csv work/ucdp.json --version 26.1
|
|
65
|
+
ccrt import acled examples/acled_synthetic.csv work/acled.json --version synthetic-2026-09-24
|
|
66
|
+
ccrt match work/ucdp.json work/acled.json work/matches
|
|
67
|
+
ccrt report work/matches/run.json --review work/matches/review.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The match run contains three candidate links and four unlinked source records.
|
|
71
|
+
Inspect `work/matches/candidates.csv`, then use the [usage guide](docs/usage.md) to
|
|
72
|
+
record decisions and export reviewed results. CCRT does not overwrite existing
|
|
73
|
+
outputs.
|
|
74
|
+
|
|
75
|
+
## Python API
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from ccrt import MatchConfig, load_acled_csv, load_ucdp_csv, reconcile
|
|
79
|
+
|
|
80
|
+
ucdp = load_ucdp_csv("examples/ucdp_synthetic.csv", version="26.1")
|
|
81
|
+
acled = load_acled_csv("examples/acled_synthetic.csv", version="synthetic-2026-09-24")
|
|
82
|
+
result = reconcile(ucdp, acled, config=MatchConfig(max_days=3, max_km=25))
|
|
83
|
+
|
|
84
|
+
for candidate in result.candidates:
|
|
85
|
+
print(candidate.left.source_id, candidate.right.source_id, candidate.score)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The [API reference](docs/api.md) covers review decisions, saved runs, reports, and
|
|
89
|
+
exports.
|
|
90
|
+
|
|
91
|
+
## Sources and capabilities
|
|
92
|
+
|
|
93
|
+
- UCDP GED 25.1 and 26.1 CSV files with modern field names, and authenticated GED
|
|
94
|
+
API access.
|
|
95
|
+
- ACLED event CSV exports with current lowercase field names. The supplied version
|
|
96
|
+
is a label for the exact export snapshot.
|
|
97
|
+
- Source-preserving JSON bundles, candidate generation, CSV and terminal review,
|
|
98
|
+
review reports, CSV export, and optional Parquet export.
|
|
99
|
+
|
|
100
|
+
See [source mappings](docs/source-mappings.md) for supported fields, precision
|
|
101
|
+
handling, and provenance. The [teaching notebook](examples/reconciliation_walkthrough.ipynb)
|
|
102
|
+
provides another offline example.
|
|
103
|
+
|
|
104
|
+
## Limitations
|
|
105
|
+
|
|
106
|
+
CCRT compares source accounts; it does not establish objective incident identity.
|
|
107
|
+
Candidate scores are uncalibrated proximity rankings, and generating a candidate
|
|
108
|
+
does not confirm correspondence. Real-world linkage accuracy has not been
|
|
109
|
+
independently evaluated. CCRT does not reconcile casualty totals or estimate
|
|
110
|
+
underreporting. See [validation notes](docs/validation.md) for software checks and
|
|
111
|
+
research limits.
|
|
112
|
+
|
|
113
|
+
## Contributing and licence
|
|
114
|
+
|
|
115
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md), the [changelog](CHANGELOG.md), and the
|
|
116
|
+
[release procedure](docs/releasing.md). The code and synthetic examples are under
|
|
117
|
+
the [MIT licence](LICENSE). UCDP and ACLED data have separate access, attribution,
|
|
118
|
+
and redistribution terms; obtain source access and check the terms for your snapshot.
|
ccrt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Conflict Casualty Reconciliation Toolkit (CCRT)
|
|
2
|
+
|
|
3
|
+
CCRT compares source accounts of conflict incidents. It imports UCDP GED and ACLED
|
|
4
|
+
records, proposes possible links, supports human review, and exports results while
|
|
5
|
+
preserving each source's original claims.
|
|
6
|
+
|
|
7
|
+
**Status:** `0.1.0` is an experimental research library.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
CCRT supports Python 3.10–3.14. To install a published release:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python -m pip install ccrt
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Install `ccrt[tables]` for pandas and Parquet support. From a checkout, use
|
|
18
|
+
`python -m pip install -e .`, or `python -m pip install -e '.[tables,dev]'` for the
|
|
19
|
+
project's development checks.
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
The example files contain invented incidents in Exampleland. From the repository
|
|
24
|
+
root, with an unused `work/` directory:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
mkdir -p work
|
|
28
|
+
ccrt import ucdp examples/ucdp_synthetic.csv work/ucdp.json --version 26.1
|
|
29
|
+
ccrt import acled examples/acled_synthetic.csv work/acled.json --version synthetic-2026-09-24
|
|
30
|
+
ccrt match work/ucdp.json work/acled.json work/matches
|
|
31
|
+
ccrt report work/matches/run.json --review work/matches/review.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The match run contains three candidate links and four unlinked source records.
|
|
35
|
+
Inspect `work/matches/candidates.csv`, then use the [usage guide](docs/usage.md) to
|
|
36
|
+
record decisions and export reviewed results. CCRT does not overwrite existing
|
|
37
|
+
outputs.
|
|
38
|
+
|
|
39
|
+
## Python API
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ccrt import MatchConfig, load_acled_csv, load_ucdp_csv, reconcile
|
|
43
|
+
|
|
44
|
+
ucdp = load_ucdp_csv("examples/ucdp_synthetic.csv", version="26.1")
|
|
45
|
+
acled = load_acled_csv("examples/acled_synthetic.csv", version="synthetic-2026-09-24")
|
|
46
|
+
result = reconcile(ucdp, acled, config=MatchConfig(max_days=3, max_km=25))
|
|
47
|
+
|
|
48
|
+
for candidate in result.candidates:
|
|
49
|
+
print(candidate.left.source_id, candidate.right.source_id, candidate.score)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The [API reference](docs/api.md) covers review decisions, saved runs, reports, and
|
|
53
|
+
exports.
|
|
54
|
+
|
|
55
|
+
## Sources and capabilities
|
|
56
|
+
|
|
57
|
+
- UCDP GED 25.1 and 26.1 CSV files with modern field names, and authenticated GED
|
|
58
|
+
API access.
|
|
59
|
+
- ACLED event CSV exports with current lowercase field names. The supplied version
|
|
60
|
+
is a label for the exact export snapshot.
|
|
61
|
+
- Source-preserving JSON bundles, candidate generation, CSV and terminal review,
|
|
62
|
+
review reports, CSV export, and optional Parquet export.
|
|
63
|
+
|
|
64
|
+
See [source mappings](docs/source-mappings.md) for supported fields, precision
|
|
65
|
+
handling, and provenance. The [teaching notebook](examples/reconciliation_walkthrough.ipynb)
|
|
66
|
+
provides another offline example.
|
|
67
|
+
|
|
68
|
+
## Limitations
|
|
69
|
+
|
|
70
|
+
CCRT compares source accounts; it does not establish objective incident identity.
|
|
71
|
+
Candidate scores are uncalibrated proximity rankings, and generating a candidate
|
|
72
|
+
does not confirm correspondence. Real-world linkage accuracy has not been
|
|
73
|
+
independently evaluated. CCRT does not reconcile casualty totals or estimate
|
|
74
|
+
underreporting. See [validation notes](docs/validation.md) for software checks and
|
|
75
|
+
research limits.
|
|
76
|
+
|
|
77
|
+
## Contributing and licence
|
|
78
|
+
|
|
79
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md), the [changelog](CHANGELOG.md), and the
|
|
80
|
+
[release procedure](docs/releasing.md). The code and synthetic examples are under
|
|
81
|
+
the [MIT licence](LICENSE). UCDP and ACLED data have separate access, attribution,
|
|
82
|
+
and redistribution terms; obtain source access and check the terms for your snapshot.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Reproducible synthetic throughput baseline. Run after installing CCRT."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import platform
|
|
6
|
+
import time
|
|
7
|
+
import tracemalloc
|
|
8
|
+
from datetime import date, timedelta
|
|
9
|
+
|
|
10
|
+
from ccrt import Dataset, MatchConfig, SourceRecord, reconcile
|
|
11
|
+
from ccrt.schema import digest
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def run(count):
|
|
15
|
+
start = date(2000, 1, 1)
|
|
16
|
+
datasets = []
|
|
17
|
+
for source in ("left", "right"):
|
|
18
|
+
records = tuple(
|
|
19
|
+
SourceRecord(
|
|
20
|
+
source,
|
|
21
|
+
str(i),
|
|
22
|
+
"synthetic-v1",
|
|
23
|
+
start + timedelta(days=i % 3650),
|
|
24
|
+
start + timedelta(days=i % 3650),
|
|
25
|
+
latitude=(i % 90) - 45,
|
|
26
|
+
longitude=(i % 180) - 90,
|
|
27
|
+
country="Exampleland",
|
|
28
|
+
date_precision=1,
|
|
29
|
+
geo_precision=1,
|
|
30
|
+
)
|
|
31
|
+
for i in range(count)
|
|
32
|
+
)
|
|
33
|
+
datasets.append(Dataset(source, "synthetic-v1", records, digest([r.to_dict() for r in records])))
|
|
34
|
+
tracemalloc.start()
|
|
35
|
+
began = time.perf_counter()
|
|
36
|
+
result = reconcile(*datasets, config=MatchConfig(max_days=1, max_km=5))
|
|
37
|
+
elapsed = time.perf_counter() - began
|
|
38
|
+
_, peak = tracemalloc.get_traced_memory()
|
|
39
|
+
tracemalloc.stop()
|
|
40
|
+
return {
|
|
41
|
+
"records_per_source": count,
|
|
42
|
+
"candidates": len(result.candidates),
|
|
43
|
+
"elapsed_seconds": round(elapsed, 3),
|
|
44
|
+
"peak_python_mib": round(peak / 1024**2, 2),
|
|
45
|
+
"python": platform.python_version(),
|
|
46
|
+
"platform": platform.platform(),
|
|
47
|
+
"distribution": "3650 dates, repeated deterministic coordinates; max_days=1, max_km=5",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
parser = argparse.ArgumentParser()
|
|
53
|
+
parser.add_argument("--records", type=int, default=10000)
|
|
54
|
+
args = parser.parse_args()
|
|
55
|
+
if not 1 <= args.records <= 100000:
|
|
56
|
+
parser.error("--records must be between 1 and 100000")
|
|
57
|
+
print(json.dumps(run(args.records), indent=2))
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Sensitivity analysis on exhaustively labeled, bounded inputs."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import csv
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import asdict
|
|
7
|
+
|
|
8
|
+
from ccrt import MatchConfig, load_dataset, reconcile
|
|
9
|
+
from ccrt.evaluation import evaluate_candidates
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
parser = argparse.ArgumentParser()
|
|
14
|
+
parser.add_argument("left")
|
|
15
|
+
parser.add_argument("right")
|
|
16
|
+
parser.add_argument("truth_csv", help="All true pairs: left_id,right_id")
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"--exhaustive",
|
|
19
|
+
action="store_true",
|
|
20
|
+
required=True,
|
|
21
|
+
help="Acknowledge that unlabeled pairs in these inputs are negative",
|
|
22
|
+
)
|
|
23
|
+
args = parser.parse_args()
|
|
24
|
+
with open(args.truth_csv, newline="", encoding="utf-8") as stream:
|
|
25
|
+
truth = {(row["left_id"], row["right_id"]) for row in csv.DictReader(stream)}
|
|
26
|
+
left, right = load_dataset(args.left), load_dataset(args.right)
|
|
27
|
+
reports = []
|
|
28
|
+
for max_days, max_km, coarse in ((0, 1, False), (3, 25, False), (3, 25, True)):
|
|
29
|
+
config = MatchConfig(max_days=max_days, max_km=max_km, include_coarse_locations=coarse)
|
|
30
|
+
result = reconcile(left, right, config=config)
|
|
31
|
+
reports.append(
|
|
32
|
+
{
|
|
33
|
+
"config": config.to_dict(),
|
|
34
|
+
"run_id": result.run_id,
|
|
35
|
+
**asdict(evaluate_candidates(result, truth)),
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
print(json.dumps(reports, indent=2))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
main()
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Exercise an installed wheel outside its checkout, with optional table support.
|
|
2
|
+
|
|
3
|
+
Run using a fresh environment's Python from a directory outside this repository:
|
|
4
|
+
/tmp/ccrt-smoke/bin/python /path/to/repo/benchmarks/smoke_install.py
|
|
5
|
+
Then install the wheel's tables extra and repeat with --tables.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import csv
|
|
10
|
+
import importlib.util
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import tempfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import ccrt
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
23
|
+
parser.add_argument("--tables", action="store_true")
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
checkout = Path(__file__).resolve().parents[1]
|
|
26
|
+
installed = Path(ccrt.__file__).resolve()
|
|
27
|
+
if installed.is_relative_to(checkout):
|
|
28
|
+
raise RuntimeError("smoke test imported the checkout; use a fresh wheel installation")
|
|
29
|
+
if not args.tables:
|
|
30
|
+
assert importlib.util.find_spec("pandas") is None, "core smoke environment contains pandas"
|
|
31
|
+
assert importlib.util.find_spec("pyarrow") is None, "core smoke environment contains PyArrow"
|
|
32
|
+
console = Path(sys.executable).parent / ("ccrt.exe" if os.name == "nt" else "ccrt")
|
|
33
|
+
child_env = dict(os.environ)
|
|
34
|
+
child_env.pop("PYTHONPATH", None)
|
|
35
|
+
|
|
36
|
+
with tempfile.TemporaryDirectory(prefix="ccrt-installed-workflow-") as directory:
|
|
37
|
+
work = Path(directory)
|
|
38
|
+
|
|
39
|
+
def run(*arguments):
|
|
40
|
+
completed = subprocess.run(
|
|
41
|
+
[str(console), *map(str, arguments)],
|
|
42
|
+
cwd=work,
|
|
43
|
+
env=child_env,
|
|
44
|
+
capture_output=True,
|
|
45
|
+
text=True,
|
|
46
|
+
)
|
|
47
|
+
if completed.returncode:
|
|
48
|
+
raise RuntimeError(f"ccrt {' '.join(map(str, arguments))}: {completed.stderr}")
|
|
49
|
+
return completed.stdout
|
|
50
|
+
|
|
51
|
+
assert "Reviewable" in run("--help")
|
|
52
|
+
assert run("--version").strip() == ccrt.__version__
|
|
53
|
+
run(
|
|
54
|
+
"import",
|
|
55
|
+
"ucdp",
|
|
56
|
+
checkout / "examples/ucdp_synthetic.csv",
|
|
57
|
+
work / "ucdp.json",
|
|
58
|
+
"--version",
|
|
59
|
+
"26.1",
|
|
60
|
+
)
|
|
61
|
+
run(
|
|
62
|
+
"import",
|
|
63
|
+
"acled",
|
|
64
|
+
checkout / "examples/acled_synthetic.csv",
|
|
65
|
+
work / "acled.json",
|
|
66
|
+
"--version",
|
|
67
|
+
"smoke",
|
|
68
|
+
)
|
|
69
|
+
run("match", work / "ucdp.json", work / "acled.json", work / "matches")
|
|
70
|
+
result = ccrt.load_run(work / "matches/run.json")
|
|
71
|
+
assert len(result.candidates) == 3
|
|
72
|
+
candidate = next(
|
|
73
|
+
c for c in result.candidates if (c.left.source_id, c.right.source_id) == ("U1", "A1")
|
|
74
|
+
)
|
|
75
|
+
run(
|
|
76
|
+
"review",
|
|
77
|
+
work / "matches/run.json",
|
|
78
|
+
work / "review.json",
|
|
79
|
+
"--candidate",
|
|
80
|
+
candidate.candidate_id,
|
|
81
|
+
"--status",
|
|
82
|
+
"accepted",
|
|
83
|
+
"--reviewer",
|
|
84
|
+
"Smoke test",
|
|
85
|
+
)
|
|
86
|
+
run(
|
|
87
|
+
"report",
|
|
88
|
+
work / "matches/run.json",
|
|
89
|
+
"--review",
|
|
90
|
+
work / "review.json",
|
|
91
|
+
"--output",
|
|
92
|
+
work / "report.json",
|
|
93
|
+
)
|
|
94
|
+
report = json.loads((work / "report.json").read_text())
|
|
95
|
+
assert (report["accepted_count"], report["unresolved_count"]) == (1, 2)
|
|
96
|
+
run(
|
|
97
|
+
"export",
|
|
98
|
+
work / "matches/run.json",
|
|
99
|
+
work / "review.csv",
|
|
100
|
+
"--format",
|
|
101
|
+
"review-csv",
|
|
102
|
+
"--review",
|
|
103
|
+
work / "review.json",
|
|
104
|
+
)
|
|
105
|
+
with (work / "review.csv").open(newline="", encoding="utf-8") as stream:
|
|
106
|
+
rows = list(csv.DictReader(stream))
|
|
107
|
+
assert sum(row["status"] == "accepted" for row in rows) == 1
|
|
108
|
+
assert sum(row["status"] == "unlinked" for row in rows) == 4
|
|
109
|
+
run("export", work / "ucdp.json", work / "records.csv", "--format", "csv")
|
|
110
|
+
if args.tables:
|
|
111
|
+
from ccrt.export import read_parquet
|
|
112
|
+
|
|
113
|
+
run("export", work / "ucdp.json", work / "ucdp.parquet", "--format", "parquet")
|
|
114
|
+
original = ccrt.load_dataset(work / "ucdp.json")
|
|
115
|
+
assert read_parquet(work / "ucdp.parquet") == original
|
|
116
|
+
assert len(original.to_pandas()) == len(original)
|
|
117
|
+
|
|
118
|
+
subprocess.run([sys.executable, "-m", "pip", "check"], check=True, env=child_env)
|
|
119
|
+
mode = "core + tables" if args.tables else "core"
|
|
120
|
+
print(f"Installed-package workflow passed ({mode}): {installed}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
main()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Public CCRT API. Source accounts stay separate from proposed event links."""
|
|
2
|
+
|
|
3
|
+
from ccrt._version import __version__
|
|
4
|
+
from ccrt.client import UCDPClient, UCDPError
|
|
5
|
+
from ccrt.loaders import ValidationError, load_acled_csv, load_csv, load_ucdp_csv
|
|
6
|
+
from ccrt.merge import Candidate, MatchConfig, Reconciliation, reconcile
|
|
7
|
+
from ccrt.report import ReviewReport, summarize_review
|
|
8
|
+
from ccrt.review import Decision, Review
|
|
9
|
+
from ccrt.schema import Dataset, SourceRecord, ValidationIssue
|
|
10
|
+
from ccrt.storage import load_dataset, load_review, load_run, save_dataset, save_review, save_run
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"__version__",
|
|
14
|
+
"SourceRecord",
|
|
15
|
+
"Dataset",
|
|
16
|
+
"ValidationIssue",
|
|
17
|
+
"ValidationError",
|
|
18
|
+
"UCDPClient",
|
|
19
|
+
"UCDPError",
|
|
20
|
+
"load_ucdp_csv",
|
|
21
|
+
"load_acled_csv",
|
|
22
|
+
"load_csv",
|
|
23
|
+
"MatchConfig",
|
|
24
|
+
"Candidate",
|
|
25
|
+
"Reconciliation",
|
|
26
|
+
"reconcile",
|
|
27
|
+
"Decision",
|
|
28
|
+
"Review",
|
|
29
|
+
"ReviewReport",
|
|
30
|
+
"summarize_review",
|
|
31
|
+
"load_dataset",
|
|
32
|
+
"save_dataset",
|
|
33
|
+
"load_run",
|
|
34
|
+
"save_run",
|
|
35
|
+
"load_review",
|
|
36
|
+
"save_review",
|
|
37
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|