t commited on
Commit
daae428
·
1 Parent(s): ef01286

fix: resolve NameError in processing.py and restore missing question routes

Browse files

- Fixed NameError 'p_info' in process_crop_v2 by using correct variable name.
- Restored '/get_topic_suggestions' and '/classified/update_single' routes in questions.py that were lost during modularization.

Files changed (2) hide show
  1. routes/processing.py +1 -1
  2. routes/questions.py +67 -0
routes/processing.py CHANGED
@@ -100,7 +100,7 @@ def process_crop_v2():
100
  max_idx = conn.execute('SELECT MAX(image_index) FROM images WHERE session_id = ?', (session_id,)).fetchone()[0]
101
  next_idx = (max_idx if max_idx is not None else -1) + 1
102
  for i, p_box in enumerate(processed_boxes):
103
- conn.execute('INSERT INTO images (session_id, image_index, filename, original_name, processed_filename, image_type, box_id) VALUES (?, ?, ?, ?, ?, ?, ?)', (session_id, next_idx + i, p_info['filename'], p_box['original_filename'], p_box['processed_filename'], 'cropped', p_box['box_id']))
104
  img_id = conn.execute('SELECT last_insert_rowid()').fetchone()[0]
105
  conn.execute("INSERT INTO questions (session_id, image_id, question_number, status, marked_solution, actual_solution) VALUES (?, ?, ?, ?, ?, ?)", (session_id, img_id, p_box.get('question_number'), p_box.get('status', 'unattempted'), p_box.get('marked_solution'), p_box.get('actual_solution')))
106
  conn.commit(); os.remove(temp_path); return jsonify({'success': True, 'processed_count': len(processed_boxes)})
 
100
  max_idx = conn.execute('SELECT MAX(image_index) FROM images WHERE session_id = ?', (session_id,)).fetchone()[0]
101
  next_idx = (max_idx if max_idx is not None else -1) + 1
102
  for i, p_box in enumerate(processed_boxes):
103
+ conn.execute('INSERT INTO images (session_id, image_index, filename, original_name, processed_filename, image_type, box_id) VALUES (?, ?, ?, ?, ?, ?, ?)', (session_id, next_idx + i, p_box['original_filename'], f"Page {page_index + 1} - Q{i + 1}", p_box['processed_filename'], 'cropped', p_box['box_id']))
104
  img_id = conn.execute('SELECT last_insert_rowid()').fetchone()[0]
105
  conn.execute("INSERT INTO questions (session_id, image_id, question_number, status, marked_solution, actual_solution) VALUES (?, ?, ?, ?, ?, ?)", (session_id, img_id, p_box.get('question_number'), p_box.get('status', 'unattempted'), p_box.get('marked_solution'), p_box.get('actual_solution')))
106
  conn.commit(); os.remove(temp_path); return jsonify({'success': True, 'processed_count': len(processed_boxes)})
routes/questions.py CHANGED
@@ -3,10 +3,77 @@ from flask import current_app, request, jsonify, render_template, flash, redirec
3
  from .common import main_bp, get_db_connection, login_required, current_user
4
  from processing import resize_image_if_needed, call_nim_ocr_api, extract_question_number_from_ocr_result
5
  from strings import ROUTE_SAVE_QUESTIONS, ROUTE_EXTRACT_QUESTION_NUMBER, ROUTE_EXTRACT_ALL_QUESTION_NUMBERS, METHOD_POST
 
 
 
6
 
7
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
8
  NVIDIA_NIM_AVAILABLE = bool(NVIDIA_API_KEY)
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  @main_bp.route('/question_entry_v2/<session_id>')
11
  @login_required
12
  def question_entry_v2(session_id):
 
3
  from .common import main_bp, get_db_connection, login_required, current_user
4
  from processing import resize_image_if_needed, call_nim_ocr_api, extract_question_number_from_ocr_result
5
  from strings import ROUTE_SAVE_QUESTIONS, ROUTE_EXTRACT_QUESTION_NUMBER, ROUTE_EXTRACT_ALL_QUESTION_NUMBERS, METHOD_POST
6
+ import requests
7
+ import json
8
+ from nvidia_prompts import BIOLOGY_PROMPT_TEMPLATE, CHEMISTRY_PROMPT_TEMPLATE, PHYSICS_PROMPT_TEMPLATE, MATHEMATICS_PROMPT_TEMPLATE
9
 
10
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
11
  NVIDIA_NIM_AVAILABLE = bool(NVIDIA_API_KEY)
12
 
