Skip to content

Commit

Permalink
some changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Samoed committed Sep 16, 2024
1 parent 1d9414b commit a78e946
Show file tree
Hide file tree
Showing 15 changed files with 432 additions and 62 deletions.
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,11 @@ POSTGRES_PORT=5432
POSTGRES_DB=example
POSTGRES_USER=example
POSTGRES_PASSWORD=example
TG_BOT_TOKEN=example
GOOGLE_CREDENTIALS_PATH=.env.example
GOOGLE_TOKEN_PATH=.env.example
TZ=Europe/Moscow
ADMIN_CHAT_ID=example
COURSE_1_EXCEL_CALENDAR_ID=1-i2YxGk_Mk_rrXM-EouOwPb1F6eNYI1IAPTyg8KT4RE
COURSE_2_EXCEL_CALENDAR_ID=1zjXZZtHvQ2OW9Uv_ylfRa1KvJFLEgjO_R5AZqeaBans
COURSE_INFO_URL=https://aith-courses.ru
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,5 @@ ics/
/db/
*.json
*.csv
client_secret_*
token.pickle
127 changes: 125 additions & 2 deletions pdm.lock

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

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies = [
"sqlalchemy>=2.0.32",
"alembic>=1.13.2",
"asyncpg>=0.29.0",
"python-telegram-bot[job-queue]>=21.5",
]
scripts = { cli="src.itmo_ai_timetable.cli:main" }

Expand Down
6 changes: 6 additions & 0 deletions src/itmo_ai_timetable/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import os

from itmo_ai_timetable.settings import Settings

settings = Settings()
os.environ["TZ"] = settings.tz
100 changes: 100 additions & 0 deletions src/itmo_ai_timetable/bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import html
import json
import traceback
from datetime import time

import pytz
from logger import get_logger
from repositories.repository import Repository
from schedule_parser import ScheduleParser
from settings import Settings
from telegram import Update
from telegram.constants import ParseMode
from telegram.ext import (
Application,
CommandHandler,
ContextTypes,
)

logger = get_logger(__name__)
settings = Settings()


async def sync_courses_table(context: ContextTypes.DEFAULT_TYPE) -> None:
await context.bot.send_message(settings.admin_chat_id, "Start sync table")
for i, (excel_url, list_name) in enumerate(settings.get_calendar_settings()):
logger.info(f"Start sync {list_name}")
parser = ScheduleParser(excel_url, list_name)
pairs = parser.parse()
not_found = await Repository.add_classes(pairs)
if not_found:
logger.warning(f"Classes not found: {not_found}")
not_found_str = [f"- {pair.start_time} {pair.end_time} {pair.name}\n" for pair in not_found]
await context.bot.send_message(
settings.admin_chat_id,
f"Classes not found: {not_found_str}\nКурс: {i}",
)
return
logger.info(f"End sync {list_name}")
await context.bot.send_message(settings.admin_chat_id, "Sync finished table")


async def update_classes_calendar(context: ContextTypes.DEFAULT_TYPE) -> None:
pass


async def ping(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: # noqa: ARG001
"""Ping bot."""
if update.message is None:
return
logger.info(f"Ping {update.message.chat.id}")
await update.message.reply_text("Pong")


async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Log the error and send a telegram message to notify the developer."""
# https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/errorhandlerbot.py
logger.error("Exception while handling an update:", exc_info=context.error)

tb_list = traceback.format_exception(None, context.error, context.error.__traceback__)
tb_string = "".join(tb_list)

update_str = update.to_dict() if isinstance(update, Update) else str(update)
message = (
"An exception was raised while handling an update\n"
f"<pre>update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}"
"</pre>\n\n"
f"<pre>context.chat_data = {html.escape(str(context.chat_data))}</pre>\n\n"
f"<pre>context.user_data = {html.escape(str(context.user_data))}</pre>\n\n"
f"<pre>{html.escape(tb_string)}</pre>"
)
await context.bot.send_message(settings.admin_chat_id, text=message, parse_mode=ParseMode.HTML)


def add_handlers(application: Application) -> None:
application.add_handler(CommandHandler("ping", ping))


def add_jobs(application: Application, time_zone_str: str) -> None:
time_zone = pytz.timezone(time_zone_str)
if application.job_queue is None:
raise ValueError("Job queue is None")
application.job_queue.run_daily(sync_courses_table, time=time(8, tzinfo=time_zone))
application.job_queue.run_daily(update_classes_calendar, time=time(hour=8, minute=5, tzinfo=time_zone))


def main() -> None:
"""Start the bot."""
logger.info("start bot")

application = Application.builder().token(settings.tg_bot_token).post_init(sync_courses_table).build()

add_handlers(application)
add_jobs(application, settings.tz)
application.add_error_handler(error_handler)

application.run_polling(allowed_updates=Update.ALL_TYPES)


if __name__ == "__main__":
main()
26 changes: 23 additions & 3 deletions src/itmo_ai_timetable/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import json
from pathlib import Path

from repositories.calendar import CalendarRepository
from repositories.course_info import CourseInfoRepository

from itmo_ai_timetable.logger import get_logger
from itmo_ai_timetable.repository import Repository
from itmo_ai_timetable.repositories.db import DBRepository
from itmo_ai_timetable.schedule_parser import ScheduleParser
from itmo_ai_timetable.selection_parser import SelectionParser
from itmo_ai_timetable.transform_ics import export_ics
Expand All @@ -16,6 +19,7 @@
class SubparserName(str, enum.Enum):
SCHEDULE = "schedule"
SELECTION = "selection"
SYNC = "sync"


def create_args() -> argparse.Namespace:
Expand Down Expand Up @@ -61,9 +65,23 @@ def create_args() -> argparse.Namespace:
type=bool,
default=False,
)
subparsers.add_parser(SubparserName.SYNC, help="Обработка excel с выборностью")

return parser.parse_args()


async def sync_calendar() -> None:
courses = await DBRepository.get_courses()
calendar = CalendarRepository()
for course in courses:
if course.timetable_id is None:
calendar_id = calendar.get_or_create_calendar(course.name)
course.timetable_id = calendar_id
if course.course_info_link is None:
course.course_info_link = CourseInfoRepository.add_link(course.name)
await DBRepository.update_courses(courses)


async def main() -> None:
logger.info("Start")
args = create_args()
Expand All @@ -76,7 +94,7 @@ async def main() -> None:
case SubparserName.SCHEDULE:
schedule = ScheduleParser(args.filepath, args.sheet_name).parse()
if args.db:
_ = await Repository.add_classes(schedule)
_ = await DBRepository.add_classes(schedule)
export_ics(schedule, output_dir)
case SubparserName.SELECTION:
results = SelectionParser(
Expand All @@ -91,7 +109,9 @@ async def main() -> None:
json.dump(results, f, ensure_ascii=False, indent=4)
course_number = args.course_number
if args.db:
await Repository.create_matching(results, course_number)
await DBRepository.create_matching(results, course_number)
case SubparserName.SYNC:
await sync_calendar()
case _:
raise ValueError(f"Unknown subparser {args.subparser}")
logger.info(f"Files exported to {output_path}")
Expand Down
23 changes: 0 additions & 23 deletions src/itmo_ai_timetable/gcal.py

This file was deleted.

Empty file.
Loading

0 comments on commit a78e946

Please sign in to comment.