Upload a light curve. Squint at the dips. Maybe find a planet.
A NASA Space Apps Challenge entry for exoplanet hunting: a web app that lets users upload stellar light-curve data (from Kepler, K2, or TESS) and visually explore it for the periodic brightness dips characteristic of a planetary transit.
MPGA = "Modern Planetary Graph Analysis" (per the app's own
Aboutcopy).
- A user picks a mission (
Kepler,K2, orTESS) and uploads a CSV file containing light-curve data (atime/t/bjd/jdcolumn and aflux/f/pdcsap_flux/sap_flux/normalized_fluxcolumn — flexible column-name matching is built in). - The backend validates and parses the CSV (
utils/validate_csv.py, via pandas), enforcing basic sanity limits (.csvextension, non-empty, 5 MB cap, at least 20 numeric rows, up to 20,000 rows). - The parsed time/flux series is passed to a detection routine (
models/exoplanet_model.py) that computes summary statistics (mean/std flux) and flags local dips below amean - 2*stddevthreshold, grouping them into transit "candidates" with an estimated epoch, period, depth, and signal-to-noise ratio. - The API returns the light curve plus any candidates as JSON; the frontend renders the light curve as an interactive Plotly.js chart and shows the detection result.
- The page also has a decorative 3D orbit visualization (Three.js), a simple built-in "Astro Assistant" chatbot with canned astronomy facts/answers, and contact/newsletter forms that just append submissions to local log files.
⚠️ Keeping it real: the current detection logic is a placeholder heuristic, not a trained ML model (see the docstring inmodels/exoplanet_model.py— "This is a placeholder demonstrating how a trained model might be wrapped... Replace internal logic with actual model"). The/api/predictendpoint also has a separate, purely mock code path that generates synthetic sine-wave light curves and hardcoded candidates regardless of the uploaded file's actual content. Treat detections as illustrative, not scientifically validated.
- 📤 CSV upload with mission selection (Kepler / K2 / TESS)
- ✅ CSV validation with flexible column-name matching and size/row limits
- 🔭 Transit-candidate detection heuristic with summary stats (epoch, period, depth, SNR)
- 📈 Interactive light-curve plotting (Plotly.js)
- 🌌 3D animated Pluto orbit background (Three.js), with OBJ/MTL/GLB model loading
- 🤖 "Astro Assistant" chatbot with pre-programmed astronomy facts and answers
- 🔔 Toast notifications (success/error/warning/info)
- 📬 Contact form and newsletter signup (appended to local log files server-side, no email delivery)
- 💚 Health-check endpoint for deployment platforms (
/health)
The repository contains two implementations; only one is currently the active/deployed app.
| Layer | Tech |
|---|---|
| Backend | Flask 3.0, Flask-CORS, Gunicorn (production WSGI server) |
| Data/science | NumPy, Pandas, Astropy (Astroquery included but optional) |
| Frontend | Vanilla JavaScript (ES6 modules, no build step), Jinja2 templates |
| Visualization | Three.js (3D Pluto orbit background), Plotly.js (light-curve charts) |
| Styling | Tailwind CSS via CDN |
This is what Dockerfile, Procfile, and start.sh run — the Dockerfile's own comment notes "Legacy React/FastAPI stack removed; this image serves Flask app with static assets."
🗄️ Legacy/parallel app — React + Vite + FastAPI (click to expand)
Still present in the repo (src/, backend_app.py, package.json, vite.config.js, tailwind.config.js) but superseded by the Flask app for deployment:
- Frontend: React 18,
@react-three/fiber+@react-three/drei(Three.js),plotly.js-dist-min,react-dropzone, Vite, Tailwind CSS (PostCSS) - Backend: FastAPI, served with Uvicorn (
backend_app.py), with the same mock/predictbehavior
.
├── app.py # Flask app (ACTIVE): routes, CSV upload, predict endpoints
├── backend_app.py # FastAPI app (legacy React backend)
├── models/
│ └── exoplanet_model.py # Mock/placeholder transit-detection heuristic
├── utils/
│ ├── validate_csv.py # CSV parsing & validation
│ └── astronomy_facts.py # Static facts used by /facts and the chatbot
├── templates/ # Jinja2 templates for the Flask app (base.html, index.html)
├── static/ # JS/CSS/images/3D models served by Flask
│ ├── js/ # main.js, state.js, pluto.js, detection.js, chatbot.js, toast.js
│ ├── css/
│ ├── models/ # 3D model assets (OBJ/MTL/GLB)
│ └── images/
├── src/ # React source for the legacy Vite/React frontend
├── public/ # Static assets for the Vite build
├── scripts/prepareAssets.mjs # Asset prep script for the Vite build
├── requirements.txt # Python deps for the Flask app
├── environment.yml # Conda environment for the FastAPI/legacy stack
├── package.json # npm scripts/deps for the React/Vite frontend
├── Dockerfile # Builds/runs the Flask app only
├── Procfile # `web: gunicorn ... app:app` (Flask, for Heroku-style platforms)
└── start.sh # Quick-start script: creates venv, installs deps, runs Flask app
Prerequisites: Python 3.11+ and pip · Node.js and npm (only needed for the legacy React frontend)
git clone https://github.com/destivano/HuntingExoPlanets_NASA_SPACE_Apps_Challenge.git
cd HuntingExoPlanets_NASA_SPACE_Apps_Challenge
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
python app.pyThe app starts on http://localhost:8080 by default (the port comes from the PORT environment variable, defaulting to 8080; set FLASK_DEBUG=true to enable debug/hot-reload mode).
Or just run the bundled quick-start script (Linux/macOS) and let it do the venv/install/run dance for you:
./start.sh🐳 Run with Docker
docker build -t mpga .
docker run -p 8080:8080 mpga⚡ Run with Gunicorn directly (production-style)
gunicorn --bind 0.0.0.0:$PORT --workers 2 --threads 4 --timeout 60 app:app(This is exactly what Procfile and the Dockerfile invoke.)
🧪 Run the legacy React/Vite frontend (optional)
npm install
npm run dev # start Vite dev server
npm run build # production build
npm run preview # preview the production build
npm run prepare:assets # run scripts/prepareAssets.mjsThe corresponding FastAPI backend can be run separately (see environment.yml for its conda dependencies), e.g. with uvicorn backend_app:app --reload.
- Start the Flask app (see above) and open it in a browser.
- Select a mission (Kepler, K2, or TESS).
- Upload a CSV file with a time column and a flux column (headers such as
time/flux,t/f,bjd/pdcsap_flux, etc. are all recognized). - Click Analyser to submit the file for analysis.
- Review the returned light curve plot and any detected transit candidates (epoch, depth, SNR, duration).
- Optionally, open "Ask Astro" to chat with the built-in astronomy-facts assistant.
- The transit-detection heuristic is a placeholder, not trained ML — see the callout above.
/api/predicthas a mock code path that can return synthetic data regardless of the uploaded CSV's actual content.- Contact/newsletter forms log locally only — no real email delivery.
MIT License — see LICENSE. Copyright (c) 2025 Mohamed Amine Arous.