dalinstone commited on
Commit
fbffb33
·
verified ·
1 Parent(s): 5c6f384

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +187 -203
app.py CHANGED
@@ -9,112 +9,176 @@ import os
9
  from pathlib import Path
10
  import time
11
 
 
12
  GRADING_RUBRIC = """
13
- GRADING RUBRIC (Total 100 points)
14
-
15
- I. APA Formatting (60 pts)
16
- 1. APA Title Page (5 pts): Requires 9 components: (1) Page number (1), (2) Title (bold), (3) Author, (4) Department, (5) University, (6) Course, (7) Instructor, (8) Due Date, (9) Centered/spaced correctly.
17
- - Scoring: 5 pts: All 9 correct. 0 pts: 1+ error.
18
- 2. APA General Guidelines (5 pts): Requires (1) Typed, (2) 1" margins, (3) 11pt Calibri or 12pt TNR, (4) Double-spaced, (5) Left-aligned, (6) Paragraphs indented 0.5".
19
- - Scoring: 5 pts: All 6 met. 2 pts: 1 missing. 0 pts: 2+ missing.
20
- 3. APA Text - Misc. Errors (5 pts): Check for: (1) Extra paragraph spacing, (2) Misspellings, (3) Typos.
21
- - Scoring: 5 pts: 0 errors. 4 pts: 1 error. 0 pts: 2+ errors.
22
- 4. APA In-text Citations - Presence (5 pts): Count missing citations for all non-common knowledge facts/opinions.
23
- - Scoring: 5 pts: 0 missing. 3.5 pts: 1-5 missing. 0 pts: 6+ missing.
24
- 5. APA In-text Citation - Formatting (5 pts): Check all in-text citations for correct APA 7 format.
25
- - Scoring: 5 pts: 0 errors. 2 pts: 1-2 errors. 0 pts: 3+ errors.
26
- 6. Ref Page - General Formatting (5 pts): Requires (1) "References" title (bold, centered, new page), (2) Alphabetical order, (3) 0.5" hanging indent.
27
- - Scoring: 5 pts: All 3 correct. 0 pts: 1+ error.
28
- 7. Ref Page - Line Spacing (5 pts): Entire page must be uniformly double-spaced.
29
- - Scoring: 5 pts: Correct. 0 pts: Any error.
30
- 8. References - Scholarly & Timely (10 pts): Must have >=3 scholarly sources, all dated within 7 years (after July 19, 2018).
31
- - Scoring: 10 pts: Both conditions met. 0 pts: Fails one or both.
32
- 9. References - Author Names (5 pts): Check all author names for correct APA 7 format (e.g., Lastname, F. M.) and correct handling of no-author sources.
33
- - Scoring: 5 pts: 0 errors. 2 pts: 1 error of author vs. title/publisher. 0 pts: General format errors.
34
- 10. References - Dates (5 pts): Check reference dates for correct APA 7 format.
35
- - Scoring: 5 pts: 0 errors. 3 pts: 1 error. 0 pts: 2+ errors.
36
- 11. References - Capitalization (5 pts): Check reference titles for APA 7 capitalization (sentence case for articles/books; title case for journals).
37
- - Scoring: 5 pts: 0 errors. 3 pts: 1 error. 0 pts: 2+ errors.
38
- 12. References - Italics (5 pts): Check references for correct APA 7 italics (journal titles/volumes, book titles).
39
- - Scoring: 5 pts: 0-1 error. 0 pts: 2+ errors.
40
- 13. References - Hyperlinks (5 pts): Requires (1) DOIs as active hyperlinks, (2) No "Retrieved from", (3) No period after URL/DOI, (4) Non-DOI URLs included.
41
- - Scoring: 5 pts: All 4 rules met. 2 pts: 1 rule fails. 0 pts: 2+ rules fail.
42
-
43
- II. Content (40 pts)
44
- 14. Introduction (10 pts): Must be 1-2 paragraphs, introduce the topic, be <=1 page long.
45
- - Scoring: 10 pts: Meets reqs, 0 typos. 5 pts: Meets reqs, 1-2 typos. 0 pts: Fails reqs OR 3+ typos.
46
- 15. Common Causes (10 pts): Discusses at least one common cause of skin lesions.
47
- - Scoring: 10 pts: Yes. 0 pts: No.
48
- 16. Nursing Considerations (10 pts): Discusses at least one nursing intervention/consideration.
49
- - Scoring: 10 pts: Yes. 0 pts: No.
50
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  REQUIRED OUTPUT FORMAT
52
  You must present your final evaluation in the following structured format. Do not deviate from this format.
53
-
54
  Grading Evaluation: NURS 305 Essay - Skin Lesions
55
  Student Submission Analysis
56
  Final Score: [Total Points] / 100
57
-
58
  Part I: APA Formatting (Score: [Points] / 60)
59
- 1. APA Title Page: [Score]/5. Justification: [Provide specific justification for the score.]
60
- 2. APA General Guidelines: [Score]/5. Justification: [Provide specific justification for the score.]
61
- 3. APA Text - Misc. Errors: [Score]/5. Justification: [Provide specific justification for the score.]
62
- 4. APA In-text Citations - Presence: [Score]/5. Justification: [Provide specific justification for the score.]
63
- 5. APA In-text Citation - Formatting: [Score]/5. Justification: [Provide specific justification for the score.]
64
- 6. APA Reference Page - General Formatting: [Score]/5. Justification: [Provide specific justification for the score.]
65
- 7. APA Reference Page - Line Spacing: [Score]/5. Justification: [Provide specific justification for the score.]
66
- 8. APA References - Scholarly & Timely: [Score]/10. Justification: [Provide specific justification for the score.]
67
- 9. APA References - Author Names: [Score]/5. Justification: [Provide specific justification for the score.]
68
- 10. APA References - Dates: [Score]/5. Justification: [Provide specific justification for the score.]
69
- 11. APA References - Capitalization: [Score]/5. Justification: [Provide specific justification for the score.]
70
- 12. APA References - Italics: [Score]/5. Justification: [Provide specific justification for the score.]
71
- 13. APA References - Hyperlinks: [Score]/5. Justification: [Provide specific justification for the score.]
72
-
73
  Part II: Content (Score: [Points] / 40)
74
- 14. Introduction: [Score]/10. Justification: [Provide specific justification for the score.]
75
- 15. Common Causes: [Score]/10. Justification: [Provide specific justification for the score.]
76
- 16. Nursing Considerations: [Score]/10. Justification: [Provide specific justification for the score.]
77
-
78
  Professor's Summary:
79
- [Provide a 2-3 sentence summary in the persona of Dr. Vance, focusing on overall performance and key areas for improvement.]
80
-
81
  """
