1111 lines
38 KiB
HTML
1111 lines
38 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0063)file:///Users/home/Downloads/deepseek_html_20260114_010afa.html -->
|
||
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Building a Professional Scheduler Bot MVP</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
}
|
||
|
||
body {
|
||
background: #f8fafc;
|
||
color: #1a202c;
|
||
overflow-x: hidden;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.presentation-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 100vh;
|
||
padding: 20px;
|
||
}
|
||
|
||
.slide {
|
||
display: none;
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 40px;
|
||
margin: 20px auto;
|
||
max-width: 1000px;
|
||
width: 90%;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.slide.active {
|
||
display: block;
|
||
}
|
||
|
||
h1, h2, h3, h4 {
|
||
color: #2d3748;
|
||
margin-bottom: 20px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 2.5rem;
|
||
text-align: center;
|
||
margin-bottom: 15px;
|
||
color: #2d3748;
|
||
}
|
||
|
||
h2 {
|
||
font-size: 2rem;
|
||
border-bottom: 2px solid #e2e8f0;
|
||
padding-bottom: 10px;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
h3 {
|
||
font-size: 1.5rem;
|
||
color: #4f46e5;
|
||
margin-top: 20px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
p {
|
||
margin-bottom: 15px;
|
||
font-size: 1.1rem;
|
||
color: #4a5568;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
ul, ol {
|
||
margin-left: 25px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
li {
|
||
margin-bottom: 8px;
|
||
color: #4a5568;
|
||
}
|
||
|
||
.code-block {
|
||
background: #1a202c;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
border-left: 4px solid #4f46e5;
|
||
font-family: 'Courier New', monospace;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.code-block code {
|
||
color: #e2e8f0;
|
||
font-size: 0.95rem;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.code-to-type {
|
||
background-color: #f7fafc;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-family: 'Courier New', monospace;
|
||
color: #2d3748;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.instruction-box {
|
||
background: #f7fafc;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
border-left: 4px solid #4f46e5;
|
||
}
|
||
|
||
.instruction-box h4 {
|
||
color: #2d3748;
|
||
margin-bottom: 10px;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.test-box {
|
||
background: #f0fff4;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
border-left: 4px solid #48bb78;
|
||
}
|
||
|
||
.test-box h4 {
|
||
color: #2d3748;
|
||
margin-bottom: 10px;
|
||
font-size: 1.2rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.test-box h4::before {
|
||
content: "💻";
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.warning-box {
|
||
background: #fef3c7;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
border-left: 4px solid #f59e0b;
|
||
}
|
||
|
||
.problem-box {
|
||
background: #fff5f5;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
border-left: 4px solid #fc8181;
|
||
}
|
||
|
||
.solution-box {
|
||
background: #e6fffa;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
border-left: 4px solid #38b2ac;
|
||
}
|
||
|
||
.nav-buttons {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.nav-button {
|
||
background: white;
|
||
color: #4f46e5;
|
||
border: 1px solid #4f46e5;
|
||
padding: 12px 25px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 1rem;
|
||
font-weight: 500;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.nav-button:hover {
|
||
background: #4f46e5;
|
||
color: white;
|
||
}
|
||
|
||
.nav-button:disabled {
|
||
background: #e2e8f0;
|
||
border-color: #cbd5e0;
|
||
color: #a0aec0;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.slide-indicator {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.indicator-dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
background: #cbd5e0;
|
||
cursor: pointer;
|
||
transition: background 0.3s;
|
||
}
|
||
|
||
.indicator-dot.active {
|
||
background: #4f46e5;
|
||
}
|
||
|
||
.slide-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.slide-number {
|
||
color: #718096;
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.project-preview {
|
||
background: #f7fafc;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
text-align: center;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.big-emoji {
|
||
font-size: 4rem;
|
||
text-align: center;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.terminal {
|
||
background: #1a202c;
|
||
color: #48bb78;
|
||
padding: 15px;
|
||
border-radius: 6px;
|
||
font-family: 'Courier New', monospace;
|
||
margin: 15px 0;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.terminal-command {
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
.terminal-comment {
|
||
color: #718096;
|
||
}
|
||
|
||
.feature-list {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.feature-card {
|
||
background: #f7fafc;
|
||
border-radius: 6px;
|
||
padding: 20px;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.feature-card h4 {
|
||
color: #4f46e5;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.before-after {
|
||
display: flex;
|
||
gap: 20px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.before, .after {
|
||
flex: 1;
|
||
padding: 15px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.before {
|
||
background: #fff5f5;
|
||
border: 1px solid #fed7d7;
|
||
}
|
||
|
||
.after {
|
||
background: #f0fff4;
|
||
border: 1px solid #c6f6d5;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.slide {
|
||
padding: 25px;
|
||
width: 95%;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
h2 {
|
||
font-size: 1.6rem;
|
||
}
|
||
|
||
.big-emoji {
|
||
font-size: 3rem;
|
||
}
|
||
|
||
.before-after {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.feature-list {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="presentation-container">
|
||
<!-- Slide 0: Title Slide -->
|
||
<div class="slide" id="slide0">
|
||
<div class="slide-header">
|
||
<h1>Upgrading to Scheduler Bot MVP</h1>
|
||
<div class="slide-number">Slide 1 of 12</div>
|
||
</div>
|
||
|
||
<div class="project-preview">
|
||
<div class="big-emoji">🤖📅</div>
|
||
<h2>From Basic Bot to Professional MVP</h2>
|
||
<p>Transform your simple scheduler into a robust, user-friendly application</p>
|
||
</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>What You'll Build</h4>
|
||
<p>A complete scheduler bot MVP with:</p>
|
||
<ul>
|
||
<li>Database for user-specific schedules</li>
|
||
<li>Interactive setup wizard</li>
|
||
<li>Multiple schedule management</li>
|
||
<li>Better error handling and UX</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 1: Current Code Analysis -->
|
||
<div class="slide active" id="slide1">
|
||
<div class="slide-header">
|
||
<h2>Analyzing Current Code Issues</h2>
|
||
<div class="slide-number">Slide 2 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🔍</div>
|
||
|
||
<div class="problem-box">
|
||
<h4>Major Problems in Current Code:</h4>
|
||
<ul>
|
||
<li><strong>No Database:</strong> Single CSV file for all users</li>
|
||
<li><strong>Fixed Schedule:</strong> Can't customize per user</li>
|
||
<li><strong>Wrong Period Time:</strong> Period_7 has incorrect time (10:00-10:40)</li>
|
||
<li><strong>Missing Periods:</strong> Only 7 periods instead of 13</li>
|
||
<li><strong>No Error Handling:</strong> Crashes easily</li>
|
||
<li><strong>Basic Features:</strong> Limited commands</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="warning-box">
|
||
<h4>Critical Bug Found!</h4>
|
||
<p>In your current code, Period_7 has wrong times:</p>
|
||
<div class="code-to-type">'Period_7': ('10:00', '10:40'), # WRONG! Should be 14:20-15:00</div>
|
||
<p>Also missing Periods 8-13 from your CSV template!</p>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 2: MVP Architecture -->
|
||
<div class="slide" id="slide2">
|
||
<div class="slide-header">
|
||
<h2>MVP Architecture Design</h2>
|
||
<div class="slide-number">Slide 3 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🏗️</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>Choose: SQLite vs NoSQL</h4>
|
||
<p>For our scheduler, <strong>SQLite is better</strong> because:</p>
|
||
<ul>
|
||
<li>Structured schedule data (days, periods, classes)</li>
|
||
<li>Easy queries (find classes by day/time)</li>
|
||
<li>Built into Python - no extra setup</li>
|
||
<li>Reliable and ACID compliant</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="feature-list">
|
||
<div class="feature-card">
|
||
<h4>Database Schema</h4>
|
||
<ul>
|
||
<li>users table (user info)</li>
|
||
<li>classes table (schedule entries)</li>
|
||
<li>Simple, clean structure</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="feature-card">
|
||
<h4>Key Features</h4>
|
||
<ul>
|
||
<li>User-specific schedules</li>
|
||
<li>Interactive setup wizard</li>
|
||
<li>Multiple schedule support</li>
|
||
<li>Better error handling</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 3: Step 1 - Fix Period Times -->
|
||
<div class="slide" id="slide3">
|
||
<div class="slide-header">
|
||
<h2>Step 1: Fix Period Times</h2>
|
||
<div class="slide-number">Slide 4 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">⏰</div>
|
||
|
||
<div class="before-after">
|
||
<div class="before">
|
||
<h4>Current (WRONG):</h4>
|
||
<div class="code-block">
|
||
<code>PERIOD_TIMES = {
|
||
'Period_1': ('09:00', '09:40'),
|
||
'Period_2': ('10:00', '10:40'),
|
||
'Period_3': ('11:00', '11:40'),
|
||
'Period_4': ('11:50', '12:30'),
|
||
'Period_5': ('12:40', '13:20'),
|
||
'Period_6': ('13:30', '14:10'),
|
||
'Period_7': ('10:00', '10:40'), # WRONG!
|
||
}</code>
|
||
</div>
|
||
</div>
|
||
<div class="after">
|
||
<h4>Fixed Version:</h4>
|
||
<div class="code-block">
|
||
<code>PERIODS = [
|
||
(1, "09:00-09:40"),
|
||
(2, "10:00-10:40"),
|
||
(3, "11:00-11:40"),
|
||
(4, "11:50-12:30"),
|
||
(5, "12:40-13:20"),
|
||
(6, "13:30-14:10"),
|
||
(7, "14:20-15:00"),
|
||
(8, "15:20-16:00"),
|
||
(9, "16:15-16:55"),
|
||
(10, "17:05-17:45"),
|
||
(11, "17:55-18:35"),
|
||
(12, "18:45-19:20"),
|
||
(13, "19:20-20:00")
|
||
]</code>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="test-box">
|
||
<h4>Why This Matters:</h4>
|
||
<p>Your current Period_7 (10:00-10:40) overlaps with Period_2! This causes wrong class detection.</p>
|
||
<p>The correct times match your CSV template with all 13 periods.</p>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 4: Step 2 - Database Setup -->
|
||
<div class="slide" id="slide4">
|
||
<div class="slide-header">
|
||
<h2>Step 2: Database Setup</h2>
|
||
<div class="slide-number">Slide 5 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🗄️</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>Create SQLite Database</h4>
|
||
<p>Add this database class to your code:</p>
|
||
</div>
|
||
|
||
<div class="code-block">
|
||
<code>import sqlite3
|
||
import datetime
|
||
|
||
class SimpleDatabase:
|
||
def __init__(self, db_file="schedule_bot.db"):
|
||
self.db_file = db_file
|
||
self.init_database()
|
||
|
||
def init_database(self):
|
||
"""Initialize database with tables"""
|
||
conn = sqlite3.connect(self.db_file)
|
||
cursor = conn.cursor()
|
||
|
||
# Users table
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS users (
|
||
user_id INTEGER PRIMARY KEY,
|
||
username TEXT,
|
||
first_name TEXT,
|
||
last_name TEXT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
)
|
||
''')
|
||
|
||
# Classes table - one row per class entry
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS classes (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
user_id INTEGER,
|
||
day TEXT,
|
||
period INTEGER,
|
||
period_time TEXT,
|
||
subject TEXT,
|
||
class_group TEXT,
|
||
room TEXT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (user_id) REFERENCES users (user_id)
|
||
)
|
||
''')
|
||
|
||
conn.commit()
|
||
conn.close()</code>
|
||
</div>
|
||
|
||
<div class="test-box">
|
||
<h4>Test Database Setup:</h4>
|
||
<div class="terminal">
|
||
<span class="terminal-comment"># Initialize database</span><br>
|
||
<span class="terminal-command">db = SimpleDatabase()</span><br>
|
||
<span class="terminal-comment"># Check if database file was created</span><br>
|
||
<span class="terminal-command">print("Database initialized successfully")</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 5: Step 3 - Interactive Setup -->
|
||
<div class="slide" id="slide5">
|
||
<div class="slide-header">
|
||
<h2>Step 3: Interactive Setup Wizard</h2>
|
||
<div class="slide-number">Slide 6 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🧙♂️</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>Add Conversation Handler</h4>
|
||
<p>Replace your simple commands with an interactive setup:</p>
|
||
</div>
|
||
|
||
<div class="code-block">
|
||
<code>from telegram.ext import ConversationHandler
|
||
from telegram import ReplyKeyboardMarkup, KeyboardButton
|
||
|
||
# Conversation states
|
||
SELECT_DAY, SELECT_PERIOD, ENTER_SUBJECT, ENTER_GROUP, ENTER_ROOM = range(5)
|
||
|
||
async def setup_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
"""Start the setup conversation"""
|
||
await update.message.reply_text(
|
||
"📅 Let's add a class to your schedule.\n"
|
||
"First, select the day:",
|
||
reply_markup=ReplyKeyboardMarkup(
|
||
[["Monday"], ["Tuesday"], ["Wednesday"],
|
||
["Thursday"], ["Friday"], ["Cancel"]],
|
||
resize_keyboard=True, one_time_keyboard=True
|
||
)
|
||
)
|
||
return SELECT_DAY</code>
|
||
</div>
|
||
|
||
<div class="solution-box">
|
||
<h4>Benefits:</h4>
|
||
<ul>
|
||
<li>Users can create their own schedules</li>
|
||
<li>No need for CSV files</li>
|
||
<li>Interactive and user-friendly</li>
|
||
<li>Multiple schedules per user</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 6: Step 4 - Enhanced Commands -->
|
||
<div class="slide" id="slide6">
|
||
<div class="slide-header">
|
||
<h2>Step 4: Enhanced Commands</h2>
|
||
<div class="slide-number">Slide 7 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🎯</div>
|
||
|
||
<div class="before-after">
|
||
<div class="before">
|
||
<h4>Current Commands:</h4>
|
||
<ul>
|
||
<li>/start - Basic welcome</li>
|
||
<li>/whereami - Current class</li>
|
||
<li>/schedule - Today's schedule</li>
|
||
<li>/tomorrow - Tomorrow's schedule</li>
|
||
<li>/help - Basic help</li>
|
||
</ul>
|
||
</div>
|
||
<div class="after">
|
||
<h4>Enhanced MVP Commands:</h4>
|
||
<ul>
|
||
<li>/setup - Interactive schedule setup</li>
|
||
<li>/whereami - Enhanced with next class info</li>
|
||
<li>/today - Today's detailed schedule</li>
|
||
<li>/tomorrow - Tomorrow's schedule</li>
|
||
<li>/week - Full weekly overview</li>
|
||
<li>/clear - Remove all classes</li>
|
||
<li>/help - Comprehensive help</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="code-block">
|
||
<code>async def where_am_i(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
"""Enhanced: Show current OR next class"""
|
||
user_id = update.effective_user.id
|
||
now = datetime.datetime.now()
|
||
current_day = now.strftime("%A")
|
||
current_time = now.strftime("%H:%M")
|
||
|
||
# Get current class from database
|
||
current_class = db.get_current_class(user_id, current_day, current_time)
|
||
|
||
if current_class:
|
||
response = f"🎯 You should be in class now!\n📚 {current_class['subject']}"
|
||
else:
|
||
# Find next class
|
||
next_class = db.get_next_class(user_id, current_day, current_time)
|
||
if next_class:
|
||
response = f"✅ Free period!\n➡️ Next: {next_class['subject']} at {next_class['time']}"
|
||
else:
|
||
response = f"🎉 No more classes today!"</code>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 7: Step 5 - Better Error Handling -->
|
||
<div class="slide" id="slide7">
|
||
<div class="slide-header">
|
||
<h2>Step 5: Better Error Handling</h2>
|
||
<div class="slide-number">Slide 8 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🛡️</div>
|
||
|
||
<div class="problem-box">
|
||
<h4>Current Issues:</h4>
|
||
<ul>
|
||
<li>Crashes if CSV file missing</li>
|
||
<li>No user input validation</li>
|
||
<li>Poor error messages</li>
|
||
<li>No logging for debugging</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="solution-box">
|
||
<h4>Add Professional Error Handling:</h4>
|
||
<div class="code-block">
|
||
<code>import logging
|
||
|
||
# Setup logging
|
||
logging.basicConfig(
|
||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||
level=logging.INFO
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
def save_class(user_id, day, period, subject, group, room):
|
||
"""Save class with error handling"""
|
||
try:
|
||
if not all([day, period, subject]):
|
||
raise ValueError("Missing required fields")
|
||
|
||
db.save_class(user_id, day, period, subject, group, room)
|
||
logger.info(f"Class saved for user {user_id}")
|
||
return True
|
||
except Exception as e:
|
||
logger.error(f"Error saving class: {e}")
|
||
return False</code>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 8: Step 6 - Requirements Update -->
|
||
<div class="slide" id="slide8">
|
||
<div class="slide-header">
|
||
<h2>Step 6: Update Requirements</h2>
|
||
<div class="slide-number">Slide 9 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">📦</div>
|
||
|
||
<div class="before-after">
|
||
<div class="before">
|
||
<h4>Current requirements.txt:</h4>
|
||
<div class="code-block">
|
||
<code># Missing file or minimal content</code>
|
||
</div>
|
||
</div>
|
||
<div class="after">
|
||
<h4>Updated requirements.txt:</h4>
|
||
<div class="code-block">
|
||
<code>python-telegram-bot==20.3
|
||
python-dotenv==1.0.0
|
||
pandas==2.1.0 # Optional for CSV import</code>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>Environment Variables (.env file):</h4>
|
||
<div class="terminal">
|
||
<span class="terminal-comment"># Create .env file in project root</span><br>
|
||
<span class="terminal-command">BOT_TOKEN=your_actual_bot_token_here</span><br>
|
||
<span class="terminal-comment"># Remove token from code for security!</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="test-box">
|
||
<h4>Install Dependencies:</h4>
|
||
<div class="terminal">
|
||
<span class="terminal-command">pip install -r requirements.txt</span><br>
|
||
<span class="terminal-comment"># Or install individually:</span><br>
|
||
<span class="terminal-command">pip install python-telegram-bot python-dotenv</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 9: Complete MVP Code -->
|
||
<div class="slide" id="slide9">
|
||
<div class="slide-header">
|
||
<h2>Complete MVP Structure</h2>
|
||
<div class="slide-number">Slide 10 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">📁</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>Project Structure:</h4>
|
||
</div>
|
||
|
||
<div class="code-block">
|
||
<code>scheduler_bot_mvp/
|
||
├── scheduler_bot.py # Main bot file
|
||
├── requirements.txt # Dependencies
|
||
├── .env # Environment variables
|
||
├── schedule_bot.db # SQLite database (auto-created)
|
||
├── simple_database.py # Database class (optional)
|
||
└── README.md # Documentation</code>
|
||
</div>
|
||
|
||
<div class="feature-list">
|
||
<div class="feature-card">
|
||
<h4>Database Functions:</h4>
|
||
<ul>
|
||
<li>save_user() - Store user info</li>
|
||
<li>save_class() - Add class entry</li>
|
||
<li>get_user_classes() - Get schedule</li>
|
||
<li>get_current_class() - Find current</li>
|
||
<li>clear_schedule() - Remove all</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="feature-card">
|
||
<h4>Bot Commands:</h4>
|
||
<ul>
|
||
<li>/setup - Interactive wizard</li>
|
||
<li>/whereami - Smart detection</li>
|
||
<li>/today - Daily schedule</li>
|
||
<li>/week - Weekly overview</li>
|
||
<li>/clear - Clean slate</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 10: Testing Your MVP -->
|
||
<div class="slide" id="slide10">
|
||
<div class="slide-header">
|
||
<h2>Testing Your MVP</h2>
|
||
<div class="slide-number">Slide 11 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🧪</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>Step-by-Step Testing:</h4>
|
||
<ol>
|
||
<li>Run the bot: <span class="code-to-type">python scheduler_bot.py</span></li>
|
||
<li>Open Telegram and find your bot</li>
|
||
<li>Send <span class="code-to-type">/start</span> command</li>
|
||
<li>Use <span class="code-to-type">/setup</span> to add classes</li>
|
||
<li>Test <span class="code-to-type">/whereami</span> throughout day</li>
|
||
<li>Try <span class="code-to-type">/today</span> and <span class="code-to-type">/week</span></li>
|
||
<li>Test error cases (wrong inputs)</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<div class="test-box">
|
||
<h4>Expected Behavior:</h4>
|
||
<ul>
|
||
<li>✅ Database file created automatically</li>
|
||
<li>✅ Interactive setup works with keyboards</li>
|
||
<li>✅ /whereami shows current or next class</li>
|
||
<li>✅ /week displays full schedule</li>
|
||
<li>✅ /clear removes all classes</li>
|
||
<li>✅ Error messages are user-friendly</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button" onclick="changeSlide(1)">
|
||
Next →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide 11: Next Steps & Challenges -->
|
||
<div class="slide" id="slide11">
|
||
<div class="slide-header">
|
||
<h2>Next Steps & Challenges</h2>
|
||
<div class="slide-number">Slide 12 of 12</div>
|
||
</div>
|
||
|
||
<div class="big-emoji">🚀</div>
|
||
|
||
<div class="instruction-box">
|
||
<h4>Enhancement Challenges:</h4>
|
||
</div>
|
||
|
||
<div class="feature-list">
|
||
<div class="feature-card">
|
||
<h4>Easy Enhancements:</h4>
|
||
<ul>
|
||
<li>Add /edit command to modify classes</li>
|
||
<li>Implement /export to generate CSV</li>
|
||
<li>Add /import from CSV feature</li>
|
||
<li>Create /reminders for upcoming classes</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="feature-card">
|
||
<h4>Advanced Features:</h4>
|
||
<ul>
|
||
<li>Multiple schedule versions</li>
|
||
<li>Class notifications (10 min before)</li>
|
||
<li>Teacher/room-specific searches</li>
|
||
<li>Web interface for management</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="solution-box">
|
||
<h4>Key Improvements Made:</h4>
|
||
<ol>
|
||
<li>✅ Fixed period times (all 13 periods)</li>
|
||
<li>✅ Added SQLite database</li>
|
||
<li>✅ Created interactive setup wizard</li>
|
||
<li>✅ Enhanced commands with better UX</li>
|
||
<li>✅ Added proper error handling</li>
|
||
<li>✅ Implemented user-specific schedules</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<div class="nav-buttons">
|
||
<button class="nav-button" onclick="changeSlide(-1)">
|
||
← Previous
|
||
</button>
|
||
<button class="nav-button">
|
||
Complete! 🎉
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Slide Indicator -->
|
||
<div class="slide-indicator">
|
||
<div class="indicator-dot" onclick="goToSlide(0)"></div>
|
||
<div class="indicator-dot active" onclick="goToSlide(1)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(2)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(3)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(4)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(5)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(6)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(7)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(8)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(9)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(10)"></div>
|
||
<div class="indicator-dot" onclick="goToSlide(11)"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Presentation state
|
||
let currentSlide = 0;
|
||
const totalSlides = 12;
|
||
|
||
// Initialize presentation
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
updateSlideNavigation();
|
||
});
|
||
|
||
// Change slide
|
||
function changeSlide(direction) {
|
||
const newSlide = currentSlide + direction;
|
||
|
||
if (newSlide >= 0 && newSlide < totalSlides) {
|
||
// Hide current slide
|
||
document.getElementById(`slide${currentSlide}`).classList.remove('active');
|
||
|
||
// Update indicator
|
||
document.querySelectorAll('.indicator-dot')[currentSlide].classList.remove('active');
|
||
|
||
// Show new slide
|
||
currentSlide = newSlide;
|
||
document.getElementById(`slide${currentSlide}`).classList.add('active');
|
||
|
||
// Update indicator
|
||
document.querySelectorAll('.indicator-dot')[currentSlide].classList.add('active');
|
||
|
||
// Update navigation buttons
|
||
updateSlideNavigation();
|
||
|
||
// Update slide numbers
|
||
updateSlideNumbers();
|
||
}
|
||
}
|
||
|
||
// Go to specific slide
|
||
function goToSlide(slideIndex) {
|
||
if (slideIndex >= 0 && slideIndex < totalSlides) {
|
||
// Hide current slide
|
||
document.getElementById(`slide${currentSlide}`).classList.remove('active');
|
||
|
||
// Update indicator
|
||
document.querySelectorAll('.indicator-dot')[currentSlide].classList.remove('active');
|
||
|
||
// Show new slide
|
||
currentSlide = slideIndex;
|
||
document.getElementById(`slide${currentSlide}`).classList.add('active');
|
||
|
||
// Update indicator
|
||
document.querySelectorAll('.indicator-dot')[currentSlide].classList.add('active');
|
||
|
||
// Update navigation buttons
|
||
updateSlideNavigation();
|
||
|
||
// Update slide numbers
|
||
updateSlideNumbers();
|
||
}
|
||
}
|
||
|
||
// Update navigation buttons
|
||
function updateSlideNavigation() {
|
||
const slides = document.querySelectorAll('.slide');
|
||
slides.forEach((slide, index) => {
|
||
const buttons = slide.querySelectorAll('.nav-button');
|
||
buttons.forEach(button => {
|
||
if (button.textContent.includes('Previous')) {
|
||
button.disabled = currentSlide === 0;
|
||
} else if (button.textContent.includes('Next')) {
|
||
button.disabled = currentSlide === totalSlides - 1;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// Update slide numbers
|
||
function updateSlideNumbers() {
|
||
const slideNumbers = document.querySelectorAll('.slide-number');
|
||
slideNumbers.forEach((number, index) => {
|
||
if (index === currentSlide) {
|
||
number.textContent = `Slide ${currentSlide + 1} of ${totalSlides}`;
|
||
}
|
||
});
|
||
}
|
||
|
||
// Keyboard navigation
|
||
document.addEventListener('keydown', function(event) {
|
||
if (event.key === 'ArrowRight' || event.key === ' ') {
|
||
changeSlide(1);
|
||
} else if (event.key === 'ArrowLeft') {
|
||
changeSlide(-1);
|
||
} else if (event.key >= '1' && event.key <= '9') {
|
||
const slideNum = parseInt(event.key) - 1;
|
||
if (slideNum < totalSlides) {
|
||
goToSlide(slideNum);
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
|
||
</body></html> |