fix: update documentation status and enhance technical description for microservice importer
Test Suite / test (24.15.0) (push) Has been cancelled

This commit is contained in:
Nils-Johan Gynther
2026-05-11 16:41:20 +02:00
parent c639eae270
commit 4596b80408
3 changed files with 230 additions and 263 deletions
+28 -203
View File
@@ -1,218 +1,43 @@
# Microservice Importer # Microservice Importer
Intern import-tjänst (`importer-api`) för [recipe-app](../recipe-app). Hanterar URL-skrapning, OCR, PDF-parsning och AI-kvittoparsning utan databas. Körs som Docker-tjänst på det interna `recipe-internal`-nätverket exponeras ej externt. Intern import-tjänst (`importer-api`) för [recipe-app](../recipe-app). Den hanterar URL-skrapning, OCR, PDF-parsning och AI-kvittoparsning utan databas. Tjänsten körs som Docker-tjänst på det interna `recipe-internal`-nätverket och exponeras inte externt.
## Dokumentstatus (2026-05-03) ## Dokumentstatus (2026-05-10)
### Målgrupp Det här dokumentet är skrivet för systemadministratörer och utvecklare som driftar eller vidareutvecklar importtjänsten. För arkitektur, drift och tekniska detaljer, se [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md). För roadmap och prioriteringar, se [next_steps_MSImporter.md](next_steps_MSImporter.md).
Detta dokument är för systemadministratörer och utvecklare som driftar eller vidareutvecklar importtjänsten.
### Tillägg från senaste sessionerna ## Vad tjänsten gör
- Regelbaserad kvittotolkning har förbättrats för multipack, enheter och antaluttryck.
- Parserstödet för brödrelaterade produkter och guardrails mot felkategorisering har utökats.
- Integrationen med klienten för kvitto-session är fortfarande stateless i denna tjänst; ingen serverlagring av användarsession infördes.
- Kvittokategorisering: nya regler för pasta, grädde, ägg, juice, godis, och potatis samt justerad AI-guardrail.
- Testinfrastruktur: parametriserade enhetstester för kvittoimport (18 testfall) och CI/CD-pipeline med automatiserad testkörning på push.- **PDF-fix:** `pdf-parse` importeras nu med `require()` (CJS-kompatibilitet). `pdfjs-dist/legacy/build/pdf.js` används som fallback för att undvika `DOMMatrix is not defined` i Node.js-miljö.
- **Retry-förbättring:** Mistral 429/503 → väntar `3000 * attempt` ms innan nytt försök.
- **Reproducerbart bygge:** `package-lock.json` spåras i git; Dockerfile kör `npm ci`.
- **AI-skippning för icke-produktrader:** `looksLikeReceiptProductLine()` filtrerar PDF-rader utan siffra (header/footer/butiksinformation) — dessa skickas inte till Mistral.
## Viktigt!! Kod- och byggpraxis!
Säkerställ att inga absoluta Windows-sökvägar används i koden, för att stödja bygg och drift på Linux/Ubuntu
--- - Tar emot URL:er, filer och markdown för importflöden
- Skrapar receptsidor och extraherar `imageUrl` när det finns
- Kör OCR för bilder och skannade dokument
- Tolkar kvitton via Mistral AI
- Returnerar strukturerad data till recipe-app utan att lagra någon session eller databaspost
## Features ## Flöden
### Quick-import (`POST /api/quick-import`) - `POST /api/quick-import` för URL-skrapning, bild-OCR och PDF-import
- **URL-skrapning** — ICA.se (JSON-LD) och generisk parser. Extraherar `imageUrl` från receptbild. - `POST /api/recipes/parse-markdown` för markdown till strukturerat recept
- **OCR (bild)** — tesseract.js, svenska+engelska. Returnerar `source: 'image'`. - `POST /api/receipt-import/parse` för kvittobild eller PDF till `ParsedReceiptItem[]`
- **PDF-parsning** — pdf-parse för digitala PDFs, OCR-fallback för skannade. - `GET /api/health` för Docker healthcheck
- **Multipart** — Tar emot antingen JSON-body (`{ url }`) eller FormData (`file`).
### Parse-Markdown (`POST /api/recipes/parse-markdown`) ## Viktigt
Tolkar Markdown-recept till strukturerat JSON utan databas.
### Kvittoparsning (`POST /api/receipt-import/parse`) - Inga absoluta Windows-sökvägar ska användas i kod eller scripts
- Bild (JPEG/PNG/WebP/HEIC/HEIF) eller PDF - Tjänsten är stateless
- **Bild:** `mistral-small-2603` (vision-kapabel) med retry-logik (3 försök, `3000 * attempt` ms fördröjning vid 429/503) - Ingen databas är konfigurerad i tjänsten
- **PDF:** `pdf-parse` → fallback `pdfjs-dist/legacy` → regelbaserad parsning → AI enbart för rader med namntext + siffra - Host-port 3001 används av `wetty` på servern och får därför inte exponeras av importtjänsten
- Returnerar `ParsedReceiptItem[]` med fälten `rawName`, `quantity`, `unit`, `price`, `brand`, `origin`
- Inbyggda regler i AI-prompten styr tolkning av `quantity`/`unit` (se nedan)
### Health (`GET /api/health`) ## Kort faktadel
Används av Docker-healthcheck i `recipe-app/compose.yml`. Returnerar `{ status: "ok" }`.
--- - Runtime: Node.js 22-alpine
- Ramverk: NestJS 10 + TypeScript 5
## Miljövariabler - OCR: `tesseract.js`
- PDF: `pdf-parse` med `pdfjs-dist/legacy` fallback
| Variabel | Beskrivning | Standardvärde | - AI: `@mistralai/mistralai`
|---|---|---| - Upload: `multer`
| `MISTRAL_API_KEY` | API-nyckel för Mistral AI | (krävs för kvittoparsning) | - Alpine-paket: `tesseract-ocr`, `tesseract-ocr-data-swe`, `tesseract-ocr-data-eng`
| `PORT` | HTTP-port | `3001` |
---
## Arkitektur
### Backend (NestJS 10, TypeScript 5, Node.js 22-alpine)
**Port:** 3001 (intern, ej exponerad till host)
```
src/
├── app.module.ts # Root module + HealthController (GET /api/health)
├── main.ts
├── common/
│ ├── filters/global-exception.filter.ts
│ └── utils/normalize-name.ts
├── web-scraping-service/ # Quick-import (URL + fil)
│ ├── web-scraping.module.ts
│ ├── controllers/quick-import.controller.ts # POST /api/quick-import
│ ├── services/quick-import.service.ts # Scraping, OCR, PDF
│ └── parsers/
│ ├── base.parser.ts # ParsedRecipe interface
│ ├── ica.parser.ts # ICA.se JSON-LD + imageUrl
│ └── generic.parser.ts
├── receipt-parsing/ # Kvittoparsning via Mistral AI
│ ├── receipt-parsing.module.ts
│ ├── receipt-parsing.controller.ts # POST /api/receipt-import/parse
│ └── receipt-parsing.service.ts
├── document-service/ # PDF-dokumentimport
│ ├── document-service.module.ts
│ ├── controllers/document-import.controller.ts
│ ├── services/document-import.service.ts
│ └── parsers/
│ ├── document.parser.ts
│ └── pdf.parser.ts
└── recipes/ # Markdown-tolkning
├── recipes.module.ts
├── recipes.controller.ts # POST /api/recipes/parse-markdown
├── recipes.service.ts
└── dto/parse-markdown.dto.ts
```
**Viktigt:** Backend har _INGEN_ databaskonfiguration — stateless service.
---
## Kvittoparsning — regler för quantity och unit
Följande regler är inbyggda i AI-prompten och styr hur Mistral tolkar mängd och enhet per produkt:
| Typ | Regel | Exempel |
|---|---|---|
| **Lösvikt** (kött, ost, frukt/grönt vägt i kassan) | `quantity` = faktisk vikt från kvittot, `unit` = `kg`/`g` | `BLANDFÄRS 20%` 0.997 kg → `quantity=0.997, unit="kg"` |
| **Förpackad vara med storlek i namn** (mejeri, dryck, konserver) | `quantity` = antal förpackningar, `unit` = `"förp"` | `MJÖLK 1,5L` × 3 → `quantity=3, unit="förp"` |
| **Multipack** (`NxYg`/`NxYml` i namn) | `quantity=1`, `unit="förp"` — räkna inte upp N | `BACON 3X120G``quantity=1, unit="förp"` |
| **Förpackat innehåll** (bröd, kex, chips) | `quantity` = antal förpackningar, `unit` = `"förp"` | `BRIOCHE SESAM` × 2 → `quantity=2, unit="förp"` |
| **Lösa styckvaror** (enstaka frukt/bröd per st) | `quantity` = antal, `unit` = `"st"` | `BANAN` × 1 → `quantity=1, unit="st"` |
Tillåtna enheter: `st`, `kg`, `g`, `l`, `dl`, `cl`, `ml`, `förp`, `pak`, `burk`, `flaska`
---
## Parser-arkitektur
### Dokument-parsers (`document-service/parsers/`)
Abstrakt bas `DocumentParser` som alla dokumenttyp-specifika parsers ärver från.
### PDF Parser (`pdf.parser.ts`)
Hanterar textbaserade PDFs via `pdf-parse`. Skannade PDFs varnas.
### Webb-parsers (`web-scraping-service/parsers/`)
Abstrakt bas `RecipeParser` med `canHandle(url)` + `parse(html)`. Implementationer:
- **`ica.parser.ts`** — ICA.se, prioriterar JSON-LD structured data, extraherar `imageUrl`
- **`generic.parser.ts`** — Fallback för alla webbplatser
---
## Deployment
`importer-api` byggs och startas via `recipe-app/compose.yml`**ej via sin egen compose-fil**.
**Serverstruktur:**
```
/opt/containers/
microservice-importer/ ← klonas separat, pullas vid deploy
recipe-app/
compose.yml ← definierar importer-api-tjänsten
deploy.sh ← kör docker compose build + up
```
**Deploy:**
```bash
# 1. Uppdatera importer (om ändringar gjorts)
cd /opt/containers/microservice-importer && git pull
# 2. Bygg och starta alla containers
cd /opt/containers/recipe-app && git pull && ./deploy.sh
```
**Loggar:**
```bash
docker logs importer-api -f
```
**Hälsokontroll:**
```bash
docker exec importer-api wget -qO- http://localhost:3001/api/health
# → {"status":"ok"}
```
**OBS:** Host-port 3001 används av `wetty` på servern. `importer-api` exponeras **aldrig** utanför Docker-nätverket — anropas via `http://importer-api:3001` från `recipe-api`.
---
## Tekniska detaljer
### Backend Stack
- **NestJS** 10 — REST API & modular architecture
- **TypeScript** 5 — Type safety
- **Node.js** 22-alpine — Runtime (Alpine Linux)
- **pdf-parse** — PDF text extraction
- **tesseract.js** — OCR (bild och skannade PDFs, svenska + engelska)
- **@mistralai/mistralai** — AI-kvittoparsning (`mistral-small-2603`)
- **multer** — Multipart file upload handling
- **Ingen databas** — Stateless service
### Systempaket (Alpine)
Installerade i Dockerfile runner-stage:
```
tesseract-ocr
tesseract-ocr-data-swe
tesseract-ocr-data-eng
```
### Error Handling
- Centraliserad `GlobalExceptionFilter` (svenska meddelanden)
- Konsistent JSON-responsformat: `{ statusCode, message, timestamp, path }`
- HTTP status codes: 200, 400, 503
---
## Framtida utbyggnader
- [x] PDF-import — textbaserad extraction
- [x] OCR för skannade bild-PDFs (tesseract.js + Alpine-paket)
- [x] Kvittoparsning via Mistral AI
- [x] ICA-receptbildsextraktion (`imageUrl` i `ParsedRecipe`)
- [ ] Fler webbplats-parsers (Arla, Tasteline, Köket.se)
- [ ] Word (.docx) import
- [ ] Swagger/OpenAPI-dokumentation
- [ ] Rate limiting / Caching
---
## Licens
MIT
---
## Support ## Support
- **Git Repo** — Gitea på `192.168.50.2:2222/nilsjohan/microservice-importer` - Git repo: Gitea på `192.168.50.2:2222/nilsjohan/microservice-importer`
+167
View File
@@ -0,0 +1,167 @@
# Teknisk beskrivning av Microservice Importer
## Dokumentstatus (2026-05-10)
Detta dokument riktar sig till utvecklare och driftansvariga för microservice-importer. Det beskriver arkitektur, drift och tekniska beslut för den interna importtjänsten.
## Roll och ansvar
`importer-api` är en stateless intern tjänst för [recipe-app](../recipe-app). Den hanterar URL-skrapning, OCR, PDF-parsning, markdown-parsning och AI-kvittoparsning utan databas eller användarsessioner.
## Arkitektur
### Körmiljö
- NestJS 10
- TypeScript 5
- Node.js 22-alpine
- Port `3001` internt
- Exponeras bara på `recipe-internal`-nätverket
### Moduler
```text
src/
├── app.module.ts # Root module + HealthController (GET /api/health)
├── main.ts
├── common/
│ ├── filters/global-exception.filter.ts
│ └── utils/normalize-name.ts
├── web-scraping-service/
│ ├── web-scraping.module.ts
│ ├── controllers/quick-import.controller.ts # POST /api/quick-import
│ ├── services/quick-import.service.ts # Scraping, OCR, PDF
│ └── parsers/
│ ├── base.parser.ts # ParsedRecipe interface
│ ├── ica.parser.ts # ICA.se JSON-LD + imageUrl
│ └── generic.parser.ts
├── receipt-parsing/
│ ├── receipt-parsing.module.ts
│ ├── receipt-parsing.controller.ts # POST /api/receipt-import/parse
│ └── receipt-parsing.service.ts
├── document-service/
│ ├── document-service.module.ts
│ ├── controllers/document-import.controller.ts
│ ├── services/document-import.service.ts
│ └── parsers/
│ ├── document.parser.ts
│ └── pdf.parser.ts
└── recipes/
├── recipes.module.ts
├── recipes.controller.ts # POST /api/recipes/parse-markdown
├── recipes.service.ts
└── dto/parse-markdown.dto.ts
```
## Endpoints
| Endpoint | Funktion |
|---|---|
| `POST /api/quick-import` | URL-skrapning, bild-OCR och PDF-import |
| `POST /api/recipes/parse-markdown` | Markdown till strukturerat recept utan databas |
| `POST /api/receipt-import/parse` | Kvittobild/PDF till `ParsedReceiptItem[]` via Mistral AI |
| `GET /api/health` | Hälsokontroll för Docker healthcheck |
## Kvittoparsning
### Modell och pipeline
- Vision-input använder `mistral-small-2603`
- PDF-flödet kör `pdf-parse` eller `pdfjs-dist/legacy/build/pdf.js` som fallback
- Regelbaserad parsning körs före AI när det är möjligt
- `looksLikeReceiptProductLine()` filtrerar bort rader utan siffra så att AI bara används för sannolika produktrader
### Mängd- och enhetsregler
Följande regler är inbyggda i kvitto-prompten:
| Typ | Regel | Exempel |
|---|---|---|
| Lösvikt | `quantity` = faktisk vikt, `unit` = `kg`/`g` | `BLANDFÄRS 20%` 0.997 kg |
| Förpackad vara med storlek i namn | `quantity` = antal förpackningar, `unit` = `förp` | `MJÖLK 1,5L` × 3 |
| Multipack | `quantity=1`, `unit=förp` | `BACON 3X120G` |
| Förpackat innehåll | `quantity` = antal förpackningar, `unit` = `förp` | `BRIOCHE SESAM` × 2 |
| Lösa styckvaror | `quantity` = antal, `unit` = `st` | `BANAN` × 1 |
Tillåtna enheter: `st`, `kg`, `g`, `l`, `dl`, `cl`, `ml`, `förp`, `pak`, `burk`, `flaska`.
### Retry och stabilitet
- Mistral 429/503 backas av med `3000 * attempt` ms
- PDF-flödet använder fallback för CJS- och Node Alpine-kompatibilitet
- `GlobalExceptionFilter` ger konsekventa felobjekt
## Deployment
`importer-api` byggs och startas via [recipe-app/compose.yml](../recipe-app/compose.yml) och inte via egen compose-fil.
### Serverlayout
```text
/opt/containers/
microservice-importer/
recipe-app/
compose.yml
deploy.sh
```
### Driftsekvens
```bash
cd /opt/containers/microservice-importer && git pull
cd /opt/containers/recipe-app && git pull && ./deploy.sh
```
### Hälsokontroll
```bash
docker exec importer-api wget -qO- http://localhost:3001/api/health
```
## Tekniska detaljer
### Byggberoenden
- `pdf-parse`
- `tesseract.js`
- `@mistralai/mistralai`
- `multer`
### Alpine-paket
- `tesseract-ocr`
- `tesseract-ocr-data-swe`
- `tesseract-ocr-data-eng`
### Viktiga tekniska beslut
- Tjänsten är stateless och saknar databaskonfiguration
- Importer exponeras aldrig externt, bara internt via Docker-nätverket
- Host-port 3001 är upptagen av `wetty` och får därför inte användas av tjänsten
## Parser-arkitektur
### Dokument-parsers
Abstrakt bas `DocumentParser` används för dokumenttypsspecifik parsing.
### PDF Parser
`pdf.parser.ts` hanterar textbaserade PDFs. Skannade PDFs varnas och kan falla tillbaka på OCR-vägen.
### Webb-parsers
- `ica.parser.ts` prioriterar JSON-LD och extraherar `imageUrl`
- `generic.parser.ts` är fallback för webbplatser utan specialparser
## Framtida utbyggnader
- Fler webbplats-parsers som Arla, Tasteline och Köket.se
- Word/import av `.docx`
- Swagger/OpenAPI-dokumentation
- Caching av skrapade sidor om belastningen mot externa webbplatser blir ett problem
## Referenser
- [README.md](README.md)
- [NEXT_STEPS.md](next_steps_MSImporter.md)
+35 -60
View File
@@ -1,21 +1,21 @@
# Plan för vidareutveckling av Microservice Importer # Plan för vidareutveckling av Microservice Importer
## Dokumentstatus (2026-05-03) ## Dokumentstatus (2026-05-10)
Detta dokument riktar sig till utvecklare och driftansvariga för microservice-importer. Detta dokument riktar sig till utvecklare och driftansvariga för microservice-importer. Teknisk referens finns i [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md).
### Senast avklarat i angränsande flöden ### Senast avklarat i angränsande flöden
- Regelbaserad kvittotolkning har stärkts för multipack/enheter och svårare radformat. - Regelbaserad kvittotolkning har stärkts för multipack, enheter och svårare radformat.
- Bröd-/rostbrödklassning har utökade guardrails för att minska felaktig kategorisering. - Bröd- och rostbrödklassning har utökade guardrails för att minska felaktig kategorisering.
- Klientens granskningsflöde och sessionpersistens i Flutter är implementerat, vilket minskar avbrott mellan parse och spara. - Klientens granskningsflöde och sessionpersistens i Flutter är implementerat, vilket minskar avbrott mellan parse och spara.
- Kvittokategorisering: nya regler för pasta, grädde, ägg, juice, godis, och potatis samt justerad AI-guardrail. - Kvittokategorisering: nya regler för pasta, grädde, ägg, juice, godis och potatis samt justerad AI-guardrail.
- Testinfrastruktur: parametriserade enhetstester för kvittoimport (18 testfall) och CI/CD-pipeline med automatiserad testkörning på push. - Testinfrastruktur: parametriserade enhetstester för kvittoimport och CI/CD-pipeline med automatiserad testkörning på push.
- **PDF-parsning stabiliserad:** `pdf-parse` använder nu `require()` för CJS-kompatibilitet; `pdfjs-dist/legacy/build/pdf.js` används som fallback för att undvika `DOMMatrix`-fel i Node.js-miljö. - PDF-parsning stabiliserad med `require()` och `pdfjs-dist/legacy/build/pdf.js` som fallback.
- **Retry-logik förbättrad:** Mistral-anrop vid 429/503 väntar nu `3000 * attempt` ms (3s, 6s, 9s) i stället för fast 1s. - Retry-logik förbättrad för 429/503.
- **Reproducerbart bygge:** `package-lock.json` är nu spårat i repot; Dockerfile använder `npm ci`. - Reproducerbart bygge via `package-lock.json` och `npm ci`.
- **AI-optimering implementerad:** `looksLikeReceiptProductLine()` filtrerar bort PDF-rader utan siffra (header/footer/butiksinformation) innan Mistral-anrop. Minskar drastiskt antal onödiga AI-anrop vid kvittoimport. - `looksLikeReceiptProductLine()` filtrerar bort PDF-rader utan siffra innan Mistral-anrop.
## Status (2026-05-03) — Driftsatt och integrerad med recipe-app ## Status (2026-05-10) — Driftsatt och integrerad med recipe-app
`microservice-importer` körs som intern tjänst (`importer-api`) i `recipe-app/compose.yml`. Alla importflöden är delegerade och driftsatta. `microservice-importer` körs som intern tjänst (`importer-api`) i `recipe-app/compose.yml`. Alla importflöden är delegerade och driftsatta.
@@ -51,7 +51,7 @@ cd /opt/containers/recipe-app && git pull && ./deploy.sh
### Medel prioritet ### Medel prioritet
- **Fler webbplats-parsers** — Specifika parsers för t.ex. Tasteline, Köket.se, Arla - **Fler webbplats-parsers** — Specifika parsers för t.ex. Tasteline, Köket.se, Arla
- **Swagger/OpenAPI** — Automatisk API-dokumentation via `@nestjs/swagger` - **Swagger/OpenAPI** — Automatisk API-dokumentation via `@nestjs/swagger`
- **Testtäckning** — Utökad enhetstesttäckning för parsers och `receipt-parsing.service.ts` (18 testfall för kvittoimport) - **Testtäckning** — Utökad enhetstesttäckning för parsers och `receipt-parsing.service.ts`
### Låg prioritet / Framtida ### Låg prioritet / Framtida
- **Caching** — Cacha skrapade sidor för att minska belastning på externa webbplatser - **Caching** — Cacha skrapade sidor för att minska belastning på externa webbplatser
@@ -62,77 +62,52 @@ cd /opt/containers/recipe-app && git pull && ./deploy.sh
## AI-optimering: Mistral-modell och pipeline ## AI-optimering: Mistral-modell och pipeline
**Nuläge:** `mistral-small-2603` används för bildinput (vision). För PDF-flödet extraheras text först via `pdf-parse`/`pdfjs-dist`; sedan regelbaserad parsning; sedan AI enbart för återstående rader. Nuläget är att `mistral-small-2603` används för bildinput (vision). För PDF-flödet extraheras text först via `pdf-parse` eller `pdfjs-dist`; därefter körs regelbaserad parsning och AI bara för kvarvarande rader.
### Implementerad optimering: AI sist i pipeline (PDF) ### Implementerad optimering: AI sist i pipeline (PDF)
Kvittopipelinen för PDF ser nu ut: Kvittopipelinen för PDF ser nu ut:
``` ```text
PDF → pdf-parse / pdfjs-dist → preprocessPdfLines → isIgnoredReceiptLine → ruleBasedParseLine → looksLikeReceiptProductLine → AI PDF → pdf-parse / pdfjs-dist → preprocessPdfLines → isIgnoredReceiptLine → ruleBasedParseLine → looksLikeReceiptProductLine → AI
``` ```
**`looksLikeReceiptProductLine(line)`** filtrerar bort rader som saknar siffra (butiksnamn, datum, välkomsttext m.m.) innan Mistral-anropet. Enbart rader med namnliknande text OCH minst ett tal skickas till AI. `looksLikeReceiptProductLine(line)` filtrerar bort rader som saknar siffra innan Mistral-anropet. Enbart rader med namnliknande text och minst ett tal skickas till AI.
**Fördelar:**
- Regelbaserad parsning hanterar standardfall gratis och snabbt (t.ex. "MJÖLK 1,5L", "BLANDFÄRS 997G")
- AI anropas bara för rader som regelverket inte kan tolka entydigt
- Möjlighet att använda en mindre/billigare modell för enklare tolkningsuppgifter
### Modellval för olika deluppgifter ### Modellval för olika deluppgifter
| Uppgift | Rekommenderad modell | Motivering | | Uppgift | Rekommenderad modell | Motivering |
|---|---|---| |---|---|---|
| Kvittoparsning (hela bilden, nuläge) | `mistral-small-2603` | Vision-förmåga krävs för bild-input | | Kvittoparsning (hela bilden) | `mistral-small-2603` | Vision-förmåga krävs för bild-input |
| Tolka OCR-text (textbaserad input) | `mistral-small-latest` eller mindre | Enklare uppgift när text redan extraherats | | Tolka OCR-text | `mistral-small-latest` eller mindre | Enklare uppgift när text redan extraherats |
| Kategorisering av enskild produktrad | `open-mistral-nemo` (7B) | Klassificering, ej vision — kan vara mycket liten | | Kategorisering av enskild produktrad | `open-mistral-nemo` (7B) | Klassificering utan vision |
**OBS:** För bild-input (JPEG/PNG/HEIC/WebP) krävs alltid en vision-kapabel modell. Optimering med mindre modell är bara möjlig när Tesseract/pdf-parse redan har extraherat text. För bild-input krävs alltid en vision-kapabel modell.
--- ---
## Framtida förbättringar ## Framtida förbättringar
### Schemalagd Uppdatering av Kategorier - Schemalagd uppdatering av kategorier om det visar sig nödvändigt igen.
- **Mål:** Implementera en schemalagd uppdatering av kategorierna en gång i veckan för att säkerställa att cachen alltid är uppdaterad.
- **Metod:** Använda `cron` för att schemalägga ett anrop till `POST /receipt-import/refresh-categories` en gång i veckan.
--- ## Nuvarande implementering
## Nuvarande Implementering - Ingen separat manuell kategoriuppdatering via Flutter-UI finns längre.
- Kvittoparsningen använder i stället nuvarande regler, retry-logik och filtrering i backend.
### Manuell Uppdatering av Kategorier
- **Mål:** Låta användaren manuellt uppdatera kategorierna via Flutter-UI.
- **Implementering:**
- En knapp i Flutter-UI:n som låter användaren trigga uppdateringen.
- Anropa `POST /receipt-import/refresh-categories` från Flutter-UI:n när användaren klickar på knappen.
```dart
// Exempel på hur du kan anropa endpointen från Flutter
Future<void> refreshCategories() async {
final response = await http.post(
Uri.parse('http://YOUR_API_URL/receipt-import/refresh-categories'),
headers: {'Authorization': 'Bearer YOUR_JWT_TOKEN'},
);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Kategorier har uppdaterats.')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Misslyckades med att uppdatera kategorier.')),
);
}
}
```
--- ---
## Arkitektur-noteringar ## Arkitektur-noteringar
- Tjänsten är **helt stateless** — ingen databas, ingen session - Tjänsten är helt stateless och saknar databas
- Kommunicerar **aldrig direkt** med internet-klienter — exponeras bara på `recipe-internal`-nätverket - Den exponeras bara på `recipe-internal`-nätverket
- `MISTRAL_API_KEY` injiceras via env (samma nyckel som `recipe-api` använder) - `MISTRAL_API_KEY` injiceras via env
- Alpine Docker-image: systempaket `tesseract-ocr`, `tesseract-ocr-data-swe`, `tesseract-ocr-data-eng` installerade via `apk` - Alpine Docker-image använder `tesseract-ocr`, `tesseract-ocr-data-swe`, `tesseract-ocr-data-eng`
- Host-port 3001 är upptagen av `wetty` på servern — `importer-api` exponeras aldrig till host - Host-port 3001 är upptagen av `wetty`, så `importer-api` exponeras aldrig till host
## Referenser
- [README.md](README.md)
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md)
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.