Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
de4c88f
Merge pull request #7 from ammarali0416/development
ammarali0416 Nov 30, 2023
5783e0c
- can upload class level files to blob storage
ammarali0416 Dec 1, 2023
9cdc981
- splitting the class management from sidebar
ammarali0416 Dec 1, 2023
4a2ebed
- student class functions in separate module
ammarali0416 Dec 1, 2023
2693cfc
- DDL for modules table
ammarali0416 Dec 1, 2023
5c5b604
- teacher can delete modules now
ammarali0416 Dec 1, 2023
a5dd2ee
- made the UI a little nicer
ammarali0416 Dec 2, 2023
48a82ac
Merge pull request #10 from ammarali0416/feature/blob_storage_upload
ammarali0416 Dec 2, 2023
667a521
- a simple widget to let the user choose context
ammarali0416 Dec 2, 2023
c154f0d
- context selection widget can find blobs
ammarali0416 Dec 3, 2023
c01bb01
- class level files always included in context
ammarali0416 Dec 3, 2023
19bd109
- successfully added files to the open ai endpoint
ammarali0416 Dec 3, 2023
edcdf7a
- create a system prompt to prep study buddy
ammarali0416 Dec 3, 2023
11927d1
- blobs uploaded to openai stored in bin
ammarali0416 Dec 3, 2023
adab1e0
- files get attached to assistant
ammarali0416 Dec 3, 2023
7e9fbf8
- saved temp file
ammarali0416 Dec 3, 2023
e9314b1
- file clean up logic change
ammarali0416 Dec 3, 2023
2576d54
Merge pull request #15 from ammarali0416/feature/choose_context
ammarali0416 Dec 3, 2023
488ba3e
- IT WORKDS
ammarali0416 Dec 4, 2023
7b68859
- feature complete
ammarali0416 Dec 4, 2023
7aec708
Merge pull request #18 from ammarali0416/freature16-chat-interface
ammarali0416 Dec 4, 2023
5448f9e
- removed re
ammarali0416 Dec 4, 2023
2870b1b
- requirements clean up
ammarali0416 Dec 4, 2023
e8fdf64
- updated requirements.txt
ammarali0416 Dec 5, 2023
fd16bdd
Add or update the Azure App Service build and deployment workflow config
ammarali0416 Dec 5, 2023
5521547
- edited requirements file
ammarali0416 Dec 5, 2023
a935691
Added descriptions for each button on sidebar
aanam20 Dec 5, 2023
101f231
- clean up on the teacher side bar buttons
ammarali0416 Dec 5, 2023
37c5c03
- student side bar cleaned up
ammarali0416 Dec 5, 2023
1dedb5d
Merge pull request #19 from ammarali0416/8-ui-Demo-Version
ammarali0416 Dec 5, 2023
0f50ac6
- decreased wait time sot imporve performance
ammarali0416 Dec 6, 2023
5c582f7
Merge pull request #20 from ammarali0416/feature/faq_fix
ammarali0416 Dec 6, 2023
20c391e
Merge branch 'feature/assignments_database' into development
ammarali0416 Dec 6, 2023
aaec53a
- made note that assignments is a future feature
ammarali0416 Dec 6, 2023
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
1 change: 1 addition & 0 deletions .bin/files.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"id": "file-gwWAHylwSsbgiGtgueJXDQy7", "bytes": 22887384, "created_at": 1701634508, "filename": "10dayMBA - 1.pdf", "object": "file", "purpose": "assistants", "status": "processed", "status_details": null}, {"id": "file-F1Up2lgzKDRblggN0NBpBxjE", "bytes": 13464, "created_at": 1701634510, "filename": "s1w1 notes.docx", "object": "file", "purpose": "assistants", "status": "processed", "status_details": null}, {"id": "file-NUzY5dlGyYfneoeUhKgwPnRt", "bytes": 13875, "created_at": 1701634512, "filename": "s2w1 notes.docx", "object": "file", "purpose": "assistants", "status": "processed", "status_details": null}]
67 changes: 67 additions & 0 deletions .github/workflows/development_timetostudybuddy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions

name: Build and deploy Python app to Azure Web App - timetostudybuddy

on:
push:
branches:
- development
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python version
uses: actions/setup-python@v1
with:
python-version: '3.9'

- name: Create and start virtual environment
run: |
python -m venv venv
source venv/bin/activate

- name: Install dependencies
run: pip install -r requirements.txt

