# Project export: UpliftMe

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Empower yourself with reminders of what you've achieved. Using Gmail and Google Calendar insights, our app delivers personalized affirmations to uplift you in the moments that matter most.
- Devpost: https://devpost.com/software/upliftme
- GitHub: https://github.com/soysaucewaso/calhacksbackend2024
- Demo: https://github.com/Robin-01/CalHacks-Compliment-App
- Team: 1 GitHub contributor(s) — waso (1 commits)

## Devpost submission (written by the team)

### Inspiration

I was inspired to create this personalized affirmation app because sometimes I feel like I’m not good enough, and looking at my achievements helps me stay motivated.

### What it does

Our application searches the user's email and calendar history, for documents it can use to uplift the user. It proves the self-deprecating users wrong and affirms people of their self worth.

### How we built it

Backend and AI Integration: Using Flask as the backend framework, we connected the frontend with AI functionalities. We integrated LangChain and Google Gemini API to enable a Retrieval-Augmented Generation (RAG) model that processes context and provides personalized responses. Google Calendar API and Gmail API were used to gather user-specific context, such as past events and emails. Data Processing and Retrieval: We used LangChain’s text splitting capabilities to handle large amounts of data, splitting the context into manageable chunks. Chroma and Google Generative AI embeddings allowed us to efficiently store and retrieve relevant information from this context, ensuring quick and relevant responses. Frontend: The frontend was built using HTML, CSS, and JavaScript, providing a smooth, responsive interface where users can interact with the chatbot in real-time. Flask handled all communication between the frontend and backend, ensuring the user’s requests were processed and responded to efficiently.

### Challenges we ran into

Frontend-Backend Integration: Since it was our first time connecting the frontend (HTML, CSS, and JavaScript) with the backend using Flask, we faced difficulties in properly implementing the communication between the two. Getting the data flow and requests to work smoothly took time and troubleshooting. Code Structure Issues: As we kept adding new features and functionalities, we realized that our initial code structure wasn’t scalable. This caused confusion and delays, as we had to reorganize and refactor large portions of the codebase to ensure everything functioned correctly. RAG Model Challenges: Our Retrieval-Augmented Generation (RAG) setup wasn’t working initially. The LLM responses were not properly using the context from the Calendar and Gmail APIs. We had to dive deep into debugging, ensuring that the context was passed correctly to the model for relevant, personalized outputs. CORS Issues in Flask: Setting up cross-origin resource sharing (CORS) was a significant challenge for us. Since Flask was handling backend requests, we needed to enable CORS to ensure the frontend could communicate with the backend without issues, which took extra time to configure properly.

### Accomplishments we're proud of

We are happy that we could integrate the back-end and front-end while using different frameworks in this project. Even though some of our members were new to the technology, the final product worked. Despite being fairly new to the Gemini 1.5 API, our team successfully implemented it in our project to get the responses.

### What we learned

