Aryan Mishra commited on
Commit
dbd4e8b
·
1 Parent(s): 7535e76

Refactor dashboard to native Streamlit UI

Browse files

Removes custom HTML/CSS theming (`unsafe_allow_html`) from the Streamlit frontend and replaces it with native Streamlit components (titles, bordered containers, progress bars, and markdown). Updates admin/predict pages and shared UI helpers accordingly, including test adjustments to assert title-based rendering. Documentation and changelog were also updated to reflect the pure-Python architecture (Streamlit + FastAPI), and obsolete Nginx SPA proxy config was removed.

CHANGELOG.md CHANGED
@@ -1,27 +1,21 @@
1
  # Changelog
2
 
3
- ## v2.0.0 - Streamlit → HTMX Migration
4
 
5
  ### Breaking Changes
6
- - Streamlit frontend (`streamlit_app/`) has been removed entirely
7
- - The separate Streamlit server on port 8501 no longer exists
8
- - Plotly dependency removed (replaced by Chart.js for client-side charts)
9
- - Streamlit dependency removed from `requirements.txt`
10
- - Docker Compose file no longer includes the `streamlit` service
11
- - `Dockerfile.streamlit` deleted
12
 
13
  ### What's New
14
- - HTMX 2.0.3 + Jinja2 frontend served directly by FastAPI
15
- - Alpine.js for reactive UI state management
16
- - Tailwind CSS (Play CDN) for styling
17
- - Chart.js for client-side chart rendering in batch results
18
- - CSRF protection via `itsdangerous` for all HTMX form submissions
19
- - SSE endpoint for live batch progress updates
20
- - Server-Sent Events support via `sse-starlette`
21
- - New dependencies: `sse-starlette`, `itsdangerous`, `pytest-asyncio`
22
 
23
  ### Notes
24
- - Single server: `uvicorn app.main:app --reload` on port 8000
25
- - JSON API endpoints at `/api/predict`, `/api/batch` are preserved
 
26
  - Swagger UI at `/docs` is preserved
27
  - All ML model code, Celery workers, DVC/MLflow integration is unchanged
 
1
  # Changelog
2
 
3
+ ## v2.1.0 - Pure-Python Stack
4
 
5
  ### Breaking Changes
6
+ - HTMX, Jinja2, Alpine.js, and Tailwind CSS frontend removed entirely
7
+ - No HTML templates, no inline HTML/CSS, no `unsafe_allow_html` anywhere in the codebase
8
+ - Frontend is a pure-Python Streamlit dashboard at `frontend/` (`streamlit run frontend/Home.py`)
 
 
 
9
 
10
  ### What's New
11
+ - Streamlit dashboard restored with native widgets only: Sentiment Analyzer, Batch Analytics, System Monitor
12
+ - Admin section gated behind an optional `ADMIN_PASSWORD`
13
+ - Dashboard talks to the FastAPI backend over HTTP (`API_BASE_URL`, default `http://localhost:8000`)
14
+ - README and docs updated to reflect the Python-only stack
 
 
 
 
15
 
16
  ### Notes
17
+ - API server: `uvicorn api.main:app --reload` on port 8000
18
+ - Dashboard: `streamlit run frontend/Home.py` on port 8501
19
+ - JSON API endpoints at `/predict`, `/batch`, `/status/{job_id}`, `/health`, `/info`, `/metrics`
20
  - Swagger UI at `/docs` is preserved
21
  - All ML model code, Celery workers, DVC/MLflow integration is unchanged
README.md CHANGED
@@ -2,13 +2,15 @@
2
 
