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

added Track 2, Sprint 6 #254

Merged
merged 1 commit into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions Track_2_ToDo_App/Sprint-06 - Advanced To-Do Details/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Sprint 6: Advanced Task Details
⏲️ _Est. time to complete: 60 min._ ⏲️

This sprint is designed to help students add additional details to the To-Do application. The sprint will walk students through adding details about the task such as Due Date, Priority, Additional Notes, and whether or not the Task has been completed.

**📕Feature: Add additional about to-do item**
1. [**📖 Add Due Date, Priority, Notes and Completion Status to To-Do item**](/Track_2_ToDo_App/Sprint-06%20-%20Advanced%20To-Do%20Details/Feature%201%20-%20Add%20Additional%20To-Do%20Details/User%20Story%201%20-%20Add%20additional%20details%20to%20to-do%20item.md)


<br/>

[🔼 Hackathon Home Page ](/Track_2_ToDo_App/README.md) | [◀ Previous Sprint](/Track_2_ToDo_App/Sprint-05%20-%20Advanced%20AI%20recommendations/README.md) | [Next sprint ▶](/Track_2_ToDo_App/Sprint-07%20-%20Advanced%20Styling%20Your%20Web%20App/README.md)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
DEBUG_APP="True"
USE_AZURE_OPENAI="True"
OPENAI_API_KEY="<get the key from https://platform.openai.com/api-keys>"
OPENAI_ORG_ID="<get the ord-id from https://platform.openai.com/account/organization>"
OPEN_AI_DEPLOYMENT_NAME="gpt-3.5-turbo"
AZURE_OPENAI_DEPLOYMENT_NAME="gpt-35-turbo"
AZURE_OPENAI_ENDPOINT=""
AZURE_OPENAI_API_KEY=""
AZURE_OPENAI_VERSION="2023-05-15"
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
###############################################################################
## Sprint 6: Advanced To-DO Details
## Feature 1: Add Additional Details to To-Do Items
## User Story 1: Add Due Date, Priority, Notes and Completion Status to To-Do item
############################################################################
import os
import json
from flask import Flask, render_template, request, redirect, url_for, g
from database import db, Todo
from recommendation_engine import RecommendationEngine
from tab import Tab

app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__)) # Get the directory of the this file
todo_file = os.path.join(basedir, 'todo_list.txt') # Create the path to the to-do list file using the directory
app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:///' + os.path.join(basedir, 'todos.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db.init_app(app)

with app.app_context():
db.create_all()

@app.before_request
def load_data_to_g():
todos = Todo.query.all()
g.todos = todos
g.todo = None
g.TabEnum = Tab
g.selectedTab = Tab.NONE


@app.route("/")
def index():
return render_template("index.html")

@app.route("/add", methods=["POST"])
def add_todo():

# Get the data from the form
todo = Todo(
name=request.form["todo"]
)

# Add the new ToDo to the list
db.session.add(todo)
db.session.commit()

# Add the new ToDo to the list
return redirect(url_for('index'))

# Details of ToDo Item
@app.route('/details/<int:id>', methods=['GET'])
def details(id):
g.selectedTab = Tab.DETAILS
g.todos = Todo.query.all()
g.todo = Todo.query.filter_by(id=id).first()

return render_template('index.html')

# Edit a new ToDo
@app.route('/edit/<int:id>', methods=['GET'])
def edit(id):
g.selectedTab = Tab.EDIT
g.todos = Todo.query.all()
g.todo = Todo.query.filter_by(id=id).first()

return render_template('index.html')

# Save existing To Do Item
@app.route('/update/<int:id>', methods=['POST'])
def update_todo(id):
g.selectedTab = Tab.DETAILS

if request.form.get('cancel') != None:
return redirect(url_for('index'))

# Get the data from the form
name = request.form['name']
due_date = request.form.get('duedate')
notes=request.form.get('notes')
priority=request.form.get('priority')
completed=request.form.get('completed')

todo = db.session.query(Todo).filter_by(id=id).first()
if todo != None:
todo.name = name

if due_date != "None":
todo.due_date = due_date

if notes != None:
todo.notes = notes

if priority != None:
todo.priority = int(priority)

if completed == None:
todo.completed = False
elif completed == "on":
todo.completed = True
#
db.session.add(todo)
db.session.commit()
#
return redirect(url_for('index'))


# Delete a ToDo
@app.route('/remove/<int:id>', methods=["POST"])
def remove_todo(id):
db.session.delete(Todo.query.filter_by(id=id).first())
db.session.commit()
return redirect(url_for('index'))

# Show AI recommendations
@app.route('/recommend/<int:id>', methods=['GET'])
@app.route('/recommend/<int:id>/<refresh>', methods=['GET'])
async def recommend(id, refresh=False):
g.selectedTab = Tab.RECOMMENDATIONS
recommendation_engine = RecommendationEngine()
g.todo = db.session.query(Todo).filter_by(id=id).first()

if g.todo and not refresh:
try:
#attempt to load any saved recommendation from the DB
if g.todo.recommendations_json is not None:
g.todo.recommendations = json.loads(g.todo.recommendations_json)
return render_template('index.html')
except ValueError as e:
print("Error:", e)

previous_links_str = None
if refresh:
g.todo.recommendations = json.loads(g.todo.recommendations_json)
# Extract links
links = [item["link"] for item in g.todo.recommendations]
# Convert list of links to a single string
previous_links_str = ", ".join(links)

