├── src ├── __init__.py ├── models.py ├── utils.py └── main.py ├── requirements.txt ├── static └── css │ └── style.css ├── .gitignore └── README.md /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit==1.32.0 2 | openai==1.12.0 3 | python-dotenv==1.0.1 -------------------------------------------------------------------------------- /src/models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pydantic import BaseModel 3 | from typing import Optional 4 | 5 | class Appointment(BaseModel): 6 | id: int 7 | date: datetime 8 | description: str 9 | is_cancelled: bool = False 10 | 11 | class ChatMessage(BaseModel): 12 | role: str # "user" or "assistant" 13 | content: str 14 | timestamp: datetime = datetime.now() 15 | -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | /* Custom styles for the appointment scheduler */ 2 | 3 | .stButton button { 4 | width: 100%; 5 | margin-top: 10px; 6 | } 7 | 8 | .appointment-card { 9 | border: 1px solid #ddd; 10 | padding: 15px; 11 | margin: 10px 0; 12 | border-radius: 5px; 13 | } 14 | 15 | .appointment-card:hover { 16 | box-shadow: 0 2px 5px rgba(0,0,0,0.1); 17 | } 18 | 19 | .cancelled { 20 | opacity: 0.6; 21 | } 22 | 23 | /* Chat styles */ 24 | .chat-message { 25 | padding: 10px; 26 | margin: 5px 0; 27 | border-radius: 5px; 28 | } 29 | 30 | .user-message { 31 | background-color: #e3f2fd; 32 | margin-left: 20%; 33 | } 34 | 35 | .assistant-message { 36 | background-color: #f5f5f5; 37 | margin-right: 20%; 38 | } 39 | -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Tuple 3 | 4 | def parse_date(date_str: str) -> Tuple[bool, datetime | None]: 5 | """Parse date string into datetime object.""" 6 | try: 7 | return True, datetime.strptime(date_str, "%Y-%m-%d %H:%M") 8 | except ValueError: 9 | return False, None 10 | 11 | def format_date(date: datetime) -> str: 12 | """Format datetime object to string.""" 13 | return date.strftime("%Y-%m-%d %H:%M") 14 | 15 | def is_valid_appointment_time(date: datetime) -> bool: 16 | """Check if appointment time is valid (in the future).""" 17 | return date > datetime.now() 18 | 19 | def format_message(message: str) -> str: 20 | """Format chat message for display.""" 21 | return message.strip() 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | .Python 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | lib/ 14 | lib64/ 15 | parts/ 16 | sdist/ 17 | var/ 18 | wheels/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | 23 | # Virtual Environment 24 | venv/ 25 | ENV/ 26 | env/ 27 | .env 28 | .venv 29 | env.bak/ 30 | venv.bak/ 31 | 32 | # IDE 33 | .idea/ 34 | .vscode/ 35 | *.swp 36 | *.swo 37 | .DS_Store 38 | 39 | # Project specific 40 | instance/ 41 | .webassets-cache 42 | .env.local 43 | .env.development.local 44 | .env.test.local 45 | .env.production.local 46 | 47 | # Logs 48 | *.log 49 | logs/ 50 | log/ 51 | 52 | # Coverage reports 53 | htmlcov/ 54 | .tox/ 55 | .coverage 56 | .coverage.* 57 | .cache 58 | nosetests.xml 59 | coverage.xml 60 | *.cover 61 | .hypothesis/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Appointment Scheduler App 2 | 3 | A Streamlit-based application for scheduling and managing appointments. 4 | 5 | ## Features 6 | - Schedule new appointments 7 | - View existing appointments 8 | - Cancel appointments 9 | - Real-time chat interface 10 | 11 | ## Setup 12 | 13 | 1. Create a virtual environment: 14 | ```bash 15 | python -m venv venv 16 | source venv/bin/activate # On Windows: venv\Scripts\activate 17 | ``` 18 | 19 | 2. Install dependencies: 20 | ```bash 21 | pip install -r requirements.txt 22 | ``` 23 | 24 | 3. Run the application: 25 | ```bash 26 | streamlit run src/main.py 27 | ``` 28 | 29 | ## Project Structure 30 | ``` 31 | ├── src/ # Source code 32 | │ ├── main.py # Main Streamlit application 33 | │ ├── models.py # Data models/classes 34 | │ ├── utils.py # Helper functions 35 | │ └── storage.py # Storage handling 36 | ├── static/ # Static files 37 | │ ├── css/ # CSS styles 38 | │ └── images/ # Images 39 | ├── templates/ # HTML templates 40 | ├── requirements.txt # Project dependencies 41 | └── README.md # This file 42 | ``` 43 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from datetime import datetime 3 | from models import Appointment, ChatMessage 4 | from utils import parse_date, format_date, is_valid_appointment_time 5 | from openai import OpenAI 6 | import os 7 | from dotenv import load_dotenv 8 | import httpx 9 | 10 | 11 | # Load environment variables 12 | load_dotenv() 13 | print("hello world") 14 | # Initialize OpenAI client 15 | http_client = httpx.Client() 16 | client = OpenAI( 17 | api_key=os.environ.get('OPENAI_API_KEY'), 18 | http_client=http_client 19 | ) 20 | if not os.environ.get("OPENAI_API_KEY"): 21 | st.error("OpenAI API key not found. Please set the OPENAI_API_KEY environment variable.") 22 | st.stop() 23 | 24 | # Page config 25 | st.set_page_config( 26 | page_title="Appointment Scheduler", 27 | page_icon="📅", 28 | layout="wide" 29 | ) 30 | 31 | # Initialize session state 32 | if "appointments" not in st.session_state: 33 | st.session_state.appointments = [] 34 | if "messages" not in st.session_state: 35 | st.session_state.messages = [] 36 | if "counter" not in st.session_state: 37 | st.session_state.counter = 1 38 | 39 | def main(): 40 | st.title("📅 Appointment Scheduler") 41 | 42 | # Sidebar 43 | with st.sidebar: 44 | st.header("Navigation") 45 | page = st.radio("Go to", ["Schedule", "View", "Chat"]) 46 | 47 | if page == "Schedule": 48 | show_schedule_page() 49 | elif page == "View": 50 | show_view_page() 51 | else: 52 | show_chat_page() 53 | 54 | def show_schedule_page(): 55 | st.header("Schedule an Appointment") 56 | 57 | with st.form("appointment_form"): 58 | date_str = st.text_input("Date and Time (YYYY-MM-DD HH:MM)") 59 | description = st.text_area("Description") 60 | submitted = st.form_submit_button("Schedule") 61 | 62 | if submitted: 63 | success, date = parse_date(date_str) 64 | if not success: 65 | st.error("Invalid date format. Please use YYYY-MM-DD HH:MM") 66 | elif not is_valid_appointment_time(date): 67 | st.error("Appointment must be in the future") 68 | else: 69 | # Create new appointment 70 | appointment = Appointment( 71 | id=st.session_state.counter, 72 | date=date, 73 | description=description 74 | ) 75 | st.session_state.appointments.append(appointment) 76 | st.session_state.counter += 1 77 | st.success(f"Appointment scheduled for {format_date(appointment.date)}") 78 | 79 | def show_view_page(): 80 | st.header("View Appointments") 81 | 82 | if not st.session_state.appointments: 83 | st.info("No appointments scheduled yet.") 84 | return 85 | 86 | # Sort appointments by date 87 | appointments = sorted(st.session_state.appointments, key=lambda x: x.date) 88 | 89 | for apt in appointments: 90 | with st.expander(f"Appointment on {format_date(apt.date)}"): 91 | st.write(f"**Description:** {apt.description}") 92 | st.write(f"**Status:** {'Cancelled' if apt.is_cancelled else 'Active'}") 93 | if not apt.is_cancelled: 94 | if st.button(f"Cancel Appointment #{apt.id}"): 95 | # Find and cancel the appointment 96 | for a in st.session_state.appointments: 97 | if a.id == apt.id: 98 | a.is_cancelled = True 99 | break 100 | st.rerun() 101 | 102 | def process_chat_command(message: str) -> str: 103 | """Process chat commands and return appropriate response""" 104 | message = message.lower().strip() 105 | 106 | # Help command 107 | if message == "help": 108 | return """I can help you schedule and cancel appointments. Here are the available commands: 109 | - To schedule: schedule [date] [time] [description] 110 | Example: schedule 2024-03-20 14:30 Dental checkup 111 | - To cancel: cancel [appointment_id] 112 | Example: cancel 1 113 | - To list: list appointments 114 | - For help: help""" 115 | 116 | # List appointments command 117 | if message == "list appointments": 118 | if not st.session_state.appointments: 119 | return "You have no appointments scheduled." 120 | 121 | active_appointments = [apt for apt in st.session_state.appointments if not apt.is_cancelled] 122 | if not active_appointments: 123 | return "You have no active appointments." 124 | 125 | response = "Here are your active appointments:\n" 126 | for apt in sorted(active_appointments, key=lambda x: x.date): 127 | response += f"- ID #{apt.id}: {format_date(apt.date)} - {apt.description}\n" 128 | return response 129 | 130 | # Schedule appointment command 131 | if message.startswith("schedule "): 132 | try: 133 | _, date_str, time_str, *desc_parts = message.split() 134 | date_time_str = f"{date_str} {time_str}" 135 | description = " ".join(desc_parts) 136 | 137 | success, date = parse_date(date_time_str) 138 | if not success: 139 | return "Invalid date format. Please use: schedule YYYY-MM-DD HH:MM [description]" 140 | 141 | if not is_valid_appointment_time(date): 142 | return "Appointment must be in the future" 143 | 144 | # Create new appointment 145 | appointment = Appointment( 146 | id=st.session_state.counter, 147 | date=date, 148 | description=description 149 | ) 150 | st.session_state.appointments.append(appointment) 151 | st.session_state.counter += 1 152 | 153 | return f"✅ Appointment #{appointment.id} scheduled for {format_date(appointment.date)}" 154 | except Exception: 155 | return "Invalid schedule command. Please use: schedule YYYY-MM-DD HH:MM [description]" 156 | 157 | # Cancel appointment command 158 | if message.startswith("cancel "): 159 | try: 160 | _, apt_id = message.split() 161 | apt_id = int(apt_id) 162 | 163 | for apt in st.session_state.appointments: 164 | if apt.id == apt_id: 165 | if apt.is_cancelled: 166 | return f"Appointment #{apt_id} is already cancelled." 167 | apt.is_cancelled = True 168 | return f"✅ Appointment #{apt_id} has been cancelled." 169 | 170 | return f"Could not find appointment with ID #{apt_id}" 171 | except Exception: 172 | return "Invalid cancel command. Please use: cancel [appointment_id]" 173 | 174 | return "I didn't understand that command. Type 'help' to see available commands." 175 | 176 | def show_chat_page(): 177 | st.header("Chat Support") 178 | 179 | # Display chat history 180 | for message in st.session_state.messages: 181 | with st.chat_message(message.role): 182 | st.write(message.content) 183 | 184 | # Chat input 185 | if prompt := st.chat_input("Type your message..."): 186 | # Add user message 187 | user_message = ChatMessage(role="user", content=prompt) 188 | st.session_state.messages.append(user_message) 189 | 190 | # Process the command and get response 191 | response = process_chat_command(prompt) 192 | 193 | # Add assistant response 194 | assistant_message = ChatMessage(role="assistant", content=response) 195 | st.session_state.messages.append(assistant_message) 196 | st.rerun() 197 | 198 | if __name__ == "__main__": 199 | main() 200 | --------------------------------------------------------------------------------