---
auto_generated: true
created: 2026-05-18
manual_updated_at: 2026-05-18
modified: 2026-05-18
name: Developer_Manual
status: active
tags:
- client/hm
- domain/devops
- tech/docker
- tech/flask
- tech/python
- type/sop
type: sop
---

# H&M EMS Product Review Tool — Developer Manual

## 1. Architecture Overview

The application is a Flask-based Python web service that serves a single-page application (SPA) interface. It is designed to be deployed behind an Apache reverse proxy.

- **Backend**: Flask (Python) handles API requests, JSON data manipulation, file locking, and authentication.
- **Frontend**: A self-contained HTML/JS/CSS interface (served as `static/index.html`) manages the UI, state, and API calls.
- **Data Storage**: 
  - **Master JSON**: Campaign data is read from and written to JSON files in `MASTER_JSON_DIR`.
  - **Changelog**: Approval/unapproval actions are logged to a separate changelog file.
  - **Images**: Static campaign images are served via the filesystem path `IMAGE_BASE_PATH`.

## 2. Tech Stack

- **Language**: Python 3.x
- **Web Framework**: Flask
- **Proxy Handling**: Werkzeug `ProxyFix` (for Apache/NGINX reverse proxy support)
- **Concurrency**: Threading (for file locks)
- **Frontend**: Vanilla JavaScript, HTML5, CSS3 (no external JS frameworks)

## 3. Local Setup

### 1. Clone and Install
```bash
# Clone the repository
git clone <repo-url>
cd hm-ems-product-review-tool

# Install dependencies
pip install -r requirements.txt
```

### 2. Configure Environment
Copy `.env.example` to `.env` and adjust paths:
```bash
cp .env.example .env
```

### 3. Run Locally
```bash
python3 server.py
```
Open `http://localhost:5000` in your browser.

### 4. Run with Gunicorn (Production/Staging)
```bash
gunicorn server:app --bind 0.0.0.0:5000 --workers 4
```

## 4. Environment Variables

| Variable | Default Value | Description |
|----------|---------------|-------------|
| `MASTER_JSON_DIR` | `./Master_Json` | Directory containing campaign JSON files (e.g., `1022A.json`). |
| `IMAGE_BASE_PATH` | `./campaign_images` | Root directory for product images. Structure: `{year}/{campaign}/Automation_LR/`. |
| `PORT` | `5000` | Port the Flask server listens on. |
| `WORKERS` | `4` | Number of Gunicorn workers. |
| `SECRET_KEY` | `hm-ems-secret-key-change-in-production` | Flask secret key for session management. **Must be changed in production.** |
| `DEPLOY_USER` | `vadym.samoilenko` | Deployment user (metadata). |
| `DEPLOY_GROUP` | `vadym.samoilenko` | Deployment group (metadata). |
| `WEB_DIR` | `/var/www/html/hm-ems-report` | Apache web root directory. |
| `APP_DIR` | `/opt/hm_ems_report` | Application root directory. |
| `DATA_DIR` | `/opt/hm-ems-data` | Data root directory. |

## 5. Key Services & Entry Points

### `server.py`
- **Entry Point**: `if __name__ == '__main__': app.run()`
- **Authentication**: 
  - Hardcoded credentials in `server.py` (`admin3_M` / `Pa$$w0rd2026_!`).
  - Uses Flask sessions and a `login_required` decorator.
  - Handles AJAX 401 responses for API requests.
- **API Endpoints**:
  - `GET /api/campaigns`: Returns list of available JSON files.
  - `POST /api/load_campaign`: Loads JSON data for a selected campaign, mapping images.
  - `POST /api/approve`: Saves edits to the master JSON, locks fields, and logs the change.
  - `GET /images/<path>`: Serves campaign images from the filesystem.

### `html_generator.py`
- **Static Content Generator**: 
  - Contains `LANGUAGE_DISPLAY_NAMES` mapping for UI labels.
  - Provides helper functions `_get_campaign_prefix`, `_get_main_image_filename`, `_get_all_image_filenames` to resolve image paths based on campaign structure.
  - *Note*: While named `html_generator`, it primarily serves as a utility module for image/path logic in this context.

### `static/index.html`
- **Frontend SPA**: 
  - Handles campaign switching, language selection, inline editing, and approval UI.
  - Communicates with backend via `fetch` API calls.
  - Implements image preview modal logic.

## 6. API Reference

### `GET /api/campaigns`
Returns a JSON list of available campaign files.
```json
["1022A.json", "2023.json"]
```

### `POST /api/load_campaign`
Request Body:
```json
{"filename": "1022A.json"}
```
Returns the full campaign JSON data with pre-computed image paths.

### `POST /api/approve`
Request Body:
```json
{
  "filename": "1022A.json",
  "article_id": "ART123456",
  "language": "de-de",
  "field": "name",
  "value": "Neuer Name"
}
```
Returns `{"status": "success", "message": "Approved"}` or `{"status": "error", "message": "..."}`.

## 7. Deployment

### Apache Configuration
The application is designed to run behind Apache with mod_wsgi. Key considerations:
1. Ensure the `APPLICATION_ROOT` is set in `server.py` (`/hm-ems-report`).
2. Configure Apache VirtualHost to proxy requests to the Gunicorn socket/port.
3. Ensure the `DEPLOY_USER` has read/write permissions to `MASTER_JSON_DIR` and `IMAGE_BASE_PATH`.

### File Permissions
```bash
chown -R vadym.samoilenko:vadym.samoilenko /opt/hm-ems-data
chmod -R 755 /opt/hm-ems-data
```

## 8. Known Gotchas

1. **Session Timeout**: Flask sessions rely on the `SECRET_KEY`. If changed, all users will be logged out.
2. **Image Path Resolution**: Image paths are relative to `IMAGE_BASE_PATH`. If the directory structure (`year/campaign/Automation_LR`) changes, `_get_all_image_filenames` must be updated.
3. **JSON Concurrency**: The `file_lock` in `server.py` uses a simple threading lock. For high-concurrency environments, consider using a database or more robust file locking mechanisms.
4. **Hardcoded Credentials**: The login credentials are hardcoded in `server.py`. For production, migrate to a proper authentication system (e.g., LDAP, OAuth).
5. **Large Campaigns**: Loading very large JSON files may cause timeout issues. Consider pagination or lazy loading in the frontend for campaigns with thousands of articles.
6. **Proxy Headers**: The `ProxyFix` middleware is configured for Apache (`x_for=1, x_proto=1, x_host=1, x_prefix=1`). Adjust these if deploying behind NGINX or other proxies.

## Related
- [[03 Resources/SOPs/DevOps/Deployment/Generic Flask Guide]]