Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish MCQ Topic and MCQ Passage Lambda Functions #18

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
39 changes: 39 additions & 0 deletions backend/create_game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import boto3
import json

# assign DynamoDB table to modify
table_name = "multiplayer-games"
dynamo = boto3.resource('dynamodb')
game_table = dynamo.Table(table_name)

def lambda_handler(event, context):
''' event should have the following fields:
- game_id: int,
- topic: string,
- questions: list of dicts
Adds the game to the game table. Returns an empty dictionary if success.
'''
try:
data = json.loads(event["body"])
game_id = data["game_id"]
topic = data["topic"]
questions = json.dumps(data["questions"])

if game_id == 0:
return {
'statusCode': 400,
'body': json.dumps({'error': 'Game ID cannot be 0'})
}

item = {"game_id": game_id, "topic": topic, "questions": questions, "players": "{}"}
game_table.put_item(Item=item)

return {
'statusCode': 200,
'body': json.dumps({})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'message': str(e)})
}
48 changes: 48 additions & 0 deletions backend/get_game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import boto3
import json

# assign DynamoDB table to modify
table_name = "multiplayer-games"
dynamo = boto3.resource('dynamodb')
game_table = dynamo.Table(table_name)

def lambda_handler(event, context):
''' event should have the following fields:
- game_id: int
Returns (topic, questions, num_players). If the game is not found, return ('', [], -1).

'''
try:
data = json.loads(event["body"])
game_id = data["game_id"]

if game_id == 0:
return {
'statusCode': 400,
'body': json.dumps({'error': 'Game ID cannot be 0'})
}

response = game_table.get_item(Key={"game_id": game_id})
if 'Item' not in response:
return {
'statusCode': 200,
'body': json.dumps({
'topic': '',
'questions': [],
'num_players': -1
})
}
else:
return {
'statusCode': 200,
'body': json.dumps({
'topic': response['Item']['topic'],
'questions': json.loads(response['Item']['questions']),
'num_players': len(json.loads(response['Item']['players']))
})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'message': str(e)})
}
56 changes: 56 additions & 0 deletions backend/lambda_functions/add_player.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import boto3
import json


table_name = "multiplayer-games"
dynamo = boto3.resource('dynamodb')
questions_table = dynamo.Table(table_name)


def lambda_handler(event, context):
''' [event] contains the following keys:
- game_id: id of game to add player results to
- score: score of player
- name: player name to display
Adds player and score to game players
'''

try:
data = json.loads(event["body"])
id = data["game_id"]
score = data["score"]
name = data["name"]

key = {'game_id': id}
response = questions_table.get_item(Key=key)
if 'Item' in response:
players = json.loads(response['Item']['players'])

# add to players
if name in players:
players[name] = max(players[name], score)
else:
players[name] = score


# update table
response = questions_table.update_item(
Key=key,
UpdateExpression="set players=:q",
ExpressionAttributeValues={
':q': json.dumps(players)},
ReturnValues="UPDATED_NEW")
return {
'statusCode': 200,
'body': json.dumps({})
}
else:
return {
'statusCode': 404,
'body': json.dumps({'error': "couldn't load players"})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'message': str(e)})
}
81 changes: 81 additions & 0 deletions backend/lambda_functions/get_mcq_passage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import boto3
import json
import os
import openai


# assign DynamoDB table to modify
table_name = "trivai-questions"
dynamo = boto3.resource('dynamodb')
questions_table = dynamo.Table(table_name)

# constants
OPENAI_API_KEY = os.environ["OPENAI_KEY"]
MAX_QUESTIONS = 20


# entry point for new events, parses event
def lambda_handler(event, context):
''' [event] contains the following keys:
- passage: passage to generate questions for
- num_questions: number of questions to generate
'''

try:
data = json.loads(event["body"])
passage = data["passage"]
num_questions = data["num_questions"]

if num_questions > MAX_QUESTIONS: # FIXME: cap instead of error?
return {
'statusCode': 400,
'body': json.dumps({"error": "Cannot generate more than %d questions at a time." % MAX_QUESTIONS})
}

status, res = mcq_passage(passage, num_questions)
if status == 200:
return {
'statusCode': 200,
'body': res
}
except Exception as e:
return {
'statusCode': 400,
'body': json.dumps({'error': e})
}


