Development¶
This guide explains how to set up Mappy for local development, validate source changes, maintain Google-style documentation comments, and build the MkDocs documentation site.
๐งฐ Development Environment¶
Mappy is a Python project with a Streamlit application and MkDocs documentation.
Recommended local setup:
git clone https://github.com/is-leeroy-jenkins/Mappy.git
cd Mappy
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
pip install -r requirements.txt
Confirm Python can import the project modules from the repository root.
๐ Environment Variables¶
Set required API keys before running workflows that call external providers.
Other provider-specific keys should also be set as environment variables if the related fetcher or generator workflows are used.
Do not hard-code secrets in source files, documentation files, notebooks, screenshots, or examples.
โถ๏ธ Running the App¶
Run the Streamlit app from the repository root.
The app should be run from the same directory that contains the flat source modules so imports such
as from maps import Maps and from geocode import Geocoder resolve correctly.
๐งช Validation Workflow¶
Run validation after modifying Python files.
For documentation work, build the MkDocs site.
For local preview:
Then open the local documentation URL shown by MkDocs.
๐ Documentation Standards¶
Mappy source files use Google-style docstrings so mkdocstrings can generate API documentation.
Every public class, function, and method should have a docstring.
Use this structure:
def example_method(self, name: str, count: int = 1) -> dict:
"""Summarize what the method does.
Purpose:
Explain why the method exists, what workflow it supports, and what
side effects or output shape callers should expect.
Args:
name: Human-readable name to process.
count: Number of times to process the name.
Returns:
dict: Result dictionary returned to the caller.
Raises:
Error: Raised after logging when validation or processing fails.
"""
๐งพ Docstring Rules¶
Follow these rules consistently.
| Rule | Required Practice |
|---|---|
| Summary line | Start with a one-line summary. Do not start the docstring with Purpose:. |
| Purpose section | Include Purpose: for meaningful classes, functions, and methods. |
| Args section | Use Args: only when the function has parameters other than self or cls. |
| Returns section | Use Returns: only when the method returns a meaningful value. |
| Constructors | Do not include Returns: in __init__ docstrings. |
| Attributes section | Use Attributes: for classes with meaningful instance state. |
| Raises section | Use Raises: when the method raises or wraps exceptions. |
No self |
Do not document self. |
No cls |
Do not document cls. |
| Google-style args | Use name: Description. not name (str): Description. |
| No legacy headers | Do not use Parameters:, Parametes:, Attribues:, or underline-style sections. |
โ Good Docstring Example¶
class Geocoder:
"""Provide forward, reverse, structured, and batch geocoding.
Purpose:
Coordinates Google Geocoding API requests through the shared Maps gateway
and optional cache backend. The class resolves free-form addresses,
latitude/longitude pairs, and city/state/country triples into a consistent
flattened output shape used by application, enrichment, and reporting
workflows.
Attributes:
maps: Google Maps gateway used for Geocoding API requests.
cache: Optional cache backend used to store and retrieve lookup results.
output: Last flattened geocoding result.
"""
โ Docstring Patterns to Avoid¶
Do not use this pattern:
def bad_method(self, value: str) -> None:
"""Purpose:
Does something.
Parameters:
self: The class instance.
value (str): Value to process.
Returns:
None.
"""
Problems:
| Problem | Why It Fails |
|---|---|
Starts with Purpose: |
The first line should be a summary. |
Uses Parameters: |
MkDocs/mkdocstrings expects configured Google-style sections. |
Documents self |
self is implicit and should not be documented. |
Uses value (str): |
This is not the preferred Google-style format for this project. |
Returns None |
Avoid noisy Returns: sections for methods that do not return useful values. |
๐ชต Logging Pattern¶
Operational modules that already wrap exceptions should use boogr.Error and boogr.Logger.
Required pattern:
from boogr import Error, Logger
try:
# operational logic
pass
except Exception as e:
exception = Error(e)
exception.module = "mappy"
exception.cause = "ClassOrWorkflowName"
exception.method = "method_name( self, value: str ) -> ReturnType"
Logger().write(exception)
raise exception
Use stable method signatures in exception.method.
Do not log:
| Do Not Log | Reason |
|---|---|
| API keys | Secret value. |
| Raw request payloads | May contain sensitive user input. |
| Full response payloads | May contain user or provider data. |
| Addresses or coordinates | User-provided location data. |
| DataFrame rows | May contain uploaded file contents. |
| File contents | May contain sensitive source data. |
| Cache keys | May contain normalized user input. |
๐งฑ Source File Responsibilities¶
| File | Development Responsibility |
|---|---|
app.py |
Streamlit UI and workflow orchestration. |
caches.py |
Cache interfaces and implementations. |
config.py |
Paths, environment readers, constants, and configuration helpers. |
core.py |
Lightweight result containers and core helpers. |
distances.py |
Distance Matrix wrapper and route normalization. |
excel.py |
CSV/XLSX read, write, and geospatial enrichment workflows. |
exceptions.py |
Project-specific exception hierarchy. |
fetchers.py |
External retrieval classes and fetch utilities. |
generators.py |
Content-generation classes and provider wrappers. |
geocode.py |
Forward, reverse, structured, and batch geocoding. |
maps.py |
Google Maps request gateway. |
places.py |
Places Text Search and Place Details fallback workflows. |
rates.py |
Request-rate limiting. |
staticmaps.py |
Google Static Maps URL generation. |
timezones.py |
Google Time Zone API workflows. |
๐งญ Adding a New Google Maps Service Wrapper¶
Use the existing service structure.
- Add a new module or class.
- Accept a
Mapsinstance in the constructor. - Validate required inputs with
throw_if(...). - Build a small request parameter dictionary.
- Call
self.maps.request(...). - Normalize the provider payload into a stable dictionary.
- Add cache support if repeated calls are likely.
- Add Google-style docstrings.
- Add wrapped logging only where the module already uses the project exception pattern.
- Add an API reference page under
docs/api/.
Example pattern:
class NewService:
"""Provide a concise service summary.
Purpose:
Explain the external workflow this service wraps and the normalized output
shape it returns.
"""
def __init__(self, maps: Maps) -> None:
"""Initialize the service wrapper.
Purpose:
Store the shared Maps gateway and initialize runtime state.
Args:
maps: Google Maps gateway used for service requests.
"""
self.maps = maps
self.data = None
def lookup(self, value: str) -> dict:
"""Resolve one service lookup.
Purpose:
Validate the lookup value, call the shared gateway, and normalize the
provider response into a dictionary.
Args:
value: Lookup value to resolve.
Returns:
dict: Normalized lookup result.
Raises:
Error: Raised after logging when validation or lookup fails.
"""
๐ฆ Adding a New Cache Backend¶
New cache backends should follow the BaseCache contract.
Required methods:
| Method | Expected Behavior |
|---|---|
get(key) |
Return cached dictionary or None. |
set(key, value, ttl=None) |
Store dictionary payload. |
delete(key) |
Remove one key. |
clear(namespace=None) |
Remove all records or namespace-prefixed records. |
contains(key) |
Return whether a usable record exists. |
stats() |
Return backend diagnostics. |
๐ Adding New Spreadsheet Output Columns¶
When extending enrichment output:
- Add the new key to
Excel.create_outputs(...). - Append a value in
Excel.append_empty(...). - Append a resolved value in
Excel.append_result(...). - Confirm row alignment is preserved for every source row.
- Update
user-guide.mdand API examples if the output is user-facing.
๐ฅ๏ธ Streamlit Development Rules¶
When updating app.py:
| Rule | Required Practice |
|---|---|
| Preserve session keys | Avoid renaming existing keys unless all call sites are updated. |
| Keep UI thin | Put business logic in service modules. |
| Avoid duplicate widget keys | Every repeated widget should have a stable unique key. |
| Avoid hidden side effects | Button handlers should clearly update session state. |
| Keep expensive work gated | External calls should happen only after explicit user action. |
| Validate inputs | Show user-facing warnings before service calls when possible. |
| Preserve layout | Avoid broad UI rewrites when only a service change is needed. |
๐ MkDocs Structure¶
Expected documentation structure:
docs/
index.md
architecture.md
user-guide.md
development.md
github-pages.md
api/
index.md
app.md
caches.md
config.md
core.md
distances.md
excel.md
exceptions.md
fetchers.md
generators.md
geocode.md
maps.md
places.md
rates.md
staticmaps.md
timezones.md
assets/
css/
extra.css
js/
extra.js
๐ API Reference Page Pattern¶
Each API reference page should use mkdocstrings.
Example for docs/api/geocode.md:
Example for docs/api/maps.md:
Example for docs/api/app.md:
๐งช Documentation Build¶
Build documentation locally.
Serve documentation locally.
Common issues:
| Error or Warning | Likely Cause | Fix |
|---|---|---|
| Page exists but is not in nav | A Markdown file exists under docs/ but is not listed in mkdocs.yml. |
Add it to nav or remove the file. |
| Nav page not found | mkdocs.yml references a Markdown file that does not exist. |
Create the file or remove the nav entry. |
| Import error in mkdocstrings | Source module cannot be imported. | Run from repo root and confirm dependencies are installed. |
| API docs missing members | Docstrings are missing or filters hide members. | Check docstrings and mkdocstrings options. |
| Favicon warning | Config references missing docs/img/favicon.ico. |
Add the file or remove the favicon/logo lines. |
๐ GitHub Pages Deployment¶
Build and deploy the site with MkDocs.
Then configure the repository Pages settings to serve from the gh-pages branch.
Expected public URL format:
If the repository uses a different owner, branch, or name, update site_url, repo_url, and
repo_name in mkdocs.yml.
โ Pre-Commit Checklist¶
Before committing documentation or source updates:
Review these items:
| Check | Status |
|---|---|
| Source compiles without syntax errors. | Required |
| No API keys or secrets are committed. | Required |
| New public methods have Google-style docstrings. | Required |
__init__ docstrings do not include Returns:. |
Required |
Args sections do not document self or cls. |
Required |
| Logger writes match wrapped exception paths. | Required |
Markdown files referenced in mkdocs.yml exist. |
Required |
| MkDocs build completes successfully. | Required |