-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
292 lines (214 loc) · 8.25 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
from collections import UserDict
from datetime import datetime, date
import pickle
def input_error(func):
def inner(*args):
try:
return func(*args)
except IndexError:
return 'Not enough params. Try help'
except ValueError:
return 'Invalid value. Try again'
except KeyError:
return 'Contact not found. Try again'
return inner
class Field:
def __init__(self, value=None):
self.value = value
def __str__(self):
return str(self.value)
def __repr__(self) -> str:
return str(self)
class Name(Field):
pass
class Phone(Field):
def __init__(self, value):
super().__init__(value)
@property
def value(self):
return self.__value
@value.setter
def value(self, value):
if not value.isdigit() or len(value) != 12:
raise ValueError('Phone number must be a 12-digit number')
self.__value = value
class Birthday(Field):
def __init__(self, value=None):
self.__value = None
if value is not None:
self.value = value
@property
def value(self):
return self.__value
@value.setter
def value(self, value):
try:
self.__value = datetime.strptime(value, '%d-%m-%Y')
except ValueError:
raise ValueError('Birthday must be in "dd-mm-yyyy" format')
def __str__(self):
return self.__value.strftime('%d-%m-%Y')
class Record:
def __init__(self, name, phone=None, birthday=None):
self.name = name
self.phones = [phone] if phone else None
self.birthday = birthday
def add_phone(self, phone):
self.phones.append(phone)
return f'Phone {phone} successfully added'
def change_phone(self, index, phone):
self.phones[index] = phone
return f'Phone {phone} successfully changed'
def delete_phone(self, phone):
self.phones.remove(phone)
def __str__(self) -> str:
return f'{str(self.name)} {", ".join([str(p) for p in self.phones])} {str(self.birthday)}'
def set_birthday(self, birthday):
self.birthday = birthday
def get_birthday(self, birthday):
return self.birthday.value if self.birthday else None
def days_to_bd(self):
if not self.birthday:
return None
today = date.today()
bd = self.birthday.value
next_birthday = bd.replace(year=today.year).date()
if next_birthday and next_birthday < today:
next_birthday = next_birthday.replace(year=today.year + 1)
if next_birthday.year - today.year > 1:
return None
if next_birthday:
days_to_birthday = (next_birthday - today).days
return days_to_birthday
class AddressBook(UserDict):
start_iterate = 0
def add_record(self, record):
name = record.name.value
self.data[name] = record
def iterator(self, page=2):
while True:
if self.start_iterate >= len(self.data):
break
yield list(self.data.values())[self.start_iterate:self.start_iterate + page]
self.start_iterate += page
def show_all(self):
result = []
for record_batch in self.iterator():
for record in record_batch:
result.append(str(record))
return '\n'.join(result)
def search(self, query):
result = []
for record in self.data.values():
if query.lower() in record.name.value.lower() or any(query in phone.value for phone in record.phones):
result.append(str(record))
return '\n'.join(result)
def save(self, file_name):
file_name = 'data.bin'
with open(file_name, "wb") as fh:
pickle.dump(self.data, fh)
print('Your address book saved successfully')
def load(self, file_name):
with open(file_name, "rb") as fh:
self.data = pickle.load(fh)
print('Your address book load successfully')
contacts = AddressBook()
def help(*args):
return '''
"hello", відповідає у консоль "How can I help you?"
"help" Викликає список доступних команд
"add ...". За цією командою бот зберігає у пам'яті (у словнику наприклад) новий контакт. Замість ...
користувач вводить ім'я та номер телефону, обов'язково через пробіл.
"change..." За цією командою бот зберігає в пам'яті новий номер телефону існуючого контакту. Замість ...
користувач вводить ім'я та індекс та новий номер телефону, обов'язково через пробіл.
"phone ...." За цією командою бот виводить у консоль номер телефону для зазначеного контакту. Замість ...
користувач вводить ім'я контакту, чий номер треба показати.
"daystobd ..." Повертає кількість днів до наступного дня народження контакту
"show all". За цією командою бот виводить всі збереженні контакти з номерами телефонів у консоль.
"search". Для пошуку за частковим співпадінням серед усіх існуючих контактів.
"good bye", "close", "exit" по будь-якій з цих команд бот завершує свою роботу після того, як виведе у консоль "Good bye!".
'''
def hello(*args):
return '''How can I help you?'''
def exit(*args):
return '''Good Bye'''
def no_command(*args):
return '''Unknown command, try again'''
def show_all(*args):
return contacts.show_all()
def search(*args):
query = args[0]
result = contacts.search(query)
if result:
return result
return "No contacts found"
@input_error
def add(*args):
name = Name(args[0])
phone = Phone(args[1])
birthday = Birthday(args[2])
rec = contacts.get(str(name))
if rec:
return rec.add_phone(phone)
record = Record(name, phone, birthday)
contacts.add_record(record)
days_to_bd = record.days_to_bd()
if days_to_bd:
print(f'{days_to_bd} days until next birthday')
return f"Added <{name.value}> with phone <{phone.value}> and birthday <{str(birthday)}>"
@input_error
def phone(*args):
name = Name(args[0])
rec = contacts.get(name.value)
if rec:
return rec.phones
return f'There are no phones with name {name}'
def change(*args):
name = Name(args[0])
index = int(args[1])
new_phone = Phone(args[2])
rec = contacts.get(str(name))
if rec:
return rec.change_phone(index, new_phone)
return f'There are no phones with name {name}'
def days_to_bd(*args):
name = Name(args[0])
rec = contacts.get(str(name))
if rec:
return rec.days_to_bd()
return f'There are no contacts with name {name}'
def save(*args):
return contacts.save("data.bin")
def load(*args):
return contacts.load("data.bin")
COMMANDS = {help: 'help',
add: 'add',
exit: ['exit', 'close', 'good bye'],
hello: 'hello',
phone: 'phone',
change: 'change',
show_all: 'show all',
days_to_bd: 'days_to_bd',
search: 'search',
save: 'save',
load: 'load'
}
def command_handler(text):
for command, kword in COMMANDS.items():
if isinstance(kword, str):
if text.lower().startswith(kword):
return command, text.replace(kword, '').strip().split()
elif isinstance(kword, list):
if text.strip().lower() in kword:
return command, []
return no_command, None
def main():
print(help())
while True:
user_input = input('>>>')
command, data = command_handler(user_input)
print(command(*data))
if command == exit:
break
if __name__ == '__main__':
main()