iyadsultan commited on
Commit
8102cc6
·
1 Parent(s): f02c5cf

send commit

Browse files
app.py CHANGED
@@ -1,17 +1,20 @@
1
  import flask
2
  from flask import Flask, render_template, request, redirect, url_for, session, flash, send_file
3
  import pandas as pd
 
4
  import random
5
  import csv
6
  from datetime import datetime
7
  import os
8
  import io
 
 
 
9
 
10
  app = Flask(__name__)
11
  app.secret_key = os.environ.get('SECRET_KEY', 'your-secret-key-here') # Gets from env or uses default
12
 
13
  # Constants
14
- PASSWORD = os.environ.get('APP_PASSWORD', "12345") # Gets from env or uses default
15
  CRITERIA = [
16
  "Up-to-date",
17
  "Accurate",
@@ -36,31 +39,194 @@ CRITERIA_DESCRIPTIONS = [
36
  "No part of the note ignores or contradicts any other part."
37
  ]
38
 
 
 
 
 
 
 
 
39
  DATA_DIR = os.environ.get('DATA_DIR', '.') # Gets from env or uses current directory
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  def load_documents():
42
  """Load documents from CSV file, excluding already evaluated ones."""
43
  try:
44
- # Load all documents
45
- df = pd.read_csv(os.path.join(DATA_DIR, 'documents.csv'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  # If description is empty, replace with empty string
48
  if 'description' in df.columns:
49
  df['description'] = df['description'].fillna('')
50
 
 
 
 
 
 
 
 
 
 
 
 
51
  # Load evaluated documents
52
  try:
53
- evaluations_df = pd.read_csv(os.path.join(DATA_DIR, 'evaluations.csv'))
54
- # Get unique document titles that have been evaluated
55
- evaluated_titles = evaluations_df['document_title'].unique()
56
- # Filter out documents that have already been evaluated
57
- df = df[~df['filename'].isin(evaluated_titles)]
58
- except FileNotFoundError:
59
- # If no evaluations file exists, all documents are available
 
 
 
 
 
 
60
  pass
61
-
62
- return df.to_dict('records')
 
 
63
  except FileNotFoundError:
 
 
 
 
 
 
 
 
64
  return []
65
 
66
  def save_evaluation(data):
@@ -71,7 +237,7 @@ def save_evaluation(data):
71
  with open(filename, 'a', newline='') as f:
72
  writer = csv.writer(f)
73
  if not file_exists:
74
- headers = ['timestamp', 'investigator_name', 'document_title', 'description'] + CRITERIA
75
  writer.writerow(headers)
76
  writer.writerow(data)
77
 
@@ -81,37 +247,133 @@ def get_results():
81
  # Load evaluations
82
  eval_df = pd.read_csv(os.path.join(DATA_DIR, 'evaluations.csv'))
83
 
84
- # Load all documents to get descriptions
85
  try:
86
  docs_df = pd.read_csv(os.path.join(DATA_DIR, 'documents.csv'))
87
- # Create a mapping of filename to description
88
  filename_to_desc = dict(zip(docs_df['filename'], docs_df['description']))
 
89
  except FileNotFoundError:
90
  filename_to_desc = {}
 
91
 
92
- return eval_df, filename_to_desc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  except FileNotFoundError:
94
- return pd.DataFrame(), {}
 
 
95
 
96
  @app.route('/', methods=['GET', 'POST'])
97
- def login():
 
98
  if request.method == 'POST':
99
- if request.form['password'] == PASSWORD:
100
- session['logged_in'] = True
101
- return redirect(url_for('evaluate'))
102
- else:
103
- flash('Invalid password')
104
- return render_template('login.html')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
  @app.route('/evaluate', methods=['GET', 'POST'])
107
  def evaluate():
108
- if not session.get('logged_in'):
109
- return redirect(url_for('login'))
 
 
 
110
 
111
  if request.method == 'POST':
112
- investigator_name = request.form['investigator_name']
113
  document_title = session.get('current_document_title')
114
  document_description = session.get('current_document_description', '')
 
 
115
 
116
  # Collect scores
117
  scores = [request.form.get(f'criteria_{i}') for i in range(len(CRITERIA))]
@@ -120,47 +382,132 @@ def evaluate():
120
  data = [datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
121
  investigator_name,
122
  document_title,
123
- document_description] + scores
 
 
124
  save_evaluation(data)
125
 
126
  flash('Evaluation saved successfully!')
 
 
 
 
 
 
 
 
127
  return redirect(url_for('evaluate'))
128
 
129
  # Get random document
130
  documents = load_documents()
131
  if not documents:
 
 
 
 
 
 
132
  return render_template('no_documents.html')
133
 
134
  document = random.choice(documents)
135
  session['current_document_title'] = document['filename']
136
  session['current_document_description'] = document.get('description', '')
 
 
 
 
 
 
 
 
137
 
138
  return render_template('evaluate.html',
139
  note=document['note'],
140
  description=document.get('description', ''),
 
141
  criteria=CRITERIA,
142
  descriptions=CRITERIA_DESCRIPTIONS,
143
- score_range=range(1, 6))
 
 
 
 
 
144
 
145
  @app.route('/results')
146
  def results():
147
- if not session.get('logged_in'):
148
- return redirect(url_for('login'))
149
-
150
- eval_df, filename_to_desc = get_results()
151
  if eval_df.empty:
152
  flash('No evaluations available yet.')
153
- return redirect(url_for('evaluate'))
154
 
155
  return render_template('results.html',
156
  evaluations=eval_df.to_dict('records'),
157
  criteria=CRITERIA,
158
  descriptions=CRITERIA_DESCRIPTIONS)
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  @app.route('/instructions')
161
  def view_instructions():
162
  """Display instructions page."""
163
- return render_template('instructions.html')
 
 
164
 
165
  @app.route('/download/instructions')
166
  def download_instructions():
@@ -173,7 +520,7 @@ def download_instructions():
173
  as_attachment=True)
174
  except FileNotFoundError:
175
  flash('Instructions file not found.')
176
- return redirect(url_for('evaluate'))
177
 
178
  @app.route('/download/template')
179
  def download_template():
@@ -186,16 +533,25 @@ def download_template():
186
  as_attachment=True)
187
  except FileNotFoundError:
188
  flash('Template file not found.')
189
- return redirect(url_for('evaluate'))
190
 
191
- @app.route('/logout')
192
- def logout():
193
- session.pop('logged_in', None)
194
- return redirect(url_for('login'))
 
 
 
 
 
 
195
 
196
  if __name__ == '__main__':
197
  # Create data directory if it doesn't exist
198
  os.makedirs(DATA_DIR, exist_ok=True)
199
 
 
 
 
200
  # Set host to 0.0.0.0 to make it accessible outside container
201
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))
 
1
  import flask
2
  from flask import Flask, render_template, request, redirect, url_for, session, flash, send_file
3
  import pandas as pd
4
+ import numpy as np
5
  import random
6
  import csv
7
  from datetime import datetime
8
  import os
9
  import io
10
+ import shutil
11
+ import traceback
12
+ import chardet
13
 
14
  app = Flask(__name__)
15
  app.secret_key = os.environ.get('SECRET_KEY', 'your-secret-key-here') # Gets from env or uses default
16
 
17
  # Constants
 
18
  CRITERIA = [
19
  "Up-to-date",
20
  "Accurate",
 
39
  "No part of the note ignores or contradicts any other part."
40
  ]
41
 
42
+ # Note origin options
43
+ NOTE_ORIGINS = [
44
+ "Human written note",
45
+ "Generative AI note",
46
+ "I am not sure"
47
+ ]
48
+
49
  DATA_DIR = os.environ.get('DATA_DIR', '.') # Gets from env or uses current directory