Working on this project exposed us to different technologies, and the workshops throughout the event gave us key insights into the latest technologies being used in the industry. Working in a time-crunch environment taught us to stay focused and continue pushing despite receiving errors and bugs. Overall, we learned the importance of teamwork and planning required to succeed in a project.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 3 recognized source files, 22 KB.
- Python (language) — detected in the code
- Flask (technology) — claimed on Devpost, not found in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- LangChain (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (4 of 4)

```
calenderclient.py
complimentapi.py
rag.py
requirements
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Initial

## Key source files (fetched from GitHub, selected and truncated for size)

### calenderclient.py

```python
import datetime
import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/calendar"]


def get_calendar_info():
  """Shows basic usage of the Google Calendar API.
  Prints the start and name of the next 10 events on the user's calendar.
  """
  creds = None

  if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
  # If there are no (valid) credentials available, let the user log in.
  if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
      creds.refresh(Request())
    else:
      flow = InstalledAppFlow.from_client_secrets_file(
          "/Users/sawyer/Downloads/gmail_credentials.json", SCOPES
      )
      creds = flow.run_local_server(port=8080)
    # Save the credentials for the next run
    with open("token.json", "w") as token:
      token.write(creds.to_json())

  try:
    service = build("calendar", "v3", credentials=creds)

    # Call the Calendar API
    now_utc = datetime.datetime.utcnow()
    one_month_ago = (now_utc - datetime.timedelta(days=30)).strftime('%Y-%m-%dT%H:%M:%SZ')
    fifteen_days_from_now = (now_utc + datetime.timedelta(days=15)).strftime('%Y-%m-%dT%H:%M:%SZ')
    # calendar_list = service.calendarList().list().execute()
    events_result = (
        service.events()
        .list(
            calendarId="nnq1jg0hi0904ivqsu0o4953ks@group.calendar.google.com",
            timeMin=one_month_ago,
            maxResults=50,
            singleEvents=True,
            orderBy="startTime",
        )
        .execute()
    )
    events = events_result.get("items", [])

    if not events:
      return "No upcoming events found."
    all_summaries = ""

    # Prints the start and name of the next 10 events
    for event in events:
        # Extract relevant information
        summary = event.get('summary', 'No title')
        start = event['start'].get('dateTime', event['start'].get('date', 'No start time'))
        end = event['end'].get('dateTime', event['end'].get('date', 'No end time'))
        location = event.get('location', 'No location specified')
        description = event.get('description', 'No description')
        organizer = event.get('organizer', {}).get('email', 'No organizer specified')

        # Get attendees (excluding the organizer)
        attendees = [
            attendee['email']
            for attendee in event.get('attendees', [])
            if not attendee.get('organizer', False)
        ]

        # Check if it's a recurring event
        is_recurring = 'recurringEventId' in event

        # Check if there are reminders
        has_reminders = event.get('reminders', {}).get('useDefault', False) or event.get('reminders', {}).get(
            'overrides', [])

        # Create a summary string
        summary_text = f"""
        Event: {summary}
        Start: {start}
        End: {end}
        Location: {location}
        Organizer: {organizer}
        Attendees: {', '.join(attendees) if attendees else 'No attendees'}
        Recurring: {'Yes' if is_recurring else 'No'}
        Reminders: {'Yes' if has_reminders else 'No'}

        Description:
        {description[:100]}{'...' if len(description) > 100 else ''}
        """

        all_summaries += summary_text
        all_summaries += "-" * 50 + "\n"
    return all_summaries

  except HttpError as error:
    return f"An error occured with the Calender: {error}"

#  print(get_calendar_info())
```

### rag.py

```python
GOOGLE_API_KEY = ""

from langchain_core.prompts import PromptTemplate
from langchain.chains.question_answering import load_qa_chain
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain_google_genai import GoogleGenerativeAIEmbeddings
from langchain_google_genai import ChatGoogleGenerativeAI
import google.generativeai as genai
genai.configure(api_key=GOOGLE_API_KEY)
class rag_model:
    def __init__(self, context):
        self.model = ChatGoogleGenerativeAI(model="gemini-pro",google_api_key=GOOGLE_API_KEY,
                                 temperature=0.2,convert_system_message_to_human=True)
        text_splitter = RecursiveCharacterTextSplitter(chunk_size=10000, chunk_overlap=1000)
        self.context = context
        texts = text_splitter.split_text(context)
        embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001",google_api_key=GOOGLE_API_KEY)
        self.vector_index = Chroma.from_texts(texts, embeddings).as_retriever(search_kwargs={"k":5})
#         self.template = """
# You are a highly positive and empathetic assistant. Based on the given context You need to cheer up the user of he is already doing somthing like that.
# Context:
# {context}
#
# User: {question}
# Positive, Supportive Response to cheer up the user:
# """

    def prompt(self,question, chat_lis):

        chat_history = ""
        for pair in chat_lis:
            for key, value in pair.items():
                chat_history += f"{key} {value}\n"


        qa_chain = RetrievalQA.from_chain_type(
            self.model,
            retriever=self.vector_index,
            return_source_documents=False,
            # chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
        )
        events = qa_chain.invoke({"query": f"Get all the context related to all the following words individually {question} in the context"})[
            'result']

        print(events)
        template = f"""
         You are a chatbot that helps the users by mentally making people feel better.
         You are given the following events from a persons life: {events} 
         """

        template += """
        {context} 
         You need to generate answer by try connecting existing question with one of the events and make the user feel better by only highlighting the things he is doing better.
         Be as concise and short as possible.
          here's the conversation so far:"""

        template += f"""{chat_history}\n"""

        template += """  User: {question}
          Assistant:
         """

        QA_CHAIN_PROMPT = PromptTemplate.from_template(template)

        q = RetrievalQA.from_chain_type(
            self.model,
            retriever=self.vector_index,
            return_source_documents=False,
            chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
        )

        return q({"query": question})['result']

        # print(question)
        # pompt = f"From the following prompt identify the activity user is not doing. only mention the activities name.{question}"
        # topic = model_2.generate_content(pompt).candidates[0].content.parts[0].text
        # print(topic)
        # #QA_CHAIN_PROMPT = PromptTemplate.from_template(self.template)
        # qa_chain = RetrievalQA.from_chain_type(
        #     self.model,
        #     retriever=self.vector_index,
        #     return_source_documents=False,
        #    # chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
        # )
        # positives = qa_chain.invoke({"query": f"All the {topic} in the context"})['result']
        # print(positives)
        # prompt = f"""
        # You are an affirmative chatbot that always emotionally helps the user.
        # Provide the user with a affirmative response to user's query {question}. Because he has been doing {positives}.
        # Your response should start by comforting the user and keep the respnse concise.
        # """
        #
        # prompt = f"""
        # context: {self.context}
        #
        # Based on the user's previous achievements, guide the conversation by acknowledging
        # their growth or progress. if the user is discussing a new challenge, recall how
        # they overcame past obstacles and offer a connection to the current struggle.
        #
        # User: {question}
        # """
        # return model_2.generate_content(prompt).candidates[0].content.parts[0].text

    def get_positive(self):
        qa_chain = RetrievalQA.from_chain_type(
            self.model,
            retriever=self.vector_index,
            return_source_documents=False,
            # chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
        )
        model_2 = genai.GenerativeModel(model_name="gemini-pro")

        positive = qa_chain.invoke({"query": f"Get one thing from the context that the user is doing good from the context."})['result']
        return model_2.generate_content(f"Reformat the following such that it directly talks about the user. {positive}").candidates[0].content.parts[0].text


```

### complimentapi.py

```python
import rag
import calenderclient

import base64
import pickle

from flask import Flask, request, jsonify
from flask_cors import CORS
from google import auth

import os
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.http import BatchHttpRequest
from google.oauth2 import credentials
import re
app = Flask(__name__)

CORS(app)

SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def getMessageIds():
    # OAuth
    creds = None
    REDIRECT_URI = 'http://localhost:58297/callback'
    TOKENPICKLEPATH = '/Users/sawyer/Documents/token.pickle'
    # get creds
    if os.path.exists(TOKENPICKLEPATH):
        with open(TOKENPICKLEPATH, 'rb') as token:
            creds = pickle.load(token)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                '/Users/sawyer/Downloads/gmail_credentials.json',scopes= SCOPES)
            creds = flow.run_local_server(port=8080)
            # Save the credentials for the next run
        with open(TOKENPICKLEPATH, 'wb') as token:
            pickle.dump(creds, token)
    service = build('gmail', 'v1', credentials=creds)

    results = service.users().messages().list(userId='me', maxResults=50).execute()
    messages = results.get('messages', [])
    return service, messages

