A Streamlit-based revenue reconciliation and discrepancy monitoring library-style toolkit for AdOps teams to ingest dual-source delivery reports, compute variance metrics, persist them, and visualize actionable anomalies in near real time.
Important
This repository currently ships as an executable Streamlit application. The core discrepancy computation logic is implemented in a reusable service module (services/calculator.py) and can be embedded as a lightweight internal logging/reconciliation component in larger data pipelines.
- Features
- Tech Stack & Architecture
- Getting Started
- Testing
- Deployment
- Usage
- Configuration
- License
- Contacts & Community Support
- Dual-source CSV ingestion workflow for internal ad server data and external SSP/vendor data.
- Deterministic reconciliation logic based on standardized dates and direct joins.
- Automatic discrepancy calculations for both impressions and revenue (percentage delta).
- SQLite-backed persistence using SQLAlchemy ORM, including schema bootstrap on startup.
- At-a-glance executive dashboard with KPI cards for discrepancy average, revenue gap, and data-point count.
- Interactive Plotly visualizations:
- Time-series discrepancy trend chart.
- Side-by-side revenue comparison bars.
- Configurable discrepancy threshold for alert-style highlighting in charts and tabular views.
- Conditional formatting in the tabular report for fast anomaly triage.
- Minimal architecture with clear separation between:
- UI layer (
ui/) - Service/business logic layer (
services/) - Persistence layer (
database/) - Runtime constants (
config/)
- UI layer (
Tip
The DiscrepancyCalculator service is intentionally small and stateless, making it easy to reuse as a pure transformation component in cron jobs, Airflow DAGs, or serverless workflows.
- Language: Python
- Web/UI: Streamlit
- Data Processing: Pandas
- Visualization: Plotly Express
- ORM/Data Access: SQLAlchemy
- Storage Engine: SQLite
- Packaging/Dependencies:
pip+requirements.txt
Revenue-Recon-Ad-Discrepancy-Monitor/
├── main.py # Streamlit entrypoint and route switcher
├── requirements.txt # Python dependency lock list (unpinned ranges)
├── LICENSE # Apache License 2.0
├── config/
│ └── settings.py # Global constants (threshold, DB name)
├── database/
│ ├── db_manager.py # Persistence manager (save/query workflow)
│ └── models.py # SQLAlchemy ORM models
├── services/
│ └── calculator.py # Core discrepancy merge-and-calculate engine
└── ui/
├── dashboard.py # KPI, charts, and styled data table
└── uploader.py # CSV upload and reconciliation trigger
- Simple-by-default persistence: SQLite is selected for zero-ops local execution and prototyping speed.
- Service isolation: Calculation logic is encapsulated in
DiscrepancyCalculatorto keep UI views thin. - Single model table:
DailyReportdenormalizes reconciled metrics for direct dashboard rendering. - Threshold-based observability: A global discrepancy threshold enables visual alerting without extra rule engines.
- UI workflow partitioning: Upload and dashboard experiences are split into dedicated pages for operational clarity.
flowchart LR
A[Internal CSV
Date, Impressions, Revenue] --> C[DiscrepancyCalculator.merge_and_calculate]
B[External CSV
Date, Impressions, Revenue] --> C
C --> D[Reconciled DataFrame
internal/external + discrepancy fields]
D --> E[DatabaseManager.save_report]
E --> F[(SQLite daily_reports)]
F --> G[DatabaseManager.get_all_data]
G --> H[Dashboard KPIs]
G --> I[Discrepancy Trend Chart]
G --> J[Revenue Comparison Chart]
G --> K[Styled Recon Table]
Note
Current join behavior is an inner merge on Date, meaning dates absent in either source are excluded from final output.
- Python
3.10+recommended pipfor dependency installation- Optional: virtual environment tooling (
venv,pipenv, orpoetry)
git clone https://github.com/<your-org>/Revenue-Recon-Ad-Discrepancy-Monitor.git
cd Revenue-Recon-Ad-Discrepancy-Monitor
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txtStart the app:
streamlit run main.pyAfter launch, open the provided local URL (typically http://localhost:8501).
This repository does not currently include a formal tests/ suite. Recommended validation commands:
# Static syntax validation
python -m compileall .
# Streamlit app smoke run
streamlit run main.pyIf you add test modules, suggested conventions are:
pytest -q
pytest tests/test_calculator.py -qWarning
Because no dedicated lint/test config is committed yet, CI pipelines should initially enforce at minimum python -m compileall . and application startup checks.
- Containerize the app for deterministic runtime behavior.
- Externalize the database if multi-user or high-volume ingestion is expected.
- Pin dependency versions in
requirements.txtfor reproducible builds. - Run behind a reverse proxy (e.g., Nginx) with TLS termination.
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8501
CMD ["streamlit", "run", "main.py", "--server.address=0.0.0.0", "--server.port=8501"]- On pull requests:
- Install dependencies.
- Run
python -m compileall .. - Optionally run
pytestonce tests are added.
- On merge:
- Build/push Docker image.
- Deploy to staging/production via your preferred orchestrator.
Caution
The current default database (adops_data.db) is file-based. For horizontally scaled deployments, migrate to PostgreSQL/MySQL and update engine configuration accordingly.
- Launch the app with
streamlit run main.py. - Open the Upload Data tab.
- Upload both required CSV files.
- Click Run Reconciliation Process.
- Navigate to Dashboard to review KPIs, trends, and detailed report rows.
import pandas as pd
from services.calculator import DiscrepancyCalculator
# Internal ad server export
internal_df = pd.DataFrame(
{
"Date": ["2026-01-01", "2026-01-02"],
"Impressions": [100000, 120000],
"Revenue": [230.50, 255.10],
}
)
# External SSP/vendor export
external_df = pd.DataFrame(
{
"Date": ["2026-01-01", "2026-01-02"],
"Impressions": [97000, 118500],
"Revenue": [225.10, 250.30],
}
)
# Reconcile and compute discrepancy percentages
result_df = DiscrepancyCalculator.merge_and_calculate(internal_df, external_df)
print(result_df[["date", "discrepancy_imps", "discrepancy_rev"]])The following constants are defined in config/settings.py:
DISCREPANCY_THRESHOLD: alert threshold used for chart markers and table highlighting.DB_NAME: SQLite filename used by persistence components.
DatabaseManager currently initializes SQLAlchemy with:
create_engine(f"sqlite:///{db_name}")To migrate to another RDBMS, inject a full SQLAlchemy URI and update initialization logic in database/db_manager.py.
Both uploaded CSVs must contain these columns with exact names:
DateImpressionsRevenue
Expected behavior:
Dateis parsed withpandas.to_datetime.- Merge key is
Date. - Output fields include:
internal_imps,external_imps,discrepancy_impsinternal_rev,external_rev,discrepancy_revpartner_name(currently placeholder:Demo Partner)
The project does not currently load environment variables, but a production-ready extension is to support:
DB_URL=sqlite:///adops_data.db
DISCREPANCY_THRESHOLD=5.0
STREAMLIT_SERVER_PORT=8501This project is licensed under the Apache License 2.0. See LICENSE for full terms.
If you find this tool useful, consider leaving a star on GitHub or supporting the author directly.