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
32 changes: 32 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Unit tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install R
run: |
sudo apt-get update
sudo apt-get install -y r-base r-base-dev

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: requirements.txt

- name: Install dependencies
run: pip install -r requirements.txt

- name: Run unit tests
run: python -m unittest discover -s tests -v
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ __pycache__/
.env
data/*.db
data/*.db-*
data/espn_active_players.json
data/ffanalytics_players.json
data/sleeper_players.json
.DS_Store
.streamlit/secrets.toml
24 changes: 22 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,38 @@ WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
PIP_NO_CACHE_DIR=1 \
R_HOME=/usr/lib/R

RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& apt-get install -y --no-install-recommends \
curl \
git \
r-base \
r-base-dev \
libcurl4-openssl-dev \
libssl-dev \
libxml2-dev \
&& rm -rf /var/lib/apt/lists/*

# CRAN may not ship ffanalytics for the distro R version; install from GitHub.
RUN Rscript -e '\
install.packages("remotes", repos = "https://cloud.r-project.org"); \
remotes::install_github( \
"FantasyFootballAnalytics/ffanalytics", \
upgrade = "never", \
dependencies = TRUE \
) \
'

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY config.py .
COPY api/ api/
COPY rag/ rag/
COPY draft/ draft/
COPY sdks/ sdks/
COPY app/ app/
COPY scripts/ scripts/

Expand Down
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 jlee733

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
54 changes: 48 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# StatShift

Local-only RAG stack matching the architecture diagram:
Local fantasy football statistics assistant. Query seeded NFL/fantasy data through a read-only API, or chat with a local LLM for open-ended analysis.

**Streamlit** → **RAG** → **FastAPI (read-only)** → **SQLite**
**RAG** → **Ollama** → **Gemma**

Factual questions (stats, matchups, injuries) are answered from the database. Conversational prompts go to Gemma.

## Prerequisites

- **Docker (recommended):** Docker Desktop or Docker Engine with Compose v2
- **Or local Python 3.11+** and [Ollama](https://ollama.com) with Gemma (`ollama pull gemma2:2b`)

## Run with Docker
The Docker image includes R, [ffanalytics](https://github.com/FantasyFootballAnalytics/ffanalytics), and `rpy2` for the Mock Draft tab — no separate R install when using Compose.

Yes — you interact with Streamlit in your **browser on your machine**. The UI container listens on `0.0.0.0:8501` and Compose publishes it to `localhost:8501`. Buttons, text input, and reruns all work normally; only the server runs inside Docker.
## Run with Docker

```bash
docker compose up --build
Expand All @@ -28,7 +30,7 @@ Optional: API docs at http://localhost:8000/docs
# Stop
docker compose down

# Reset DB + models
# Reset DB + models (re-seeds fantasy football sample data)
docker compose down -v
```

Expand All @@ -40,7 +42,7 @@ Services:
| `api` | FastAPI read-only API | 8000 |
| `ollama` | Gemma inference | 11434 |

Inside the Compose network, the UI talks to `http://api:8000` and `http://ollama:11434`. You do not need to call those from the browser.
Inside the Compose network, the UI talks to `http://api:8000` and `http://ollama:11434`.

## Local setup (without Docker)

Expand Down Expand Up @@ -73,7 +75,37 @@ chmod +x scripts/run_api.sh scripts/run_ui.sh
./scripts/run_ui.sh
```

Open http://127.0.0.1:8501 and ask questions about the seeded basketball stats.
Open http://127.0.0.1:8501 and ask about players, weekly matchups, injuries, or PPR scoring from the seeded data.

If you previously ran an older build with different seed data, delete `data/statshift.db` or run `docker compose down -v` before re-initializing.

## Mock Draft (Docker Compose)

With the stack running (`docker compose up --build`), open **http://localhost:8501** → **Mock Draft**. The player pool comes from **ffanalytics** (projections for Standard / Half-PPR / PPR plus ADP). R and ffanalytics are already in the `ui` image.

The cache lives in the **`statshift_data` volume** at `/app/data/ffanalytics_players.json` (24-hour TTL). It survives container restarts until you run `docker compose down -v`.

**First visit:** opening Mock Draft or clicking **Refresh player pool** triggers a scrape (often several minutes). **Later visits** use the cache.

Optional — pre-warm the cache before using the UI:

```bash
# One-off sync service (stack does not need to be up)
docker compose --profile sync run --rm ffanalytics-sync

# Or, while ui is running
docker compose exec ui python scripts/sync_ffanalytics_players.py
```

After rebuilding the image (`docker compose up --build`), run a sync again if ffanalytics or R packages changed.

## Tests

```bash
python -m unittest discover -s tests -v
```

CI runs the same suite on pull requests and pushes to `main` (ffanalytics parsing tests do not call R).

## Configuration

Expand All @@ -96,3 +128,13 @@ API_BASE_URL=http://127.0.0.1:8000
| `GET /categories` | Distinct categories |

`POST`, `PUT`, `PATCH`, and `DELETE` return **405** — writes are blocked at the API layer; SQLite is opened in read-only mode for queries.

## Sample data categories

| Category | Examples |
|----------|----------|
| `player` | McCaffrey workload, Chase targets, Kelce red-zone usage |
| `team` | Bills pace and scoring |
| `matchup` | Week 12 RB defensive matchups |
| `injury` | Week 15 practice reports |
| `league` | PPR scoring, passing leaders |
2 changes: 1 addition & 1 deletion api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

app = FastAPI(
title="StatShift API",
description="Local read-only API over SQLite. Write operations are blocked.",
description="Read-only fantasy football stats API over SQLite. Write operations are blocked.",
version="0.1.0",
)

Expand Down
Loading
Loading