Spaces:
Sleeping
Add cocktail recommendation system with data processing and web interface
Browse files- Implemented main application logic in `app.py` using Streamlit for user interaction.
- Created `CocktailDataProcessor` class in `data_processor.py` for loading, cleaning, and storing cocktail data in a PostgreSQL database.
- Added a debug script `debug.py` to troubleshoot the system components.
- Developed a demo setup script `demo_setup.py` for quick initialization with sample data.
- Introduced Docker support with `docker-compose.yml` for easy deployment.
- Added a quickstart script `quickstart.py` to guide users through setup and installation.
- Included a requirements file `requirements.txt` for dependency management.
- Created a setup script `setup.sh` for automated environment setup.
- Implemented a test script `test_system.py` to verify system components and configurations.
- Added a README file in the `data` directory to guide users on dataset requirements.
- Dockerfile +1 -1
- src/.env.example +9 -0
- src/.gitignore +219 -0
- src/Dockerfile +28 -0
- src/README.md +210 -0
- src/SETUP_GUIDE.md +142 -0
- src/{streamlit_app.py → app.py} +0 -0
- src/data/README.md +19 -0
- src/data_processor.py +256 -0
- src/debug.py +203 -0
- src/demo_setup.py +96 -0
- src/docker-compose.yml +37 -0
- src/quickstart.py +116 -0
- src/requirements.txt +9 -0
- src/setup.sh +33 -0
- src/test_system.py +165 -0
|
@@ -18,4 +18,4 @@ EXPOSE 8501
|
|
| 18 |
|
| 19 |
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
|
| 20 |
|
| 21 |
-
ENTRYPOINT ["streamlit", "run", "src/
|
|
|
|
| 18 |
|
| 19 |
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
|
| 20 |
|
| 21 |
+
ENTRYPOINT ["streamlit", "run", "src/app.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Environment variables
|
| 2 |
+
DB_HOST=localhost
|
| 3 |
+
DB_PORT=5432
|
| 4 |
+
DB_NAME=cocktails_db
|
| 5 |
+
DB_USER=postgres
|
| 6 |
+
DB_PASSWORD=your_password
|
| 7 |
+
|
| 8 |
+
# Vector embedding model
|
| 9 |
+
MODEL_NAME=all-MiniLM-L6-v2
|
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[codz]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
share/python-wheels/
|
| 24 |
+
*.egg-info/
|
| 25 |
+
.installed.cfg
|
| 26 |
+
*.egg
|
| 27 |
+
MANIFEST
|
| 28 |
+
|
| 29 |
+
# Project-specific files
|
| 30 |
+
.env
|
| 31 |
+
data/cocktails.csv
|
| 32 |
+
data/*.csv
|
| 33 |
+
logs/
|
| 34 |
+
*.log
|
| 35 |
+
|
| 36 |
+
# PyInstaller
|
| 37 |
+
# Usually these files are written by a python script from a template
|
| 38 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 39 |
+
*.manifest
|
| 40 |
+
*.spec
|
| 41 |
+
|
| 42 |
+
# Installer logs
|
| 43 |
+
pip-log.txt
|
| 44 |
+
pip-delete-this-directory.txt
|
| 45 |
+
|
| 46 |
+
# Unit test / coverage reports
|
| 47 |
+
htmlcov/
|
| 48 |
+
.tox/
|
| 49 |
+
.nox/
|
| 50 |
+
.coverage
|
| 51 |
+
.coverage.*
|
| 52 |
+
.cache
|
| 53 |
+
nosetests.xml
|
| 54 |
+
coverage.xml
|
| 55 |
+
*.cover
|
| 56 |
+
*.py.cover
|
| 57 |
+
.hypothesis/
|
| 58 |
+
.pytest_cache/
|
| 59 |
+
cover/
|
| 60 |
+
|
| 61 |
+
# Translations
|
| 62 |
+
*.mo
|
| 63 |
+
*.pot
|
| 64 |
+
|
| 65 |
+
# Django stuff:
|
| 66 |
+
*.log
|
| 67 |
+
local_settings.py
|
| 68 |
+
db.sqlite3
|
| 69 |
+
db.sqlite3-journal
|
| 70 |
+
|
| 71 |
+
# Flask stuff:
|
| 72 |
+
instance/
|
| 73 |
+
.webassets-cache
|
| 74 |
+
|
| 75 |
+
# Scrapy stuff:
|
| 76 |
+
.scrapy
|
| 77 |
+
|
| 78 |
+
# Sphinx documentation
|
| 79 |
+
docs/_build/
|
| 80 |
+
|
| 81 |
+
# PyBuilder
|
| 82 |
+
.pybuilder/
|
| 83 |
+
target/
|
| 84 |
+
|
| 85 |
+
# Jupyter Notebook
|
| 86 |
+
.ipynb_checkpoints
|
| 87 |
+
|
| 88 |
+
# IPython
|
| 89 |
+
profile_default/
|
| 90 |
+
ipython_config.py
|
| 91 |
+
|
| 92 |
+
# pyenv
|
| 93 |
+
# For a library or package, you might want to ignore these files since the code is
|
| 94 |
+
# intended to run in multiple environments; otherwise, check them in:
|
| 95 |
+
# .python-version
|
| 96 |
+
|
| 97 |
+
# pipenv
|
| 98 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 99 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 100 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 101 |
+
# install all needed dependencies.
|
| 102 |
+
#Pipfile.lock
|
| 103 |
+
|
| 104 |
+
# UV
|
| 105 |
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
| 106 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 107 |
+
# commonly ignored for libraries.
|
| 108 |
+
#uv.lock
|
| 109 |
+
|
| 110 |
+
# poetry
|
| 111 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
| 112 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 113 |
+
# commonly ignored for libraries.
|
| 114 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
| 115 |
+
#poetry.lock
|
| 116 |
+
#poetry.toml
|
| 117 |
+
|
| 118 |
+
# pdm
|
| 119 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
| 120 |
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
| 121 |
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
| 122 |
+
#pdm.lock
|
| 123 |
+
#pdm.toml
|
| 124 |
+
.pdm-python
|
| 125 |
+
.pdm-build/
|
| 126 |
+
|
| 127 |
+
# pixi
|
| 128 |
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
| 129 |
+
#pixi.lock
|
| 130 |
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
| 131 |
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
| 132 |
+
.pixi
|
| 133 |
+
|
| 134 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 135 |
+
__pypackages__/
|
| 136 |
+
|
| 137 |
+
# Celery stuff
|
| 138 |
+
celerybeat-schedule
|
| 139 |
+
celerybeat.pid
|
| 140 |
+
|
| 141 |
+
# SageMath parsed files
|
| 142 |
+
*.sage.py
|
| 143 |
+
|
| 144 |
+
# Environments
|
| 145 |
+
.env
|
| 146 |
+
.envrc
|
| 147 |
+
.venv
|
| 148 |
+
env/
|
| 149 |
+
venv/
|
| 150 |
+
ENV/
|
| 151 |
+
env.bak/
|
| 152 |
+
venv.bak/
|
| 153 |
+
|
| 154 |
+
# Spyder project settings
|
| 155 |
+
.spyderproject
|
| 156 |
+
.spyproject
|
| 157 |
+
|
| 158 |
+
# Rope project settings
|
| 159 |
+
.ropeproject
|
| 160 |
+
|
| 161 |
+
# mkdocs documentation
|
| 162 |
+
/site
|
| 163 |
+
|
| 164 |
+
# mypy
|
| 165 |
+
.mypy_cache/
|
| 166 |
+
.dmypy.json
|
| 167 |
+
dmypy.json
|
| 168 |
+
|
| 169 |
+
# Pyre type checker
|
| 170 |
+
.pyre/
|
| 171 |
+
|
| 172 |
+
# pytype static type analyzer
|
| 173 |
+
.pytype/
|
| 174 |
+
|
| 175 |
+
# Cython debug symbols
|
| 176 |
+
cython_debug/
|
| 177 |
+
|
| 178 |
+
# PyCharm
|
| 179 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
| 180 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
| 181 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
| 182 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
| 183 |
+
#.idea/
|
| 184 |
+
|
| 185 |
+
# Abstra
|
| 186 |
+
# Abstra is an AI-powered process automation framework.
|
| 187 |
+
# Ignore directories containing user credentials, local state, and settings.
|
| 188 |
+
# Learn more at https://abstra.io/docs
|
| 189 |
+
.abstra/
|
| 190 |
+
|
| 191 |
+
# Visual Studio Code
|
| 192 |
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
| 193 |
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
| 194 |
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
| 195 |
+
# you could uncomment the following to ignore the entire vscode folder
|
| 196 |
+
# .vscode/
|
| 197 |
+
|
| 198 |
+
# Ruff stuff:
|
| 199 |
+
.ruff_cache/
|
| 200 |
+
|
| 201 |
+
# PyPI configuration file
|
| 202 |
+
.pypirc
|
| 203 |
+
|
| 204 |
+
# Cursor
|
| 205 |
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
| 206 |
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
| 207 |
+
# refer to https://docs.cursor.com/context/ignore-files
|
| 208 |
+
.cursorignore
|
| 209 |
+
.cursorindexingignore
|
| 210 |
+
|
| 211 |
+
# Marimo
|
| 212 |
+
marimo/_static/
|
| 213 |
+
marimo/_lsp/
|
| 214 |
+
__marimo__/
|
| 215 |
+
.vscode
|
| 216 |
+
*.npy
|
| 217 |
+
*.csv
|
| 218 |
+
*.json
|
| 219 |
+
.env.supabase
|
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install system dependencies
|
| 6 |
+
RUN apt-get update && apt-get install -y \
|
| 7 |
+
gcc \
|
| 8 |
+
postgresql-client \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Copy requirements first for better caching
|
| 12 |
+
COPY requirements.txt .
|
| 13 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 14 |
+
|
| 15 |
+
# Copy application code
|
| 16 |
+
COPY . .
|
| 17 |
+
|
| 18 |
+
# Create data directory
|
| 19 |
+
RUN mkdir -p data logs
|
| 20 |
+
|
| 21 |
+
# Expose Streamlit port
|
| 22 |
+
EXPOSE 8501
|
| 23 |
+
|
| 24 |
+
# Health check
|
| 25 |
+
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
|
| 26 |
+
|
| 27 |
+
# Run the application
|
| 28 |
+
CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🍹 AI-Powered Cocktail Suggestions
|
| 2 |
+
|
| 3 |
+
An intelligent cocktail recommendation system using vector databases and AI embeddings to suggest the perfect drinks based on your preferences.
|
| 4 |
+
|
| 5 |
+
## 🎯 Project Overview
|
| 6 |
+
|
| 7 |
+
This project creates a smart cocktail recommendation system that
|
| 8 |
+
|
| 9 |
+
- Stores cocktail recipes in a vector database using pgvector
|
| 10 |
+
- Uses AI embeddings to understand cocktail characteristics
|
| 11 |
+
- Provides personalized suggestions based on user preferences
|
| 12 |
+
- Features a beautiful Streamlit web interface
|
| 13 |
+
|
| 14 |
+
## 🏗️ Architecture
|
| 15 |
+
|
| 16 |
+
- **Database**: PostgreSQL with pgvector extension for vector similarity search
|
| 17 |
+
- **AI Model**: SentenceTransformers for generating embeddings
|
| 18 |
+
- **Web Framework**: Streamlit for the user interface
|
| 19 |
+
- **Dataset**: Kaggle cocktails dataset with 600+ recipes
|
| 20 |
+
|
| 21 |
+
## 📊 Dataset
|
| 22 |
+
|
| 23 |
+
**Source**: https://www.kaggle.com/datasets/aadyasingh55/cocktails/data
|
| 24 |
+
|
| 25 |
+
## 🛠️ Technology Stack
|
| 26 |
+
|
| 27 |
+
- **Vector Database**: [pgvector](https://github.com/pgvector/pgvector)
|
| 28 |
+
- **Web Framework**: Streamlit
|
| 29 |
+
- **AI/ML**: SentenceTransformers, scikit-learn
|
| 30 |
+
- **Database**: PostgreSQL
|
| 31 |
+
- **Language**: Python 3.8+
|
| 32 |
+
|
| 33 |
+
## 🚀 Quick Start
|
| 34 |
+
|
| 35 |
+
### Option 1: Docker (Recommended)
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
# Clone the repository
|
| 39 |
+
git clone https://github.com/ThuanNaN/aio2025_cocktail_suggestions
|
| 40 |
+
cd aio2025_cocktail_suggestions
|
| 41 |
+
|
| 42 |
+
# Download the dataset
|
| 43 |
+
# Place cocktails.csv in the data/ directory
|
| 44 |
+
|
| 45 |
+
# Start with Docker Compose
|
| 46 |
+
docker-compose up -d
|
| 47 |
+
|
| 48 |
+
# Set up the database (first time only)
|
| 49 |
+
docker-compose exec cocktail-app python database_setup.py
|
| 50 |
+
docker-compose exec cocktail-app python data_processor.py
|
| 51 |
+
|
| 52 |
+
# Access the app at http://localhost:8501
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
### Option 2: Local Setup
|
| 56 |
+
|
| 57 |
+
```bash
|
| 58 |
+
# Clone the repository
|
| 59 |
+
git clone <repository-url>
|
| 60 |
+
cd aio2025_cocktail_suggestions
|
| 61 |
+
|
| 62 |
+
# Run the quick setup
|
| 63 |
+
python quickstart.py
|
| 64 |
+
|
| 65 |
+
# Or manual setup:
|
| 66 |
+
pip install -r requirements.txt
|
| 67 |
+
cp .env.example .env
|
| 68 |
+
# Edit .env with your database credentials
|
| 69 |
+
|
| 70 |
+
# Set up PostgreSQL with pgvector
|
| 71 |
+
# Run the database setup
|
| 72 |
+
python database_setup.py
|
| 73 |
+
|
| 74 |
+
# Process and store the cocktail data
|
| 75 |
+
python data_processor.py
|
| 76 |
+
|
| 77 |
+
# Start the Streamlit app
|
| 78 |
+
streamlit run app.py
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
## 📋 Prerequisites
|
| 82 |
+
|
| 83 |
+
### For Local Setup
|
| 84 |
+
|
| 85 |
+
- Python 3.8+
|
| 86 |
+
- PostgreSQL with pgvector extension
|
| 87 |
+
- Git
|
| 88 |
+
|
| 89 |
+
### For Docker Setup
|
| 90 |
+
|
| 91 |
+
- Docker and Docker Compose
|
| 92 |
+
|
| 93 |
+
## 🔧 Configuration
|
| 94 |
+
|
| 95 |
+
1. **Environment Variables** (`.env` file):
|
| 96 |
+
|
| 97 |
+
```env
|
| 98 |
+
DB_HOST=localhost
|
| 99 |
+
DB_PORT=5432
|
| 100 |
+
DB_NAME=cocktails_db
|
| 101 |
+
DB_USER=postgres
|
| 102 |
+
DB_PASSWORD=your_password
|
| 103 |
+
MODEL_NAME=all-MiniLM-L6-v2
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
2. **Database Setup**:
|
| 107 |
+
- Install PostgreSQL
|
| 108 |
+
- Install pgvector extension
|
| 109 |
+
- Create database and user
|
| 110 |
+
|
| 111 |
+
3. **Dataset**:
|
| 112 |
+
- Download from Kaggle
|
| 113 |
+
- Place `cocktails.csv` in `data/` directory
|
| 114 |
+
|
| 115 |
+
## 🎮 Features
|
| 116 |
+
|
| 117 |
+
### 🔍 Search Options
|
| 118 |
+
|
| 119 |
+
- **By Name**: Find specific cocktails
|
| 120 |
+
- **By Ingredients**: Get suggestions based on available ingredients
|
| 121 |
+
- **By Style/Mood**: Find drinks matching your mood (sweet, strong, refreshing, etc.)
|
| 122 |
+
- **By Occasion**: Perfect drinks for parties, date nights, etc.
|
| 123 |
+
- **Mixed Preferences**: Combine multiple criteria
|
| 124 |
+
- **By Category**: Browse by drink categories
|
| 125 |
+
- **Random Discovery**: Let AI surprise you
|
| 126 |
+
|
| 127 |
+
### 🎨 User Interface
|
| 128 |
+
|
| 129 |
+
- Modern, responsive design
|
| 130 |
+
- Real-time search and filtering
|
| 131 |
+
- Similarity scores for recommendations
|
| 132 |
+
- Detailed recipe information
|
| 133 |
+
- Ingredient highlighting
|
| 134 |
+
|
| 135 |
+
### 🧠 AI Features
|
| 136 |
+
|
| 137 |
+
- Vector similarity search
|
| 138 |
+
- Semantic understanding of preferences
|
| 139 |
+
- Contextual recommendations
|
| 140 |
+
- Personalized suggestions
|
| 141 |
+
|
| 142 |
+
## 📁 Project Structure
|
| 143 |
+
|
| 144 |
+
```text
|
| 145 |
+
aio2025_cocktail_suggestions/
|
| 146 |
+
├── app.py # Main Streamlit application
|
| 147 |
+
├── database_setup.py # Database initialization
|
| 148 |
+
├── data_processor.py # Data processing and embedding generation
|
| 149 |
+
├── recommender.py # Recommendation engine
|
| 150 |
+
├── requirements.txt # Python dependencies
|
| 151 |
+
├── docker-compose.yml # Docker setup
|
| 152 |
+
├── Dockerfile # Docker configuration
|
| 153 |
+
├── quickstart.py # Quick setup script
|
| 154 |
+
├── setup.sh # Bash setup script
|
| 155 |
+
├── .env.example # Environment variables template
|
| 156 |
+
├── data/ # Dataset directory
|
| 157 |
+
│ ├── README.md
|
| 158 |
+
│ └── final_cocktails.csv # (Download required)
|
| 159 |
+
└── README.md # This file
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
## 🔬 How It Works
|
| 163 |
+
|
| 164 |
+
1. **Data Processing**: Cocktail recipes are processed and converted into high-dimensional vectors using SentenceTransformers
|
| 165 |
+
2. **Vector Storage**: Embeddings are stored in PostgreSQL with pgvector for efficient similarity search
|
| 166 |
+
3. **Recommendation**: User preferences are converted to vectors and matched against the database using cosine similarity
|
| 167 |
+
4. **Ranking**: Results are ranked by similarity score and presented through the web interface
|
| 168 |
+
|
| 169 |
+
## 🎯 Use Cases
|
| 170 |
+
|
| 171 |
+
- **Home Bartenders**: Discover new cocktails based on available ingredients
|
| 172 |
+
- **Cocktail Enthusiasts**: Explore drinks by style and preference
|
| 173 |
+
- **Event Planning**: Find perfect drinks for specific occasions
|
| 174 |
+
- **Learning**: Understand cocktail composition and flavor profiles
|
| 175 |
+
|
| 176 |
+
## 🔮 Future Enhancements
|
| 177 |
+
|
| 178 |
+
- User rating system
|
| 179 |
+
- Personal cocktail collection
|
| 180 |
+
- Ingredient substitution suggestions
|
| 181 |
+
- Nutritional information
|
| 182 |
+
- Social sharing features
|
| 183 |
+
- Mobile app version
|
| 184 |
+
|
| 185 |
+
## 🤝 Contributing
|
| 186 |
+
|
| 187 |
+
1. Fork the repository
|
| 188 |
+
2. Create a feature branch
|
| 189 |
+
3. Make your changes
|
| 190 |
+
4. Add tests if applicable
|
| 191 |
+
5. Submit a pull request
|
| 192 |
+
|
| 193 |
+
## 📄 License
|
| 194 |
+
|
| 195 |
+
This project is open source and available under the MIT License.
|
| 196 |
+
|
| 197 |
+
## 🆘 Troubleshooting
|
| 198 |
+
|
| 199 |
+
**Common Issues:**
|
| 200 |
+
|
| 201 |
+
1. **Database Connection Error**: Check your `.env` file and ensure PostgreSQL is running
|
| 202 |
+
2. **pgvector Extension**: Make sure pgvector is properly installed in PostgreSQL
|
| 203 |
+
3. **Dataset Not Found**: Download the cocktails.csv file and place it in the data/ directory
|
| 204 |
+
4. **Memory Issues**: The embedding generation can be memory-intensive; consider processing in batches
|
| 205 |
+
|
| 206 |
+
**Support:**
|
| 207 |
+
|
| 208 |
+
- Check the logs in the `logs/` directory
|
| 209 |
+
- Ensure all dependencies are installed correctly
|
| 210 |
+
- Verify database credentials and connectivity
|
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🍹 Cocktail Suggestions - Project Setup Guide
|
| 2 |
+
|
| 3 |
+
## 📁 What We Built
|
| 4 |
+
|
| 5 |
+
A complete AI-powered cocktail recommendation system with:
|
| 6 |
+
|
| 7 |
+
### Core Components
|
| 8 |
+
|
| 9 |
+
1. **`database_setup.py`** - PostgreSQL + pgvector setup
|
| 10 |
+
2. **`data_processor.py`** - Kaggle dataset processing and embedding generation
|
| 11 |
+
3. **`recommender.py`** - AI-powered recommendation engine
|
| 12 |
+
4. **`app.py`** - Beautiful Streamlit web interface
|
| 13 |
+
|
| 14 |
+
### Supporting Files
|
| 15 |
+
|
| 16 |
+
- **`requirements.txt`** - All Python dependencies
|
| 17 |
+
- **`docker-compose.yml`** - Complete Docker setup
|
| 18 |
+
- **`quickstart.py`** - Automated setup script
|
| 19 |
+
- **`test_system.py`** - System verification
|
| 20 |
+
- **`.env.example`** - Configuration template
|
| 21 |
+
|
| 22 |
+
## 🚀 Getting Started (Choose One Method)
|
| 23 |
+
|
| 24 |
+
### Method 1: Docker (Easiest) 🐳
|
| 25 |
+
|
| 26 |
+
```bash
|
| 27 |
+
# 1. Download the cocktail dataset
|
| 28 |
+
# Go to: https://www.kaggle.com/datasets/aadyasingh55/cocktails/data
|
| 29 |
+
# Download and place cocktails.csv in data/ folder
|
| 30 |
+
|
| 31 |
+
# 2. Start everything with Docker
|
| 32 |
+
docker-compose up -d
|
| 33 |
+
|
| 34 |
+
# 3. Initialize the database (one-time setup)
|
| 35 |
+
docker-compose exec cocktail-app python database_setup.py
|
| 36 |
+
docker-compose exec cocktail-app python data_processor.py
|
| 37 |
+
|
| 38 |
+
# 4. Open http://localhost:8501 in your browser
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
### Method 2: Local Setup 💻
|
| 42 |
+
|
| 43 |
+
```bash
|
| 44 |
+
# 1. Install dependencies
|
| 45 |
+
pip install -r requirements.txt
|
| 46 |
+
|
| 47 |
+
# 2. Set up environment
|
| 48 |
+
cp .env.example .env
|
| 49 |
+
# Edit .env with your PostgreSQL credentials
|
| 50 |
+
|
| 51 |
+
# 3. Download dataset to data/cocktails.csv
|
| 52 |
+
|
| 53 |
+
# 4. Set up database
|
| 54 |
+
python database_setup.py
|
| 55 |
+
|
| 56 |
+
# 5. Process data and generate embeddings
|
| 57 |
+
python data_processor.py
|
| 58 |
+
|
| 59 |
+
# 6. Start the web app
|
| 60 |
+
streamlit run app.py
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
### Method 3: Quick Setup Script 🔧
|
| 64 |
+
|
| 65 |
+
```bash
|
| 66 |
+
# Run the automated setup
|
| 67 |
+
python quickstart.py
|
| 68 |
+
|
| 69 |
+
# Follow the instructions shown
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
## 📋 Prerequisites
|
| 73 |
+
|
| 74 |
+
### For Docker
|
| 75 |
+
|
| 76 |
+
- Docker and Docker Compose
|
| 77 |
+
- The cocktail dataset (cocktails.csv)
|
| 78 |
+
|
| 79 |
+
### For Local Setup
|
| 80 |
+
|
| 81 |
+
- Python 3.8+
|
| 82 |
+
- PostgreSQL with pgvector extension
|
| 83 |
+
- The cocktail dataset (cocktails.csv)
|
| 84 |
+
|
| 85 |
+
## 🎯 How to Use the App
|
| 86 |
+
|
| 87 |
+
### Search Options
|
| 88 |
+
|
| 89 |
+
1. **🔍 By Name** - Search for specific cocktails
|
| 90 |
+
2. **🥃 By Ingredients** - Get suggestions based on what you have
|
| 91 |
+
3. **🎭 By Style** - Find drinks by mood (sweet, strong, fruity, etc.)
|
| 92 |
+
4. **🎉 By Occasion** - Perfect drinks for parties, dates, etc.
|
| 93 |
+
5. **🎲 Mixed Preferences** - Combine multiple criteria
|
| 94 |
+
6. **📂 By Category** - Browse drink categories
|
| 95 |
+
7. **🎰 Random Discovery** - Let AI surprise you
|
| 96 |
+
|
| 97 |
+
### Features
|
| 98 |
+
|
| 99 |
+
- Real-time similarity matching
|
| 100 |
+
- Beautiful, responsive interface
|
| 101 |
+
- Detailed recipes and ingredients
|
| 102 |
+
- Similarity scores for each recommendation
|
| 103 |
+
|
| 104 |
+
## 🔧 Troubleshooting
|
| 105 |
+
|
| 106 |
+
### Common Issues
|
| 107 |
+
|
| 108 |
+
1. **"Import errors"** - Install requirements: `pip install -r requirements.txt`
|
| 109 |
+
|
| 110 |
+
2. **"Database connection failed"** - Check PostgreSQL is running and .env file
|
| 111 |
+
|
| 112 |
+
3. **"pgvector extension not found"** - Install pgvector in PostgreSQL
|
| 113 |
+
|
| 114 |
+
4. **"Dataset not found"** - Download cocktails.csv to data/ folder
|
| 115 |
+
|
| 116 |
+
5. **"Memory issues"** - The AI model needs ~2GB RAM for embeddings
|
| 117 |
+
|
| 118 |
+
### Test Your Setup
|
| 119 |
+
|
| 120 |
+
```bash
|
| 121 |
+
python test_system.py
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
## 🎉 What's Next?
|
| 125 |
+
|
| 126 |
+
After setup, you can
|
| 127 |
+
|
| 128 |
+
- Explore 600+ cocktail recipes
|
| 129 |
+
- Get personalized recommendations
|
| 130 |
+
- Discover new drinks based on your preferences
|
| 131 |
+
- Learn about cocktail ingredients and preparation
|
| 132 |
+
|
| 133 |
+
## 🆘 Need Help?
|
| 134 |
+
|
| 135 |
+
1. Check the detailed README.md
|
| 136 |
+
2. Run the test script: `python test_system.py`
|
| 137 |
+
3. Check logs in the logs/ directory
|
| 138 |
+
4. Ensure all dependencies are installed correctly
|
| 139 |
+
|
| 140 |
+
---
|
| 141 |
+
|
| 142 |
+
**Enjoy discovering your perfect cocktail! 🍹**
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dataset Directory
|
| 2 |
+
|
| 3 |
+
Please download the cocktail dataset from Kaggle and place it here.
|
| 4 |
+
|
| 5 |
+
**Dataset URL:** https://www.kaggle.com/datasets/aadyasingh55/cocktails/data
|
| 6 |
+
|
| 7 |
+
**Expected filename:** cocktails.csv
|
| 8 |
+
|
| 9 |
+
## Dataset Structure
|
| 10 |
+
|
| 11 |
+
The dataset should contain columns like:
|
| 12 |
+
|
| 13 |
+
- strDrink (cocktail name)
|
| 14 |
+
- strCategory (category)
|
| 15 |
+
- strAlcoholic (alcoholic/non-alcoholic)
|
| 16 |
+
- strGlass (glass type)
|
| 17 |
+
- strInstructions (recipe instructions)
|
| 18 |
+
- strIngredient1, strIngredient2, ... (ingredients)
|
| 19 |
+
- strMeasure1, strMeasure2, ... (measurements)
|
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
from sentence_transformers import SentenceTransformer
|
| 3 |
+
from database_setup import DatabaseSetup
|
| 4 |
+
import os
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
|
| 7 |
+
load_dotenv()
|
| 8 |
+
|
| 9 |
+
class CocktailDataProcessor:
|
| 10 |
+
def __init__(self):
|
| 11 |
+
self.model_name = os.getenv('MODEL_NAME', 'all-MiniLM-L6-v2')
|
| 12 |
+
self.model = SentenceTransformer(self.model_name)
|
| 13 |
+
self.db_setup = DatabaseSetup()
|
| 14 |
+
|
| 15 |
+
def load_data(self, csv_path):
|
| 16 |
+
"""Load cocktail data from CSV file"""
|
| 17 |
+
try:
|
| 18 |
+
df = pd.read_csv(csv_path)
|
| 19 |
+
print(f"Loaded {len(df)} cocktails from {csv_path}")
|
| 20 |
+
return df
|
| 21 |
+
except Exception as e:
|
| 22 |
+
print(f"Error loading data: {e}")
|
| 23 |
+
return None
|
| 24 |
+
|
| 25 |
+
def clean_data(self, df):
|
| 26 |
+
"""Clean and preprocess the cocktail data"""
|
| 27 |
+
# Auto-detect column names (handle both old and new formats)
|
| 28 |
+
name_col = 'name' if 'name' in df.columns else 'strDrink'
|
| 29 |
+
category_col = 'category' if 'category' in df.columns else 'strCategory'
|
| 30 |
+
alcoholic_col = 'alcoholic' if 'alcoholic' in df.columns else 'strAlcoholic'
|
| 31 |
+
glass_col = 'glassType' if 'glassType' in df.columns else 'strGlass'
|
| 32 |
+
instructions_col = 'instructions' if 'instructions' in df.columns else 'strInstructions'
|
| 33 |
+
|
| 34 |
+
print(f"Detected columns: name='{name_col}', category='{category_col}', alcoholic='{alcoholic_col}', glass='{glass_col}'")
|
| 35 |
+
|
| 36 |
+
# Remove duplicates based on name
|
| 37 |
+
if name_col in df.columns:
|
| 38 |
+
df = df.drop_duplicates(subset=[name_col])
|
| 39 |
+
print(f"After removing duplicates: {len(df)} cocktails")
|
| 40 |
+
|
| 41 |
+
# Fill missing values
|
| 42 |
+
df = df.fillna('')
|
| 43 |
+
|
| 44 |
+
# Create a combined text for embedding
|
| 45 |
+
df['combined_text'] = ''
|
| 46 |
+
|
| 47 |
+
if name_col in df.columns:
|
| 48 |
+
df['combined_text'] += df[name_col].astype(str) + ' '
|
| 49 |
+
if category_col in df.columns:
|
| 50 |
+
df['combined_text'] += df[category_col].astype(str) + ' '
|
| 51 |
+
if alcoholic_col in df.columns:
|
| 52 |
+
df['combined_text'] += df[alcoholic_col].astype(str) + ' '
|
| 53 |
+
if glass_col in df.columns:
|
| 54 |
+
df['combined_text'] += df[glass_col].astype(str) + ' '
|
| 55 |
+
|
| 56 |
+
# Handle ingredients (could be in different formats)
|
| 57 |
+
if 'ingredients' in df.columns:
|
| 58 |
+
# New format: ingredients as string/list
|
| 59 |
+
df['combined_text'] += df['ingredients'].astype(str) + ' '
|
| 60 |
+
else:
|
| 61 |
+
# Old format: strIngredient1, strIngredient2, etc.
|
| 62 |
+
ingredient_cols = [col for col in df.columns if col.startswith('strIngredient')]
|
| 63 |
+
for col in ingredient_cols:
|
| 64 |
+
df['combined_text'] += df[col].astype(str) + ' '
|
| 65 |
+
|
| 66 |
+
# Add instructions if available
|
| 67 |
+
if instructions_col in df.columns:
|
| 68 |
+
df['combined_text'] += df[instructions_col].astype(str) + ' '
|
| 69 |
+
|
| 70 |
+
# Clean the combined text
|
| 71 |
+
df['combined_text'] = df['combined_text'].str.replace(r'\s+', ' ', regex=True).str.strip()
|
| 72 |
+
|
| 73 |
+
print(f"Sample combined text: {df['combined_text'].iloc[0][:100]}...")
|
| 74 |
+
|
| 75 |
+
return df
|
| 76 |
+
|
| 77 |
+
def generate_embeddings(self, texts):
|
| 78 |
+
"""Generate embeddings for the given texts"""
|
| 79 |
+
embeddings = self.model.encode(texts, show_progress_bar=True)
|
| 80 |
+
return embeddings
|
| 81 |
+
|
| 82 |
+
def create_recipe_text(self, row):
|
| 83 |
+
"""Create a readable recipe from the row data"""
|
| 84 |
+
# Auto-detect column names
|
| 85 |
+
name_col = 'name' if 'name' in row else 'strDrink'
|
| 86 |
+
category_col = 'category' if 'category' in row else 'strCategory'
|
| 87 |
+
alcoholic_col = 'alcoholic' if 'alcoholic' in row else 'strAlcoholic'
|
| 88 |
+
glass_col = 'glassType' if 'glassType' in row else 'strGlass'
|
| 89 |
+
instructions_col = 'instructions' if 'instructions' in row else 'strInstructions'
|
| 90 |
+
|
| 91 |
+
recipe = f"Drink: {row.get(name_col, '')}\n"
|
| 92 |
+
recipe += f"Category: {row.get(category_col, '')}\n"
|
| 93 |
+
recipe += f"Type: {row.get(alcoholic_col, '')}\n"
|
| 94 |
+
recipe += f"Glass: {row.get(glass_col, '')}\n"
|
| 95 |
+
|
| 96 |
+
if row.get(instructions_col):
|
| 97 |
+
recipe += f"Instructions: {row[instructions_col]}\n"
|
| 98 |
+
|
| 99 |
+
recipe += "Ingredients:\n"
|
| 100 |
+
|
| 101 |
+
# Handle new format (ingredients as string/list)
|
| 102 |
+
if 'ingredients' in row and row['ingredients']:
|
| 103 |
+
try:
|
| 104 |
+
import ast
|
| 105 |
+
ingredients_str = row['ingredients']
|
| 106 |
+
|
| 107 |
+
# Parse ingredients list
|
| 108 |
+
if ingredients_str.startswith('['):
|
| 109 |
+
ingredients = ast.literal_eval(ingredients_str)
|
| 110 |
+
else:
|
| 111 |
+
ingredients = [ingredients_str]
|
| 112 |
+
|
| 113 |
+
# Parse measures if available
|
| 114 |
+
measures = []
|
| 115 |
+
if 'ingredientMeasures' in row and row['ingredientMeasures']:
|
| 116 |
+
measures_str = row['ingredientMeasures']
|
| 117 |
+
if measures_str.startswith('['):
|
| 118 |
+
measures = ast.literal_eval(measures_str)
|
| 119 |
+
else:
|
| 120 |
+
measures = [measures_str]
|
| 121 |
+
|
| 122 |
+
# Combine ingredients with measures
|
| 123 |
+
for i, ingredient in enumerate(ingredients):
|
| 124 |
+
if ingredient and str(ingredient).strip() and str(ingredient).strip() != 'None':
|
| 125 |
+
if i < len(measures) and measures[i] and str(measures[i]).strip() != 'None':
|
| 126 |
+
recipe += f"- {measures[i]} {ingredient}\n"
|
| 127 |
+
else:
|
| 128 |
+
recipe += f"- {ingredient}\n"
|
| 129 |
+
|
| 130 |
+
except Exception as e:
|
| 131 |
+
# Fallback: treat as simple string
|
| 132 |
+
recipe += f"- {row['ingredients']}\n"
|
| 133 |
+
|
| 134 |
+
else:
|
| 135 |
+
# Handle old format (strIngredient1, strIngredient2, etc.)
|
| 136 |
+
for i in range(1, 16): # Assuming max 15 ingredients
|
| 137 |
+
ingredient = row.get(f'strIngredient{i}')
|
| 138 |
+
measure = row.get(f'strMeasure{i}')
|
| 139 |
+
if ingredient and str(ingredient).strip() and str(ingredient).strip() != 'nan':
|
| 140 |
+
if measure and str(measure).strip() and str(measure).strip() != 'nan':
|
| 141 |
+
recipe += f"- {measure} {ingredient}\n"
|
| 142 |
+
else:
|
| 143 |
+
recipe += f"- {ingredient}\n"
|
| 144 |
+
|
| 145 |
+
return recipe
|
| 146 |
+
|
| 147 |
+
def get_ingredients_list(self, row):
|
| 148 |
+
"""Extract ingredients as a comma-separated string"""
|
| 149 |
+
ingredients = []
|
| 150 |
+
|
| 151 |
+
# Handle new format (ingredients as string/list)
|
| 152 |
+
if 'ingredients' in row and row['ingredients']:
|
| 153 |
+
try:
|
| 154 |
+
import ast
|
| 155 |
+
ingredients_str = row['ingredients']
|
| 156 |
+
|
| 157 |
+
# Parse ingredients list
|
| 158 |
+
if ingredients_str.startswith('['):
|
| 159 |
+
ingredients_list = ast.literal_eval(ingredients_str)
|
| 160 |
+
for ingredient in ingredients_list:
|
| 161 |
+
if ingredient and str(ingredient).strip() and str(ingredient).strip() != 'None':
|
| 162 |
+
ingredients.append(str(ingredient).strip())
|
| 163 |
+
else:
|
| 164 |
+
# Single ingredient as string
|
| 165 |
+
if ingredients_str.strip():
|
| 166 |
+
ingredients.append(ingredients_str.strip())
|
| 167 |
+
|
| 168 |
+
except Exception as e:
|
| 169 |
+
# Fallback: treat as simple string
|
| 170 |
+
if row['ingredients'].strip():
|
| 171 |
+
ingredients.append(row['ingredients'].strip())
|
| 172 |
+
|
| 173 |
+
else:
|
| 174 |
+
# Handle old format (strIngredient1, strIngredient2, etc.)
|
| 175 |
+
for i in range(1, 16):
|
| 176 |
+
ingredient = row.get(f'strIngredient{i}')
|
| 177 |
+
if ingredient and str(ingredient).strip() and str(ingredient).strip() != 'nan':
|
| 178 |
+
ingredients.append(str(ingredient).strip())
|
| 179 |
+
|
| 180 |
+
return ', '.join(ingredients)
|
| 181 |
+
|
| 182 |
+
def store_cocktails(self, df):
|
| 183 |
+
"""Store cocktails with embeddings in the database"""
|
| 184 |
+
try:
|
| 185 |
+
conn = self.db_setup.get_connection()
|
| 186 |
+
cursor = conn.cursor()
|
| 187 |
+
|
| 188 |
+
# Clear existing data
|
| 189 |
+
cursor.execute("DELETE FROM cocktails")
|
| 190 |
+
|
| 191 |
+
print(f"Generating embeddings for {len(df)} cocktails...")
|
| 192 |
+
# Generate all embeddings at once (much more efficient)
|
| 193 |
+
all_embeddings = self.generate_embeddings(df['combined_text'].tolist())
|
| 194 |
+
|
| 195 |
+
print("Storing cocktails in database...")
|
| 196 |
+
for idx, (_, row) in enumerate(df.iterrows()):
|
| 197 |
+
# Get pre-computed embedding
|
| 198 |
+
embedding = all_embeddings[idx]
|
| 199 |
+
|
| 200 |
+
# Prepare data with auto-detected column names
|
| 201 |
+
name_col = 'name' if 'name' in row else 'strDrink'
|
| 202 |
+
category_col = 'category' if 'category' in row else 'strCategory'
|
| 203 |
+
alcoholic_col = 'alcoholic' if 'alcoholic' in row else 'strAlcoholic'
|
| 204 |
+
glass_col = 'glassType' if 'glassType' in row else 'strGlass'
|
| 205 |
+
|
| 206 |
+
name = row.get(name_col, '')
|
| 207 |
+
ingredients = self.get_ingredients_list(row)
|
| 208 |
+
recipe = self.create_recipe_text(row)
|
| 209 |
+
glass = row.get(glass_col, '')
|
| 210 |
+
category = row.get(category_col, '')
|
| 211 |
+
iba = row.get('strIBA', '') # This might not exist in new format
|
| 212 |
+
alcoholic = row.get(alcoholic_col, '')
|
| 213 |
+
|
| 214 |
+
# Insert into database
|
| 215 |
+
cursor.execute("""
|
| 216 |
+
INSERT INTO cocktails (name, ingredients, recipe, glass, category, iba, alcoholic, embedding)
|
| 217 |
+
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
| 218 |
+
""", (name, ingredients, recipe, glass, category, iba, alcoholic, embedding.tolist()))
|
| 219 |
+
|
| 220 |
+
if (idx + 1) % 100 == 0:
|
| 221 |
+
print(f"Stored {idx + 1} cocktails...")
|
| 222 |
+
|
| 223 |
+
conn.commit()
|
| 224 |
+
cursor.close()
|
| 225 |
+
conn.close()
|
| 226 |
+
|
| 227 |
+
print(f"Successfully stored {len(df)} cocktails in the database")
|
| 228 |
+
|
| 229 |
+
except Exception as e:
|
| 230 |
+
print(f"Error storing cocktails: {e}")
|
| 231 |
+
if 'conn' in locals():
|
| 232 |
+
conn.rollback()
|
| 233 |
+
conn.close()
|
| 234 |
+
|
| 235 |
+
def process_and_store(self, csv_path):
|
| 236 |
+
"""Complete pipeline to process and store cocktail data"""
|
| 237 |
+
# Load data
|
| 238 |
+
df = self.load_data(csv_path)
|
| 239 |
+
if df is None:
|
| 240 |
+
return
|
| 241 |
+
|
| 242 |
+
# Clean data
|
| 243 |
+
df = self.clean_data(df)
|
| 244 |
+
|
| 245 |
+
# Store in database
|
| 246 |
+
self.store_cocktails(df)
|
| 247 |
+
|
| 248 |
+
if __name__ == "__main__":
|
| 249 |
+
processor = CocktailDataProcessor()
|
| 250 |
+
# Assuming the CSV file is in the data directory
|
| 251 |
+
csv_path = "data/final_cocktails.csv"
|
| 252 |
+
if os.path.exists(csv_path):
|
| 253 |
+
processor.process_and_store(csv_path)
|
| 254 |
+
else:
|
| 255 |
+
print(f"Please download the cocktails dataset and place it at {csv_path}")
|
| 256 |
+
print("Dataset URL: https://www.kaggle.com/datasets/aadyasingh55/cocktails/data")
|
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Debug script to help troubleshoot the cocktail recommendation system
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
+
|
| 10 |
+
load_dotenv()
|
| 11 |
+
|
| 12 |
+
def check_database():
|
| 13 |
+
"""Check database connection and contents"""
|
| 14 |
+
print("🔍 Checking database...")
|
| 15 |
+
|
| 16 |
+
try:
|
| 17 |
+
from database_setup import DatabaseSetup
|
| 18 |
+
db = DatabaseSetup()
|
| 19 |
+
|
| 20 |
+
# Test connection
|
| 21 |
+
conn = db.get_connection()
|
| 22 |
+
cursor = conn.cursor()
|
| 23 |
+
|
| 24 |
+
# Check if cocktails table exists
|
| 25 |
+
cursor.execute("""
|
| 26 |
+
SELECT EXISTS (
|
| 27 |
+
SELECT FROM information_schema.tables
|
| 28 |
+
WHERE table_name = 'cocktails'
|
| 29 |
+
);
|
| 30 |
+
""")
|
| 31 |
+
table_exists = cursor.fetchone()[0]
|
| 32 |
+
|
| 33 |
+
if not table_exists:
|
| 34 |
+
print("❌ Cocktails table doesn't exist")
|
| 35 |
+
print("Run: python database_setup.py")
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
print("✅ Cocktails table exists")
|
| 39 |
+
|
| 40 |
+
# Check number of cocktails
|
| 41 |
+
cursor.execute("SELECT COUNT(*) FROM cocktails")
|
| 42 |
+
count = cursor.fetchone()[0]
|
| 43 |
+
print(f"📊 Found {count} cocktails in database")
|
| 44 |
+
|
| 45 |
+
if count == 0:
|
| 46 |
+
print("❌ No cocktails in database")
|
| 47 |
+
print("Run: python data_processor.py")
|
| 48 |
+
return False
|
| 49 |
+
|
| 50 |
+
# Check if embeddings exist
|
| 51 |
+
cursor.execute("SELECT COUNT(*) FROM cocktails WHERE embedding IS NOT NULL")
|
| 52 |
+
embedding_count = cursor.fetchone()[0]
|
| 53 |
+
print(f"🧠 {embedding_count} cocktails have embeddings")
|
| 54 |
+
|
| 55 |
+
# Test a simple query
|
| 56 |
+
cursor.execute("SELECT name FROM cocktails LIMIT 3")
|
| 57 |
+
samples = cursor.fetchall()
|
| 58 |
+
print("📝 Sample cocktails:")
|
| 59 |
+
for sample in samples:
|
| 60 |
+
print(f" - {sample[0]}")
|
| 61 |
+
|
| 62 |
+
cursor.close()
|
| 63 |
+
conn.close()
|
| 64 |
+
|
| 65 |
+
return count > 0 and embedding_count > 0
|
| 66 |
+
|
| 67 |
+
except Exception as e:
|
| 68 |
+
print(f"❌ Database error: {e}")
|
| 69 |
+
return False
|
| 70 |
+
|
| 71 |
+
def test_recommender():
|
| 72 |
+
"""Test the recommendation engine"""
|
| 73 |
+
print("\n🧠 Testing recommender...")
|
| 74 |
+
|
| 75 |
+
try:
|
| 76 |
+
from recommender import CocktailRecommender
|
| 77 |
+
recommender = CocktailRecommender()
|
| 78 |
+
|
| 79 |
+
# Test random cocktails (simplest query)
|
| 80 |
+
print("Testing random cocktails...")
|
| 81 |
+
random_results = recommender.get_random_cocktails(3)
|
| 82 |
+
|
| 83 |
+
if random_results:
|
| 84 |
+
print(f"✅ Random query returned {len(random_results)} results")
|
| 85 |
+
for result in random_results:
|
| 86 |
+
cocktail = recommender.format_cocktail_result(result)
|
| 87 |
+
print(f" - {cocktail['name']}")
|
| 88 |
+
else:
|
| 89 |
+
print("❌ Random query returned no results")
|
| 90 |
+
return False
|
| 91 |
+
|
| 92 |
+
# Test ingredient search
|
| 93 |
+
print("\nTesting ingredient search...")
|
| 94 |
+
ingredient_results = recommender.recommend_by_ingredients(['vodka'], limit=3)
|
| 95 |
+
|
| 96 |
+
if ingredient_results:
|
| 97 |
+
print(f"✅ Ingredient search returned {len(ingredient_results)} results")
|
| 98 |
+
for result in ingredient_results:
|
| 99 |
+
cocktail = recommender.format_cocktail_result(result)
|
| 100 |
+
print(f" - {cocktail['name']} (Similarity: {cocktail.get('similarity', 'N/A')}%)")
|
| 101 |
+
else:
|
| 102 |
+
print("❌ Ingredient search returned no results")
|
| 103 |
+
|
| 104 |
+
return True
|
| 105 |
+
|
| 106 |
+
except Exception as e:
|
| 107 |
+
print(f"❌ Recommender error: {e}")
|
| 108 |
+
import traceback
|
| 109 |
+
traceback.print_exc()
|
| 110 |
+
return False
|
| 111 |
+
|
| 112 |
+
def check_environment():
|
| 113 |
+
"""Check environment variables"""
|
| 114 |
+
print("🔧 Checking environment...")
|
| 115 |
+
|
| 116 |
+
required_vars = ['DB_HOST', 'DB_PORT', 'DB_NAME', 'DB_USER', 'DB_PASSWORD']
|
| 117 |
+
|
| 118 |
+
for var in required_vars:
|
| 119 |
+
value = os.getenv(var)
|
| 120 |
+
if value:
|
| 121 |
+
# Hide password
|
| 122 |
+
display_value = "***" if "PASSWORD" in var else value
|
| 123 |
+
print(f"✅ {var}: {display_value}")
|
| 124 |
+
else:
|
| 125 |
+
print(f"❌ {var}: Not set")
|
| 126 |
+
|
| 127 |
+
# Check if .env file exists
|
| 128 |
+
if os.path.exists('.env'):
|
| 129 |
+
print("✅ .env file exists")
|
| 130 |
+
else:
|
| 131 |
+
print("❌ .env file not found")
|
| 132 |
+
print("Copy .env.example to .env and configure it")
|
| 133 |
+
|
| 134 |
+
def check_dataset():
|
| 135 |
+
"""Check if dataset exists"""
|
| 136 |
+
print("\n📊 Checking dataset...")
|
| 137 |
+
|
| 138 |
+
csv_path = "data/final_cocktails.csv"
|
| 139 |
+
if os.path.exists(csv_path):
|
| 140 |
+
print(f"✅ Dataset found at {csv_path}")
|
| 141 |
+
|
| 142 |
+
# Check file size
|
| 143 |
+
size = os.path.getsize(csv_path)
|
| 144 |
+
print(f"📏 File size: {size / 1024 / 1024:.1f} MB")
|
| 145 |
+
|
| 146 |
+
# Try to read first few lines
|
| 147 |
+
try:
|
| 148 |
+
import pandas as pd
|
| 149 |
+
df = pd.read_csv(csv_path, nrows=5)
|
| 150 |
+
print(f"📋 Columns: {list(df.columns)[:5]}...")
|
| 151 |
+
print(f"📈 Sample rows: {len(df)}")
|
| 152 |
+
return True
|
| 153 |
+
except Exception as e:
|
| 154 |
+
print(f"��� Error reading dataset: {e}")
|
| 155 |
+
return False
|
| 156 |
+
else:
|
| 157 |
+
print(f"❌ Dataset not found at {csv_path}")
|
| 158 |
+
print("Download from: https://www.kaggle.com/datasets/aadyasingh55/cocktails/data")
|
| 159 |
+
return False
|
| 160 |
+
|
| 161 |
+
def main():
|
| 162 |
+
print("🍹 Cocktail Recommendation System - Debug Tool")
|
| 163 |
+
print("=" * 50)
|
| 164 |
+
|
| 165 |
+
# Check environment
|
| 166 |
+
check_environment()
|
| 167 |
+
|
| 168 |
+
# Check dataset
|
| 169 |
+
dataset_ok = check_dataset()
|
| 170 |
+
|
| 171 |
+
# Check database
|
| 172 |
+
db_ok = check_database()
|
| 173 |
+
|
| 174 |
+
# Test recommender if database is OK
|
| 175 |
+
if db_ok:
|
| 176 |
+
recommender_ok = test_recommender()
|
| 177 |
+
else:
|
| 178 |
+
recommender_ok = False
|
| 179 |
+
|
| 180 |
+
print("\n📋 Summary:")
|
| 181 |
+
print(f"Dataset: {'✅' if dataset_ok else '❌'}")
|
| 182 |
+
print(f"Database: {'✅' if db_ok else '❌'}")
|
| 183 |
+
print(f"Recommender: {'✅' if recommender_ok else '❌'}")
|
| 184 |
+
|
| 185 |
+
if not dataset_ok:
|
| 186 |
+
print("\n💡 Next steps:")
|
| 187 |
+
print("1. Download the cocktail dataset")
|
| 188 |
+
print("2. Place it as data/cocktails.csv")
|
| 189 |
+
elif not db_ok:
|
| 190 |
+
print("\n💡 Next steps:")
|
| 191 |
+
print("1. Configure .env file")
|
| 192 |
+
print("2. Run: python database_setup.py")
|
| 193 |
+
print("3. Run: python data_processor.py")
|
| 194 |
+
elif not recommender_ok:
|
| 195 |
+
print("\n💡 Next steps:")
|
| 196 |
+
print("1. Check the error messages above")
|
| 197 |
+
print("2. Verify database connectivity")
|
| 198 |
+
else:
|
| 199 |
+
print("\n🎉 Everything looks good!")
|
| 200 |
+
print("You can now run: streamlit run app.py")
|
| 201 |
+
|
| 202 |
+
if __name__ == "__main__":
|
| 203 |
+
main()
|
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Quick demo setup using sample data
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
|
| 9 |
+
def setup_demo():
|
| 10 |
+
print("🍹 Setting up demo with sample data...")
|
| 11 |
+
|
| 12 |
+
# Check if we have the full dataset
|
| 13 |
+
full_dataset = "data/cocktails.csv"
|
| 14 |
+
sample_dataset = "data/sample_cocktails.csv"
|
| 15 |
+
|
| 16 |
+
dataset_to_use = None
|
| 17 |
+
|
| 18 |
+
if os.path.exists(full_dataset):
|
| 19 |
+
print(f"✅ Found full dataset: {full_dataset}")
|
| 20 |
+
dataset_to_use = full_dataset
|
| 21 |
+
elif os.path.exists(sample_dataset):
|
| 22 |
+
print(f"✅ Using sample dataset: {sample_dataset}")
|
| 23 |
+
dataset_to_use = sample_dataset
|
| 24 |
+
else:
|
| 25 |
+
print("❌ No dataset found")
|
| 26 |
+
return False
|
| 27 |
+
|
| 28 |
+
# Set up database
|
| 29 |
+
print("🗄️ Setting up database...")
|
| 30 |
+
try:
|
| 31 |
+
from database_setup import DatabaseSetup
|
| 32 |
+
db_setup = DatabaseSetup()
|
| 33 |
+
db_setup.create_database()
|
| 34 |
+
db_setup.setup_pgvector()
|
| 35 |
+
print("✅ Database setup complete")
|
| 36 |
+
except Exception as e:
|
| 37 |
+
print(f"❌ Database setup failed: {e}")
|
| 38 |
+
return False
|
| 39 |
+
|
| 40 |
+
# Process data
|
| 41 |
+
print("🧠 Processing cocktail data...")
|
| 42 |
+
try:
|
| 43 |
+
from data_processor import CocktailDataProcessor
|
| 44 |
+
processor = CocktailDataProcessor()
|
| 45 |
+
processor.process_and_store(dataset_to_use)
|
| 46 |
+
print("✅ Data processing complete")
|
| 47 |
+
except Exception as e:
|
| 48 |
+
print(f"❌ Data processing failed: {e}")
|
| 49 |
+
import traceback
|
| 50 |
+
traceback.print_exc()
|
| 51 |
+
return False
|
| 52 |
+
|
| 53 |
+
# Test the system
|
| 54 |
+
print("🧪 Testing the system...")
|
| 55 |
+
try:
|
| 56 |
+
from recommender import CocktailRecommender
|
| 57 |
+
recommender = CocktailRecommender()
|
| 58 |
+
|
| 59 |
+
# Test random cocktails
|
| 60 |
+
results = recommender.get_random_cocktails(3)
|
| 61 |
+
if results:
|
| 62 |
+
print(f"✅ System test successful - found {len(results)} cocktails")
|
| 63 |
+
for result in results:
|
| 64 |
+
cocktail = recommender.format_cocktail_result(result)
|
| 65 |
+
print(f" - {cocktail['name']}")
|
| 66 |
+
else:
|
| 67 |
+
print("❌ System test failed - no cocktails returned")
|
| 68 |
+
return False
|
| 69 |
+
|
| 70 |
+
except Exception as e:
|
| 71 |
+
print(f"❌ System test failed: {e}")
|
| 72 |
+
return False
|
| 73 |
+
|
| 74 |
+
return True
|
| 75 |
+
|
| 76 |
+
def main():
|
| 77 |
+
print("🚀 Cocktail Demo Setup")
|
| 78 |
+
print("=" * 30)
|
| 79 |
+
|
| 80 |
+
if setup_demo():
|
| 81 |
+
print("\n🎉 Demo setup complete!")
|
| 82 |
+
print("\nYou can now run:")
|
| 83 |
+
print(" streamlit run app.py")
|
| 84 |
+
print("\nOr test with:")
|
| 85 |
+
print(" python debug.py")
|
| 86 |
+
|
| 87 |
+
print("\n💡 To use the full dataset:")
|
| 88 |
+
print("1. Download cocktails.csv from Kaggle")
|
| 89 |
+
print("2. Place it in data/cocktails.csv")
|
| 90 |
+
print("3. Run this script again")
|
| 91 |
+
else:
|
| 92 |
+
print("\n❌ Demo setup failed!")
|
| 93 |
+
print("Please check the error messages above")
|
| 94 |
+
|
| 95 |
+
if __name__ == "__main__":
|
| 96 |
+
main()
|
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
services:
|
| 2 |
+
postgres:
|
| 3 |
+
image: pgvector/pgvector:pg15
|
| 4 |
+
environment:
|
| 5 |
+
POSTGRES_DB: cocktails_db
|
| 6 |
+
POSTGRES_USER: postgres
|
| 7 |
+
POSTGRES_PASSWORD: your_password
|
| 8 |
+
ports:
|
| 9 |
+
- "5432:5432"
|
| 10 |
+
volumes:
|
| 11 |
+
- postgres_data:/var/lib/postgresql/data
|
| 12 |
+
healthcheck:
|
| 13 |
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
| 14 |
+
interval: 30s
|
| 15 |
+
timeout: 10s
|
| 16 |
+
retries: 3
|
| 17 |
+
|
| 18 |
+
cocktail-app:
|
| 19 |
+
build: .
|
| 20 |
+
ports:
|
| 21 |
+
- "8501:8501"
|
| 22 |
+
environment:
|
| 23 |
+
- DB_HOST=postgres
|
| 24 |
+
- DB_PORT=5432
|
| 25 |
+
- DB_NAME=cocktails_db
|
| 26 |
+
- DB_USER=postgres
|
| 27 |
+
- DB_PASSWORD=your_password
|
| 28 |
+
- MODEL_NAME=all-MiniLM-L6-v2
|
| 29 |
+
depends_on:
|
| 30 |
+
postgres:
|
| 31 |
+
condition: service_healthy
|
| 32 |
+
volumes:
|
| 33 |
+
- ./data:/app/data
|
| 34 |
+
- ./logs:/app/logs
|
| 35 |
+
|
| 36 |
+
volumes:
|
| 37 |
+
postgres_data:
|
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Quick start script for the Cocktail Suggestions project
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import subprocess
|
| 9 |
+
|
| 10 |
+
def check_python_version():
|
| 11 |
+
"""Check if Python version is compatible"""
|
| 12 |
+
if sys.version_info < (3, 8):
|
| 13 |
+
print("❌ Python 3.8 or higher is required")
|
| 14 |
+
return False
|
| 15 |
+
print(f"✅ Python {sys.version_info.major}.{sys.version_info.minor} detected")
|
| 16 |
+
return True
|
| 17 |
+
|
| 18 |
+
def install_dependencies():
|
| 19 |
+
"""Install required dependencies"""
|
| 20 |
+
print("📦 Installing dependencies...")
|
| 21 |
+
try:
|
| 22 |
+
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
|
| 23 |
+
print("✅ Dependencies installed successfully")
|
| 24 |
+
return True
|
| 25 |
+
except subprocess.CalledProcessError:
|
| 26 |
+
print("❌ Failed to install dependencies")
|
| 27 |
+
return False
|
| 28 |
+
|
| 29 |
+
def check_env_file():
|
| 30 |
+
"""Check if .env file exists"""
|
| 31 |
+
if not os.path.exists('.env'):
|
| 32 |
+
print("📝 Creating .env file from template...")
|
| 33 |
+
if os.path.exists('.env.example'):
|
| 34 |
+
import shutil
|
| 35 |
+
shutil.copy('.env.example', '.env')
|
| 36 |
+
print("⚠️ Please edit .env file with your database credentials!")
|
| 37 |
+
else:
|
| 38 |
+
print("❌ .env.example not found")
|
| 39 |
+
return False
|
| 40 |
+
print("✅ .env file exists")
|
| 41 |
+
return True
|
| 42 |
+
|
| 43 |
+
def create_directories():
|
| 44 |
+
"""Create necessary directories"""
|
| 45 |
+
dirs = ['data', 'logs']
|
| 46 |
+
for dir_name in dirs:
|
| 47 |
+
os.makedirs(dir_name, exist_ok=True)
|
| 48 |
+
print(f"📁 Created directory: {dir_name}")
|
| 49 |
+
|
| 50 |
+
def check_dataset():
|
| 51 |
+
"""Check if dataset exists"""
|
| 52 |
+
csv_path = "data/cocktails.csv"
|
| 53 |
+
if os.path.exists(csv_path):
|
| 54 |
+
print(f"✅ Dataset found at {csv_path}")
|
| 55 |
+
return True
|
| 56 |
+
else:
|
| 57 |
+
print(f"⚠️ Dataset not found at {csv_path}")
|
| 58 |
+
print("Please download from: https://www.kaggle.com/datasets/aadyasingh55/cocktails/data")
|
| 59 |
+
return False
|
| 60 |
+
|
| 61 |
+
def main():
|
| 62 |
+
print("🍹 Cocktail Suggestions - Quick Start")
|
| 63 |
+
print("=" * 40)
|
| 64 |
+
|
| 65 |
+
# Check Python version
|
| 66 |
+
if not check_python_version():
|
| 67 |
+
return
|
| 68 |
+
|
| 69 |
+
# Create directories
|
| 70 |
+
create_directories()
|
| 71 |
+
|
| 72 |
+
# Check/create .env file
|
| 73 |
+
if not check_env_file():
|
| 74 |
+
return
|
| 75 |
+
|
| 76 |
+
# Install dependencies
|
| 77 |
+
if not install_dependencies():
|
| 78 |
+
return
|
| 79 |
+
|
| 80 |
+
# Check dataset
|
| 81 |
+
dataset_exists = check_dataset()
|
| 82 |
+
|
| 83 |
+
print("\n🎉 Setup completed!")
|
| 84 |
+
print("\nNext steps:")
|
| 85 |
+
print("1. Configure your database credentials in .env")
|
| 86 |
+
if not dataset_exists:
|
| 87 |
+
print("2. Download and place the cocktail dataset in data/cocktails.csv")
|
| 88 |
+
print("3. Run: python database_setup.py")
|
| 89 |
+
print("4. Run: python data_processor.py")
|
| 90 |
+
else:
|
| 91 |
+
print("2. Run: python database_setup.py")
|
| 92 |
+
print("3. Run: python data_processor.py")
|
| 93 |
+
print("4. Run: streamlit run app.py")
|
| 94 |
+
|
| 95 |
+
# Try to import key dependencies to verify installation
|
| 96 |
+
print("\n🔍 Verifying installations...")
|
| 97 |
+
try:
|
| 98 |
+
import streamlit
|
| 99 |
+
print("✅ Streamlit installed")
|
| 100 |
+
except ImportError:
|
| 101 |
+
print("❌ Streamlit not installed")
|
| 102 |
+
|
| 103 |
+
try:
|
| 104 |
+
import psycopg2
|
| 105 |
+
print("✅ psycopg2 installed")
|
| 106 |
+
except ImportError:
|
| 107 |
+
print("❌ psycopg2 not installed")
|
| 108 |
+
|
| 109 |
+
try:
|
| 110 |
+
import sentence_transformers
|
| 111 |
+
print("✅ sentence-transformers installed")
|
| 112 |
+
except ImportError:
|
| 113 |
+
print("❌ sentence-transformers not installed")
|
| 114 |
+
|
| 115 |
+
if __name__ == "__main__":
|
| 116 |
+
main()
|
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
+
pandas
|
| 3 |
+
numpy
|
| 4 |
+
psycopg2-binary
|
| 5 |
+
pgvector
|
| 6 |
+
sentence-transformers
|
| 7 |
+
scikit-learn
|
| 8 |
+
python-dotenv
|
| 9 |
+
requests
|
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Setup script for the Cocktail Suggestions project
|
| 4 |
+
|
| 5 |
+
echo "🍹 Setting up Cocktail Suggestions Project..."
|
| 6 |
+
|
| 7 |
+
# Create necessary directories
|
| 8 |
+
echo "📁 Creating directories..."
|
| 9 |
+
mkdir -p data
|
| 10 |
+
mkdir -p logs
|
| 11 |
+
|
| 12 |
+
# Create .env file if it doesn't exist
|
| 13 |
+
if [ ! -f .env ]; then
|
| 14 |
+
echo "📝 Creating .env file..."
|
| 15 |
+
cp .env.example .env
|
| 16 |
+
echo "⚠️ Please edit .env file with your database credentials!"
|
| 17 |
+
fi
|
| 18 |
+
|
| 19 |
+
# Install Python dependencies
|
| 20 |
+
echo "📦 Installing Python dependencies..."
|
| 21 |
+
pip install -r requirements.txt
|
| 22 |
+
|
| 23 |
+
echo "✅ Setup complete!"
|
| 24 |
+
echo ""
|
| 25 |
+
echo "Next steps:"
|
| 26 |
+
echo "1. Edit .env file with your database credentials"
|
| 27 |
+
echo "2. Set up PostgreSQL with pgvector extension"
|
| 28 |
+
echo "3. Download the cocktail dataset from:"
|
| 29 |
+
echo " https://www.kaggle.com/datasets/aadyasingh55/cocktails/data"
|
| 30 |
+
echo "4. Place the CSV file in the data/ directory"
|
| 31 |
+
echo "5. Run: python database_setup.py"
|
| 32 |
+
echo "6. Run: python data_processor.py"
|
| 33 |
+
echo "7. Run: streamlit run app.py"
|
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Simple test script to verify the system components
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import sys
|
| 7 |
+
import os
|
| 8 |
+
|
| 9 |
+
def test_imports():
|
| 10 |
+
"""Test if all required packages can be imported"""
|
| 11 |
+
print("Testing imports...")
|
| 12 |
+
|
| 13 |
+
try:
|
| 14 |
+
import pandas as pd
|
| 15 |
+
print("✅ pandas imported successfully")
|
| 16 |
+
except ImportError as e:
|
| 17 |
+
print(f"❌ pandas import failed: {e}")
|
| 18 |
+
return False
|
| 19 |
+
|
| 20 |
+
try:
|
| 21 |
+
import numpy as np
|
| 22 |
+
print("✅ numpy imported successfully")
|
| 23 |
+
except ImportError as e:
|
| 24 |
+
print(f"❌ numpy import failed: {e}")
|
| 25 |
+
return False
|
| 26 |
+
|
| 27 |
+
try:
|
| 28 |
+
import streamlit as st
|
| 29 |
+
print("✅ streamlit imported successfully")
|
| 30 |
+
except ImportError as e:
|
| 31 |
+
print(f"❌ streamlit import failed: {e}")
|
| 32 |
+
return False
|
| 33 |
+
|
| 34 |
+
try:
|
| 35 |
+
import psycopg2
|
| 36 |
+
print("✅ psycopg2 imported successfully")
|
| 37 |
+
except ImportError as e:
|
| 38 |
+
print(f"❌ psycopg2 import failed: {e}")
|
| 39 |
+
return False
|
| 40 |
+
|
| 41 |
+
try:
|
| 42 |
+
from sentence_transformers import SentenceTransformer
|
| 43 |
+
print("✅ sentence-transformers imported successfully")
|
| 44 |
+
except ImportError as e:
|
| 45 |
+
print(f"❌ sentence-transformers import failed: {e}")
|
| 46 |
+
return False
|
| 47 |
+
|
| 48 |
+
try:
|
| 49 |
+
from dotenv import load_dotenv
|
| 50 |
+
print("✅ python-dotenv imported successfully")
|
| 51 |
+
except ImportError as e:
|
| 52 |
+
print(f"❌ python-dotenv import failed: {e}")
|
| 53 |
+
return False
|
| 54 |
+
|
| 55 |
+
return True
|
| 56 |
+
|
| 57 |
+
def test_files():
|
| 58 |
+
"""Test if required files exist"""
|
| 59 |
+
print("\nTesting file structure...")
|
| 60 |
+
|
| 61 |
+
required_files = [
|
| 62 |
+
'app.py',
|
| 63 |
+
'database_setup.py',
|
| 64 |
+
'data_processor.py',
|
| 65 |
+
'recommender.py',
|
| 66 |
+
'requirements.txt',
|
| 67 |
+
'.env.example'
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
all_good = True
|
| 71 |
+
for file in required_files:
|
| 72 |
+
if os.path.exists(file):
|
| 73 |
+
print(f"✅ {file} exists")
|
| 74 |
+
else:
|
| 75 |
+
print(f"❌ {file} missing")
|
| 76 |
+
all_good = False
|
| 77 |
+
|
| 78 |
+
return all_good
|
| 79 |
+
|
| 80 |
+
def test_database_connection():
|
| 81 |
+
"""Test database connection"""
|
| 82 |
+
print("\nTesting database connection...")
|
| 83 |
+
|
| 84 |
+
try:
|
| 85 |
+
from dotenv import load_dotenv
|
| 86 |
+
load_dotenv()
|
| 87 |
+
|
| 88 |
+
import psycopg2
|
| 89 |
+
|
| 90 |
+
# Try to connect to default postgres database first
|
| 91 |
+
host = os.getenv('DB_HOST', 'localhost')
|
| 92 |
+
port = os.getenv('DB_PORT', '5432')
|
| 93 |
+
user = os.getenv('DB_USER', 'postgres')
|
| 94 |
+
password = os.getenv('DB_PASSWORD', 'your_password')
|
| 95 |
+
|
| 96 |
+
conn = psycopg2.connect(
|
| 97 |
+
host=host,
|
| 98 |
+
port=port,
|
| 99 |
+
user=user,
|
| 100 |
+
password=password,
|
| 101 |
+
database='postgres'
|
| 102 |
+
)
|
| 103 |
+
conn.close()
|
| 104 |
+
print("✅ Database connection successful")
|
| 105 |
+
return True
|
| 106 |
+
|
| 107 |
+
except Exception as e:
|
| 108 |
+
print(f"❌ Database connection failed: {e}")
|
| 109 |
+
print("Make sure PostgreSQL is running and credentials are correct in .env")
|
| 110 |
+
return False
|
| 111 |
+
|
| 112 |
+
def test_model_loading():
|
| 113 |
+
"""Test if the AI model can be loaded"""
|
| 114 |
+
print("\nTesting AI model loading...")
|
| 115 |
+
|
| 116 |
+
try:
|
| 117 |
+
from sentence_transformers import SentenceTransformer
|
| 118 |
+
model = SentenceTransformer('all-MiniLM-L6-v2')
|
| 119 |
+
print("✅ AI model loaded successfully")
|
| 120 |
+
|
| 121 |
+
# Test embedding generation
|
| 122 |
+
test_text = "vodka cranberry cocktail"
|
| 123 |
+
embedding = model.encode([test_text])
|
| 124 |
+
print(f"✅ Embedding generated successfully (shape: {embedding.shape})")
|
| 125 |
+
return True
|
| 126 |
+
|
| 127 |
+
except Exception as e:
|
| 128 |
+
print(f"❌ Model loading failed: {e}")
|
| 129 |
+
return False
|
| 130 |
+
|
| 131 |
+
def main():
|
| 132 |
+
print("🧪 Running System Tests")
|
| 133 |
+
print("=" * 40)
|
| 134 |
+
|
| 135 |
+
# Test imports
|
| 136 |
+
imports_ok = test_imports()
|
| 137 |
+
|
| 138 |
+
# Test files
|
| 139 |
+
files_ok = test_files()
|
| 140 |
+
|
| 141 |
+
# Test database (only if .env exists)
|
| 142 |
+
db_ok = True
|
| 143 |
+
if os.path.exists('.env'):
|
| 144 |
+
db_ok = test_database_connection()
|
| 145 |
+
else:
|
| 146 |
+
print("\n⚠️ Skipping database test (.env file not found)")
|
| 147 |
+
|
| 148 |
+
# Test model loading
|
| 149 |
+
model_ok = test_model_loading()
|
| 150 |
+
|
| 151 |
+
print("\n📊 Test Summary:")
|
| 152 |
+
print(f"Imports: {'✅' if imports_ok else '❌'}")
|
| 153 |
+
print(f"Files: {'✅' if files_ok else '❌'}")
|
| 154 |
+
print(f"Database: {'✅' if db_ok else '❌'}")
|
| 155 |
+
print(f"AI Model: {'✅' if model_ok else '❌'}")
|
| 156 |
+
|
| 157 |
+
if all([imports_ok, files_ok, db_ok, model_ok]):
|
| 158 |
+
print("\n🎉 All tests passed! System is ready.")
|
| 159 |
+
return 0
|
| 160 |
+
else:
|
| 161 |
+
print("\n❌ Some tests failed. Please check the issues above.")
|
| 162 |
+
return 1
|
| 163 |
+
|
| 164 |
+
if __name__ == "__main__":
|
| 165 |
+
sys.exit(main())
|