I’m developing telegram bot. My bot helps users to remember their things to do. So the thing is there are many types of such things to do which user can create: everyday, every week, few times a day, once a year, etc. My bot has a command which shows user’s records. To do it properly I wrote a function which makes text of the answer (e.g. if type is "everyday" then bot answers "everyday at HH:MM", if type is "every week" then bot says "every monday at HH:MM").
My problem is that to do it I made a dictionary where keys are types of such records, values are text which bot might tell user. But this dictionary doesn’t work correct: when I pass record type "month" or "everyday" or any other to the function it always get value of key "week" although it should get value of key "month" or "everyday". I get KeyError besause of it
Code:
Main function with the command to show records:
@dp.message_handler(commands=['reminders'], state="*")
async def show_reminder(message: types.Message):
"""
Shows user's records
"""
records = get_records(int(message.from_user.id))
if records:
await message.answer(messages.show_records_message)
for item in records:
await message.answer(make_record_text(item))
else:
###
The function which creates text of the answer:
def make_record_text(record: Record) -> str:
"""
Creates an answer with user's record using Record
"""
days_of_week_dict = {
'monday': "every monday",
'tuesday': "every tuesday",
'wednesday': "every wednesday",
'thursday': "every thursday",
'friday': "every friday",
'saturday': "every saturday",
'sunday': "every sunday",
}
print(f'record = {record}')
print(f'record type = {record.type}')
print(f'record date = {record.date}')
print(f'record time = {record.time}')
periods = {'year': f'every year {record.time}',
'once': record.date,
'week': f'{days_of_week_dict[record.time]}',
'month': f'every month on {record.time}',
'several_min': f'every {record.time} minutes',
'everyday': f'everyday at {record.time}',
'several_hours': f'every {record.time} hours',
'every_few_days': f'every {record.time} days',
}
return messages.list_records_message.format(title=record.title,
date=periods[record.type],
id=record.id)
output:
record = Record(user=1234, title='month', date='month', time='25', type='month', need_delete=0, id=1)
record type = month
record date = month
record time = 25
Class Record:
class Record(NamedTuple):
user: int
title: str
date: Union[type(datetime.datetime), str]
time: Union[type(datetime.datetime), str]
type: Literal['everyday', 'few_times_a_day', 'every_few_days',
'week', 'month', 'year', 'once']
need_delete: bool
id: Optional[int]
Error message:
File "C:Userspizhlo21DesktopFolderpythontg_bot_remindercontroller.py", line 121, in show_reminder
await message.answer(make_record_text(item))
File "C:Userspizhlo21DesktopFolderpythontg_bot_remindercontroller.py", line 146, in make_record_text
'week': f'{days_of_week_dict[record.time]}',
KeyError: '25'
Why does it happen if I pass "month"?
2
Answers
The issue is that you’re building the whole
periods
dict before knowing whether therecord.time
value is valid for that kind of period, and evaluatingf'{days_of_week_dict[record.time]}'
has Python try and accessdays_of_week_dict["25"]
, which isn’t going to fly.I switched your
namedtuple
for adataclass
below since it feels more accurate, but the actual fix is thematch
statement that only formats the human-readable period that’s appropriate for thetype
. (If your Python version is older than 3.10 and doesn’t havematch
, you can replace it withif self.type == "year"
, etc.)This outputs
The issue arises because:
periods
You can fix that by changing bracket notation to
.get()
dictionary method, which will evaluate asNone
if the key is non-existing (or other value if you specify).So changing
periods
assignment to:Should fix the problem.