82
 
 
83
  BASE_PROMPT_TEMPLATE = """
84
- You are about to assume a role. Carefully review the persona, context, task, and output requirements before proceeding.
85
 
86
- 1. PERSONA
87
- You are Dr. Stone, a meticulous and experienced Associate Professor of Nursing at a major university. You have been teaching for over 20 years, with a specialization in medical-surgical nursing. You are known for your high standards, particularly regarding academic integrity and the strict application of the American Psychological Association (APA) 7th Edition formatting guidelines. Your feedback is always direct, precise, and aimed at preparing students for the rigorous documentation standards required in the healthcare profession. Your tone is professional, authoritative, and educational. You do not offer praise for meeting baseline expectations; you simply state that the requirements have been met. Your criticism is specific, referencing the exact rule or rubric criterion that was violated.
88
 
89
- 2. CONTEXT
90
- You are grading an essay for your undergraduate course, NURS 310: Fundamentals of Nursing. The assignment is an essay on the topic of "Skin Lesions." You will be provided with the student's paper as an attached file, and optionally an example paper file for reference.
 
 
 
91
 
92
- 3. PRIMARY TASK
93
- Your task is to analyze the attached student paper file and grade it with absolute precision according to the provided rubric. You must function as a perfect and unflinchingly accurate grader. There is no room for subjective interpretation or leniency. You will identify every error in content, formatting, spelling, grammar, and citation, and assign points strictly based on the rubric's quantitative thresholds.
94
-
95
- Your output should be a single JSON format structure response as indicated by the example below.
96
-
97
- **DO NOT** provide any introductory text, conversational pleasantries, or explanations outside of the requested JSON structure. Your entire response must be a single, valid JSON object.
98
-
99
- **Use the following rubric to grade the attached student paper:**
100
  {GRADING_RUBRIC}
101
 
102
- **Example of the required JSON output format:**
 
 
 
 
103
  {{
104
- "finalGrade": 88,
105
- "pointDeductions": {{
106
- "Content & Analysis": 2,
107
- "Organization & Structure": 0,
108
- "APA Formatting & Citations": 8,
109
- "Clarity & Mechanics": 2
110
- }},
111
- "feedback": {{
112
- "Content & Analysis": "The thesis was slightly unfocused, but the evidence used was strong.",
113
- "Organization & Structure": "No points deducted.",
114
- "APA Formatting & Citations": "Multiple errors in the reference list formatting and three missing in-text citations.",
115
- "Clarity & Mechanics": "Minor grammatical errors and occasional awkward phrasing."
116
- }},
117
- "summary": "This is a strong paper with excellent critical analysis. The final grade was primarily impacted by significant APA formatting errors, which should be the main focus for improvement."
 
 
 
 
 
 
 
 
118
  }}
119
  """
120
 
@@ -123,154 +187,96 @@ class GradingResult:
123
  """Holds the structured result of a single graded essay."""
124
  file_name: str
125
  success: bool
126
- grade: Optional[int] = None
127
- deductions: Dict[str, int] = field(default_factory=dict)
128
- feedback: Dict[str, str] = field(default_factory=dict)
129
- summary: Optional[str] = None
130
  error_message: Optional[str] = None
131
 
132
  class GeminiGrader:
133
  """Manages interaction with the Google Gemini API for grading."""
134
  def __init__(self, api_key: str):
135
- print("[DEBUG] Initializing GeminiGrader...")
136
  genai.configure(api_key=api_key)
137
  self.model = genai.GenerativeModel(model_name="gemini-1.5-pro-latest")
138
- print("[DEBUG] GeminiGrader initialized successfully.")
139
 