g.todo.recommendations = await recommendation_engine.get_recommendations(g.todo.name, previous_links_str)

# Save the recommendations to the database
try:
g.todo.recommendations_json = json.dumps(g.todo.recommendations)
db.session.add(g.todo)
db.session.commit()
except Exception as e:
print(f"Error adding and committing todo: {e}")
return

return render_template('index.html')


if __name__ == "__main__":
app.run(debug=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Integer, String, Boolean, JSON, func
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
pass

db = SQLAlchemy(model_class=Base)

class Todo(db.Model):
id = db.Column(Integer, primary_key=True)
name = db.Column(String(100), nullable=False)
notes = db.Column(String(100))
priority = db.Column(Integer, default=0)
completed = db.Column(Boolean, default=False)
recommendations_json = db.Column(db.JSON) #json serialized version of recommendations for storing in DB
due_date = db.Column(String(50))

#transient variables (i.e., not stored in db)
recommendations = [] #recommendations as a collection

def __str__(self):
return self.name

def priority_str(self):
"""
Returns the priority as a string.
"""
str = "Not Set"
if self.priority == 1:
str = "High"
elif self.priority == 2:
str = "Medium"
elif self.priority == 3:
str = "Low"

return str

def completed_str(self):
"""
Returns the completed status as a string.
"""
if self.completed:
return "Yes"
else:
return "No"



Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from enum import Enum

class Priority(Enum):
NONE = 0,
HIGH = 1,
MEDIUM = 2,
LOW = 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import json
import asyncio
import semantic_kernel as sk
from services import Service
from openai import AzureOpenAI
from dotenv import dotenv_values

config = dotenv_values(".env")

#uses the USE_AZURE_OPENAI variable from the .env file to determine which AI service to use
#False means use OpenAI, True means use Azure OpenAI
selectedService = Service.AzureOpenAI if config.get("USE_AZURE_OPENAI") == "True" else Service.OpenAI
deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()

class RecommendationEngine:
def __init__(self):
deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()

self.client = AzureOpenAI(azure_endpoint = endpoint,
api_key=api_key,
api_version="2024-02-15-preview"
)


async def get_recommendations(self, keyword, previous_links_str=None):
prompt = f"""Please return 5 recommendations based on the input string: '{keyword}' using correct JSON syntax that contains a title and a hyperlink back to the supporting website. RETURN ONLY JSON AND NOTHING ELSE"""
system_prompt = """You are an administrative assistant bot who is good at giving
recommendations for tasks that need to be done by referencing website links that can provide
assistance to helping complete the task.

If there are not any recommendations simply return an empty collection.

EXPECTED OUTPUT:
Provide your response as a JSON object with the following schema:
[{"title": "...", "link": "..."},
{"title": "...", "link": "..."},
{"title": "...", "link": "..."}]
"""

if previous_links_str is not None:
prompt = prompt + f". EXCLUDE the following links from your recommendations: {previous_links_str}"

message_text = [{"role":"system","content":system_prompt},
{"role":"user","content":prompt},]

response = self.client.chat.completions.create(
model=deployment,
messages = message_text,
temperature=0.14,
max_tokens=800,
top_p=0.17,
frequency_penalty=0,
presence_penalty=0,
stop=None
)

result = response.choices[0].message.content
print(result)

try:
recommendation = json.loads(result)
except Exception as e:
print(f"Error loading recommendations: {e}")
recommendation = [{"title": "Sorry, unable to recommendation at this time", "link": ""}]

return recommendation

async def test_recommendation_engine():
engine = RecommendationEngine()
recommendations = await engine.get_recommendations("Buy a birthday gift for mom")
count = 1
for recommendation in recommendations:
print(f"{count} - {recommendation['title']}: {recommendation['link']}")
count += 1

if __name__ == "__main__":
asyncio.run(test_recommendation_engine())
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
This module defines an enumeration representing different services.
"""

from enum import Enum


class Service(Enum):
"""
Attributes:
OpenAI (str): Represents the OpenAI service.
AzureOpenAI (str): Represents the Azure OpenAI service.
HuggingFace (str): Represents the HuggingFace service.
"""

OpenAI = "openai"
AzureOpenAI = "azureopenai"
HuggingFace = "huggingface"
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
body {
background-color: f0f0f0; /* light grey */
color: darkslategray; /* default font color */
font-family: Arial, sans-serif;
background-image: url("../images/Designer02.jpeg");
background-repeat: no-repeat;
background-position: center;
background-size:cover;
}

h1 {
color: darkgray; /* font for the h1 header*/
}

.list-group-item {
color: #333; /* dark grey */
}



Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
document.addEventListener("DOMContentLoaded", function() {
const nameInput = document.getElementById("todo");

//add javascript to support speech recognition for the todo input field
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.lang = "en-US";
recognition.interimResults = false;

window.captureVoice = function() {
recognition.start();
nameInput.value = "Your microphone is activated, speak to record voice";
};

recognition.onresult = function(event) {
const transcript = event.results[0][0].transcript;
const recognizedText = transcript.endsWith('.') ? transcript.slice(0, -1) : transcript;
nameInput.value = recognizedText;
};

recognition.onspeechend = function() {
recognition.stop();
};

recognition.onnomatch = function(event) {
nameInput.value = "I didn't recognize that prompt.";
};

recognition.onerror = function(event) {
nameInput.value = "Error occurred in recognition: " + event.error;
};
});
Loading
Loading