# Tourist Recommender System

A local-first tourist recommendation web application for **Paris, Istanbul, Tokyo, and Rio de Janeiro**. Uses **PageRank**, **KMeans**, and **KNN** algorithms to rank and recommend places of interest. Images are stored as base64 JPEG in SQLite and served from the backend — no client-side API calls.

---

## Windows

```bat
run.bat
```

Then open **http://127.0.0.1:8000** in your browser.

---

## Manual Setup

```bash
pip install -r requirements.txt
python manage.py migrate
python data_processor/cache_images.py      # download & store all images in DB
python data_processor/extract_colors.py    # extract dominant color palettes
python data_processor/parallel_processor.py  # PageRank + KNN + KMeans (~30s)
python manage.py runserver
```

---

## Project Structure

```
BigDFinal/
├── tourist_recommender/        # Django project settings & URLs
├── cities/                     # Main Django app
│   ├── models.py               # City, Place, Category, SimilarPlace
│   ├── views.py                # index, city_detail, poi_detail, search,
│   │                           #   place_image, city_image (base64 serving)
│   └── admin.py
├── data_collector/
│   └── collect.py              # Scrapes Wikipedia; populates DB
├── data_processor/
│   ├── cache_images.py         # Downloads images -> stores base64 in DB
│   ├── extract_colors.py       # KMeans color palette extraction (from DB)
│   ├── parallel_processor.py   # Orchestrates all processing (multiprocessing)
│   ├── pagerank.py             # Custom PageRank on place-link graph
│   ├── knn_structural.py       # TF-IDF + category KNN (same-city similarity)
│   └── kmeans_image.py         # Color histogram KMeans + cosine similarity
├── templates/                  # Django HTML templates (Bootstrap 5)
├── static/css/style.css        # Custom CSS
├── data/image_features/        # Cached numpy image feature vectors
├── db.sqlite3                  # SQLite database
├── requirements.txt
└── run.bat                     # Setup
```

---

## Database Schema

| Table | Key Columns | Description |
|-------|-------------|-------------|
| `cities_city` | id, name, country, lat, lon, image_data | City master data + base64 image |
| `cities_place` | id, city_id (FK), name, description, lat, lon, pagerank_score, image_data, dominant_colors | Place of interest |
| `cities_category` | id, name | Normalised category (3NF) |
| `cities_placecategory` | place_id, category_id | M:M junction (3NF) |
| `cities_placelink` | from_place_id, to_place_id | Directed graph for PageRank |
| `cities_similarplace` | main_place_id, similar_place_id, sim_score, similarity_type | Precomputed recommendations |

---

## Algorithms

### 1. PageRank
Graph: Places are nodes; edges connect places sharing >=1 category (`PlaceLink`).
Implementation: Custom power-iteration in `data_processor/pagerank.py`.
Result: Each place gets a `pagerank_score ∈ [0,1]` stored in `cities_place`.
UI use: Default sort order on the city view.

### 2. KNN – Structural Similarity (same city)
Features: TF-IDF (200 dims) on descriptions + one-hot category vector (weighted ×2).
Algorithm: sklearn `NearestNeighbors` with cosine distance.
Result: Top-6 structurally similar places per place, stored as `similarity_type='structural_same_city'`.

### 3. KMeans + KNN – Image Similarity
Features: RGB colour histograms (32 bins x 3 channels = 96 dims) extracted from the base64 images stored in the DB. Synthetic category-palette features used as fallback when images are unavailable.
Clustering: `KMeans(k=8)` on the full feature matrix.
Same-city: Top-6 by cosine similarity within the same city -> `image_same_city`.
cross-city: Top-6 from other cities, prioritising same cluster -> `image_other_city`.

### 4. Parallel Computing
`multiprocessing.Pool` runs PageRank and KNN workers one process per city simultaneously.
A `Manager().Queue()` acts as the shared message-passing channel (collective communication).
Results are gathered (reduce step) and bulk-inserted in the main process.

---

## Image Pipeline

Images are downloaded once and stored permanently in the SQLite database as base64-encoded JPEG, so the running application makes zero external API calls.

```
cache_images.py
  Converts stored thumb URLs to direct Wikimedia Commons URLs
  Falls back to Wikipedia API lookup when stored URL is stale (404)
  Uses Wikipedia search API as a second fallback
  Bot User-Agent (CulturalHeritageBot) for higher Wikimedia rate limits
  Stores downscaled (800×600 max) JPEG as base64 in Place.image_data / City.image_data

extract_colors.py
  Reads image_data from DB, runs KMeans(k=5) on pixels -> stores 5 hex colors
  in Place.dominant_colors, displayed as a color bar on the POI detail page
```
Images are served via Django views (`/place/<id>/image/`, `/city/<id>/image/`) with a 24-hour browser cache header.
---
Data collected via Wikipedia API (`action=query`, MediaWiki endpoint). Places validated by requiring name, coordinates, and at least one category. All 199/199 places and 4/4 cities have images stored in the database.
