Skip to content

Commit

Permalink
Automarking Fixes (#68)
Browse files Browse the repository at this point in the history
* fixed ai stuff, extracted prompts into markdown

* piazza style comments

* deleted the commented code

* type fixes
  • Loading branch information
erensunerr authored Apr 30, 2024
1 parent c653b18 commit 0d0a565
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 145 deletions.
42 changes: 5 additions & 37 deletions client/src/components/Comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Form } from "react-bootstrap";
import { useEffect, useState } from "react";
import { CaretUpFill, CaretUp } from "react-bootstrap-icons";
import { useRemark } from "react-remark";
import dayjs from 'dayjs';
import dayjs from "dayjs";

type CommentProps = NonEditableCommentProps | EditableCommentProps;

Expand Down Expand Up @@ -54,11 +54,7 @@ const Upvoter = ({ commentId, upVotes, hasUpvoted }: UpvoterProps) => {
);
};

const NonEditableComment = ({ comment, onSubmit }: NonEditableCommentProps) => {
const [isReplying, setIsReplying] = useState(false);
const [repliesOpen, setRepliesOpen] = useState(false);
const repliesAvailable = comment.replies.length !== 0;

const NonEditableComment = ({ comment }: NonEditableCommentProps) => {
const [renderedMarkdown, setMarkdownSource] = useRemark();

useEffect(() => {
Expand All @@ -72,8 +68,9 @@ const NonEditableComment = ({ comment, onSubmit }: NonEditableCommentProps) => {
<Card className="mt-3">
<Card.Header>
<div className="d-flex justify-content-between align-items-center">
<strong>{comment.userId}</strong> {/* Displaying username */}
<small>{dayjs(comment.createdAt).format('MMM D, YYYY')}</small> {/* Formatting and displaying date */}
<strong>{comment.userId}</strong> {/* Displaying username */}
<small>{dayjs(comment.createdAt).format("MMM D, YYYY")}</small>{" "}
{/* Formatting and displaying date */}
</div>
</Card.Header>
<Card.Body>
Expand All @@ -84,42 +81,13 @@ const NonEditableComment = ({ comment, onSubmit }: NonEditableCommentProps) => {
upVotes={comment.upVotes}
hasUpvoted={comment.hasUserUpvoted}
/>
<Button
variant="primary"
onClick={() => setIsReplying(prev => !prev)}
>
{isReplying ? "Stop Replying" : "Reply"}
</Button>
{repliesAvailable && (
<Button
variant="secondary"
className="ms-3"
onClick={() => setRepliesOpen(prev => !prev)}
>
{repliesOpen ? "Close Replies" : "See Replies"}
</Button>
)}
</div>
</Card.Body>
</Card>
<div className="mt-3 ms-3">
{isReplying && (
<Comment editable={true} onSubmit={onSubmit} parentId={comment.id} />
)}
{repliesAvailable && repliesOpen && comment.replies.map(c => (
<Comment
key={c.id}
comment={c}
onSubmit={onSubmit}
editable={false}
/>
))}
</div>
</>
);
};


const EditableComment = ({ onSubmit, parentId }: EditableCommentProps) => {
const [newComment, setNewComment] = useState<string>("");
return (
Expand Down
10 changes: 5 additions & 5 deletions client/src/hooks/useCheckRole.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { useState, useEffect } from 'react';
import { useState, useEffect } from "react";

const useCheckRole = () => {
const [isAdmin, setIsAdmin] = useState(false);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
fetch("/api/users/whoami")
.then(response => response.json())
.then(data => {
setIsAdmin(data.role === 'admin');
.then((response) => response.json())
.then((data) => {
setIsAdmin(data.role === "admin");
setIsLoading(false);
})
.catch(error => {
.catch((error) => {
console.error("Error fetching user role:", error);
setIsLoading(false);
});
Expand Down
110 changes: 75 additions & 35 deletions client/src/pages/Solution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,31 @@ import { SolutionData } from "../types/SolutionData.ts";
import { CommentData } from "../types/CommentData.ts";
import Button from "../components/Button.tsx";
import Comment from "../components/Comment.tsx";
import useCheckRole from '../hooks/useCheckRole'; // Make sure the path is correct
import dayjs from 'dayjs';
import useCheckRole from "../hooks/useCheckRole"; // Make sure the path is correct
import dayjs from "dayjs";

function loadSolution(id, setter) {
fetch(`/api/solutions/${id}`)
.then(resp => resp.json())
.then(data => setter(data))
.catch(err => console.error(err));
.then((resp) => resp.json())
.then((data) => setter(data))
.catch((err) => {
console.error(err);
});
}

function loadComments(id, setter) {
fetch(`/api/comments/${id}`)
.then(resp => resp.json())
.then(data => setter(data))
.catch(err => console.error(err));
.then((resp) => resp.json())
.then((data) => setter(data))
.catch((err) => {
console.error(err);
});
}

const Solution = () => {
const { id } = useParams();
const [solutionData, setSolutionData] = useState(null);
const [comments, setComments] = useState([]);
const [solutionData, setSolutionData] = useState<SolutionData | null>(null);
const [comments, setComments] = useState<CommentData[]>([]);
const { isAdmin, isLoading } = useCheckRole();
const [currentUserId, setCurrentUserId] = useState(null);

Expand All @@ -38,22 +42,24 @@ const Solution = () => {

useEffect(() => {
fetch("/api/users/whoami")
.then(response => response.json())
.then(data => {
.then((response) => response.json())
.then((data) => {
setCurrentUserId(data.username);
})
.catch(error => {
.catch((error) => {
console.error("Error fetching current user ID:", error);
});
}, []);

const handleDeleteSolution = () => {
if (!isAdmin && solutionData.userId !== currentUserId) return;
fetch(`/api/solutions/${id}`, { method: 'DELETE' })
fetch(`/api/solutions/${id}`, { method: "DELETE" })
.then(() => {
window.location.href = '/';
window.location.href = "/";
})
.catch(err => console.error('Failed to delete solution', err));
.catch((err) => {
console.error("Failed to delete solution", err);
});
};

if (isLoading) {
Expand All @@ -62,20 +68,32 @@ const Solution = () => {

const handleDelete = (commentId) => {
if (!isAdmin) return;
fetch(`/api/comments/${commentId}`, { method: 'DELETE' })
.then(() => setComments(comments => comments.filter(comment => comment.id !== commentId)))
.catch(err => console.error('Failed to delete comment', err));
fetch(`/api/comments/${commentId}`, { method: "DELETE" })
.then(() => {
setComments((comments) =>
comments.filter((comment) => comment.id !== commentId),
);
})
.catch((err) => {
console.error("Failed to delete comment", err);
});
};

const handleSubmit = (parentId, text) => {
const endpoint = parentId ? `/api/comments/reply/${parentId}` : `/api/comments/${solutionData.id}`;
const endpoint = parentId
? `/api/comments/reply/${parentId}`
: `/api/comments/${solutionData.id}`;
fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ solutionId: solutionData.id, text })
body: JSON.stringify({ solutionId: solutionData.id, text }),
})
.then(() => loadComments(id, setComments))
.catch(err => console.error(err));
.then(() => {
loadComments(id, setComments);
})
.catch((err) => {
console.error(err);
});
};

return (
Expand All @@ -85,23 +103,31 @@ const Solution = () => {
<h2>Solution</h2>
{solutionData && (
<Card>
<Card.Header>
<Card.Header>
<div className="d-flex justify-content-between align-items-center">
<strong>By: {solutionData.userId}</strong>
<small>{dayjs(solutionData.createdAt).format('MMMM D, YYYY')}</small>
<small>
{dayjs(solutionData.createdAt).format("MMMM D, YYYY")}
</small>
</div>
</Card.Header>
<Card.Body>
<Card.Title>{solutionData.title}</Card.Title>
<Card.Text>{solutionData.description}</Card.Text>

{solutionData.diagram && (
<Card.Img variant="bottom" src={`/api/solutions/diagrams/${solutionData.diagram}`} alt="Solution Diagram" />
<Card.Img
variant="bottom"
src={`/api/solutions/diagrams/${solutionData.diagram}`}
alt="Solution Diagram"
/>
)}
</Card.Body>
{isAdmin || solutionData.userId === currentUserId ? (
<Card.Footer>
<Button variant="danger" onClick={handleDeleteSolution}>Delete Solution</Button>
<Button variant="danger" onClick={handleDeleteSolution}>
Delete Solution
</Button>
</Card.Footer>
) : null}
</Card>
Expand All @@ -111,13 +137,27 @@ const Solution = () => {
<Row className="mt-5 justify-content-center">
<Col md={6}>
<h2>Comments</h2>
<Comment editable={true} onSubmit={handleSubmit} />ç
{comments.map(comment => (
<div key={comment.id}>
<Comment comment={comment} editable={false} onSubmit={handleSubmit} />
{isAdmin && <Button variant="danger" onClick={() => handleDelete(comment.id)}>Delete</Button>}
</div>
))}
<Comment editable={true} onSubmit={handleSubmit} />
{comments &&
comments.map((comment) => (
<div key={comment.id}>
<Comment
comment={comment}
editable={false}
onSubmit={handleSubmit}
/>
{isAdmin && (
<Button
variant="danger"
onClick={() => {
handleDelete(comment.id);
}}
>
Delete
</Button>
)}
</div>
))}
</Col>
</Row>
</Container>
Expand Down
1 change: 1 addition & 0 deletions client/src/types/CommentData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export type CommentData = {
text: string;
upVotes: number;
hasUserUpvoted: boolean;
createdAt: string | number | Date;
replies: CommentData[];
};
12 changes: 4 additions & 8 deletions server/AI/AITA.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@ class AITA {

const diagramBuffer = await fs.readFile(diagramLocation);
const diagramb64 = diagramBuffer.toString("base64");
const systemPromptText = await fs.readFile("./server/AI/SystemPrompt.md");
console.log(systemPromptText);

const prompt = ChatPromptTemplate.fromMessages([
[
"system",
"You are a teaching assistant for a software architecture course. You are excellent at providing" +
" feedback to solutions written by users to software architecture challenges, including questions about UML" +
" diagrams, software architecture patterns and SOLID principles. The images you receive will be of UML" +
" diagrams. Provide" +
" extensive and helpful feedback as a teaching assistant.",
],
["system", systemPromptText],
[
"user",
[
Expand Down Expand Up @@ -64,6 +59,7 @@ class AITA {
return [chainRunId, feedback];
}
static async feedback_for_comment(comment_chain, challenge, solution) {
// Not in use
// console.log(comment_chain);
// console.log(challenge);
// console.log(solution);
Expand Down
23 changes: 23 additions & 0 deletions server/AI/SystemPrompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
You are a teaching assistant for a software architecture course. You are excellent at providing feedback to
solutions written by users to software architecture challenges, including questions about UML diagrams,
software architecture patterns and SOLID principles. The images you receive will be of UML diagrams. Provide extensive and helpful feedback as a teaching assistant.

If the user provides a prompt that's unrelated to software architecture, tell them the following sentence and
nothing else.

AITA can only reply to questions pertaining to software architecture.

Again, you are a teaching assistant and you act for the betterment of students without revealing answers too quickly.
Your job is to facilitate their learning.


Example interactions:

User: tell me a story about bears.
You: AITA can only reply to questions pertaining to software architecture.

User: asdfhga sdsfdsfjkfdjk
You: AITA can only reply to questions pertaining to software architecture.

User: Tell me the solid principles
You: Single Responsibility Principle, Open / Closed Principle, ...
Loading

0 comments on commit 0d0a565

Please sign in to comment.