# πŸ—οΈ System Architecture ## High-Level Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ FRONTEND β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Login/ β”‚ β”‚ Dashboard β”‚ β”‚ Register β”‚ β”‚ β”‚ β”‚ Register β”‚ β”‚ (Jinja2) β”‚ β”‚ Page β”‚ β”‚ β”‚ β”‚ (Jinja2) β”‚ β”‚ + TailwindCSSβ”‚ β”‚ (Jinja2) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ JavaScript (Fetch API) β”‚ β”‚ + Chart.js for viz β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ FASTAPI BACKEND β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ API ROUTERS β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Auth β”‚ β”‚Predictionβ”‚ β”‚Dashboard β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Router β”‚ β”‚ Router β”‚ β”‚ Router β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ /api/authβ”‚ β”‚/api/pred β”‚ β”‚ /pages β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ SERVICES β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Auth β”‚ β”‚ ML β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ β”‚ β”‚ β”‚ β”‚(JWT, bcrypt) β”‚ β”‚ (Model) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Visualization Service β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ (WordCloud, Charts) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ DATA LAYER β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ SQLAlchemyβ”‚ β”‚ Pydantic β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Models β”‚ β”‚ Schemas β”‚ β”‚ β”‚ β”‚ β”‚ β”‚(ORM Layer)β”‚ β”‚(Validation) β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ DATABASE β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Users Table β”‚ β”‚ PredictionHistory β”‚ β”‚ β”‚ β”‚ - id (PK) β”‚ β”‚ - id (PK) β”‚ β”‚ β”‚ β”‚ - username β”‚ β”‚ - user_id (FK) β”‚ β”‚ β”‚ β”‚ - email β”‚ β”‚ - product_name β”‚ β”‚ β”‚ β”‚ - hashed_password β”‚ β”‚ - comment β”‚ β”‚ β”‚ β”‚ - created_at β”‚ β”‚ - predicted_rating β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - confidence_score β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - created_at β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ SQLite Database β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## Request Flow Examples ### 1️⃣ User Login Flow ``` User enters credentials β”‚ β–Ό [Login.html] β”‚ β–Ό POST /api/auth/login β”‚ β–Ό [Auth Router] β”‚ β–Ό [Auth Service] ──► Verify password (bcrypt) β”‚ Generate JWT token β–Ό [Database] ──► Query User table β”‚ β–Ό Return JWT token to frontend β”‚ β–Ό Store token in localStorage β”‚ β–Ό Redirect to /dashboard ``` ### 2️⃣ Single Prediction Flow ``` User enters comment β”‚ β–Ό [Dashboard.html] β”‚ β–Ό POST /api/predict/single (with JWT token in header) β”‚ β–Ό [Prediction Router] β”‚ β–Ό [Auth Service] ──► Verify JWT token β”‚ β–Ό [ML Service] ──► predict_single(comment) β”‚ (DUMMY: return random rating) β–Ό [Database] ──► Save to PredictionHistory β”‚ β–Ό Return {rating, confidence} β”‚ β–Ό Display result in UI ``` ### 3️⃣ Batch CSV Prediction Flow ``` User uploads CSV file β”‚ β–Ό [Dashboard.html] β”‚ β–Ό POST /api/predict/batch (multipart/form-data) β”‚ β–Ό [Prediction Router] β”‚ β–Ό Parse CSV ──► Extract comments β”‚ β–Ό [ML Service] ──► predict_batch(comments) β”‚ For each comment: β”‚ predict_single() β–Ό [Visualization Service] β”‚ β”œβ”€β”€β–Ί generate_wordcloud() β”‚ Save PNG to /static/uploads/ β”‚ └──► calculate_rating_distribution() Count 1⭐, 2⭐, 3⭐, 4⭐, 5⭐ β”‚ β–Ό [Database] ──► Save all predictions β”‚ β–Ό Return: - wordcloud_url - rating_distribution - results array β”‚ β–Ό [Dashboard.html] β”‚ β”œβ”€β”€β–Ί Render Chart.js bar chart β”œβ”€β”€β–Ί Display word cloud image β”œβ”€β”€β–Ί Populate results table └──► Enable CSV download ``` --- ## Technology Stack Details ### Backend ``` FastAPI (0.104.1) β”œβ”€β”€ Auto-generates Swagger UI (/docs) β”œβ”€β”€ Automatic data validation (Pydantic) β”œβ”€β”€ Async support └── Built-in dependency injection SQLAlchemy (2.0.23) β”œβ”€β”€ ORM for database operations β”œβ”€β”€ Models: User, PredictionHistory └── Automatic table creation JWT Authentication β”œβ”€β”€ python-jose for token generation β”œβ”€β”€ passlib[bcrypt] for password hashing └── OAuth2PasswordBearer for token validation ``` ### Frontend ``` Jinja2 Templates β”œβ”€β”€ Server-side rendering β”œβ”€β”€ Template inheritance (base.html) └── Context variables from backend TailwindCSS (CDN) β”œβ”€β”€ Utility-first CSS framework β”œβ”€β”€ Responsive design └── Custom animations Chart.js (CDN) β”œβ”€β”€ Interactive bar charts └── Rating distribution visualization JavaScript (Vanilla) β”œβ”€β”€ Fetch API for HTTP requests β”œβ”€β”€ LocalStorage for JWT token └── Dynamic DOM manipulation ``` ### Visualization ``` WordCloud (1.9.3) β”œβ”€β”€ Generate word cloud images β”œβ”€β”€ Vietnamese stopwords support └── Save to PNG files Matplotlib (3.8.2) β”œβ”€β”€ Render word cloud to image └── Non-GUI backend (Agg) ``` --- ## File Responsibilities ### Backend Files | File | Purpose | |------|---------| | `main.py` | FastAPI app initialization, router inclusion | | `config.py` | Configuration (SECRET_KEY, products list) | | `database.py` | SQLAlchemy engine, session management | | `models.py` | Database table definitions (User, PredictionHistory) | | `schemas.py` | Pydantic models for request/response validation | ### Router Files | File | Purpose | |------|---------| | `routers/auth.py` | Register, login, get current user | | `routers/prediction.py` | Single/batch prediction, history | | `routers/dashboard.py` | Serve HTML pages (login, register, dashboard) | ### Service Files | File | Purpose | |------|---------| | `services/auth_service.py` | JWT generation, password hashing, token validation | | `services/ml_service.py` | ML model wrapper, prediction logic (DUMMY) | | `services/visualization_service.py` | WordCloud generation, chart data | ### Frontend Files | File | Purpose | |------|---------| | `templates/base.html` | Base layout with navigation, CDN imports | | `templates/login.html` | Login form with JWT handling | | `templates/register.html` | Registration form | | `templates/dashboard.html` | Main interface (product select, predictions, viz) | --- ## Security Features 1. **Password Hashing:** bcrypt with salt 2. **JWT Tokens:** Signed with SECRET_KEY (HS256) 3. **Token Expiration:** 24 hours 4. **Protected Routes:** Dependency injection (`get_current_user`) 5. **CORS:** Configured for security 6. **Input Validation:** Pydantic schemas --- ## Database Schema ```sql -- Users Table CREATE TABLE users ( id INTEGER PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, hashed_password VARCHAR(255) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- PredictionHistory Table CREATE TABLE prediction_history ( id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL, product_name VARCHAR(200) NOT NULL, comment TEXT NOT NULL, predicted_rating INTEGER NOT NULL, confidence_score FLOAT, prediction_type VARCHAR(20) DEFAULT 'single', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); ``` --- ## API Response Examples ### POST /api/auth/login ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer" } ``` ### POST /api/predict/single ```json { "predicted_rating": 5, "confidence_score": 0.92, "comment": "SαΊ£n phαΊ©m rαΊ₯t tα»‘t..." } ``` ### POST /api/predict/batch ```json { "total_predictions": 20, "rating_distribution": { "1": 2, "2": 3, "3": 5, "4": 6, "5": 4 }, "wordcloud_url": "/static/uploads/wordclouds/wordcloud_20241125_143022.png", "results": [ { "Comment": "SαΊ£n phαΊ©m tα»‘t", "Predicted_Rating": 5, "Confidence": 0.95 } ], "csv_download_url": "/api/predict/download/1/1700924622.123" } ``` --- ## Deployment Checklist Before production: - [ ] Change `SECRET_KEY` in config.py - [ ] Set `reload=False` in uvicorn - [ ] Configure CORS properly - [ ] Use PostgreSQL instead of SQLite - [ ] Add environment variables (.env file) - [ ] Set up HTTPS - [ ] Add rate limiting - [ ] Configure logging - [ ] Add error monitoring - [ ] Set up backup strategy --- This architecture provides: βœ… **Separation of Concerns** βœ… **Scalability** (easy to add features) βœ… **Maintainability** (clear file structure) βœ… **Security** (JWT, password hashing) βœ… **Documentation** (auto-generated Swagger) βœ… **Testing** (clear API endpoints)