def test_find_nonexistent_entry_by_name(self): book = PhoneBook() book.add_entry('João', '(11) 1234 4321') entry = book.find_by_name('Maria') self.assertIsNone(entry)
def test_lookup_by_name(self): phonebook = PhoneBook( ) # Click alt+enter and choose a create new class phonebook.add( "Bob", '12345' ) # PyCharm is highlight a word which needed to define -> click right ande choose 'Add method add() to class PhoneBook' number = phonebook.lookup( "Bob") # Click 'Add method lookup() to class PhoneBook' self.assertEqual("12345", number)
def __init__(self, manufacturer, model, os): """ init method """ self.manufacturer = manufacturer self.model = model self.os = os self.owner = "No owner yet" self._phonebook = PhoneBook()
def compare_phonebooks(expect: a1.PhoneBook, actual: a1.PhoneBook) -> bool: exp_curr = expect.get_head() act_curr = actual.get_head() identical = True while identical and exp_curr: if act_curr and exp_curr.get_element() != act_curr.get_element(): identical = False else: exp_curr = exp_curr.get_next() act_curr = act_curr.get_next() if act_curr: identical = False return identical
class Phone: """ Phone class """ def __init__(self, manufacturer, model, os): """ init method """ self.manufacturer = manufacturer self.model = model self.os = os self.owner = "No owner yet" self._phonebook = PhoneBook() def call(self): """ Decide who to call """ print(self._phonebook.contacts) contact_name = input("Enter contactname: ") self._make_call(contact_name) def _make_call(self, name): """ Pretend to make an actual phonecall """ contact = self._phonebook.get_contact(name) print("Calling number", contact, "...")
def run(self): self._load_configuration() self._init_database() self.pb = PhoneBook(self.dbconn) logging.info("Starting IRCThread thread") self.irc_thread = IRCThread(self, self.config['server'], self.config['server_port'], self.config['nickname'], self.config['channel']) self.irc_thread.start() logging.info("Starting webserver") http_thread = HTTPThread(self, ('0.0.0.0', 8090)) http_thread.start() logging.info("Starting main loop") self._main_loop()
class PhoneBookTest(unittest.TestCase): # This is run every time a test is set up def setUp(self) -> None: # this will be now created for each test cases, so no need to create instance in every test function # rather use self.phonebook self.phonebook = PhoneBook() def tearDown(self) -> None: pass def test_lookup_by_name(self): self.phonebook.add("Aftab", 12345) number = self.phonebook.lookup("Aftab") self.assertEqual(number, 12345) def test_empty_phonebook_is_consistent(self): self.assertTrue(self.phonebook.is_consistent())
class PhoneBookTest(unittest.TestCase): def setUp(self) -> None: # Super class self.phonebook = PhoneBook() # def tearDown(self) -> None: # With setUp we need use tearDown - this using to release from setUp test value. # pass # Because this setUp have everything in the memory we dosen't needed this. def test_lookup_by_name(self): # phonebook = PhoneBook() self.phonebook.add("Bob", '12345') # Change to self.phonebook number = self.phonebook.lookup("Bob") # Change to self.phonebook self.assertEqual("12345", number) def test_missing_name(self): # phonebook = PhoneBook() with self.assertRaises(KeyError): self.phonebook.lookup("missing") # Change to self.phonebook # @unittest.skip("WIP") # This features working to skip test after this command def test_empty_phonebook_is_consisten(self): # phonebook = PhoneBook() # We have 3 duplicates this line self.assertTrue( self.phonebook.is_consistent()) # Change to self.phonebook
def fileParse(xml_file): # parse selected avm_file from fileDialog while True: try: global tree, names tree = ET.ElementTree(file=xml_file) root = tree.getroot() print(root) no_count.set(len(root[0])) names = PhoneBook(tree).address() lbox.insert("end", *names[0].values()) return tree break except ET.ParseError: _msgBox_error() print("Oops! That was no valid XML. Try another one ...") break
def main(): phonebook = PhoneBook("Examples/Classes/contacts.csv", ";") # Kaksi oliota. Oliot on luotu samasta luokasta, mutta niillä on omat muuttujien arvonsa (olion tila) person1 = Contact("Maija", "Meikäläinen", "123454321", 1990) person2 = Contact("Matti", "Meikäläinen", "987656789", 1991) person3 = Student("Aimo", "Opiskelija", "1232123", 2000, 12345) phonebook.AddContact(person1) phonebook.AddContact(person2) phonebook.AddContact(person3) phonebook.PrintContacts() phonebook.SaveContacts(";")
def main(): phonebook = PhoneBook("Examples/Classes/contacts.csv") person1 = Contact("Maija", "Meikäläinen", "123456789", 1990) person2 = Contact("Matti", "Meikäläinen", "987654321", 1991) person3 = Student("Osku", "Opiskelija", "987656789", 2000, 54321) person4 = Teacher("Sami", "Kojo-Fry", "12121212", 1987, "A3-??") phonebook.AddContact(person1) phonebook.AddContact(person2) phonebook.AddContact(person3) phonebook.AddContact(person4) phonebook.PrintContacts() phonebook.SaveContacts()
class PhoneBookTest(unittest.TestCase): def setUp(self): self.phonebook = PhoneBook() def tearDown(self): pass def test_lookup_by_name(self): self.phonebook.add("Bob", "1234") number = self.phonebook.lookup("Bob") self.assertEqual("1234", number) def test_missing_name(self): with self.assertRaises(KeyError): self.phonebook.lookup("missing") @unittest.skip("WIP") def test_empty_phonebook_is_consistent(self): self.assertTrue(self.phonebook.is_consistant())
def setUp(self) -> None: """ The setUp method is called each time a test case is executed. :return: PhoneBook Class. """ self.phonebook = PhoneBook()
class PhoneBookTest(unittest.TestCase): def setUp(self) -> None: """ The setUp method is called each time a test case is executed. :return: PhoneBook Class. """ self.phonebook = PhoneBook() def tearDown(self) -> None: """ This class is used to release the resource which are used by the testcases like in the case of using some files for testing or maybe the use of some database or so. As of now in this code its not required and hence it is passed. """ pass def test_lookup_by_name(self): self.phonebook.add("Arjun", "12345") number = self.phonebook.lookup("Arjun") self.assertEqual("12345", number) def test_missing_name(self): with self.assertRaises(KeyError): self.phonebook.lookup("Missing") def test_empty_phonebook_is_consistent(self): self.assertTrue(self.phonebook.is_consistent()) def test_is_consistent_with_different_entries(self): self.phonebook.add("bob", "123454") self.phonebook.add("Anna", "012345") self.assertTrue(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_entries(self): self.phonebook.add("bob", "123454") self.phonebook.add("Anna", "123454") self.assertFalse(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_prefix(self): self.phonebook.add("bob", "12345") self.phonebook.add("Sue", "123") self.assertFalse(self.phonebook.is_consistent())
def setUp(self): self.phonebook = PhoneBook()
class PhoneBookTest(unittest.TestCase): def setUp(self): self.phonebook = PhoneBook() # This method is great to set something like write in files or database # def tearDown(self): # pass def test_lookup_by_name(self): self.phonebook.add('Luis', '12345') number = self.phonebook.lookup('Luis') self.assertEqual('12345', number) def test_missing_name(self): with self.assertRaises(KeyError): self.phonebook.lookup('missing') # @unittest.skip("WIP") -> to skip test def test_empty_phonebook_is_consistent(self): self.assertTrue(self.phonebook.is_consistent()) def test_is_consistent_with_different_entries(self): self.phonebook.add('Luis', '12345') self.phonebook.add('Vilella', '0012345') self.assertTrue(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_entries(self): self.phonebook.add('Luis', '12345') self.phonebook.add('Felipe', '12345') self.assertFalse(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_prefix(self): self.phonebook.add('Luis', '12345') self.phonebook.add('Felipe', '123') self.assertFalse(self.phonebook.is_consistent())
class PhoneBookTest(unittest.TestCase): # setUp method is inherited from TestCase super class and called BEFORE each test method def setUp(self) -> None: # construct a new phonebook instance before each test case executes self.phonebook = PhoneBook() # tearDown method is called AFTER each test method # release resources reserved in setUp method OR during test # not needed in memory but needed for CI testing of database connection or filesystems # def tearDown(self) -> None: # pass def test_lookup_by_name(self): # construct a new phonebook class instance # phonebook = PhoneBook() # insert new name and number into phonebook self.phonebook.add("Bob", "12345") # lookup same name I just added number = self.phonebook.lookup("Bob") # check that the name i've looked up is associated to the number # first value is value were checking against, second argument is actual value from unit test self.assertEqual("12345", number) def test_missing_name(self): # assertRaises ensures everything in with context manager will throw a KeyError exception # KeyError is raised whenever a dictionary object is requested and the key is not present # In our case the name Giles was not present in the phonbook with self.assertRaises(KeyError): self.phonebook.lookup("Giles") # annotation to skip a unittest # @unittest.skip("WIP") def test_empty_phonebook_is_consistent(self): # is_consistent method should return true for empty phonebook self.assertTrue(self.phonebook.is_consistent()) def test_is_consistent_with_different_entries(self): # Arrange step to add entries # Act step where we check for consistency # assert whether it is or not self.phonebook.add("Bob", "12345") self.phonebook.add("Anna", "012345") self.assertTrue(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_entries(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Sue", "12345") self.assertFalse(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_prefix(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Sue", "123") self.assertFalse(self.phonebook.is_consistent())
class PhoneBookTest(unittest.TestCase): def setUp(self) -> None: self.phonebook = PhoneBook() def tearDown(self) -> None: pass def test_lookup_by_name(self): self.phonebook.add("Bob", "12345") number = self.phonebook.lookup("Bob") self.assertEqual("12345", number) def test_missing_name(self): with self.assertRaises(KeyError): self.phonebook.lookup("missing") def test_empty_phonebook_is_consistent(self): self.assertTrue(self.phonebook.is_consistent()) def test_is_consistent_with_different_entries(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Anna", "012345") self.assertTrue(self.phonebook.is_consistent()) def test_is_inconsistent_with_duplicate_entries(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Sue", "12345") self.assertFalse(self.phonebook.is_consistent()) def test_is_inconsistent_with_duplicate_prefix(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Sue", "123") self.assertFalse(self.phonebook.is_consistent()) def test_phonebook_adds_names_and_numbers(self): self.phonebook.add("Sue", "123343") self.assertIn("Sue", self.phonebook.get_names()) self.assertIn("123343", self.phonebook.get_numbers())
def test_add_and_lookup_entry(): phonebook = PhoneBook() phonebook.add("Bob", "12345") assert "12345" == phonebook.lookup("Bob")
def init_mw(self, tmw=None): """ Method to initialize main window (menus and frames) """ t0 = time.time() alarmApp = self tmw = tmw if isinstance(tmw, Qt.QMainWindow) else CleanMainWindow() tmw.setWindowTitle('PANIC') tmw.menuBar = Qt.QMenuBar(tmw) tmw.toolsMenu = Qt.QMenu('Tools', tmw.menuBar) tmw.fileMenu = Qt.QMenu('File', tmw.menuBar) tmw.viewMenu = Qt.QMenu('View', tmw.menuBar) tmw.helpMenu = Qt.QMenu('Help', tmw.menuBar) tmw.setMenuBar(tmw.menuBar) [ tmw.menuBar.addAction(a.menuAction()) for a in (tmw.fileMenu, tmw.toolsMenu, tmw.helpMenu, tmw.viewMenu) ] toolbar = Qt.QToolBar(tmw) toolbar.setIconSize(Qt.QSize(20, 20)) tmw.helpMenu.addAction(getThemeIcon("applications-system"), "Webpage", lambda: os.system('konqueror %s &' % PANIC_URL)) tmw.toolsMenu.addAction(getThemeIcon("applications-system"), "Jive", lambda: os.system('jive &')) tmw.toolsMenu.addAction(getThemeIcon("applications-system"), "Astor", lambda: os.system('astor &')) tmw.fileMenu.addAction(getThemeIcon(":/designer/back.png"), "Export to CSV file", alarmApp.saveToFile) tmw.fileMenu.addAction(getThemeIcon(":/designer/forward.png"), "Import from CSV file", alarmApp.loadFromFile) tmw.fileMenu.addAction(getThemeIcon(":/designer/filereader.png"), "Use external editor", alarmApp.editFile) tmw.fileMenu.addAction(getThemeIcon("applications-system"), "Exit", tmw.close) tmw.viewMenu.connect(tmw.viewMenu, Qt.SIGNAL('aboutToShow()'), alarmApp.setViewMenu) from phonebook import PhoneBook alarmApp.tools['bookApp'] = WindowManager.addWindow( PhoneBook(container=tmw)) tmw.toolsMenu.addAction(getThemeIcon("x-office-address-book"), "PhoneBook", alarmApp.tools['bookApp'].show) toolbar.addAction(getThemeIcon("x-office-address-book"), "PhoneBook", alarmApp.tools['bookApp'].show) trend_action = (getThemeIcon(":/designer/qwtplot.png"), 'Trend', lambda: WindowManager.addWindow( widgets.get_archive_trend(show=True))) tmw.toolsMenu.addAction(*trend_action) toolbar.addAction(*trend_action) alarmApp.tools['config'] = WindowManager.addWindow( dacWidget(container=tmw)) tmw.toolsMenu.addAction(getThemeIcon("applications-system"), "Advanced Configuration", alarmApp.tools['config'].show) toolbar.addAction(getThemeIcon("applications-system"), "Advanced Configuration", alarmApp.tools['config'].show) toolbar.setMovable(False) toolbar.setFloatable(False) tmw.addToolBar(toolbar) if SNAP_ALLOWED: alarmApp.tools['history'] = WindowManager.addWindow( ahWidget(container=tmw)) tmw.toolsMenu.addAction(getThemeIcon("office-calendar"), "Alarm History Viewer", alarmApp.tools['history'].show) toolbar.addAction(getThemeIcon("office-calendar"), "Alarm History Viewer", alarmApp.tools['history'].show) else: trace("Unable to load SNAP", 'History Viewer Disabled!') alarm_preview_action = (getThemeIcon("accessories-calculator"), "Alarm Calculator", lambda g=alarmApp: WindowManager.addWindow( AlarmPreview.showEmptyAlarmPreview(g))) [o.addAction(*alarm_preview_action) for o in (tmw.toolsMenu, toolbar)] try: import PyTangoArchiving.widget.ArchivingBrowser MSW = PyTangoArchiving.widget.ArchivingBrowser.ModelSearchWidget alarmApp.tools['finder'] = WindowManager.addWindow(MSW()) tmw.toolsMenu.addAction(getThemeIcon("system-search"), "Attribute Finder", alarmApp.tools['finder'].show) toolbar.addAction(getThemeIcon("system-search"), "Attribute Finder", alarmApp.tools['finder'].show) except: print('PyTangoArchiving not available') #traceback.print_exc() import panic.gui.panel def showNewAlarmPanel(s=self, a=alarmApp): i = len([ w for w in WindowManager.getWindowsNames() if w.startswith('panel') ]) name = 'panel%d' % i a.tools[name] = WindowManager.addWindow( panic.gui.panel.QAlarmPanel()) a.tools[name].setModel(s.view) a.tools[name].show() url = os.path.dirname(panic.__file__) + '/gui/icon/panel-view.png' panel_icon = Qt.QIcon(Qt.QPixmap(url)) alarm_panel_action = (panel_icon, "Alarm Panel", showNewAlarmPanel) [o.addAction(*alarm_panel_action) for o in (tmw.toolsMenu, toolbar)] import panic.gui.views alarmApp.tools['rawview'] = WindowManager.addWindow( panic.gui.views.ViewRawBrowser()) #url = os.path.dirname(panic.__file__)+'/gui/icon/panel-view.png' #panel_icon = Qt.QIcon(Qt.QPixmap(url)) alarm_panel_action = ( getThemeIcon('actions:leftjust.svg'), "RawView", lambda s=self: alarmApp.tools['rawview'].setModel(self)) [o.addAction(*alarm_panel_action) for o in (tmw.toolsMenu, )] print('Toolbars created after %s seconds' % (time.time() - t0)) tmw.setCentralWidget(alarmApp) tmw.show() return tmw
class SMS900(): """ The main class to use """ def __init__(self, configuration_path): """ The init method for the main class. Should probably add an example or two here. """ self.configuration_path = configuration_path self.events = queue.Queue() self.config = None self.dbconn = None self.irc_thread = None self.pb = None def run(self): """ Starts the main loop""" self._load_configuration() self._init_database() self.pb = PhoneBook(self.dbconn) logging.info("Starting IRCThread thread") self.irc_thread = IRCThread(self, self.config['server'], self.config['server_port'], self.config['nickname'], self.config['channel']) self.irc_thread.start() logging.info("Starting webserver") http_thread = HTTPThread(self, ('0.0.0.0', 8090)) http_thread.start() logging.info("Starting main loop") self._main_loop() def _load_configuration(self): with open(self.configuration_path, 'r') as file: self.config = json.load(file) # FIXME: Check that we got everything we'll be needing def _init_database(self): self.dbconn = sqlite3.connect('sms900.db', isolation_level=None) conn = self.dbconn.cursor() try: conn.execute( "create table phonebook (" " id integer primary key," " nickname text UNIQUE," " number text UNIQUE" ")" ) except sqlite3.Error as err: logging.info("Failed to create table(s): %s", err) def queue_event(self, event_type, data): """ queues event, stupid doc string i know """ event = {'event_type': event_type} event.update(data) self.events.put(event) def _main_loop(self): while True: event = self.events.get() self._handle_event(event) def _handle_event(self, event): try: logging.info('EVENT: %s', event) if event['event_type'] == 'SEND_SMS': sender_hm = event['hostmask'] number = self._get_num_from_nick_or_num(event['number']) nickname = self._get_nickname_from_hostmask(sender_hm) # FIXME: Check the sender msg = "<%s> %s" % (nickname, event['msg']) self._send_sms(number, msg) elif event['event_type'] == 'ADD_PB_ENTRY': number = self._get_canonicalized_number(event['number']) nickname = event['nickname'] self.pb.add_number(nickname, number) self._send_privmsg(self.config['channel'], 'Added %s with number %s' % (nickname, number)) elif event['event_type'] == 'DEL_PB_ENTRY': nickname = event['nickname'] oldnumber = self.pb.get_number(nickname) self.pb.del_entry(nickname) self._send_privmsg(self.config['channel'], 'Removed contact %s (number: %s)' % (nickname, oldnumber)) elif event['event_type'] == 'LOOKUP_CARRIER': number = event['number'] number = self._get_canonicalized_number(number) self._lookup_carrier(number) elif event['event_type'] == 'SMS_RECEIVED': number = event['number'] sms_msg = event['msg'] sender = self.pb.get_nickname(number) mrex = re.search('ett MMS.+hamtamms.+koden ([^ ]+)', event['msg']) if mrex: self._download_mms(sender, mrex.group(1)) return msg = '<%s> %s' % (sender, sms_msg) self._send_privmsg(self.config['channel'], msg) except (SMS900InvalidNumberFormatException, SMS900InvalidAddressbookEntry) as err: self._send_privmsg(self.config['channel'], "Error: %s" % err) except Exception as err: self._send_privmsg(self.config['channel'], "Unknown error: %s" % err) traceback.print_exc() def _send_sms(self, number, message): logging.info('Sending sms ( %s -> %s )', message, number) try: client = TwilioRestClient(self.config['twilio_account_sid'], self.config['twilio_auth_token']) message_data = client.messages.create(to=number, from_=self.config['twilio_number'], body=message,) self._send_privmsg(self.config['channel'], "Sent %s sms to number %s" % (message_data.num_segments, number)) except twilio.TwilioRestException as err: self._send_privmsg(self.config['channel'], ("Failed to send sms: %s", err)) def _lookup_carrier(self, number): logging.info('Looking up number %s', number) try: client = TwilioLookupsClient(self.config['twilio_account_sid'], self.config['twilio_auth_token']) number_data = client.phone_numbers.get(number, include_carrier_info=True) self._send_privmsg(self.config['channel'], '%s is %s, carrier: %s' % (number, number_data.carrier['type'], number_data.carrier['name'])) except twilio.TwilioRestException as err: self._send_privmsg(self.config['channel'], "Failed to lookup number: %s" % err) def _send_privmsg(self, target, msg): self.irc_thread.send_privmsg(target, msg) def _get_canonicalized_number(self, number): match = re.match(r'^\+[0-9]+$', number) if match: logging.info('number %s already canonicalized, returning as is', number) return number match = re.match(r'^0(7[0-9]{8})$', number) if match: new_number = '+46%s' % match.group(1) logging.info('number %s was canonicalized and returned as %s', number, new_number) return new_number raise SMS900InvalidNumberFormatException("%s is not a valid number", number) def _get_num_from_nick_or_num(self, number_or_name): try: number_or_name = self._get_canonicalized_number(number_or_name) except SMS900InvalidNumberFormatException: try: number_or_name = self.pb.get_number(number_or_name) except SMS900InvalidAddressbookEntry as err2: raise SMS900InvalidNumberFormatException( "%s is not a valid number or existing nickname: %s" % (number_or_name, err2)) return number_or_name def _get_nickname_from_hostmask(self, hostmask): nick = re.match(r'^([^\!]+)', hostmask) if not nick: # FIXME return hostmask else: return nick.group(1) def _download_mms(self, sender, code): rel_path = str(uuid.uuid4()) save_path = path.join( self.config['mms_save_path'], rel_path ) mkdir(save_path) # FIXME: Hack. msisdn = self.config['twilio_number'].replace('+46', '0') fetcher = MMSFetcher(msisdn, code, save_path) files = fetcher.download_all() base_url = "%s/%s" % ( self.config['external_mms_url'], rel_path ) mms_summary, summary_contains_all = self._get_mms_summary(base_url, files) if mms_summary: self._send_privmsg( self.config['channel'], "[MMS] <%s> %s" % (sender, mms_summary) ) if not summary_contains_all: self._send_privmsg( self.config['channel'], "Received %d file(s): %s" % (len(files), base_url) ) def _get_mms_summary(self, base_url, files): try: text = None img_url = None # Find the first text and the first image file, if any for full_path in files: match = re.search(r'\.([^.]+)$', full_path) if match: if not img_url: if match.group(1) in ['jpg', 'jpeg', 'png']: filename = path.basename(full_path) img_url = "%s/%s" % (base_url, filename) if not text: if match.group(1) in ['txt']: with open(full_path, 'r', encoding='utf-8', errors='ignore') as file: text = file.read() if text or img_url: parts = [text, img_url] message = ", ".join([p for p in parts if p]) summary_contains_all = (len(parts) == len(files)) return message, summary_contains_all except Exception: traceback.print_exc() return None, False
def setUp(self) -> None: # this will be now created for each test cases, so no need to create instance in every test function # rather use self.phonebook self.phonebook = PhoneBook()
class PhoneBookTest(unittest.TestCase): # Runs before EVERY test def setUp(self) -> None: self.phonebook = PhoneBook() # Runs after EVERY test def tearDown(self) -> None: pass def test_lookup_by_name(self): self.phonebook.add("Bob", "12345") number = self.phonebook.lookup("Bob") self.assertEqual("12345", number) def test_missing_name_raises_error(self): # This will test that any of the code below the "with" stmt throws a KeyError with self.assertRaises(KeyError): self.phonebook.lookup("fake number") def test_is_consistent_when_empty(self): self.assertTrue(self.phonebook.is_consistent()) def test_is_consistent_when_all_nums_different(self): self.phonebook.add("Bob", "1234567") self.phonebook.add("Sue", "6171830") self.assertTrue(self.phonebook.is_consistent()) def test_is_NOT_consistent_when_duplicates(self): self.phonebook.add("Bob", "1234567") self.phonebook.add("Sue", "1234567") self.assertFalse(self.phonebook.is_consistent()) def test_is_NOT_consistent_when_duplicate_prefixes(self): self.phonebook.add("Bob", "1234567") self.phonebook.add("Sue", "123") self.assertFalse(self.phonebook.is_consistent()) def test_phonebook_adds_names_and_numbers(self): self.phonebook.add("Sue", "12345") self.phonebook.add("Bob", "1234567") self.assertIn("Sue", self.phonebook.get_names()) self.assertIn("12345", self.phonebook.get_numbers()) self.assertIn("Bob", self.phonebook.get_names()) self.assertIn("1234567", self.phonebook.get_numbers())
def run_app(): book = PhoneBook('personal book') # populate the book with test data for i in range(10): book.add_contact('Jhon' + str(i), 'Smith', '+71234567809' + str(i), favorite_contact=False, telegram='@jhony', email='*****@*****.**') book.add_contact('Jhon_fav', 'Smith', '+712345678090', favorite_contact=book.find_contact('Jhon2', 'Smith'), telegram='@jhony', email='*****@*****.**') book.add_contact('Jhon_fav2', 'Smith', '+712345671090', favorite_contact=book.find_contact('Jhon3', 'Smith'), telegram='@jhony', email='*****@*****.**') print(info_message) while True: cmd = input("Выберите действие:\n").lower() if cmd in cmd_list: if cmd == 'q': break elif cmd == 'p': book.print_contacts() elif cmd == 's': firstname = input("Введите имя:\n") lastname = input("Введите фамилию:\n") print(book.find_contact(firstname, lastname)) elif cmd == 'a': firstname = input("Введите имя:\n") lastname = input("Введите фамилию:\n") phone_number = input("Введите номер:\n") fav_contact_id = input( "Введите любимый контакт по номеру в списке или Enter для пропуска:\n" ) if fav_contact_id: fav_contact = book.find_contact_by_id(fav_contact_id) if not fav_contact: print("Любимый контакт не найдет. Продолжение работы.") print( 'Введите дополнительную информацию в формате "key=value" или Enter для пропуска:\n' ) additional_info = dict() while True: v = input("Введите дополнительную информацию :\n").strip() if v == '': break else: additional_info.update([tuple(v.split('='))]) book.add_contact(firstname, lastname, phone_number, fav_contact, **additional_info) elif cmd == 'd': phone_number = input("Введите номер телефона:\n") print("Контакт(ы) удилен(ы)" if book. del_contact(phone_number) else "Контакт не найден") elif cmd == 'f': pprint(book.get_all_favorite_numbers())
def setUp(self) -> None: # construct a new phonebook instance before each test case executes self.phonebook = PhoneBook()
def phonebook(tmpdir): phonebook = PhoneBook(tmpdir) return phonebook
def test_lookup_by_name(self): phonebook = PhoneBook() phonebook.add("Bob", '12345') number = phonebook.lookup("Bob") self.assertEqual("12345", number)
def test_missing_name( self ): # What happend if we have name which doesn;t exist in phonebook phonebook = PhoneBook() with self.assertRaises(KeyError): phonebook.lookup("missing")
def test_phonebook_gives_access_to_names_and_numbers(): phonebook = PhoneBook() phonebook.add("Alice", "67890") assert "Alice" in phonebook.names() assert "67890" in phonebook.numbers()
class PhoneBookTest(unittest.TestCase): # Test fixture def setUp(self): self.phonebook = PhoneBook() # Test fixture # Tear down resources that were initialized in setUp def tearDown(self): pass # Test case def test_lookup_by_name(self): # Test case name self.phonebook.add("Bob", "12345") # Arrange number = self.phonebook.lookup("Bob") # Act self.assertEqual("12345", number) # assert def test_missing_name(self): with self.assertRaises(KeyError): self.phonebook.lookup("missing") def test_is_consistent_with_empty_phonebook(self): self.assertTrue(self.phonebook.is_consistent()) # Not good test case design. We'll miss a lot of the tests if there is a failure early in the test. # Testing stops when an exception is encountered # Name of test case isn't specific @unittest.skip("Bad test case design.") def test_is_consistent(self): self.phonebook.add("Bob", "12345") self.assertTrue(self.phonebook.is_consistent()) self.phonebook.add("Anna", "012345") self.assertTrue(self.phonebook.is_consistent()) self.phonebook.add("Sue", "12345") # identitcal to Bob self.assertFalse(self.phonebook.is_consistent()) self.phonebook.add("Sue", "123") # prefix of Bob self.assertFalse(self.phonebook.is_consistent()) def test_is_consistent_with_different_entries(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Anna", "012345") self.assertTrue(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_entries(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Sue", "12345") self.assertFalse(self.phonebook.is_consistent()) def test_inconsistent_with_duplicate_prefix(self): self.phonebook.add("Bob", "12345") self.phonebook.add("Sue", "123") self.assertFalse(self.phonebook.is_consistent()) def test_phonebook_adds_names_and_numbers(self): self.phonebook.add("Sue", "123343") # Act self.assertIn( "Sue", self.phonebook.get_names()) # assert made on the same Act self.assertIn( "123343", self.phonebook.get_numbers()) # assert made on the same Act
def setUp(self) -> None: self.phonebook = PhoneBook()
class SMS900(): """ The main class to use """ def __init__(self, configuration_path): """ The init method for the main class. Should probably add an example or two here. """ self.configuration_path = configuration_path self.events = queue.Queue() self.config = None self.dbconn = None self.irc_thread = None self.pb = None def run(self): """ Starts the main loop""" self._load_configuration() self._init_database() self.pb = PhoneBook(self.dbconn) self.indexer = Indexer() logging.info("Starting IRCThread thread") self.irc_thread = IRCThread(self, self.config['server'], self.config['server_port'], self.config['nickname'], self.config['channel']) self.irc_thread.start() logging.info("Starting webserver") http_thread = HTTPThread(self, ('0.0.0.0', 8090)) http_thread.start() logging.info("Starting main loop") self._main_loop() def _load_configuration(self): with open(self.configuration_path, 'r') as file: self.config = json.load(file) # FIXME: Check that we got everything we'll be needing def _init_database(self): self.dbconn = sqlite3.connect('sms900.db', isolation_level=None) conn = self.dbconn.cursor() try: conn.execute( "create table phonebook (" " id integer primary key," " nickname text UNIQUE," " number text UNIQUE" ")" ) conn.execute( "create table phonebook_email (" " email text primary key collate nocase," " nickname text" ")" ) except sqlite3.Error as err: logging.info("Failed to create table(s): %s", err) def queue_event(self, event_type, data): """ queues event, stupid doc string i know """ event = {'event_type': event_type} event.update(data) self.events.put(event) def _main_loop(self): while True: event = self.events.get() self._handle_event(event) def _handle_event(self, event): try: logging.info('EVENT: %s', event) if event['event_type'] == 'SEND_SMS': sender_hm = event['hostmask'] number = self._get_num_from_nick_or_num(event['number']) nickname = self._get_nickname_from_hostmask(sender_hm) # FIXME: Check the sender msg = "<%s> %s" % (nickname, event['msg']) self._send_sms(number, msg) elif event['event_type'] == 'ADD_PB_ENTRY': nickname = event['nickname'] if event['number']: number = self._get_canonicalized_number(event['number']) self.pb.add_number(nickname, number) self._send_privmsg(self.config['channel'], 'Added %s with number %s' % (nickname, number)) elif event['email']: email = event['email'] self.pb.add_email(nickname, email) self._send_privmsg(self.config['channel'], 'Added email %s for %s' % (email, nickname)) elif event['event_type'] == 'DEL_PB_ENTRY': if event['nickname']: nickname = event['nickname'] oldnumber = self.pb.get_number(nickname) self.pb.del_entry(nickname) self._send_privmsg(self.config['channel'], 'Removed contact %s (number: %s)' % (nickname, oldnumber)) elif event['email']: email = event['email'] nickname = self.pb.get_nickname_from_email(email) self.pb.del_email(email) self._send_privmsg(self.config['channel'], 'Removed %s for contact %s' % (email, nickname)) elif event['event_type'] == 'LOOKUP_CARRIER': number = event['number'] number = self._get_canonicalized_number(number) self._lookup_carrier(number) elif event['event_type'] == 'REINDEX_ALL': self._reindex_all() elif event['event_type'] == 'SMS_RECEIVED': number = event['number'] sms_msg = event['msg'] try: sender = self.pb.get_nickname(number) except SMS900InvalidAddressbookEntry: sender = number msg = '<%s> %s' % (sender, sms_msg) self._send_privmsg(self.config['channel'], msg) elif event['event_type'] == 'GITHUB_WEBHOOK': self._handle_github_event(event['data']) elif event['event_type'] == 'MAILGUN_INCOMING': self._handle_incoming_mms(event['data']) except (SMS900InvalidNumberFormatException, SMS900InvalidAddressbookEntry) as err: self._send_privmsg(self.config['channel'], "Error: %s" % err) except Exception as err: self._send_privmsg(self.config['channel'], "Unknown error: %s" % err) traceback.print_exc() def _send_sms(self, number, message): logging.info('Sending sms ( %s -> %s )', message, number) try: client = TwilioRestClient(self.config['twilio_account_sid'], self.config['twilio_auth_token']) message_data = client.messages.create(to=number, from_=self.config['twilio_number'], body=message,) self._send_privmsg(self.config['channel'], "Sent %s sms to number %s" % (message_data.num_segments, number)) except twilio.TwilioRestException as err: self._send_privmsg(self.config['channel'], "Failed to send sms: %s" % err) def _lookup_carrier(self, number): logging.info('Looking up number %s', number) try: client = TwilioLookupsClient(self.config['twilio_account_sid'], self.config['twilio_auth_token']) number_data = client.phone_numbers.get(number, include_carrier_info=True) self._send_privmsg(self.config['channel'], '%s is %s, carrier: %s' % (number, number_data.carrier['type'], number_data.carrier['name'])) except twilio.TwilioRestException as err: self._send_privmsg(self.config['channel'], "Failed to lookup number: %s" % err) def _send_privmsg(self, target, msg): self.irc_thread.send_privmsg(target, msg) def _get_canonicalized_number(self, number): match = re.match(r'^\+[0-9]+$', number) if match: logging.info('number %s already canonicalized, returning as is', number) return number match = re.match(r'^0(7[0-9]{8})$', number) if match: new_number = '+46%s' % match.group(1) logging.info('number %s was canonicalized and returned as %s', number, new_number) return new_number raise SMS900InvalidNumberFormatException("%s is not a valid number", number) def _get_num_from_nick_or_num(self, number_or_name): try: number_or_name = self._get_canonicalized_number(number_or_name) except SMS900InvalidNumberFormatException: try: number_or_name = self.pb.get_number(number_or_name) except SMS900InvalidAddressbookEntry as err2: raise SMS900InvalidNumberFormatException( "%s is not a valid number or existing nickname: %s" % (number_or_name, err2)) return number_or_name def _get_nickname_from_hostmask(self, hostmask): nick = re.match(r'^([^\!]+)', hostmask) if not nick: # FIXME return hostmask else: return nick.group(1) def _handle_github_event(self, data): try: repository_name = data['repository']['full_name'] for commit in data['commits']: self._send_privmsg( self.config['channel'], "[%s] %s (%s)" % ( repository_name, commit['message'], commit['author']['username'] ) ) except KeyError as err: logging.exception("Failed to parse data from github webhook, reason: %s", err) def _handle_incoming_mms(self, data): rel_path = str(uuid.uuid4()) save_path = path.join( self.config['mms_save_path'], rel_path ) mkdir(save_path) [sender, files] = self._parse_mms_data(data, save_path) sender = self._map_mms_sender_to_nickname(sender) base_url = "%s/%s" % ( self.config['external_mms_url'], rel_path ) mms_summary, summary_contains_all = self._get_mms_summary(base_url, files) if mms_summary: self._send_privmsg( self.config['channel'], "[MMS] <%s> %s" % (sender, mms_summary) ) if not summary_contains_all: self._send_privmsg( self.config['channel'], "[MMS] <%s> Received %d file(s): %s" % (sender, len(files), base_url) ) self.indexer.generate_local_index(save_path) self.indexer.generate_global_index(self.config['mms_save_path']) def _reindex_all(self): self.indexer.reindex_all(self.config['mms_save_path']) def _map_mms_sender_to_nickname(self, sender): m = re.match('^([^<]*<)?([^<]+@[^>]+)>?', sender) if not m: return sender email = m.group(2) try: return self.pb.get_nickname_from_email(email) except SMS900InvalidAddressbookEntry: logging.exception("No nickname found for email %s", email) m = re.match('^([0-9]+)\@', email) if m: try: number = self._get_canonicalized_number("+" + m.group(1)) return self.pb.get_nickname(number) except SMS900InvalidAddressbookEntry: logging.exception("No number found for %s" , m.group(1)) except SMS900InvalidNumberFormatException: logging.exception("Weirdly formatted number: %s", m.group(1)) return email def _parse_mms_data(self, data, save_path): payload = data['payload'] files = [] i = 0 sender = None if data['type'] == 'form-data': for part in payload.parts: disposition = part.headers[b'Content-Disposition'].decode('utf-8', 'ignore') if 'name="from"' in disposition: sender = part.content.decode('utf-8', 'ignore') continue elif 'name="body-plain"' in disposition: contents = part.content.decode('utf-8', 'ignore') if not len(contents.strip()): continue filename = path.join(save_path, '%d-body.txt' % i) i += 1 with open(filename, 'w') as f: f.write(contents) files.append(filename) continue m = re.search('filename="([^"]+)"', disposition, re.IGNORECASE) if m: filename = path.join(save_path, '%d-%s' % (i, m.group(1))) i += 1 with open(filename, 'wb') as f: f.write(part.content) files.append(filename) continue if b'Content-Type' in part.headers: # Probably something we need to handle better, but # let's just dump it in a file for now. filename = path.join(save_path, '%d-unknown' % i) i += 1 with open(filename, 'wb') as f: f.write(part.content) files.append(filename) continue logging.info("Ignoring: %s" % disposition) elif data['type'] == 'urlencoded': if 'sender' in payload: for _sender in payload['sender']: sender = _sender break if 'body-plain' in payload: for body in payload['body-plain']: filename = path.join(save_path, '%d-body.txt' % i) i += 1 with open(filename, 'w') as f: f.write(body) files.append(filename) return [sender, files] def _get_mms_summary(self, base_url, files): try: text = None img_url = None # Find the first text and the first image file, if any for full_path in files: match = re.search(r'\.([^.]+)$', full_path) if match: extension = match.group(1).lower() if not img_url: if extension in ['jpg', 'jpeg', 'png']: filename = path.basename(full_path) img_url = "%s/%s" % (base_url, filename) if not text: if extension in ['txt']: with open(full_path, 'r', encoding='utf-8', errors='ignore') as file: text = file.read() # Only show one line text_lines = text.splitlines() if len(text_lines): text = text_lines[0] if len(text_lines) > 1: text += " [%d lines]" % len(text_lines) if text or img_url: parts = [p for p in [text, img_url] if p] message = ", ".join(parts) summary_contains_all = (len(parts) == len(files)) return message, summary_contains_all except Exception: traceback.print_exc() return None, False