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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI

on:
push:
branches: [master]
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint (ruff)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python -m pip install ruff
- run: ruff check src tests
- run: ruff format --check src tests

test:
name: Test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
- name: Install
run: python -m pip install -e ".[dev,osm,contour]"
- name: Test
run: pytest --cov --cov-report=term-missing

build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python -m pip install build twine
- run: python -m build
- run: twine check dist/*
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
45 changes: 45 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Docs

on:
push:
branches: [master]
paths: ["docs/**", "mkdocs.yml", "src/**", "CHANGELOG.md", "CONTRIBUTING.md", ".github/workflows/docs.yml"]
pull_request:
paths: ["docs/**", "mkdocs.yml", "src/**", "CHANGELOG.md", "CONTRIBUTING.md", ".github/workflows/docs.yml"]
workflow_dispatch:

permissions:
contents: write

jobs:
build:
name: Build (strict)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- run: python -m pip install -e ".[docs]"
- run: mkdocs build --strict

deploy:
name: Deploy to GitHub Pages
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- run: python -m pip install -e ".[docs]"
- name: Publish to the gh-pages branch
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
mkdocs gh-deploy --force --no-history
47 changes: 47 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Publish to PyPI

# Publishes when a version tag (v1.2.3) is pushed. Uses PyPI "trusted
# publishing" (OpenID Connect), so no API token is stored in the repository:
# register this workflow once at https://pypi.org/manage/account/publishing/
# (owner: adaj, repository: predspot, workflow: publish.yml, environment: pypi).

on:
push:
tags: ["v[0-9]+.[0-9]+.[0-9]+*"]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Check that the tag matches the package version
run: |
python -m pip install -e .
PKG_VERSION="v$(python -c 'import predspot; print(predspot.__version__)')"
echo "tag=${GITHUB_REF_NAME} package=${PKG_VERSION}"
test "${GITHUB_REF_NAME}" = "${PKG_VERSION}"
- run: python -m pip install build twine
- run: python -m build
- run: twine check dist/*
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/predspot
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@
build/
dist/
*.egg-info/
.coverage
htmlcov/
.ruff_cache/
site/
examples/cache/
.cache/
65 changes: 65 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Changelog

All notable changes to Predspot are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
[Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.2.0] - 2026-09

Revival release: the code base now targets Python 3.10+ with current
versions of pandas (>= 2.2), GeoPandas (>= 1.0), scikit-learn and statsmodels.

### Added
- `crime_mapping.get_city_shape("Natal, RN, Brazil")` — thin wrapper around
`osmnx.geocode_to_gdf` returning the raw city shape.
- `examples/natal.ipynb`: an executed end-to-end walkthrough on Natal with
synthetic data, also rendered in the documentation.
- `PredictionPipeline.evaluate` accepts a list of scorings and returns a
DataFrame (one CV pass for all metrics).
- README section explaining the framework (thesis, Chapter 3, Figures 7-12);
the README is now the documentation home page.
- `crime_mapping.load_study_area("City, Country")` fetches a study area
boundary from OpenStreetMap via `osmnx` (`pip install predspot[osm]`).
- `synthetic.generate_crimes` generates synthetic events inside any study
area: Gaussian hotspots plus uniform background, with trend, annual cycle,
day-of-week and hour-of-day patterns; reproducible with `seed`.
- `QuadratCount` mapping (event counts per cell) as a first-class alternative
to `KDE`, usable with hexagonal (`create_gridhexagonal`) and square
(`create_gridsquares`) grids inside `PredictionPipeline`.
- `pipeline.build_default_pipeline` and reproducible
`pipeline.generate_testdata(..., seed=...)`.
- `PredictionPipeline.features`, `.next_time` and `random_state`.
- Test suite (pytest) and continuous integration for Python 3.10-3.13.
- Documentation rebuilt with MkDocs (Material + mkdocstrings), deployed
automatically to GitHub Pages; replaces the Sphinx site.
- `pyproject.toml` packaging (src layout) and automated PyPI publishing.

### Changed
- `tfreq` is optional in the feature classes (inferred from the series).
- Debug `print`s replaced with the `logging` module (`predspot` logger); the
`debug=` arguments were removed.
- Wrapper estimators expose their inner estimator as `.estimator`
(previously `._estimator`).
- `Dataset` no longer modifies the input DataFrame and requires the study
area to have a CRS.
- Grid centroids are computed in a projected CRS; grids accept study areas in
any CRS.
- `geojsoncontour` is an optional dependency (`pip install predspot[contour]`).

### Removed
- Sphinx documentation sources and the committed HTML build.
- `QuadratCount2`, `KGrid` and the hard dependencies on `descartes`,
`contextily` and `rtree`.

### Fixed
- Compatibility with pandas 2/3 (`'ME'` offsets, `DataFrame.append`,
positional `Series` indexing), GeoPandas 1.x (`sjoin(predicate=)`, CRS
strings, `gpd.datasets`) and scikit-learn 1.x (`FeatureUnion` internals).
- `Seasonality`/`Trend` never called `STL(...).fit()`.
- `FeatureScaling` had no `fit`, so scalers inside a `Pipeline` were never fitted.

## [0.1.3] - 2020

Original master's thesis release.
52 changes: 52 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Contributing

Thanks for your interest in Predspot! Issues and pull requests are welcome.

## Development setup

```bash
git clone https://github.com/adaj/predspot.git
cd predspot
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,osm,contour]"
```

## Checks

```bash
ruff check src tests # lint
ruff format src tests # format
pytest # tests (~10 s)
PREDSPOT_NETWORK_TESTS=1 pytest tests/test_load_study_area.py # also query OpenStreetMap
```

CI runs the same checks on Python 3.10 to 3.13 for every pull request.

## Documentation

The site is built with [MkDocs](https://www.mkdocs.org/) and
[Material](https://squidfunk.github.io/mkdocs-material/); API pages come from the
docstrings via mkdocstrings.

```bash
pip install -e ".[docs]"
mkdocs serve # live preview at http://127.0.0.1:8000
mkdocs build --strict # what CI runs
```

Pushing to `master` deploys the site to GitHub Pages automatically. The home
page is generated from `README.md` (see `docs/hooks/readme.py`), and the
example notebook is rendered from `examples/natal.ipynb`; regenerate and
re-execute it with:

```bash
python examples/build_natal_notebook.py
jupyter nbconvert --to notebook --execute --inplace examples/natal.ipynb
```

## Releasing

1. Bump `__version__` in `src/predspot/__init__.py` and update `CHANGELOG.md`.
2. Merge to `master`, then tag and push: `git tag v0.2.0 && git push origin v0.2.0`.
3. The `Publish to PyPI` workflow builds the distribution and uploads it via
PyPI trusted publishing.
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include LICENSE README.md CHANGELOG.md CONTRIBUTING.md
recursive-include tests *.py
prune docs
Loading
Loading