root commited on
Commit
d423ac0
·
1 Parent(s): 7d4bbcc

feat: add user setting to disable AI topic suggestions

Browse files

- Added use_ai_suggestions column to users table with migration.
- Added toggle in settings page to enable/disable AI suggestions.
- Updated manual classification wizard to respect the setting (hides AI buttons and disables automated fetches).
- Updated keyboard shortcuts to skip AI calls when disabled.

database.py CHANGED
@@ -395,11 +395,17 @@ def setup_database():
395
  cursor.execute("SELECT group_name FROM sessions LIMIT 1")
396
  except sqlite3.OperationalError:
397
  cursor.execute("ALTER TABLE sessions ADD COLUMN group_name TEXT")
 
 
 
 
398
 
399
- try:
400
- cursor.execute("SELECT pdf_viewer FROM users LIMIT 1")
401
- except sqlite3.OperationalError:
402
- cursor.execute("ALTER TABLE users ADD COLUMN pdf_viewer TEXT DEFAULT 'adobe'")
 
 
403
 
404
  # Migration to normalize subject names (e.g., "CHEMISTRY" to "Chemistry", "PHYSICS" to "Physics", etc.)
405
  try:
 
395
  cursor.execute("SELECT group_name FROM sessions LIMIT 1")
396
  except sqlite3.OperationalError:
397
  cursor.execute("ALTER TABLE sessions ADD COLUMN group_name TEXT")
398
+ try:
399
+ cursor.execute("SELECT pdf_viewer FROM users LIMIT 1")
400
+ except sqlite3.OperationalError:
401
+ cursor.execute("ALTER TABLE users ADD COLUMN pdf_viewer TEXT DEFAULT 'native'")
402
 
403
+ try:
404
+ cursor.execute("SELECT use_ai_suggestions FROM users LIMIT 1")
405
+ except sqlite3.OperationalError:
406
+ cursor.execute("ALTER TABLE users ADD COLUMN use_ai_suggestions INTEGER DEFAULT 1")
407
+
408
+ conn.commit()
409
 
410
  # Migration to normalize subject names (e.g., "CHEMISTRY" to "Chemistry", "PHYSICS" to "Physics", etc.)
411
  try:
settings_routes.py CHANGED
@@ -35,6 +35,9 @@ def settings():
35
  # --- Handle Two-Page Crop Toggle ---
36
  two_page_crop = 1 if request.form.get('two_page_crop') else 0
37
 
 
 
 
38
  # --- Handle Classifier Model Setting ---
39
  classifier_model = request.form.get('classifier_model', 'gemini')
40
  if classifier_model not in ['gemini', 'nova', 'gemma']:
@@ -75,8 +78,8 @@ def settings():
75
 
76
  # --- Update Database ---
77
  conn = get_db_connection()
78
- conn.execute('UPDATE users SET neetprep_enabled = ?, v2_default = ?, magnifier_enabled = ?, two_page_crop = ?, dpi = ?, color_rm_dpi = ?, classifier_model = ?, pdf_viewer = ? WHERE id = ?',
79
- (neetprep_enabled, v2_default, magnifier_enabled, two_page_crop, dpi, color_rm_dpi, classifier_model, pdf_viewer, current_user.id))
80
  conn.commit()
81
  conn.close()
82
 
@@ -85,6 +88,7 @@ def settings():
85
  current_user.v2_default = v2_default
86
  current_user.magnifier_enabled = magnifier_enabled
87
  current_user.two_page_crop = two_page_crop
 
88
  current_user.dpi = dpi
89
  current_user.color_rm_dpi = color_rm_dpi
90
  current_user.classifier_model = classifier_model
 
35
  # --- Handle Two-Page Crop Toggle ---
36
  two_page_crop = 1 if request.form.get('two_page_crop') else 0
37
 
38
+ # --- Handle AI Suggestions Toggle ---
39
+ use_ai_suggestions = 1 if request.form.get('use_ai_suggestions') else 0
40
+
41
  # --- Handle Classifier Model Setting ---
42
  classifier_model = request.form.get('classifier_model', 'gemini')
43
  if classifier_model not in ['gemini', 'nova', 'gemma']:
 
78
 
79
  # --- Update Database ---
80
  conn = get_db_connection()
81
+ conn.execute('UPDATE users SET neetprep_enabled = ?, v2_default = ?, magnifier_enabled = ?, two_page_crop = ?, use_ai_suggestions = ?, dpi = ?, color_rm_dpi = ?, classifier_model = ?, pdf_viewer = ? WHERE id = ?',
82
+ (neetprep_enabled, v2_default, magnifier_enabled, two_page_crop, use_ai_suggestions, dpi, color_rm_dpi, classifier_model, pdf_viewer, current_user.id))
83
  conn.commit()
