Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions sql/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,18 @@ CREATE TABLE ideas (
creation_date TIMESTAMP NOT NULL DEFAULT NOW()
);

-- Daily Stats Table --
CREATE TABLE IF NOT EXISTS daily_stats (
id SERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_tasks INT NOT NULL DEFAULT 0,
created_ideas INT NOT NULL DEFAULT 0,
stats_day TIMESTAMP NOT NULL DEFAULT NOW()
);

ALTER TABLE daily_stats
ADD COLUMN stats_day_date DATE NOT NULL DEFAULT CURRENT_DATE;

CREATE UNIQUE INDEX IF NOT EXISTS unique_daily_stat_per_user
ON daily_stats (user_id, stats_day_date);

1 change: 1 addition & 0 deletions telegram_bot_project/.idea/dictionaries/project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions telegram_bot_project/bot/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from bot.buttons import *
from messages import MESSAGES
from service.idea import IdeaService
from service.myday import MyDayService
from service.task import TaskService
from service.user import UserService
from states import DialogStates
Expand Down Expand Up @@ -68,6 +69,7 @@ async def callback_idea_process(callback_query: types.CallbackQuery, state: FSMC
return
else:
await IdeaService.create_user_idea(user_id, idea)
await MyDayService.increment_idea_count(user_id)

print(f"--[INFO] - User {user_id} ({user_name}) saved idea: {idea}")
await callback_query.message.answer(MESSAGES[language]["IDEA_SAVED"], reply_markup=idea_reply_keyboard())
Expand Down Expand Up @@ -102,6 +104,7 @@ async def callback_task_deadline(callback_query: types.CallbackQuery, state: FSM
print(f"--[INFO] - User {user_id} ({user_name}) saved task: {saved_task}")

await TaskService.create_task(user_id, saved_task, False)
await MyDayService.increment_task_count(user_id)
await callback_query.message.answer(MESSAGES[language]["TASK_DEADLINE_NO"], reply_markup=task_menu_keyboard())
await state.clear()
case _:
Expand Down
26 changes: 25 additions & 1 deletion telegram_bot_project/bot/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from service.user import UserService
from bot.buttons import *
from states import DialogStates
from service.myday import MyDayService

# Start Command Handler
async def start_command(message: types.Message):
Expand Down Expand Up @@ -412,4 +413,27 @@ async def send_feedback_command(message: types.Message, state: FSMContext):
await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM'])
else:
await message.answer(MESSAGES[language]['SMTP_MESSAGE_TEXT'])
await state.set_state(DialogStates.feedback_message)
await state.set_state(DialogStates.feedback_message)

# Show daily stats command
async def show_daily_stats_command(message: types.Message):
user_id: int = message.from_user.id
user_find = await UserService.get_user_by_id(user_id)
language = await UserService.get_user_language(user_id) or "ENGLISH"

if not user_find:
await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM'])
return

stats = await MyDayService.get_today_stats(user_id)
if not stats:
created_ideas = 0
completed_tasks = 0
created_tasks = 0
else:
created_ideas = stats.get("created_ideas", 0)
completed_tasks = stats.get("completed_tasks", 0)
created_tasks = stats.get("created_tasks", 0)

text = generate_daily_stats_message(language, created_ideas, completed_tasks, created_tasks)
await message.answer(text, parse_mode="Markdown", reply_markup=menu_reply_keyboard())
8 changes: 6 additions & 2 deletions telegram_bot_project/bot/handlers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from aiogram.fsm.context import FSMContext
from aiogram.types import Message
from datetime import datetime

from service.smtp import SmtpService
from bot.buttons import *
Expand All @@ -10,6 +11,7 @@
from states import DialogStates
from service.routine import RoutineService
from bot.utills import check_valid_time, validate_text
from service.myday import MyDayService

async def process_idea_save(message: Message, state: FSMContext) -> None:
user_id = message.from_user.id
Expand Down Expand Up @@ -79,6 +81,7 @@ async def process_idea_delete(message: Message, state: FSMContext) -> None:
print(f"--[DEBUG] Deleting idea with id {real_id} for user {user_id}")

await IdeaService.delete_user_idea(real_id)
await MyDayService.dicrement_idea_count(user_id)

await message.answer(MESSAGES[language]['IDEA_DELETED'].format(user_number, idea_to_delete['idea_name']), reply_markup=idea_reply_keyboard())
await state.clear()
Expand Down Expand Up @@ -165,8 +168,6 @@ async def process_task_save(message: Message, state: FSMContext):
await message.answer(MESSAGES[language]['TASK_DEADLINE_ASK'], reply_markup=task_reply_keyboard())
await state.update_data(task=task)

from datetime import datetime

async def process_task_deadline(message: Message, state: FSMContext):
user_id = message.from_user.id
user_find = await UserService.get_user_by_id(user_id)
Expand All @@ -188,6 +189,7 @@ async def process_task_deadline(message: Message, state: FSMContext):
print(f"--[INFO] User with id: {user_id} provided deadline: {deadline_dt}")

await TaskService.create_task(user_id, task, False, deadline_dt)
await MyDayService.increment_task_count(user_id)
await message.answer(MESSAGES[language]['TASK_SAVED'], reply_markup=task_menu_keyboard())
await state.clear()

Expand Down Expand Up @@ -220,6 +222,7 @@ async def process_task_delete(message: Message, state: FSMContext):

print(f"--[INFO] User with id: {user_id} deleted task with id: {real_id}")
await TaskService.delete_task(real_id)
await MyDayService.dicrement_task_count(user_id)
await message.answer(MESSAGES[language]['TASK_DELETED'].format(user_number, task_to_delete['task_name']), reply_markup=task_menu_keyboard())
await state.clear()

Expand Down Expand Up @@ -251,6 +254,7 @@ async def process_task_complete(message: Message, state: FSMContext):

print(f"--[INFO] User with id: {user_id} completed task with id: {real_id}")
await TaskService.toggle_task_status(real_id)
await MyDayService.increment_completed_tasks(user_id)
await message.answer(MESSAGES[language]['COMPLETE_TASK_SUCCESS'].format(user_number, task_to_complete['task_name']), reply_markup=task_menu_keyboard())
await state.clear()
except ValueError:
Expand Down
88 changes: 88 additions & 0 deletions telegram_bot_project/bot/scheduler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from datetime import datetime, date, time, timedelta
from sqlalchemy import text
from config import get_session
from service.myday import MyDayService

async def create_or_update_daily_stats_for_users():
async with get_session() as session:
result = await session.execute(text("SELECT id FROM users"))
user_ids = result.scalars().all()

today = date.today()
start_of_day = datetime.combine(today, time.min)
start_of_next_day = start_of_day + timedelta(days=1)

for user_id in user_ids:
tasks_result = await session.execute(
text("""
SELECT COUNT(*) FROM tasks
WHERE user_id = :user_id AND creation_date >= :start_of_day AND creation_date < :start_of_next_day
"""),
{
"user_id": user_id,
"start_of_day": start_of_day,
"start_of_next_day": start_of_next_day
}
)
tasks_count = tasks_result.scalar() or 0

completed_tasks_result = await session.execute(
text("""
SELECT COUNT(*) FROM tasks
WHERE user_id = :user_id AND status = TRUE AND creation_date >= :start_of_day AND creation_date < :start_of_next_day
"""),
{
"user_id": user_id,
"start_of_day": start_of_day,
"start_of_next_day": start_of_next_day
}
)
completed_tasks_count = completed_tasks_result.scalar() or 0

ideas_result = await session.execute(
text("""
SELECT COUNT(*) FROM ideas
WHERE user_id = :user_id AND creation_date >= :start_of_day AND creation_date < :start_of_next_day
"""),
{
"user_id": user_id,
"start_of_day": start_of_day,
"start_of_next_day": start_of_next_day
}
)
ideas_count = ideas_result.scalar() or 0

existing = await session.execute(
text("""
SELECT id FROM daily_stats
WHERE user_id = :user_id AND stats_day_date = CURRENT_DATE
"""),
{"user_id": user_id}
)
stat_id = existing.scalar()

if stat_id is None:
await MyDayService.create_daily_stats(
user_id=user_id,
created_tasks=tasks_count,
created_ideas=ideas_count,
completed_tasks=completed_tasks_count
)
else:
await session.execute(
text("""
UPDATE daily_stats
SET created_tasks = :created_tasks,
created_ideas = :created_ideas,
completed_tasks = :completed_tasks
WHERE id = :stat_id
"""),
{
"created_tasks": tasks_count,
"created_ideas": ideas_count,
"completed_tasks": completed_tasks_count,
"stat_id": stat_id
}
)

await session.commit()
11 changes: 8 additions & 3 deletions telegram_bot_project/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import asyncio

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from aiogram import Bot, Dispatcher, F
from aiogram.filters import Command
from aiogram.fsm.storage.memory import MemoryStorage
Expand All @@ -10,7 +10,9 @@
from bot.commands import *
from bot.callbacks import *
from bot.fallbacks import *
from bot.scheduler import create_or_update_daily_stats_for_users

scheduler: AsyncIOScheduler = AsyncIOScheduler()
storage: MemoryStorage = MemoryStorage()
dp = Dispatcher(storage=storage)

Expand Down Expand Up @@ -159,8 +161,8 @@ async def feedback(message: Message, state: FSMContext):

@dp.message(Command("myday"))
@dp.message(lambda m: m.text == BUTTON_MYDAY)
async def my_day():
pass
async def my_day(message: Message):
await show_daily_stats_command(message)

@dp.callback_query(F.data.in_({"morning_view", "evening_view"}))
async def callback_routine(callback_query: CallbackQuery):
Expand All @@ -184,6 +186,9 @@ async def process_fallback(message: Message, state: FSMContext):

# Main Function
async def main():
scheduler.add_job(create_or_update_daily_stats_for_users, 'cron', hour=0, minute=0)
scheduler.start()
await create_or_update_daily_stats_for_users()
bot = Bot(token=TOKEN)
await dp.start_polling(bot)

Expand Down
Loading