async def receive_new_project_settings(message: types.Message, user: User, state: FSMContext): file = await bot.get_file(message.document['file_id']) settings = io.BytesIO() await file.download(settings) try: s = json.load(settings) async with OrmSession() as session: async with state.proxy() as data: project = await session.get(Project, data['project_id']) project.settings = s await session.commit() await message.reply(text(text('Проект', project.name), text('Новые настройки для проекта', project.name), code(json.dumps(s, indent=4, sort_keys=True)), sep='\n'), parse_mode=types.ParseMode.MARKDOWN) await state.finish() except json.decoder.JSONDecodeError: await message.reply( 'Это совсем не похоже на json-файл, пришлите другой файл', reply_markup=types.ForceReply(selective=True)) except UnicodeDecodeError: await message.reply( 'Это совсем не похоже на json-файл, у него вообще неизвестный бинарный формат, пришлите другой файл', reply_markup=types.ForceReply(selective=True))
async def process_project_settigns_file(message: types.Message, user: User, chat: Chat, state: FSMContext): dt = message.date file = await bot.get_file(message.document['file_id']) settings = io.BytesIO() await file.download(settings) try: s = json.load(settings) async with OrmSession() as session: async with state.proxy() as data: project = Project( owner_user_id=user.id, name=data['project_name'], chat_id=chat.id, period_bucket_mode=human_period_bucket_modes[data['human_period_bucket_mode']], created_dt=dt, settings=s ) session.add(project) await session.commit() await message.reply( text( text('Проект', project.name, 'создан'), text('Примененные настройки:'), code(json.dumps(s, indent=4, sort_keys=True)), sep='\n' ), parse_mode=types.ParseMode.MARKDOWN ) await state.finish() except json.decoder.JSONDecodeError: await message.reply( 'Это совсем не похоже на json-файл, пришлите другой файл', reply_markup=types.ForceReply(selective=True) )
async def process_note_text(message: types.Message, chat: Chat, state: FSMContext): dt = message.date async with state.proxy() as data: async with OrmSession() as session: select_stmt = select(Project)\ .where( Project.chat_id == chat.id )\ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() item_in_list_pos = data['item_in_list_pos'] item_id = data['items_ids'][item_in_list_pos] log_message = ItemNote(project_id=project.id, item_id=item_id, text=message.text, created_dt=dt) logging.info(log_message) session.add(log_message) await session.commit() await message.reply(emojize( text(text('Все, так и запишу:'), text(' :pencil2:', message.text), sep='\n')), disable_web_page_preview=True) await state.finish()
async def process_project_name(message: types.Message, user: User, state: FSMContext): async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.name == message.text, Project.owner_user_id == user.id, ) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() if not project: await state.update_data(project_name=message.text) await NewProjectForm.next() keyboard = [ [types.KeyboardButton(mode_name)] for mode_name in human_period_bucket_modes.keys() ] await message.reply( f'Выберите, когда обновлять списки задач', reply_markup=types.ReplyKeyboardMarkup(keyboard=keyboard, selective=True) ) else: await message.reply( f'Проект "{message.text}" уже существует, выберите другое имя', reply_markup=types.ForceReply(selective=True) )
async def notification_processing_loop(): nsktz = pytz.timezone('Asia/Novosibirsk') while True: async with OrmSession() as session: select_stmt = select(Project) projects_result = await session.execute(select_stmt) for project in projects_result.scalars(): await process_project_settings_and_send_messages( session, project, datetime.now(nsktz)) await asyncio.sleep(10)
async def log_message_about_work(message: types.Message, user: User, chat: Chat, state: FSMContext): dt = message.date async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.chat_id == chat.id ) \ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() bucket = PeriodBucket.new(project.period_bucket_mode, dt) items_list = await project.get_for_period(session, bucket) items_num = len(items_list.items) if items_list else 0 if items_num > 0: items_texts = [ item.text for item in sorted(items_list.items, key=lambda i: i.id) ] item_new_status = message.get_command(pure=True) await state.update_data(item_new_status=item_new_status) await state.update_data(items_num=items_num) await state.update_data(items_texts=items_texts) await state.update_data(items_ids=[ item.id for item in sorted(items_list.items, key=lambda i: i.id) ]) if items_num > 1: await ChangeItemStatusForm.item_id.set() keyboard = [[ types.KeyboardButton( f'{i+1}. {txt if len(txt) < 32 else txt[:29] + "..."}') ] for i, txt in enumerate(items_texts)] await message.reply( 'Выберите, пожалуйста, задачу из списка ' f'текущего периода по порядковому номеру от 1 до {items_num}', reply_markup=types.ReplyKeyboardMarkup(keyboard=keyboard, selective=True)) else: items_list.items[0].status = ItemStatus[item_new_status] await session.commit() await message.reply(emojize( text(text('В плане один единственный пункт:'), text(' :pushpin:', items_texts[0]), text(human_item_status[items_list.items[0].status]), sep='\n')), disable_web_page_preview=True) else: await message.answer( 'Извините, у вас ничего не запланировано в ближайшее время')
async def todo_for_today(message: types.Message, chat: Chat): dt = message.date async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.chat_id == chat.id ) \ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() bucket = PeriodBucket.new(project.period_bucket_mode, dt) todo_list = await project.get_for_period(session, bucket, with_log_messages=True) if project.period_bucket_mode == PeriodBucketModes.daily: period_bucket_word = 'на сегодня' elif project.period_bucket_mode == PeriodBucketModes.onworkingdays: period_bucket_word = 'на сегодня' elif project.period_bucket_mode == PeriodBucketModes.weekly: period_bucket_word = 'на текущую неделю' elif project.period_bucket_mode == PeriodBucketModes.monthly: period_bucket_word = 'в этом месяце' elif project.period_bucket_mode == PeriodBucketModes.yearly: period_bucket_word = 'в этом году' else: period_bucket_word = '' if todo_list: message_content = [ text('Вот, что вы', period_bucket_word, 'планировали:') if period_bucket_word else text('Вот, что вы планировали:'), text('') ] for item in sorted(todo_list.items, key=lambda i: i.id): message_content.append(text(':pushpin:', item.text)) for log_message in item.notes: message_content.append( text(' :paperclip:', log_message.text)) message_content += [text(''), text('Все точно получится!')] else: message_content = [ text('Никаких планов нет.'), # TODO предложение создать план и описание способа ] await message.answer( emojize(text(*message_content, sep='\n')), disable_web_page_preview=True, )
async def todo_for_next_time(message: types.Message, chat: Chat): # TODO почти copy-paste, разобраться dt = message.date async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.chat_id == chat.id ) \ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() bucket = PeriodBucket.new(project.period_bucket_mode, dt).get_next() todo_list = await project.get_for_period(session, bucket) if todo_list: if project.period_bucket_mode == PeriodBucketModes.daily: period_bucket_word = 'на следующий день' elif project.period_bucket_mode == PeriodBucketModes.onworkingdays: period_bucket_word = 'на следующий рабочий день' elif project.period_bucket_mode == PeriodBucketModes.weekly: period_bucket_word = 'на следующую неделю' elif project.period_bucket_mode == PeriodBucketModes.monthly: period_bucket_word = 'на следующий месяц' elif project.period_bucket_mode == PeriodBucketModes.yearly: period_bucket_word = 'в следующем году' else: period_bucket_word = '' message_content = [ text('Вот, что запланировано вами ', period_bucket_word, ':', sep='') if period_bucket_word else text('Вот, что запланировано вами:'), text('') ] + [ text(':pushpin: ' + item.text) for item in sorted(todo_list.items, key=lambda i: i.id) ] + [text(''), text('Но не отвлекайтесь, пожалуйста.')] else: message_content = [ text('Никаких планов нет.'), # TODO предложение создать план и описание способа ] await message.answer( emojize(text(*message_content, sep='\n')), disable_web_page_preview=True, )
async def log_message_about_work(message: types.Message, chat: Chat, state: FSMContext): dt = message.date async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.chat_id == chat.id ) \ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() bucket = PeriodBucket.new(project.period_bucket_mode, dt) items_list = await project.get_for_period(session, bucket) items_num = len(items_list.items) if items_list else 0 if items_num > 0: items_texts = [item.text for item in items_list.items] await state.update_data(items_num=items_num) await state.update_data(items_texts=items_texts) await state.update_data( items_ids=[item.id for item in items_list.items]) if items_num > 1: await AddNoteToItemForm.item_id.set() keyboard = [[ types.KeyboardButton( f'{i+1}. {txt if len(txt) < 32 else txt[:29] + "..."}') ] for i, txt in enumerate(items_texts)] await message.reply( f'Напишите, пожалуйста, порядковый номер сегодняшней задачи от 1 до {items_num}', reply_markup=types.ReplyKeyboardMarkup(keyboard=keyboard, selective=True)) else: await state.update_data(item_in_list_pos=0) await AddNoteToItemForm.note_text.set() await message.reply( emojize( text(text('В плане один единственный пункт:'), text(' :pushpin:', items_texts[0]), text('Напишите свое сообщение и я его сохраню'), sep='\n')), reply_markup=types.ForceReply(selective=True), disable_web_page_preview=True) else: await message.answer( 'Извините, записи можно вести пока только по сегодняшним планам, ' 'а у вас ничего не запланировано')
async def start_project_settings_updating(message: types.Message, user: User): async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.owner_user_id == user.id, ) projects_result = await session.execute(select_stmt) keyboard = [] for project in projects_result.scalars(): if not keyboard or len(keyboard[-1]) < 2: keyboard.append([]) keyboard[-1].append(types.KeyboardButton(project.name)) if keyboard: await UpdateProjectSettingsForm.project_name.set() await message.reply('Выберите проект', reply_markup=types.ReplyKeyboardMarkup( keyboard=keyboard, selective=True)) else: await message.reply('У вас нет проектов')
async def receive_project_name(message: types.Message, user: User, state: FSMContext): async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.name == message.text, Project.owner_user_id == user.id, ) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() if project: await state.update_data(project_id=project.id) await UpdateProjectSettingsForm.next() await message.reply( 'Проект ' + message.text + '\nПришлите, пожалуйста, json-файл с настройками', reply_markup=types.ForceReply(selective=True)) else: await message.reply(f'Проекта "{message.text}" не существует')
async def pre_process(self, obj, data, *args): from_user = types.User.get_current() async with OrmSession() as session: # TODO take care about caching user = await session.get(User, from_user.id) data['user'] = user data['is_new_user'] = False if not data['user']: dt = obj.date log.info(f'Creating user @{from_user.username} with id {from_user.id}') user = User( id=from_user.id, username=from_user.username, first_name=from_user.first_name, last_name=from_user.last_name, lang=from_user.language_code, joined_dt=dt ) session.add(user) await session.commit() data['user'] = user data['is_new_user'] = True
async def process_item_id(message: types.Message, state: FSMContext): async with state.proxy() as data: mo = re.match(r'(\d+)(?:\. )?(.*)', message.text) if mo: item_in_list_pos = int(mo.group(1)) - 1 text_fragment = mo.group(2) if not text_fragment or text_fragment == data['items_texts'][ item_in_list_pos] or data['items_texts'][ item_in_list_pos].startswith(text_fragment[:-3]): item_new_status = data['item_new_status'] items_num = data['items_num'] if item_in_list_pos < items_num: item_id = data['items_ids'][item_in_list_pos] async with OrmSession() as session: item = await session.get(Item, item_id) item.status = ItemStatus[item_new_status] await session.commit() await message.reply( emojize( text(text(human_item_status[item.status], ':', sep=''), text(':pushpin:', item.text), sep='\n')), reply_markup=types.ReplyKeyboardRemove(), disable_web_page_preview=True) await state.finish() else: await message.reply( f'В вашем сегодняшнем плане нет столько пунктов, напишите число от 1 до {items_num}' ) else: await message.reply( 'Укажите, пожалуйста, либо просто порядковый номер без всего, либо нажмите на предложенную кнопку' ) else: await message.reply( 'Укажите, пожалуйста, либо просто порядковый номер без всего, либо нажмите на предложенную кнопку' )
async def pre_process(self, obj, data, *args): if isinstance(obj, types.ChatMemberUpdated): sender_chat = types.ChatMemberUpdated.get_current().chat else: sender_chat = types.Chat.get_current() async with OrmSession() as session: # TODO take care about caching chat = await session.get(Chat, sender_chat.id) data['chat'] = chat data['is_new_chat'] = False if not data['chat']: dt = obj.date log.info(f'Creating chat @{sender_chat.username} with id {sender_chat.id}') chat = Chat( id=sender_chat.id, type=sender_chat.type, username=sender_chat.username, joined_dt=dt ) session.add(chat) await session.commit() data['chat'] = chat data['is_new_chat'] = True
async def create_tomorrow_todo_list(message: types.Message, chat: Chat): dt = message.date async with OrmSession() as session: parsed_todo_items = get_bulleted_items_list_from_message(message) if parsed_todo_items: select_stmt = select(Project) \ .where( Project.chat_id == chat.id ) \ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() bucket = PeriodBucket.new(project.period_bucket_mode, dt).get_next() new_todo_list = await project.create_new_for_period_with_items_or_append_to_existing( session, bucket, dt, parsed_todo_items) await session.commit() if project.period_bucket_mode == PeriodBucketModes.daily: reply_message_text = text( text('Я запишу, что вы запланировали:') if new_todo_list else text('К тому, что вы уже запланировали я добавлю:'), text(''), *[ text(':inbox_tray: ', parsed_item) for parsed_item in parsed_todo_items ], text(''), text( 'Завтра я напомню об этом. Чтобы посмотреть планы в любой момент, можно набрать /planned' ), sep='\n') else: if project.period_bucket_mode == PeriodBucketModes.weekly: period_bucket_word = 'на следующую неделю' elif project.period_bucket_mode == PeriodBucketModes.monthly: period_bucket_word = 'на следующий месяц' elif project.period_bucket_mode == PeriodBucketModes.yearly: period_bucket_word = 'на следующий год' else: period_bucket_word = '' reply_message_text = text( (text('Я запишу ваши планы на ', period_bucket_word, ':', sep='') if new_todo_list else text('К вашим планам', period_bucket_word, 'я добавлю:')) if period_bucket_word else (text('Я запишу ваши планы') if new_todo_list else text('К вашим планам я добавлю:')), text(''), *[ text(':inbox_tray: ', parsed_item) for parsed_item in parsed_todo_items ], text(''), text( 'Завтра я напомню об этом. Чтобы посмотреть планы в любой момент, можно набрать /planned' ), sep='\n') await message.reply( emojize(reply_message_text), disable_web_page_preview=True, )
async def create_today_todo_list(message: types.Message, chat: Chat): dt = message.date async with OrmSession() as session: parsed_todo_items = get_bulleted_items_list_from_message(message) if parsed_todo_items: select_stmt = select(Project) \ .where( Project.chat_id == chat.id ) \ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() bucket = PeriodBucket.new(project.period_bucket_mode, dt) if bucket.is_valid(): new_todo_list = await project.create_new_for_period_with_items_or_append_to_existing( session, bucket, dt, parsed_todo_items) await session.commit() if project.period_bucket_mode in [ PeriodBucketModes.daily, PeriodBucketModes.onworkingdays ]: reply_message_text = text( text( 'План составлен не с вечера, но и день в день - тоже замечательно. Вот, пожалуйста:' ) if new_todo_list else text('К вашим сегодняшним планам я добавлю:'), text(''), *[ text(':inbox_tray:', parsed_item) for parsed_item in parsed_todo_items ], text(''), text( 'Чтобы свериться со списком запланированных дел, можно набрать /todo' ), sep='\n') else: if project.period_bucket_mode == PeriodBucketModes.weekly: period_bucket_word = 'на текущую неделю' elif project.period_bucket_mode == PeriodBucketModes.monthly: period_bucket_word = 'на текущий месяц' elif project.period_bucket_mode == PeriodBucketModes.yearly: period_bucket_word = 'на текущий год' else: period_bucket_word = '' reply_message_text = text( (text('Я запишу ваши планы на ', period_bucket_word, ':', sep='') if new_todo_list else text( 'К вашим планам', period_bucket_word, 'я добавлю:')) if period_bucket_word else (text('Я запишу ваши планы') if new_todo_list else text('К вашим планам я добавлю:')), text(''), *[ text(':inbox_tray:', parsed_item) for parsed_item in parsed_todo_items ], text(''), text( 'Чтобы свериться со списком запланированных дел, можно набрать /todo' ), sep='\n') await message.reply( emojize(reply_message_text), disable_web_page_preview=True, ) else: await message.reply( text( 'Сегодня выходной, никаких планов, пожалуйста. Ничего не буду добавлять' ), disable_web_page_preview=True, )
async def status_report(message: types.Message, chat: Chat): if message.get_command() == '/wsr': start_dt = message.date + relativedelta( weekday=WE(-1), hour=0, minute=0, second=0, microsecond=0) end_dt = message.date + relativedelta(weekday=WE, hour=0, minute=0, second=0, microsecond=0) \ - relativedelta(days=1) else: start_dt = message.date + relativedelta( day=1, hour=0, minute=0, second=0, microsecond=0) end_dt = message.date + relativedelta(months=1, day=1, hour=0, minute=0, second=0, microsecond=0, days=-1) grid = generate_grid(start_dt, end_dt) grid = [[[i[0], i[1]] for i in week] for week in grid] async with OrmSession() as session: select_stmt = select(Project) \ .where( Project.chat_id == chat.id ) \ .order_by(Project.id) projects_result = await session.execute(select_stmt) project = projects_result.scalars().first() from_period = PeriodBucket.new(project.period_bucket_mode, start_dt) todo_lists = await project.get_since(session, from_period, with_log_messages=True) message_content = [] for todo_list in todo_lists: bucket = PeriodBucket.get_by_key(todo_list.period_bucket_key) for todo_item in todo_list.items: message_content.append( text(':spiral_calendar_pad:', str(bucket), ':pushpin:', todo_item.text)) for log_message in todo_item.notes: message_content.append( text(':paperclip:', log_message.text)) message_content.append(text('')) if bucket.start(): for week in grid: for i in week: if i[1].date() == bucket.start().date(): i[0] = i[0].replace('white', 'purple') import io file = io.StringIO(emojize(text(*message_content, sep='\n'))) for week in grid: for i in week: if i[1].date() == datetime.now().date(): if 'white' in i[0] or 'black' in i[0]: i[0] = i[0].replace('circle', 'large_square') else: i[0] = i[0].replace('circle', 'square') grid = [[i[0] for i in week] for week in grid] await message.answer_document( file, caption=emojize( text(text( f'Отчет о проделанной работе с {start_dt.date()} по {end_dt.date()}' ), text(''), text('Пн Вт Ср Чт Пт Сб Вс'), *[text(*week, sep='') for week in grid], sep='\n')))