50
+ ERROR_LOG = [] # Store recent errors for debugging
51
+
52
+ def log_error(error_msg):
53
+ """Log an error message for debugging."""
54
+ ERROR_LOG.append(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: {error_msg}")
55
+ # Keep only the most recent 10 errors
56
+ while len(ERROR_LOG) > 10:
57
+ ERROR_LOG.pop(0)
58
+
59
+ def detect_encoding(file_path):
60
+ """Detect the encoding of a file to handle different character encodings."""
61
+ with open(file_path, 'rb') as f:
62
+ result = chardet.detect(f.read())
63
+ return result['encoding']
64
 
65
  def load_documents():
66
  """Load documents from CSV file, excluding already evaluated ones."""
67
  try:
68
+ file_path = os.path.join(DATA_DIR, 'documents.csv')
69
+
70
+ if not os.path.exists(file_path):
71
+ log_error(f"File not found: {file_path}")
72
+ return []
73
+
74
+ # Try to detect file encoding
75
+ try:
76
+ encoding = detect_encoding(file_path)
77
+ log_error(f"Detected encoding: {encoding}")
78
+ except Exception as e:
79
+ encoding = 'utf-8' # Default to UTF-8 if detection fails
80
+ log_error(f"Failed to detect encoding, using utf-8: {str(e)}")
81
+
82
+ # Try multiple parsing approaches
83
+ try:
84
+ # First attempt: Use pandas with standard settings
85
+ df = pd.read_csv(file_path, encoding=encoding)
86
+ log_error("Successfully parsed CSV with standard settings")
87
+ except Exception as e1:
88
+ log_error(f"First parsing attempt failed: {str(e1)}")
89
+ try:
90
+ # Second attempt: Use maximum quoting
91
+ df = pd.read_csv(
92
+ file_path,
93
+ encoding=encoding,
94
+ quoting=csv.QUOTE_ALL,
95
+ escapechar='\\'
96
+ )
97
+ log_error("Successfully parsed CSV with QUOTE_ALL")
98
+ except Exception as e2:
99
+ log_error(f"Second parsing attempt failed: {str(e2)}")
100
+ try:
101
+ # Third attempt: Use minimal quoting
102
+ df = pd.read_csv(
103
+ file_path,
104
+ encoding=encoding,
105
+ quoting=csv.QUOTE_MINIMAL,
106
+ escapechar='\\',
107
+ error_bad_lines=False # Skip bad lines
108
+ )
109
+ log_error("Successfully parsed CSV with QUOTE_MINIMAL")
110
+ except Exception as e3:
111
+ log_error(f"Third parsing attempt failed: {str(e3)}")
112
+ # Final attempt: Use Python's CSV module directly
113
+ log_error("Attempting to parse with CSV module directly")
114
+ rows = []
115
+ headers = None
116
+ try:
117
+ with open(file_path, 'r', encoding=encoding, newline='') as f:
118
+ reader = csv.reader(f)
119
+ headers = next(reader)
120
+ for row in reader:
121
+ if len(row) >= 4: # Ensure we have at least 4 columns
122
+ rows.append(row)
123
+
124
+ if not headers or not rows:
125
+ log_error("No valid data or headers found in CSV")
126
+ return []
127
+
128
+ df = pd.DataFrame(rows, columns=headers)
129
+ log_error(f"Successfully parsed with CSV module. Found {len(rows)} rows.")
130
+ except Exception as e4:
131
+ log_error(f"All parsing attempts failed: {str(e4)}")
132
+ return []
133
+
134
+ # Log dataframe information to help debugging
135
+ log_error(f"DataFrame columns: {df.columns.tolist()}")
136
+ log_error(f"DataFrame shape: {df.shape}")
137
+
138
+ # Ensure we have the required columns
139
+ required_columns = ['filename', 'description', 'mrn', 'note']
140
+ missing_columns = [col for col in required_columns if col not in df.columns]
141
+
142
+ if missing_columns:
143
+ log_error(f"Missing required columns: {missing_columns}")
144
+ # Try to handle common column name variations
145
+ column_map = {}
146
+ for req_col in missing_columns:
147
+ # Check for case-insensitive matches
148
+ matches = [col for col in df.columns if col.lower() == req_col.lower()]
149
+ if matches:
150
+ column_map[matches[0]] = req_col
151
+
152
+ # Rename columns if matches found
153
+ if column_map:
154
+ df = df.rename(columns=column_map)
155
+ log_error(f"Renamed columns: {column_map}")
156
+ # Check again for missing columns
157
+ missing_columns = [col for col in required_columns if col not in df.columns]
158
+
159
+ if missing_columns:
160
+ # If still missing columns, try to infer them from position
161
+ if len(df.columns) >= 4 and len(missing_columns) <= 4:
162
+ log_error("Attempting to infer columns by position")
163
+ new_columns = []
164
+ df_columns = df.columns.tolist()
165
+
166
+ for i, req_col in enumerate(required_columns):
167
+ if i < len(df_columns):
168
+ if req_col in df.columns:
169
+ new_columns.append(req_col)
170
+ else:
171
+ new_columns.append(req_col)
172
+ # Rename the column
173
+ old_col = df_columns[i]
174
+ column_map[old_col] = req_col
175
+
176
+ if column_map:
177
+ df = df.rename(columns=column_map)
178
+ log_error(f"Inferred columns by position: {column_map}")
179
+
180
+ # Final check for required columns
181
+ missing_columns = [col for col in required_columns if col not in df.columns]
182
+ if missing_columns:
183
+ log_error(f"Still missing required columns after all attempts: {missing_columns}")
184
+ raise ValueError(f"Missing required columns in documents.csv: {', '.join(missing_columns)}")
185
 
186
  # If description is empty, replace with empty string
187
  if 'description' in df.columns:
188
  df['description'] = df['description'].fillna('')
189
 
190
+ # If MRN is empty, replace with empty string
191
+ if 'mrn' in df.columns:
192
+ df['mrn'] = df['mrn'].fillna('')
193
+
194
+ # Convert all columns to string to ensure compatability
195
+ for col in df.columns:
196
+ df[col] = df[col].astype(str)
197
+
198
+ # Log first few rows to help debugging
199
+ log_error(f"First row: {df.iloc[0].to_dict() if len(df) > 0 else 'No rows'}")
200
+
201
  # Load evaluated documents
202
  try:
203
+ evaluations_path = os.path.join(DATA_DIR, 'evaluations.csv')
204
+ if os.path.exists(evaluations_path):
205
+ evaluations_df = pd.read_csv(evaluations_path)
206
+ # Get unique document titles that have been evaluated
207
+ evaluated_titles = evaluations_df['document_title'].unique()
208
+ # Filter out documents that have already been evaluated
209
+ df = df[~df['filename'].isin(evaluated_titles)]
210
+ log_error(f"Found {len(evaluated_titles)} already evaluated documents")
211
+ else:
212
+ log_error("No evaluations file found, all documents available")
213
+ except Exception as e:
214
+ log_error(f"Error loading evaluations: {str(e)}")
215
+ # If error, assume no evaluations
216
  pass
217
+
218
+ documents = df.to_dict('records')
219
+ log_error(f"Returning {len(documents)} documents for evaluation")
220
+ return documents
221
  except FileNotFoundError:
222
+ log_error(f"The documents.csv file was not found in {DATA_DIR}")
223
+ flash("The documents.csv file was not found. Please create this file with your documents for evaluation.")
224
+ return []
225
+ except Exception as e:
226
+ error_msg = f"Error loading documents: {str(e)}"
227
+ log_error(error_msg)
228
+ flash(f"{error_msg}. Please check the format of your documents.csv file.")
229
+ traceback.print_exc() # Print the full traceback for debugging
230
  return []
231
 
232
  def save_evaluation(data):
 
237
  with open(filename, 'a', newline='') as f:
238
  writer = csv.writer(f)
239
  if not file_exists:
240
+ headers = ['timestamp', 'investigator_name', 'document_title', 'description', 'mrn', 'note_origin'] + CRITERIA
241
  writer.writerow(headers)
242
  writer.writerow(data)
243
 
 
247
  # Load evaluations
248
  eval_df = pd.read_csv(os.path.join(DATA_DIR, 'evaluations.csv'))
249
 
250
+ # Load all documents to get descriptions and MRN
251
  try:
252
  docs_df = pd.read_csv(os.path.join(DATA_DIR, 'documents.csv'))
253
+ # Create a mapping of filename to description and MRN
254
  filename_to_desc = dict(zip(docs_df['filename'], docs_df['description']))
255
+ filename_to_mrn = dict(zip(docs_df['filename'], docs_df['mrn']))
256
  except FileNotFoundError:
257
  filename_to_desc = {}
258
+ filename_to_mrn = {}
259
 
260
+ return eval_df, filename_to_desc, filename_to_mrn
261
+ except FileNotFoundError:
262
+ return pd.DataFrame(), {}, {}
263
+
264
+ def get_total_document_count():
265
+ """Get the total number of documents."""
266
+ try:
267
+ df = pd.read_csv(os.path.join(DATA_DIR, 'documents.csv'))
268
+ return len(df)
269
+ except Exception:
270
+ return 0
271
+
272
+ def get_evaluated_document_count():
273
+ """Get the count of evaluated documents."""
274
+ try:
275
+ eval_df = pd.read_csv(os.path.join(DATA_DIR, 'evaluations.csv'))
276
+ return len(eval_df['document_title'].unique())
277
  except FileNotFoundError:
278
+ return 0
279
+ except Exception:
280
+ return 0
281
 
282
  @app.route('/', methods=['GET', 'POST'])
283
+ def index():
284
+ """Landing page with file upload and evaluator name."""
285
  if request.method == 'POST':
286
+ # Check if the post request has the file part
287
+ if 'file' not in request.files:
288
+ error_msg = 'No file part in the request. Please try again.'
289
+ log_error(error_msg)
290
+ flash(error_msg)
291
+ return redirect(request.url)
292
+
293
+ file = request.files['file']
294
+ evaluator_name = request.form.get('evaluator_name', '').strip()
295
+
296
+ # If user does not select file, browser also
297
+ # submits an empty part without filename
298
+ if file.filename == '':
299
+ error_msg = 'No file selected. Please select a file to upload.'
300
+ log_error(error_msg)
301
+ flash(error_msg)
302
+ return redirect(request.url)
303
+
304
+ if not evaluator_name:
305
+ error_msg = 'Please enter your name as the evaluator.'
306
+ log_error(error_msg)
307
+ flash(error_msg)
308
+ return redirect(request.url)
309
+
310
+ # Store evaluator name in session
311
+ session['evaluator_name'] = evaluator_name
312
+
313
+ if file:
314
+ try:
315
+ # Create a secure file path
316
+ file_path = os.path.join(DATA_DIR, 'documents.csv')
317
+
318
+ # Save the file
319
+ file.save(file_path)
320
+ log_error(f"File saved to {file_path}")
321
+ flash('File successfully uploaded.')
322
+
323
+ # Check file size and contents
324
+ file_size = os.path.getsize(file_path)
325
+ log_error(f"File size: {file_size} bytes")
326
+
327
+ if file_size == 0:
328
+ error_msg = 'The uploaded file is empty. Please check your file and try again.'
329
+ log_error(error_msg)
330
+ flash(error_msg)
331
+ return redirect(url_for('index'))
332
+
333
+ # Preview the file contents
334
+ try:
335
+ with open(file_path, 'r', encoding='utf-8') as f:
336
+ first_lines = [next(f) for _ in range(5) if f]
337
+ log_error(f"File preview: {first_lines}")
338
+ except Exception as e:
339
+ log_error(f"Could not preview file: {str(e)}")
340
+
341
+ # Try to load documents to verify the file format
342
+ docs = load_documents()
343
+
344
+ if not docs:
345
+ error_msg = 'No valid documents found in the uploaded file. Please check the file format and try again.'
346
+ log_error(error_msg)
347
+ flash(error_msg)
348
+ return redirect(url_for('index'))
349
+
350
+ # Success - redirect to the evaluation page
351
+ log_error(f"Successfully loaded {len(docs)} documents, redirecting to evaluate page")
352
+ return redirect(url_for('evaluate'))
353
+
354
+ except Exception as e:
355
+ error_msg = f'Error during file upload: {str(e)}'
356
+ log_error(error_msg)
357
+ flash(f'{error_msg}. Please try again.')
358
+ traceback.print_exc() # Print the full traceback for debugging
359
+ return redirect(url_for('index'))
360
+
361
+ return render_template('index.html')
362
 
363
  @app.route('/evaluate', methods=['GET', 'POST'])
364
  def evaluate():
365
+ """Evaluation page for documents."""
366
+ # Check if evaluator name is set
367
+ if 'evaluator_name' not in session:
368
+ flash('Please enter your name and upload a file first')
369
+ return redirect(url_for('index'))
370
 
371
  if request.method == 'POST':
372
+ investigator_name = session.get('evaluator_name')
373
  document_title = session.get('current_document_title')
374
  document_description = session.get('current_document_description', '')
375
+ document_mrn = session.get('current_document_mrn', '')
376
+ note_origin = request.form.get('note_origin', 'Not sure')
377
 
378
  # Collect scores
379
  scores = [request.form.get(f'criteria_{i}') for i in range(len(CRITERIA))]
 
382
  data = [datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
383
  investigator_name,
384
  document_title,
385
+ document_description,
386
+ document_mrn,
387
+ note_origin] + scores
388
  save_evaluation(data)
389
 
390
  flash('Evaluation saved successfully!')
391
+
392
+ # Check if all documents have been evaluated
393
+ documents = load_documents()
394
+ if not documents:
395
+ # All documents have been evaluated, redirect to results
396
+ flash('All evaluations completed! Here are the results.')
397
+ return redirect(url_for('results'))
398
+
399
  return redirect(url_for('evaluate'))
400
 
401
  # Get random document
402
  documents = load_documents()
403
  if not documents:
404
+ # Check if there are any evaluations
405
+ eval_df, _, _ = get_results()
406
+ if not eval_df.empty:
407
+ # There are evaluations, so all documents have been evaluated
408
+ flash('All evaluations completed! Here are the results.')
409
+ return redirect(url_for('results'))
410
  return render_template('no_documents.html')
411
 
412
  document = random.choice(documents)
413
  session['current_document_title'] = document['filename']
414
  session['current_document_description'] = document.get('description', '')
415
+ session['current_document_mrn'] = document.get('mrn', '')
416
+
417
+ # Get progress information
418
+ total_docs = get_total_document_count()
419
+ evaluated_docs = get_evaluated_document_count()
420
+ progress = 0
421
+ if total_docs > 0:
422
+ progress = int((evaluated_docs / total_docs) * 100)
423
 
424
  return render_template('evaluate.html',
425
  note=document['note'],
426
  description=document.get('description', ''),
427
+ mrn=document.get('mrn', ''),
428
  criteria=CRITERIA,
429
  descriptions=CRITERIA_DESCRIPTIONS,
430
+ note_origins=NOTE_ORIGINS,
431
+ score_range=range(1, 6),
432
+ evaluator_name=session.get('evaluator_name', ''),
433
+ progress=progress,
434
+ evaluated_docs=evaluated_docs,
435
+ total_docs=total_docs)
436
 
437
  @app.route('/results')
438
  def results():
439
+ """Results page showing all evaluations."""
440
+ eval_df, filename_to_desc, filename_to_mrn = get_results()
 
 
441
  if eval_df.empty:
442
  flash('No evaluations available yet.')
443
+ return redirect(url_for('index'))
444
 
445
  return render_template('results.html',
446
  evaluations=eval_df.to_dict('records'),
447
  criteria=CRITERIA,
448
  descriptions=CRITERIA_DESCRIPTIONS)
449
 
450
+ @app.route('/export-csv')
451
+ def export_csv():
452
+ """Export evaluations to CSV file."""
453
+ try:
454
+ eval_df, _, _ = get_results()
455
+ if eval_df.empty:
456
+ flash('No evaluations available to export.')
457
+ return redirect(url_for('results'))
458
+
459
+ # Create in-memory CSV
460
+ output = io.StringIO()
461
+ eval_df.to_csv(output, index=False, quoting=csv.QUOTE_ALL)
462
+ output.seek(0)
463
+
464
+ # Convert to BytesIO for send_file
465
+ mem = io.BytesIO()
466
+ mem.write(output.getvalue().encode('utf-8'))
467
+ mem.seek(0)
468
+
469
+ return send_file(
470
+ mem,
471
+ mimetype='text/csv',
472
+ as_attachment=True,
473
+ download_name=f'evaluations_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'
474
+ )
475
+ except Exception as e:
476
+ error_msg = f'Error exporting CSV: {str(e)}'
477
+ log_error(error_msg)
478
+ flash(error_msg)
479
+ return redirect(url_for('results'))
480
+
481
+ @app.route('/debug')
482
+ def debug():
483
+ """Debug page showing application state."""
484
+ # Get documents
485
+ documents = load_documents()
486
+
487
+ # Get evaluations
488
+ evaluations, _, _ = get_results()
489
+ if not evaluations.empty:
490
+ evaluations = evaluations.to_dict('records')
491
+ else:
492
+ evaluations = []
493
+
494
+ # Check if files exist
495
+ documents_exists = os.path.exists(os.path.join(DATA_DIR, 'documents.csv'))
496
+ evaluations_exists = os.path.exists(os.path.join(DATA_DIR, 'evaluations.csv'))
497
+
498
+ return render_template('debug.html',
499
+ documents=documents,
500
+ evaluations=evaluations,
501
+ documents_exists=documents_exists,
502
+ evaluations_exists=evaluations_exists,
503
+ errors=ERROR_LOG)
504
+
505
  @app.route('/instructions')
506
  def view_instructions():
507
  """Display instructions page."""
508
+ return render_template('instructions.html',
509
+ criteria=CRITERIA,
510
+ descriptions=CRITERIA_DESCRIPTIONS)
511
 
512
  @app.route('/download/instructions')
513
  def download_instructions():
 
520
  as_attachment=True)
