patrykslomka Claude Opus 4.6 commited on
Commit
9028070
·
1 Parent(s): 4a1a4b0

fix: resolve circular import, analysis API, locale mixing, and UX bugs

Browse files

- Extract limiter to extensions.py to break circular import between app.py and blueprints
- Fix generate_study_explanation calling .get() on raw Response (needs .json() first)
- Pass locale parameter through /analyze and learning quiz fallback
- Change /uploads/<filename> from require_auth to optional_auth for <img> tags
- Prevent learn card collapse when selecting text or clicking inside expanded content

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

app.py CHANGED
@@ -4,14 +4,7 @@ import logging
4
  import torch
5
  from flask import Flask, jsonify
6
  from flask_cors import CORS
7
- from flask_limiter import Limiter
8
- from flask_limiter.util import get_remote_address
9
-
10
- limiter = Limiter(
11
- get_remote_address,
12
- default_limits=["60 per minute"],
13
- storage_uri="memory://",
14
- )
15
  from dotenv import load_dotenv
16
  from src.dental_ontology import DentalOntology
17
  from src.claude_integration import ClaudeAssistant
 
4
  import torch
5
  from flask import Flask, jsonify
6
  from flask_cors import CORS
7
+ from extensions import limiter
 
 
 
 
 
 
 
8
  from dotenv import load_dotenv
9
  from src.dental_ontology import DentalOntology
10
  from src.claude_integration import ClaudeAssistant
blueprints/analysis.py CHANGED
@@ -17,8 +17,8 @@ from flask import (
17
  from werkzeug.utils import secure_filename
18
  from PIL import Image, ImageDraw, ImageFont
19
 
20
- from app import limiter
21
- from src.auth import require_auth
22
  from src.analysis_helpers import allowed_file, ALLOWED_EXTENSIONS, normalize_image_orientation
23
  from src.analysis_service import _run_analysis
24
  from src.image_processing import (
@@ -65,7 +65,7 @@ def index():
65
  # ---------------------------------------------------------------------------
66
 
67
  @analysis_bp.route('/uploads/<filename>')
68
- @require_auth
69
  def uploaded_file(filename):
70
  filename = secure_filename(filename)
71
  if not filename:
@@ -100,7 +100,8 @@ def analyze():
100
  normalize_image_orientation(file_path)
101
 
102
  try:
103
- result = _run_analysis(file_path, filename, False)
 
104
  _save_analysis_to_firestore(
105
  current_app.extensions.get('firestore'),
106
  g.user['clerk_id'], filename, result,
 
17
  from werkzeug.utils import secure_filename
18
  from PIL import Image, ImageDraw, ImageFont
19
 
20
+ from extensions import limiter
21
+ from src.auth import require_auth, optional_auth
22
  from src.analysis_helpers import allowed_file, ALLOWED_EXTENSIONS, normalize_image_orientation
23
  from src.analysis_service import _run_analysis
24
  from src.image_processing import (
 
65
  # ---------------------------------------------------------------------------
66
 
67
  @analysis_bp.route('/uploads/<filename>')
68
+ @optional_auth
69
  def uploaded_file(filename):
70
  filename = secure_filename(filename)
71
  if not filename:
 
100
  normalize_image_orientation(file_path)
101
 
102
  try:
103
+ locale = request.form.get('locale', 'en')
104
+ result = _run_analysis(file_path, filename, False, locale=locale)
105
  _save_analysis_to_firestore(
106
  current_app.extensions.get('firestore'),
107
  g.user['clerk_id'], filename, result,
blueprints/learning.py CHANGED
@@ -6,7 +6,7 @@ import random
6
  from flask import Blueprint, jsonify, request, current_app, g, send_from_directory, abort
7
  from werkzeug.utils import secure_filename
8
 
9
- from app import limiter
10
  from src.auth import optional_auth
11
 
12
  learning_bp = Blueprint('learning', __name__, url_prefix='/learn')
@@ -137,7 +137,7 @@ def check_quiz_answer():
137
  logger.info('Quiz check: no cached predictions, running analysis for %s', filename)
138
  try:
139
  from src.analysis_service import _run_analysis
140
- result = _run_analysis(image_path, filename, use_vision=False)
141
  predictions = result.get('predictions', [])
142
  # Cache the predictions for future quiz use
143
  with open(predictions_path, 'w') as f:
 
6
  from flask import Blueprint, jsonify, request, current_app, g, send_from_directory, abort
7
  from werkzeug.utils import secure_filename
8
 
9
+ from extensions import limiter
10
  from src.auth import optional_auth
11
 
12
  learning_bp = Blueprint('learning', __name__, url_prefix='/learn')
 
137
  logger.info('Quiz check: no cached predictions, running analysis for %s', filename)
138
  try:
139
  from src.analysis_service import _run_analysis
140
+ result = _run_analysis(image_path, filename, use_vision=False, locale=locale)
141
  predictions = result.get('predictions', [])
142
  # Cache the predictions for future quiz use
143
  with open(predictions_path, 'w') as f:
extensions.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from flask_limiter import Limiter
2
+ from flask_limiter.util import get_remote_address
3
+
4
+ limiter = Limiter(
5
+ get_remote_address,
6
+ default_limits=["60 per minute"],
7
+ storage_uri="memory://",
8
+ )
src/claude_integration.py CHANGED
@@ -466,7 +466,11 @@ Return ONLY the JSON object, no additional text."""
466
  "system": system_msg,
467
  "messages": [{"role": "user", "content": user_msg}],
468
  }
469
- result = self._api_request(data, timeout=30)
 
 
 
 
470
  content = result.get("content", [])
471
  if not content:
472
  return None
 
466
  "system": system_msg,
467
  "messages": [{"role": "user", "content": user_msg}],
468
  }
469
+ response = self._api_request(data, timeout=30)
470
+ if response.status_code != 200:
471
+ logger.error("Study explanation API error: %s %s", response.status_code, response.text[:200])
472
+ return None
473
+ result = response.json()
474
  content = result.get("content", [])
475
  if not content:
476
  return None
static/js/learning.js CHANGED
@@ -364,7 +364,12 @@ export async function initLearnBrowser() {
364
  <p class="learn-card-preview">${cond.definition?.substring(0, 100) || ''}...</p>
365
  `;
366
 
367
- card.addEventListener('click', () => expandLearnCard(card, cond.key, locale));
 
 
 
 
 
368
  grid.appendChild(card);
369
  }
370
 
 
364
  <p class="learn-card-preview">${cond.definition?.substring(0, 100) || ''}...</p>
365
  `;
366
 
367
+ card.addEventListener('click', (e) => {
368
+ // Don't toggle if user is selecting text or clicked inside expanded content
369
+ if (window.getSelection().toString()) return;
370
+ if (card.classList.contains('expanded') && e.target.closest('.learn-card-content')) return;
371
+ expandLearnCard(card, cond.key, locale);
372
+ });
373
  grid.appendChild(card);
374
  }
375