3
  [![Python](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://python.org)
4
  [![FastAPI](https://img.shields.io/badge/FastAPI-0.115-00a393.svg)](https://fastapi.tiangolo.com)
5
- [![HTMX](https://img.shields.io/badge/HTMX-2.0-3d72d4.svg)](https://htmx.org)
6
  [![DVC](https://img.shields.io/badge/DVC-3.51-945dd6.svg)](https://dvc.org)
7
  [![MLflow](https://img.shields.io/badge/MLflow-2.15-0194E2.svg)](https://mlflow.org)
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
 
10
  **Analyse product reviews in English, Hindi, and Hinglish — extract aspects and their sentiment in real time.**
11
 
 
 
12
  ---
13
 
14
  ## Quick Start
@@ -16,14 +18,22 @@
16
  ```bash
17
  git clone <repo-url> && cd multilingual-absa
18
  python -m venv .venv && source .venv/bin/activate
19
- pip install -r requirements.txt
20
- PYTHONPATH=. uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
 
 
 
 
 
 
 
 
 
21
  ```
22
 
23
  Open:
24
- - **Web UI** → [`http://localhost:8000/predict`](http://localhost:8000/predict)
25
- - **Batch upload** → [`http://localhost:8000/batch`](http://localhost:8000/batch)
26
- - **API docs** → [`http://localhost:8000/docs`](http://localhost:8000/docs)
27
 
28
  ---
29
 
@@ -50,37 +60,38 @@ The system identifies **aspects** (specific features like "battery life", "sound
50
 
51
  ```
52
  .
53
- ├── app/ # FastAPI web application
54
- │ ├── main.py # Entry point — uvicorn app.main:app
55
- │ ├── core/ # App configuration (templates, settings)
56
- │ ├── middleware/ # CSRF, rate limiting, metrics, DB deps
57
- │ ├── routes/ # API endpoints (predict, batch, health) + HTMX pages
58
  │ ├── schemas/ # Pydantic models + SQLAlchemy ORM
59
  │ ├── services/ # ABSA inference pipeline, language detection
60
- ── tasks/ # Celery batch processing workers
61
- ├── static/ # CSS design system
62
- ── templates/ # Jinja2 + HTMX frontend
63
- │ ├── base.html # Layout with Alpine.js, Tailwind, HTMX
64
- │ ├── pages/ # Page templates (predict, batch, monitor)
65
- │ ├── partials/ # HTMX fragment partials
66
- │ └── macros/ # Reusable UI macros (badges, icons)
67
- ├── absa/ # Core ML library (pure Python)
68
- │ ├── data/ # Data loading, preprocessing, augmentation
69
  │ ├── models/ # Training scripts (ONNX, Transformers, baselines)
70
  │ ├── evaluation/ # Cross-lingual eval, latency benchmarking
71
  │ ├── training/ # MLflow experiment tracking
72
  │ └── utils/ # Path configuration
 
 
 
 
 
73
  ├── docker/ # Containerisation
74
- │ ├── Dockerfile # Production app image
75
  │ ├── Dockerfile.prod # Production image (HuggingFace Hub model source)
76
  │ ├── docker-compose.yml # Full stack: API + worker + DB + Redis + monitoring
77
  │ └── docker-compose.prod.yml # Production overrides
78
  ├── tests/ # Test suite
79
  │ ├── api/ # API endpoint tests
80
- │ ├── web/ # Page rendering + HTMX fragment tests
81
  │ └── unit/ # Unit tests (bio tagger, lang detect)
82
  ├── scripts/ # Utility scripts
 
83
  ├── docs/ # Documentation
 
 
84
  ├── .env.example # Environment variable template
85
  ├── dvc.yaml # DVC data pipeline
86
  └── pyproject.toml # Project metadata
@@ -91,26 +102,23 @@ The system identifies **aspects** (specific features like "battery life", "sound
91
  ## Architecture
92
 
93
  ```
94
- ┌─────────┐ HTMX / Alpine.js ┌──────────────────┐
95
- Browser │◄───────────────────────►│ FastAPI (8000)
96
- └─────────┘ app/main.py
97
- │ │
98
- ┌─────────────┐ │
99
- │ │ Jinja2 │ │
100
- Templates
101
- └─────────────┘
102
- │ ┌─────────────┐ │
103
- │ JSON REST │ │
104
- │ │ API │ │
105
- └─────────────┘
106
- ──────────────────
107
-
108
- ┌────────────────────────┼────────────────────┐
109
-
110
- ──────────┐ ┌───────▼──────┐ ┌───────▼──────┐
111
- │ PostgreSQL │ │ Redis/Celery │ │ Prometheus │
112
- │ (results) │ │ (batch jobs) │ │ + Grafana │
113
- └───────────┘ └──────────────┘ └──────────────┘
114
  ```
115
 
116
  ### Inference Stack
@@ -127,13 +135,12 @@ The pipeline auto-selects: **ONNX INT8** → **ONNX FP32** → **Rule-based**. N
127
 
128
  ## Features
129
 
130
- ### Web UI (HTMX + Jinja2)
131
 
132
- - **Single Prediction** — Real-time aspect/sentiment analysis with inline text highlighting
133
- - **Batch Processing** — CSV upload with drag-and-drop, progress bar, Chart.js visualisations
134
- - **System Monitor** — Live health checks, performance metrics, endpoint activity log
135
- - **CSRF Protected** — All form submissions protected via itsdangerous tokens
136
- - **Dark Theme** — Material Design 3 colour system, responsive layout
137
 
138
  ### API (RESTful JSON)
139
 
@@ -142,6 +149,7 @@ The pipeline auto-selects: **ONNX INT8** → **ONNX FP32** → **Rule-based**. N
142
  | `/predict` | POST | Analyse a single review |
143
  | `/batch` | POST | Upload CSV for bulk analysis |
144
  | `/status/{job_id}` | GET | Check batch job progress |
 
145
  | `/health` | GET | API health check |
146
  | `/info` | GET | Model metadata |
147
  | `/metrics` | GET | Prometheus metrics |
@@ -169,7 +177,7 @@ dvc push # Upload to remote storage
169
  ### Run Tests
170
 
171
  ```bash
172
- PYTHONPATH=. pytest tests/ -v
173
  ```
174
 
175
  ### Run Full Stack (Docker)
@@ -189,9 +197,10 @@ Copy `.env.example` to `.env` and configure:
189
  | `DATABASE_URL` | ✅ | PostgreSQL or SQLite connection string |
190
  | `REDIS_URL` | ✅ | Redis connection for Celery |
191
  | `CORS_ORIGINS` | ❌ | Allowed CORS origins (default: localhost) |
192
- | `CSRF_SECRET` | ❌ | Secret for CSRF token signing |
193
  | `MODEL_PATH` | ❌ | Path to ONNX model directory |
194
- | `MODEL_SOURCE` | ❌ | `local` or `huggingface_hub` |
 
 
195
 
196
  ---
197
 
@@ -199,13 +208,13 @@ Copy `.env.example` to `.env` and configure:
199
 
200
  | Document | Contents |
201
  |----------|----------|
202
- | [API](docs/API.md) | Full API reference with schemas and examples |
203
- | [Architecture](docs/ARCHITECTURE.md) | System design, data flow, deployment |
204
  | [Deployment](docs/DEPLOYMENT.md) | Production setup, Docker, Railway |
205
  | [Database](docs/DATABASE.md) | Schema, migrations, query patterns |
206
  | [Security](docs/SECURITY.md) | Threat model, audit results, mitigations |
207
  | [Tech Stack](docs/TECH_STACK.md) | Framework versions, rationale, trade-offs |
208
- | [HTMX Migration](docs/HTMX_MIGRATION.md) | Notes on Streamlit HTMX transition |
209
 
210
  ---
211
 
 
2
 
3
  [![Python](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://python.org)
4
  [![FastAPI](https://img.shields.io/badge/FastAPI-0.115-00a393.svg)](https://fastapi.tiangolo.com)
5
+ [![Streamlit](https://img.shields.io/badge/Streamlit-1.37-FF4B4B.svg)](https://streamlit.io)
6
  [![DVC](https://img.shields.io/badge/DVC-3.51-945dd6.svg)](https://dvc.org)
7
  [![MLflow](https://img.shields.io/badge/MLflow-2.15-0194E2.svg)](https://mlflow.org)
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
 
10
  **Analyse product reviews in English, Hindi, and Hinglish — extract aspects and their sentiment in real time.**
11
 
12
+ A pure-Python stack: a Streamlit dashboard on top of a FastAPI JSON API, backed by a multilingual ML pipeline (ONNX inference with a rule-based fallback).
13
+
14
  ---
15
 
16
  ## Quick Start
 
18
  ```bash
19
  git clone <repo-url> && cd multilingual-absa
20
  python -m venv .venv && source .venv/bin/activate
21
+ pip install -e ".[dev]"
22
+ ```
23
+
24
+ Start the API (port 8000) and the dashboard (port 8501) in two terminals:
25
+
26
+ ```bash
27
+ # Terminal 1 — REST API
28
+ uvicorn api.main:app --reload --host 0.0.0.0 --port 8000
29
+
30
+ # Terminal 2 — Streamlit dashboard
31
+ streamlit run frontend/Home.py
32
  ```
33
 
34
  Open:
35
+ - **Dashboard** → [`http://localhost:8501`](http://localhost:8501)
36
+ - **API docs (Swagger)** → [`http://localhost:8000/docs`](http://localhost:8000/docs)
 
37
 
38
  ---
39
 
 
60
 
61
  ```
62
  .
63
+ ├── api/ # FastAPI REST API (pure Python)
64
+ │ ├── main.py # Entry point — uvicorn api.main:app
65
+ │ ├── middleware/ # Rate limiting, metrics, DB deps
66
+ │ ├── routes/ # /predict, /batch, /status, /health, /info
 
67
  │ ├── schemas/ # Pydantic models + SQLAlchemy ORM
68
  │ ├── services/ # ABSA inference pipeline, language detection
69
+ ── tasks/ # Celery batch processing workers
70
+ ├── src/absa/ # Core ML library (pure Python)
71
+ ── data/ # Loading, preprocessing, augmentation, transliteration
 
 
 
 
 
 
72
  │ ├── models/ # Training scripts (ONNX, Transformers, baselines)
73
  │ ├── evaluation/ # Cross-lingual eval, latency benchmarking
74
  │ ├── training/ # MLflow experiment tracking
75
  │ └── utils/ # Path configuration
76
+ ├── frontend/ # Streamlit dashboard (pure Python, no HTML templates)
77
+ │ ├── Home.py # Entry point — streamlit run frontend/Home.py
78
+ │ ├── absa_client.py # Thin HTTP client for the FastAPI backend
79
+ │ ├── ui.py # Native Streamlit UI helpers
80
+ │ └── views/ # Pages: predict, admin (overview/batch/monitor)
81
  ├── docker/ # Containerisation
82
+ │ ├── Dockerfile # Production API image
83
  │ ├── Dockerfile.prod # Production image (HuggingFace Hub model source)
84
  │ ├── docker-compose.yml # Full stack: API + worker + DB + Redis + monitoring
85
  │ └── docker-compose.prod.yml # Production overrides
86
  ├── tests/ # Test suite
87
  │ ├── api/ # API endpoint tests
88
+ │ ├── web/ # Streamlit page + client tests
89
  │ └── unit/ # Unit tests (bio tagger, lang detect)
90
  ├── scripts/ # Utility scripts
91
+ ├── monitoring/ # Prometheus + Grafana config
92
  ├── docs/ # Documentation
93
+ ├── data/ # Datasets (managed by DVC)
94
+ ├── models/ # ONNX model artifacts
95
  ├── .env.example # Environment variable template
96
  ├── dvc.yaml # DVC data pipeline
97
  └── pyproject.toml # Project metadata
 
102
  ## Architecture
103
 
104
  ```
105
+ ┌───────── native Streamlit ┌──────────────────────────────
106
+ Browser │◄────────────────────►│ Streamlit Dashboard (8501)
107
+ └───────── frontend/ — pure Python UI
108
+ └──────────────┬───────────────┘
109
+ HTTP (httpx)
110
+ ┌──────────────▼───────────────┐
111
+ FastAPI REST API (8000)
112
+ api/main.py — JSON endpoints
113
+ ──────────────┬───────────────┘
114
+
115
+ ┌───────────────────────────┼────────────────────┐
116
+
117
+ ────────────┐ ┌─────────▼────────┐ ┌────────▼───────┐
118
+ PostgreSQL / │ │ Redis / Celery │ │ Prometheus + │
119
+ │ SQLite │ │ (batch jobs) │ │ Grafana │
120
+ (results) └──────────────────┘ └────────────────┘
121
+ ─────────────
 
 
 
122
  ```
123
 
124
  ### Inference Stack
 
135
 
136
  ## Features
137
 
138
+ ### Dashboard (Streamlit)
139
 
140
+ - **Sentiment Analyzer** — Real-time aspect/sentiment analysis with confidence bars
141
+ - **Batch Analytics** — CSV upload, async Celery job, live progress, result download
142
+ - **System Monitor** — Health checks, service metadata, auto-refresh diagnostics
143
+ - **Admin Lock** — Optional password-gated admin section (`ADMIN_PASSWORD`)
 
144
 
145
  ### API (RESTful JSON)
146
 
 
149
  | `/predict` | POST | Analyse a single review |
150
  | `/batch` | POST | Upload CSV for bulk analysis |
151
  | `/status/{job_id}` | GET | Check batch job progress |
152
+ | `/download/{job_id}` | GET | Download batch results (CSV) |
153
  | `/health` | GET | API health check |
154
  | `/info` | GET | Model metadata |
155
  | `/metrics` | GET | Prometheus metrics |
 
177
  ### Run Tests
178
 
179
  ```bash
180
+ PYTHONPATH=.:src pytest tests/ -v
181
  ```
182
 
183
  ### Run Full Stack (Docker)
 
197
  | `DATABASE_URL` | ✅ | PostgreSQL or SQLite connection string |
198
  | `REDIS_URL` | ✅ | Redis connection for Celery |
199
  | `CORS_ORIGINS` | ❌ | Allowed CORS origins (default: localhost) |
 
200
  | `MODEL_PATH` | ❌ | Path to ONNX model directory |
201
+ | `MAX_BATCH_SIZE` | ❌ | Max reviews per batch job |
202
+ | `ADMIN_PASSWORD` | ❌ | Password for admin dashboard sections |
203
+ | `API_BASE_URL` | ❌ | Base URL the dashboard uses to reach the API |
204
 
205
  ---
206
 
 
208
 
209
  | Document | Contents |
210
  |----------|----------|
211
+ | [API](docs/API_DOCUMENTATION.md) | Full API reference with schemas and examples |
212
+ | [Architecture](docs/architecture.md) | System design, data flow, deployment |
213
  | [Deployment](docs/DEPLOYMENT.md) | Production setup, Docker, Railway |
214
  | [Database](docs/DATABASE.md) | Schema, migrations, query patterns |
215
  | [Security](docs/SECURITY.md) | Threat model, audit results, mitigations |
216
  | [Tech Stack](docs/TECH_STACK.md) | Framework versions, rationale, trade-offs |
217
+ | [System Design](docs/SYSTEM_DESIGN.md) | End-to-end design and data flow |
218
 
219
  ---
220
 
coverage.xml ADDED
@@ -0,0 +1,1748 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" ?>
2
+ <coverage version="7.14.0" timestamp="1786277891188" lines-valid="1482" lines-covered="432" line-rate="0.2915" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
3
+ <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.14.0 -->
4
+ <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
5
+ <sources>
6
+ <source>/Users/theogengineer/Projects/Multilingual-Absa</source>
7
+ <source>/Users/theogengineer/Projects/Multilingual-Absa/api</source>
8
+ </sources>
9
+ <packages>
10
+ <package name="." line-rate="1" branch-rate="0" complexity="0">
11
+ <classes>
12
+ <class name="__init__.py" filename="__init__.py" complexity="0" line-rate="1" branch-rate="0">
13
+ <methods/>
14
+ <lines/>
15
+ </class>
16
+ <class name="main.py" filename="main.py" complexity="0" line-rate="1" branch-rate="0">
17
+ <methods/>
18
+ <lines>
19
+ <line number="1" hits="1"/>
20
+ <line number="2" hits="1"/>
21
+ <line number="4" hits="1"/>
22
+ <line number="5" hits="1"/>
23
+ <line number="6" hits="1"/>
24
+ <line number="7" hits="1"/>
25
+ <line number="8" hits="1"/>
26
+ <line number="9" hits="1"/>
27
+ <line number="11" hits="1"/>
28
+ <line number="13" hits="1"/>
29
+ <line number="14" hits="1"/>
30
+ <line number="15" hits="1"/>
31
+ <line number="16" hits="1"/>
32
+ <line number="17" hits="1"/>
33
+ <line number="20" hits="1"/>
34
+ <line number="21" hits="1"/>
35
+ <line number="23" hits="1"/>
36
+ <line number="24" hits="1"/>
37
+ <line number="26" hits="1"/>
38
+ <line number="27" hits="1"/>
39
+ <line number="29" hits="1"/>
40
+ <line number="31" hits="1"/>
41
+ <line number="34" hits="1"/>
42
+ <line number="36" hits="1"/>
43
+ <line number="43" hits="1"/>
44
+ <line number="44" hits="1"/>
45
+ <line number="46" hits="1"/>
46
+ <line number="53" hits="1"/>
47
+ <line number="54" hits="1"/>
48
+ <line number="56" hits="1"/>
49
+ </lines>
50
+ </class>
51
+ </classes>
52
+ </package>
53
+ <package name="core" line-rate="1" branch-rate="0" complexity="0">
54
+ <classes>
55
+ <class name="__init__.py" filename="core/__init__.py" complexity="0" line-rate="1" branch-rate="0">
56
+ <methods/>
57
+ <lines/>
58
+ </class>
59
+ </classes>
60
+ </package>
61
+ <package name="middleware" line-rate="0.95" branch-rate="0" complexity="0">
62
+ <classes>
63
+ <class name="__init__.py" filename="middleware/__init__.py" complexity="0" line-rate="1" branch-rate="0">
64
+ <methods/>
65
+ <lines/>
66
+ </class>
67
+ <class name="dependencies.py" filename="middleware/dependencies.py" complexity="0" line-rate="0.9444" branch-rate="0">
68
+ <methods/>
69
+ <lines>
70
+ <line number="1" hits="1"/>
71
+ <line number="2" hits="1"/>
72
+ <line number="3" hits="1"/>
73
+ <line number="4" hits="1"/>
74
+ <line number="6" hits="1"/>
75
+ <line number="8" hits="1"/>
76
+ <line number="10" hits="1"/>
77
+ <line number="11" hits="0"/>
78
+ <line number="16" hits="1"/>
79
+ <line number="17" hits="1"/>
80
+ <line number="18" hits="1"/>
81
+ <line number="20" hits="1"/>
82
+ <line number="21" hits="1"/>
83
+ <line number="25" hits="1"/>
84
+ <line number="26" hits="1"/>
85
+ <line number="27" hits="1"/>
86
+ <line number="28" hits="1"/>
87
+ <line number="30" hits="1"/>
88
+ </lines>
89
+ </class>
90
+ <class name="metrics.py" filename="middleware/metrics.py" complexity="0" line-rate="1" branch-rate="0">
91
+ <methods/>
92
+ <lines>
93
+ <line number="1" hits="1"/>
94
+ <line number="3" hits="1"/>
95
+ </lines>
96
+ </class>
97
+ </classes>
98
+ </package>
99
+ <package name="routes" line-rate="0.6574" branch-rate="0" complexity="0">
100
+ <classes>
101
+ <class name="__init__.py" filename="routes/__init__.py" complexity="0" line-rate="1" branch-rate="0">
102
+ <methods/>
103
+ <lines/>
104
+ </class>
105
+ <class name="predict.py" filename="routes/predict.py" complexity="0" line-rate="0.6463" branch-rate="0">
106
+ <methods/>
107
+ <lines>
108
+ <line number="1" hits="1"/>
109
+ <line number="2" hits="1"/>
110
+ <line number="3" hits="1"/>
111
+ <line number="4" hits="1"/>
112
+ <line number="5" hits="1"/>
113
+ <line number="6" hits="1"/>
114
+ <line number="7" hits="1"/>
115
+ <line number="8" hits="1"/>
116
+ <line number="10" hits="1"/>
117
+ <line number="11" hits="1"/>
118
+ <line number="12" hits="1"/>
119
+ <line number="13" hits="1"/>
120
+ <line number="14" hits="1"/>
121
+ <line number="15" hits="1"/>
122
+ <line number="18" hits="1"/>
123
+ <line number="19" hits="1"/>
124
+ <line number="20" hits="1"/>
125
+ <line number="21" hits="1"/>
126
+ <line number="24" hits="1"/>
127
+ <line number="27" hits="1"/>
128
+ <line number="32" hits="1"/>
129
+ <line number="33" hits="1"/>
130
+ <line number="34" hits="1"/>
131
+ <line number="36" hits="1"/>
132
+ <line number="37" hits="1"/>
133
+ <line number="45" hits="1"/>
134
+ <line number="46" hits="1"/>
135
+ <line number="48" hits="1"/>
136
+ <line number="49" hits="0"/>
137
+ <line number="50" hits="0"/>
138
+ <line number="53" hits="1"/>
139
+ <line number="54" hits="1"/>
140
+ <line number="55" hits="1"/>
141
+ <line number="57" hits="1"/>
142
+ <line number="58" hits="0"/>
143
+ <line number="60" hits="1"/>
144
+ <line number="61" hits="0"/>
145
+ <line number="63" hits="1"/>
146
+ <line number="64" hits="1"/>
147
+ <line number="65" hits="1"/>
148
+ <line number="66" hits="0"/>
149
+ <line number="69" hits="1"/>
150
+ <line number="70" hits="1"/>
151
+ <line number="71" hits="1"/>
152
+ <line number="74" hits="1"/>
153
+ <line number="75" hits="1"/>
154
+ <line number="76" hits="0"/>
155
+ <line number="77" hits="0"/>
156
+ <line number="78" hits="0"/>
157
+ <line number="80" hits="1"/>
158
+ <line number="81" hits="1"/>
159
+ <line number="82" hits="0"/>
160
+ <line number="83" hits="0"/>
161
+ <line number="87" hits="1"/>
162
+ <line number="88" hits="0"/>
163
+ <line number="89" hits="0"/>
164
+ <line number="93" hits="1"/>
165
+ <line number="94" hits="1"/>
166
+ <line number="95" hits="1"/>
167
+ <line number="96" hits="1"/>
168
+ <line number="97" hits="1"/>
169
+ <line number="100" hits="1"/>
170
+ <line number="102" hits="1"/>
171
+ <line number="105" hits="0"/>
172
+ <line number="106" hits="0"/>
173
+ <line number="107" hits="0"/>
174
+ <line number="108" hits="0"/>
175
+ <line number="113" hits="1"/>
176
+ <line number="114" hits="1"/>
177
+ <line number="115" hits="0"/>
178
+ <line number="116" hits="0"/>
179
+ <line number="117" hits="0"/>
180
+ <line number="118" hits="0"/>
181
+ <line number="119" hits="0"/>
182
+ <line number="120" hits="0"/>
183
+ <line number="121" hits="0"/>
184
+ <line number="122" hits="0"/>
185
+ <line number="123" hits="0"/>
186
+ <line number="125" hits="0"/>
187
+ <line number="126" hits="0"/>
188
+ <line number="127" hits="0"/>
189
+ <line number="129" hits="0"/>
190
+ </lines>
191
+ </class>
192
+ <class name="results.py" filename="routes/results.py" complexity="0" line-rate="0.6923" branch-rate="0">
193
+ <methods/>
194
+ <lines>
195
+ <line number="1" hits="1"/>
196
+ <line number="2" hits="1"/>
197
+ <line number="3" hits="1"/>
198
+ <line number="5" hits="1"/>
199
+ <line number="8" hits="1"/>
200
+ <line number="9" hits="1"/>
201
+ <line number="11" hits="1"/>
202
+ <line number="14" hits="1"/>
203
+ <line number="15" hits="1"/>
204
+ <line number="16" hits="1"/>
205
+ <line number="23" hits="1"/>
206
+ <line number="24" hits="1"/>
207
+ <line number="25" hits="1"/>
208
+ <line number="26" hits="1"/>
209
+ <line number="27" hits="1"/>
210
+ <line number="29" hits="1"/>
211
+ <line number="31" hits="1"/>
212
+ <line number="32" hits="1"/>
213
+ <line number="33" hits="0"/>
214
+ <line number="34" hits="0"/>
215
+ <line number="35" hits="0"/>
216
+ <line number="36" hits="0"/>
217
+ <line number="37" hits="0"/>
218
+ <line number="38" hits="0"/>
219
+ <line number="39" hits="0"/>
220
+ <line number="40" hits="0"/>
221
+ </lines>
222
+ </class>
223
+ </classes>
224
+ </package>
225
+ <package name="schemas" line-rate="1" branch-rate="0" complexity="0">
226
+ <classes>
227
+ <class name="__init__.py" filename="schemas/__init__.py" complexity="0" line-rate="1" branch-rate="0">
228
+ <methods/>
229
+ <lines/>
230
+ </class>
231
+ <class name="db_models.py" filename="schemas/db_models.py" complexity="0" line-rate="1" branch-rate="0">
232
+ <methods/>
233
+ <lines>
234
+ <line number="1" hits="1"/>
235
+ <line number="2" hits="1"/>
236
+ <line number="3" hits="1"/>
237
+ <line number="4" hits="1"/>
238
+ <line number="6" hits="1"/>
239
+ <line number="9" hits="1"/>
240
+ <line number="10" hits="1"/>
241
+ <line number="12" hits="1"/>
242
+ <line number="13" hits="1"/>
243
+ <line number="14" hits="1"/>
244
+ <line number="15" hits="1"/>
245
+ <line number="18" hits="1"/>
246
+ <line number="21" hits="1"/>
247
+ <line number="22" hits="1"/>
248
+ <line number="24" hits="1"/>
249
+ <line number="25" hits="1"/>
250
+ <line number="26" hits="1"/>
251
+ <line number="27" hits="1"/>
252
+ <line number="28" hits="1"/>
253
+ <line number="29" hits="1"/>
254
+ <line number="30" hits="1"/>
255
+ <line number="33" hits="1"/>
256
+ <line number="34" hits="1"/>
257
+ <line number="36" hits="1"/>
258
+ <line number="37" hits="1"/>
259
+ <line number="38" hits="1"/>
260
+ <line number="39" hits="1"/>
261
+ <line number="40" hits="1"/>
262
+ <line number="43" hits="1"/>
263
+ </lines>
264
+ </class>
265
+ <class name="schemas.py" filename="schemas/schemas.py" complexity="0" line-rate="1" branch-rate="0">
266
+ <methods/>
267
+ <lines>
268
+ <line number="1" hits="1"/>
269
+ <line number="2" hits="1"/>
270
+ <line number="5" hits="1"/>
271
+ <line number="6" hits="1"/>
272
+ <line number="7" hits="1"/>
273
+ <line number="9" hits="1"/>
274
+ <line number="12" hits="1"/>
275
+ <line number="13" hits="1"/>
276
+ <line number="14" hits="1"/>
277
+ <line number="15" hits="1"/>
278
+ <line number="16" hits="1"/>
279
+ <line number="17" hits="1"/>
280
+ <line number="19" hits="1"/>
281
+ <line number="22" hits="1"/>
282
+ <line number="23" hits="1"/>
283
+ <line number="24" hits="1"/>
284
+ <line number="25" hits="1"/>
285
+ <line number="26" hits="1"/>
286
+ <line number="27" hits="1"/>
287
+ <line number="29" hits="1"/>
288
+ <line number="32" hits="1"/>
289
+ <line number="33" hits="1"/>
290
+ <line number="34" hits="1"/>
291
+ <line number="35" hits="1"/>
292
+ <line number="36" hits="1"/>
293
+ <line number="37" hits="1"/>
294
+ <line number="39" hits="1"/>
295
+ </lines>
296
+ </class>
297
+ </classes>
298
+ </package>
299
+ <package name="services" line-rate="0.6923" branch-rate="0" complexity="0">
300
+ <classes>
301
+ <class name="__init__.py" filename="services/__init__.py" complexity="0" line-rate="1" branch-rate="0">
302
+ <methods/>
303
+ <lines/>
304
+ </class>
305
+ <class name="absa_pipeline.py" filename="services/absa_pipeline.py" complexity="0" line-rate="0.6915" branch-rate="0">
306
+ <methods/>
307
+ <lines>
308
+ <line number="13" hits="1"/>
309
+ <line number="14" hits="1"/>
310
+ <line number="15" hits="1"/>
311
+ <line number="16" hits="1"/>
312
+ <line number="17" hits="1"/>
313
+ <line number="18" hits="1"/>
314
+ <line number="20" hits="1"/>
315
+ <line number="22" hits="1"/>
316
+ <line number="23" hits="1"/>
317
+ <line number="26" hits="1"/>
318
+ <line number="27" hits="1"/>
319
+ <line number="31" hits="1"/>
320
+ <line number="33" hits="1"/>
321
+ <line number="34" hits="0"/>
322
+ <line number="35" hits="0"/>
323
+ <line number="36" hits="0"/>
324
+ <line number="38" hits="0"/>
325
+ <line number="39" hits="0"/>
326
+ <line number="40" hits="0"/>
327
+ <line number="44" hits="1"/>
328
+ <line number="151" hits="1"/>
329
+ <line number="256" hits="1"/>
330
+ <line number="346" hits="1"/>
331
+ <line number="424" hits="1"/>
332
+ <line number="448" hits="1"/>
333
+ <line number="466" hits="1"/>
334
+ <line number="472" hits="1"/>
335
+ <line number="479" hits="1"/>
336
+ <line number="481" hits="1"/>
337
+ <line number="482" hits="1"/>
338
+ <line number="485" hits="1"/>
339
+ <line number="490" hits="1"/>
340
+ <line number="491" hits="1"/>
341
+ <line number="492" hits="1"/>
342
+ <line number="493" hits="1"/>
343
+ <line number="494" hits="1"/>
344
+ <line number="496" hits="1"/>
345
+ <line number="497" hits="1"/>
346
+ <line number="499" hits="1"/>
347
+ <line number="501" hits="1"/>
348
+ <line number="502" hits="1"/>
349
+ <line number="503" hits="1"/>
350
+ <line number="505" hits="1"/>
351
+ <line number="506" hits="1"/>
352
+ <line number="507" hits="1"/>
353
+ <line number="508" hits="0"/>
354
+ <line number="510" hits="1"/>
355
+ <line number="511" hits="1"/>
356
+ <line number="512" hits="1"/>
357
+ <line number="515" hits="1"/>
358
+ <line number="517" hits="1"/>
359
+ <line number="518" hits="1"/>
360
+ <line number="519" hits="1"/>
361
+ <line number="520" hits="1"/>
362
+ <line number="521" hits="1"/>
363
+ <line number="522" hits="1"/>
364
+ <line number="523" hits="1"/>
365
+ <line number="524" hits="1"/>
366
+ <line number="525" hits="1"/>
367
+ <line number="526" hits="1"/>
368
+ <line number="527" hits="0"/>
369
+ <line number="533" hits="1"/>
370
+ <line number="534" hits="1"/>
371
+ <line number="535" hits="1"/>
372
+ <line number="536" hits="1"/>
373
+ <line number="537" hits="1"/>
374
+ <line number="538" hits="1"/>
375
+ <line number="539" hits="1"/>
376
+ <line number="541" hits="1"/>
377
+ <line number="542" hits="1"/>
378
+ <line number="549" hits="1"/>
379
+ <line number="551" hits="1"/>
380
+ <line number="552" hits="1"/>
381
+ <line number="553" hits="1"/>
382
+ <line number="555" hits="1"/>
383
+ <line number="556" hits="0"/>
384
+ <line number="557" hits="0"/>
385
+ <line number="558" hits="0"/>
386
+ <line number="559" hits="0"/>
387
+ <line number="562" hits="0"/>
388
+ <line number="565" hits="0"/>
389
+ <line number="566" hits="0"/>
390
+ <line number="567" hits="0"/>
391
+ <line number="569" hits="1"/>
392
+ <line number="570" hits="1"/>
393
+ <line number="571" hits="1"/>
394
+ <line number="572" hits="1"/>
395
+ <line number="573" hits="1"/>
396
+ <line number="574" hits="1"/>
397
+ <line number="575" hits="1"/>
398
+ <line number="577" hits="1"/>
399
+ <line number="578" hits="0"/>
400
+ <line number="579" hits="0"/>
401
+ <line number="580" hits="0"/>
402
+ <line number="581" hits="0"/>
403
+ <line number="582" hits="0"/>
404
+ <line number="583" hits="0"/>
405
+ <line number="584" hits="0"/>
406
+ <line number="585" hits="0"/>
407
+ <line number="587" hits="1"/>
408
+ <line number="588" hits="1"/>
409
+ <line number="590" hits="1"/>
410
+ <line number="592" hits="1"/>
411
+ <line number="593" hits="1"/>
412
+ <line number="594" hits="1"/>
413
+ <line number="595" hits="1"/>
414
+ <line number="597" hits="1"/>
415
+ <line number="598" hits="0"/>
416
+ <line number="600" hits="1"/>
417
+ <line number="602" hits="1"/>
418
+ <line number="610" hits="1"/>
419
+ <line number="611" hits="0"/>
420
+ <line number="615" hits="1"/>
421
+ <line number="616" hits="0"/>
422
+ <line number="617" hits="0"/>
423
+ <line number="618" hits="0"/>
424
+ <line number="619" hits="0"/>
425
+ <line number="620" hits="0"/>
426
+ <line number="621" hits="0"/>
427
+ <line number="622" hits="0"/>
428
+ <line number="624" hits="0"/>
429
+ <line number="625" hits="0"/>
430
+ <line number="626" hits="0"/>
431
+ <line number="627" hits="0"/>
432
+ <line number="632" hits="0"/>
433
+ <line number="633" hits="0"/>
434
+ <line number="634" hits="0"/>
435
+ <line number="635" hits="0"/>
436
+ <line number="636" hits="0"/>
437
+ <line number="637" hits="0"/>
438
+ <line number="638" hits="0"/>
439
+ <line number="639" hits="0"/>
440
+ <line number="640" hits="0"/>
441
+ <line number="641" hits="0"/>
442
+ <line number="643" hits="0"/>
443
+ <line number="644" hits="0"/>
444
+ <line number="645" hits="0"/>
445
+ <line number="646" hits="0"/>
446
+ <line number="647" hits="0"/>
447
+ <line number="649" hits="0"/>
448
+ <line number="650" hits="0"/>
449
+ <line number="651" hits="0"/>
450
+ <line number="658" hits="0"/>
451
+ <line number="659" hits="0"/>
452
+ <line number="660" hits="0"/>
453
+ <line number="661" hits="0"/>
454
+ <line number="662" hits="0"/>
455
+ <line number="671" hits="0"/>
456
+ <line number="675" hits="1"/>
457
+ <line number="676" hits="1"/>
458
+ <line number="677" hits="1"/>
459
+ <line number="678" hits="1"/>
460
+ <line number="681" hits="1"/>
461
+ <line number="682" hits="1"/>
462
+ <line number="686" hits="1"/>
463
+ <line number="687" hits="1"/>
464
+ <line number="689" hits="1"/>
465
+ <line number="690" hits="1"/>
466
+ <line number="694" hits="1"/>
467
+ <line number="695" hits="1"/>
468
+ <line number="696" hits="1"/>
469
+ <line number="698" hits="1"/>
470
+ <line number="701" hits="1"/>
471
+ <line number="702" hits="1"/>
472
+ <line number="703" hits="1"/>
473
+ <line number="705" hits="1"/>
474
+ <line number="706" hits="1"/>
475
+ <line number="715" hits="1"/>
476
+ <line number="717" hits="1"/>
477
+ <line number="723" hits="1"/>
478
+ <line number="724" hits="1"/>
479
+ <line number="726" hits="1"/>
480
+ <line number="727" hits="1"/>
481
+ <line number="729" hits="1"/>
482
+ <line number="730" hits="1"/>
483
+ <line number="731" hits="1"/>
484
+ <line number="732" hits="1"/>
485
+ <line number="733" hits="1"/>
486
+ <line number="734" hits="1"/>
487
+ <line number="735" hits="1"/>
488
+ <line number="736" hits="0"/>
489
+ <line number="737" hits="1"/>
490
+ <line number="738" hits="1"/>
491
+ <line number="739" hits="1"/>
492
+ <line number="741" hits="1"/>
493
+ <line number="742" hits="1"/>
494
+ <line number="744" hits="1"/>
495
+ <line number="746" hits="1"/>
496
+ <line number="747" hits="1"/>
497
+ <line number="749" hits="1"/>
498
+ <line number="750" hits="1"/>
499
+ <line number="751" hits="1"/>
500
+ <line number="753" hits="1"/>
501
+ <line number="754" hits="1"/>
502
+ <line number="755" hits="1"/>
503
+ <line number="756" hits="1"/>
504
+ <line number="757" hits="1"/>
505
+ <line number="758" hits="1"/>
506
+ <line number="760" hits="1"/>
507
+ <line number="761" hits="1"/>
508
+ <line number="764" hits="1"/>
509
+ </lines>
510
+ </class>
511
+ <class name="lang_service.py" filename="services/lang_service.py" complexity="0" line-rate="0.7" branch-rate="0">
512
+ <methods/>
513
+ <lines>
514
+ <line number="1" hits="1"/>
515
+ <line number="2" hits="1"/>
516
+ <line number="5" hits="1"/>
517
+ <line number="6" hits="1"/>
518
+ <line number="10" hits="1"/>
519
+ <line number="11" hits="1"/>
520
+ <line number="12" hits="1"/>
521
+ <line number="13" hits="0"/>
522
+ <line number="15" hits="1"/>
523
+ <line number="16" hits="1"/>
524
+ <line number="17" hits="0"/>
525
+ <line number="18" hits="0"/>
526
+ <line number="19" hits="0"/>
527
+ <line number="20" hits="0"/>
528
+ <line number="22" hits="0"/>
529
+ <line number="25" hits="1"/>
530
+ <line number="26" hits="1"/>
531
+ <line number="27" hits="1"/>
532
+ <line number="28" hits="1"/>
533
+ <line number="31" hits="1"/>
534
+ </lines>
535
+ </class>
536
+ </classes>
537
+ </package>
538
+ <package name="src.absa" line-rate="1" branch-rate="0" complexity="0">
539
+ <classes>
540
+ <class name="__init__.py" filename="src/absa/__init__.py" complexity="0" line-rate="1" branch-rate="0">
541
+ <methods/>
542
+ <lines/>
543
+ </class>
544
+ </classes>
545
+ </package>
546
+ <package name="src.absa.data" line-rate="0.2" branch-rate="0" complexity="0">
547
+ <classes>
548
+ <class name="__init__.py" filename="src/absa/data/__init__.py" complexity="0" line-rate="1" branch-rate="0">
549
+ <methods/>
550
+ <lines/>
551
+ </class>
552
+ <class name="augmentation.py" filename="src/absa/data/augmentation.py" complexity="0" line-rate="0" branch-rate="0">
553
+ <methods/>
554
+ <lines>
555
+ <line number="6" hits="0"/>
556
+ <line number="7" hits="0"/>
557
+ <line number="8" hits="0"/>
558
+ <line number="9" hits="0"/>
559
+ <line number="10" hits="0"/>
560
+ <line number="11" hits="0"/>
561
+ <line number="12" hits="0"/>
562
+ <line number="14" hits="0"/>
563
+ <line number="17" hits="0"/>
564
+ <line number="18" hits="0"/>
565
+ <line number="19" hits="0"/>
566
+ <line number="20" hits="0"/>
567
+ <line number="21" hits="0"/>
568
+ <line number="23" hits="0"/>
569
+ <line number="24" hits="0"/>
570
+ <line number="26" hits="0"/>
571
+ <line number="27" hits="0"/>
572
+ <line number="29" hits="0"/>
573
+ <line number="30" hits="0"/>
574
+ <line number="31" hits="0"/>
575
+ <line number="32" hits="0"/>
576
+ <line number="33" hits="0"/>
577
+ <line number="35" hits="0"/>
578
+ <line number="36" hits="0"/>
579
+ <line number="39" hits="0"/>
580
+ <line number="42" hits="0"/>
581
+ <line number="45" hits="0"/>
582
+ <line number="46" hits="0"/>
583
+ <line number="47" hits="0"/>
584
+ <line number="48" hits="0"/>
585
+ <line number="50" hits="0"/>
586
+ <line number="51" hits="0"/>
587
+ <line number="53" hits="0"/>
588
+ <line number="54" hits="0"/>
589
+ <line number="55" hits="0"/>
590
+ <line number="56" hits="0"/>
591
+ <line number="57" hits="0"/>
592
+ <line number="60" hits="0"/>
593
+ <line number="61" hits="0"/>
594
+ <line number="64" hits="0"/>
595
+ <line number="65" hits="0"/>
596
+ <line number="67" hits="0"/>
597
+ <line number="69" hits="0"/>
598
+ <line number="70" hits="0"/>
599
+ <line number="73" hits="0"/>
600
+ <line number="74" hits="0"/>
601
+ <line number="75" hits="0"/>
602
+ <line number="76" hits="0"/>
603
+ <line number="77" hits="0"/>
604
+ <line number="78" hits="0"/>
605
+ <line number="79" hits="0"/>
606
+ <line number="80" hits="0"/>
607
+ <line number="81" hits="0"/>
608
+ <line number="82" hits="0"/>
609
+ <line number="83" hits="0"/>
610
+ <line number="84" hits="0"/>
611
+ <line number="86" hits="0"/>
612
+ <line number="88" hits="0"/>
613
+ <line number="89" hits="0"/>
614
+ <line number="92" hits="0"/>
615
+ <line number="93" hits="0"/>
616
+ <line number="94" hits="0"/>
617
+ <line number="95" hits="0"/>
618
+ <line number="96" hits="0"/>
619
+ <line number="98" hits="0"/>
620
+ <line number="101" hits="0"/>
621
+ <line number="102" hits="0"/>
622
+ <line number="103" hits="0"/>
623
+ <line number="104" hits="0"/>
624
+ <line number="105" hits="0"/>
625
+ <line number="108" hits="0"/>
626
+ <line number="109" hits="0"/>
627
+ </lines>
628
+ </class>
629
+ <class name="bio_tagger.py" filename="src/absa/data/bio_tagger.py" complexity="0" line-rate="0.9362" branch-rate="0">
630
+ <methods/>
631
+ <lines>
632
+ <line number="1" hits="1"/>
633
+ <line number="2" hits="1"/>
634
+ <line number="5" hits="1"/>
635
+ <line number="16" hits="1"/>
636
+ <line number="18" hits="1"/>
637
+ <line number="19" hits="1"/>
638
+ <line number="20" hits="1"/>
639
+ <line number="23" hits="1"/>
640
+ <line number="36" hits="1"/>
641
+ <line number="39" hits="1"/>
642
+ <line number="41" hits="1"/>
643
+ <line number="42" hits="1"/>
644
+ <line number="43" hits="1"/>
645
+ <line number="45" hits="1"/>
646
+ <line number="46" hits="1"/>
647
+ <line number="49" hits="1"/>
648
+ <line number="50" hits="1"/>
649
+ <line number="52" hits="1"/>
650
+ <line number="53" hits="1"/>
651
+ <line number="54" hits="1"/>
652
+ <line number="55" hits="1"/>
653
+ <line number="58" hits="1"/>
654
+ <line number="61" hits="1"/>
655
+ <line number="66" hits="1"/>
656
+ <line number="69" hits="1"/>
657
+ <line number="73" hits="1"/>
658
+ <line number="75" hits="0"/>
659
+ <line number="77" hits="1"/>
660
+ <line number="79" hits="1"/>
661
+ <line number="82" hits="1"/>
662
+ <line number="93" hits="1"/>
663
+ <line number="94" hits="1"/>
664
+ <line number="96" hits="1"/>
665
+ <line number="97" hits="1"/>
666
+ <line number="98" hits="1"/>
667
+ <line number="99" hits="1"/>
668
+ <line number="100" hits="1"/>
669
+ <line number="101" hits="1"/>
670
+ <line number="102" hits="1"/>
671
+ <line number="103" hits="1"/>
672
+ <line number="106" hits="0"/>
673
+ <line number="108" hits="1"/>
674
+ <line number="109" hits="1"/>
675
+ <line number="110" hits="1"/>
676
+ <line number="112" hits="1"/>
677
+ <line number="113" hits="0"/>
678
+ <line number="115" hits="1"/>
679
+ </lines>
680
+ </class>
681
+ <class name="dataset.py" filename="src/absa/data/dataset.py" complexity="0" line-rate="0" branch-rate="0">
682
+ <methods/>
683
+ <lines>
684
+ <line number="1" hits="0"/>
685
+ <line number="2" hits="0"/>
686
+ <line number="3" hits="0"/>
687
+ <line number="4" hits="0"/>
688
+ <line number="5" hits="0"/>
689
+ <line number="6" hits="0"/>
690
+ <line number="9" hits="0"/>
691
+ <line number="10" hits="0"/>
692
+ <line number="11" hits="0"/>
693
+ <line number="13" hits="0"/>
694
+ <line number="14" hits="0"/>
695
+ <line number="16" hits="0"/>
696
+ <line number="17" hits="0"/>
697
+ <line number="19" hits="0"/>
698
+ <line number="25" hits="0"/>
699
+ <line number="26" hits="0"/>
700
+ <line number="27" hits="0"/>
701
+ <line number="28" hits="0"/>
702
+ <line number="29" hits="0"/>
703
+ <line number="31" hits="0"/>
704
+ <line number="33" hits="0"/>
705
+ <line number="35" hits="0"/>
706
+ <line number="39" hits="0"/>
707
+ <line number="40" hits="0"/>
708
+ <line number="41" hits="0"/>
709
+ <line number="42" hits="0"/>
710
+ <line number="43" hits="0"/>
711
+ <line number="44" hits="0"/>
712
+ <line number="45" hits="0"/>
713
+ <line number="47" hits="0"/>
714
+ <line number="48" hits="0"/>
715
+ <line number="49" hits="0"/>
716
+ <line number="50" hits="0"/>
717
+ <line number="51" hits="0"/>
718
+ <line number="52" hits="0"/>
719
+ <line number="61" hits="0"/>
720
+ <line number="67" hits="0"/>
721
+ <line number="68" hits="0"/>
722
+ <line number="70" hits="0"/>
723
+ <line number="71" hits="0"/>
724
+ <line number="74" hits="0"/>
725
+ <line number="75" hits="0"/>
726
+ </lines>
727
+ </class>
728
+ <class name="hf_dataset.py" filename="src/absa/data/hf_dataset.py" complexity="0" line-rate="0" branch-rate="0">
729
+ <methods/>
730
+ <lines>
731
+ <line number="1" hits="0"/>
732
+ <line number="2" hits="0"/>
733
+ <line number="3" hits="0"/>
734
+ <line number="4" hits="0"/>
735
+ <line number="5" hits="0"/>
736
+ <line number="6" hits="0"/>
737
+ <line number="7" hits="0"/>
738
+ <line number="8" hits="0"/>
739
+ <line number="9" hits="0"/>
740
+ <line number="11" hits="0"/>
741
+ <line number="14" hits="0"/>
742
+ <line number="15" hits="0"/>
743
+ <line number="16" hits="0"/>
744
+ <line number="17" hits="0"/>
745
+ <line number="18" hits="0"/>
746
+ <line number="19" hits="0"/>
747
+ <line number="20" hits="0"/>
748
+ <line number="21" hits="0"/>
749
+ <line number="24" hits="0"/>
750
+ <line number="26" hits="0"/>
751
+ <line number="27" hits="0"/>
752
+ <line number="29" hits="0"/>
753
+ <line number="30" hits="0"/>
754
+ <line number="31" hits="0"/>
755
+ <line number="32" hits="0"/>
756
+ <line number="34" hits="0"/>
757
+ <line number="35" hits="0"/>
758
+ <line number="37" hits="0"/>
759
+ <line number="44" hits="0"/>
760
+ <line number="47" hits="0"/>
761
+ <line number="49" hits="0"/>
762
+ <line number="50" hits="0"/>
763
+ <line number="52" hits="0"/>
764
+ <line number="53" hits="0"/>
765
+ <line number="54" hits="0"/>
766
+ <line number="56" hits="0"/>
767
+ <line number="57" hits="0"/>
768
+ <line number="58" hits="0"/>
769
+ <line number="60" hits="0"/>
770
+ <line number="61" hits="0"/>
771
+ <line number="63" hits="0"/>
772
+ <line number="71" hits="0"/>
773
+ <line number="74" hits="0"/>
774
+ <line number="75" hits="0"/>
775
+ <line number="76" hits="0"/>
776
+ <line number="77" hits="0"/>
777
+ <line number="78" hits="0"/>
778
+ <line number="79" hits="0"/>
779
+ <line number="80" hits="0"/>
780
+ <line number="81" hits="0"/>
781
+ <line number="82" hits="0"/>
782
+ <line number="84" hits="0"/>
783
+ <line number="85" hits="0"/>
784
+ <line number="88" hits="0"/>
785
+ <line number="89" hits="0"/>
786
+ <line number="90" hits="0"/>
787
+ <line number="91" hits="0"/>
788
+ <line number="94" hits="0"/>
789
+ <line number="95" hits="0"/>
790
+ <line number="96" hits="0"/>
791
+ <line number="99" hits="0"/>
792
+ <line number="100" hits="0"/>
793
+ <line number="102" hits="0"/>
794
+ <line number="107" hits="0"/>
795
+ <line number="111" hits="0"/>
796
+ <line number="112" hits="0"/>
797
+ <line number="114" hits="0"/>
798
+ <line number="115" hits="0"/>
799
+ <line number="121" hits="0"/>
800
+ <line number="122" hits="0"/>
801
+ <line number="123" hits="0"/>
802
+ <line number="124" hits="0"/>
803
+ <line number="125" hits="0"/>
804
+ <line number="126" hits="0"/>
805
+ <line number="128" hits="0"/>
806
+ <line number="136" hits="0"/>
807
+ <line number="141" hits="0"/>
808
+ <line number="142" hits="0"/>
809
+ <line number="147" hits="0"/>
810
+ <line number="150" hits="0"/>
811
+ <line number="153" hits="0"/>
812
+ <line number="157" hits="0"/>
813
+ <line number="159" hits="0"/>
814
+ <line number="167" hits="0"/>
815
+ <line number="175" hits="0"/>
816
+ <line number="178" hits="0"/>
817
+ <line number="179" hits="0"/>
818
+ <line number="182" hits="0"/>
819
+ <line number="183" hits="0"/>
820
+ </lines>
821
+ </class>
822
+ <class name="hindi_loader.py" filename="src/absa/data/hindi_loader.py" complexity="0" line-rate="0" branch-rate="0">
823
+ <methods/>
824
+ <lines>
825
+ <line number="1" hits="0"/>
826
+ <line number="2" hits="0"/>
827
+ <line number="3" hits="0"/>
828
+ <line number="4" hits="0"/>
829
+ <line number="7" hits="0"/>
830
+ <line number="8" hits="0"/>
831
+ <line number="9" hits="0"/>
832
+ <line number="10" hits="0"/>
833
+ <line number="11" hits="0"/>
834
+ <line number="13" hits="0"/>
835
+ <line number="15" hits="0"/>
836
+ <line number="16" hits="0"/>
837
+ <line number="18" hits="0"/>
838
+ <line number="21" hits="0"/>
839
+ <line number="22" hits="0"/>
840
+ <line number="23" hits="0"/>
841
+ <line number="24" hits="0"/>
842
+ <line number="25" hits="0"/>
843
+ <line number="27" hits="0"/>
844
+ <line number="28" hits="0"/>
845
+ <line number="29" hits="0"/>
846
+ <line number="30" hits="0"/>
847
+ <line number="31" hits="0"/>
848
+ <line number="33" hits="0"/>
849
+ <line number="35" hits="0"/>
850
+ <line number="36" hits="0"/>
851
+ <line number="37" hits="0"/>
852
+ <line number="39" hits="0"/>
853
+ <line number="41" hits="0"/>
854
+ <line number="43" hits="0"/>
855
+ <line number="49" hits="0"/>
856
+ <line number="50" hits="0"/>
857
+ <line number="52" hits="0"/>
858
+ <line number="53" hits="0"/>
859
+ <line number="56" hits="0"/>
860
+ <line number="57" hits="0"/>
861
+ </lines>
862
+ </class>
863
+ <class name="lang_detect.py" filename="src/absa/data/lang_detect.py" complexity="0" line-rate="0.9643" branch-rate="0">
864
+ <methods/>
865
+ <lines>
866
+ <line number="1" hits="1"/>
867
+ <line number="2" hits="1"/>
868
+ <line number="3" hits="1"/>
869
+ <line number="5" hits="1"/>
870
+ <line number="8" hits="1"/>
871
+ <line number="10" hits="1"/>
872
+ <line number="11" hits="1"/>
873
+ <line number="12" hits="1"/>
874
+ <line number="15" hits="1"/>
875
+ <line number="24" hits="1"/>
876
+ <line number="25" hits="0"/>
877
+ <line number="27" hits="1"/>
878
+ <line number="28" hits="1"/>
879
+ <line number="29" hits="1"/>
880
+ <line number="31" hits="1"/>
881
+ <line number="32" hits="1"/>
882
+ <line number="33" hits="1"/>
883
+ <line number="34" hits="1"/>
884
+ <line number="36" hits="1"/>
885
+ <line number="37" hits="1"/>
886
+ <line number="39" hits="1"/>
887
+ <line number="41" hits="1"/>
888
+ <line number="42" hits="1"/>
889
+ <line number="44" hits="1"/>
890
+ <line number="45" hits="1"/>
891
+ <line number="46" hits="1"/>
892
+ <line number="47" hits="1"/>
893
+ <line number="49" hits="1"/>
894
+ </lines>
895
+ </class>
896
+ <class name="preprocess.py" filename="src/absa/data/preprocess.py" complexity="0" line-rate="0" branch-rate="0">
897
+ <methods/>
898
+ <lines>
899
+ <line number="1" hits="0"/>
900
+ <line number="2" hits="0"/>
901
+ <line number="3" hits="0"/>
902
+ <line number="6" hits="0"/>
903
+ <line number="8" hits="0"/>
904
+ <line number="9" hits="0"/>
905
+ <line number="12" hits="0"/>
906
+ <line number="15" hits="0"/>
907
+ <line number="18" hits="0"/>
908
+ <line number="21" hits="0"/>
909
+ <line number="24" hits="0"/>
910
+ <line number="25" hits="0"/>
911
+ <line number="28" hits="0"/>
912
+ <line number="31" hits="0"/>
913
+ <line number="32" hits="0"/>
914
+ <line number="34" hits="0"/>
915
+ </lines>
916
+ </class>
917
+ <class name="transliterate.py" filename="src/absa/data/transliterate.py" complexity="0" line-rate="0" branch-rate="0">
918
+ <methods/>
919
+ <lines>
920
+ <line number="1" hits="0"/>
921
+ <line number="2" hits="0"/>
922
+ <line number="3" hits="0"/>
923
+ <line number="5" hits="0"/>
924
+ <line number="7" hits="0"/>
925
+ <line number="8" hits="0"/>
926
+ <line number="10" hits="0"/>
927
+ <line number="11" hits="0"/>
928
+ <line number="12" hits="0"/>
929
+ <line number="13" hits="0"/>
930
+ <line number="18" hits="0"/>
931
+ <line number="20" hits="0"/>
932
+ <line number="21" hits="0"/>
933
+ <line number="23" hits="0"/>
934
+ <line number="24" hits="0"/>
935
+ <line number="27" hits="0"/>
936
+ <line number="28" hits="0"/>
937
+ <line number="29" hits="0"/>
938
+ <line number="30" hits="0"/>
939
+ <line number="33" hits="0"/>
940
+ <line number="39" hits="0"/>
941
+ <line number="40" hits="0"/>
942
+ <line number="44" hits="0"/>
943
+ <line number="45" hits="0"/>
944
+ <line number="47" hits="0"/>
945
+ </lines>
946
+ </class>
947
+ </classes>
948
+ </package>
949
+ <package name="src.absa.evaluation" line-rate="0" branch-rate="0" complexity="0">
950
+ <classes>
951
+ <class name="__init__.py" filename="src/absa/evaluation/__init__.py" complexity="0" line-rate="1" branch-rate="0">
952
+ <methods/>
953
+ <lines/>
954
+ </class>
955
+ <class name="benchmark_latency.py" filename="src/absa/evaluation/benchmark_latency.py" complexity="0" line-rate="0" branch-rate="0">
956
+ <methods/>
957
+ <lines>
958
+ <line number="5" hits="0"/>
959
+ <line number="6" hits="0"/>
960
+ <line number="7" hits="0"/>
961
+ <line number="8" hits="0"/>
962
+ <line number="9" hits="0"/>
963
+ <line number="11" hits="0"/>
964
+ <line number="12" hits="0"/>
965
+ <line number="14" hits="0"/>
966
+ <line number="15" hits="0"/>
967
+ <line number="16" hits="0"/>
968
+ <line number="17" hits="0"/>
969
+ <line number="20" hits="0"/>
970
+ <line number="21" hits="0"/>
971
+ <line number="24" hits="0"/>
972
+ <line number="25" hits="0"/>
973
+ <line number="26" hits="0"/>
974
+ <line number="27" hits="0"/>
975
+ <line number="29" hits="0"/>
976
+ <line number="31" hits="0"/>
977
+ <line number="32" hits="0"/>
978
+ <line number="33" hits="0"/>
979
+ <line number="37" hits="0"/>
980
+ <line number="38" hits="0"/>
981
+ <line number="39" hits="0"/>
982
+ <line number="40" hits="0"/>
983
+ <line number="42" hits="0"/>
984
+ <line number="43" hits="0"/>
985
+ <line number="45" hits="0"/>
986
+ <line number="47" hits="0"/>
987
+ <line number="48" hits="0"/>
988
+ <line number="49" hits="0"/>
989
+ <line number="51" hits="0"/>
990
+ <line number="54" hits="0"/>
991
+ <line number="55" hits="0"/>
992
+ <line number="56" hits="0"/>
993
+ <line number="57" hits="0"/>
994
+ <line number="58" hits="0"/>
995
+ <line number="60" hits="0"/>
996
+ <line number="61" hits="0"/>
997
+ <line number="62" hits="0"/>
998
+ <line number="64" hits="0"/>
999
+ <line number="65" hits="0"/>
1000
+ <line number="67" hits="0"/>
1001
+ <line number="70" hits="0"/>
1002
+ <line number="71" hits="0"/>
1003
+ <line number="72" hits="0"/>
1004
+ <line number="74" hits="0"/>
1005
+ <line number="75" hits="0"/>
1006
+ <line number="81" hits="0"/>
1007
+ <line number="83" hits="0"/>
1008
+ <line number="84" hits="0"/>
1009
+ <line number="85" hits="0"/>
1010
+ <line number="88" hits="0"/>
1011
+ <line number="91" hits="0"/>
1012
+ <line number="98" hits="0"/>
1013
+ <line number="99" hits="0"/>
1014
+ <line number="100" hits="0"/>
1015
+ <line number="103" hits="0"/>
1016
+ <line number="106" hits="0"/>
1017
+ <line number="112" hits="0"/>
1018
+ <line number="113" hits="0"/>
1019
+ <line number="116" hits="0"/>
1020
+ <line number="117" hits="0"/>
1021
+ <line number="118" hits="0"/>
1022
+ <line number="123" hits="0"/>
1023
+ <line number="124" hits="0"/>
1024
+ <line number="125" hits="0"/>
1025
+ <line number="126" hits="0"/>
1026
+ <line number="130" hits="0"/>
1027
+ <line number="134" hits="0"/>
1028
+ <line number="135" hits="0"/>
1029
+ <line number="136" hits="0"/>
1030
+ <line number="137" hits="0"/>
1031
+ <line number="138" hits="0"/>
1032
+ <line number="139" hits="0"/>
1033
+ <line number="140" hits="0"/>
1034
+ <line number="141" hits="0"/>
1035
+ <line number="144" hits="0"/>
1036
+ <line number="145" hits="0"/>
1037
+ </lines>
1038
+ </class>
1039
+ <class name="cross_lingual_eval.py" filename="src/absa/evaluation/cross_lingual_eval.py" complexity="0" line-rate="0" branch-rate="0">
1040
+ <methods/>
1041
+ <lines>
1042
+ <line number="1" hits="0"/>
1043
+ <line number="2" hits="0"/>
1044
+ <line number="3" hits="0"/>
1045
+ <line number="4" hits="0"/>
1046
+ <line number="10" hits="0"/>
1047
+ <line number="11" hits="0"/>
1048
+ <line number="13" hits="0"/>
1049
+ <line number="16" hits="0"/>
1050
+ <line number="17" hits="0"/>
1051
+ <line number="18" hits="0"/>
1052
+ <line number="19" hits="0"/>
1053
+ <line number="20" hits="0"/>
1054
+ <line number="21" hits="0"/>
1055
+ <line number="22" hits="0"/>
1056
+ <line number="25" hits="0"/>
1057
+ <line number="26" hits="0"/>
1058
+ <line number="29" hits="0"/>
1059
+ <line number="30" hits="0"/>
1060
+ <line number="32" hits="0"/>
1061
+ <line number="33" hits="0"/>
1062
+ <line number="36" hits="0"/>
1063
+ <line number="38" hits="0"/>
1064
+ <line number="39" hits="0"/>
1065
+ <line number="41" hits="0"/>
1066
+ <line number="44" hits="0"/>
1067
+ <line number="48" hits="0"/>
1068
+ <line number="50" hits="0"/>
1069
+ <line number="59" hits="0"/>
1070
+ <line number="60" hits="0"/>
1071
+ <line number="62" hits="0"/>
1072
+ <line number="64" hits="0"/>
1073
+ <line number="66" hits="0"/>
1074
+ <line number="67" hits="0"/>
1075
+ <line number="69" hits="0"/>
1076
+ <line number="70" hits="0"/>
1077
+ <line number="71" hits="0"/>
1078
+ <line number="73" hits="0"/>
1079
+ <line number="74" hits="0"/>
1080
+ <line number="75" hits="0"/>
1081
+ <line number="76" hits="0"/>
1082
+ <line number="77" hits="0"/>
1083
+ <line number="79" hits="0"/>
1084
+ <line number="82" hits="0"/>
1085
+ <line number="85" hits="0"/>
1086
+ <line number="86" hits="0"/>
1087
+ <line number="87" hits="0"/>
1088
+ <line number="89" hits="0"/>
1089
+ <line number="90" hits="0"/>
1090
+ <line number="91" hits="0"/>
1091
+ <line number="93" hits="0"/>
1092
+ <line number="95" hits="0"/>
1093
+ <line number="100" hits="0"/>
1094
+ <line number="104" hits="0"/>
1095
+ <line number="105" hits="0"/>
1096
+ <line number="107" hits="0"/>
1097
+ <line number="108" hits="0"/>
1098
+ <line number="109" hits="0"/>
1099
+ <line number="115" hits="0"/>
1100
+ <line number="116" hits="0"/>
1101
+ <line number="118" hits="0"/>
1102
+ <line number="119" hits="0"/>
1103
+ <line number="120" hits="0"/>
1104
+ <line number="122" hits="0"/>
1105
+ <line number="123" hits="0"/>
1106
+ <line number="132" hits="0"/>
1107
+ <line number="133" hits="0"/>
1108
+ </lines>
1109
+ </class>
1110
+ <class name="final_eval.py" filename="src/absa/evaluation/final_eval.py" complexity="0" line-rate="0" branch-rate="0">
1111
+ <methods/>
1112
+ <lines>
1113
+ <line number="1" hits="0"/>
1114
+ <line number="2" hits="0"/>
1115
+ <line number="5" hits="0"/>
1116
+ <line number="8" hits="0"/>
1117
+ <line number="37" hits="0"/>
1118
+ <line number="38" hits="0"/>
1119
+ <line number="39" hits="0"/>
1120
+ <line number="41" hits="0"/>
1121
+ <line number="42" hits="0"/>
1122
+ <line number="44" hits="0"/>
1123
+ <line number="46" hits="0"/>
1124
+ <line number="49" hits="0"/>
1125
+ <line number="50" hits="0"/>
1126
+ <line number="51" hits="0"/>
1127
+ <line number="54" hits="0"/>
1128
+ <line number="55" hits="0"/>
1129
+ <line number="57" hits="0"/>
1130
+ <line number="60" hits="0"/>
1131
+ <line number="61" hits="0"/>
1132
+ </lines>
1133
+ </class>
1134
+ </classes>
1135
+ </package>
1136
+ <package name="src.absa.models" line-rate="0" branch-rate="0" complexity="0">
1137
+ <classes>
1138
+ <class name="__init__.py" filename="src/absa/models/__init__.py" complexity="0" line-rate="1" branch-rate="0">
1139
+ <methods/>
1140
+ <lines/>
1141
+ </class>
1142
+ <class name="baseline.py" filename="src/absa/models/baseline.py" complexity="0" line-rate="0" branch-rate="0">
1143
+ <methods/>
1144
+ <lines>
1145
+ <line number="1" hits="0"/>
1146
+ <line number="2" hits="0"/>
1147
+ <line number="3" hits="0"/>
1148
+ <line number="4" hits="0"/>
1149
+ <line number="5" hits="0"/>
1150
+ <line number="6" hits="0"/>
1151
+ <line number="7" hits="0"/>
1152
+ <line number="8" hits="0"/>
1153
+ <line number="9" hits="0"/>
1154
+ <line number="10" hits="0"/>
1155
+ <line number="11" hits="0"/>
1156
+ <line number="14" hits="0"/>
1157
+ <line number="15" hits="0"/>
1158
+ <line number="16" hits="0"/>
1159
+ <line number="17" hits="0"/>
1160
+ <line number="18" hits="0"/>
1161
+ <line number="19" hits="0"/>
1162
+ <line number="20" hits="0"/>
1163
+ <line number="21" hits="0"/>
1164
+ <line number="24" hits="0"/>
1165
+ <line number="37" hits="0"/>
1166
+ <line number="38" hits="0"/>
1167
+ <line number="40" hits="0"/>
1168
+ <line number="41" hits="0"/>
1169
+ <line number="42" hits="0"/>
1170
+ <line number="44" hits="0"/>
1171
+ <line number="45" hits="0"/>
1172
+ <line number="46" hits="0"/>
1173
+ <line number="47" hits="0"/>
1174
+ <line number="48" hits="0"/>
1175
+ <line number="51" hits="0"/>
1176
+ <line number="52" hits="0"/>
1177
+ <line number="53" hits="0"/>
1178
+ <line number="57" hits="0"/>
1179
+ <line number="60" hits="0"/>
1180
+ <line number="63" hits="0"/>
1181
+ <line number="66" hits="0"/>
1182
+ <line number="70" hits="0"/>
1183
+ <line number="71" hits="0"/>
1184
+ <line number="73" hits="0"/>
1185
+ <line number="74" hits="0"/>
1186
+ <line number="76" hits="0"/>
1187
+ <line number="79" hits="0"/>
1188
+ <line number="80" hits="0"/>
1189
+ <line number="85" hits="0"/>
1190
+ <line number="86" hits="0"/>
1191
+ <line number="87" hits="0"/>
1192
+ <line number="90" hits="0"/>
1193
+ <line number="91" hits="0"/>
1194
+ <line number="92" hits="0"/>
1195
+ <line number="95" hits="0"/>
1196
+ <line number="96" hits="0"/>
1197
+ <line number="97" hits="0"/>
1198
+ <line number="99" hits="0"/>
1199
+ <line number="106" hits="0"/>
1200
+ <line number="118" hits="0"/>
1201
+ <line number="119" hits="0"/>
1202
+ <line number="120" hits="0"/>
1203
+ <line number="121" hits="0"/>
1204
+ <line number="122" hits="0"/>
1205
+ <line number="125" hits="0"/>
1206
+ <line number="133" hits="0"/>
1207
+ <line number="136" hits="0"/>
1208
+ <line number="137" hits="0"/>
1209
+ <line number="138" hits="0"/>
1210
+ <line number="140" hits="0"/>
1211
+ <line number="143" hits="0"/>
1212
+ <line number="144" hits="0"/>
1213
+ </lines>
1214
+ </class>
1215
+ <class name="export_onnx.py" filename="src/absa/models/export_onnx.py" complexity="0" line-rate="0" branch-rate="0">
1216
+ <methods/>
1217
+ <lines>
1218
+ <line number="6" hits="0"/>
1219
+ <line number="8" hits="0"/>
1220
+ <line number="9" hits="0"/>
1221
+ <line number="14" hits="0"/>
1222
+ <line number="16" hits="0"/>
1223
+ <line number="17" hits="0"/>
1224
+ <line number="18" hits="0"/>
1225
+ <line number="19" hits="0"/>
1226
+ <line number="22" hits="0"/>
1227
+ <line number="25" hits="0"/>
1228
+ <line number="27" hits="0"/>
1229
+ <line number="28" hits="0"/>
1230
+ <line number="30" hits="0"/>
1231
+ <line number="31" hits="0"/>
1232
+ <line number="32" hits="0"/>
1233
+ <line number="37" hits="0"/>
1234
+ <line number="38" hits="0"/>
1235
+ <line number="41" hits="0"/>
1236
+ <line number="42" hits="0"/>
1237
+ <line number="46" hits="0"/>
1238
+ <line number="48" hits="0"/>
1239
+ <line number="50" hits="0"/>
1240
+ <line number="51" hits="0"/>
1241
+ <line number="52" hits="0"/>
1242
+ <line number="54" hits="0"/>
1243
+ <line number="55" hits="0"/>
1244
+ <line number="58" hits="0"/>
1245
+ <line number="59" hits="0"/>
1246
+ <line number="60" hits="0"/>
1247
+ <line number="62" hits="0"/>
1248
+ <line number="63" hits="0"/>
1249
+ <line number="64" hits="0"/>
1250
+ <line number="65" hits="0"/>
1251
+ <line number="66" hits="0"/>
1252
+ <line number="69" hits="0"/>
1253
+ <line number="77" hits="0"/>
1254
+ <line number="85" hits="0"/>
1255
+ <line number="86" hits="0"/>
1256
+ </lines>
1257
+ </class>
1258
+ <class name="train_aspect_extraction.py" filename="src/absa/models/train_aspect_extraction.py" complexity="0" line-rate="0" branch-rate="0">
1259
+ <methods/>
1260
+ <lines>
1261
+ <line number="1" hits="0"/>
1262
+ <line number="2" hits="0"/>
1263
+ <line number="3" hits="0"/>
1264
+ <line number="4" hits="0"/>
1265
+ <line number="12" hits="0"/>
1266
+ <line number="13" hits="0"/>
1267
+ <line number="15" hits="0"/>
1268
+ <line number="18" hits="0"/>
1269
+ <line number="27" hits="0"/>
1270
+ <line number="28" hits="0"/>
1271
+ <line number="30" hits="0"/>
1272
+ <line number="32" hits="0"/>
1273
+ <line number="36" hits="0"/>
1274
+ <line number="42" hits="0"/>
1275
+ <line number="46" hits="0"/>
1276
+ <line number="49" hits="0"/>
1277
+ <line number="56" hits="0"/>
1278
+ <line number="57" hits="0"/>
1279
+ <line number="59" hits="0"/>
1280
+ <line number="60" hits="0"/>
1281
+ <line number="61" hits="0"/>
1282
+ <line number="63" hits="0"/>
1283
+ <line number="64" hits="0"/>
1284
+ <line number="66" hits="0"/>
1285
+ <line number="67" hits="0"/>
1286
+ <line number="74" hits="0"/>
1287
+ <line number="76" hits="0"/>
1288
+ <line number="92" hits="0"/>
1289
+ <line number="102" hits="0"/>
1290
+ <line number="103" hits="0"/>
1291
+ <line number="105" hits="0"/>
1292
+ <line number="106" hits="0"/>
1293
+ <line number="107" hits="0"/>
1294
+ <line number="109" hits="0"/>
1295
+ <line number="110" hits="0"/>
1296
+ <line number="111" hits="0"/>
1297
+ <line number="115" hits="0"/>
1298
+ <line number="122" hits="0"/>
1299
+ <line number="125" hits="0"/>
1300
+ <line number="128" hits="0"/>
1301
+ <line number="129" hits="0"/>
1302
+ </lines>
1303
+ </class>
1304
+ <class name="train_joint_absa.py" filename="src/absa/models/train_joint_absa.py" complexity="0" line-rate="0" branch-rate="0">
1305
+ <methods/>
1306
+ <lines>
1307
+ <line number="5" hits="0"/>
1308
+ <line number="6" hits="0"/>
1309
+ <line number="7" hits="0"/>
1310
+ <line number="8" hits="0"/>
1311
+ <line number="16" hits="0"/>
1312
+ <line number="17" hits="0"/>
1313
+ <line number="18" hits="0"/>
1314
+ <line number="19" hits="0"/>
1315
+ <line number="23" hits="0"/>
1316
+ <line number="24" hits="0"/>
1317
+ <line number="26" hits="0"/>
1318
+ <line number="29" hits="0"/>
1319
+ <line number="30" hits="0"/>
1320
+ <line number="31" hits="0"/>
1321
+ <line number="32" hits="0"/>
1322
+ <line number="33" hits="0"/>
1323
+ <line number="34" hits="0"/>
1324
+ <line number="35" hits="0"/>
1325
+ <line number="38" hits="0"/>
1326
+ <line number="39" hits="0"/>
1327
+ <line number="40" hits="0"/>
1328
+ <line number="41" hits="0"/>
1329
+ <line number="42" hits="0"/>
1330
+ <line number="44" hits="0"/>
1331
+ <line number="45" hits="0"/>
1332
+ <line number="48" hits="0"/>
1333
+ <line number="51" hits="0"/>
1334
+ <line number="53" hits="0"/>
1335
+ <line number="55" hits="0"/>
1336
+ <line number="69" hits="0"/>
1337
+ <line number="73" hits="0"/>
1338
+ <line number="85" hits="0"/>
1339
+ <line number="86" hits="0"/>
1340
+ <line number="89" hits="0"/>
1341
+ <line number="92" hits="0"/>
1342
+ <line number="93" hits="0"/>
1343
+ <line number="95" hits="0"/>
1344
+ <line number="96" hits="0"/>
1345
+ <line number="97" hits="0"/>
1346
+ <line number="100" hits="0"/>
1347
+ <line number="101" hits="0"/>
1348
+ <line number="102" hits="0"/>
1349
+ <line number="107" hits="0"/>
1350
+ <line number="110" hits="0"/>
1351
+ <line number="116" hits="0"/>
1352
+ <line number="118" hits="0"/>
1353
+ <line number="119" hits="0"/>
1354
+ <line number="120" hits="0"/>
1355
+ <line number="122" hits="0"/>
1356
+ <line number="131" hits="0"/>
1357
+ <line number="132" hits="0"/>
1358
+ <line number="133" hits="0"/>
1359
+ <line number="134" hits="0"/>
1360
+ <line number="135" hits="0"/>
1361
+ <line number="136" hits="0"/>
1362
+ <line number="137" hits="0"/>
1363
+ <line number="140" hits="0"/>
1364
+ <line number="142" hits="0"/>
1365
+ <line number="143" hits="0"/>
1366
+ <line number="146" hits="0"/>
1367
+ <line number="154" hits="0"/>
1368
+ <line number="156" hits="0"/>
1369
+ <line number="159" hits="0"/>
1370
+ <line number="160" hits="0"/>
1371
+ <line number="162" hits="0"/>
1372
+ <line number="164" hits="0"/>
1373
+ <line number="167" hits="0"/>
1374
+ <line number="168" hits="0"/>
1375
+ <line number="169" hits="0"/>
1376
+ <line number="170" hits="0"/>
1377
+ <line number="172" hits="0"/>
1378
+ <line number="173" hits="0"/>
1379
+ <line number="174" hits="0"/>
1380
+ <line number="178" hits="0"/>
1381
+ <line number="195" hits="0"/>
1382
+ <line number="196" hits="0"/>
1383
+ <line number="198" hits="0"/>
1384
+ <line number="200" hits="0"/>
1385
+ <line number="205" hits="0"/>
1386
+ <line number="206" hits="0"/>
1387
+ <line number="209" hits="0"/>
1388
+ <line number="210" hits="0"/>
1389
+ </lines>
1390
+ </class>
1391
+ <class name="train_multilingual.py" filename="src/absa/models/train_multilingual.py" complexity="0" line-rate="0" branch-rate="0">
1392
+ <methods/>
1393
+ <lines>
1394
+ <line number="5" hits="0"/>
1395
+ <line number="6" hits="0"/>
1396
+ <line number="7" hits="0"/>
1397
+ <line number="15" hits="0"/>
1398
+ <line number="16" hits="0"/>
1399
+ <line number="17" hits="0"/>
1400
+ <line number="18" hits="0"/>
1401
+ <line number="20" hits="0"/>
1402
+ <line number="23" hits="0"/>
1403
+ <line number="24" hits="0"/>
1404
+ <line number="25" hits="0"/>
1405
+ <line number="26" hits="0"/>
1406
+ <line number="29" hits="0"/>
1407
+ <line number="30" hits="0"/>
1408
+ <line number="31" hits="0"/>
1409
+ <line number="35" hits="0"/>
1410
+ <line number="36" hits="0"/>
1411
+ <line number="37" hits="0"/>
1412
+ <line number="39" hits="0"/>
1413
+ <line number="40" hits="0"/>
1414
+ <line number="41" hits="0"/>
1415
+ <line number="42" hits="0"/>
1416
+ <line number="43" hits="0"/>
1417
+ <line number="44" hits="0"/>
1418
+ <line number="46" hits="0"/>
1419
+ <line number="49" hits="0"/>
1420
+ <line number="54" hits="0"/>
1421
+ <line number="55" hits="0"/>
1422
+ <line number="56" hits="0"/>
1423
+ <line number="57" hits="0"/>
1424
+ <line number="59" hits="0"/>
1425
+ <line number="60" hits="0"/>
1426
+ <line number="61" hits="0"/>
1427
+ <line number="64" hits="0"/>
1428
+ <line number="65" hits="0"/>
1429
+ <line number="68" hits="0"/>
1430
+ <line number="70" hits="0"/>
1431
+ <line number="71" hits="0"/>
1432
+ <line number="72" hits="0"/>
1433
+ <line number="75" hits="0"/>
1434
+ <line number="76" hits="0"/>
1435
+ <line number="77" hits="0"/>
1436
+ <line number="79" hits="0"/>
1437
+ <line number="80" hits="0"/>
1438
+ <line number="81" hits="0"/>
1439
+ <line number="83" hits="0"/>
1440
+ <line number="84" hits="0"/>
1441
+ <line number="86" hits="0"/>
1442
+ <line number="88" hits="0"/>
1443
+ <line number="89" hits="0"/>
1444
+ <line number="90" hits="0"/>
1445
+ <line number="92" hits="0"/>
1446
+ <line number="93" hits="0"/>
1447
+ <line number="97" hits="0"/>
1448
+ <line number="99" hits="0"/>
1449
+ <line number="111" hits="0"/>
1450
+ <line number="120" hits="0"/>
1451
+ <line number="121" hits="0"/>
1452
+ <line number="123" hits="0"/>
1453
+ <line number="124" hits="0"/>
1454
+ <line number="128" hits="0"/>
1455
+ <line number="129" hits="0"/>
1456
+ <line number="130" hits="0"/>
1457
+ <line number="131" hits="0"/>
1458
+ <line number="133" hits="0"/>
1459
+ <line number="134" hits="0"/>
1460
+ <line number="135" hits="0"/>
1461
+ <line number="136" hits="0"/>
1462
+ <line number="138" hits="0"/>
1463
+ <line number="139" hits="0"/>
1464
+ <line number="140" hits="0"/>
1465
+ <line number="143" hits="0"/>
1466
+ <line number="144" hits="0"/>
1467
+ </lines>
1468
+ </class>
1469
+ <class name="train_qlora.py" filename="src/absa/models/train_qlora.py" complexity="0" line-rate="0" branch-rate="0">
1470
+ <methods/>
1471
+ <lines>
1472
+ <line number="5" hits="0"/>
1473
+ <line number="6" hits="0"/>
1474
+ <line number="7" hits="0"/>
1475
+ <line number="16" hits="0"/>
1476
+ <line number="17" hits="0"/>
1477
+ <line number="18" hits="0"/>
1478
+ <line number="19" hits="0"/>
1479
+ <line number="20" hits="0"/>
1480
+ <line number="23" hits="0"/>
1481
+ <line number="26" hits="0"/>
1482
+ <line number="27" hits="0"/>
1483
+ <line number="28" hits="0"/>
1484
+ <line number="29" hits="0"/>
1485
+ <line number="30" hits="0"/>
1486
+ <line number="33" hits="0"/>
1487
+ <line number="34" hits="0"/>
1488
+ <line number="35" hits="0"/>
1489
+ <line number="36" hits="0"/>
1490
+ <line number="38" hits="0"/>
1491
+ <line number="41" hits="0"/>
1492
+ <line number="42" hits="0"/>
1493
+ <line number="48" hits="0"/>
1494
+ <line number="49" hits="0"/>
1495
+ <line number="52" hits="0"/>
1496
+ <line number="54" hits="0"/>
1497
+ <line number="55" hits="0"/>
1498
+ <line number="56" hits="0"/>
1499
+ <line number="63" hits="0"/>
1500
+ <line number="71" hits="0"/>
1501
+ <line number="72" hits="0"/>
1502
+ <line number="76" hits="0"/>
1503
+ <line number="77" hits="0"/>
1504
+ <line number="78" hits="0"/>
1505
+ <line number="79" hits="0"/>
1506
+ <line number="81" hits="0"/>
1507
+ <line number="83" hits="0"/>
1508
+ <line number="84" hits="0"/>
1509
+ <line number="88" hits="0"/>
1510
+ <line number="90" hits="0"/>
1511
+ <line number="104" hits="0"/>
1512
+ <line number="114" hits="0"/>
1513
+ <line number="115" hits="0"/>
1514
+ <line number="117" hits="0"/>
1515
+ <line number="118" hits="0"/>
1516
+ <line number="121" hits="0"/>
1517
+ <line number="122" hits="0"/>
1518
+ <line number="125" hits="0"/>
1519
+ <line number="128" hits="0"/>
1520
+ <line number="129" hits="0"/>
1521
+ </lines>
1522
+ </class>
1523
+ <class name="train_sentiment.py" filename="src/absa/models/train_sentiment.py" complexity="0" line-rate="0" branch-rate="0">
1524
+ <methods/>
1525
+ <lines>
1526
+ <line number="1" hits="0"/>
1527
+ <line number="2" hits="0"/>
1528
+ <line number="3" hits="0"/>
1529
+ <line number="4" hits="0"/>
1530
+ <line number="5" hits="0"/>
1531
+ <line number="13" hits="0"/>
1532
+ <line number="14" hits="0"/>
1533
+ <line number="16" hits="0"/>
1534
+ <line number="19" hits="0"/>
1535
+ <line number="28" hits="0"/>
1536
+ <line number="29" hits="0"/>
1537
+ <line number="31" hits="0"/>
1538
+ <line number="32" hits="0"/>
1539
+ <line number="35" hits="0"/>
1540
+ <line number="44" hits="0"/>
1541
+ <line number="45" hits="0"/>
1542
+ <line number="46" hits="0"/>
1543
+ <line number="47" hits="0"/>
1544
+ <line number="49" hits="0"/>
1545
+ <line number="50" hits="0"/>
1546
+ <line number="51" hits="0"/>
1547
+ <line number="52" hits="0"/>
1548
+ <line number="54" hits="0"/>
1549
+ <line number="55" hits="0"/>
1550
+ <line number="59" hits="0"/>
1551
+ <line number="61" hits="0"/>
1552
+ <line number="63" hits="0"/>
1553
+ <line number="66" hits="0"/>
1554
+ <line number="73" hits="0"/>
1555
+ <line number="74" hits="0"/>
1556
+ <line number="76" hits="0"/>
1557
+ <line number="77" hits="0"/>
1558
+ <line number="78" hits="0"/>
1559
+ <line number="80" hits="0"/>
1560
+ <line number="81" hits="0"/>
1561
+ <line number="83" hits="0"/>
1562
+ <line number="84" hits="0"/>
1563
+ <line number="91" hits="0"/>
1564
+ <line number="93" hits="0"/>
1565
+ <line number="110" hits="0"/>
1566
+ <line number="111" hits="0"/>
1567
+ <line number="113" hits="0"/>
1568
+ <line number="116" hits="0"/>
1569
+ <line number="118" hits="0"/>
1570
+ <line number="129" hits="0"/>
1571
+ <line number="130" hits="0"/>
1572
+ <line number="132" hits="0"/>
1573
+ <line number="133" hits="0"/>
1574
+ <line number="134" hits="0"/>
1575
+ <line number="136" hits="0"/>
1576
+ <line number="137" hits="0"/>
1577
+ <line number="138" hits="0"/>
1578
+ <line number="141" hits="0"/>
1579
+ <line number="142" hits="0"/>
1580
+ <line number="143" hits="0"/>
1581
+ <line number="144" hits="0"/>
1582
+ <line number="146" hits="0"/>
1583
+ <line number="153" hits="0"/>
1584
+ <line number="159" hits="0"/>
1585
+ <line number="160" hits="0"/>
1586
+ <line number="163" hits="0"/>
1587
+ <line number="164" hits="0"/>
1588
+ </lines>
1589
+ </class>
1590
+ </classes>
1591
+ </package>
1592
+ <package name="src.absa.training" line-rate="0" branch-rate="0" complexity="0">
1593
+ <classes>
1594
+ <class name="__init__.py" filename="src/absa/training/__init__.py" complexity="0" line-rate="1" branch-rate="0">
1595
+ <methods/>
1596
+ <lines/>
1597
+ </class>
1598
+ <class name="mlflow_utils.py" filename="src/absa/training/mlflow_utils.py" complexity="0" line-rate="0" branch-rate="0">
1599
+ <methods/>
1600
+ <lines>
1601
+ <line number="1" hits="0"/>
1602
+ <line number="2" hits="0"/>
1603
+ <line number="3" hits="0"/>
1604
+ <line number="6" hits="0"/>
1605
+ <line number="7" hits="0"/>
1606
+ <line number="10" hits="0"/>
1607
+ <line number="13" hits="0"/>
1608
+ <line number="15" hits="0"/>
1609
+ <line number="16" hits="0"/>
1610
+ <line number="19" hits="0"/>
1611
+ <line number="37" hits="0"/>
1612
+ <line number="39" hits="0"/>
1613
+ <line number="40" hits="0"/>
1614
+ <line number="41" hits="0"/>
1615
+ <line number="43" hits="0"/>
1616
+ <line number="44" hits="0"/>
1617
+ <line number="45" hits="0"/>
1618
+ <line number="46" hits="0"/>
1619
+ <line number="48" hits="0"/>
1620
+ <line number="52" hits="0"/>
1621
+ <line number="55" hits="0"/>
1622
+ <line number="68" hits="0"/>
1623
+ <line number="70" hits="0"/>
1624
+ <line number="71" hits="0"/>
1625
+ <line number="72" hits="0"/>
1626
+ <line number="74" hits="0"/>
1627
+ <line number="81" hits="0"/>
1628
+ <line number="82" hits="0"/>
1629
+ <line number="84" hits="0"/>
1630
+ </lines>
1631
+ </class>
1632
+ </classes>
1633
+ </package>
1634
+ <package name="src.absa.utils" line-rate="1" branch-rate="0" complexity="0">
1635
+ <classes>
1636
+ <class name="__init__.py" filename="src/absa/utils/__init__.py" complexity="0" line-rate="1" branch-rate="0">
1637
+ <methods/>
1638
+ <lines/>
1639
+ </class>
1640
+ <class name="config.py" filename="src/absa/utils/config.py" complexity="0" line-rate="1" branch-rate="0">
1641
+ <methods/>
1642
+ <lines>
1643
+ <line number="1" hits="1"/>
1644
+ <line number="3" hits="1"/>
1645
+ <line number="4" hits="1"/>
1646
+ <line number="5" hits="1"/>
1647
+ <line number="6" hits="1"/>
1648
+ <line number="7" hits="1"/>
1649
+ <line number="8" hits="1"/>
1650
+ <line number="10" hits="1"/>
1651
+ <line number="11" hits="1"/>
1652
+ <line number="12" hits="1"/>
1653
+ <line number="13" hits="1"/>
1654
+ <line number="15" hits="1"/>
1655
+ <line number="16" hits="1"/>
1656
+ <line number="17" hits="1"/>
1657
+ <line number="18" hits="1"/>
1658
+ <line number="20" hits="1"/>
1659
+ <line number="21" hits="1"/>
1660
+ </lines>
1661
+ </class>
1662
+ </classes>
1663
+ </package>
1664
+ <package name="tasks" line-rate="0.2174" branch-rate="0" complexity="0">
1665
+ <classes>
1666
+ <class name="__init__.py" filename="tasks/__init__.py" complexity="0" line-rate="1" branch-rate="0">
1667
+ <methods/>
1668
+ <lines>
1669
+ <line number="1" hits="1"/>
1670
+ <line number="2" hits="1"/>
1671
+ <line number="4" hits="1"/>
1672
+ <line number="6" hits="1"/>
1673
+ <line number="10" hits="1"/>
1674
+ </lines>
1675
+ </class>
1676
+ <class name="batch_tasks.py" filename="tasks/batch_tasks.py" complexity="0" line-rate="0.1562" branch-rate="0">
1677
+ <methods/>
1678
+ <lines>
1679
+ <line number="1" hits="1"/>
1680
+ <line number="2" hits="1"/>
1681
+ <line number="3" hits="1"/>
1682
+ <line number="4" hits="1"/>
1683
+ <line number="5" hits="1"/>
1684
+ <line number="6" hits="1"/>
1685
+ <line number="7" hits="1"/>
1686
+ <line number="8" hits="1"/>
1687
+ <line number="11" hits="1"/>
1688
+ <line number="12" hits="1"/>
1689
+ <line number="13" hits="0"/>
1690
+ <line number="14" hits="0"/>
1691
+ <line number="15" hits="0"/>
1692
+ <line number="16" hits="0"/>
1693
+ <line number="17" hits="0"/>
1694
+ <line number="19" hits="0"/>
1695
+ <line number="20" hits="0"/>
1696
+ <line number="23" hits="0"/>
1697
+ <line number="24" hits="0"/>
1698
+ <line number="25" hits="0"/>
1699
+ <line number="27" hits="0"/>
1700
+ <line number="28" hits="0"/>
1701
+ <line number="30" hits="0"/>
1702
+ <line number="31" hits="0"/>
1703
+ <line number="32" hits="0"/>
1704
+ <line number="34" hits="0"/>
1705
+ <line number="36" hits="0"/>
1706
+ <line number="37" hits="0"/>
1707
+ <line number="38" hits="0"/>
1708
+ <line number="51" hits="0"/>
1709
+ <line number="52" hits="0"/>
1710
+ <line number="53" hits="0"/>
1711
+ <line number="55" hits="0"/>
1712
+ <line number="57" hits="0"/>
1713
+ <line number="62" hits="0"/>
1714
+ <line number="63" hits="0"/>
1715
+ <line number="64" hits="0"/>
1716
+ <line number="67" hits="0"/>
1717
+ <line number="68" hits="0"/>
1718
+ <line number="76" hits="0"/>
1719
+ <line number="77" hits="0"/>
1720
+ <line number="90" hits="0"/>
1721
+ <line number="91" hits="0"/>
1722
+ <line number="104" hits="0"/>
1723
+ <line number="105" hits="0"/>
1724
+ <line number="107" hits="0"/>
1725
+ <line number="108" hits="0"/>
1726
+ <line number="109" hits="0"/>
1727
+ <line number="111" hits="0"/>
1728
+ <line number="112" hits="0"/>
1729
+ <line number="113" hits="0"/>
1730
+ <line number="115" hits="0"/>
1731
+ <line number="116" hits="0"/>
1732
+ <line number="117" hits="0"/>
1733
+ <line number="118" hits="0"/>
1734
+ <line number="119" hits="0"/>
1735
+ <line number="120" hits="0"/>
1736
+ <line number="121" hits="0"/>
1737
+ <line number="124" hits="0"/>
1738
+ <line number="125" hits="0"/>
1739
+ <line number="126" hits="0"/>
1740
+ <line number="127" hits="0"/>
1741
+ <line number="128" hits="0"/>
1742
+ <line number="129" hits="0"/>
1743
+ </lines>
1744
+ </class>
1745
+ </classes>
1746
+ </package>
1747
+ </packages>
1748
+ </coverage>
docker/nginx.conf DELETED
@@ -1,31 +0,0 @@
1
- server {
2
- listen 80;
3
- server_name localhost;
4
-
5
- root /usr/share/nginx/html;
6
- index index.html;
7
-
8
- # Security headers
9
- add_header X-Content-Type-Options "nosniff" always;
10
- add_header X-Frame-Options "DENY" always;
11
- add_header X-XSS-Protection "1; mode=block" always;
12
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
13
- add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
14
- add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.tailwindcss.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'" always;
15
-
16
- # Single Page App routing
17
- location / {
18
- try_files $uri $uri/ /index.html;
19
- }
20
-
21
- # Proxy API requests
22
- location /api/ {
23
- # Rewrite /api/predict to /predict on the backend
24
- rewrite ^/api/(.*) /$1 break;
25
- proxy_pass http://api:8000;
26
- proxy_set_header Host $host;
27
- proxy_set_header X-Real-IP $remote_addr;
28
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
29
- proxy_set_header X-Forwarded-Proto $scheme;
30
- }
31
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/SECURITY.md CHANGED
@@ -9,7 +9,7 @@
9
  | HTTPS | ❌ Not enforced | Expects reverse proxy to terminate TLS |
10
  | Input Validation | ✅ Partial | Pydantic validation present; no server-side max_length |
11
  | SQL Injection | ✅ Protected | SQLAlchemy ORM parameterized queries |
12
- | XSS | ✅ Protected | Jinja2 auto-escaping |
13
  | CSRF | ❌ Not implemented | No CSRF middleware; CORS `"*"` mitigates partially |
14
  | Secrets Management | ⚠️ Manual | `.env` gitignored; Docker Compose has hardcoded dev creds |
15
  | Rate Limiting | ❌ Not implemented | No throttling on any endpoint |
 
9
  | HTTPS | ❌ Not enforced | Expects reverse proxy to terminate TLS |
10
  | Input Validation | ✅ Partial | Pydantic validation present; no server-side max_length |
11
  | SQL Injection | ✅ Protected | SQLAlchemy ORM parameterized queries |
12
+ | XSS | ✅ Protected | Streamlit dashboard; no raw HTML rendering, user input via native widgets |
13
  | CSRF | ❌ Not implemented | No CSRF middleware; CORS `"*"` mitigates partially |
14
  | Secrets Management | ⚠️ Manual | `.env` gitignored; Docker Compose has hardcoded dev creds |
15
  | Rate Limiting | ❌ Not implemented | No throttling on any endpoint |
docs/TECH_STACK.md CHANGED
@@ -7,14 +7,12 @@
7
  | **Python** | 3.11+ | Runtime | All backend/ML code |
8
  | **FastAPI** | 0.111.0 | REST API framework | `api/` routes and middleware |
9
  | **Uvicorn** | 0.29.0 | ASGI server | API entry point |
10
- | **React** | 18.2.0 | Frontend framework | `dashboard/src/` |
11
- | **Vite** | 5.0.0 | Build tool | `dashboard/vite.config.js` |
12
- | **TailwindCSS** | 3.3.5 | CSS framework | `dashboard/src/index.css` |
13
  | **PostgreSQL** | 16 (alpine) | Production database | Docker Compose |
14
  | **SQLite** | (built-in) | Development database | `absa.db` |
15
  | **Redis** | 7 (alpine) | Celery broker + cache | `api/tasks/` |
16
- | **Docker** | 27.x | Containerization | `config/docker/` |
17
- | **Docker Compose** | 3.8+ | Orchestration | `config/docker/docker-compose.yml` |
18
 
19
  ## ML / AI Stack
20
 
@@ -39,7 +37,7 @@
39
  | Technology | Version | Purpose | Where Used |
40
  |------------|---------|---------|------------|
41
  | **MLflow** | 2.13.0 | Experiment tracking | `src/training/mlflow_utils.py`, all `src/models/` |
42
- | **DVC** | 3.51.1 | Data version control | `config/dvc.yaml`, `.dvc/` |
43
  | **Evidently AI** | 0.4.30 | Data drift monitoring | `scripts/drift_monitor.py` |
44
  | **Prometheus** | latest | Metrics collection | `monitoring/prometheus.yml` |
45
  | **Grafana** | latest | Dashboard visualization | `monitoring/grafana/dashboards/` |
@@ -62,24 +60,15 @@
62
 
63
  | Technology | Version | Purpose |
64
  |------------|---------|---------|
65
- | **@tanstack/react-query** | 5.0.0 | Server state, caching, polling |
66
- | **react-router-dom** | 6.20.0 | Client routing |
67
- | **react-hot-toast** | 2.4.1 | Toast notifications |
68
- | **react-dropzone** | 14.2.3 | File upload drag-and-drop |
69
- | **axios** | 1.6.0 | HTTP client with retry |
70
- | **recharts** | 2.10.0 | Charts (line, bar, pie/donut) |
71
- | **lucide-react** | 0.290.0 | Icons |
72
- | **autoprefixer** | 10.4.16 | CSS vendor prefixes |
73
- | **postcss** | 8.4.31 | CSS processor |
74
 
75
  ## Infrastructure / Deployment
76
 
77
  | Technology | Purpose |
78
  |------------|---------|
79
  | **Docker** (multi-stage) | Build optimization |
80
- | **Nginx (alpine)** | SPA serving + API proxy |
81
  | **Railway** | API + worker cloud hosting |
82
- | **Vercel** | Frontend SPA hosting |
83
  | **HuggingFace Hub** | Model storage/pull |
84
 
85
  ## Version Compatibility Matrix
 
7
  | **Python** | 3.11+ | Runtime | All backend/ML code |
8
  | **FastAPI** | 0.111.0 | REST API framework | `api/` routes and middleware |
9
  | **Uvicorn** | 0.29.0 | ASGI server | API entry point |
10
+ | **Streamlit** | 1.37.0 | Dashboard frontend (pure Python) | `frontend/` |
 
 
11
  | **PostgreSQL** | 16 (alpine) | Production database | Docker Compose |
12
  | **SQLite** | (built-in) | Development database | `absa.db` |
13
  | **Redis** | 7 (alpine) | Celery broker + cache | `api/tasks/` |
14
+ | **Docker** | 27.x | Containerization | `docker/` |
15
+ | **Docker Compose** | 3.8+ | Orchestration | `docker/docker-compose.yml` |
16
 
17
  ## ML / AI Stack
18
 
 
37
  | Technology | Version | Purpose | Where Used |
38
  |------------|---------|---------|------------|
39
  | **MLflow** | 2.13.0 | Experiment tracking | `src/training/mlflow_utils.py`, all `src/models/` |
40
+ | **DVC** | 3.51.1 | Data version control | `dvc.yaml`, `.dvc/` |
41
  | **Evidently AI** | 0.4.30 | Data drift monitoring | `scripts/drift_monitor.py` |
42
  | **Prometheus** | latest | Metrics collection | `monitoring/prometheus.yml` |
43
  | **Grafana** | latest | Dashboard visualization | `monitoring/grafana/dashboards/` |
 
60
 
61
  | Technology | Version | Purpose |
62
  |------------|---------|---------|
63
+ | **Streamlit** | 1.37.0 | Native dashboard widgets no HTML templates or JS build step |
64
+ | **httpx** | 0.28.0 | HTTP client talking to the FastAPI backend |
 
 
 
 
 
 
 
65
 
66
  ## Infrastructure / Deployment
67
 
68
  | Technology | Purpose |
69
  |------------|---------|
70
  | **Docker** (multi-stage) | Build optimization |
 
71
  | **Railway** | API + worker cloud hosting |
 
72
  | **HuggingFace Hub** | Model storage/pull |
73
 
74
  ## Version Compatibility Matrix
docs/architecture.md CHANGED
@@ -8,9 +8,8 @@ Multilingual ABSA is a production-ready Aspect-Based Sentiment Analysis system s
8
 
9
  ```mermaid
10
  graph TB
11
- Client[Client Browser / API Consumer]
12
- Vercel[Vercel CDN]
13
- Nginx[Nginx Reverse Proxy]
14
  API[FastAPI Server]
15
  Pipeline[ABSA Pipeline]
16
  Lang[Language Detection]
@@ -23,9 +22,8 @@ graph TB
23
  Graf[Grafana]
24
  MLflow[(MLflow<br/>Experiment Tracking)]
25
 
26
- Client --> Vercel
27
- Vercel --> Nginx
28
- Nginx --> API
29
  API --> Pipeline
30
  Pipeline --> Lang
31
  Pipeline --> Neural
@@ -43,10 +41,9 @@ graph TB
43
  ```mermaid
44
  graph LR
45
  subgraph "Presentation Layer"
46
- SPA[HTMX Frontend]
47
- T_TAIL[TailwindCSS Theme]
48
- RECH[Recharts Visualizations]
49
- RQ[HTMX Polling]
50
  end
51
  subgraph "API Layer"
52
  FAST[FastAPI]
@@ -98,7 +95,7 @@ graph TB
98
  subgraph "Docker Compose (Local)"
99
  DC_API[API Service<br/>uvicorn:8000]
100
  DC_WORKER[Celery Worker]
101
- DC_DASH[Dashboard<br/>Nginx:80]
102
  DC_PG[PostgreSQL:5432]
103
  DC_REDIS[Redis:6379]
104
  DC_PROM[Prometheus:9090]
@@ -110,10 +107,6 @@ graph TB
110
  RW_PG[PostgreSQL]
111
  RW_REDIS[Redis]
112
  end
113
- subgraph "Vercel (Production)"
114
- VC_DASH[HTMX Frontend]
115
- VC_RW_ROUTE[rewrite /api/* -> Railway]
116
- end
117
 
118
  DC_DASH --> DC_API
119
  DC_API --> DC_PG
@@ -123,8 +116,6 @@ graph TB
123
  DC_PROM -->|scrape| DC_API
124
  DC_GRAF --> DC_PROM
125
 
126
- VC_DASH --> VC_RW_ROUTE
127
- VC_RW_ROUTE --> RW_API
128
  RW_API --> RW_PG
129
  RW_API --> RW_REDIS
130
  RW_WORKER --> RW_REDIS
@@ -232,12 +223,10 @@ sequenceDiagram
232
  ```mermaid
233
  graph TB
234
  subgraph "Edge"
235
- DNS[DNS: Vercel]
236
- SSL[TLS Termination]
237
  end
238
  subgraph "Frontend Hosting"
239
- FE[Vercel<br/>Static SPA]
240
- FE_CDN[Global CDN]
241
  end
242
  subgraph "Backend Hosting"
243
  BE[Railway<br/>Docker Container]
@@ -254,10 +243,8 @@ graph TB
254
  MLFLOW[MLflow<br/>SQLite Backend]
255
  end
256
 
257
- DNS --> SSL
258
  SSL --> FE
259
- FE --> FE_CDN
260
- FE_CDN --> BE
261
  BE --> HEALTH
262
  BE --> AUTO
263
  BE --> PG
@@ -277,4 +264,4 @@ graph TB
277
  | **ONNX INT8 dynamic quantization** | 4x smaller, 4.6x faster than PyTorch with only 1% F1 drop |
278
  | **SQLite for dev, PostgreSQL for prod** | Zero-config local dev, production-grade concurrency |
279
  | **Celery for batch only** | Single-review inference is fast enough for synchronous response |
280
- | **Mock data in dashboard charts** | Decoupled frontend/backend development; real integration deferred |
 
8
 
9
  ```mermaid
10
  graph TB
11
+ Client[Client Browser]
12
+ DASH[Streamlit Dashboard :8501]
 
13
  API[FastAPI Server]
14
  Pipeline[ABSA Pipeline]
15
  Lang[Language Detection]
 
22
  Graf[Grafana]
23
  MLflow[(MLflow<br/>Experiment Tracking)]
24
 
25
+ Client --> DASH
26
+ DASH -->|HTTP / JSON| API
 
27
  API --> Pipeline
28
  Pipeline --> Lang
29
  Pipeline --> Neural
 
41
  ```mermaid
42
  graph LR
43
  subgraph "Presentation Layer"
44
+ SPA[Streamlit Dashboard<br/>frontend/ - pure Python]
45
+ WIDGETS[Native Widgets<br/>No HTML templates]
46
+ CLIENT[httpx API Client]
 
47
  end
48
  subgraph "API Layer"
49
  FAST[FastAPI]
 
95
  subgraph "Docker Compose (Local)"
96
  DC_API[API Service<br/>uvicorn:8000]
97
  DC_WORKER[Celery Worker]
98
+ DC_DASH[Streamlit Dashboard<br/>frontend:8501]
99
  DC_PG[PostgreSQL:5432]
100
  DC_REDIS[Redis:6379]
101
  DC_PROM[Prometheus:9090]
 
107
  RW_PG[PostgreSQL]
108
  RW_REDIS[Redis]
109
  end
 
 
 
 
110
 
111
  DC_DASH --> DC_API
112
  DC_API --> DC_PG
 
116
  DC_PROM -->|scrape| DC_API
117
  DC_GRAF --> DC_PROM
118
 
 
 
119
  RW_API --> RW_PG
120
  RW_API --> RW_REDIS
121
  RW_WORKER --> RW_REDIS
 
223
  ```mermaid
224
  graph TB
225
  subgraph "Edge"
226
+ SSL[TLS Termination<br/>Reverse Proxy]
 
227
  end
228
  subgraph "Frontend Hosting"
229
+ FE[Streamlit Dashboard<br/>Pure Python]
 
230
  end
231
  subgraph "Backend Hosting"
232
  BE[Railway<br/>Docker Container]
 
243
  MLFLOW[MLflow<br/>SQLite Backend]
244
  end
245
 
 
246
  SSL --> FE
247
+ FE --> BE
 
248
  BE --> HEALTH
249
  BE --> AUTO
250
  BE --> PG
 
264
  | **ONNX INT8 dynamic quantization** | 4x smaller, 4.6x faster than PyTorch with only 1% F1 drop |
265
  | **SQLite for dev, PostgreSQL for prod** | Zero-config local dev, production-grade concurrency |
266
  | **Celery for batch only** | Single-review inference is fast enough for synchronous response |
267
+ | **Streamlit dashboard over the REST API** | Thin, pure-Python UI layer; the dashboard never imports the ML pipeline directly |
frontend/Home.py CHANGED
@@ -8,7 +8,6 @@ Navigation split:
8
  from __future__ import annotations
9
 
10
  import streamlit as st
11
- from ui import apply_theme
12
 
13
  st.set_page_config(
14
  page_title="Multilingual ABSA",
@@ -17,8 +16,6 @@ st.set_page_config(
17
  initial_sidebar_state="expanded",
18
  )
19
 
20
- apply_theme()
21
-
22
  analyzer = st.Page(
23
  "views/predict.py",
24
  title="Sentiment Analyzer",
 
8
  from __future__ import annotations
9
 
10
  import streamlit as st
 
11
 
12
  st.set_page_config(
13
  page_title="Multilingual ABSA",
 
16
  initial_sidebar_state="expanded",
17
  )
18
 
 
 
19
  analyzer = st.Page(
20
  "views/predict.py",
21
  title="Sentiment Analyzer",
frontend/ui.py CHANGED
@@ -1,4 +1,8 @@
1
- """Shared UI helpers for the Streamlit dashboard: theme, cards, badges."""
 
 
 
 
2
 
3
  from __future__ import annotations
4
 
@@ -7,11 +11,11 @@ from typing import Iterable
7
 
8
  import streamlit as st
9
 
10
- SENTIMENT_COLORS: dict[str, str] = {
11
- "positive": "#16a34a",
12
- "negative": "#dc2626",
13
- "neutral": "#64748b",
14
- "conflict": "#9333ea",
15
  }
16
 
17
  LANGUAGE_LABELS: dict[str, str] = {
@@ -41,172 +45,39 @@ _SAMPLE_REVIEWS: list[dict[str, str]] = [
41
  ]
42
 
43
 
44
- def apply_theme() -> None:
45
- """Inject custom CSS: modern gradient hero, cards, badges, spacing."""
46
- st.markdown(
47
- """
48
- <style>
49
- :root {
50
- --absa-primary: #6366f1;
51
- --absa-primary-2: #8b5cf6;
52
- --absa-accent: #06b6d4;
53
- }
54
-
55
- /* Modern gradient hero banner */
56
- .absa-hero {
57
- background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 55%, #06b6d4 100%);
58
- border-radius: 1.25rem;
59
- padding: 2.5rem 2.5rem 2rem 2.5rem;
60
- color: white;
61
- margin-bottom: 1.5rem;
62
- box-shadow: 0 10px 30px rgba(99, 102, 241, 0.35);
63
- }
64
- .absa-hero h1 { font-size: 2.4rem; font-weight: 800; margin: 0 0 0.4rem 0; letter-spacing: -0.02em; }
65
- .absa-hero p { font-size: 1.05rem; opacity: 0.92; margin: 0; max-width: 46rem; }
66
-
67
- /* Action / feature cards */
68
- .absa-card {
69
- background: linear-gradient(180deg, #ffffff, #f8fafc);
70
- border: 1px solid #e2e8f0;
71
- border-radius: 1rem;
72
- padding: 1.35rem 1.4rem;
73
- margin-bottom: 1rem;
74
- box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
75
- transition: transform 0.15s ease, box-shadow 0.15s ease;
76
- }
77
- .absa-card:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08); }
78
- .absa-card .card-title { font-size: 1.15rem; font-weight: 700; color: #0f172a; margin-bottom: 0.3rem; }
79
- .absa-card .card-desc { font-size: 0.92rem; color: #475569; margin-bottom: 0.7rem; }
80
- .absa-card .card-icon { font-size: 1.6rem; margin-bottom: 0.4rem; }
81
-
82
- /* Aspect result card */
83
- .absa-result {
84
- border: 1px solid #e2e8f0;
85
- border-left: 6px solid #94a3b8;
86
- border-radius: 0.9rem;
87
- padding: 1rem 1.2rem;
88
- margin-bottom: 0.85rem;
89
- background: #ffffff;
90
- }
91
- .absa-result .aspect-name { font-weight: 700; color: #0f172a; font-size: 1.05rem; }
92
- .absa-result .confidence { color: #64748b; font-size: 0.9rem; margin-top: 0.3rem; }
93
-
94
- /* Sentiment badge */
95
- .absa-badge {
96
- display: inline-block;
97
- padding: 0.22rem 0.8rem;
98
- border-radius: 999px;
99
- font-size: 0.78rem;
100
- font-weight: 700;
101
- letter-spacing: 0.04em;
102
- text-transform: uppercase;
103
- color: #fff;
104
- }
105
-
106
- /* Confidence bar */
107
- .absa-bar {
108
- height: 8px;
109
- border-radius: 999px;
110
- background: #e2e8f0;
111
- overflow: hidden;
112
- margin-top: 0.5rem;
113
- }
114
- .absa-bar-fill { height: 100%; border-radius: 999px; }
115
-
116
- /* Language pill */
117
- .absa-pill {
118
- display: inline-block;
119
- background: #eef2ff;
120
- color: #4338ca;
121
- border-radius: 999px;
122
- padding: 0.15rem 0.7rem;
123
- font-size: 0.8rem;
124
- font-weight: 600;
125
- margin-right: 0.4rem;
126
- }
127
-
128
- div[data-testid="stMetric"] {
129
- background: #ffffff;
130
- border: 1px solid #e2e8f0;
131
- border-radius: 0.9rem;
132
- padding: 0.9rem 1rem;
133
- box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
134
- }
135
-
136
- .stButton > button[kind="primary"] {
137
- background: linear-gradient(135deg, #6366f1, #8b5cf6);
138
- border: none;
139
- font-weight: 600;
140
- }
141
-
142
- footer { visibility: hidden; }
143
- </style>
144
- """,
145
- unsafe_allow_html=True,
146
- )
147
-
148
-
149
  def hero(title: str, subtitle: str) -> None:
150
- """Render the gradient hero banner."""
151
- st.markdown(
152
- f"""
153
- <div class="absa-hero">
154
- <h1>{title}</h1>
155
- <p>{subtitle}</p>
156
- </div>
157
- """,
158
- unsafe_allow_html=True,
159
- )
160
 
161
 
162
- def feature_card(icon: str, title: str, description: str, accent: str = "#6366f1") -> None:
163
- """Render a clickable-style feature card."""
164
- st.markdown(
165
- f"""
166
- <div class="absa-card">
167
- <div class="card-icon">{icon}</div>
168
- <div class="card-title">{title}</div>
169
- <div class="card-desc">{description}</div>
170
- </div>
171
- """,
172
- unsafe_allow_html=True,
173
- )
174
-
175
 
176
- def sentiment_badge(sentiment: str) -> str:
177
- """HTML for a colored sentiment badge."""
178
- color = SENTIMENT_COLORS.get(sentiment, "#64748b")
179
- return f'<span class="absa-badge" style="background:{color};">{sentiment}</span>'
180
 
181
-
182
- def aspect_card(aspect: str, sentiment: str, confidence: float) -> str:
183
- """HTML for a single aspect result card with a confidence bar."""
184
- color = SENTIMENT_COLORS.get(sentiment, "#64748b")
185
- pct = max(0.0, min(100.0, float(confidence) * 100.0))
186
- return (
187
- f'<div class="absa-result" style="border-left-color:{color};">'
188
- f'<div class="aspect-name">{aspect} &nbsp; {sentiment_badge(sentiment)}</div>'
189
- f'<div class="confidence">Confidence: {confidence:.2f}</div>'
190
- f'<div class="absa-bar"><div class="absa-bar-fill" style="width:{pct:.1f}%;background:{color};"></div></div>'
191
- f"</div>"
192
- )
193
 
194
 
195
  def render_aspects(aspects: Iterable[dict]) -> None:
196
- """Render a list of aspect dicts as styled cards."""
197
  items = list(aspects)
198
  if not items:
199
  st.info("No aspects detected in this text.")
200
  return
201
- html = "".join(
202
- aspect_card(
203
- str(a.get("aspect", "N/A")),
204
- str(a.get("sentiment", "neutral")),
205
- float(a.get("confidence", 0.0)),
206
- )
207
- for a in items
208
- )
209
- st.markdown(html, unsafe_allow_html=True)
210
 
211
 
212
  def language_options() -> list[str]:
@@ -230,16 +101,10 @@ def require_admin() -> bool:
230
  if st.session_state.get("admin_ok"):
231
  return True
232
 
 
233
  st.markdown(
234
- """
235
- <div style="max-width:28rem;margin:4rem auto;text-align:center;">
236
- <div style="font-size:2.5rem;">🔒</div>
237
- <h2>Admin access</h2>
238
- <p style="color:#64748b;">This section is restricted. Enter the admin
239
- password to view application status and features.</p>
240
- </div>
241
- """,
242
- unsafe_allow_html=True,
243
  )
244
  candidate = st.text_input("Admin password", type="password", key="admin_password")
245
  if st.button("Unlock", type="primary", key="admin_unlock"):
 
1
+ """Shared UI helpers for the Streamlit dashboard.
2
+
3
+ This module uses only native Streamlit components and Markdown — no inline
4
+ HTML or CSS. Styling is delegated to Streamlit's built-in theming.
5
+ """
6
 
7
  from __future__ import annotations
8
 
 
11
 
12
  import streamlit as st
13
 
14
+ SENTIMENT_EMOJI: dict[str, str] = {
15
+ "positive": "🟢",
16
+ "negative": "🔴",
17
+ "neutral": "",
18
+ "conflict": "🟣",
19
  }
20
 
21
  LANGUAGE_LABELS: dict[str, str] = {
 
45
  ]
46
 
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  def hero(title: str, subtitle: str) -> None:
49
+ """Render a page header using native Streamlit elements."""
50
+ st.title(title)
51
+ st.markdown(subtitle)
 
 
 
 
 
 
 
52
 
53
 
54
+ def feature_card(icon: str, title: str, description: str) -> None:
55
+ """Render a feature summary as a native bordered container."""
56
+ with st.container(border=True):
57
+ st.markdown(f"**{icon} {title}**")
58
+ st.write(description)
 
 
 
 
 
 
 
 
59
 
 
 
 
 
60
 
61
+ def sentiment_label(sentiment: str) -> str:
62
+ """Human-friendly label for a sentiment class."""
63
+ return f"{SENTIMENT_EMOJI.get(sentiment, '⚪')} {sentiment.capitalize()}"
 
 
 
 
 
 
 
 
 
64
 
65
 
66
  def render_aspects(aspects: Iterable[dict]) -> None:
67
+ """Render aspect results as native bordered cards with a confidence bar."""
68
  items = list(aspects)
69
  if not items:
70
  st.info("No aspects detected in this text.")
71
  return
72
+ for item in items:
73
+ aspect = str(item.get("aspect", "N/A"))
74
+ sentiment = str(item.get("sentiment", "neutral"))
75
+ confidence = float(item.get("confidence", 0.0))
76
+ with st.container(border=True):
77
+ col_name, col_sent = st.columns([3, 1])
78
+ col_name.markdown(f"**{aspect}**")
79
+ col_sent.markdown(sentiment_label(sentiment))
80
+ st.progress(confidence, text=f"Confidence: {confidence:.2f}")
81
 
82
 
83
  def language_options() -> list[str]:
 
101
  if st.session_state.get("admin_ok"):
102
  return True
103
 
104
+ st.title("🔒 Admin access")
105
  st.markdown(
106
+ "This section is restricted. Enter the admin password to view "
107
+ "application status and features."
 
 
 
 
 
 
 
108
  )
109
  candidate = st.text_input("Admin password", type="password", key="admin_password")
110
  if st.button("Unlock", type="primary", key="admin_unlock"):
frontend/views/admin/batch.py CHANGED
@@ -8,9 +8,7 @@ import time
8
  import pandas as pd
9
  import streamlit as st
10
  from absa_client import get_client
11
- from ui import apply_theme, hero, require_admin
12
-
13
- apply_theme()
14
 
15
  if not require_admin():
16
  st.stop()
@@ -29,19 +27,10 @@ with col_up:
29
  uploaded = st.file_uploader("Upload a CSV file", type=["csv"], accept_multiple_files=False)
30
 
31
  with col_hint:
32
- st.markdown(
33
- """
34
- <div class="absa-card">
35
- <div class="card-title">Required format</div>
36
- <div class="card-desc">A CSV with a <code>text</code> column. Limits:</div>
37
- <ul>
38
- <li>Max 10,000 rows</li>
39
- <li>Max 50 MB</li>
40
- </ul>
41
- </div>
42
- """,
43
- unsafe_allow_html=True,
44
- )
45
 
46
  df_preview = None
47
  if uploaded is not None:
 
8
  import pandas as pd
9
  import streamlit as st
10
  from absa_client import get_client
11
+ from ui import hero, require_admin
 
 
12
 
13
  if not require_admin():
14
  st.stop()
 
27
  uploaded = st.file_uploader("Upload a CSV file", type=["csv"], accept_multiple_files=False)
28
 
29
  with col_hint:
30
+ with st.container(border=True):
31
+ st.markdown("**Required format**")
32
+ st.write("A CSV with a `text` column. Limits:")
33
+ st.markdown("- Max 10,000 rows\n- Max 50 MB")
 
 
 
 
 
 
 
 
 
34
 
35
  df_preview = None
36
  if uploaded is not None:
frontend/views/admin/monitor.py CHANGED
@@ -4,9 +4,7 @@ from __future__ import annotations
4
 
5
  import streamlit as st
6
  from absa_client import get_client
7
- from ui import apply_theme, hero, require_admin
8
-
9
- apply_theme()
10
 
11
  if not require_admin():
12
  st.stop()
@@ -46,7 +44,7 @@ col_c.metric("Languages", "en · hi · hinglish")
46
 
47
  st.caption(
48
  "Tip: run the API with `uvicorn api.main:app --port 8000` and this "
49
- "dashboard with `streamlit run streamlit_app/Home.py`."
50
  )
51
 
52
  if auto_refresh:
 
4
 
5
  import streamlit as st
6
  from absa_client import get_client
7
+ from ui import hero, require_admin
 
 
8
 
9
  if not require_admin():
10
  st.stop()
 
44
 
45
  st.caption(
46
  "Tip: run the API with `uvicorn api.main:app --port 8000` and this "
47
+ "dashboard with `streamlit run frontend/Home.py`."
48
  )
49
 
50
  if auto_refresh:
frontend/views/admin/overview.py CHANGED
@@ -4,9 +4,7 @@ from __future__ import annotations
4
 
5
  import streamlit as st
6
  from absa_client import get_client
7
- from ui import apply_theme, feature_card, hero, require_admin
8
-
9
- apply_theme()
10
 
11
  if not require_admin():
12
  st.stop()
@@ -82,17 +80,11 @@ with col_c:
82
  "Health checks, service metadata, and auto-refresh diagnostics.",
83
  )
84
 
85
- st.markdown(
86
- """
87
- <div class="absa-card">
88
- <div class="card-title">REST API</div>
89
- <div class="card-desc">
90
- All dashboard features are backed by the JSON API at <code>/docs</code>:
91
- </div>
92
- <code>POST /predict</code> · <code>POST /batch</code> ·
93
- <code>GET /status/{job_id}</code> · <code>GET /health</code> ·
94
- <code>GET /info</code> · <code>GET /metrics</code>
95
- </div>
96
- """,
97
- unsafe_allow_html=True,
98
- )
 
4
 
5
  import streamlit as st
6
  from absa_client import get_client
7
+ from ui import feature_card, hero, require_admin
 
 
8
 
9
  if not require_admin():
10
  st.stop()
 
80
  "Health checks, service metadata, and auto-refresh diagnostics.",
81
  )
82
 
83
+ with st.container(border=True):
84
+ st.markdown("**REST API**")
85
+ st.write("All dashboard features are backed by the JSON API at `/docs`:")
86
+ st.code(
87
+ "POST /predict · POST /batch\n"
88
+ "GET /status/{job_id} · GET /health\n"
89
+ "GET /info · GET /metrics"
90
+ )
 
 
 
 
 
 
frontend/views/predict.py CHANGED
@@ -8,22 +8,14 @@ from __future__ import annotations
8
 
9
  import streamlit as st
10
  from absa_client import get_client
11
- from ui import LANGUAGE_LABELS, apply_theme, language_options, render_aspects
12
 
13
  client = get_client()
14
- apply_theme()
15
 
16
- st.markdown(
17
- """
18
- <div style="text-align:center;padding:1.2rem 0 0.5rem 0;">
19
- <h1 style="font-size:2.3rem;margin:0;letter-spacing:-0.02em;">💬 Sentiment Analyzer</h1>
20
- <p style="color:#64748b;font-size:1.05rem;margin-top:0.4rem;">
21
- Paste a review or comment in English, Hindi, or Hinglish and get
22
- aspect-level sentiment instantly.
23
- </p>
24
- </div>
25
- """,
26
- unsafe_allow_html=True,
27
  )
28
 
29
  # ── Input ─────────────────────────────────────────────────────────────────────
 
8
 
9
  import streamlit as st
10
  from absa_client import get_client
11
+ from ui import LANGUAGE_LABELS, hero, language_options, render_aspects
12
 
13
  client = get_client()
 
14
 
15
+ hero(
16
+ "💬 Sentiment Analyzer",
17
+ "Paste a review or comment in English, Hindi, or Hinglish and get "
18
+ "aspect-level sentiment instantly.",
 
 
 
 
 
 
 
19
  )
20
 
21
  # ── Input ─────────────────────────────────────────────────────────────────────
tests/web/test_streamlit_app.py CHANGED
@@ -45,6 +45,7 @@ def test_predict_is_clean_input_to_results_view():
45
  at = AppTest.from_file(os.path.join(ROOT, APP_FILES["predict"]))
46
  at.run(timeout=30)
47
  rendered = " ".join(str(m.value) for m in at.markdown)
 
48
  assert "Sentiment Analyzer" in rendered
49
  assert len(at.text_area) >= 1
50
  assert any(b.label == "Analyze Sentiment" for b in at.button)
@@ -62,6 +63,7 @@ def test_home_navigation_lands_on_analyzer():
62
  at.run(timeout=30)
63
  assert not at.exception
64
  rendered = " ".join(str(m.value) for m in at.markdown)
 
65
  assert "Sentiment Analyzer" in rendered
66
 
67
 
 
45
  at = AppTest.from_file(os.path.join(ROOT, APP_FILES["predict"]))
46
  at.run(timeout=30)
47
  rendered = " ".join(str(m.value) for m in at.markdown)
48
+ rendered += " " + " ".join(str(t.value) for t in at.title)
49
  assert "Sentiment Analyzer" in rendered
50
  assert len(at.text_area) >= 1
51
  assert any(b.label == "Analyze Sentiment" for b in at.button)
 
63
  at.run(timeout=30)
64
  assert not at.exception
65
  rendered = " ".join(str(m.value) for m in at.markdown)
66
+ rendered += " " + " ".join(str(t.value) for t in at.title)
67
  assert "Sentiment Analyzer" in rendered
68
 
69