This is a web application that allows users to upload a PDF of a wine list, and it will extract the wine names, enrich the data with information from Vivino, and display it in a filterable list.
- Upload a PDF wine list.
- Extract wine names from the PDF using Google's Gemini API.
- Enrich wine data with information from the Vivino API while keeping unmatched wines and failed lookups visible.
- Calculate the Vivino match similarity with the original wine name using Sorensen-Dice coefficient.
- Display the enriched wine list in a filterable and sortable format.
- Containerized with Docker for easy setup and deployment.
- Frontend:
- React
- TypeScript
- Vite
- Tailwind CSS
- Backend:
- FastAPI (Python)
- Uvicorn
- APIs:
- Google Gemini
- Vivino (unofficial)
- Containerization:
- Docker
- Docker Compose
- Docker and Docker Compose
- Node.js and
npm(for frontend development without Docker) - Python 3.12+ and
uv(for backend development without Docker)
-
Clone the repository:
git clone https://github.com/haoren-zhou/wine-list-tool.git cd wine-list-tool -
Set up environment variables:
- Create a
.envfile in thebackenddirectory. You can copysample.envas a template. Docker Compose loads this file at runtime; the image excludes it.GEMINI_API_KEY: Your Google Gemini API key, from Google AI Studio.GEMINI_MODEL_ID(Optional): Specific Gemini model ID, see Gemini API Docs for valid model IDs- Default:
gemini-3.5-flash-lite
- Default:
- Create a
-
Build and run with Docker Compose:
docker network create front-tier docker compose up --build
The application will be available at http://localhost.
- Navigate to http://localhost.
- Click the upload area, use the keyboard-accessible file picker, or drop a
.pdfwine list onto the upload area. Processing starts when you select or drop a valid file. - Review the wine count and available ratings. Counts include unique name/vintage/volume entries. Rows show original menu names. Open "Details" beside the missing-rating count for unmatched wines and lookup failures.
- Search, filter, or sort the list. On mobile, open "Filters" to set a rating, budget, type, or format. Enter a whole-dollar budget or leave it unlimited. Initial filters include unrated wines; use "Reset filters" to restore the full list.
- Expand a wine for details, or choose "Upload another file" to start over.
FASTAPI_SERVER_ADDR: The address of the FastAPI backend (backend:8000).
FRONTEND_ORIGINS: Comma-separated list of allowed frontend origins for CORS. Change this if deploying service externally.GEMINI_API_KEYandGEMINI_MODEL_IDcan also be set here, overriding the.envfile in thebackenddirectory.VIVINO_CACHE_PATH: Optional reference-mapping cache path, default.cache/vivino-mappings.json. Compose persists/app/.cachein a named volume. Cached mappings allow startup during a Vivino outage; without a cache, unavailable grape/style names display asN.A.. See backend documentation for refresh and cleanup behavior.
(cd backend && uv run --locked pytest && uv run --locked ruff check .)
(cd frontend && npm ci && npm test && npm run lint && npm run build)The tests mock Gemini and Vivino, so they do not upload documents or require API access.
- Improve error handling in frontend (output meaningful message if error occurs)
- Add screenshots/demo to docs
- Test using event stream to construct wine list from API stream (use
generate_content_streaminstead ofgenerate_content)- Parse JSON format in backend