# Optional: Add step to run tests here (PyTest, Django test suites, etc.)
- name: Zip artifact for deployment
run: zip release.zip ./* -r

- name: Upload artifact for deployment jobs
uses: actions/upload-artifact@v3
with:
name: python-app
path: |
release.zip
!venv/

deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- name: Download artifact from build job
uses: actions/download-artifact@v3
with:
name: python-app

- name: Unzip artifact for deployment
run: unzip release.zip

- name: 'Deploy to Azure Web App'
uses: azure/webapps-deploy@v2
id: deploy-to-webapp
with:
app-name: 'timetostudybuddy'
slot-name: 'Production'
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_55AB58D01B2644DE951E25FDE92530B2 }}
8 changes: 8 additions & 0 deletions SQL Scripts/Objects.sql
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ CREATE TABLE STUDYBUDDY.FAQs (
CONSTRAINT FK_FAQs_Class FOREIGN KEY (class_id) REFERENCES master.STUDYBUDDY.Classes(class_id),
CONSTRAINT FK_FAQs_User FOREIGN KEY (user_id) REFERENCES STUDYBUDDY.Users(user_id)
);

CREATE TABLE master.STUDYBUDDY.Modules (
module_id int IDENTITY(1,1) NOT NULL,
module_name nvarchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
class_id int NOT NULL,
CONSTRAINT PK__Modules__ModuleID PRIMARY KEY (module_id),
CONSTRAINT FK_ClassModule FOREIGN KEY (class_id) REFERENCES master.STUDYBUDDY.Classes(class_id)
);
181 changes: 181 additions & 0 deletions Scripts/__chatscreen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# __chatscreen.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: ammar syed ali <https://www.linkedin.co +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2023/12/02 15:17:52 by ammar syed #+# #+# #
# Updated: 2023/12/02 15:17:52 by ammar syed ### ########.fr #
# #
# **************************************************************************** #
import streamlit as st
import os
import openai
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient
from Scripts import azsqldb, sessionvars, azblob as azb
import json
from dotenv import load_dotenv, find_dotenv

load_dotenv(find_dotenv())

sessionvars.initialize_session_vars()

def delete_files_from_openai():
total_files = 0
progress_value = 0

# First list of files
files = st.session_state.ai_client.files.list()
total_files += len(files.data) if files.data else 0

# Second list of files
assistant_files = st.session_state.ai_client.beta.assistants.files.list(
assistant_id=os.getenv("OPENAI_ASSISTANT")
)
total_files += len(assistant_files.data) if assistant_files.data else 0

# Create a progress bar
progress_bar = st.progress(0, text='Getting ready to study :)!')

# Function to update progress
def update_progress():
nonlocal progress_value
progress_value += 1
progress_bar.progress(progress_value / total_files)

# Delete files from the first list
for file in files.data:
file_id = file.id
st.session_state.ai_client.files.delete(file_id=file_id)
print(f"Deleted file {file_id}")
update_progress()

# Delete files from the second list
for file in assistant_files.data:
file_id = file.id
st.session_state.ai_client.beta.assistants.files.delete(
assistant_id=os.getenv("OPENAI_ASSISTANT"),
file_id=file_id
)
print(f"Deleted file {file_id}")
update_progress()

# Complete the progress bar if there were no files
if total_files == 0:
progress_bar.progress(1)
progress_bar.empty()



def context_selection():
"""
A widget that allows the user to select what context the chatbot has access to
"""
modules = azsqldb.get_modules(st.session_state.class_info['class_id'], st.session_state.sqlcursor)
context_container = st.container()
with context_container:
st.info("Choose the modules StudyBuddy will help you with")
# Counter to track the current column
col_counter = 0
# Iterating over the modules
for module_name, module_id in modules.items():
# Every three modules, create a new row of columns
if col_counter % 3 == 0:
col1, col2, col3 = st.columns(3)

# Place the checkbox in the current column
if col_counter % 3 == 0:
with col1:
st.checkbox(module_name, key=f"module_{module_id}")
elif col_counter % 3 == 1:
with col2:
st.checkbox(module_name, key=f"module_{module_id}")
elif col_counter % 3 == 2:
with col3:
st.checkbox(module_name, key=f"module_{module_id}")

# Increment the column counter
col_counter += 1

# Add a button to submit the selected modules
if st.button("Let's Study!"):
# Get the selected modules
selected_modules = []
for module_name, module_id in modules.items():
if st.session_state[f"module_{module_id}"]:
selected_modules.append(module_name)
# If the user didn't select any modules, display a warning
if len(selected_modules) == 0:
st.warning("Please select at least one module")
else:
st.session_state.selected_modules = selected_modules
st.session_state.context_selection_toggle = False
st.experimental_rerun()

def initialize_chat():
module_learning_outcomes, class_learning_outcomes = azsqldb.get_learning_outcomes(
st.session_state.class_info['class_id'],
st.session_state.selected_modules,
st.session_state.sqlcursor)

faq_df = azsqldb.get_questions_usernames(st.session_state.class_info['class_id'], st.session_state.sqlcursor)

initial_prompt = f"""
<INFO> INITIAL PROMPT </INFO>
You're chatting with {st.session_state.user_info['username']}\n
Their role is : {st.session_state.user_info['role']}\n
The class is : {st.session_state.class_info['class_name']}\n
The class learning outcomes are:\n {class_learning_outcomes}\n
You are going to discuss the following modules:\n
"""

for module, outcome in module_learning_outcomes.items():
initial_prompt += f" -Module: {module}\n\n"
initial_prompt += f" -Learning outcomes: {outcome}\n\n"

initial_prompt += f"Here is info on the files you recieved:\n\n{st.session_state.blobs_to_retrieve} \n\n"

initial_prompt += f"Here are the FAQs for this class:\n\n{faq_df}"

return initial_prompt


def upload_files_ai(blob_paths):
blob_service_client = BlobServiceClient.from_connection_string(os.getenv("AZURE_STORAGE_CONNECTION_STRING"))
container_client = blob_service_client.get_container_client(os.getenv("AZURE_CONTAINER"))
client = st.session_state.ai_client

# Base directory (assuming this script is in the /scripts subdirectory)
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Paths
staging_dir = os.path.join(base_dir, 'staging')
json_path = os.path.join(base_dir, '.bin', 'files.json')

# Ensure directories exist
os.makedirs(staging_dir, exist_ok=True)
os.makedirs(os.path.dirname(json_path), exist_ok=True)

# List to store file objects
uploaded_files = []

for blob_path in blob_paths:
# Adjust the path to save in the staging directory
staging_path = os.path.join(staging_dir, os.path.basename(blob_path))

# Download the file from Azure Blob
blob_client = container_client.get_blob_client(blob_path)
with open(staging_path, "wb") as download_file:
download_file.write(blob_client.download_blob().readall())

# Upload the file to OpenAI
with open(staging_path, "rb") as file:
response = client.files.create(file=file, purpose="assistants")
uploaded_files.append(response)

# Delete the file from the staging directory
os.remove(staging_path)

# Return this list of file ids
return [file_obj.id for file_obj in uploaded_files]
74 changes: 74 additions & 0 deletions Scripts/__classmanager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# __classmanager.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: ammar syed ali <https://www.linkedin.co +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2023/12/01 09:05:41 by ammar syed #+# #+# #
# Updated: 2023/12/01 09:05:41 by ammar syed ### ########.fr #
# #
# **************************************************************************** #

import streamlit as st
from Scripts import azsqldb, sessionvars

sessionvars.initialize_session_vars()

def fetch_class_data():
return azsqldb.get_classes(st.session_state.user_info['user_id'],
st.session_state.user_info['role'],
st.session_state.sqlcursor)

def show_class():
# Fetch class data
class_data = fetch_class_data()

col1, col2 = st.columns([1.4,1])

if class_data:
# Select box for choosing the class
selected_class_name = col1.selectbox("Select class:", list(class_data.keys()), index=list(class_data.keys()).index(st.session_state.selected_class_name) if st.session_state.selected_class_name in class_data else 0, label_visibility='hidden')
st.session_state.selected_class_name = selected_class_name
# Save the class code for the selected class
if selected_class_name:
selected_class_info = class_data[selected_class_name]
st.session_state.class_info = selected_class_info
col2.text_input(label="Class Code", label_visibility='hidden', value= f"Code: {selected_class_info['class_code']}", max_chars=0, key='class_code', disabled=True)
else:
st.selectbox("Select class:", ["No classes available"])
# if-else to show different messages for teachers and students with no classes
if st.session_state.user_info['role'] == 'teacher':
st.write("You haven't created any classes yet.")
else:
st.write("You are not enrolled in any classes yet.")


def create_new_class():

new_class_name = st.text_input("Enter the name for the new class", "Name?", label_visibility='hidden')
new_class_learning_outcomes = st.text_area("Enter the learning outcomes for the new class", "Enter the learning outcomes for the new class", label_visibility='hidden')

if st.button("Submit", use_container_width=True):
if new_class_name and new_class_learning_outcomes:
azsqldb.new_class(st.session_state.user_info['user_id'], st.session_state.sqlcursor, new_class_name, new_class_learning_outcomes)
class_data = fetch_class_data() # Refresh the class data
st.session_state.selected_class_name = new_class_name # Update the selected class name
st.session_state.show_new_class_input = False # Hide the input fields after submission
st.experimental_rerun() # Rerun the script to reflect the changes
else:
st.warning('Both class name and learning outcomes must be populated.')

def join_class():
# Input field and button for joining a new class
new_class_code = st.text_input("Enter the class code")
join_class_button = st.button("Join Class", use_container_width= True)
# Block to handle form submission
if join_class_button and new_class_code:
join_message = azsqldb.join_class(st.session_state.user_info['user_id'], st.session_state.sqlcursor, new_class_code)
st.warning(join_message)
# Handle the a successful class join
if join_message == "You have successfully joined the class!":
class_data = fetch_class_data() # Refresh the class data
st.session_state.selected_class_name = list(class_data.keys())[-1] # Update the selected class name to the newly joined class
st.experimental_rerun() # Rerun the script to reflect the changes
Loading