- Mise à jour du README.md avec les nouvelles fonctionnalités - Documentation API mise à jour avec les intégrations externes - Guide d'installation avec bootstrap automatisé - Architecture mise à jour avec Celery et intégrations - CHANGELOG détaillé avec toutes les nouvelles fonctionnalités - Nouvelle documentation des fonctionnalités v1.2.0 Nouvelles sections documentées: - Pipeline de traitement asynchrone avec Celery - Intégrations avec APIs externes (Cadastre, Géorisques, BODACC, etc.) - Clients d'intégration (AnythingLLM, Neo4j, OpenSearch) - Configuration d'environnement centralisée - Script bootstrap automatisé - Monitoring et observabilité - Exemples d'utilisation et API
226 lines
8.3 KiB
Python
226 lines
8.3 KiB
Python
"""
|
|
Tâches de vérification et contrôle qualité
|
|
"""
|
|
import logging
|
|
from typing import Dict, Any
|
|
from services.worker.celery_app import app
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@app.task(bind=True, name='verification.verify_document')
|
|
def verify_document(self, doc_id: str, entities: Dict[str, Any], doc_type: str, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Vérification et contrôle qualité d'un document
|
|
|
|
Args:
|
|
doc_id: Identifiant du document
|
|
entities: Entités extraites
|
|
doc_type: Type de document
|
|
context: Contexte de traitement
|
|
|
|
Returns:
|
|
Résultat des vérifications
|
|
"""
|
|
try:
|
|
logger.info(f"Début des vérifications pour le document {doc_id}")
|
|
|
|
# Mise à jour du statut
|
|
self.update_state(
|
|
state='PROGRESS',
|
|
meta={'current_step': 'verification', 'progress': 0}
|
|
)
|
|
|
|
# Vérifications externes avec les APIs
|
|
verification_results = {}
|
|
|
|
# 1. Vérification des adresses via Cadastre
|
|
if 'bien' in entities and 'adresse' in entities['bien']:
|
|
try:
|
|
from services.worker.utils.external_apis import ExternalAPIManager
|
|
api_manager = ExternalAPIManager()
|
|
address_result = await api_manager.verify_address(
|
|
entities['bien']['adresse'],
|
|
entities['bien'].get('code_postal'),
|
|
entities['bien'].get('ville')
|
|
)
|
|
verification_results['cadastre'] = address_result
|
|
except Exception as e:
|
|
logger.error(f"Erreur vérification Cadastre: {e}")
|
|
verification_results['cadastre'] = {'status': 'error', 'error': str(e)}
|
|
|
|
# 2. Vérification des risques géologiques
|
|
if 'bien' in entities and 'adresse' in entities['bien']:
|
|
try:
|
|
from services.worker.utils.external_apis import ExternalAPIManager
|
|
api_manager = ExternalAPIManager()
|
|
risks_result = await api_manager.check_geological_risks(
|
|
entities['bien']['adresse']
|
|
)
|
|
verification_results['georisques'] = risks_result
|
|
except Exception as e:
|
|
logger.error(f"Erreur vérification Géorisques: {e}")
|
|
verification_results['georisques'] = {'status': 'error', 'error': str(e)}
|
|
|
|
# 3. Vérification des entreprises (si applicable)
|
|
if 'vendeur' in entities and 'nom' in entities['vendeur']:
|
|
try:
|
|
from services.worker.utils.external_apis import ExternalAPIManager
|
|
api_manager = ExternalAPIManager()
|
|
company_result = await api_manager.verify_company(
|
|
entities['vendeur']['nom']
|
|
)
|
|
verification_results['bodacc'] = company_result
|
|
except Exception as e:
|
|
logger.error(f"Erreur vérification BODACC: {e}")
|
|
verification_results['bodacc'] = {'status': 'error', 'error': str(e)}
|
|
|
|
# 4. Vérification des personnes
|
|
if 'vendeur' in entities or 'acheteur' in entities:
|
|
try:
|
|
from services.worker.utils.external_apis import ExternalAPIManager
|
|
api_manager = ExternalAPIManager()
|
|
|
|
# Vérification du vendeur
|
|
if 'vendeur' in entities:
|
|
person_result = await api_manager.verify_person(
|
|
entities['vendeur'].get('prenom', ''),
|
|
entities['vendeur'].get('nom', ''),
|
|
entities['vendeur'].get('date_naissance')
|
|
)
|
|
verification_results['person_vendeur'] = person_result
|
|
|
|
# Vérification de l'acheteur
|
|
if 'acheteur' in entities:
|
|
person_result = await api_manager.verify_person(
|
|
entities['acheteur'].get('prenom', ''),
|
|
entities['acheteur'].get('nom', ''),
|
|
entities['acheteur'].get('date_naissance')
|
|
)
|
|
verification_results['person_acheteur'] = person_result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Erreur vérification personnes: {e}")
|
|
verification_results['person_verification'] = {'status': 'error', 'error': str(e)}
|
|
|
|
import time
|
|
time.sleep(2) # Simulation du traitement
|
|
|
|
# Calcul du score de vraisemblance basé sur les vérifications
|
|
credibility_score = _calculate_credibility_score(verification_results)
|
|
|
|
result = {
|
|
'doc_id': doc_id,
|
|
'status': 'completed',
|
|
'verifications': verification_results,
|
|
'credibility_score': credibility_score,
|
|
'manual_review_required': credibility_score < 0.75,
|
|
'processing_time': 2.0
|
|
}
|
|
|
|
logger.info(f"Vérifications terminées pour le document {doc_id} (score: {credibility_score})")
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors des vérifications du document {doc_id}: {e}")
|
|
raise
|
|
|
|
@app.task(name='verification.batch_verify')
|
|
def batch_verify_documents(doc_ids: list, entities_list: list, doc_types: list) -> Dict[str, Any]:
|
|
"""
|
|
Vérification en lot de documents
|
|
|
|
Args:
|
|
doc_ids: Liste des identifiants de documents
|
|
entities_list: Liste des entités correspondantes
|
|
doc_types: Liste des types de documents correspondants
|
|
|
|
Returns:
|
|
Résultats des vérifications en lot
|
|
"""
|
|
if len(doc_ids) != len(entities_list) or len(doc_ids) != len(doc_types):
|
|
raise ValueError("Le nombre de documents, entités et types doit être identique")
|
|
|
|
logger.info(f"Vérification en lot de {len(doc_ids)} documents")
|
|
|
|
results = []
|
|
for doc_id, entities, doc_type in zip(doc_ids, entities_list, doc_types):
|
|
try:
|
|
result = verify_document.delay(doc_id, entities, doc_type, {}).get()
|
|
results.append(result)
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors de la vérification en lot pour {doc_id}: {e}")
|
|
results.append({
|
|
'doc_id': doc_id,
|
|
'status': 'failed',
|
|
'error': str(e)
|
|
})
|
|
|
|
return {
|
|
'batch_status': 'completed',
|
|
'total_documents': len(doc_ids),
|
|
'results': results
|
|
}
|
|
|
|
@app.task(name='verification.update_external_data')
|
|
def update_external_data():
|
|
"""
|
|
Mise à jour des données externes (APIs gouvernementales)
|
|
"""
|
|
logger.info("Mise à jour des données externes")
|
|
|
|
# TODO: Implémenter la mise à jour des données externes
|
|
# - Synchronisation avec les APIs gouvernementales
|
|
# - Mise à jour des caches
|
|
# - Actualisation des référentiels
|
|
|
|
return {
|
|
'status': 'completed',
|
|
'updated_sources': ['cadastre', 'georisques', 'bodacc'],
|
|
'timestamp': '2025-01-09T10:00:00Z'
|
|
}
|
|
|
|
def _calculate_credibility_score(verification_results: Dict[str, Any]) -> float:
|
|
"""
|
|
Calcul du score de vraisemblance basé sur les vérifications
|
|
|
|
Args:
|
|
verification_results: Résultats des vérifications
|
|
|
|
Returns:
|
|
Score de vraisemblance entre 0 et 1
|
|
"""
|
|
total_score = 0.0
|
|
total_weight = 0.0
|
|
|
|
# Poids des différentes vérifications
|
|
weights = {
|
|
'cadastre': 0.3,
|
|
'georisques': 0.2,
|
|
'bodacc': 0.2,
|
|
'person_vendeur': 0.15,
|
|
'person_acheteur': 0.15
|
|
}
|
|
|
|
for verification_type, result in verification_results.items():
|
|
if verification_type in weights:
|
|
weight = weights[verification_type]
|
|
total_weight += weight
|
|
|
|
if result.get('status') == 'verified':
|
|
confidence = result.get('confidence', 0.8)
|
|
total_score += confidence * weight
|
|
elif result.get('status') == 'not_found':
|
|
# Pas trouvé n'est pas forcément négatif
|
|
total_score += 0.5 * weight
|
|
elif result.get('status') == 'error':
|
|
# Erreur réduit le score
|
|
total_score += 0.2 * weight
|
|
|
|
# Normalisation du score
|
|
if total_weight > 0:
|
|
final_score = total_score / total_weight
|
|
else:
|
|
final_score = 0.5 # Score par défaut si aucune vérification
|
|
|
|
return min(max(final_score, 0.0), 1.0)
|