84
  conn.close()
85
 
 
88
  current_user.v2_default = v2_default
89
  current_user.magnifier_enabled = magnifier_enabled
90
  current_user.two_page_crop = two_page_crop
91
+ current_user.use_ai_suggestions = use_ai_suggestions
92
  current_user.dpi = dpi
93
  current_user.color_rm_dpi = color_rm_dpi
94
  current_user.classifier_model = classifier_model
templates/question_entry_v2.html CHANGED
@@ -540,20 +540,24 @@
540
  <div class="mb-2">
541
  <label class="form-label small text-muted mb-2">Chapter / Topic</label>
542
  <div class="input-group input-group-lg">
543
- <input type="text" id="topic_input" class="form-control bg-dark text-white border-secondary" placeholder="Start typing or use AI...">
544
  <button class="btn btn-outline-secondary" type="button" onclick="document.getElementById('topic_input').value=''; document.getElementById('topic_input').focus();">
545
  <i class="bi bi-x-lg"></i>
546
  </button>
 
547
  <button class="btn btn-info" type="button" id="btn_get_suggestion" onclick="getAiSuggestion()">
548
  <span class="spinner-border spinner-border-sm d-none" role="status"></span>
549
  <i class="bi bi-stars"></i>
550
  </button>
 
551
  </div>
552
  </div>
553
 
554
  <!-- Suggestions -->
555
  <div id="ai_suggestion_container" class="d-none">
 
556
  <label class="form-label d-block small text-info mb-2 mt-1"><i class="bi bi-robot me-1"></i>AI Suggested:</label>
 
557
  <div id="ai_suggestion_chips" class="d-flex flex-wrap gap-2"></div>
558
  </div>
559
  <div id="ai_error_msg" class="alert alert-danger d-none mt-2 py-2"></div>
@@ -1903,8 +1907,8 @@
1903
  input.select();
1904
  }, 100);
1905
 
1906
- // Auto-load suggestions if topic is empty
1907
- if (!document.getElementById('topic_input').value) {
1908
  getAiSuggestion();
1909
  }
1910
  }
@@ -2138,14 +2142,18 @@
2138
  // Clear and fetch fresh suggestions for the new subject
2139
  input.value = '';
2140
  input.focus();
2141
- getAiSuggestion();
 
 
2142
  }
2143
  }
2144
 
2145
  // Alt + S to trigger AI suggestion
2146
  if (e.altKey && (e.key === 's' || e.key === 'S')) {
2147
- e.preventDefault();
2148
- getAiSuggestion();
 
 
2149
  }
2150
  });
2151
  });
 
540
  <div class="mb-2">
541
  <label class="form-label small text-muted mb-2">Chapter / Topic</label>
542
  <div class="input-group input-group-lg">
543
+ <input type="text" id="topic_input" class="form-control bg-dark text-white border-secondary" placeholder="Start typing...">
544
  <button class="btn btn-outline-secondary" type="button" onclick="document.getElementById('topic_input').value=''; document.getElementById('topic_input').focus();">
545
  <i class="bi bi-x-lg"></i>
546
  </button>
547
+ {% if current_user.use_ai_suggestions %}
548
  <button class="btn btn-info" type="button" id="btn_get_suggestion" onclick="getAiSuggestion()">
549
  <span class="spinner-border spinner-border-sm d-none" role="status"></span>
550
  <i class="bi bi-stars"></i>
551
  </button>
552
+ {% endif %}
553
  </div>
554
  </div>
555
 
556
  <!-- Suggestions -->
557
  <div id="ai_suggestion_container" class="d-none">
558
+ {% if current_user.use_ai_suggestions %}
559
  <label class="form-label d-block small text-info mb-2 mt-1"><i class="bi bi-robot me-1"></i>AI Suggested:</label>
560
+ {% endif %}
561
  <div id="ai_suggestion_chips" class="d-flex flex-wrap gap-2"></div>
562
  </div>
563
  <div id="ai_error_msg" class="alert alert-danger d-none mt-2 py-2"></div>
 
1907
  input.select();
1908
  }, 100);
1909
 
1910
+ // Auto-load suggestions if topic is empty and setting enabled
1911
+ if (!document.getElementById('topic_input').value && {% if current_user.use_ai_suggestions %}true{% else %}false{% endif %}) {
1912
  getAiSuggestion();
1913
  }
1914
  }
 
2142
  // Clear and fetch fresh suggestions for the new subject
2143
  input.value = '';
2144
  input.focus();
