ocr extract
This commit is contained in:
@@ -1,41 +1,9 @@
|
||||
# Claude Learn: Backend
|
||||
# Claude Learn: backend
|
||||
<!-- paths: backend/**/*.py, backend/modules/**/*, requirements.txt -->
|
||||
|
||||
**Domain**: backend
|
||||
**Last updated**: 2026-01-06
|
||||
**Sessions recorded**: 1
|
||||
|
||||
Knowledge about FastAPI, Python services, Oracle DB, and backend architecture.
|
||||
|
||||
---
|
||||
|
||||
## Patterns
|
||||
|
||||
### ProfileRegistry cu Hot-Reload pentru Store Profiles
|
||||
**Discovered**: 2026-01-06 (feature: ocr-store-profiles)
|
||||
**Description**: Sistem de înregistrare profile OCR folosind decorator `@ProfileRegistry.register` cu hot-reload via `importlib.reload()`. Permite adăugarea/modificarea profilelor fără restart server.
|
||||
|
||||
**Example** (`backend/modules/data_entry/services/ocr/profiles/__init__.py`):
|
||||
```python
|
||||
class ProfileRegistry:
|
||||
_profiles: Dict[str, Type["BaseStoreProfile"]] = {}
|
||||
_instances: Dict[str, "BaseStoreProfile"] = {}
|
||||
|
||||
@classmethod
|
||||
def register(cls, profile_class):
|
||||
"""Decorator to register a store profile class."""
|
||||
for cui in profile_class.CUI_LIST:
|
||||
cls._profiles[cls._normalize_cui(cui)] = profile_class
|
||||
return profile_class
|
||||
|
||||
@classmethod
|
||||
def reload_all(cls):
|
||||
"""Hot-reload all profile modules via importlib.reload()."""
|
||||
cls._instances.clear()
|
||||
for module_name in cls._get_profile_module_names():
|
||||
importlib.reload(sys.modules[f"backend...profiles.{module_name}"])
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
## P: ProfileRegistry cu Hot-Reload pentru Store Profiles
|
||||
@2026-01-06 #registry-pattern #hot-reload #decorator | inferred:med
|
||||
Sistem de înregistrare profile OCR folosind decorator `@ProfileRegistry.register` cu hot-reload via `importlib.reload()`. Permite adăugarea/modificarea profilelor fără restart server.
|
||||
```python
|
||||
@ProfileRegistry.register
|
||||
class LidlProfile(BaseStoreProfile):
|
||||
@@ -45,36 +13,12 @@ class LidlProfile(BaseStoreProfile):
|
||||
# Lookup
|
||||
profile = ProfileRegistry.get_profile("22891860")
|
||||
|
||||
# Hot-reload (endpoint)
|
||||
POST /api/data-entry/ocr/profiles/reload
|
||||
# Hot-reload endpoint: POST /api/data-entry/ocr/profiles/reload
|
||||
```
|
||||
|
||||
**Tags**: registry-pattern, hot-reload, decorator, ocr, singleton
|
||||
|
||||
---
|
||||
|
||||
### Script generare cod Python din analiză PDF
|
||||
**Discovered**: 2026-01-06 (feature: ocr-store-profiles)
|
||||
**Description**: Script care analizează PDF-uri via OCR API, detectează pattern-uri (TVA format, date format, payment) și generează automat cod Python pentru profile noi. Include JWT auth, async polling, și verificare sintaxă.
|
||||
|
||||
**Example** (`scripts/generate_store_profile.py`):
|
||||
```python
|
||||
def analyze_tva_patterns(results: List[Dict]) -> Dict:
|
||||
"""Detectează format TVA dominant din rezultatele OCR."""
|
||||
tva_formats = defaultdict(int)
|
||||
for text in raw_texts:
|
||||
if re.search(r'TVA\s+[A-D]\s+\d{1,2}', text_upper):
|
||||
tva_formats["lidl_multi_rate"] += 1
|
||||
if re.search(r'BAZA\s+TVA', text_upper):
|
||||
tva_formats["table"] += 1
|
||||
return {"dominant_format": max(tva_formats, key=tva_formats.get)}
|
||||
|
||||
def generate_profile_code(store_name, cui, tva_analysis, ...):
|
||||
"""Generează cod Python pentru clasa de profil."""
|
||||
# Template-based generation cu OCR error variants
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
## P: Script generare cod Python din analiză PDF
|
||||
@2026-01-06 #code-generation #ocr #automation | inferred:med
|
||||
Script care analizează PDF-uri via OCR API, detectează pattern-uri (TVA format, date format, payment) și generează automat cod Python pentru profile noi. Include JWT auth, async polling, și verificare sintaxă.
|
||||
```bash
|
||||
# Dry-run pentru preview
|
||||
python scripts/generate_store_profile.py \
|
||||
@@ -88,19 +32,19 @@ python scripts/generate_store_profile.py \
|
||||
--output backend/.../profiles/magazin_nou.py
|
||||
```
|
||||
|
||||
**Tags**: code-generation, ocr, automation, cli-tool
|
||||
## G: OCR Worker Pool rulează în procese separate - restart complet necesar
|
||||
@2026-01-07 #ocr #worker-pool #hot-reload | inferred:med
|
||||
**P**: Modificările în fișierele de profile OCR nu sunt încărcate automat de uvicorn --reload pentru că OCR worker pool rulează în procese separate (multiprocessing).
|
||||
**S**: Trebuie restart COMPLET al backend-ului cu `./start-backend.sh restart`. Hot-reload-ul uvicorn nu reîncarcă procesele worker separate.
|
||||
|
||||
---
|
||||
## G: TOTAL_PATTERNS în profile OCR trebuie să aibă grupuri de captură
|
||||
@2026-01-07 #ocr #regex #profiles | inferred:high
|
||||
**P**: Pattern-uri regex în `TOTAL_PATTERNS` fără grupuri `()` cauzează `IndexError: no such group` când `super().extract_total()` încearcă `match.group(1)`.
|
||||
**S**: TOTAL_PATTERNS din subclase TREBUIE să includă grupuri de captură pentru compatibilitate cu `BaseStoreProfile.extract_total()`:
|
||||
```python
|
||||
# GREȘIT - fără grup de captură
|
||||
TOTAL_PATTERNS = [(r'SUMA\s+TOTALA\s*:', 0.98)]
|
||||
|
||||
## Gotchas
|
||||
|
||||
_(None recorded yet)_
|
||||
|
||||
---
|
||||
|
||||
## Statistics
|
||||
|
||||
- **Total Patterns**: 2
|
||||
- **Total Gotchas**: 0
|
||||
- **Last Session**: 2026-01-06
|
||||
- **Sessions Recorded**: 1
|
||||
# CORECT - cu grup de captură
|
||||
TOTAL_PATTERNS = [(r'SUMA\s+TOTALA\s*:\s*([\d\s.,]+)', 0.98)]
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user