File size: 9,807 Bytes
abc1d97
60e9d75
 
 
857d022
60e9d75
 
abc1d97
60e9d75
abc1d97
 
60e9d75
0c2fdb7
60e9d75
0c2fdb7
60e9d75
 
 
 
 
 
 
 
 
 
 
 
 
0c2fdb7
60e9d75
 
0c2fdb7
60e9d75
0c2fdb7
60e9d75
0c2fdb7
60e9d75
 
0c2fdb7
60e9d75
0c2fdb7
60e9d75
0c2fdb7
60e9d75
 
 
 
 
 
 
 
 
 
 
 
 
 
0c2fdb7
60e9d75
0c2fdb7
60e9d75
0c2fdb7
60e9d75
 
 
0c2fdb7
60e9d75
 
 
 
 
 
 
 
0c2fdb7
60e9d75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0c2fdb7
60e9d75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0c2fdb7
60e9d75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0c2fdb7
60e9d75
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
---
title: DIS IPL 2026  Predictions
emoji: 🏏
colorFrom: blue
colorTo: red
sdk: docker
app_port: 7860
pinned: false
license: mit
---

# 🏏 DIS IPL 2026 — Team Match Predictions

A full-featured IPL match prediction app for your team. Built with Python (Flask) + SQLite.

---

## ⚡ Quick Start

```bash
pip install -r requirements.txt
set ADMIN_PASSWORD=your-admin-password-for-results-panel
# Optional: set PORT=7860 to match Hugging Face; default local port is 5000
set SECRET_KEY=long-random-string-for-session-cookies
python app.py
```

When you run **`python app.py`**, **live reload is on by default**: editing `app.py`, templates, `static/`, or seed JSON (`users.json`, etc.) restarts the dev server automatically. Set **`FLASK_DEBUG=0`** (or `false` / `off`) to disable. Production / Hugging Face uses **gunicorn** and does not use this dev reloader.

Open **`http://127.0.0.1:5000/`** when running `python app.py` locally (default port **5000**). On Hugging Face Spaces, use the Space URL. **No passwords** for team members — pick your name on the home page (roster comes from the database after the first sync from `users.json`; new players from Admin appear automatically).  
Admins use **Admin login** in the nav and enter `ADMIN_PASSWORD` (legacy: `ADMIN_SECRET` if `ADMIN_PASSWORD` is unset).

### Hugging Face Spaces (free tier — persistent state without paid disk)

The Space container disk is **ephemeral**. The app can save `ipl_predictions.db` to:

