# Project export: Schedulify

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: Your personal online web-planner built on google's gemini pro that utilizes user's daily tasks into an organized, and user-friendly schedule especially useful for those procrastinators, aka students.
- Devpost: https://devpost.com/software/schedulify-ukins6
- GitHub: https://github.com/Htetty/schedulify.git
- Team: 1 GitHub contributor(s) — Htetty (1 commits)

## Devpost submission (written by the team)

### Inspiration

As full-time college students who are also working part-time, it is a daily struggle to keep up with our schedule organized while managing our academic assignments and personal responsibilities. The demands of our schedule often lead us to prioritize tasks over essential self-care. To help college students like us strike a balance between their academic or work responsibility and their self-care routine, our team has come up with a scheduling program utilizing AI to help us stay organized.

### What it does

The program guides users to input their daily routines, such as wake-up, meal-times, and bedtime, forming the foundation for a personalized schedule. Users can add tasks by providing details like title, description, and estimated time, tailoring the schedule to their needs. The AI then generates a balanced schedule, integrating tasks with essential self-care activities like sleep and meals. This approach promotes efficient time management, helping users improve both productivity and well-being while maintaining a sustainable balance between work and self-care.

### How we built it

First, we came up with a design in Figma that served as our outline of the website before adding any scripts or functions. We soon began writing codes using backend tools such as Node.js and Express.js, as well as frontend tools such as HTML, CSS, and Javascript tools. After that we made a Google Cloud server and created an API key, allowing us to use Gemini-Pro in our code. Using our varied skillsets, we produced a website built on HTML, CSS, and Javascript powered by Google's Gemini AI, capable of reading user input and generating schedules.

### Challenges we ran into

We had a few problems with implementing the GeminiAI into our project but the one that kept persisting was giving Gemini AI the prompt to fulfill our intended purpose of creating an effective and organized schedule for users.

### Accomplishments we're proud of

As all of us are beginners, so we came to the hackathon without expecting much. So we’re proud of the fact that we learned so much in just a mere 36 hours and managed to build a program that works the way we wanted it to work.

### What we learned

We were all really proud at what we have accomplished in these 36 hours. Throughout the experience, we learned how to bond and learn each other's skills when it comes to coding. This event was really impactful for us as it taught us how to utilize AI in our programs, how to use API, javascript and express.

### What's next

