dalinstone commited on
Commit
c99f844
Β·
verified Β·
1 Parent(s): 878b015

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -19
app.py CHANGED
@@ -9,6 +9,7 @@ from typing import List, Dict, Optional
9
  import os
10
  from pathlib import Path
11
  import time
 
12
 
13
  GRADING_RUBRIC = """
14
  DETAILED GRADING RUBRIC & INSTRUCTIONS
@@ -281,6 +282,13 @@ Your output should be a single JSON format structure response as indicated by th
281
  **Use the following rubric to grade the essay:**
282
  {GRADING_RUBRIC}
283
 
 
 
 
 
 
 
 
284
  **Instructions:**
285
  1. Read the entire essay provided below.
286
  2. Assess the essay against each category in the rubric.
@@ -346,6 +354,20 @@ class EssayParser:
346
  raise IOError(
347
  f"Could not read file: {os.path.basename(file_path)}. Error: {e}")
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
  class GeminiGrader:
351
  """Manages interaction with the Google Gemini API for grading."""
@@ -382,6 +404,12 @@ class GeminiGrader:
382
  Sends the essay to Gemini for grading and parses the JSON response.
383
  This is a synchronous method designed to be run in a thread pool.
384
  """
 
 
 
 
 
 
385
  prompt_with_essay = f"{GEMINI_PROMPT}\n{essay_text}"
386
  try:
387
  response = self.model.generate_content(prompt_with_essay)
@@ -421,7 +449,7 @@ class GeminiGrader:
421
  # --- Gradio Application ---
422
 
423
  async def grade_papers_concurrently(
424
- files: List[gr.File], api_key: str, progress=gr.Progress(track_tqdm=True)
425
  ) -> (str, str):
426
  """
427
  The main asynchronous function that orchestrates the grading process.
@@ -434,6 +462,15 @@ async def grade_papers_concurrently(
434
  if not files:
435
  raise gr.Error("Please upload at least one Word document.")
436
 
 
 
 
 
 
 
 
 
 
437
  try:
438
  grader = GeminiGrader(api_key)
439
  except ValueError as e:
@@ -451,7 +488,8 @@ async def grade_papers_concurrently(
451
  executor,
452
  process_single_file,
453
  file_path,
454
- grader
 
455
  )
456
  for file_path in file_paths
457
  ]
@@ -502,7 +540,7 @@ async def grade_papers_concurrently(
502
  return output_markdown, f"{status}\n{runtime}"
503
 
504
 
505
- def process_single_file(file_path: str, grader: GeminiGrader) -> GradingResult:
506
  """
507
  Synchronous wrapper function to parse and grade one file.
508
  This function is what runs in each thread of the ThreadPoolExecutor.
@@ -516,7 +554,7 @@ def process_single_file(file_path: str, grader: GeminiGrader) -> GradingResult:
516
  success=False,
517
  error_message="The document is empty or contains no readable text."
518
  )
519
- return grader.grade_essay(essay_text, file_name)
520
  except Exception as e:
521
  return GradingResult(file_name=file_name, success=False, error_message=str(e))
522
 
@@ -529,11 +567,11 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Nursing Essay Grader") as demo:
529
  # πŸ“ Gemini-Powered Nursing Essay Grader
530
  Upload one or more student essays in Word format (`.docx`) to have them graded by AI.
531
  1. Enter your Google API Key (enabling the Gemini API in your Google Cloud project is required).
532
- 2. Upload the `.docx` files.
533
- 3. Click "Grade All Papers". The results will appear below.
 
534
  """
535
  )
536
-
537
  with gr.Row():
538
  api_key_input = gr.Textbox(
539
  label="Google API Key",
@@ -541,21 +579,26 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Nursing Essay Grader") as demo:
541
  type="password",
542
  scale=1
543
  )
544
-
545
- file_uploads = gr.File(
546
- label="Upload Word Document Essays",
547
- file_count="multiple",
548
- file_types=[".docx"],
549
- type="filepath" # Use filepath for easier handling
550
- )
 
 
 
 
 
 
 
 
551
 
552
  grade_button = gr.Button("πŸš€ Grade All Papers", variant="primary")
553
-
554
  gr.Markdown("---")
555
  gr.Markdown("## πŸ“Š Grading Results")
556
-
557
  results_output = gr.Markdown(label="Formatted Grades")
558
-
559
  status_output = gr.Textbox(
560
  label="Runtime Status",
561
  lines=2,
@@ -564,9 +607,9 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Nursing Essay Grader") as demo:
564
 
565
  grade_button.click(
566
  fn=grade_papers_concurrently,
567
- inputs=[file_uploads, api_key_input],
568
  outputs=[results_output, status_output]
569
  )
570
 
571
  if __name__ == "__main__":
572
- demo.launch(debug=True)
 
9
  import os
10
  from pathlib import Path
11
  import time
12
+ import fitz
13
 
14
  GRADING_RUBRIC = """
15
  DETAILED GRADING RUBRIC & INSTRUCTIONS
 
282
  **Use the following rubric to grade the essay:**
283
  {GRADING_RUBRIC}
284
 
