Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 25 additions & 30 deletions backend/routers/voice.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
IssueCategory
)
from backend.voice_service import get_voice_service
from backend.utils import generate_reference_id
from backend.utils import generate_reference_id, save_issue_db

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -255,37 +255,24 @@ def _save_audio_file():
with open(audio_file_path, 'wb') as f:
f.write(audio_content)

def _delete_audio_file_best_effort():
try:
if os.path.exists(audio_file_path):
os.remove(audio_file_path)
except Exception as cleanup_error:
logger.warning("Failed to delete orphaned audio file %s: %s", audio_file_path, cleanup_error)

await run_in_threadpool(_save_audio_file)

try:
# Store relative path for portability
relative_audio_path = os.path.join("data", "audio_recordings", audio_filename)

# Blockchain feature: calculate integrity hash for the report
# Performance Boost: Use thread-safe cache to eliminate DB query for last hash
prev_hash = blockchain_last_hash_cache.get("last_hash")
if prev_hash is None:
# Cache miss: Fetch only the last hash from DB
prev_issue = await run_in_threadpool(
lambda: db.query(Issue.integrity_hash).order_by(Issue.id.desc()).first()
)
prev_hash = prev_issue[0] if prev_issue and prev_issue[0] else ""
blockchain_last_hash_cache.set(data=prev_hash, key="last_hash")
# Store relative path for portability
relative_audio_path = os.path.join("data", "audio_recordings", audio_filename)

# Blockchain feature: calculate integrity hash for the report
# Performance Boost: Use thread-safe cache to eliminate DB query for last hash
prev_hash = blockchain_last_hash_cache.get("last_hash")
if prev_hash is None:
# Cache miss: Fetch only the last hash from DB
# Use await run_in_threadpool for DB query if needed, or just do it in-thread
prev_issue = db.query(Issue.integrity_hash).order_by(Issue.id.desc()).first()
prev_hash = prev_issue[0] if prev_issue and prev_issue[0] else ""
blockchain_last_hash_cache.set(data=prev_hash, key="last_hash")

# Simple but effective SHA-256 chaining
# Format must match backend/routers/issues.py for a consistent chain
hash_content = f"{final_description}|{issue_category.value}|{prev_hash}"
except Exception:
db.rollback()
await run_in_threadpool(_delete_audio_file_best_effort)
raise
# Simple but effective SHA-256 chaining
# Format must match backend/routers/issues.py for a consistent chain
hash_content = f"{final_description}|{issue_category.value}|{prev_hash}"
integrity_hash = hashlib.sha256(hash_content.encode()).hexdigest()

# Create issue in database
Expand All @@ -301,6 +288,7 @@ def _delete_audio_file_best_effort():
location=location,
source='voice',
status='open',
# Blockchain integrity fields
integrity_hash=integrity_hash,
previous_integrity_hash=prev_hash,
# Voice-specific fields
Expand All @@ -312,10 +300,11 @@ def _delete_audio_file_best_effort():
audio_file_path=relative_audio_path # Store relative path
)

# Standard synchronous DB operations for simplicity and thread-safety
db.add(new_issue)
db.commit()
db.refresh(new_issue)

# Update cache for next report AFTER successful DB commit
blockchain_last_hash_cache.set(data=integrity_hash, key="last_hash")

Expand All @@ -331,6 +320,12 @@ def _delete_audio_file_best_effort():
raise
except Exception as e:
logger.error(f"Error submitting voice issue: {e}", exc_info=True)
# Clean up audio file if database transaction fails
if 'audio_file_path' in locals() and os.path.exists(audio_file_path):
try:
os.remove(audio_file_path)
except Exception as cleanup_error:
logger.warning(f"Failed to cleanup audio file: {cleanup_error}")
raise HTTPException(status_code=500, detail=f"Failed to submit voice issue: {str(e)}")


Expand Down
Loading