We would like to add features that give users more customizations to their preferred schedule. We would also like to implement google calendar API so that our website can cross referenced the generated schedule from our website to google calendar. We also plan to incorporate Schedulify into mobile devices, allowing for more accessibility.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 3 recognized source files, 30 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- Google Gemini (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- OpenAI (technology) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (4 of 4)

```
schedulify/index.js
schedulify/package.json
schedulify/public/frontpage.css
schedulify/public/index.html
```

### Dependencies

- schedulify/package.json: @google/generative-ai@^0.21.0, axios@^1.7.7, body-parser@^1.20.3, cors@^2.8.5, dotenv@^16.4.5, express@^4.21.1, express-session@^1.18.1, node-fetch@^3.3.2, nodemon@^3.1.7, openai@^4.68.1

### Recent commits (newest first)

- Add files via upload

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

### schedulify/package.json

```
{
  "name": "calhacksprep",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.js"
  },
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@google/generative-ai": "^0.21.0",
    "axios": "^1.7.7",
    "body-parser": "^1.20.3",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.21.1",
    "express-session": "^1.18.1",
    "node-fetch": "^3.3.2",
    "nodemon": "^3.1.7",
    "openai": "^4.68.1"
  }
}

```

### schedulify/index.js

```javascript
// Using ES modules for consistency
import express from 'express';
import bodyParser from 'body-parser';
import session from 'express-session';
import cors from 'cors';
import path from 'path';
import { fileURLToPath } from 'url';
import dotenv from 'dotenv';
import { GoogleGenerativeAI } from '@google/generative-ai';

dotenv.config(); 

console.log(process.env.API_KEY);

// Initialize Gemini with the API key from environment variables
const genAI = new GoogleGenerativeAI(process.env.API_KEY);

const app = express();
const PORT = 3000;

const __filename = fileURLToPath(import.meta.url);  
const __dirname = path.dirname(__filename);         

// Middleware
app.use(cors({ origin: '*' }));  // Allow cross-origin requests
app.use(bodyParser.urlencoded({ extended: true }));  // Parse URL-encoded bodies
app.use(bodyParser.json());  // Parse JSON bodies

// Session middleware
app.use(session({
  secret: process.env.SESSION_SECRET || 'default-secret-key',  // Secret for session, set this in .env for security
  resave: false,  // Don't save session if unmodified
  saveUninitialized: true,  // Save a session even if uninitialized
  cookie: { maxAge: 3600000 }  // 1-hour session expiry
}));

// Serve static files from the 'public' directory
app.use(express.static('public'));

// Serve the main HTML file
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html')); 
});

// Route to set user schedule
app.post('/set-schedule', (req, res) => {
  const { wakeUp, lunch, dinner, sleep } = req.body;

  // Ensure all fields are provided before saving
  if (!wakeUp || !lunch || !dinner || !sleep) {
      return res.status(400).send({ error: 'All schedule fields are required' });
  }

  // Store the schedule in the session
  req.session.schedule = { wakeUp, lunch, dinner, sleep };
  
  console.log("Saved schedule:", req.session.schedule);  // Log the session schedule for debugging
  res.send('Schedule saved successfully');
});

// Route to get user schedule
app.get('/get-schedule', (req, res) => {
  if (req.session.schedule) {
    res.json(req.session.schedule);  // Return the schedule from the session
  } else {
    res.status(404).send({ message: 'No schedule found' });  // No schedule set
  }
});

// Route to analyze tasks and get suggestions from Gemini
app.post('/analyze-task', async (req, res) => {
  try {
      const { prompt } = req.body;

      // Check if the schedule is stored in the session
      const schedule = req.session.schedule;
      if (!schedule || !schedule.wakeUp || !schedule.lunch || !schedule.dinner || !schedule.sleep) {
          return res.status(400).json({ error: 'Please provide specific times for Wake up, Lunch, Dinner, and Sleep.' });
      }

      // Construct the full prompt for the AI
      const fullPrompt = `
      My schedule for today is:
      Wake up: ${schedule.wakeUp}
      Lunch: ${schedule.lunch}
      Dinner: ${schedule.dinner}
      Sleep: ${schedule.sleep}

      My tasks are: 
      "${prompt}"

      Please provide an optimized schedule that includes the new task, while respecting the existing schedule as much as possible.
      Output the schedule as a list of items, each with a time and a task.`;
      
      // Log the prompt to debug
      console.log("Prompt sent to Gemini AI:", fullPrompt);

      const model = genAI.getGenerativeModel({ model: "gemini-pro" });
      const response = await model.generateContent(fullPrompt);
      const generatedText = response.response.text();

      req.session.generatedSchedule = generatedText;
      res.json({ suggestedSchedule: generatedText });

  } catch (error) {
      console.error('Error interacting with Gemini AI:', error);
      res.status(500).json({ error: 'An error occurred while processing the task.' });
  }
});

app.get('/get-generated-schedule', (req, res) => {
  if (req.session.generatedSchedule) {
    res.json({ suggestedSchedule: req.session.generatedSchedule }); 
  } else {
    res.status(404).send({ message: 'No generated schedule found' }); 
  }
});

// Start the server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
```

### schedulify/public/frontpage.css

```css
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  height: 100%;
  width: 100%;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

@keyframes move {
  100% {
      transform: translate3d(0, 0, 1px) rotate(360deg);
  }
}

.background {
  position: fixed;
  width: 100vw;
  height: 100vh;
  top: 0;
  left: 0;
  background: whitesmoke;
  overflow: hidden;
}

.background span {
  width: 50vmin;
  height: 50vmin;
  border-radius: 50vmin;
  backface-visibility: hidden;
  position: absolute;
  animation: move;
  animation-duration: 45;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}


.background span:nth-child(0) {
  color: #4385f3;
  top: 2%;
  left: 88%;
  animation-duration: 24s;
  animation-delay: -2s;
  transform-origin: 7vw 21vh;
  box-shadow: 100vmin 0 12.519259363528331vmin currentColor;
}
.background span:nth-child(1) {
  color: #fdc44a;
  top: 12%;
  left: 80%;
  animation-duration: 34s;
  animation-delay: -31s;
  transform-origin: -11vw 11vh;
  box-shadow: 100vmin 0 12.57249878526276vmin currentColor;
}
.background span:nth-child(2) {
  color: #ff0000;
  top: 10%;
  left: 71%;
  animation-duration: 16s;
  animation-delay: -22s;
  transform-origin: -9vw -13vh;
  box-shadow: -100vmin 0 12.901998885834029vmin currentColor;
}
.background span:nth-child(3) {
  color: #4385f3;
  top: 30%;
  left: 1%;
  animation-duration: 49s;
  animation-delay: -24s;
  transform-origin: -15vw -5vh;
  box-shadow: 100vmin 0 12.862004763198485vmin currentColor;
}
.background span:nth-child(4) {
  color: #ff0000;
  top: 23%;
  left: 80%;
  animation-duration: 55s;
  animation-delay: -37s;
  transform-origin: -16vw 20vh;
  box-shadow: 100vmin 0 12.703148125509324vmin currentColor;
}
.background span:nth-child(5) {
  color: #ff0000;
  top: 70%;
  left: 51%;
  animation-duration: 41s;
  animation-delay: -44s;
  transform-origin: -15vw -5vh;
  box-shadow: -100vmin 0 12.54158095662213vmin currentColor;
}
.background span:nth-child(6) {
  color: #4385f3;
  top: 92%;
  left: 86%;
  animation-duration: 30s;
  animation-delay: -44s;
  transform-origin: -5vw 1vh;
  box-shadow: -100vmin 0 13.266862527160638vmin currentColor;
}
.background span:nth-child(7) {
  color: #4385f3;
  top: 55%;
  left: 29%;
  animation-duration: 40s;
  animation-delay: -35s;
  transform-origin: -12vw -24vh;
  box-shadow: 100vmin 0 13.319501706357062vmin currentColor;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
}

.container {
  width: 90%;
  height: 90%;
  max-width: 1200px;
  background-color: rgba(255, 255, 255, 0%);
  border-radius: 20px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
  padding: 20px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
  z-index: 1;
}

.calenderBg1 {
  translate: -1px 75px;
  position: absolute;
  z-index: -1;
  background-color: red;
  opacity: 60%;
  width: 1160px;
  height: 20px;
}

.calenderBg2 {
  translate: -1px 95px;
  position: absolute;
  z-index: -1;
  background-color: white;
  box-shadow: rgba(0, 0, 0, 0.5);
  opacity: 60%;
  width: 1160px;
  height: 95px;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.profileBtn {
  cursor: pointer;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: lightblue;
  background-color: linear-gradient(0deg, rgba(41,105,172,0) 0%, rgba(0,159,255,1) 83%, rgba(8,183,239,1) 100%);
}

.calendarBtn {
  position: absolute;
  translate: 70px;
  cursor: pointer;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: lightblue;
  background-color: linear-gradient(0deg, rgba(41,105,172,0) 0%, rgba(0,159,255,1) 83%, rgba(8,183,239,1) 100%);
}

.calendarStylizeBtn {
  cursor: pointer;
}

.search-bar {
  opacity: 80%;
  cursor: text;
  position: absolute;
  translate: 145px;
  width: 50%;
  padding: 24px;
  border-radius: 20px;
  border: 2px solid gray;
  font-size: 16px;
}

.days {
  display: flex;
  justify-content: space-between;
  margin: 20px 0;
}

.day {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  position: relative;
  translate: 0px 27px;
  flex: 1;
  background-color: #d9d9d9;
  border: none;
  border-radius: 20px;
  padding: 29px;
  margin: 0 30px;
  text-align: center;
  font-weight: bold;
  font-size: 18px;
  cursor: pointer;
  transition: background-color 0.5s;
  transition: transform 250ms;
}

.day.active {
  background-color: #a59d9d;
  color: white;
  box-shadow: rgba(0, 0, 0, 0.5);
}

.day:hover {
  box-shadow: rgba(0, 0, 0, 0.5);
  transition: 0.5s;
  transform: translateY(-3px);
  background-color: #a59d9d;
  color: white;
}

.notificationBar {
  cursor: pointer;
  position: relative;
  translate: 0px 13px;
  background-color: gainsboro;
  box-shadow: rgba(0, 0, 0, 0.5);
  border-radius: 10px;
  padding: 15px;
  display: flex;
  align-items: center;
  margin-bottom: 20px;
}

.notificationBtn {
  width: 30px;
  margin-right: 15px;
}

.dateDisplay {
  position: absolute;
  translate: 770px 5px;
  font-size: 15px;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

.panels-container {
  display: flex; /* Use flexbox to place the panels side by side */
  justify-content: space-between;
  gap: 20px; /* Space between panels */
}

.panel {
  flex: 1; /* Each panel takes up equal space */
  background-color: white;
  border-radius: 10px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  padding: 20px;
  overflow-y: auto;
}

.todo-list, .completed-list {
  list-style: none;
  padding: 0;
}

.todo-list li, .completed-list li {
  margin-bottom: 15px;
}

.todo-list input[type="checkbox"], .completed-list input[type="checkbox"] {
  margin-right: 10px;
}

.task-item {
  di
[truncated — 3790 more characters]
```

### schedulify/public/index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="frontpage.css">
    <title>Schedulify</title>
</head>
<body id="body">
    <div class="background" style="z-index:-1">
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
     </div>
     <div class="container" id="all">
        <div class="calenderBg1"></div>
        <div class="calenderBg2"></div>
        <div class="header">
            <div class="profileBtn" src="account_circle_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg" alt="Account profile">
                <img src="account_circle_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg" alt="Account profile" width="50px" height="50px">
            </div>
            <div class="calendarBtn" src="calendar-check.svg" alt="Calendar">
                <img src="calendar-check.svg" alt="Calendar" width="30px" height="30px" style="translate: 10px 10px">
            </div>
            <div class="calendarStylizeBtn" src="more_horiz_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg" alt="Stylize Calendar">
                <img src="more_horiz_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg" alt="Stylize Calendar" width="20px" height="20px" style="translate: -1135px 62px">
            </div>
            <input type="text" class="search-bar" id="task-input" placeholder="+ Add a new task...">
        </div>
        <div class="dateDisplay"><h1 id="current-date"></h1></div>
        <div class="days">
            <button type="button" class="day">Mon</button>
            <button type="button" class="day">Tues</button>
            <button type="button" class="day">Wed</button>
            <button type="button" class="day">Thurs</button>
            <button type="button" class="day">Fri</button>
            <button type="button" class="day">Sat</button>
            <button type="button" class="day">Sun</button>
        </div>
<div class="notificationBar">
        <img src="bell.svg" alt="Notification bell" width="30px" height="30px" style="translate: 0px"/>
        <p id="greeting">Hello! Generate your schedule for the day:</p>
    </div>


    <div class="panel">
        <h3>Schedule</h3>
        <br>
        <ul class="schedule-list" id="schedule-list">  
        </ul>
        <button onclick="generatePrompt()" class="createB">Create</button> 
    </div>
</div>



<div class="time-input" id="time-input">
    <h2 style="color:black">Create an event for Monday</h2>
    <input type="text" id="title" placeholder="Task Name">
    <input type="text" id="description" placeholder="Task Description">
    <label for="duration" style="translate: 100px"></label>
    <input type="number" id="duration" min="0" value="0" required> 
    <label for="time" id="time-label" style="translate: 0px;"><p style="margin-right: 20px">How much of the day do you want to spend on this?</p></label>
    <select name="time" id="time">
        <option>Most of my time</option>
        <option>A reasonable amount of my time</option>
        <option>Not too long</option>
    </select>
    <button id="genButton" onclick="generate()">Add</button>
</div>

<div class="schedule-input" id="schedule-input">
    <h3>Set Your Daily Schedule</h3>
    <form id="schedule-form" method="POST">
        <label>Wake Up Time: <input type="time" id="wakeUp" name="wakeUp" required></label><br>
        <label>Lunch Time: <input type="time" id="lunch" name="lunch" required></label><br>
        <label>Dinner Time: <input type="time" id="dinner" name="dinner" required></label><br>
        <label>Sleep Time: <input type="time" id="sleep" name="sleep" required></label><br>
        <input type="submit" class="save-sche" value="Save Schedule">
    </form>
</div>

<script>
    let tasks = [   ];

    document.getElementById("task-input").addEventListener("keypress", function(event) {
            if (event.key === "Enter") {
                var taskValue = event.target.value; 
                document.getElementById("title").value = taskValue; 
                document.getElementById("time-input").style.display = "block";
                document.getElementById("all").style.opacity = "0.15"; // Dim background when task input is shown
                event.target.value = "";
            }
        });
    
function closeDiv() {
    var timeInput = document.getElementById("time-input");
    var webBody = document.getElementById("all");

    timeInput.style.display = "none"; // Hide the time-input div
    webBody.style.opacity = "1"; // Reset the page opacity
}

function addTask() {
    var taskInput = document.getElementById("task-input");
    var taskValue = taskInput.value;
    var timeInput = document.getElementById("time-input");
    var webBody = document.getElementById("all");
    var titleValue = document.getElementById("title").value;


    //var newTask = document.createElement("li");
    //var checkbox = document.createElement("input");
    //checkbox.type = "checkbox";
    //checkbox.addEventListener("change", moveTask); 

    //newTask.appendChild(checkbox);
    //newTask.appendChild(document.createTextNode(` ${taskValue}`));  // Using backticks for template literals

    //var todoList = document.getElementById("todo-list");
    //todoList.appendChild(newTask);

    document.getElementById("time-input").style.display = "block";
    webBody.style.opacity = "0.15";
    timeInput.style.opacity = "1"; 

    document.getElementById("title").value = taskValue;

    taskInput.value = "";
    document.getElementById("title").value = "";

}

function moveTask(event) {
    var listItem = event.target.parentNode; 
    var completedList = document.getElementById("completed-list");

    completedList.appendChild(listItem);

    event.target.disabled = true;
}

function generate() {
const describeEvent = document.getElementById("description");

[truncated — 10912 more characters]
```