285
+ **For your reference, here is an example of a perfectly formatted paper. Use it as a guide for what correct formatting looks like:**
286
+ ---
287
+ **EXAMPLE PAPER START**
288
+ {EXAMPLE_PAPER}
289
+ **EXAMPLE PAPER END**
290
+ ---
291
+
292
  **Instructions:**
293
  1. Read the entire essay provided below.
294
  2. Assess the essay against each category in the rubric.
 
354
  raise IOError(
355
  f"Could not read file: {os.path.basename(file_path)}. Error: {e}")
356
 
357
+ @staticmethod
358
+ def parse_pdf(file_path: str) -> str:
359
+ """Extracts all text from a PDF document."""
360
+ try:
361
+ doc = fitz.open(file_path)
362
+ text = ""
363
+ for page in doc:
364
+ text += page.get_text()
365
+ return text
366
+ except Exception as e:
367
+ raise IOError(
368
+ f"Could not read PDF file: {os.path.basename(file_path)}. Error: {e}")
369
+
370
+
371
 
372
  class GeminiGrader:
373
  """Manages interaction with the Google Gemini API for grading."""
 
404
  Sends the essay to Gemini for grading and parses the JSON response.
405
  This is a synchronous method designed to be run in a thread pool.
406
  """
407
+ # Inject the rubric and the example paper text into the main prompt template
408
+ final_prompt = BASE_PROMPT_TEMPLATE.format(
409
+ GRADING_RUBRIC=GRADING_RUBRIC,
410
+ EXAMPLE_PAPER=example_paper_text
411
+ )
412
+
413
  prompt_with_essay = f"{GEMINI_PROMPT}\n{essay_text}"
414
  try:
415
  response = self.model.generate_content(prompt_with_essay)
 
449
  # --- Gradio Application ---
450
 
451
  async def grade_papers_concurrently(
452
+ files: List[gr.File], example_paper_file: gr.File, api_key: str, progress=gr.Progress(track_tqdm=True)
453
  ) -> (str, str):
454
  """
455
  The main asynchronous function that orchestrates the grading process.
 
462
  if not files:
463
  raise gr.Error("Please upload at least one Word document.")
464
 
465
+ # Process the optional example paper
466
+ example_paper_text = "No example paper provided."
467
+ if example_paper_file:
468
+ try:
469
+ progress(0, desc="Parsing example paper...")
470
+ example_paper_text = EssayParser.parse_pdf(example_paper_file.name)
471
+ except IOError as e:
472
+ raise gr.Error(f"Failed to process example PDF: {e}")
473
+
474
  try:
475
  grader = GeminiGrader(api_key)
476
  except ValueError as e:
 
488
  executor,
489
  process_single_file,
490
  file_path,
491
+ grader,
492
+ example_paper_text
493
  )
494
  for file_path in file_paths
495
  ]
 
540
  return output_markdown, f"{status}\n{runtime}"
541
 
542
 
543
+ def process_single_file(file_path: str, grader: GeminiGrader, example_paper_text: str) -> GradingResult:
544
  """
545
  Synchronous wrapper function to parse and grade one file.
546
  This function is what runs in each thread of the ThreadPoolExecutor.
 
554
  success=False,
555
  error_message="The document is empty or contains no readable text."
556
  )
557
+ return grader.grade_essay(essay_text, file_name, example_paper_text)
558
  except Exception as e:
559
  return GradingResult(file_name=file_name, success=False, error_message=str(e))
560
 
 
567
  # πŸ“ Gemini-Powered Nursing Essay Grader
568
  Upload one or more student essays in Word format (`.docx`) to have them graded by AI.
569
  1. Enter your Google API Key (enabling the Gemini API in your Google Cloud project is required).
570
+ 2. Upload the `.docx` files you want to grade.
571
+ 3. Optionally, upload a single `.pdf` file as a "perfect" example for the AI to reference.
572
+ 4. Click "Grade All Papers". The results will appear below.
573
  """
574
  )
 
575
  with gr.Row():
576
  api_key_input = gr.Textbox(
577
  label="Google API Key",
 
579
  type="password",
580
  scale=1
581
  )
582
+ with gr.Row():
583
+ file_uploads = gr.File(
584
+ label="Upload Word Document Essays to Grade",
585
+ file_count="multiple",
586
+ file_types=[".docx"],
587
+ type="filepath",
588
+ scale=2
589
+ )
590
+ example_paper_upload = gr.File(
591
+ label="Upload Example PDF Paper (Optional)",
592
+ file_count="single",
593
+ file_types=[".pdf"],
594
+ type="filepath",
595
+ scale=1
596
+ )
597
 
598
  grade_button = gr.Button("πŸš€ Grade All Papers", variant="primary")
 
599
  gr.Markdown("---")
600
  gr.Markdown("## πŸ“Š Grading Results")
 
601
  results_output = gr.Markdown(label="Formatted Grades")
 
602
  status_output = gr.Textbox(
603
  label="Runtime Status",
604
  lines=2,
 
607
 
608
  grade_button.click(
609
  fn=grade_papers_concurrently,
610
+ inputs=[file_uploads, example_paper_upload, api_key_input],
611
  outputs=[results_output, status_output]
612
  )
613
 
614
  if __name__ == "__main__":
615
+ demo.launch(debug=True)