structuredEmails = []
lines = 0
def handle_message_request(request_id, response, exception):
    if exception is not None:
        print(f"Error fetching message with id {request_id}: {exception}")
    else:
        # Process the response (email content)
        payload = response['payload']
        #print(f"Successfully Fetched")
        if 'parts' not in payload:
            return
        headers = {nvpair['name']: nvpair['value'] for nvpair in payload['headers']}
        subject = headers['Subject']
        sender = headers['From']
        date = headers['Date']
        encoded_data = payload['parts'][0]['body']['data']
        data = base64.urlsafe_b64decode(encoded_data).decode('utf-8')
        # cleaned_text = re.sub(r'<[^>]*>(\s*\n)*', '', data)
        # remove empty lines
        cleaned_text = re.sub(r'\n[\s*\n]+', '\n', data)
        # remove links
        cleaned_text = re.sub('[^\s]*://[^\s]*', '', cleaned_text)
        # lines += cleaned_text.count('\n') + 1
        structuredEmails.append(str({'Subject': subject, 'Sender': sender, 'Date': date}))

def getStructuredEmails():
    service, messages = getMessageIds()

    if not messages:
        print("No messages found.")
    else:
        print("Top 50 emails:")
        lines = 0
        i = 0
        jump = 3
        while i < len(messages):
            batch = BatchHttpRequest(callback=handle_message_request,
                                     batch_uri='https://www.googleapis.com/batch/gmail/v1')
            for j in range(i,min(i+jump,len(messages))):
                batch.add(service.users().messages().get(userId='me', id=messages[j]['id'], format='full'))
            batch.execute()
            i += jump