521
  except FileNotFoundError:
522
  flash('Instructions file not found.')
523
+ return redirect(url_for('index'))
524
 
525
  @app.route('/download/template')
526
  def download_template():
 
533
  as_attachment=True)
534
  except FileNotFoundError:
535
  flash('Template file not found.')
536
+ return redirect(url_for('index'))
537
 
538
+ @app.route('/reset', methods=['POST'])
539
+ def reset():
540
+ """Reset the session and return to the landing page."""
541
+ session.clear()
542
+ # Remove evaluations.csv if it exists
543
+ evaluations_path = os.path.join(DATA_DIR, 'evaluations.csv')
544
+ if os.path.exists(evaluations_path):
545
+ os.remove(evaluations_path)
546
+ flash('Session reset. You can start a new evaluation.')
547
+ return redirect(url_for('index'))
548
 
549
  if __name__ == '__main__':
550
  # Create data directory if it doesn't exist
551
  os.makedirs(DATA_DIR, exist_ok=True)
552
 
553
+ # Set debug mode for troubleshooting
554
+ app.config['DEBUG'] = True
555
+
556
  # Set host to 0.0.0.0 to make it accessible outside container
557
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))
documents.csv CHANGED
@@ -1,5 +1,4 @@
1
- filename,description,note
2
- doc1.txt,Patient with chronic heart failure,The patient is a 67-year-old male with a history of chronic heart failure who presented with increasing dyspnea on exertion. Echocardiogram showed an ejection fraction of 35%. BNP was elevated at 850 pg/mL. The patient was initiated on guideline-directed medical therapy including an ACE inhibitor and beta-blocker. Loop diuretic was optimized for volume management. The patient showed improvement in symptoms after 3 days of treatment and was discharged with close follow-up scheduled with cardiology in 1 week.
3
- doc2.txt,Emergency department visit for acute asthma exacerbation,32-year-old female with history of asthma presented to the ED with acute onset of wheezing and shortness of breath. Symptoms began 2 hours prior to arrival after exposure to cat dander. Patient used albuterol inhaler twice without relief. On arrival: HR 110, RR 24, O2 sat 92% on room air. Exam showed diffuse wheezing and prolonged expiratory phase. Patient received nebulized albuterol/ipratropium and IV methylprednisolone with significant improvement. After 4 hours of observation and repeated treatments, patient was discharged home on a 5-day course of oral prednisone with instructions to follow up with PCP.
4
- doc3.txt,Post-operative note for laparoscopic cholecystectomy,The patient underwent an uncomplicated laparoscopic cholecystectomy for symptomatic cholelithiasis. Four ports were placed in the standard fashion. The gallbladder was removed intact through the umbilical port site. Total blood loss was minimal at approximately 20cc. The patient tolerated the procedure well and was transferred to the recovery room in stable condition. The patient was discharged on postoperative day 1 after demonstrating ability to tolerate oral intake and ambulate independently. Discharge medications included as-needed pain control. Follow-up was scheduled in 2 weeks for wound check.
5
- doc4.txt,Initial psychiatric evaluation for major depressive disorder,Patient is a 42-year-old divorced female presenting with a 6-month history of persistent low mood, anhedonia, insomnia, poor concentration, and passive suicidal ideation without plan or intent. Symptoms began after job loss and divorce. PHQ-9 score is 21, indicating severe depression. No previous psychiatric history or treatment. Family history is positive for depression in mother. Mental status examination showed psychomotor retardation, depressed affect, and negative thought content. Diagnosis: Major Depressive Disorder, recurrent, severe without psychotic features. Plan: Start sertraline 50mg daily with weekly follow-up initially. Safety plan established. Referral to psychotherapy made.
 
1
+ filename,description,mrn,note
2
+ "progress_note_1.txt","Primary Care Follow-up Visit","MRN12345678","Patient is a 57-year-old male with history of hypertension, type 2 diabetes, and hyperlipidemia presenting for routine follow-up. BP today is 138/82, weight stable at 87kg. A1c improved from 7.8 to 7.2. Patient reports good medication adherence and states he is walking 30 minutes daily. Physical exam unremarkable. Assessment: 1) Hypertension - well controlled, continue current regimen 2) T2DM - improving, continue metformin 1000mg BID 3) Hyperlipidemia - LDL at goal, continue atorvastatin. Plan: Continue current medications, follow-up in 3 months, routine labs before next visit."
3
+ "ed_visit_note.txt","Emergency Department Visit for Chest Pain","MRN87654321","45-year-old female with no significant past medical history presents with sudden onset substernal chest pain that started 2 hours ago while at rest. Pain is 7/10, described as pressure-like, radiating to left arm, associated with mild SOB and nausea. Vitals: T 37.0, HR 102, BP 142/88, RR 18, SpO2 98% on RA. EKG shows NSR without ST changes. Initial troponin negative. CXR without acute findings. Patient given aspirin 325mg, sublingual nitroglycerin with relief of symptoms. Assessment: Acute chest pain, unclear etiology, low-intermediate risk for ACS. Plan: Admit to observation unit for serial troponins and stress test in AM."
4
+ "discharge_summary.txt","Hospital Discharge Summary - Pneumonia","MRN23456789","72-year-old male with COPD admitted for community-acquired pneumonia. Patient presented with 4 days of productive cough, fever, and worsening shortness of breath. CXR showed RLL infiltrate. Treated with IV ceftriaxone and azithromycin for 3 days with clinical improvement, then transitioned to oral antibiotics. O2 requirements decreased from 4L to baseline 2L. Pulmonary function optimized with scheduled bronchodilators. Discharged home on 7-day course of amoxicillin-clavulanate with pulmonology follow-up in 2 weeks. Patient educated on importance of pneumococcal and influenza vaccinations. Follow-up CXR recommended in 6 weeks to ensure resolution."
 
evaluations.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ timestamp,investigator_name,document_title,description,mrn,note_origin,Up-to-date,Accurate,Thorough,Useful,Organized,Comprehensible,Succinct,Synthesized,Internally Consistent
2
+ 2025-03-07 17:03:41,Iyad Sultan,progress_note_1.txt,Primary Care Follow-up Visit,MRN12345678,Human written note,3,3,3,3,3,3,3,3,3
3
+ 2025-03-07 17:03:56,Iyad Sultan,ed_visit_note.txt,Emergency Department Visit for Chest Pain,MRN87654321,Generative AI note,3,3,3,3,3,3,3,3,3
4
+ 2025-03-07 17:04:13,Iyad Sultan,discharge_summary.txt,Hospital Discharge Summary - Pneumonia,MRN23456789,Human written note,3,3,3,4,4,3,4,4,3
instructions.md CHANGED
@@ -4,12 +4,11 @@ This document provides detailed instructions for using the Human Notes Evaluator
4
 
5
  ## Contents
