-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* init commit of meal endpoints * meal reminder endpoint * email template typos * add to serverless * function outline * dump meal group data * meal summary lambda * remove debug statement * change get to post * add attendees table name * Adding a trigger Meal reminder button * log meal reminders * Remove debug * add env variable * import time * Missed empty var --------- Co-authored-by: Connor Monaghan <[email protected]>
- Loading branch information
Showing
12 changed files
with
365 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
'use client' | ||
import React, { useState } from "react"; | ||
|
||
export default function DiningPageClient() { | ||
const [status,setStatus] = useState({} as any) | ||
const handleSubmit = async (e) => { | ||
e.preventDefault(); | ||
|
||
const response = await fetch('/api/admin/meal/trigger', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({}), | ||
}) | ||
console.log(response) | ||
setStatus(response.ok ? {success: true, message: "Emails Triggered"} : { success: false, message: "Error" }) | ||
} | ||
|
||
return ( | ||
<div> | ||
{JSON.stringify(status) == "{}" ? null : <p className={`w-full p-3 rounded-lg ${status.success ? "bg-green-800": "bg-red-600"}`}>{status.message}</p> } | ||
<button onClick={handleSubmit} className="py-3 px-6 mt-3 float-right bg-chillired-500 rounded-lg block">Trigger Meal Reminder</button> | ||
</div> | ||
|
||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from "react"; | ||
import Layout from "@components/layout/layout"; | ||
import { Container } from "@components/layout/container"; | ||
import Navigation from "@components/admin/navigation"; | ||
import DiningPageClient from "./meal-client" | ||
// import { currentUser } from "@clerk/nextjs/server"; | ||
|
||
const pages = [ | ||
{ name: 'Admin', href: '/admin', current: true }, | ||
{ name: 'Dining', href: '/admin/dining', current: true }, | ||
] | ||
|
||
export default async function AdminUserPage() { | ||
|
||
// const loggedInUser = await currentUser(); | ||
|
||
return (<Layout> | ||
<section className={`flex-1 relative transition duration-150 ease-out body-font overflow-hidden bg-none text-white`}> | ||
{" "} | ||
<Container width="large" padding="tight" className={`flex-1 pb-2`} size="top"> | ||
<Navigation pages={pages} /> | ||
</Container> | ||
<Container width="large" padding="tight" className={`flex-1 pb-2`} size="none"> | ||
<DiningPageClient /> | ||
</Container> | ||
</section> | ||
</Layout> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { createClerkClient } from '@clerk/backend'; | ||
import { auth } from '@clerk/nextjs/server'; | ||
import { NextRequest } from 'next/server'; | ||
|
||
export async function POST(_req: NextRequest) { | ||
const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }); | ||
const {userId} = auth(); | ||
|
||
if(!userId){ | ||
return Response.json({error: "User is not signed in."}, { status: 401 }); | ||
} | ||
|
||
const requestingUser = await clerkClient.users.getUser(userId); | ||
if(!requestingUser.publicMetadata.admin){ | ||
return Response.json({error: "User is does not have permissions."}, { status: 401 }); | ||
} | ||
|
||
const triggerUrl = process.env.LAMBDA_MEAL_TRIGGER_REMINDER | ||
console.log(triggerUrl) | ||
const triggerResponse = await fetch(triggerUrl, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
}) | ||
|
||
return triggerResponse.ok ? Response.json({message: "Email triggered"}, {status: 200}) : Response.json({message: "Email Failed"}, {status: 500}) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import json | ||
import os | ||
import logging | ||
|
||
import boto3 | ||
from boto3.dynamodb.conditions import Key, Attr | ||
from shared import DecimalEncoder | ||
|
||
logger = logging.getLogger() | ||
logger.setLevel(logging.INFO) | ||
|
||
## ENV | ||
event_table_name = os.environ.get("EVENT_TABLE_NAME") | ||
logger.info(event_table_name) | ||
|
||
dynamodb_client = boto3.client('dynamodb') | ||
db = boto3.resource('dynamodb') | ||
table = db.Table(event_table_name) | ||
|
||
def err(msg:str, code=400, logmsg=None, **kwargs): | ||
logmsg = logmsg if logmsg else msg | ||
logger.error(logmsg) | ||
for k in kwargs: | ||
logger.error(k+":"+kwargs[k]) | ||
return { | ||
'statusCode': code, | ||
'body': json.dumps({'error': msg}) | ||
} | ||
|
||
def lambda_handler(event, context): | ||
scan_response = table.scan(FilterExpression=Key('PK').begins_with('GROUP#')) | ||
|
||
return { | ||
'statusCode': 200, | ||
'body': json.dumps(scan_response, cls=DecimalEncoder.DecimalEncoder) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import json | ||
import logging | ||
|
||
import boto3 | ||
from boto3.dynamodb.conditions import Key, Attr | ||
from shared import DecimalEncoder | ||
import os | ||
import time | ||
|
||
## ENV | ||
attendees_table_name = os.environ.get("ATTENDEES_TABLE_NAME") | ||
send_email_lambda = os.environ.get("SEND_EMAIL_LAMBDA") | ||
event_table_name = os.environ.get("EVENT_TABLE_NAME") | ||
|
||
logger = logging.getLogger() | ||
logger.setLevel("INFO") | ||
|
||
# profile_name='danceengine-admin' | ||
# boto3.setup_default_session(profile_name=profile_name) | ||
# logging.basicConfig() | ||
|
||
db = boto3.resource('dynamodb') | ||
table = db.Table(attendees_table_name) | ||
event_table = db.Table(event_table_name) | ||
|
||
lambda_client = boto3.client('lambda') | ||
|
||
def err(msg:str, code=400, logmsg=None, **kwargs): | ||
logmsg = logmsg if logmsg else msg | ||
logger.error(logmsg) | ||
for k in kwargs: | ||
logger.error(k+":"+kwargs[k]) | ||
return { | ||
'statusCode': code, | ||
'body': json.dumps({'error': msg}) | ||
} | ||
|
||
def lambda_handler(event, context): | ||
logger.info(f"Event {event}") | ||
if event['requestContext']['http']['method'] != "POST": return err("Method not allowed, make POST request", code=405) | ||
|
||
response = table.scan(FilterExpression=Key('active').eq(True)) | ||
|
||
filtered_items = [item for item in response['Items'] if (item['access'][2] == 1) and ('meal_preferences' not in item or item['meal_preferences'] == None or any(choice == -1 for choice in item['meal_preferences']['choices']))] | ||
# logger.info(filtered_items) | ||
|
||
#! If no matches should problably return a 404 | ||
for item in filtered_items: | ||
# send the email with these details | ||
logger.info("Invoking send_email lambda") | ||
response = lambda_client.invoke( | ||
FunctionName=send_email_lambda, | ||
InvocationType='Event', | ||
Payload=json.dumps({ | ||
'email_type':"meal_reminder", | ||
'email':item['email'], | ||
'ticket_number':item['ticket_number'], | ||
}, cls=DecimalEncoder.DecimalEncoder), | ||
) | ||
logger.info(response) | ||
|
||
event_table.put_item( | ||
Item={ | ||
'PK': "EMAIL#MEALREMINDER".format(), | ||
'SK': "DETAIL#{}".format(time.time()), | ||
'timestamp': "{}".format(time.time()) | ||
} | ||
) | ||
|
||
return {'statusCode': 200 } | ||
|
||
# lambda_handler({'requestContext':{'http':{'method':"GET"}}}, None) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import json | ||
import logging | ||
|
||
import boto3 | ||
from boto3.dynamodb.conditions import Key, Attr | ||
from shared import DecimalEncoder | ||
import os | ||
|
||
## ENV | ||
attendees_table_name = os.environ.get("ATTENDEES_TABLE_NAME") | ||
event_table_name = os.environ.get("EVENT_TABLE_NAME") | ||
|
||
logger = logging.getLogger() | ||
logger.setLevel("INFO") | ||
|
||
# profile_name='danceengine-admin' | ||
# boto3.setup_default_session(profile_name=profile_name) | ||
# logging.basicConfig() | ||
|
||
db = boto3.resource('dynamodb') | ||
table = db.Table(attendees_table_name) | ||
event_table = db.Table(event_table_name) | ||
|
||
lambda_client = boto3.client('lambda') | ||
|
||
def err(msg:str, code=400, logmsg=None, **kwargs): | ||
logmsg = logmsg if logmsg else msg | ||
logger.error(logmsg) | ||
for k in kwargs: | ||
logger.error(k+":"+kwargs[k]) | ||
return { | ||
'statusCode': code, | ||
'body': json.dumps({'error': msg}) | ||
} | ||
|
||
def get_last_email(): | ||
response = table.scan(FilterExpression=Key('PK').eq("EMAIL#MEALREMINDER")) | ||
sorted_list = sorted(response['Items'], key=lambda item: item['timestamp']) if len(response['Items'] > 0) else None | ||
|
||
return sorted_list | ||
|
||
def lambda_handler(event, context): | ||
logger.info(f"Event {event}") | ||
if event['requestContext']['http']['method'] != "GET": return err("Method not allowed, make GET request", code=405) | ||
|
||
response = table.scan(FilterExpression=Key('active').eq(True)) | ||
|
||
course_mappings = [ | ||
{0: "Vegetable Terrine", 1: "Chicken Liver Pate"}, | ||
{0: "Roasted Onion", 1: "Fish and Prawn Risotto", 2: "Chicken Supreme"}, | ||
{0: "Fruit Platter", 1: "Bread and Butter Pudding"} | ||
] | ||
num_courses = 3 | ||
course_frequencies = [{} for _ in range(num_courses)] | ||
|
||
not_selected_count = 0 # All choices are -1 (not selected) | ||
incomplete_count = 0 # Some choices are -1 (incomplete selection) | ||
not_wanted_count = 0 # Any choice is -99 (not wanted) | ||
selected_count = 0 # All choices are >= 0 (options selected) | ||
|
||
filtered_items = [item for item in response['Items'] if (item['access'][2] == 1)] | ||
|
||
#! If no matches should problably return a 404 | ||
for item in filtered_items: | ||
if ('meal_preferences' not in item): | ||
not_selected_count += 1 | ||
continue | ||
elif (item['meal_preferences'] is None): | ||
not_selected_count += 1 | ||
continue | ||
|
||
meal_prefs = item['meal_preferences'] | ||
if meal_prefs and 'choices' in meal_prefs: | ||
choices = meal_prefs['choices'] | ||
|
||
if all(choice == -1 for choice in choices): | ||
not_selected_count += 1 | ||
elif any(choice == -1 for choice in choices): | ||
incomplete_count += 1 | ||
elif any(choice == -99 for choice in choices): | ||
not_wanted_count += 1 | ||
elif all(choice >= 0 for choice in choices): | ||
selected_count += 1 | ||
|
||
for i, choice in enumerate(choices): | ||
if choice >= 0 and choice in course_mappings[i]: | ||
dish_name = course_mappings[i][choice] | ||
if dish_name in course_frequencies[i]: | ||
course_frequencies[i][dish_name] += 1 | ||
else: | ||
course_frequencies[i][dish_name] = 1 | ||
|
||
logger.info(f"Statistics: Not selected: {not_selected_count}, Incomplete: {incomplete_count}, Not wanted: {not_wanted_count}, Selected: {selected_count}") | ||
logger.info(f"Course Frequencies: {course_frequencies}") | ||
|
||
return { | ||
'statusCode': 200, | ||
'body': json.dumps({ | ||
'statistics': { | ||
'not_selected_count': not_selected_count, | ||
'incomplete_count': incomplete_count, | ||
'not_wanted_count': not_wanted_count, | ||
'selected_count': selected_count, | ||
'course_frequencies': course_frequencies, | ||
'reminders_sent': get_last_email() | ||
}}, cls=DecimalEncoder.DecimalEncoder) | ||
} | ||
|
||
# from pprint import pprint | ||
# pprint(lambda_handler({'requestContext':{'http':{'method':"GET"}}}, None)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.