2145
+ if ({% if current_user.use_ai_suggestions %}true{% else %}false{% endif %}) {
2146
+ getAiSuggestion();
2147
+ }
2148
  }
2149
  }
2150
 
2151
  // Alt + S to trigger AI suggestion
2152
  if (e.altKey && (e.key === 's' || e.key === 'S')) {
2153
+ if ({% if current_user.use_ai_suggestions %}true{% else %}false{% endif %}) {
2154
+ e.preventDefault();
2155
+ getAiSuggestion();
2156
+ }
2157
  }
2158
  });
2159
  });
templates/settings.html CHANGED
@@ -98,6 +98,15 @@
98
  Choose the AI model for automatic question classification. Gemini uses Google's Gemini API, while Nova uses Amazon's Nova model via OpenRouter. Requires relevant API keys to be set.
99
  </div>
100
  </div>
 
 
 
 
 
 
 
 
 
101
  </fieldset>
102
  <hr>
103
  <fieldset>
 
98
  Choose the AI model for automatic question classification. Gemini uses Google's Gemini API, while Nova uses Amazon's Nova model via OpenRouter. Requires relevant API keys to be set.
99
  </div>
100
  </div>
101
+ <div class="mb-3 form-check form-switch">
102
+ <input type="checkbox" class="form-check-input" id="use_ai_suggestions" name="use_ai_suggestions" {% if current_user.use_ai_suggestions %}checked{% endif %}>
103
+ <label class="form-check-label" for="use_ai_suggestions">
104
+ Enable AI Topic Suggestions
105
+ </label>
106
+ <div class="form-text">
107
+ Show AI-suggested topics and chapters in the manual classification wizard. If disabled, you can still type manually.
108
+ </div>
109
+ </div>
110
  </fieldset>
111
  <hr>
112
  <fieldset>
user_auth.py CHANGED
@@ -5,7 +5,7 @@ from utils import get_db_connection
5
 
6
  class User(UserMixin):
7
  """User model for Flask-Login."""
8
- def __init__(self, id, username, email, password_hash, neetprep_enabled, dpi, color_rm_dpi, v2_default=0, magnifier_enabled=1, two_page_crop=0, google_token=None, classifier_model='gemini', pdf_viewer='adobe'):
9
  self.id = id
10
  self.username = username
11
  self.email = email
@@ -19,6 +19,7 @@ class User(UserMixin):
19
  self.google_token = google_token
20
  self.classifier_model = classifier_model
21
  self.pdf_viewer = pdf_viewer # 'adobe', 'browser', or 'legacy'
 
22
 
23
  @staticmethod
24
  def get(user_id):
@@ -40,7 +41,8 @@ class User(UserMixin):
40
  user_data.get('two_page_crop', 0),
41
  user_data.get('google_token'),
42
  user_data.get('classifier_model', 'gemini'),
43
- user_data.get('pdf_viewer', 'adobe')
 
44
  )
45
  return None
46
 
@@ -64,7 +66,8 @@ class User(UserMixin):
64
  user_data.get('two_page_crop', 0),
65
  user_data.get('google_token'),
66
  user_data.get('classifier_model', 'gemini'),
67
- user_data.get('pdf_viewer', 'adobe')
 
68
  )
69
  return None
70
 
 
5
 
6
  class User(UserMixin):
7
  """User model for Flask-Login."""
8
+ def __init__(self, id, username, email, password_hash, neetprep_enabled, dpi, color_rm_dpi, v2_default=0, magnifier_enabled=1, two_page_crop=0, google_token=None, classifier_model='gemini', pdf_viewer='adobe', use_ai_suggestions=1):
9
  self.id = id
10
  self.username = username
11
  self.email = email
 
19
  self.google_token = google_token
20
  self.classifier_model = classifier_model
21
  self.pdf_viewer = pdf_viewer # 'adobe', 'browser', or 'legacy'
22
+ self.use_ai_suggestions = use_ai_suggestions
23
 
24
  @staticmethod
25
  def get(user_id):
 
41
  user_data.get('two_page_crop', 0),
42
  user_data.get('google_token'),
43
  user_data.get('classifier_model', 'gemini'),
44
+ user_data.get('pdf_viewer', 'adobe'),
45
+ user_data.get('use_ai_suggestions', 1)
46
  )
47
  return None
48
 
 
66
  user_data.get('two_page_crop', 0),
67
  user_data.get('google_token'),
68
  user_data.get('classifier_model', 'gemini'),
69
+ user_data.get('pdf_viewer', 'adobe'),
70
+ user_data.get('use_ai_suggestions', 1)
71
  )
72
  return None
73