Пример #1
0
    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)
Пример #2
0
 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)
Пример #3
0
 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()
Пример #4
0
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
Пример #5
0
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, "...")
Пример #6
0
    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()
Пример #7
0
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
Пример #9
0
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
Пример #10
0
    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()
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(";")
Пример #12
0
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()
Пример #13
0
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())
Пример #14
0
 def setUp(self) -> None:
     """
     The setUp method is called each time a test case is executed.
     :return: PhoneBook Class.
     """
     self.phonebook = PhoneBook()
Пример #15
0
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())
Пример #16
0
 def setUp(self):
     self.phonebook = PhoneBook()
Пример #17
0
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())
Пример #18
0
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())
Пример #19
0
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())
Пример #20
0
def test_add_and_lookup_entry():
    phonebook = PhoneBook()
    phonebook.add("Bob", "12345")
    assert "12345" == phonebook.lookup("Bob")
Пример #21
0
    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
Пример #22
0
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
Пример #23
0
 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())
Пример #25
0
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())
Пример #26
0
 def setUp(self) -> None:
     # construct a new phonebook instance before each test case executes
     self.phonebook = PhoneBook()
Пример #27
0
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")
Пример #30
0
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()
Пример #31
0
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
Пример #32
0
 def setUp(self) -> None:
     self.phonebook = PhoneBook()
Пример #33
0
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