diff --git a/website/pages/api/hello.js b/website/pages/api/hello.js deleted file mode 100644 index aee21e9a..00000000 --- a/website/pages/api/hello.js +++ /dev/null @@ -1,5 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction - -export default function handler(req, res) { - res.status(200).json({ name: "John Doe" }); -} diff --git a/website/pages/api/new_task.js b/website/pages/api/new_task.js index d9bd5012..790c3f23 100644 --- a/website/pages/api/new_task.js +++ b/website/pages/api/new_task.js @@ -2,16 +2,25 @@ import { unstable_getServerSession } from "next-auth/next"; import { authOptions } from "./auth/[...nextauth]"; /** - * Returns a list of prompts from the Labeler Backend. + * Returns a new task created from the Task Backend. We do a few things here: + * + * 1) Get the task from the backend and register the requesting user. + * 2) Store the task in our local database. + * 3) Send and Ack to the Task Backend with our local id for the task. + * 4) Return everything to the client. */ export default async (req, res) => { const session = await unstable_getServerSession(req, res, authOptions); + // Return nothing if the user isn't registered. if (!session) { res.status(401).end(); return; } + // Fetch the new task. + // + // This needs to be refactored into an easier to use library. const taskRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/`, { method: "POST", headers: { @@ -29,6 +38,7 @@ export default async (req, res) => { }); const task = await taskRes.json(); + // Store the task and link it to the user.. const registeredTask = await prisma.registeredTask.create({ data: { task, @@ -40,6 +50,7 @@ export default async (req, res) => { }, }); + // Update the backend with our Task ID const ackRes = await fetch( `${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, { @@ -55,5 +66,6 @@ export default async (req, res) => { ); const ack = await ackRes.json(); + // Send the results to the client. res.status(200).json(registeredTask); }; diff --git a/website/pages/api/prompts.js b/website/pages/api/prompts.js deleted file mode 100644 index 9d09641c..00000000 --- a/website/pages/api/prompts.js +++ /dev/null @@ -1,28 +0,0 @@ -import { unstable_getServerSession } from "next-auth/next"; -import { authOptions } from "./auth/[...nextauth]"; - -/** - * Returns a list of prompts from the Labeler Backend. - */ -export default async (req, res) => { - const session = await unstable_getServerSession(req, res, authOptions); - - if (!session) { - res.status(401).end(); - return; - } - try { - const promptRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/prompts`, { - headers: { - "X-API-Key": process.env.FASTAPI_KEY, - }, - }); - const prompts = await promptRes.json(); - - res.status(200).json(prompts); - } catch (error) { - console.error(error); - res.status(500); - } - res.end(); -}; diff --git a/website/pages/api/update_task.js b/website/pages/api/update_task.js index f68cd709..45b1f72e 100644 --- a/website/pages/api/update_task.js +++ b/website/pages/api/update_task.js @@ -2,18 +2,28 @@ import { unstable_getServerSession } from "next-auth/next"; import { authOptions } from "./auth/[...nextauth]"; /** - * Returns a list of prompts from the Labeler Backend. + * Stores the task interaction with the Task Backend and then returns the next task generated. + * + * This implicity does a few things: + * 1) Stores the answer with the Task Backend. + * 2) Records the new task in our local database. + * 3) (TODO) Acks the new task with our local task ID to the Task Backend. + * 4) Returns the newly created task to the client. */ export default async (req, res) => { const session = await unstable_getServerSession(req, res, authOptions); + // Return nothing if the user isn't registered. if (!session) { res.status(401).end(); return; } + // Parse out the local task ID and the interaction contents. const { id, content } = await JSON.parse(req.body); + // Log the interaction locally to create our user_post_id needed by the Task + // Backend. const interaction = await prisma.taskInteraction.create({ data: { content, @@ -25,6 +35,8 @@ export default async (req, res) => { }, }); + // Send the interaction to the Task Backend. This automatically fetches the + // next task in the sequence (or the done task). const interactionRes = await fetch( `${process.env.FASTAPI_URL}/api/v1/tasks/interaction`, { @@ -46,9 +58,23 @@ export default async (req, res) => { }), } ); - console.log(interactionRes.status); const newTask = await interactionRes.json(); - console.log(newTask); - res.status(200).json(newTask); + // Stores the new task with our database. + const newRegisteredTask = await prisma.registeredTask.create({ + data: { + task: newTask, + user: { + connect: { + id: session.user.id, + }, + }, + }, + }); + + // TODO: Ack the task with the Task Backend using the newly created local + // task ID. + + // Send the next task in the sequence to the client. + res.status(200).json(newRegisteredTask); }; diff --git a/website/pages/new_task.js b/website/pages/new_task.js index 5308323f..77577662 100644 --- a/website/pages/new_task.js +++ b/website/pages/new_task.js @@ -8,6 +8,10 @@ import useSWRMutation from "swr/mutation"; const fetcher = (url) => axios.get(url).then((res) => res.data); +/** + * A helper function to post updates to tasks. + * This ensures the content sent is serialized to JSON. + */ async function sendRequest(url, { arg }) { return fetch(url, { method: "POST", @@ -16,49 +20,79 @@ async function sendRequest(url, { arg }) { } export default function NewPage() { - const [done, setDone] = useState(false); + // Use an array of tasks that record the sequence of steps until a task is + // deemed complete. + const [tasks, setTasks] = useState([]); + + // A quick reference to the input element. This should be factored into the + // component doing the actual task rendering. const responseEl = useRef(null); - const { - data: registeredTask, - errors, - isLoading, - } = useSWRImmutable("/api/new_task", fetcher); + + // Fetch the very fist task. We can ignore everything except isLoading + // because the onSuccess handler will update `tasks` when ready. + const { isLoading } = useSWRImmutable("/api/new_task", fetcher, { + onSuccess: (data) => { + setTasks([data]); + }, + }); + + // Every time we submit an answer to the latest task, let the backend handle + // all the interactions then add the resulting task to the queue. This ends + // when we hit the done task. const { trigger, isMutating } = useSWRMutation( "/api/update_task", sendRequest, { onSuccess: async (data) => { const newTask = await data.json(); - console.log(newTask); - setDone(true); + // This is the more efficient way to update a react state array. + setTasks((oldTasks) => [...oldTasks, newTask]); }, } ); - const submitResponse = () => { + // Trigger a mutation that updates the current task. We should probably + // signal somewhere that this interaction is being processed. + const submitResponse = (t) => { trigger({ - id: registeredTask.id, + id: t.id, content: { rating: responseEl.current.value, }, }); }; + + // Show something informative while loading the first task. if (isLoading) { return
Loading
; } + // Iterate through each of the tasks and show it's contents, get a response to it, or show the done state. + // + // Right now this just works for the rating task. + // + // Displaying and fetching results for each task type should be factored into + // different components that handle the presentation and response structures. + // The results should be packaged into a single object with all the fields + // sent to the backend. return (
-
{registeredTask.id}
-
{registeredTask.task.type}
-
{registeredTask.task.text}
-
{registeredTask.task.summary}
-
- {registeredTask.task.scale.min} to {registeredTask.task.scale.max} -
- - - {done &&
Done!
} + {tasks.map((t) => ( +
+
{t.task.type}
+
{t.task.text}
+ {t.task.summary && ( + <> +
{t.task.summary}
+
+ {t.task.scale.min} to {t.task.scale.max} +
+ + + + )} +
+ ))}
); }