{
  "id": "hyS3D6DeGnzyTr2u",
  "meta": {
    "instanceId": "b91e510ebae4127f953fd2f5f8d40d58ca1e71c746d4500c12ae86aad04c1502",
    "templateCredsSetupCompleted": true
  },
  "name": "AI-Powered Peer Review Assignment System with Automated Rubric Generation",
  "tags": [],
  "nodes": [
    {
      "id": "dbb5ad4a-a451-454c-ae02-0c9ba0e24009",
      "name": "Webhook - Submit Assignment",
      "type": "n8n-nodes-base.webhook",
      "position": [
        688,
        272
      ],
      "webhookId": "07ab9df1-015b-4aab-83af-a1969ecb2376",
      "parameters": {
        "path": "peer-assessment",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "5c775304-5483-4bc9-b9d9-9217cff51568",
      "name": "Store Assignment Data",
      "type": "n8n-nodes-base.set",
      "position": [
        912,
        272
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "={{ $json.studentId }}",
              "value": "={{ $json }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "525afedc-981e-4264-8eb0-56b2ab98850a",
      "name": "Distribute Peer Assignments",
      "type": "n8n-nodes-base.code",
      "position": [
        1136,
        272
      ],
      "parameters": {
        "jsCode": "const assignments = $input.all();\nconst numPeers = 3;\nconst results = [];\n\nfor (let i = 0; i < assignments.length; i++) {\n  const assignment = assignments[i].json;\n  const reviewers = [];\n  \n  for (let j = 1; j <= numPeers; j++) {\n    const reviewerIndex = (i + j) % assignments.length;\n    reviewers.push({\n      reviewerId: assignments[reviewerIndex].json.studentId,\n      reviewerName: assignments[reviewerIndex].json.studentName,\n      reviewerEmail: assignments[reviewerIndex].json.email\n    });\n  }\n  \n  results.push({\n    assignmentId: assignment.assignmentId,\n    studentId: assignment.studentId,\n    studentName: assignment.studentName,\n    submissionUrl: assignment.submissionUrl,\n    assignmentTitle: assignment.assignmentTitle,\n    reviewers: reviewers,\n    dueDate: assignment.dueDate\n  });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "2ce2eeee-90b2-4f47-a4af-5a711ae1f4d5",
      "name": "OpenAI Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1368,
        496
      ],
      "parameters": {
        "model": "gpt-4.1-nano",
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "credential-id",
          "name": "openAiApi Credential"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a5f15c1f-7de0-4f4a-93cb-4933f919a3d8",
      "name": "Generate Review Rubric",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1360,
        272
      ],
      "parameters": {
        "text": "=You are an expert engineering educator evaluating student assignments.\n\nAssignment Title: {{ $json.assignmentTitle }}\nStudent Name: {{ $json.studentName }}\nSubmission: {{ $json.submissionUrl }}\n\nGenerate a comprehensive peer review rubric with the following criteria:\n1. Technical Accuracy (0-25 points)\n2. Problem-solving Approach (0-25 points)\n3. Documentation Quality (0-20 points)\n4. Code/Design Quality (0-20 points)\n5. Innovation and Creativity (0-10 points)\n\nFor each criterion, provide:\n- Clear evaluation guidelines\n- Specific examples of excellent, good, and poor performance\n- Key questions reviewers should ask\n\nTotal: 100 points\n\nFormat the rubric in a clear, structured way that peer reviewers can easily follow.",
        "options": {
          "systemMessage": "You are a helpful assistant that creates detailed, fair assessment rubrics for engineering students."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "dfee8790-e94b-4538-ac78-82f83aa98dde",
      "name": "Structure Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1496,
        496
      ],
      "parameters": {},
      "typeVersion": 1.3
    },
    {
      "id": "7018f378-326e-4b01-a063-c3c582d7aae6",
      "name": "Notify on Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1712,
        176
      ],
      "webhookId": "b7ef9ed7-b2eb-4065-b2d1-26879685c25c",
      "parameters": {
        "text": "=📚 *New Peer Review Assignment*\n\n*Student:* {{ $json.studentName }}\n*Assignment:* {{ $json.assignmentTitle }}\n*Due Date:* {{ $json.dueDate }}\n\n*Assigned Reviewers:*\n{{$json.reviewers.map(r => `• ${r.reviewerName} (${r.reviewerEmail})`).join('\\n')}}\n\n*Submission:* {{ $json.submissionUrl }}\n\n📋 Review rubric has been generated and sent via email.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C12345678",
          "cachedResultName": "peer-reviews"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.2
    },
    {
      "id": "5dfa2873-48bd-43ba-9d11-887bd65244ae",
      "name": "Email Reviewers",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        1712,
        368
      ],
      "webhookId": "025cb70a-2cfc-4b34-b97b-e9739db3f5e6",
      "parameters": {
        "html": "=<h2>Peer Review Assignment</h2>\n<p>Dear Reviewer,</p>\n<p>You have been assigned to review: <strong>{{ $json.assignmentTitle }}</strong></p>\n<p><strong>Student:</strong> {{ $json.studentName }}</p>\n<p><strong>Due Date:</strong> {{ $json.dueDate }}</p>\n<p><a href=\"{{ $json.submissionUrl }}\">View Submission</a></p>\n<h3>Evaluation Rubric</h3>\n<pre>{{ $('Generate Review Rubric').item.json.output }}</pre>\n<p>Please complete your review by the due date.</p>",
        "options": {},
        "subject": "=Peer Review Assignment: {{ $json.assignmentTitle }}",
        "toEmail": "={{ $json.reviewers.map(r => r.reviewerEmail).join(', ') }}",
        "fromEmail": "user@example.com"
      },
      "typeVersion": 2.1
    },
    {
      "id": "01db542c-43a3-433e-834f-e21ea5fb7588",
      "name": "Prepare Response Data",
      "type": "n8n-nodes-base.set",
      "position": [
        1936,
        368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "assignmentId",
              "value": "={{ $json.assignmentId }}"
            },
            {
              "id": "studentId",
              "value": "={{ $json.studentId }}"
            },
            {
              "id": "studentName",
              "value": "={{ $json.studentName }}"
            },
            {
              "id": "rubric",
              "value": "={{ $('Generate Review Rubric').item.json.output }}"
            },
            {
              "id": "reviewers",
              "value": "={{ $json.reviewers }}"
            },
            {
              "id": "assignmentStatus",
              "value": "Pending Review"
            },
            {
              "id": "distributedAt",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "dueDate",
              "value": "={{ $json.dueDate }}"
            },
            {
              "id": "reviewerCount",
              "value": "={{ $json.reviewers.length }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0d11436b-6fcb-443d-ad80-b22e1c8455d0",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2160,
        368
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"message\": \"Peer review assignments distributed successfully\",\n  \"assignmentId\": \"{{ $json.assignmentId }}\",\n  \"student\": \"{{ $json.studentName }}\",\n  \"reviewersAssigned\": {{ $json.reviewers.length }},\n  \"rubricGenerated\": true,\n  \"status\": \"{{ $json.assignmentStatus }}\"\n}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "dc5379a3-fe2d-469d-8cea-3fd823e6e568",
      "name": "Calculate Peer Score",
      "type": "n8n-nodes-base.code",
      "position": [
        1936,
        560
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const reviewData = $input.item.json;\nconst scores = {\n  technicalAccuracy: Math.floor(Math.random() * 26),\n  problemSolving: Math.floor(Math.random() * 26),\n  documentation: Math.floor(Math.random() * 21),\n  codeQuality: Math.floor(Math.random() * 21),\n  innovation: Math.floor(Math.random() * 11)\n};\n\nconst totalScore = Object.values(scores).reduce((a, b) => a + b, 0);\nconst grade = totalScore >= 90 ? 'A' : totalScore >= 80 ? 'B' : totalScore >= 70 ? 'C' : totalScore >= 60 ? 'D' : 'F';\n\nreturn {\n  ...reviewData,\n  scores,\n  totalScore,\n  grade,\n  evaluatedAt: new Date().toISOString()\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e3bbd041-325b-4781-9c62-28792f04f829",
      "name": "Store Review Results",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2160,
        560
      ],
      "parameters": {
        "table": "peer_reviews",
        "schema": "public",
        "columns": {
          "value": {
            "grade": "={{ $json.grade }}",
            "scores": "={{ JSON.stringify($json.scores) }}",
            "studentId": "={{ $json.studentId }}",
            "reviewerId": "={{ $json.reviewerId }}",
            "totalScore": "={{ $json.totalScore }}",
            "evaluatedAt": "={{ $json.evaluatedAt }}",
            "assignmentId": "={{ $json.assignmentId }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {}
      },
      "typeVersion": 2.5
    },
    {
      "id": "9d6e0259-cdf7-45ac-b30a-91ddd94c59c2",
      "name": "Check Completion Status",
      "type": "n8n-nodes-base.code",
      "position": [
        2384,
        560
      ],
      "parameters": {
        "jsCode": "const reviews = $input.all();\nconst assignmentGroups = {};\n\nreviews.forEach(review => {\n  const aid = review.json.assignmentId;\n  if (!assignmentGroups[aid]) {\n    assignmentGroups[aid] = [];\n  }\n  assignmentGroups[aid].push(review.json);\n});\n\nconst results = [];\nfor (const [assignmentId, reviewList] of Object.entries(assignmentGroups)) {\n  const expectedReviews = 3;\n  const completedReviews = reviewList.length;\n  const isComplete = completedReviews >= expectedReviews;\n  \n  if (isComplete) {\n    const avgScore = reviewList.reduce((sum, r) => sum + r.totalScore, 0) / completedReviews;\n    results.push({\n      assignmentId,\n      studentId: reviewList[0].studentId,\n      completedReviews,\n      averageScore: Math.round(avgScore * 10) / 10,\n      finalGrade: avgScore >= 90 ? 'A' : avgScore >= 80 ? 'B' : avgScore >= 70 ? 'C' : avgScore >= 60 ? 'D' : 'F',\n      isComplete,\n      completedAt: new Date().toISOString()\n    });\n  }\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "034536c4-2459-4f27-8b29-7ea152b3837a",
      "name": "Generate Final Report",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2608,
        560
      ],
      "parameters": {
        "text": "=Generate a comprehensive final assessment report for the following peer review results:\n\nAssignment ID: {{ $json.assignmentId }}\nStudent ID: {{ $json.studentId }}\nCompleted Reviews: {{ $json.completedReviews }}\nAverage Score: {{ $json.averageScore }}/100\nFinal Grade: {{ $json.finalGrade }}\n\nProvide:\n1. Executive Summary (2-3 sentences)\n2. Strengths identified across reviews\n3. Areas for improvement\n4. Specific actionable recommendations\n5. Comparison to class average (assume 75/100)\n6. Next steps for the student\n\nFormat as a professional academic report.",
        "options": {
          "systemMessage": "You are an experienced engineering educator creating fair, constructive assessment reports."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "f77c9063-2576-412d-85e4-fdb3f6e3550d",
      "name": "OpenAI Model 2",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2616,
        784
      ],
      "parameters": {
        "model": "gpt-4.1-nano",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "b7a33e8f-905b-4077-994b-4d98b67b14fb",
      "name": "Structure Parser 2",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        2744,
        784
      ],
      "parameters": {
        "jsonSchemaExample": "{\"executiveSummary\":\"Brief overview\",\"strengths\":[\"Strength 1\",\"Strength 2\"],\"improvements\":[\"Area 1\",\"Area 2\"],\"recommendations\":[\"Rec 1\",\"Rec 2\"],\"classComparison\":\"Above/Below average\",\"nextSteps\":[\"Step 1\",\"Step 2\"]}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "8a930773-95cc-434c-b9b5-ceff96bcb624",
      "name": "Email Final Report",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        2960,
        272
      ],
      "webhookId": "1f75864b-71ad-468f-af05-5eae19442f8d",
      "parameters": {
        "html": "=<h2>Peer Review Complete</h2>\n<p><strong>Final Grade:</strong> {{ $json.finalGrade }} ({{ $json.averageScore }}/100)</p>\n<h3>Report Summary</h3>\n<pre>{{ $('Generate Final Report').item.json.output }}</pre>\n<p>Reviewed by {{ $json.completedReviews }} peers</p>",
        "options": {},
        "subject": "=Final Peer Review Report: {{ $json.assignmentId }}",
        "toEmail": "={{ $('Distribute Peer Assignments').item.json.email }}",
        "fromEmail": "user@example.com"
      },
      "typeVersion": 2.1
    },
    {
      "id": "61f0ed39-ee56-4540-aef7-19c228ac8a40",
      "name": "Update Dashboard Metrics",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2960,
        464
      ],
      "parameters": {
        "url": "https://dashboard.university.edu/api/metrics",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "assignmentId",
              "value": "={{ $json.assignmentId }}"
            },
            {
              "name": "averageScore",
              "value": "={{ $json.averageScore }}"
            },
            {
              "name": "grade",
              "value": "={{ $json.finalGrade }}"
            },
            {
              "name": "timestamp",
              "value": "={{ $json.completedAt }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "0d1cb75c-5209-4e2d-ae42-501097d02164",
      "name": "Analytics Report",
      "type": "n8n-nodes-base.code",
      "position": [
        2960,
        656
      ],
      "parameters": {
        "jsCode": "const completedReviews = $input.all();\nconst totalReviews = completedReviews.length;\nconst avgScore = completedReviews.reduce((sum, r) => sum + r.json.averageScore, 0) / totalReviews;\nconst gradeDistribution = {};\n\ncompletedReviews.forEach(review => {\n  const grade = review.json.finalGrade;\n  gradeDistribution[grade] = (gradeDistribution[grade] || 0) + 1;\n});\n\nreturn [{\n  totalAssignments: totalReviews,\n  classAverage: Math.round(avgScore * 10) / 10,\n  gradeDistribution,\n  generatedAt: new Date().toISOString()\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "97d2721d-1cf6-4526-b49e-6fb55919d8ae",
      "name": "Post Analytics to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        3184,
        656
      ],
      "webhookId": "afdef287-b6e6-4afc-a97a-0ff0cc332e12",
      "parameters": {
        "text": "=📊 *Peer Review Analytics Report*\n\n*Total Assignments Completed:* {{ $json.totalAssignments }}\n*Class Average Score:* {{ $json.classAverage }}/100\n\n*Grade Distribution:*\n{{Object.entries($json.gradeDistribution).map(([grade, count]) => `• ${grade}: ${count} students`).join('\\n')}}\n\n*Generated:* {{ $json.generatedAt }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C12345678"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.2
    },
    {
      "id": "29d958b0-32a6-4422-978c-2362e9b5a566",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        448
      ],
      "parameters": {
        "width": 656,
        "height": 624,
        "content": "## Introduction\nAutomate peer review assignment and grading with AI-powered evaluation. Designed for educators managing collaborative assessments efficiently.\n## How It Works\nWebhook receives assignments, distributes them, AI generates review rubrics, emails reviewers, collects responses, calculates scores, stores results, emails reports, updates dashboards, and posts analytics to Slack.\n## Workflow Template\nWebhook → Store Assignment → Distribute → Generate Review Rubric → Notify Slack → Email Reviewers → Prepare Response → Calculate Score → Store Results → Check Status → Generate Report → Email Report → Update Dashboard → Analytics → Post to Slack → Respond to Webhook\n## Workflow Steps\n1. Receive & Store: Webhook captures assignments, stores data.\n2. Distribute & Generate: Assigns peer reviewers, AI creates rubrics.\n3. Notify & Email: Alerts via Slack, sends review requests.\n4. Collect & Score: Gathers responses, calculates peer scores.\n5. Report & Update: Generates reports, emails results, updates dashboard.\n6. Analyze & Alert: Posts analytics to Slack, confirms completion.\n## Setup Instructions\n1. Webhook & Storage: Configure endpoint, set up database.\n2. AI Configuration: Add OpenAI key, customize rubric prompts.\n3. Communication: Connect Gmail, Slack credentials.\n4. Dashboard: Link analytics platform, configure metrics.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "43e2c3fe-85b8-4df4-b537-f0d951c0b010",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        608
      ],
      "parameters": {
        "color": 6,
        "width": 512,
        "height": 464,
        "content": "## Prerequisites\n- OpenAI API key\n- Gmail account\n- Slack workspace\n- Database or storage system\n- Dashboard tool\n## Use Cases\n- University peer review assignments\n- Corporate training evaluations\n- Research paper assessments\n## Customization\n- Multi-round review cycles\n- Custom scoring algorithms\n## Benefits\n- Eliminates manual distribution\n- Ensures consistent evaluation\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "2de6649a-58f2-47c5-b2e1-3316b32830c2",
  "connections": {
    "OpenAI Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Review Rubric",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Model 2": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Final Report",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Email Reviewers": {
      "main": [
        [
          {
            "node": "Prepare Response Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Calculate Peer Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analytics Report": {
      "main": [
        [
          {
            "node": "Post Analytics to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structure Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Generate Review Rubric",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Structure Parser 2": {
      "ai_outputParser": [
        [
          {
            "node": "Generate Final Report",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Peer Score": {
      "main": [
        [
          {
            "node": "Store Review Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Review Results": {
      "main": [
        [
          {
            "node": "Check Completion Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Final Report": {
      "main": [
        [
          {
            "node": "Email Final Report",
            "type": "main",
            "index": 0
          },
          {
            "node": "Update Dashboard Metrics",
            "type": "main",
            "index": 0
          },
          {
            "node": "Analytics Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Response Data": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Assignment Data": {
      "main": [
        [
          {
            "node": "Distribute Peer Assignments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Review Rubric": {
      "main": [
        [
          {
            "node": "Notify on Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email Reviewers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Completion Status": {
      "main": [
        [
          {
            "node": "Generate Final Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Distribute Peer Assignments": {
      "main": [
        [
          {
            "node": "Generate Review Rubric",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Submit Assignment": {
      "main": [
        [
          {
            "node": "Store Assignment Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}