- **[Storage Buckets](https://huggingface.co/docs/hub/storage-buckets)** — S3-style object storage (see the [Python buckets guide](https://huggingface.co/docs/huggingface_hub/guides/buckets)).
- **A private Hub dataset** — Git + LFS history per upload.

You can set **both** `DIS_STATE_BUCKET` and `DIS_STATE_REPO`: every save uploads to **each** configured target (redundant backup). On restore, the app tries the **bucket first**, then the **dataset** if the local file is still missing or too small.

#### What you need to do (one-time)

1. **Create a Hugging Face account** (free) if you don’t have one.
2. **Create storage** (one or both):
   - **Bucket:** private bucket, e.g. **`Jay-Rajput/dis-ipl-state`** (Hub UI or [`hf buckets create`](https://huggingface.co/docs/huggingface_hub/guides/buckets)).
   - **Dataset:** private dataset with the same or another id, e.g. **`Jay-Rajput/dis-ipl-state`** ([new dataset](https://huggingface.co/new-dataset)). Dataset and bucket are different Hub resources; the id string can match on both sides.
3. **Create an access token** with **read/write** on the bucket and dataset you use: [Settings → Access Tokens](https://huggingface.co/settings/tokens).
4. **Create a Space**: [New Space](https://huggingface.co/new-space) → SDK **Docker** → connect this repo. **CPU basic** is enough (free).
5. **Add Space secrets** (Space → **Settings** → **Repository secrets**):
   | Secret | Value |
   |--------|--------|
   | `ADMIN_PASSWORD` | Strong password for the admin panel |
   | `SECRET_KEY` | Long random string (keep the same across redeploys so sessions stay valid) |
   | `HF_TOKEN` | Token with write access to your bucket and/or dataset |
   | `DIS_STATE_BUCKET` | *(optional)* Bucket id, e.g. `Jay-Rajput/dis-ipl-state` |
   | `DIS_STATE_REPO` | *(optional)* Dataset id, e.g. `Jay-Rajput/dis-ipl-state` |

6. **Rebuild / restart** the Space. Use the app once, then wait **~10–30 seconds** so the background upload finishes before you restart the Space to test restore.

#### How it behaves

- After a **prediction** is saved or **results are settled** (points applied for every player who had a pick), the app **uploads the full database** to **each** configured target (bucket and/or dataset) **before the page finishes loading**, so the file on Hub always includes **all members**’ predictions and scores. Other admin tweaks still use a quick background upload.
- On a **cold start**, if the local DB is missing or very small, it tries **bucket first**, then **dataset**.
- Optional **`DIS_FORCE_HUB_RESTORE=1`** forces a download from Hub (overwrites local DB). Use only when you intend to reset from remote state.

#### Optional (not required for free hosting)

- Paid Hugging Face **persistent disk** mounted at `/data`: you may set **`DIS_DATA_DIR=/data`** to keep SQLite on that volume ([Spaces storage docs](https://huggingface.co/docs/hub/spaces-storage)).
- To override bundled JSON at runtime, place `users.json` / `matches.json` / `players.json` under the same directory as the database when using `DIS_DATA_DIR`.

**Bundled data:** `users.json`, `matches.json`, and `players.json` in the repo seed the DB; the name picker reads **active users from the database**.

---

## 🎮 How It Works

### Points System
| Event | Points |
|---|---|
| ✅ Correct winner | **+bid amount** (e.g. bid 100 → earn 100) |
| ❌ Wrong winner | **−bid amount** (e.g. bid 100 → lose 100) |
| ⭐ Correct MOTM | **+75 bonus** |
| 🚫 Wrong MOTM | **−25 penalty** |
| 🤷 No MOTM predicted | **0** (safe, no risk) |
| 🌧️ Match abandoned | **bid refunded** (no winner/MOTM points) |
| 🚫 No prediction made | **0** (missed opportunity only) |

### Starting Points
- Every player starts with **1,000 points**
- Admin can adjust per-player starting points when adding them
- Points can go negative (no floor — creates drama!)

### Bid Rules
- Minimum bid: **10 points**
- Maximum bid: **500 points** OR your current balance (whichever is lower)
- Quick-bid buttons: 10%, 25%, 50%, 75%, 100% of your max bid

### Prediction Lock
- Predictions lock automatically **at the scheduled match start time**
- Once locked, no new predictions or edits are allowed
- The lock is automatic — no admin action needed

---

## 👥 Admin Guide

### Adding Matches
1. Go to **Admin → Add Match**
2. Select Team 1, Team 2, date, and time
3. Add venue and match number (optional but recommended)
4. Predictions are allowed **only on the match’s calendar day**, until the scheduled start time, then they lock automatically

### Setting Results
1. Go to **Admin → Set Results**
2. Select the winner and Man of the Match
3. Click "Set Result & Settle" — points are calculated automatically
4. If you made a mistake, check **"Reverse previous settlement & recalculate"** and re-submit

### Managing Users
- Add players: **Admin → Users → Add New Player**
- Manually adjust points (for disputes, bonuses, etc.)
- Disable/enable users

### Match Statuses
| Status | Meaning |
|---|---|
| `upcoming` | Predictions open |
| `locked` | Auto-locked at scheduled start (no new predictions) |
| `live` | Match in progress (manual — set for drama!) |
| `completed` | Match done, result can be entered |
| `abandoned` | Rain/DLS/etc — bids are automatically refunded |
| `postponed` | Rescheduled — set new date/time by deleting and re-adding |

---

## 🌍 Real-World Scenarios Handled

| Scenario | How it's handled |
|---|---|
| **2 matches in a day** | Both appear on dashboard; predict each separately |
| **Match abandoned** | Admin sets status → bids refunded automatically |
| **Wrong result entered** | Admin uses "Recalculate" to reverse & redo |
| **User forgets to predict** | No penalty — just missed opportunity |
| **User has ≤ 10 pts** | Can bid all remaining points (no forced minimum) |
| **Player name variations** (V Kohli vs Virat Kohli) | Fuzzy last-name matching |
| **No MOTM predicted** | Zero impact — neither bonus nor penalty |
| **Admin needs to correct MOTM** | Re-enter result with "Recalculate" checked |
| **New user joins mid-season** | Admin adds with custom starting points |
| **Suspended/banned user** | Admin disables account — they can't log in |

---

## 🗂️ File Structure

```
ipl-predictions/
├── app.py                    # Main Flask app (all routes + logic)
├── requirements.txt
├── ipl_predictions.db        # SQLite DB (auto-created)
└── templates/
    ├── base.html             # Shared layout, nav, styling
    ├── login.html            # Login page
    ├── dashboard.html        # Home — today's matches
    ├── matches.html          # All matches list
    ├── predict.html          # Prediction form
    ├── leaderboard.html      # Full leaderboard + podium
    ├── history.html          # Personal stats & history
    ├── admin.html            # Admin panel
    └── admin_predictions.html # Per-match prediction viewer
```

---

## 🔧 Configuration (in app.py)

```python
POINTS_CONFIG = {
    'initial': 1000,           # Starting points per player
    'min_bid': 10,             # Minimum bid
    'max_bid': 500,            # Maximum bid cap
    'correct_winner': 1.0,    # Multiplier on bid for correct winner
    'wrong_winner': -1.0,     # Multiplier on bid for wrong winner
    'correct_motm': 75,        # Flat bonus for correct MOTM
    'wrong_motm': -25,         # Flat penalty for wrong MOTM
    'lock_minutes_before': 0,  # 0 = at start; set e.g. 30 to lock N minutes before
}
```

Change these values and restart the app. Existing settled predictions are unaffected.

---

## 🚀 Production Deployment

For production (not just local use):

```bash
# Install gunicorn
pip install gunicorn

# Run with gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 app:app
```

Also set a real secret key:
```bash
export SECRET_KEY="your-very-long-random-secret-key-here"
```

---

## 🏏 IPL 2025 Teams

| Team | Abbr |
|---|---|
| Mumbai Indians | MI |
| Chennai Super Kings | CSK |
| Royal Challengers Bengaluru | RCB |
| Kolkata Knight Riders | KKR |
| Sunrisers Hyderabad | SRH |
| Delhi Capitals | DC |
| Rajasthan Royals | RR |
| Punjab Kings | PBKS |
| Lucknow Super Giants | LSG |
| Gujarat Titans | GT |