6
  1. [Overview](#overview)
7
- 2. [Login Process](#login-process)
8
- 3. [Preparing Documents for Evaluation](#preparing-documents-for-evaluation)
9
- 4. [Evaluating Documents](#evaluating-documents)
10
- 5. [Viewing Results](#viewing-results)
11
- 6. [Understanding Evaluation Criteria](#understanding-evaluation-criteria)
12
- 7. [Troubleshooting](#troubleshooting)
13
 
14
  ## Overview
15
 
@@ -20,12 +19,25 @@ The Human Notes Evaluator is designed to systematically assess documents (such a
20
  - Tracks which documents have been evaluated
21
  - Displays results in a table format
22
 
23
- ## Login Process
 
 
 
 
 
 
 
24
 
25
- 1. Access the application through your browser
26
- 2. Enter the password at the login screen
27
- - Default password: `12345` (this can be changed by an administrator)
28
- 3. Upon successful login, you will be directed to the evaluation interface
 
 
 
 
 
 
29
 
30
  ## Preparing Documents for Evaluation
31
 
@@ -36,39 +48,20 @@ To add your own documents for evaluation:
36
  3. Add your documents using the following format:
37
  - **filename**: A unique identifier for each document (not shown to evaluators)
38
  - **description**: (Optional) A brief description of the document context
 
39
  - **note**: The full text of the document to be evaluated
40
  4. Save the file as CSV format
41
- 5. Upload the completed CSV file as `documents.csv` to replace the existing file
42
 
43
  ### Example CSV Format:
44
 
45
  ```
46
- filename,description,note
47
- doc1.txt,Patient with heart failure,This is the full text of the first document...
48
- doc2.txt,Emergency room visit,This is the full text of the second document...
49
- doc3.txt,,This is a document without a description...
50
  ```
51
 
52
- ## Evaluating Documents
53
-
54
- 1. After logging in, you'll see a randomly selected document for evaluation
55
- 2. Enter your name in the "Evaluator Name" field
56
- 3. For each criterion, select a rating from 1-5 (Not at all to Extremely)
57
- 4. All criteria must be rated before submission
58
- 5. Click "Submit Evaluation" to record your assessment
59
- 6. The system will then present another random document for evaluation
60
- 7. Continue until all documents have been evaluated
61
-
62
- ## Viewing Results
63
-
64
- 1. Click the "View Results" button in the header
65
- 2. Results are displayed in a table showing:
66
- - Timestamp of evaluation
67
- - Document identifier
68
- - Document description (if provided)
69
- - Evaluator name
70
- - Scores for each criterion
71
-
72
  ## Understanding Evaluation Criteria
73
 
74
  Documents are evaluated on a 1-5 scale (Not at all = 1, Extremely = 5) across these criteria:
@@ -114,18 +107,13 @@ Documents are evaluated on a 1-5 scale (Not at all = 1, Extremely = 5) across th
114
  ### Common Issues:
115
 
116
  1. **No documents available for evaluation**
117
- - Ensure the `documents.csv` file exists and is properly formatted
118
  - Check that not all documents have already been evaluated
119
 
120
  2. **Unable to submit evaluation**
121
  - Verify that all criteria have been rated
122
- - Check that you've entered an evaluator name
123
-
124
- 3. **Login issues**
125
- - Confirm you're using the correct password
126
- - Contact the administrator if the password doesn't work
127
 
128
- 4. **Results not showing**
129
  - Results will only appear after at least one document has been evaluated
130
 
131
  For additional support, please contact your administrator.
 
4
 
5
  ## Contents
6
  1. [Overview](#overview)
7
+ 2. [Evaluating Documents](#evaluating-documents)
8
+ 3. [Viewing Results](#viewing-results)
9
+ 4. [Preparing Documents for Evaluation](#preparing-documents-for-evaluation)
10
+ 5. [Understanding Evaluation Criteria](#understanding-evaluation-criteria)
11
+ 6. [Troubleshooting](#troubleshooting)
 
12
 
13
  ## Overview
14
 
 
19
  - Tracks which documents have been evaluated
20
  - Displays results in a table format
21
 
22
+ ## Evaluating Documents
23
+
24
+ 1. Enter your name in the "Evaluator Name" field
25
+ 2. For each criterion, select a rating from 1-5 (Not at all to Extremely)
26
+ 3. All criteria must be rated before submission
27
+ 4. Click "Submit Evaluation" to record your assessment
28
+ 5. The system will then present another random document for evaluation
29
+ 6. Continue until all documents have been evaluated
30
 
31
+ ## Viewing Results
32
+
33
+ 1. Click the "View Results" button in the header
34
+ 2. Results are displayed in a table showing:
35
+ - Timestamp of evaluation
36
+ - Document identifier
37
+ - Document description (if provided)
38
+ - Medical Record Number (MRN)
39
+ - Evaluator name
40
+ - Scores for each criterion
41
 
42
  ## Preparing Documents for Evaluation
43
 
 
48
  3. Add your documents using the following format:
49
  - **filename**: A unique identifier for each document (not shown to evaluators)
50
  - **description**: (Optional) A brief description of the document context
51
+ - **mrn**: Medical Record Number for the patient (to facilitate chart lookups)
52
  - **note**: The full text of the document to be evaluated
53
  4. Save the file as CSV format
54
+ 5. Upload the completed CSV file using the file upload on the home page
55
 
56
  ### Example CSV Format:
57
 
58
  ```
59
+ filename,description,mrn,note
60
+ doc1.txt,Patient with heart failure,MRN12345,This is the full text of the first document...
61
+ doc2.txt,Emergency room visit,MRN67890,This is the full text of the second document...
62
+ doc3.txt,,MRN24680,This is a document without a description...
63
  ```
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  ## Understanding Evaluation Criteria
66
 
67
  Documents are evaluated on a 1-5 scale (Not at all = 1, Extremely = 5) across these criteria:
 
107
  ### Common Issues:
108
 
109
  1. **No documents available for evaluation**
110
+ - Ensure the CSV file you uploaded exists and is properly formatted
111
  - Check that not all documents have already been evaluated
112
 
113
  2. **Unable to submit evaluation**
114
  - Verify that all criteria have been rated
 
 
 
 
 
115
 
116
+ 3. **Results not showing**
117
  - Results will only appear after at least one document has been evaluated
118
 
119
  For additional support, please contact your administrator.
project_status.md CHANGED
@@ -15,21 +15,54 @@
15
  - Added functionality to view instructions in the browser
16
  - Implemented file download functionality for instructions and template files
17
  - Added links to resources throughout the application
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  ## Current Status
20
  - Application is ready for deployment on Hugging Face Spaces
21
  - All necessary files have been created and configured
22
  - Sample data is included for testing
23
  - Comprehensive user instructions are available both in-app and as downloadable files
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  ## Next Steps
26
  - Deploy application to Hugging Face Spaces
27
  - Test application functionality in the Hugging Face environment
28
  - Consider adding additional features:
29
- - Export results to CSV/Excel
30
  - Data visualization for evaluation results
31
  - Custom evaluation criteria configuration
32
- - User authentication for multiple evaluators
33
 
34
  ## Open Questions
35
  - None at this time
 
15
  - Added functionality to view instructions in the browser
16
  - Implemented file download functionality for instructions and template files
17
  - Added links to resources throughout the application
18
+ - Removed login page and authentication requirements for direct access to evaluation
19
+ - Improved error handling for CSV parsing to handle commas in text fields
20
+ - Enhanced the no_documents.html template with detailed troubleshooting guidance
21
+ - Fixed CSV formatting in example files to use proper quoting
22
+ - Added a landing page with file upload and evaluator name entry
23
+ - Moved evaluator name input to the landing page and stored in session
24
+ - Added navigation links to easily move between pages
25
+ - Implemented session reset functionality for starting new evaluations
26
+ - Added progress tracking with progress bar during evaluation
27
+ - Added automatic redirection to results when all documents are evaluated
28
+ - Implemented CSV export functionality for evaluation results
29
+ - Fixed table layout issues with responsive design for the results table
30
+ - Fixed file upload issues with improved error handling
31
+ - Added a debug page showing application state for troubleshooting
32
+ - Implemented error logging for better issue diagnosis
33
+ - Enhanced CSV parsing with multiple fallback methods for greater compatibility
34
+ - Added MRN (Medical Record Number) field to identify patient charts
35
+ - Updated all templates and sample files to include MRN information
36
+ - Implemented MRN display in the evaluation and results pages
37
+ - Significantly enhanced CSV parsing with automatic encoding detection
38
+ - Updated sample data with realistic clinical notes
39
+ - Improved error messages for file upload problems
40
+ - Added handling for common CSV format variations and column name mismatches
41
 
42
  ## Current Status
43
  - Application is ready for deployment on Hugging Face Spaces
44
  - All necessary files have been created and configured
45
  - Sample data is included for testing
46
  - Comprehensive user instructions are available both in-app and as downloadable files
47
+ - Landing page now provides file upload and evaluator name entry
48
+ - Robust error handling for CSV parsing issues
49
+ - Enhanced navigation between all pages
50
+ - Progress tracking during evaluation process
51
+ - Ability to export evaluation results to CSV
52
+ - Responsive design for better display on various devices
53
+ - Debug mode available for identifying issues
54
+ - Multiple CSV parsing methods for better compatibility with different file formats
55
+ - MRN tracking for patient chart identification
56
+ - Realistic clinical note examples included in sample files
57
+ - Enhanced error reporting and diagnosis capabilities
58
 
59
  ## Next Steps
60
  - Deploy application to Hugging Face Spaces
61
  - Test application functionality in the Hugging Face environment
62
  - Consider adding additional features:
 
63
  - Data visualization for evaluation results
64
  - Custom evaluation criteria configuration
65
+ - User authentication for multiple evaluators (if needed in the future)
66
 
67
  ## Open Questions
68
  - None at this time
readme.md CHANGED
@@ -1,97 +1,12 @@
1
- # Human Notes Evaluator
2
-
3
- This application allows for systematic evaluation of clinical notes or other documents based on a set of predefined criteria. The evaluator interface presents documents randomly to evaluators until all have been assessed.
4
-
5
- ## Features
6
-
7
- - Random presentation of documents to reduce bias
8
- - Evaluation across multiple criteria
9
- - Secure login system
10
- - Results table showing all evaluations
11
- - Docker containerization for easy deployment on Hugging Face Spaces
12
-
13
- ## Criteria for Evaluation
14
-
15
- Documents are evaluated on a 1-5 scale (Not at all to Extremely) across these criteria:
16
-
17
- 1. **Up-to-date**: The note contains the most recent test results and recommendations.
18
- 2. **Accurate**: The note is true. It is free of incorrect information.
19
- 3. **Thorough**: The note is complete and documents all of the issues of importance to the patient.
20
- 4. **Useful**: The note is extremely relevant, providing valuable information and/or analysis.
21
- 5. **Organized**: The note is well-formed and structured in a way that helps the reader understand the patient's clinical course.
22
- 6. **Comprehensible**: The note is clear, without ambiguity or sections that are difficult to understand.
23
- 7. **Succinct**: The note is brief, to the point, and without redundancy.
24
- 8. **Synthesized**: The note reflects the author's understanding of the patient's status and ability to develop a plan of care.
25
- 9. **Internally Consistent**: No part of the note ignores or contradicts any other part.
26
-
27
- ## How to Use
28
-
29
- ### Login
30
- - Default password: `12345` (you can change this in the app.py file or set as an environment variable)
31
-
32
- ### Adding Your Own Documents
33
- 1. Download the sample template (`sample_documents_template.csv`)
34
- 2. Fill in your documents following the template format:
35
- - **filename**: A unique identifier for the document (not visible to evaluators)
36
- - **description**: (Optional) A brief description of the document
37
- - **note**: The full text of the document to be evaluated
38
- 3. Upload your completed CSV file as `documents.csv` to your Hugging Face Space
39
-
40
- ### Evaluating Documents
41
- 1. Enter your name as the evaluator
42
- 2. Rate each document across all criteria
43
- 3. Submit your evaluation
44
- 4. Continue until all documents are evaluated
45
-
46
- ### Viewing Results
47
- - Click "View Results" to see all evaluations in a table format
48
-
49
- ## For Developers
50
-
51
- ### Running Locally
52
-
53
- 1. Clone the repository
54
- 2. Install dependencies:
55
- ```
56
- pip install -r requirements.txt
57
- ```
58
- 3. Run the application:
59
- ```
60
- python app.py
61
- ```
62
-
63
- ### Environment Variables
64
-
65
- - `SECRET_KEY`: Flask session encryption key (default: 'your-secret-key-here')
66
- - `APP_PASSWORD`: Login password (default: '12345')
67
- - `DATA_DIR`: Directory for data files (default: current directory)
68
- - `PORT`: Port to run the application (default: 7860)
69
-
70
- ### Docker Usage
71
-
72
- Build and run the Docker container:
73
-
74
- ```bash
75
- docker build -t human-evaluator .
76
- docker run -p 7860:7860 human-evaluator
77
- ```
78
-
79
- ## Deploying to Hugging Face Spaces
80
-
81
- 1. Create a new Space on Hugging Face with Docker runtime
82
- 2. Upload all files to the Space repository
83
- 3. Make sure to include your `documents.csv` file with the documents you want to evaluate
84
- 4. The Space will automatically build and deploy the application
85
-
86
- ## File Structure
87
-
88
- - `app.py`: Main application code
89
- - `Dockerfile`: Docker configuration
90
- - `requirements.txt`: Python dependencies
91
- - `documents.csv`: Your documents for evaluation
92
- - `templates/`: HTML templates
93
- - `static/`: CSS and other static assets
94
-
95
- ## Sample Data
96
-
97
- A sample `documents.csv` file is included with example medical notes for testing purposes.
 
1
+ ---
2
+ title: Human Evaluator
3
+ emoji: 🏆
4
+ colorFrom: indigo
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ short_description: Human evaluation of medical notes
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -12,3 +12,5 @@ pytz==2024.2
12
  six==1.17.0
13
  tzdata==2024.2
14
  werkzeug==3.1.3
 
 
 
12
  six==1.17.0
13
  tzdata==2024.2
14
  werkzeug==3.1.3
15
+ chardet==5.2.0
16
+ chardet==5.2.0
sample_documents_template.csv CHANGED
@@ -1,4 +1,4 @@
1
- filename,description,note
2
- document1.txt,Optional description for document 1,"Enter the full text of the note here. This could be a medical note, a report, or any text that needs to be evaluated."
3
- document2.txt,Optional description for document 2,"Enter the full text of the second note here. Make sure to include all relevant information that evaluators need to assess the quality of the note."
4
- document3.txt,,"Notes without descriptions are also acceptable. Just leave the description field empty, like in this example."
 
1
+ filename,description,mrn,note
2
+ "progress_note_1.txt","Primary Care Follow-up Visit","MRN12345678","Patient is a 57-year-old male with history of hypertension, type 2 diabetes, and hyperlipidemia presenting for routine follow-up. BP today is 138/82, weight stable at 87kg. A1c improved from 7.8 to 7.2. Patient reports good medication adherence and states he is walking 30 minutes daily. Physical exam unremarkable. Assessment: 1) Hypertension - well controlled, continue current regimen 2) T2DM - improving, continue metformin 1000mg BID 3) Hyperlipidemia - LDL at goal, continue atorvastatin. Plan: Continue current medications, follow-up in 3 months, routine labs before next visit."
3
+ "ed_visit_note.txt","Emergency Department Visit for Chest Pain","MRN87654321","45-year-old female with no significant past medical history presents with sudden onset substernal chest pain that started 2 hours ago while at rest. Pain is 7/10, described as pressure-like, radiating to left arm, associated with mild SOB and nausea. Vitals: T 37.0, HR 102, BP 142/88, RR 18, SpO2 98% on RA. EKG shows NSR without ST changes. Initial troponin negative. CXR without acute findings. Patient given aspirin 325mg, sublingual nitroglycerin with relief of symptoms. Assessment: Acute chest pain, unclear etiology, low-intermediate risk for ACS. Plan: Admit to observation unit for serial troponins and stress test in AM."
4
+ "discharge_summary.txt","Hospital Discharge Summary - Pneumonia","MRN23456789","72-year-old male with COPD admitted for community-acquired pneumonia. Patient presented with 4 days of productive cough, fever, and worsening shortness of breath. CXR showed RLL infiltrate. Treated with IV ceftriaxone and azithromycin for 3 days with clinical improvement, then transitioned to oral antibiotics. O2 requirements decreased from 4L to baseline 2L. Pulmonary function optimized with scheduled bronchodilators. Discharged home on 7-day course of amoxicillin-clavulanate with pulmonology follow-up in 2 weeks. Patient educated on importance of pneumococcal and influenza vaccinations. Follow-up CXR recommended in 6 weeks to ensure resolution."
static/style.css CHANGED
@@ -60,12 +60,24 @@ h1, h2, h3 {
60
  border: 1px solid #eee;
61
  }
62
 
63
- .description-box {
64
  margin-bottom: 15px;
 
 
 
 
 
 
65
  padding: 10px;
66
  background-color: #e9f7fe;
67
  border-radius: 4px;
68
  border-left: 3px solid #3498db;
 
 
 
 
 
 
69
  }
70
 
71
  .note-content {
@@ -181,6 +193,55 @@ h1, h2, h3 {
181
  text-decoration: underline;
182
  }
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  @media (max-width: 768px) {
185
  .container {
186
  width: 95%;
@@ -195,4 +256,12 @@ h1, h2, h3 {
195
  flex-direction: column;
196
  gap: 5px;
197
  }
 
 
 
 
 
 
 
 
198
  }
 
60
  border: 1px solid #eee;
61
  }
62
 
63
+ .doc-info {
64
  margin-bottom: 15px;
65
+ display: flex;
66
+ flex-wrap: wrap;
67
+ gap: 10px;
68
+ }
69
+
70
+ .info-item {
71
  padding: 10px;
72
  background-color: #e9f7fe;
73
  border-radius: 4px;
74
  border-left: 3px solid #3498db;
75
+ flex: 1 1 45%;
76
+ }
77
+
78
+ .mrn-info {
79
+ background-color: #f0f9eb;
80
+ border-left-color: #67c23a;
81
  }
82
 
83
  .note-content {
 
193
  text-decoration: underline;
194
  }
195
 
196
+ .evaluator-info {
197
+ background-color: #f0f7ff;
198
+ padding: 10px 15px;
199
+ border-radius: 4px;
200
+ margin-bottom: 20px;
201
+ border-left: 3px solid #3498db;
202
+ }
203
+
204
+ .evaluator-info p {
205
+ margin: 0;
206
+ font-size: 16px;
207
+ }
208
+
209
+ /* Progress bar styles */
210
+ .progress-bar-container {
211
+ margin-bottom: 20px;
212
+ }
213
+
214
+ .progress-info {
215
+ display: flex;
216
+ justify-content: space-between;
217
+ margin-bottom: 5px;
218
+ }
219
+
220
+ .progress-info p {
221
+ margin: 0;
222
+ font-size: 14px;
223
+ color: #666;
224
+ }
225
+
226
+ .progress-bar {
227
+ height: 10px;
228
+ background-color: #f0f0f0;
229
+ border-radius: 5px;
230
+ overflow: hidden;
231
+ }
232
+
233
+ .progress-fill {
234
+ height: 100%;
235
+ background-color: #3498db;
236
+ border-radius: 5px;
237
+ transition: width 0.3s ease;
238
+ }
239
+
240
+ /* Table responsive styles */
241
+ .table-wrapper {
242
+ overflow-x: auto;
243
+ }
244
+
245
  @media (max-width: 768px) {
246
  .container {
247
  width: 95%;
 
256
  flex-direction: column;
257
  gap: 5px;
258
  }
259
+
260
+ .doc-info {
261
+ flex-direction: column;
262
+ }
263
+
264
+ .info-item {
265
+ flex: 1 1 100%;
266
+ }
267
  }
templates/debug.html ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Debug Information</title>
5
+ <style>
6
+ body {
7
+ font-family: monospace;
8
+ padding: 20px;
9
+ background-color: #f5f5f5;
10
+ }
11
+ .debug-container {
12
+ background-color: white;
13
+ padding: 20px;
14
+ border-radius: 5px;
15
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
16
+ max-width: 1000px;
17
+ margin: 0 auto;
18
+ }
19
+ h1 {
20
+ border-bottom: 2px solid #eee;
21
+ padding-bottom: 10px;
22
+ }
23
+ .section {
24
+ margin-bottom: 30px;
25
+ }
26
+ .section h2 {
27
+ background-color: #f0f0f0;
28
+ padding: 10px;
29
+ border-radius: 5px;
30
+ }
31
+ .debug-item {
32
+ border-bottom: 1px solid #eee;
33
+ padding: 10px 0;
34
+ }
35
+ .key {
36
+ font-weight: bold;
37
+ color: #333;
38
+ display: inline-block;
39
+ width: 200px;
40
+ }
41
+ .value {
42
+ font-family: monospace;
43
+ background-color: #f9f9f9;
44
+ padding: 3px 6px;
45
+ border-radius: 3px;
46
+ color: #0066cc;
47
+ display: inline-block;
48
+ white-space: pre-wrap;
49
+ word-break: break-all;
50
+ max-width: 700px;
51
+ }
52
+ .error {
53
+ color: red;
54
+ background-color: #fff0f0;
55
+ padding: 10px;
56
+ border-radius: 5px;
57
+ margin: 10px 0;
58
+ border-left: 4px solid red;
59
+ }
60
+ .success {
61
+ color: green;
62
+ background-color: #f0fff0;
63
+ padding: 10px;
64
+ border-radius: 5px;
65
+ margin: 10px 0;
66
+ border-left: 4px solid green;
67
+ }
68
+ .back-link {
69
+ margin-top: 20px;
70
+ display: inline-block;
71
+ padding: 8px 15px;
72
+ background-color: #0066cc;
73
+ color: white;
74
+ text-decoration: none;
75
+ border-radius: 4px;
76
+ }
77
+ .back-link:hover {
78
+ background-color: #0055aa;
79
+ }
80
+ </style>
81
+ </head>
82
+ <body>
83
+ <div class="debug-container">
84
+ <h1>Debug Information</h1>
85
+
86
+ <div class="section">
87
+ <h2>Session Data</h2>
88
+ {% if session %}
89
+ {% for key, value in session.items() %}
90
+ <div class="debug-item">
91
+ <span class="key">{{ key }}</span>: <span class="value">{{ value }}</span>
92
+ </div>
93
+ {% endfor %}
94
+ {% else %}
95
+ <div class="error">No session data available</div>
96
+ {% endif %}
97
+ </div>
98
+
99
+ <div class="section">
100
+ <h2>Files Available</h2>
101
+ <div class="debug-item">
102
+ <span class="key">documents.csv</span>:
103
+ <span class="value">
104
+ {% if documents_exists %}
105
+ <span class="success">File exists</span>
106
+ {% else %}
107
+ <span class="error">File does not exist</span>
108
+ {% endif %}
109
+ </span>
110
+ </div>
111
+ <div class="debug-item">
112
+ <span class="key">evaluations.csv</span>:
113
+ <span class="value">
114
+ {% if evaluations_exists %}
115
+ <span class="success">File exists</span>
116
+ {% else %}
117
+ <span class="error">File does not exist</span>
118
+ {% endif %}
119
+ </span>
120
+ </div>
121
+ </div>
122
+
123
+ <div class="section">
124
+ <h2>Document Data</h2>
125
+ {% if documents %}
126
+ <div class="success">Found {{ documents|length }} document(s) available for evaluation</div>
127
+ {% for doc in documents %}
128
+ <div class="debug-item">
129
+ <span class="key">Document {{ loop.index }}</span>:
130
+ <span class="value">{{ doc.filename }} ({{ doc.description }})</span>
131
+ </div>
132
+ {% endfor %}
133
+ {% else %}
134
+ <div class="error">No documents found or all documents have been evaluated</div>
135
+ {% endif %}
136
+ </div>
137
+
138
+ <div class="section">
139
+ <h2>Evaluations</h2>
140
+ {% if evaluations %}
141
+ <div class="success">Found {{ evaluations|length }} evaluation(s)</div>
142
+ {% for eval in evaluations %}
143
+ <div class="debug-item">
144
+ <span class="key">Evaluation {{ loop.index }}</span>:
145
+ <span class="value">{{ eval.document_title }} by {{ eval.investigator_name }}</span>
146
+ </div>
147
+ {% endfor %}
148
+ {% else %}
149
+ <div class="error">No evaluations found</div>
150
+ {% endif %}
151
+ </div>
152
+
153
+ <div class="section">
154
+ <h2>Recent Errors</h2>
155
+ {% if errors %}
156
+ {% for error in errors %}
157
+ <div class="error">{{ error }}</div>
158
+ {% endfor %}
159
+ {% else %}
160
+ <div class="success">No errors recorded</div>
161
+ {% endif %}
162
+ </div>
163
+
164
+ <a href="{{ url_for('index') }}" class="back-link">Back to Home</a>
165
+ </div>
166
+ </body>
167
+ </html>
templates/evaluate.html CHANGED
@@ -4,6 +4,30 @@
4
  <head>
5
  <title>Evaluate Document</title>
6
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  </head>
8
  <body>
9
  <div class="container">
@@ -12,28 +36,43 @@
12
  <div class="header-links">
13
  <a href="{{ url_for('view_instructions') }}" class="instructions-btn">Instructions</a>
14
  <a href="{{ url_for('results') }}" class="results-btn">View Results</a>
15
- <a href="{{ url_for('logout') }}" class="logout-btn">Logout</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  </div>
17
  </div>
18
 
19
  <div class="note-container">
20
  <h2>Document Content:</h2>
21
- {% if description %}
22
- <div class="description-box">
23
- <strong>Description:</strong> {{ description }}
 
 
 
 
 
 
 
 
24
  </div>
25
- {% endif %}
26
  <div class="note-content">
27
  {{ note }}
28
  </div>
29
  </div>
30
 
31
  <form method="POST">
32
- <div class="form-group">
33
- <label for="investigator_name">Evaluator Name:</label>
34
- <input type="text" id="investigator_name" name="investigator_name" required>
35
- </div>
36
-
37
  <div class="criteria-container">
38
  {% for i in range(criteria|length) %}
39
  <div class="criteria-group">
@@ -54,6 +93,19 @@
54
  {% endfor %}
55
  </div>
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  <button type="submit" class="submit-btn">Submit Evaluation</button>
58
  </form>
59
 
 
4
  <head>
5
  <title>Evaluate Document</title>
6
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
7
+ <style>
8
+ .form-section {
9
+ margin: 20px 0;
10
+ padding: 15px;
11
+ background-color: #f5f5f5;
12
+ border-radius: 8px;
13
+ }
14
+
15
+ .origin-options {
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 10px;
19
+ }
20
+
21
+ .origin-option {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 10px;
25
+ }
26
+
27
+ .origin-option input[type="radio"] {
28
+ margin: 0;
29
+ }
30
+ </style>
31
  </head>
32
  <body>
33
  <div class="container">
 
36
  <div class="header-links">
37
  <a href="{{ url_for('view_instructions') }}" class="instructions-btn">Instructions</a>
38
  <a href="{{ url_for('results') }}" class="results-btn">View Results</a>
39
+ <a href="{{ url_for('index') }}" class="evaluate-btn">Back to Home</a>
40
+ </div>
41
+ </div>
42
+
43
+ <div class="evaluator-info">
44
+ <p><strong>Evaluator:</strong> {{ evaluator_name }}</p>
45
+ </div>
46
+
47
+ <div class="progress-bar-container">
48
+ <div class="progress-info">
49
+ <p>Progress: <strong>{{ evaluated_docs }} of {{ total_docs }}</strong> documents evaluated ({{ progress }}%)</p>
50
+ </div>
51
+ <div class="progress-bar">
52
+ <div class="progress-fill" style="width: {{ progress }}%;"></div>
53
  </div>
54
  </div>
55
 
56
  <div class="note-container">
57
  <h2>Document Content:</h2>
58
+ <div class="doc-info">
59
+ {% if description %}
60
+ <div class="info-item">
61
+ <strong>Description:</strong> {{ description }}
62
+ </div>
63
+ {% endif %}
64
+ {% if mrn %}
65
+ <div class="info-item mrn-info">
66
+ <strong>MRN:</strong> {{ mrn }}
67
+ </div>
68
+ {% endif %}
69
  </div>
 
70
  <div class="note-content">
71
  {{ note }}
72
  </div>
73
  </div>
74
 
75
  <form method="POST">
 
 
 
 
 
76
  <div class="criteria-container">
77
  {% for i in range(criteria|length) %}
78
  <div class="criteria-group">
 
93
  {% endfor %}
94
  </div>
95
 
96
+ <div class="form-section">
97
+ <h3>Note Origin Assessment</h3>
98
+ <div class="origin-options">
99
+ {% for origin in note_origins %}
100
+ <div class="origin-option">
101
+ <input type="radio" id="origin_{{ loop.index }}" name="note_origin"
102
+ value="{{ origin }}" {% if loop.first %}checked{% endif %}>
103
+ <label for="origin_{{ loop.index }}">{{ origin }}</label>
104
+ </div>
105
+ {% endfor %}
106
+ </div>
107
+ </div>
108
+
109
  <button type="submit" class="submit-btn">Submit Evaluation</button>
110
  </form>
111
 
templates/index.html ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- templates/index.html -->
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <title>Human Notes Evaluator</title>
6
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
7
+ <style>
8
+ .upload-container {
9
+ max-width: 600px;
10
+ margin: 0 auto;
11
+ text-align: center;
12
+ }
13
+
14
+ .upload-form {
15
+ background-color: #f9f9f9;
16
+ padding: 25px;
17
+ border-radius: 8px;
18
+ border: 1px solid #eee;
19
+ margin-top: 30px;
20
+ }
21
+
22
+ .file-input-wrapper {
23
+ margin: 20px 0;
24
+ padding: 20px;
25
+ border: 2px dashed #ccc;
26
+ border-radius: 5px;
27
+ text-align: center;
28
+ transition: all 0.3s;
29
+ }
30
+
31
+ .file-input-wrapper:hover {
32
+ border-color: #3498db;
33
+ }
34
+
35
+ .file-input-wrapper input[type="file"] {
36
+ display: block;
37
+ width: 100%;
38
+ margin-top: 10px;
39
+ }
40
+
41
+ .steps-container {
42
+ margin-bottom: 30px;
43
+ text-align: left;
44
+ }
45
+
46
+ .step {
47
+ margin-bottom: 15px;
48
+ display: flex;
49
+ align-items: flex-start;
50
+ }
51
+
52
+ .step-number {
53
+ display: inline-block;
54
+ width: 25px;
55
+ height: 25px;
56
+ background-color: #3498db;
57
+ color: white;
58
+ border-radius: 50%;
59
+ text-align: center;
60
+ line-height: 25px;
61
+ margin-right: 10px;
62
+ flex-shrink: 0;
63
+ }
64
+
65
+ .step-content {
66
+ flex-grow: 1;
67
+ }
68
+
69
+ .submit-container {
70
+ margin-top: 30px;
71
+ }
72
+
73
+ .preview-info {
74
+ text-align: left;
75
+ margin-top: 15px;
76
+ font-size: 14px;
77
+ color: #666;
78
+ }
79
+
80
+ .preview-table {
81
+ width: 100%;
82
+ border-collapse: collapse;
83
+ margin-top: 10px;
84
+ font-size: 14px;
85
+ }
86
+
87
+ .preview-table th, .preview-table td {
88
+ border: 1px solid #ddd;
89
+ padding: 8px;
90
+ text-align: left;
91
+ }
92
+
93
+ .preview-table th {
94
+ background-color: #f2f2f2;
95
+ }
96
+
97
+ .debug-link {
98
+ margin-top: 20px;
99
+ text-align: center;
100
+ font-size: 12px;
101
+ }
102
+
103
+ .debug-link a {
104
+ color: #999;
105
+ text-decoration: none;
106
+ }
107
+
108
+ .debug-link a:hover {
109
+ text-decoration: underline;
110
+ }
111
+ </style>
112
+ </head>
113
+ <body>
114
+ <div class="container">
115
+ <div class="header">
116
+ <h1>Human Notes Evaluator</h1>
117
+ <div class="header-links">
118
+ <a href="{{ url_for('view_instructions') }}" class="instructions-btn">Instructions</a>
119
+ <a href="{{ url_for('results') }}" class="results-btn">View Results</a>
120
+ </div>
121
+ </div>
122
+
123
+ <div class="upload-container">
124
+ <h2>Welcome to the Human Notes Evaluator</h2>
125
+ <p>This application allows you to evaluate clinical notes based on several quality criteria.</p>
126
+
127
+ <div class="steps-container">
128
+ <h3>Getting Started</h3>
129
+
130
+ <div class="step">
131
+ <span class="step-number">1</span>
132
+ <div class="step-content">
133
+ <strong>Prepare your documents file</strong>
134
+ <p>Create a CSV file with columns for filename, description, MRN, and note content.
135
+ You can <a href="{{ url_for('download_template') }}">download a template</a> to get started.</p>
136
+ </div>
137
+ </div>
138
+
139
+ <div class="step">
140
+ <span class="step-number">2</span>
141
+ <div class="step-content">
142
+ <strong>Enter your name</strong>
143
+ <p>Provide your name as the evaluator so we can track who completed each evaluation.</p>
144
+ </div>
145
+ </div>
146
+
147
+ <div class="step">
148
+ <span class="step-number">3</span>
149
+ <div class="step-content">
150
+ <strong>Upload your documents file</strong>
151
+ <p>Select and upload your CSV file containing the clinical notes to evaluate.</p>
152
+ </div>
153
+ </div>
154
+
155
+ <div class="step">
156
+ <span class="step-number">4</span>
157
+ <div class="step-content">
158
+ <strong>Start evaluating</strong>
159
+ <p>After uploading, you'll be presented with clinical notes to evaluate one by one.</p>
160
+ </div>
161
+ </div>
162
+ </div>
163
+
164
+ <div class="upload-form">
165
+ <h3>Upload Documents and Begin Evaluation</h3>
166
+ <form method="POST" enctype="multipart/form-data" action="{{ url_for('index') }}">
167
+ <div class="form-group">
168
+ <label for="evaluator_name">Your Name (as Evaluator):</label>
169
+ <input type="text" id="evaluator_name" name="evaluator_name" required>
170
+ </div>
171
+
172
+ <div class="file-input-wrapper">
173
+ <label for="file">Select CSV file with documents to evaluate:</label>
174
+ <input type="file" id="file" name="file" accept=".csv" required>
175
+ <div class="preview-info">
176
+ <p>Your CSV file should have these columns:</p>
177
+ <table class="preview-table">
178
+ <thead>
179
+ <tr>
180
+ <th>filename</th>
181
+ <th>description</th>
182
+ <th>mrn</th>
183
+ <th>note</th>
184
+ </tr>
185
+ </thead>
186
+ <tbody>
187
+ <tr>
188
+ <td>progress_note_1.txt</td>
189
+ <td>Primary Care Follow-up Visit</td>
190
+ <td>MRN12345678</td>
191
+ <td>Patient is a 57-year-old male with history of hypertension, type 2 diabetes...</td>
192
+ </tr>
193
+ </tbody>
194
+ </table>
195
+ </div>
196
+ </div>
197
+
198
+ <div class="submit-container">
199
+ <button type="submit" class="submit-btn">Upload & Begin Evaluation</button>
200
+ </div>
201
+ </form>
202
+ </div>
203
+
204
+ {% if session.get('evaluator_name') %}
205
+ <div style="margin-top: 20px;">
206
+ <form method="POST" action="{{ url_for('reset') }}">
207
+ <button type="submit" class="logout-btn">Reset & Start New Evaluation</button>
208
+ </form>
209
+ </div>
210
+ {% endif %}
211
+
212
+ <div class="debug-link">
213
+ <a href="{{ url_for('debug') }}">Debug Information</a>
214
+ </div>
215
+ </div>
216
+
217
+ {% with messages = get_flashed_messages() %}
218
+ {% if messages %}
219
+ {% for message in messages %}
220
+ <div class="alert">{{ message }}</div>
221
+ {% endfor %}
222
+ {% endif %}
223
+ {% endwith %}
224
+
225
+ <div class="resource-links">
226
+ <p>
227
+ <strong>Resources:</strong>
228
+ <a href="{{ url_for('download_instructions') }}">Download Instructions</a> |
229
+ <a href="{{ url_for('download_template') }}">Download Template CSV</a>
230
+ </p>
231
+ </div>
232
+ </div>
233
+ </body>
234
+ </html>
templates/instructions.html CHANGED
@@ -73,7 +73,7 @@
73
  <h1>Instructions</h1>
74
  <div class="header-links">
75
  <a href="{{ url_for('evaluate') }}" class="evaluate-btn">Back to Evaluation</a>
76
- <a href="{{ url_for('logout') }}" class="logout-btn">Logout</a>
77
  </div>
78
  </div>
79
 
@@ -85,10 +85,9 @@
85
  <h2>Contents</h2>
86
  <ol>
87
  <li><a href="#overview">Overview</a></li>
88
- <li><a href="#login-process">Login Process</a></li>
89
- <li><a href="#preparing-documents">Preparing Documents for Evaluation</a></li>
90
  <li><a href="#evaluating-documents">Evaluating Documents</a></li>
91
  <li><a href="#viewing-results">Viewing Results</a></li>
 
92
  <li><a href="#understanding-criteria">Understanding Evaluation Criteria</a></li>
93
  <li><a href="#troubleshooting">Troubleshooting</a></li>
94
  </ol>
@@ -102,15 +101,28 @@
102
  <li>Displays results in a table format</li>
103
  </ul>
104
 
105
- <h2 id="login-process">Login Process</h2>
 
 
 
 
 
 
 
 
 
 
106
  <ol>
107
- <li>Access the application through your browser</li>
108
- <li>Enter the password at the login screen
109
  <ul>
110
- <li>Default password: <code>12345</code> (this can be changed by an administrator)</li>
 
 
 
 
111
  </ul>
112
  </li>
113
- <li>Upon successful login, you will be directed to the evaluation interface</li>
114
  </ol>
115
 
116
  <h2 id="preparing-documents">Preparing Documents for Evaluation</h2>
@@ -126,7 +138,7 @@
126
  </ul>
127
  </li>
128
  <li>Save the file as CSV format</li>
129
- <li>Upload the completed CSV file as <code>documents.csv</code> to replace the existing file</li>
130
  </ol>
131
 
132
  <h3>Example CSV Format:</h3>
@@ -135,31 +147,6 @@ doc1.txt,Patient with heart failure,This is the full text of the first document.
135
  doc2.txt,Emergency room visit,This is the full text of the second document...
136
  doc3.txt,,This is a document without a description...</pre>
137
 
138
- <h2 id="evaluating-documents">Evaluating Documents</h2>
139
- <ol>
140
- <li>After logging in, you'll see a randomly selected document for evaluation</li>
141
- <li>Enter your name in the "Evaluator Name" field</li>
142
- <li>For each criterion, select a rating from 1-5 (Not at all to Extremely)</li>
143
- <li>All criteria must be rated before submission</li>
144
- <li>Click "Submit Evaluation" to record your assessment</li>
145
- <li>The system will then present another random document for evaluation</li>
146
- <li>Continue until all documents have been evaluated</li>
147
- </ol>
148
-
149
- <h2 id="viewing-results">Viewing Results</h2>
150
- <ol>
151
- <li>Click the "View Results" button in the header</li>
152
- <li>Results are displayed in a table showing:
153
- <ul>
154
- <li>Timestamp of evaluation</li>
155
- <li>Document identifier</li>
156
- <li>Document description (if provided)</li>
157
- <li>Evaluator name</li>
158
- <li>Scores for each criterion</li>
159
- </ul>
160
- </li>
161
- </ol>
162
-
163
  <h2 id="understanding-criteria">Understanding Evaluation Criteria</h2>
164
  <p>Documents are evaluated on a 1-5 scale (Not at all = 1, Extremely = 5) across these criteria:</p>
165
 
@@ -178,7 +165,7 @@ doc3.txt,,This is a document without a description...</pre>
178
  <li>
179
  <strong>No documents available for evaluation</strong>
180
  <ul>
181
- <li>Ensure the <code>documents.csv</code> file exists and is properly formatted</li>
182
  <li>Check that not all documents have already been evaluated</li>
183
  </ul>
184
  </li>
@@ -186,14 +173,6 @@ doc3.txt,,This is a document without a description...</pre>
186
  <strong>Unable to submit evaluation</strong>
187
  <ul>
188
  <li>Verify that all criteria have been rated</li>
189
- <li>Check that you've entered an evaluator name</li>
190
- </ul>
191
- </li>
192
- <li>
193
- <strong>Login issues</strong>
194
- <ul>
195
- <li>Confirm you're using the correct password</li>
196
- <li>Contact the administrator if the password doesn't work</li>
197
  </ul>
198
  </li>
199
  <li>
 
73
  <h1>Instructions</h1>
74
  <div class="header-links">
75
  <a href="{{ url_for('evaluate') }}" class="evaluate-btn">Back to Evaluation</a>
76
+ <a href="{{ url_for('index') }}" class="evaluate-btn">Back to Home</a>
77
  </div>
78
  </div>
79
 
 
85
  <h2>Contents</h2>
86
  <ol>
87
  <li><a href="#overview">Overview</a></li>
 
 
88
  <li><a href="#evaluating-documents">Evaluating Documents</a></li>
89
  <li><a href="#viewing-results">Viewing Results</a></li>
90
+ <li><a href="#preparing-documents">Preparing Documents for Evaluation</a></li>
91
  <li><a href="#understanding-criteria">Understanding Evaluation Criteria</a></li>
92
  <li><a href="#troubleshooting">Troubleshooting</a></li>
93
  </ol>
 
101
  <li>Displays results in a table format</li>
102
  </ul>
103
 
104
+ <h2 id="evaluating-documents">Evaluating Documents</h2>
105
+ <ol>
106
+ <li>Enter your name in the "Evaluator Name" field</li>
107
+ <li>For each criterion, select a rating from 1-5 (Not at all to Extremely)</li>
108
+ <li>All criteria must be rated before submission</li>
109
+ <li>Click "Submit Evaluation" to record your assessment</li>
110
+ <li>The system will then present another random document for evaluation</li>
111
+ <li>Continue until all documents have been evaluated</li>
112
+ </ol>
113
+
114
+ <h2 id="viewing-results">Viewing Results</h2>
115
  <ol>
116
+ <li>Click the "View Results" button in the header</li>
117
+ <li>Results are displayed in a table showing:
118
  <ul>
119
+ <li>Timestamp of evaluation</li>
120
+ <li>Document identifier</li>
121
+ <li>Document description (if provided)</li>
122
+ <li>Evaluator name</li>
123
+ <li>Scores for each criterion</li>
124
  </ul>
125
  </li>
 
126
  </ol>
127
 
128
  <h2 id="preparing-documents">Preparing Documents for Evaluation</h2>
 
138
  </ul>
139
  </li>
140
  <li>Save the file as CSV format</li>
141
+ <li>Upload the completed CSV file using the file upload on the home page</li>
142
  </ol>
143
 
144
  <h3>Example CSV Format:</h3>
 
147
  doc2.txt,Emergency room visit,This is the full text of the second document...
148
  doc3.txt,,This is a document without a description...</pre>
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  <h2 id="understanding-criteria">Understanding Evaluation Criteria</h2>
151
  <p>Documents are evaluated on a 1-5 scale (Not at all = 1, Extremely = 5) across these criteria:</p>
152
 
 
165
  <li>
166
  <strong>No documents available for evaluation</strong>
167
  <ul>
168
+ <li>Ensure the CSV file you uploaded exists and is properly formatted</li>
169
  <li>Check that not all documents have already been evaluated</li>
170
  </ul>
171
  </li>
 
173
  <strong>Unable to submit evaluation</strong>
174
  <ul>
175
  <li>Verify that all criteria have been rated</li>
 
 
 
 
 
 
 
 
176
  </ul>
177
  </li>
178
  <li>
templates/no_documents.html CHANGED
@@ -2,20 +2,114 @@
2
  <!DOCTYPE html>
3
  <html>
4
  <head>
5
- <title>No Documents Available</title>
6
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  </head>
8
  <body>
9
  <div class="container">
10
  <div class="header">
11
- <h1>Document Evaluation Complete</h1>
12
- <a href="{{ url_for('logout') }}" class="logout-btn">Logout</a>
 
 
 
 
13
  </div>
14
-
15
- <div class="message-container">
16
- <p>All available documents have been evaluated. Thank you for your participation!</p>
17
- <p>You can find the evaluation results in the evaluations.csv file.</p>
 
 
 
 
18
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </div>
20
  </body>
21
  </html>
 
2
  <!DOCTYPE html>
3
  <html>
4
  <head>
5
+ <title>No Documents Available - Human Notes Evaluator</title>
6
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
7
+ <style>
8
+ .help-section {
9
+ background-color: #f8f9fa;
10
+ padding: 20px;
11
+ border-radius: 5px;
12
+ margin-top: 20px;
13
+ border-left: 4px solid #3498db;
14
+ }
15
+ .help-section h2 {
16
+ margin-top: 0;
17
+ color: #2c3e50;
18
+ }
19
+ .code-block {
20
+ background-color: #f1f1f1;
21
+ padding: 15px;
22
+ border-radius: 5px;
23
+ font-family: monospace;
24
+ white-space: pre-wrap;
25
+ margin: 15px 0;
26
+ }
27
+ .step {
28
+ margin-bottom: 15px;
29
+ }
30
+ .step-number {
31
+ display: inline-block;
32
+ width: 25px;
33
+ height: 25px;
34
+ background-color: #3498db;
35
+ color: white;
36
+ border-radius: 50%;
37
+ text-align: center;
38
+ line-height: 25px;
39
+ margin-right: 10px;
40
+ }
41
+ </style>
42
  </head>
43
  <body>
44
  <div class="container">
45
  <div class="header">
46
+ <h1>No Documents Available</h1>
47
+ <div class="header-links">
48
+ <a href="{{ url_for('view_instructions') }}" class="instructions-btn">Instructions</a>
49
+ <a href="{{ url_for('results') }}" class="results-btn">View Results</a>
50
+ <a href="{{ url_for('index') }}" class="evaluate-btn">Back to Home</a>
51
+ </div>
52
  </div>
53
+
54
+ <div class="alert">
55
+ <p><strong>No documents are available for evaluation.</strong> This could be because:</p>
56
+ <ul>
57
+ <li>The CSV file you uploaded is missing or has an incorrect format</li>
58
+ <li>All documents have already been evaluated</li>
59
+ <li>There is an error in the format of your CSV file</li>
60
+ </ul>
61
  </div>
62
+
63
+ <div class="help-section">
64
+ <h2>How to Add Documents for Evaluation</h2>
65
+
66
+ <div class="step">
67
+ <span class="step-number">1</span>
68
+ <strong>Download the template file:</strong>
69
+ <a href="{{ url_for('download_template') }}">sample_documents_template.csv</a>
70
+ </div>
71
+
72
+ <div class="step">
73
+ <span class="step-number">2</span>
74
+ <strong>Open the template in a spreadsheet program</strong> (Excel, Google Sheets, etc.)
75
+ </div>
76
+
77
+ <div class="step">
78
+ <span class="step-number">3</span>
79
+ <strong>Add your documents using the following format:</strong>
80
+ <div class="code-block">filename,description,note
81
+ "doc1.txt","Patient with heart failure","The patient is a 67-year-old male with a history of chronic heart failure who presented with increasing dyspnea on exertion."
82
+ "doc2.txt","Emergency department visit","32-year-old female with history of asthma presented to the ED with acute onset of wheezing and shortness of breath."</div>
83
+ <p><em>Note: Make sure to properly quote text fields that contain commas</em></p>
84
+ </div>
85
+
86
+ <div class="step">
87
+ <span class="step-number">4</span>
88
+ <strong>Save the file as CSV format</strong>
89
+ </div>
90
+
91
+ <div class="step">
92
+ <span class="step-number">5</span>
93
+ <strong>Return to the home page and upload your file</strong>
94
+ <p><a href="{{ url_for('index') }}" class="submit-btn">Go to Home Page</a></p>
95
+ </div>
96
+ </div>
97
+
98
+ <div class="resource-links">
99
+ <p>
100
+ <strong>Resources:</strong>
101
+ <a href="{{ url_for('download_instructions') }}">Download Instructions</a> |
102
+ <a href="{{ url_for('download_template') }}">Download Template CSV</a>
103
+ </p>
104
+ </div>
105
+
106
+ {% with messages = get_flashed_messages() %}
107
+ {% if messages %}
108
+ {% for message in messages %}
109
+ <div class="alert">{{ message }}</div>
110
+ {% endfor %}
111
+ {% endif %}
112
+ {% endwith %}
113
  </div>
114
  </body>
115
  </html>
templates/results.html CHANGED
@@ -5,22 +5,64 @@
5
  <title>Evaluation Results</title>
6
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
7
  <style>
 
 
 
 
 
8
  table {
9
  width: 100%;
10
  border-collapse: collapse;
11
  margin-top: 20px;
 
12
  }
 
13
  th, td {
14
  padding: 10px;
15
  border: 1px solid #ddd;
16
  text-align: left;
 
17
  }
 
18
  th {
19
  background-color: #f2f2f2;
 
 
 
20
  }
 
21
  tr:nth-child(even) {
22
  background-color: #f9f9f9;
23
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  </style>
25
  </head>
26
  <body>
@@ -29,38 +71,64 @@
29
  <h1>Evaluation Results</h1>
30
  <div class="header-links">
31
  <a href="{{ url_for('evaluate') }}" class="evaluate-btn">Continue Evaluating</a>
32
- <a href="{{ url_for('logout') }}" class="logout-btn">Logout</a>
 
33
  </div>
34
  </div>
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  <div class="results-container">
37
  <h2>Results Table</h2>
38
- <table>
39
- <thead>
40
- <tr>
41
- <th>Timestamp</th>
42
- <th>Document</th>
43
- <th>Description</th>
44
- <th>Evaluator</th>
45
- {% for criterion in criteria %}
46
- <th>{{ criterion }}</th>
47
- {% endfor %}
48
- </tr>
49
- </thead>
50
- <tbody>
51
- {% for eval in evaluations %}
52
- <tr>
53
- <td>{{ eval.timestamp }}</td>
54
- <td>{{ eval.document_title }}</td>
55
- <td>{{ eval.description }}</td>
56
- <td>{{ eval.investigator_name }}</td>
57
- {% for criterion in criteria %}
58
- <td>{{ eval[criterion] }}</td>
 
 
 
 
 
 
 
59
  {% endfor %}
60
- </tr>
61
- {% endfor %}
62
- </tbody>
63
- </table>
 
 
 
 
 
 
 
64
  </div>
65
 
66
  {% with messages = get_flashed_messages() %}
 
5
  <title>Evaluation Results</title>
6
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
7
  <style>
8
+ .results-container {
9
+ overflow-x: auto; /* Add horizontal scrolling for the table */
10
+ margin-top: 20px;
11
+ }
12
+
13
  table {
14
  width: 100%;
15
  border-collapse: collapse;
16
  margin-top: 20px;
17
+ min-width: 800px; /* Set a minimum width so smaller screens will scroll */
18
  }
19
+
20
  th, td {
21
  padding: 10px;
22
  border: 1px solid #ddd;
23
  text-align: left;
24
+ font-size: 0.9em; /* Slightly smaller font size */
25
  }
26
+
27
  th {
28
  background-color: #f2f2f2;
29
+ position: sticky;
30
+ top: 0;
31
+ z-index: 10;
32
  }
33
+
34
  tr:nth-child(even) {
35
  background-color: #f9f9f9;
36
  }
37
+
38
+ .export-btn {
39
+ margin-top: 20px;
40
+ margin-bottom: 20px;
41
+ text-align: right;
42
+ }
43
+
44
+ .export-btn a {
45
+ background-color: #28a745;
46
+ padding: 10px 15px;
47
+ color: white;
48
+ border-radius: 4px;
49
+ text-decoration: none;
50
+ display: inline-flex;
51
+ align-items: center;
52
+ gap: 5px;
53
+ }
54
+
55
+ .export-btn a:hover {
56
+ background-color: #218838;
57
+ }
58
+
59
+ /* Icon for export button */
60
+ .export-icon {
61
+ width: 16px;
62
+ height: 16px;
63
+ display: inline-block;
64
+ margin-right: 5px;
65
+ }
66
  </style>
67
  </head>
68
  <body>
 
71
  <h1>Evaluation Results</h1>
72
  <div class="header-links">
73
  <a href="{{ url_for('evaluate') }}" class="evaluate-btn">Continue Evaluating</a>
74
+ <a href="{{ url_for('view_instructions') }}" class="instructions-btn">Instructions</a>
75
+ <a href="{{ url_for('index') }}" class="evaluate-btn">Back to Home</a>
76
  </div>
77
  </div>
78
+
79
+ <div class="export-btn">
80
+ <a href="{{ url_for('export_csv') }}">
81
+ <svg class="export-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
82
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
83
+ <polyline points="7 10 12 15 17 10"></polyline>
84
+ <line x1="12" y1="15" x2="12" y2="3"></line>
85
+ </svg>
86
+ Export to CSV
87
+ </a>
88
+ </div>
89
 
90
  <div class="results-container">
91
  <h2>Results Table</h2>
92
+ <div class="table-wrapper">
93
+ <table>
94
+ <thead>
95
+ <tr>
96
+ <th>Timestamp</th>
97
+ <th>Document</th>
98
+ <th>Description</th>
99
+ <th>MRN</th>
100
+ <th>Evaluator</th>
101
+ {% for criterion in criteria %}
102
+ <th>{{ criterion }}</th>
103
+ {% endfor %}
104
+ <th>Note Origin Assessment</th>
105
+ </tr>
106
+ </thead>
107
+ <tbody>
108
+ {% for eval in evaluations %}
109
+ <tr>
110
+ <td>{{ eval.timestamp }}</td>
111
+ <td>{{ eval.document_title }}</td>
112
+ <td>{{ eval.description }}</td>
113
+ <td>{{ eval.mrn }}</td>
114
+ <td>{{ eval.investigator_name }}</td>
115
+ {% for criterion in criteria %}
116
+ <td>{{ eval[criterion] }}</td>
117
+ {% endfor %}
118
+ <td>{{ eval.note_origin }}</td>
119
+ </tr>
120
  {% endfor %}
121
+ </tbody>
122
+ </table>
123
+ </div>
124
+ </div>
125
+
126
+ <div class="resource-links">
127
+ <p>
128
+ <strong>Resources:</strong>
129
+ <a href="{{ url_for('download_instructions') }}">Download Instructions</a> |
130
+ <a href="{{ url_for('download_template') }}">Download Template CSV</a>
131
+ </p>
132
  </div>
133
 
134
  {% with messages = get_flashed_messages() %}