140
  def grade_essay(self, student_paper_file: object, example_paper_file: Optional[object]) -> GradingResult:
141
  """Sends files to Gemini for grading and parses the JSON response."""
142
- print(f"[DEBUG] Inside grade_essay for student paper: {student_paper_file.display_name}")
143
  prompt_text = BASE_PROMPT_TEMPLATE.format(GRADING_RUBRIC=GRADING_RUBRIC)
144
-
145
- content_list = [prompt_text, student_paper_file]
146
  if example_paper_file:
147
- print(f"[DEBUG] Example paper '{example_paper_file.display_name}' is being used.")
148
- content_list.append(example_paper_file)
149
 
150
  try:
151
- print(f"[DEBUG] >>> Calling the Gemini model for grading... THIS IS THE LONG WAIT. <<<")
152
- response = self.model.generate_content(content_list, request_options={'timeout': 600}) # 10 minute timeout
153
- print(f"[DEBUG] <<< Model response received for {student_paper_file.display_name}. Parsing JSON.")
154
  cleaned_response = response.text.strip().replace("```json", "").replace("```", "")
155
  data = json.loads(cleaned_response)
156
 
157
- required_keys = ["finalGrade", "pointDeductions", "feedback", "summary"]
158
- if not all(key in data for key in required_keys):
159
- raise KeyError("Model response missing required keys.")
160
-
161
- print(f"[DEBUG] Successfully parsed response for {student_paper_file.display_name}.")
162
  return GradingResult(
163
  file_name=student_paper_file.display_name,
164
  success=True,
165
- grade=data["finalGrade"],
166
- deductions=data["pointDeductions"],
167
- feedback=data["feedback"],
168
- summary=data["summary"]
169
  )
170
  except json.JSONDecodeError as e:
171
- print("\n" + "="*20 + " JSON PARSE ERROR " + "="*20)
172
- print(f"[DEBUG] The model returned a malformed JSON. Error: {e}")
173
- print("[DEBUG] --- FAULTY RESPONSE FROM MODEL ---")
174
- print(cleaned_response)
175
- print("[DEBUG] --- END OF FAULTY RESPONSE ---")
176
- print("="*58 + "\n")
177
- return GradingResult(
178
- file_name=student_paper_file.display_name,
179
- success=False,
180
- error_message=f"Model returned malformed JSON. Details: {e}"
181
- )
182
-
183
  except Exception as e:
184
- print(f"[DEBUG] !!! An error occurred in grade_essay: {e}")
185
- return GradingResult(
186
- file_name=student_paper_file.display_name,
187
- success=False,
188
- error_message=f"An API or model error occurred: {str(e)}"
189
- )
190
 
191
  def process_single_file(file_path: str, grader: GeminiGrader, example_paper_file_obj: Optional[object]) -> GradingResult:
192
  """Uploads a single student paper and calls the grader."""
193
- file_name = os.path.basename(file_path)
194
- print(f"[DEBUG] Starting process_single_file for: {file_name}")
195
  student_paper_file_obj = None
196
  try:
197
- print(f"[DEBUG] -> Uploading student paper '{file_name}' to Gemini Files API...")
198
- student_paper_file_obj = genai.upload_file(path=file_path, display_name=file_name)
199
- print(f"[DEBUG] -> Upload successful for '{file_name}'. Handing off to grade_essay.")
200
  return grader.grade_essay(student_paper_file_obj, example_paper_file_obj)
201
  except Exception as e:
202
- print(f"[DEBUG] !!! An error occurred in process_single_file for '{file_name}': {e}")
203
- return GradingResult(file_name=file_name, success=False, error_message=str(e))
204
  finally:
205
  if student_paper_file_obj:
206
- print(f"[DEBUG] -> Cleaning up uploaded file: {student_paper_file_obj.name}")
207
  genai.delete_file(student_paper_file_obj.name)
208
- print(f"[DEBUG] -> Cleanup complete for {file_name}.")
209
 
210
  async def grade_papers_concurrently(
211
  files: List[gr.File], example_paper_file: gr.File, api_key: str, progress=gr.Progress(track_tqdm=True)
212
  ) -> (str, str):
213
  """The main asynchronous function that orchestrates the grading process."""
214
- print("\n" + "="*50)
215
- print("[DEBUG] grade_papers_concurrently initiated.")
216
  start_time = time.time()
217
- if not api_key:
218
- raise gr.Error("Google API Key is required.")
219
- if not files:
220
- raise gr.Error("Please upload at least one paper to grade.")
221
 
222
  example_paper_file_obj = None
223
  try:
224
  grader = GeminiGrader(api_key)
225
-
226
  if example_paper_file:
227
- print("[DEBUG] Example paper provided. Uploading it now...")
228
  progress(0, desc="Uploading example paper...")
229
  example_paper_file_obj = genai.upload_file(path=example_paper_file.name, display_name=os.path.basename(example_paper_file.name))
230
- print(f"[DEBUG] Example paper uploaded successfully: {example_paper_file_obj.name}")
231
 
232
  file_paths = [file.name for file in files]
233
  total_files = len(file_paths)
234
- print(f"[DEBUG] Preparing to grade {total_files} paper(s).")
235
 