def mcq_passage(passage, num_questions):
mcq_passage_prompt = """
Create %d questions about the following passage in exactly the following json format, which is surrounded in brackets. Make sure each question has exactly 4 answer choices. Do not output anything other than the questions in the specified format:
[
{
"question": question,
"options": [option 0, option 1, option 2, option 3],
"answer_id": answer_id
},
{
"question": question,
"options": [option 0, option 1, option 2, option 3],
"answer_id": answer_id
}, ...
]
Passage: %s
""" % (num_questions, passage)
return get_chatgpt(mcq_passage_prompt)


# get ChatGPT response to given prompt
def get_chatgpt(prompt): # (int, str)
completion = openai.ChatCompletion.create(
api_key=OPENAI_API_KEY,
model="gpt-3.5-turbo",
messages=[{"role": "user",
"content": prompt}]
)
content = completion["choices"][0]["message"]["content"] # str

try:
return 200, content
except:
return 400, json.dumps({"error": "OpenAI did not return a json string"})
113 changes: 113 additions & 0 deletions backend/lambda_functions/get_mcq_topic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import boto3
import json
import os
import openai
import random


# assign DynamoDB table to modify
table_name = "trivai-questions"
dynamo = boto3.resource('dynamodb')
questions_table = dynamo.Table(table_name)

# constants
OPENAI_API_KEY = os.environ["OPENAI_KEY"]
MAX_QUESTIONS = 20


# entry point for new events, parses event
def lambda_handler(event, context):
''' [event] contains the following keys:
- topic: subject to generate questions for
- num_questions: number of questions to generate
'''

try:
data = json.loads(event["body"])
topic = data["topic"]
num_questions = data["num_questions"]

if num_questions > MAX_QUESTIONS: # FIXME: cap instead of error?
return {
'statusCode': 400,
'body': json.dumps({"error": "Cannot generate more than %d questions at a time." % MAX_QUESTIONS})
}

res = ""
key = {'topic_id': topic}
response = questions_table.get_item(Key=key)
if 'Item' in response:
# list of dictionaries
questions = json.loads(response['Item']['questions'])
if len(questions) < num_questions:
# Generate more questions and put the updated list of questions into the database
status, more_questions = mcq_topic(
topic, num_questions - len(questions))
questions = questions + \
json.loads(more_questions) # list of dicts
response = questions_table.update_item(
Key=key,
UpdateExpression="set questions=:q",
ExpressionAttributeValues={
':q': json.dumps(questions)},
ReturnValues="UPDATED_NEW")
else:
# Randomly choose num_questions questions to return
questions = random.sample(questions, num_questions)
# return questions
return {
'statusCode': 200,
'body': json.dumps(questions)
}
else:
status, res = mcq_topic(topic, num_questions)
# store if not error
if status == 200:
item = {"topic_id": topic, "questions": res}
print(item)
questions_table.put_item(Item=item)
# return json.loads(res)
return {
'statusCode': 200,
'body': res
}
except Exception as e:
return {
'statusCode': 400,
'body': json.dumps({'error': e})
}


def mcq_topic(topic, num_questions): # (int, str)
mcq_topic_prompt = """
Create %d questions about %s in exactly the following json format, including the outer brackets:
[
{
"question": question,
"options": [option 0, option 1, option 2, option 3],
"answer_id": answer_id
},
{
"question": question,
"options": [option 0, option 1, option 2, option 3],
"answer_id": answer_id
}, ...
]
""" % (num_questions, topic)
return get_chatgpt(mcq_topic_prompt)


# get ChatGPT response to given prompt
def get_chatgpt(prompt): # (int, str)
completion = openai.ChatCompletion.create(
api_key=OPENAI_API_KEY,
model="gpt-3.5-turbo",
messages=[{"role": "user",
"content": prompt}]
)
content = completion["choices"][0]["message"]["content"] # str

try:
return 200, content
except:
return 400, json.dumps({"error": "OpenAI did not return a json string"})
37 changes: 37 additions & 0 deletions backend/lambda_functions/get_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import boto3
import json


table_name = "multiplayer-games"
dynamo = boto3.resource('dynamodb')
questions_table = dynamo.Table(table_name)


def lambda_handler(event, context):
''' [event] contains the following keys:
- game_id: id of game to get results for
Returns all finished players for game game_id
'''
try:
data = json.loads(event["body"])
id = data["game_id"]

key = {'game_id': id}
response = questions_table.get_item(Key=key)
if 'Item' in response:
players = json.loads(response['Item']['players'])
players = json.dumps(players)
return {
'statusCode': 200,
'body': players
}
else:
return {
'statusCode': 404,
'body': json.dumps({'error': "couldn't load players"})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'message': str(e)})
}
Binary file added backend/lambda_functions/openai-layer.zip
Binary file not shown.