I’m working on a project where I need to allow users to input various events (e.g., lectures, meetings) with details such as name, location, date, and time. Once the user has added these events, I want to generate a timetable that visually displays these events in a structured format.
I’m using React for the front end, and I’m wondering if there are any libraries, packages, or approaches that can help me achieve this. I want to display the timetable as a table or grid, with each event occupying the appropriate time slot.
This is my backend code that connects to the ChatGPT4 API to generate the output:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List
import openai
app = FastAPI()
# Configure CORS to allow requests from your React frontend's origin (e.g., http://localhost:3000).
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # Replace with the actual origin of your React app.
allow_credentials=True,
allow_methods=["*"], # Allow all HTTP methods (GET, POST, etc.).
allow_headers=["*"], # Allow all headers in the request.
)
# Set your OpenAI API key
api_key = 'YOUR KEY'
class Event(BaseModel):
name: str
location: str
from_date: str
finish_date: str
time: str
event_type: str
event_time: str
eventColor: str # Add this field back
eventNotes: str
def generate_event_timetable(event_data):
# Define the prompt for ChatGPT (same as before)
prompt = f"Organise my time in 3 timetables as a table with these inputs:nName: {event_data.name}nLocation: {event_data.location}nFrom Date: {event_data.from_date}nFinish Date: {event_data.finish_date}nTime: {event_data.time}nEvent Type: {event_data.event_type}nEvent Time: {event_data.event_time}nEvent Color: {event_data.eventColor}nEvent Notes: {event_data.eventNotes}n"
# Send the prompt to ChatGPT (same as before)
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant that generates event timetables."},
{"role": "user", "content": prompt},
],
max_tokens=200,
api_key=api_key
)
# Get the table HTML generated by ChatGPT
event_table_html = format_event_table([event_data])
# Append the HTML table to the response content
response.choices[0].message["content"] += event_table_html
return response.choices[0].message["content"]
def format_event_table(events):
table_html = '<table>'
table_html += '<tr><th>Name</th><th>Location</th><th>From Date</th><th>Finish Date</th><th>Time</th><th>Event Type</th><th>Event Time</th><th>Event Notes</th></tr>'
for event in events:
table_html += f'<tr><td>{event.name}</td><td>{event.location}</td><td>{event.from_date}</td><td>{event.finish_date}</td><td>{event.time}</td><td>{event.event_type}</td><td>{event.event_time}</td><td>{event.eventNotes}</td></tr>'
table_html += '</table>'
return table_html
@app.post("/create_events/")
def create_events(events: List[Event]):
# Initialize an empty list to store responses for each event
responses_from_gpt = []
# Iterate through each event and generate a response from ChatGPT
for event in events:
response_from_gpt = generate_event_timetable(event)
responses_from_gpt.append(response_from_gpt)
# Return the events data along with the responses from ChatGPT
return {"events_data": [event.dict() for event in events], "gpt_responses": responses_from_gpt}
@app.post("/generate_timetable/")
def generate_timetable(events: List[Event]):
# Create a list to store event descriptions
event_descriptions = []
# Iterate through events and create descriptions
for event in events:
description = f"Event: {event.name}nLocation: {event.location}nFrom Date: {event.from_date}nFinish Date: {event.finish_date}nTime: {event.time}nEvent Type: {event.event_type}nEvent Time: {event.event_time}nEvent Notes: {event.eventNotes}n"
event_descriptions.append(description)
# Join event descriptions to create the combined timetable
combined_timetable = "n".join(event_descriptions)
return {"generated_timetable": combined_timetable}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
And this below is my front-end written in React:
import React, { useState, ChangeEvent } from 'react';
import axios from 'axios';
import '../styles/GenerateCalendar.css';
interface EventData {
name: string;
location: string;
from_date: string;
finish_date: string;
time: string;
event_type: string;
event_time: string;
// eventColor: string;
eventNotes: string;
}
function GenerateCalendar() {
const [events, setEvents] = useState<EventData[]>([]);
const [eventName, setEventName] = useState('');
const [eventLocation, setEventLocation] = useState('');
const [eventFromDate, setEventFromDate] = useState('');
const [eventFinishDate, setEventFinishDate] = useState('');
const [eventTime, setEventTime] = useState('');
const [selectedOptionRow2, setSelectedOptionRow2] = useState('');
const [selectedOptionRow3, setSelectedOptionRow3] = useState('');
const [eventColor, setEventColor] = useState('');
const [eventNotes, setEventNotes] = useState('');
const [gptResponses, setGptResponses] = useState<string[]>([]);
const [timetable, setTimetable] = useState<string>('');
const handleOptionChangeRow2 = (option: string) => {
setSelectedOptionRow2(option);
};
const handleOptionChangeRow3 = (option: string) => {
setSelectedOptionRow3(option);
};
const handleEventNameChange = (e: ChangeEvent<HTMLInputElement>) => {
setEventName(e.target.value);
};
const handleEventLocationChange = (e: ChangeEvent<HTMLInputElement>) => {
setEventLocation(e.target.value);
};
const handleEventFromDateChange = (e: ChangeEvent<HTMLInputElement>) => {
setEventFromDate(e.target.value);
};
const handleEventFinishDateChange = (e: ChangeEvent<HTMLInputElement>) => {
setEventFinishDate(e.target.value);
};
const handleEventTimeChange = (e: ChangeEvent<HTMLInputElement>) => {
setEventTime(e.target.value);
};
const handleEventColorChange = (e: ChangeEvent<HTMLInputElement>) => {
setEventColor(e.target.value);
};
const handleEventNotesChange = (e: ChangeEvent<HTMLInputElement>) => {
setEventNotes(e.target.value);
};
const handleAddEvent = (e: React.FormEvent) => {
e.preventDefault();
const eventData = {
name: eventName,
location: eventLocation,
from_date: eventFromDate,
finish_date: eventFinishDate,
time: eventTime,
event_type: selectedOptionRow2,
event_time: selectedOptionRow3,
eventColor: eventColor,
eventNotes: eventNotes,
};
// Push the new event to the events array
setEvents([...events, eventData]);
};
const handleGenerateTimetable = async () => {
try {
const response = await axios.post('http://localhost:8000/generate_timetable/', events);
if (response.status === 200) {
console.log('Timetable generated successfully');
console.log('Timetable Data:', response.data);
// Update the 'timetable' state to store the generated timetable
const generatedTimetable = response.data.generated_timetable;
setTimetable(generatedTimetable);
}
} catch (error) {
console.error('Error generating timetable', error);
}
};
return (
<div className="Calendar">
<header>
<h1 style={{ textAlign: 'center' }}>Enter Your Calendar</h1>
</header>
<form onSubmit={handleAddEvent}>
<div className="name-table-wrapper" style={{ borderRadius: '100%' }}>
<table className="name-table" style={{ borderRadius: 'inherit' }}>
<tbody>
<tr style={{ borderRadius: '100%' }}>
<td style={{ width: '150px' }}>Name:</td>
<td style={{ width: '666.5px' }}>
<input
type="text"
value={eventName}
onChange={handleEventNameChange}
style={{ borderColor: 'transparent', width: '100%' }}
/>
</td>
</tr>
</tbody>
</table>
</div>
<table className="options-table">
<tbody>
<tr>
<td style={{ width: '150px' }}>Event:</td>
<td
style={{ width: '120px' }}
onClick={() => handleOptionChangeRow2('Lecture')}
className={selectedOptionRow2 === 'Lecture' ? 'selected' : ''}
>
Lecture
</td>
<td
style={{ width: '120px' }}
onClick={() => handleOptionChangeRow2('Laboratory')}
className={selectedOptionRow2 === 'Laboratory' ? 'selected' : ''}
>
Laboratory
</td>
<td
style={{ width: '120px' }}
onClick={() => handleOptionChangeRow2('Extracurricular')}
className={selectedOptionRow2 === 'Extracurricular' ? 'selected' : ''}
>
Extracurricular
</td>
<td
style={{ width: '120px' }}
onClick={() => handleOptionChangeRow2('Commute time')}
className={selectedOptionRow2 === 'Commute time' ? 'selected' : ''}
>
Commute time
</td>
<td
style={{ width: '120px' }}
onClick={() => handleOptionChangeRow2('Study')}
className={selectedOptionRow2 === 'Study' ? 'selected' : ''}
>
Study
</td>
</tr>
</tbody>
</table>
<table className="options-table">
<tbody>
<tr>
<td style={{ width: '150px' }}>Event time:</td>
<td
onClick={() => handleOptionChangeRow3('Strict')}
className={selectedOptionRow3 === 'Strict' ? 'selected' : ''}
>
Strict
</td>
<td
onClick={() => handleOptionChangeRow3('Flexible but preferred')}
className={selectedOptionRow3 === 'Flexible but preferred' ? 'selected' : ''}
>
Flexible but preferred
</td>
<td
onClick={() => handleOptionChangeRow3('Flexible')}
className={selectedOptionRow3 === 'Flexible' ? 'selected' : ''}
>
Flexible
</td>
</tr>
</tbody>
</table>
<table className="options-table">
<tbody>
<tr>
<td style={{ width: '150px' }}>Location:</td>
<td>
<input
type="text"
value={eventLocation}
onChange={handleEventLocationChange}
style={{ width: '666.5px' }}
/>
</td>
</tr>
</tbody>
</table>
<table className="options-table">
<tbody>
<tr>
<td style={{ width: '150px' }}>Date and Time:</td>
<td style={{ width: '135px' }}>From</td>
<td>
<input
type="text"
value={eventFromDate}
onChange={handleEventFromDateChange}
/>
</td>
<td style={{ width: '150px' }}>Finish</td>
<td>
<input
type="text"
value={eventFinishDate}
onChange={handleEventFinishDateChange}
/>
</td>
</tr>
</tbody>
</table>
{/* <table className="options-table">
<tbody>
<tr>
<td style={{ width: '150px' }}>Colour of event:</td>
<td>
<input
type="text"
value={eventColor}
onChange={handleEventColorChange}
/>
</td>
</tr>
</tbody>
</table> */}
<table className="options-table">
<tbody>
<tr>
<td style={{ width: '150px' }}>Notes:</td>
<td>
<input
type="text"
value={eventNotes}
onChange={handleEventNotesChange}
style={{ width: '666.5px' }}
/>
</td>
</tr>
</tbody>
</table>
<button type="submit">Add Event</button>
</form>
<button onClick={handleGenerateTimetable}>Generate Timetable</button>
{events.map((event, index) => (
<div key={index}>
<h2>Event {index + 1}:</h2>
<p>Name: {event.name}</p>
<p>Location: {event.location}</p>
<p>From Date: {event.from_date}</p>
<p>Finish Date: {event.finish_date}</p>
<p>Time: {event.time}</p>
<p>Event Type: {event.event_type}</p>
<p>Event Time: {event.event_time}</p>
{/* <p>Color: {event.eventColor}</p> */}
<p>Notes: {event.eventNotes}</p>
</div>
))}
{/* {timetable && (
<div>
<h2>Generated Timetable:</h2>
<p>{timetable}</p>
</div>
)} */}
{timetable && (
<div>
<h2>Generated Timetable:</h2>
<div dangerouslySetInnerHTML={{ __html: timetable }} />
</div>
)}
</div>
);
}
export default GenerateCalendar;
This is how the front-end looks like:

It just gets the user input and sends it to the backend, and the backend connects to the GPT4 API to generate the timetable. The problem is that, right now, it doesn’t generate timetable, it just generates text like this:

But the generated timetable should be like this:

Specifically, I’d like to know:
Is it possible this to be done? And if yes, how it is possible?