236
  with ThreadPoolExecutor(max_workers=1) as executor:
237
- print("[DEBUG] ThreadPoolExecutor started (max_workers=1).")
238
  loop = asyncio.get_event_loop()
239
- tasks = [
240
- loop.run_in_executor(
241
- executor,
242
- process_single_file,
243
- file_path,
244
- grader,
245
- example_paper_file_obj
246
- )
247
- for file_path in file_paths
248
- ]
249
 
250
- results = []
251
- print("[DEBUG] Waiting for grading tasks to complete...")
252
- for i, future in enumerate(asyncio.as_completed(tasks)):
253
- result = await future
254
- print(f"[DEBUG] Task {i+1}/{total_files} completed for file: {result.file_name}")
255
- results.append(result)
256
-
257
- print("[DEBUG] All grading tasks finished. Formatting final output.")
258
- # --- Format the final output ---
259
  successful_grades = [res for res in results if res.success]
260
  failed_grades = [res for res in results if not res.success]
261
- output_markdown = ""
262
  for result in successful_grades:
 
263
  output_markdown += f"### ✅ Grade for: **{result.file_name}**\n"
264
- output_markdown += f"**Final Grade:** {result.grade}/100\n\n"
265
- deductions_str = ""
266
- for category, points in result.deductions.items():
267
- if points > 0:
268
- deductions_str += f"- **{category}:** Lost {points} points. *Reason: {result.feedback.get(category, 'N/A')}*\n"
269
- if not deductions_str:
270
- deductions_str = "Excellent work! No points were deducted.\n"
271
- output_markdown += "**Point Deductions Breakdown:**\n" + deductions_str + "\n"
272
- output_markdown += f"**Summary:** {result.summary}\n"
273
  output_markdown += "---\n"
 
274
  if failed_grades:
275
  output_markdown += "### ❌ Failed Papers\n"
276
  for result in failed_grades:
@@ -281,15 +287,11 @@ async def grade_papers_concurrently(
281
  end_time = time.time()
282
  runtime = f"Total runtime: {end_time - start_time:.2f} seconds."
283
  status = f"Grading complete. {len(successful_grades)} papers graded successfully, {len(failed_grades)} failed."
284
- print(f"[DEBUG] Process finished. {runtime}")
285
- print("="*50 + "\n")
286
  return output_markdown, f"{status}\n{runtime}"
287
 
288
  finally:
289
  if example_paper_file_obj:
290
- print("[DEBUG] Final cleanup: Deleting example paper from API.")
291
  genai.delete_file(example_paper_file_obj.name)
292
- print("[DEBUG] Final cleanup complete.")
293
 
294
  # --- Build the Gradio Interface ---
295
  with gr.Blocks(theme=gr.themes.Soft(), title="Nursing Essay Grader") as demo:
@@ -304,34 +306,16 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Nursing Essay Grader") as demo:
304
  """
305
  )
306
  api_key_input = gr.Textbox(label="Google API Key", placeholder="Enter your Google API Key here", type="password")
307
-
308
  with gr.Row():
309
- file_uploads = gr.File(
310
- label="Upload Essays to Grade",
311
- file_count="multiple",
312
- file_types=['.pdf', '.docx'],
313
- type="filepath",
314
- scale=2
315
- )
316
- example_paper_upload = gr.File(
317
- label="Upload Example Paper (Optional)",
318
- file_count="single",
319
- file_types=['.pdf', '.docx'],
320
- type="filepath",
321
- scale=1
322
- )
323
-
324
  grade_button = gr.Button("🚀 Grade All Papers", variant="primary")
325
  gr.Markdown("---")
326
  gr.Markdown("## 📊 Grading Results")
327
  results_output = gr.Markdown(label="Formatted Grades")
328
  status_output = gr.Textbox(label="Runtime Status", lines=2, interactive=False)
329
 
330
- grade_button.click(
331
- fn=grade_papers_concurrently,
332
- inputs=[file_uploads, example_paper_upload, api_key_input],
333
- outputs=[results_output, status_output]
334
- )
335
 
336
  if __name__ == "__main__":
337
  demo.launch(debug=True)
 
9
  from pathlib import Path
10
  import time
11
 
12
+ # The original, verbose rubric has been restored to provide maximum detail to the model.
13
  GRADING_RUBRIC = """
14
+ DETAILED GRADING RUBRIC & INSTRUCTIONS
15
+ You will assess the paper against the following 16 criteria. For each criterion, you will determine the student's score based on a forensic analysis of their work.
16
+ I. APA Formatting (Total 60 points)
17
+ 1. APA Title Page (5 pts):
18
+ Check for the 9 required components of a student title page. For this task, the 9 components are defined as: (1) Page number in the header (Page 1), (2) Paper title (bolded), (3) Author's name, (4) Department name, (5) University name, (6) Course number and name, (7) Instructor's name, (8) Assignment due date, (9) All elements are centered and correctly spaced in the upper half of the page.
19
+ Scoring:
20
+ 5 pts: All 9 components are present and correctly formatted.
21
+ 0 pts: 1 or more components are missing or incorrectly formatted.
22
+ 2. APA General Guidelines - Main Body (5 pts):
23
+ Check for adherence to all of the following:
24
+ (1) Paper is typed.
25
+ (2) 1-inch margins on all sides.
26
+ (3) Font is either 11 pt Calibri or 12 pt Times New Roman (consistently).
27
+ The 3 additional main body text requirements are: (4) All text is double-spaced, (5) Text is aligned to the left margin (not justified), (6) The first line of every paragraph is indented 0.5 inches.
28
+ Scoring:
29
+ 5 pts: All 6 guidelines are met perfectly.
30
+ 2 pts: 1 guideline is not met.
31
+ 0 pts: 2 or more guidelines are not met.
32
+ 3. APA Text - Main Body Misc. Errors (5 pts):
33
+ Scan the entire document for the following: (1) Extra spacing between paragraphs, (2) Misspellings, (3) Typographical errors.
34
+ Scoring:
35
+ 5 pts: 0 errors found.
36
+ 4 pts: Exactly 1 error found.
37
+ 0 pts: 2 or more errors found.
38
+ 4. APA In-text Citations - Presence (5 pts):
39
+ Analyze the text: Identify every statement of fact, statistic, or opinion that is not common knowledge and requires a citation. Count how many of these required citations are missing.
40
+ Scoring:
41
+ 5 pts: 0 missing in-text citations.
42
+ 3.5 pts: 1 to 5 missing in-text citations.
43
+ 0 pts: 6 or more missing in-text citations.
44
+ 5. APA In-text Citation - Formatting (5 pts):
45
+ Analyze every provided in-text citation. Check for correct APA 7th Ed. format (e.g., (Author, Year) for parenthetical; Author (Year) for narrative; et al. usage; multiple authors). Count every single formatting error.
46
+ Scoring:
47
+ 5 pts: 0 formatting errors.
48
+ 2 pts: 1 to 2 formatting errors.
49
+ 0 pts: 3 or more formatting errors.
50
+ 6. APA Reference Page - General Formatting (5 pts):
51
+ Check for the following: (1) The title "References" is on a new page, centered, and bolded. (2) All entries are alphabetized by the first author's last name. (3) A 0.5-inch hanging indent is applied to all entries.
52
+ Scoring:
53
+ 5 pts: The entire page meets all 3 general formatting expectations.
54
+ 0 pts: 1 or more errors are present.
55
+ 7. APA Reference Page - Line Spacing (5 pts):
56
+ Check for the following: The entire reference page, including between entries, is uniformly double-spaced.
57
+ Scoring:
58
+ 5 pts: The entire page adheres to APA double-spacing rules.
59
+ 0 pts: 1 or more line spacing errors are present (e.g., single spacing, extra space between entries).
60
+ 8. APA References - Scholarly & Timely (10 pts):
61
+ Analyze the reference list. A "scholarly" reference is a peer-reviewed journal article, an academic book/chapter, or a publication from a professional organization (e.g., CDC, WHO). A non-scholarly source would be a general website, blog, or news article. A timely reference is one published within the last 7 years (i.e., between July 20, 2018, and July 19, 2025).
62
+ Check for two conditions: (1) Are there at least 3 scholarly references? AND (2) Are all of those references dated within the last 7 years?
63
+ Scoring:
64
+ 10 pts: At least 3 scholarly references are cited, AND all references are dated within the last 7 years.
65
+ 0 pts: Fewer than 3 scholarly references are cited, OR one or more references are older than 7 years.
66
+ 9. APA References - Author Names (5 pts):
67
+ Analyze all author names on the reference page. Check for correct format: Lastname, F. M.. Also check for correct handling of sources with no author (the title or organization name moves to the author position).
68
+ Scoring:
69
+ 5 pts: All author names are formatted perfectly.
70
+ 2 pts: There is 1 error where an author's name should have been a title/publisher, or vice versa.
71
+ 0 pts: There are general, repeated formatting errors in author names.
72
+ 10. APA References - Dates (5 pts):
73
+ Analyze the date for each reference. Check for correct APA 7th Ed. format (e.g., (Year). for journals/books; (Year, Month Day). for web sources). Count every error.
74
+ Scoring:
75
+ 5 pts: 0 errors.
76
+ 3 pts: Exactly 1 error.
77
+ 0 pts: 2 or more errors.
78
+ 11. APA References - Capitalization (5 pts):
79
+ Analyze the titles in each reference. Check for correct APA 7th Ed. capitalization rules: Sentence case for article and book titles. Title case for periodical (journal) titles. Count every error.
80
+ Scoring:
81
+ 5 pts: 0 errors.
82
+ 3 pts: Exactly 1 error.
83
+ 0 pts: 2 or more errors.
84
+ 12. APA References - Italics (5 pts):
85
+ Analyze each reference for correct italicization. Check for APA 7th Ed. rules: Italicize journal titles and volume numbers. Italicize book titles. Count every error.
86
+ Scoring:
87
+ 5 pts: 0 or 1 error in italicization.
88
+ 0 pts: 2 or more errors in italicization.
89
+ 13. APA References - Hyperlinks (5 pts):
90
+ Analyze all DOIs and URLs. The 4 guidelines are: (1) All DOIs are presented as a full, active hyperlink (e.g., https://doi.org/...). (2) The phrase "Retrieved from" is NOT used before a URL or DOI. (3) There is no period after the DOI or URL. (4) URLs that are not DOIs are included.
91
+ Scoring:
92
+ 5 pts: All 4 guidelines are followed for all relevant references.
93
+ 2 pts: 1 of the 4 guidelines is not followed.
94
+ 0 pts: 2 or more of the 4 guidelines are not followed.
95
+ II. Content (Total 40 points)
96
+ 14. Skin Lesion: Introduction (10 pts):
97
+ Analyze the introduction. Check that it: (1) Is one or two paragraphs long. (2) Clearly introduces the topic of skin lesions and states the issue to be examined. (3) Is not more than one page long. (4) Is free of spelling/typographical errors.
98
+ Scoring:
99
+ 10 pts: Meets all requirements with 0 spelling/typo errors.
100
+ 5 pts: Meets length/content requirements but has 1-2 spelling/typo errors.
101
+ 0 pts: Does not include an introduction, OR the introduction is more than 1 page long, OR it has 3 or more spelling/typographical errors.
102
+ 15. Skin Lesion: Common Causes (10 pts):
103
+ Analyze the body of the essay. Check if the paper discusses at least one common cause of skin lesions (e.g., infection, trauma, allergic reactions, systemic disease).
104
+ Scoring:
105
+ 10 pts: Discusses at least 1 common cause.
106
+ 0 pts: Does not discuss any common causes.
107
+ 16. Skin Lesion: Nursing Considerations (10 pts):
108
+ Analyze the body of the essay. Check if the paper discusses at least one specific nursing consideration or intervention for patients with skin lesions (e.g., wound care, patient education, assessment techniques like ABCDE for melanoma, comfort measures).
109
+ Scoring:
110
+ 10 pts: Discusses at least 1 nursing intervention/consideration.
111
+ 0 pts: Does not discuss any nursing interventions/considerations.
112
  REQUIRED OUTPUT FORMAT
113
  You must present your final evaluation in the following structured format. Do not deviate from this format.
 
114
  Grading Evaluation: NURS 305 Essay - Skin Lesions
115
  Student Submission Analysis
116
  Final Score: [Total Points] / 100
 
117
  Part I: APA Formatting (Score: [Points] / 60)
118
+ 1. APA Title Page: [Score]/5. Justification: [State precisely why the score was given. E.g., "Met all 9 requirements." or "0 points awarded. The title page was missing the course number and instructor's name."]
119
+ 2. APA General Guidelines: [Score]/5. Justification: [E.g., "5 points awarded. The document used 12 pt Times New Roman, 1-inch margins, double-spacing, left alignment, and paragraph indents." or "2 points awarded. The left and right margins were set to 1.25 inches instead of the required 1 inch."]
120
+ 3. APA Text - Misc. Errors: [Score]/5. Justification: [E.g., "4 points awarded. One typographical error ('hte' instead of 'the') was noted in paragraph 3."]
121
+ 4. APA In-text Citations - Presence: [Score]/5. Justification: [E.g., "3.5 points awarded. Analysis identified 4 statements requiring a citation that were not cited."]
122
+ 5. APA In-text Citation - Formatting: [Score]/5. Justification: [E.g., "2 points awarded. Two citations used an ampersand in the narrative format (e.g., 'Smith & Jones (2022) found...') which is incorrect."]
123
+ 6. APA Reference Page - General Formatting: [Score]/5. Justification: [E.g., "0 points awarded. The title 'References' was not bolded, and a hanging indent was not used."]
124
+ 7. APA Reference Page - Line Spacing: [Score]/5. Justification: [E.g., "0 points awarded. An extra space was added between each reference entry."]
125
+ 8. APA References - Scholarly & Timely: [Score]/10. Justification: [E.g., "10 points awarded. The paper cited 4 peer-reviewed journal articles, all published between 2020 and 2024." or "0 points awarded. Only two scholarly sources were used, and one reference was from 2016."]
126
+ 9. APA References - Author Names: [Score]/5. Justification: [E.g., "5 points awarded. All author names were formatted correctly."]
127
+ 10. APA References - Dates: [Score]/5. Justification: [E.g., "3 points awarded. One reference was missing the period after the year: (2021) instead of (2021)."]
128
+ 11. APA References - Capitalization: [Score]/5. Justification: [E.g., "0 points awarded. The titles of two journal articles were written in title case instead of sentence case."]
129
+ 12. APA References - Italics: [Score]/5. Justification: [E.g., "5 points awarded. One error noted where a journal volume number was not italicized. This falls within the 0-1 error threshold for full points."]
130
+ 13. APA References - Hyperlinks: [Score]/5. Justification: [E.g., "2 points awarded. The phrase 'Retrieved from' was incorrectly used before a URL."]
 
131
  Part II: Content (Score: [Points] / 40)
132
+ 14. Introduction: [Score]/10. Justification: [E.g., "10 points awarded. The introduction was a single, concise paragraph that clearly stated the paper's focus. No errors noted."]
133
+ 15. Common Causes: [Score]/10. Justification: [E.g., "10 points awarded. The paper successfully discussed infectious agents as a common cause of skin lesions."]
134
+ 16. Nursing Considerations: [Score]/10. Justification: [E.g., "0 points awarded. The paper failed to discuss any nursing interventions or specific considerations for patients with skin lesions."]
 
135
  Professor's Summary:
136
+ [Provide a 2-3 sentence summary in the persona of Dr. Vance. E.g., "While the content discussing the cause of lesions was adequate, the submission demonstrated significant and widespread deficiencies in adhering to APA 7th Edition standards. These formatting and citation skills are non-negotiable in academic and professional nursing. Careful review of the APA manual is required before the next submission."]
 
137
  """
138
 
139
+ # The prompt has been updated to request the new, more verbose JSON structure.
140
  BASE_PROMPT_TEMPLATE = """
141
+ You are Dr. Stone, a meticulous Associate Professor of Nursing. Your task is to analyze the attached student paper file and grade it with absolute precision against the provided rubric. You may also be provided with an example of a perfectly formatted paper for your reference.
142
 
143
+ Your entire response must be a single, valid JSON object and nothing else.
 
144
 
145
+ **GRADING INSTRUCTIONS:**
146
+ 1. Carefully analyze the student's paper file, paying close attention to all formatting details (margins, fonts, spacing, etc.) and content.
147
+ 2. For each of the 16 criteria in the rubric, provide a score and a brief, specific justification for that score.
148
+ 3. Calculate the final score by summing the scores from all 16 criteria.
149
+ 4. Provide a 2-3 sentence professional summary of the paper's performance.
150
 
151
+ **RUBRIC:**
 
 
 
 
 
 
 
152
  {GRADING_RUBRIC}
153
 
154
+ **REQUIRED JSON OUTPUT FORMAT:**
155
+ Your output must be a JSON object with three top-level keys: `finalScore`, `gradingBreakdown`, and `summary`.
156
+ The `gradingBreakdown` key must contain a list of 16 objects, one for each criterion in the rubric.
157
+
158
+ **EXAMPLE OF THE REQUIRED JSON OUTPUT FORMAT:**
159
  {{
160
+ "finalScore": 89,
161
+ "gradingBreakdown": [
162
+ {{
163
+ "criterion": "1. APA Title Page",
164
+ "score": 5,
165
+ "maxScore": 5,
166
+ "justification": "All 9 required components are present and correctly formatted."
167
+ }},
168
+ {{
169
+ "criterion": "2. APA General Guidelines",
170
+ "score": 2,
171
+ "maxScore": 5,
172
+ "justification": "The document uses 1.25-inch margins instead of the required 1-inch, which is one guideline violation."
173
+ }},
174
+ {{
175
+ "criterion": "3. APA Text - Misc. Errors",
176
+ "score": 4,
177
+ "maxScore": 5,
178
+ "justification": "One typographical error ('hte' instead of 'the') was noted in paragraph 3."
179
+ }}
180
+ ],
181
+ "summary": "This is a strong paper with excellent critical analysis. The final grade was primarily impacted by minor but frequent APA formatting errors, which should be the main focus for improvement."
182
  }}
183
  """
184
 
 
187
  """Holds the structured result of a single graded essay."""
188
  file_name: str
189
  success: bool
190
+ # This now stores the entire JSON response for detailed output
191
+ raw_response: Optional[dict] = None
 
 
192
  error_message: Optional[str] = None
193
 
194
  class GeminiGrader:
195
  """Manages interaction with the Google Gemini API for grading."""
196
  def __init__(self, api_key: str):
 
197
  genai.configure(api_key=api_key)
198
  self.model = genai.GenerativeModel(model_name="gemini-1.5-pro-latest")
 
199
 
200
  def grade_essay(self, student_paper_file: object, example_paper_file: Optional[object]) -> GradingResult:
201
  """Sends files to Gemini for grading and parses the JSON response."""
 
202
  prompt_text = BASE_PROMPT_TEMPLATE.format(GRADING_RUBRIC=GRADING_RUBRIC)
203
+ content_list = ["Please grade the attached student paper.", student_paper_file, prompt_text]
 
204
  if example_paper_file:
205
+ content_list.insert(2, "Use this second file as a reference example of a perfectly formatted paper.")
206
+ content_list.insert(3, example_paper_file)
207
 
208
  try:
209
+ response = self.model.generate_content(content_list, request_options={'timeout': 600})
 
 
210
  cleaned_response = response.text.strip().replace("```json", "").replace("```", "")
211
  data = json.loads(cleaned_response)
212
 
213
+ # Basic validation for the new structure
214
+ if not all(key in data for key in ["finalScore", "gradingBreakdown", "summary"]):
215
+ raise KeyError("Model response missing one or more required top-level keys.")
216
+
 
217
  return GradingResult(
218
  file_name=student_paper_file.display_name,
219
  success=True,
220
+ raw_response=data
 
 
 
221
  )
222
  except json.JSONDecodeError as e:
223
+ error_details = f"Model returned malformed JSON. Error: {e}. Raw Response: {cleaned_response}"
224
+ return GradingResult(file_name=student_paper_file.display_name, success=False, error_message=error_details)
 
 
 
 
 
 
 
 
 
 
225
  except Exception as e:
226
+ return GradingResult(file_name=student_paper_file.display_name, success=False, error_message=f"An API or model error occurred: {str(e)}")
 
 
 
 
 
227
 
228
  def process_single_file(file_path: str, grader: GeminiGrader, example_paper_file_obj: Optional[object]) -> GradingResult:
229
  """Uploads a single student paper and calls the grader."""
 
 
230
  student_paper_file_obj = None
231
  try:
232
+ student_paper_file_obj = genai.upload_file(path=file_path, display_name=os.path.basename(file_path))
 
 
233
  return grader.grade_essay(student_paper_file_obj, example_paper_file_obj)
234
  except Exception as e:
235
+ return GradingResult(file_name=os.path.basename(file_path), success=False, error_message=str(e))
 
236
  finally:
237
  if student_paper_file_obj:
 
238
  genai.delete_file(student_paper_file_obj.name)
 
239
 
240
  async def grade_papers_concurrently(
241
  files: List[gr.File], example_paper_file: gr.File, api_key: str, progress=gr.Progress(track_tqdm=True)
242
  ) -> (str, str):
243
  """The main asynchronous function that orchestrates the grading process."""
 
 
244
  start_time = time.time()
245
+ if not api_key: raise gr.Error("Google API Key is required.")
246
+ if not files: raise gr.Error("Please upload at least one paper to grade.")
 
 
247
 
248
  example_paper_file_obj = None
249
  try:
250
  grader = GeminiGrader(api_key)
 
251
  if example_paper_file:
 
252
  progress(0, desc="Uploading example paper...")
253
  example_paper_file_obj = genai.upload_file(path=example_paper_file.name, display_name=os.path.basename(example_paper_file.name))
 
254
 
255
  file_paths = [file.name for file in files]
256
  total_files = len(file_paths)
 
257
 
258
  with ThreadPoolExecutor(max_workers=1) as executor:
 
259
  loop = asyncio.get_event_loop()
260
+ tasks = [loop.run_in_executor(executor, process_single_file, fp, grader, example_paper_file_obj) for fp in file_paths]
261
+ results = [await f for f in asyncio.as_completed(tasks)]
262
+ progress(1)
 
 
 
 
 
 
 
263
 
264
+ # This section is updated to format the new, verbose JSON structure
265
+ output_markdown = ""
 
 
 
 
 
 
 
266
  successful_grades = [res for res in results if res.success]
267
  failed_grades = [res for res in results if not res.success]
268
+
269
  for result in successful_grades:
270
+ response_data = result.raw_response
271
  output_markdown += f"### ✅ Grade for: **{result.file_name}**\n"
272
+ output_markdown += f"**Final Score:** {response_data.get('finalScore', 'N/A')}/100\n\n"
273
+ output_markdown += "**Detailed Grading Breakdown:**\n"
274
+ for item in response_data.get('gradingBreakdown', []):
275
+ output_markdown += f"- **{item.get('criterion', 'N/A')}**: {item.get('score', 'N/A')} / {item.get('maxScore', 'N/A')}\n"
276
+ output_markdown += f" - *Justification: {item.get('justification', 'No justification provided.')}*\n"
277
+ output_markdown += f"\n**Summary:** {response_data.get('summary', 'No summary provided.')}\n"
 
 
 
278
  output_markdown += "---\n"
279
+
280
  if failed_grades:
281
  output_markdown += "### ❌ Failed Papers\n"
282
  for result in failed_grades:
 
287
  end_time = time.time()
288
  runtime = f"Total runtime: {end_time - start_time:.2f} seconds."
289
  status = f"Grading complete. {len(successful_grades)} papers graded successfully, {len(failed_grades)} failed."
 
 
290
  return output_markdown, f"{status}\n{runtime}"
291
 
292
  finally:
293
  if example_paper_file_obj:
 
294
  genai.delete_file(example_paper_file_obj.name)
 
295
 
296
  # --- Build the Gradio Interface ---
297
  with gr.Blocks(theme=gr.themes.Soft(), title="Nursing Essay Grader") as demo:
 
306
  """
307
  )
308
  api_key_input = gr.Textbox(label="Google API Key", placeholder="Enter your Google API Key here", type="password")
 
309
  with gr.Row():
310
+ file_uploads = gr.File(label="Upload Essays to Grade", file_count="multiple", file_types=['.pdf', '.docx'], type="filepath", scale=2)
311
+ example_paper_upload = gr.File(label="Upload Example Paper (Optional)", file_count="single", file_types=['.pdf', '.docx'], type="filepath", scale=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  grade_button = gr.Button("🚀 Grade All Papers", variant="primary")
313
  gr.Markdown("---")
314
  gr.Markdown("## 📊 Grading Results")
315
  results_output = gr.Markdown(label="Formatted Grades")
316
  status_output = gr.Textbox(label="Runtime Status", lines=2, interactive=False)
317
 
318
+ grade_button.click(fn=grade_papers_concurrently, inputs=[file_uploads, example_paper_upload, api_key_input], outputs=[results_output, status_output])
 
 
 
 
319
 
320
  if __name__ == "__main__":
321
  demo.launch(debug=True)