Skip to content

Commit

Permalink
Merge pull request #179 from wizelineacademy/sqs-integration-with-lalo
Browse files Browse the repository at this point in the history
Sqs integration with lalo
  • Loading branch information
pedroalonsoms authored Jun 2, 2024
2 parents f9ba5c8 + 4e55ea4 commit 6faa5d1
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 53 deletions.
1 change: 1 addition & 0 deletions .github/workflows/compile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
env:
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
POSTGRES_URL: ${{ secrets.POSTGRES_URL }}
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}

jobs:
build:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
env:
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
POSTGRES_URL: ${{ secrets.POSTGRES_URL }}
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}

jobs:
build:
Expand Down Expand Up @@ -45,6 +46,7 @@ jobs:
run: |
sst secret set AuthSecret ${{ secrets.AUTH_SECRET }} --stage=prod
sst secret set PostgresURL ${{ secrets.POSTGRES_URL }} --stage=prod
sst secret set OpenAIKey ${{ secrets.OPENAI_KEY }} --stage=prod
- name: Deploy to AWS with SST
run: |
Expand Down
71 changes: 58 additions & 13 deletions app/(base)/projects/[projectId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import GaugeChart from "@/components/GaugeChart";
import { useMutation, useQuery } from "@tanstack/react-query";
import {
deleteProjectById,
getUpdateFeedbackHistory,
getProjectById,
updateFeedback,
} from "@/services/project";
Expand All @@ -17,6 +18,7 @@ import {
} from "@/services/sprintSurvey";

import ChevronDownIcon from "@/components/icons/ChevronDownIcon";
import NoDataCard from "@/components/NoDataCard";
import Loader from "@/components/Loader";

const Project = ({ params }: { params: { projectId: string } }) => {
Expand Down Expand Up @@ -160,19 +162,7 @@ const Project = ({ params }: { params: { projectId: string } }) => {
<div className="relative z-10 flex items-center gap-1">
{/* popup */}
{isUpdateFeedbackPopupOpen && (
<div className="absolute right-full top-full z-0 text-nowrap rounded-xl bg-white p-4 drop-shadow-lg">
<h2 className="mb-10 text-xl font-medium">
Update History
</h2>
<div className="flex gap-20">
<p>Survey 04/04/2024</p>
<p className="text-green-600">Completed</p>
</div>
<div className="flex gap-20">
<p>Survey 04/04/2024</p>
<p className="text-green-600">Completed</p>
</div>
</div>
<UpdateFeedbackHistoryPopup projectId={projectId} />
)}

{/* open-popup-button */}
Expand Down Expand Up @@ -301,4 +291,59 @@ const Project = ({ params }: { params: { projectId: string } }) => {
);
};

const UpdateFeedbackHistoryPopup = ({ projectId }: { projectId: number }) => {
const updateFeedbackHistoryQuery = useQuery({
queryKey: ["update-feedback-history"],
queryFn: () => getUpdateFeedbackHistory({ projectId }),
retry: false,
});

const formatDate = (date: Date) => {
return new Date(date).toLocaleString("default", {
day: "numeric",
month: "short",
year: "numeric",
});
};

if (updateFeedbackHistoryQuery.isError) {
return <NoDataCard text={updateFeedbackHistoryQuery.error.message} />;
}

if (
updateFeedbackHistoryQuery.isLoading ||
!updateFeedbackHistoryQuery.data
) {
return <p>Loading...</p>;
}

return (
<div className="absolute right-full top-full z-0 text-nowrap rounded-xl bg-white p-4 drop-shadow-lg">
<h2 className="mb-4 text-xl font-medium">Update History</h2>
{updateFeedbackHistoryQuery.data.map((survey, idx) => (
<div key={idx} className="flex justify-between gap-20">
<p>
{survey.type === "SPRINT"
? "Sprint Survey"
: survey.type === "FINAL"
? "Final Survey"
: "UNREACHABLE"}{" "}
-{" "}
<span className="text-gray-500">
{formatDate(survey.scheduledAt)}
</span>
</p>
{survey.status === "COMPLETED" ? (
<p className="text-green-600">Completed</p>
) : survey.status === "PENDING" ? (
<p className="text-red-600">Pending</p>
) : survey.status === "NOT_AVAILABLE" ? (
<p className="text-gray-600">Pending</p>
) : null}
</div>
))}
</div>
);
};

export default Project;
1 change: 1 addition & 0 deletions dev-sst.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ fi

sst secret set AuthSecret $AUTH_SECRET --stage=dev
sst secret set PostgresURL $POSTGRES_URL --stage=dev
sst secret set OpenAIKey $OPENAI_KEY --stage=dev
sst dev next dev --stage dev
32 changes: 23 additions & 9 deletions handlers/subscriber.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { SQSMessageBody } from "@/types/types";
import { feedbackAnalysis } from "@/services/rag";
import { DeleteMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
import { Resource } from "sst";

// TODO: this stuff should come from either @sst/sqs or @aws/sqs
interface Record {
Expand All @@ -16,16 +18,28 @@ interface EventType {
}

export const handler = async (event: EventType) => {
const messageBody = JSON.parse(event.Records[0].body) as SQSMessageBody;
const messageBody = JSON.parse(event.Records[0].body) as {
sprintSurveyId: number;
};
console.log("HANDLER", messageBody.sprintSurveyId);
console.log("RECORDS", event.Records);

const min = 10000; // 10 seconds in milliseconds
const max = 30000; // 30 seconds in milliseconds
const randomDuration = Math.floor(Math.random() * (max - min + 1)) + min;
// Note: we should wrap everything inside a try-catch block to avoid putting SQS messages in retention state
try {
await feedbackAnalysis(messageBody.sprintSurveyId);
} catch (error) {
console.log("ERROR:", error);
}

// Set the timeout with the random duration
setTimeout(() => {
console.log(messageBody);
}, randomDuration);
const client = new SQSClient();
const response = await client.send(
new DeleteMessageCommand({
QueueUrl: Resource.FeedbackFlowQueue.url,
ReceiptHandle: event.Records[0].receiptHandle,
}),
);

console.log("del response", response);

return "ok";
};
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 90 additions & 18 deletions services/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import {
sprintSurvey,
user,
} from "@/db/schema";
import { and, eq, ne, or } from "drizzle-orm";
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
import { and, asc, desc, eq, ne, or } from "drizzle-orm";
import {
SQSClient,
SendMessageBatchCommand,
SendMessageBatchRequestEntry,
} from "@aws-sdk/client-sqs";
import { Resource } from "sst";
import { SQSMessageBody } from "@/types/types";

export async function getProjects() {
// get all of the projects in which the user is either a member or a manager
Expand Down Expand Up @@ -155,24 +158,93 @@ export async function getCoworkersInProject(sprintSurveyId: number) {
);
}

export async function updateFeedback(projectId: number) {
console.log(projectId);
export async function getUpdateFeedbackHistory({
projectId,
}: {
projectId: number;
}) {
type SurveyStatus = "COMPLETED" | "PENDING" | "NOT_AVAILABLE";
interface Survey {
type: "SPRINT" | "FINAL";
scheduledAt: Date;
status: SurveyStatus;
}

const client = new SQSClient();
const getStatus = (scheduledAt: Date, processed: boolean): SurveyStatus => {
const today = new Date();
if (scheduledAt <= today) {
// meaning it was already sent to users
if (processed) {
return "COMPLETED";
} else {
return "PENDING";
}
}
return "NOT_AVAILABLE";
};

for (let i = 0; i < 20; i++) {
console.log("SCHEDULED EVENT NO", i);
const surveys: Survey[] = [];

const messageBody = {
projectId: i,
content: "Hello from the subscriber",
} as SQSMessageBody;
const sprintSurveys = await db
.select()
.from(sprintSurvey)
.where(eq(sprintSurvey.projectId, projectId))
.orderBy(asc(sprintSurvey.scheduledAt));

sprintSurveys.forEach((s) => {
surveys.push({
type: "SPRINT",
// TODO: make these types from drizzle schema to be non-null
scheduledAt: s.scheduledAt as Date,
status: getStatus(s.scheduledAt as Date, s.processed as boolean),
});
});

const finalSurveys = await db
.select()
.from(finalSurvey)
.where(eq(finalSurvey.projectId, projectId))
.orderBy(asc(finalSurvey.scheduledAt));

finalSurveys.forEach((s) => {
surveys.push({
type: "FINAL",
// TODO: make these types from drizzle schema to be non-null
scheduledAt: s.scheduledAt as Date,
status: getStatus(s.scheduledAt as Date, s.processed as boolean),
});
});

if (surveys.length === 0) {
throw new Error("No feedback history available");
}

return surveys;
}

await client.send(
new SendMessageCommand({
QueueUrl: Resource.FeedbackFlowQueue.url,
MessageBody: JSON.stringify(messageBody),
export async function updateFeedback(projectId: number) {
console.log("UPDATE_FEEDBACK", projectId);
const client = new SQSClient();

const sprintSurveyIds = [52, 53, 54, 55]; // This should come from db

const entries: SendMessageBatchRequestEntry[] = sprintSurveyIds.map(
(sprintSurveyId) => ({
Id: crypto.randomUUID(),
MessageGroupId: "UPDATE_FEEDBACK",
MessageDeduplicationId: crypto.randomUUID(),
MessageBody: JSON.stringify({
sprintSurveyId,
}),
);
}
}),
);

const response = await client.send(
new SendMessageBatchCommand({
QueueUrl: Resource.FeedbackFlowQueue.url,
Entries: entries,
}),
);

console.log("response", response);
}
8 changes: 2 additions & 6 deletions services/rag.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"use server";
import OpenAI from "openai";
import db from "@/db/drizzle";
import { and, count, eq, inArray } from "drizzle-orm";
import { auth } from "@/auth";
import similarity from "compute-cosine-similarity";

import {
Expand Down Expand Up @@ -516,15 +514,13 @@ async function getQuestionsSkills(sprintSurveyId: number) {
} */

export async function rulerAnalysis() {
const session = await auth();
const userId = session?.user?.id;
if (!userId) throw new Error("You must be signed in");

// recommend resources only if the mood of the user is negative, check the previous resources recommended to experiment
}

// Main function
export async function feedbackAnalysis(sprintSurveyId: number) {
console.log(`*** PROCESSING SPRINTSURVEYID ${sprintSurveyId} ***`);

const processedSurvey = await db
.select({ processed: sprintSurvey.processed })
.from(sprintSurvey)
Expand Down
8 changes: 8 additions & 0 deletions sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ declare module "sst" {
type: "sst.sst.Secret"
value: string
}
FeedbackFlowAppf: {
type: "sst.aws.Nextjs"
url: string
}
FeedbackFlowBucket: {
name: string
type: "sst.aws.Bucket"
Expand All @@ -15,6 +19,10 @@ declare module "sst" {
type: "sst.aws.Queue"
url: string
}
OpenAIKey: {
type: "sst.sst.Secret"
value: string
}
PostgresURL: {
type: "sst.sst.Secret"
value: string
Expand Down
Loading

0 comments on commit 6faa5d1

Please sign in to comment.