#
# getStructuredEmails()
# emailsstr = '['+",".join(structuredEmails)+']'
# print(emailsstr)
# calenderstr = calenderclient.get_calendar_info()
# inputstr = f"EMAILS: {emailsstr}, CALENDAR: {calenderstr}"


model = rag.rag_model("""
Personal Information:
Name: Alex Johnson
Age: 20
Location: Seattle, WA
Occupation: Full-time Student at the University of Washington
Major: Computer Science
Bio: A dedicated computer science student with a passion for artificial intelligence, game development, and machine learning. Alex loves to explore the latest in technology and enjoys coding challenges, video games, and hiking in the Pacific Northwest.
Google Calendar:
Class Schedule:
CS 324 - Machine Learning: Monday, Wednesday, Friday at 10 AM (in-person)
CS 376 - Game Development: Tuesday and Thursday at 1 PM (in-person)
MATH 307 - Linear Algebra: Monday and Wednesday at 2 PM (online)
PHYS 121 - Physics I: Thursday at 3 PM (in-person lab)
Important Deadlines:
CS 324 Midterm Exam: October 30, 2024, at 9 AM
MATH 307 Assignment 4 Submission: October 22, 2024, by 11:59 PM
Game Development Project Prototype: November 5, 2024, 5 PM
Personal Events:
Weekly hiking trips every Saturday at 8 AM with friends.
Study group for Machine Learning every Wednesday at 7 PM.
Attending the Seattle Tech Meetup on November 2 at 6 PM.
Canvas (Class Scores and Assignments):
CS 324 - Machine Learning:
Current Grade: 88%
Assignments:
Completed Assignment 1: 92%
Completed Assignment 2: 85%
Midterm Review Exam: Upcoming
CS 376 - Game Development:
Current Grade: 93%
Projects:
Prototype Submission: 95%
Group Project Status: In Progress
MATH 307 - Linear Algebra:
Current Grade: 75%
Next Homework Submission: October 22
PHYS 121 - Physics I:
Current Grade: 82%
Upcoming Lab: October 26, 2024
Interests & Hobbies:
Interests:
Deeply interested in artificial intelligence and machine learning, especially computer vision.
Enjoys game development, exploring new game engines like Unity and Unreal Engine, with hopes to build an indie game.
Regularly participates in hackathons, often collaborating on AI-powered applications.
Hobbies:
Video games, especially strategy and puzzle-based games like "Portal" and "Civilization VI."
Hiking and outdoor photography, with a focus on capturing the beauty of the Pacific Northwest.
Coding side projects, such as building personal websites and experimenting with AI models like GPT and LLaMA.
Emails:
School-related Emails:
Received an email from the professor about additional review sessions for Machine Learning midterms.
Notification from the career center regarding upcoming tech internship opportunities and deadlines.
Feedback on the last Linear Algebra assignment with suggestions for improvement.
Personal Emails:
Newsletters from TechCrunch and Hack
[truncated — 7728 more characters]
```