13
+ def get_nvidia_prompt(subject, input_questions):
14
+ if subject.lower() == 'biology': return BIOLOGY_PROMPT_TEMPLATE.format(input_questions=input_questions)
15
+ if subject.lower() == 'chemistry': return CHEMISTRY_PROMPT_TEMPLATE.format(input_questions=input_questions)
16
+ if subject.lower() == 'physics': return PHYSICS_PROMPT_TEMPLATE.format(input_questions=input_questions)
17
+ if subject.lower() == 'mathematics': return MATHEMATICS_PROMPT_TEMPLATE.format(input_questions=input_questions)
18
+ return None
19
+
20
+ @main_bp.route('/get_topic_suggestions', methods=['POST'])
21
+ @login_required
22
+ def get_topic_suggestions():
23
+ data = request.json
24
+ question_text, image_id, subject = data.get('question_text'), data.get('image_id'), data.get('subject')
25
+ if not subject: return jsonify({'error': 'Subject is required'}), 400
26
+ if not question_text and image_id:
27
+ try:
28
+ conn = get_db_connection()
29
+ row = conn.execute('SELECT question_text, processed_filename, i.session_id FROM questions q JOIN images i ON q.image_id = i.id WHERE i.id = ?', (image_id,)).fetchone()
30
+ if row:
31
+ if row['question_text']: question_text = row['question_text']
32
+ else:
33
+ image_path = os.path.join(current_app.config['PROCESSED_FOLDER'], row['processed_filename'])
34
+ if os.path.exists(image_path):
35
+ question_text = " ".join(item['text_prediction']['text'] for item in call_nim_ocr_api(resize_image_if_needed(image_path))['data'][0]['text_detections'])
36
+ conn.execute('UPDATE questions SET question_text = ? WHERE image_id = ?', (question_text, image_id))
37
+ conn.commit()
38
+ conn.close()
39
+ except Exception as e: return jsonify({'error': f"OCR failed: {str(e)}"}), 500
40
+ if not question_text: return jsonify({'error': 'Could not obtain question text.'}), 400
41
+ prompt_content = get_nvidia_prompt(subject, f"1. {question_text}")
42
+ if not prompt_content: return jsonify({'error': f'Unsupported subject: {subject}'}), 400
43
+ if not NVIDIA_API_KEY: return jsonify({'error': 'NVIDIA_API_KEY not set'}), 500
44
+ try:
45
+ res = requests.post('https://integrate.api.nvidia.com/v1/chat/completions', headers={'Authorization': f'Bearer {NVIDIA_API_KEY}', 'Accept': 'application/json', 'Content-Type': 'application/json'}, json={"model": "nvidia/nemotron-3-nano-30b-a3b", "messages": [{"content": prompt_content, "role": "user"}], "temperature": 0.2, "top_p": 1, "max_tokens": 1024, "stream": False}, timeout=30)
46
+ res.raise_for_status()
47
+ content = res.json()['choices'][0]['message']['content']
48
+ if "```json" in content: content = content.split("```json")[1].split("```")[0].strip()
49
+ elif "```" in content: content = content.split("```")[1].split("```")[0].strip()
50
+ data = json.loads(content)
51
+ suggestions = []
52
+ if data.get('data'):
53
+ suggestions.append(data['data'][0].get('chapter_title', 'Unclassified'))
54
+ if 'other_possible_chapters' in data['data'][0]:
55
+ others = data['data'][0]['other_possible_chapters']
56
+ if isinstance(others, list): suggestions.extend(others)
57
+ return jsonify({'success': True, 'suggestions': suggestions, 'full_response': data})
58
+ except Exception as e: return jsonify({'error': str(e)}), 500
59
+
60
+ @main_bp.route('/classified/update_single', methods=['POST'])
61
+ @login_required
62
+ def update_question_classification_single():
63
+ data = request.json
64
+ image_id, subject, chapter = data.get('image_id'), data.get('subject'), data.get('chapter')
65
+ if not image_id: return jsonify({'error': 'Image ID is required'}), 400
66
+ try:
67
+ conn = get_db_connection()
68
+ owner = conn.execute("SELECT s.user_id FROM images i JOIN sessions s ON i.session_id = s.id WHERE i.id = ?", (image_id,)).fetchone()
69
+ if not owner or owner['user_id'] != current_user.id:
70
+ conn.close(); return jsonify({'error': 'Unauthorized'}), 403
71
+ conn.execute('UPDATE questions SET subject = ?, chapter = ? WHERE image_id = ?', (subject, chapter, image_id))
72
+ conn.commit(); conn.close()
73
+ return jsonify({'success': True})
74
+ except Exception as e: return jsonify({'error': str(e)}), 500
75
+
76
+
77
  @main_bp.route('/question_entry_v2/<session_id>')
78
  @login_required
79
  def question_entry_v2(session_id):