def test_addressbook(): ab = utils.open_addressbook('mybook') print ab ab.sort(['fname', 'lname']) ab2 = AddressBook() ab2.import_contacts('Handsome') print ab2
class TestAddressBookBasic(TestCase): def setUp(self): self.book = AddressBook() def test_create_address_book(self): assert len(self.book.persons) == 0 assert len(self.book.groups) == 0 def test_add_person(self): person = self.book.create_person(first_name="Test", last_name="Man") assert len(self.book.persons) == 1 assert person.first_name == "Test" assert person.last_name == "Man" def test_add_person_with_attributes(self): emails = ["*****@*****.**", "*****@*****.**"] phones = ["123", "456"] addresses = ["Baker st", "Lenin st"] person = self.book.create_person(first_name="Test", last_name="Man", emails=emails, phones=phones, addresses=addresses) assert person.emails == emails assert person.phones == phones assert person.addresses == addresses def test_person_wrong_attribute_types(self): phones = [12345] with pytest.raises(AssertionError): self.book.create_person(first_name="Test", last_name="Man", phones=phones) def test_add_group(self): group = self.book.create_group(name="Friends") assert len(self.book.groups) == 1 assert group.name == "Friends" def test_group_members(self): group = self.book.create_group(name="Friends") person1 = self.book.create_person(first_name="Test", last_name="Man") person2 = self.book.create_person(first_name="Test", last_name="Dog") group.add_member(person1) group.add_member(person2) assert len(group.members) == 2 assert sorted(group.members, key=lambda p: p.last_name) == [person2, person1] def test_person_groups(self): group1 = self.book.create_group(name="Friends") group2 = self.book.create_group(name="Foes") person = self.book.create_person(first_name="Test", last_name="Man") group1.add_member(person) group2.add_member(person) assert len(person.groups) == 2 assert sorted(person.groups, key=lambda p: p.name) == [group2, group1]
def main(): '''Load settings, start status icon and get to work.''' from addressbook import AddressBook from status_icon import StatusIcon # try to load settings conf = Conf() # load data and fill AddressBook addressbook = AddressBook(conf) addressbook.reload() # show status icon status_icon = StatusIcon(addressbook, conf) # check every 60 seconds for new day # TODO: update until end of day according to current clock settings? # (might not the best idea if user changes current time) import gobject gobject.timeout_add(60000, status_icon.check_new_day) gtk.main()
class TestAddressBookSearchByEmail(TestCase): def setUp(self): self.book = AddressBook() def test_find_person_by_email(self): person = self.book.create_person(first_name="Test", last_name="Man", emails=["*****@*****.**"]) result = self.book.person_by_email("*****@*****.**") assert len(result) == 1 assert result[0] == person def test_find_person_by_email_prefix(self): person = self.book.create_person(first_name="Test", last_name="Man", emails=["*****@*****.**"]) result = self.book.person_by_email("test") assert len(result) == 1 assert result[0] == person def test_find_person_by_email_no_result(self): person = self.book.create_person(first_name="Test", last_name="Man", emails=["*****@*****.**"]) result = self.book.person_by_email("foo") assert len(result) == 0
def create_list_append(Addresses, name): address_dict = Addresses address_list = [] for address in address_dict['Addresses']: address_obj = PersonAddress(address['first_name'],address['last_name'],address['Address'],address['City'],address['State'],address['Zip_code'],address['Phone_number']) address_list.append(address_obj) print("\r") print(address_obj) print("\r") file_to_open = name.replace(".json","") file_to_open = AddressBook(file_to_open) while True: try: action = int(input("Enter 1 if you want to edit the addressbook(Enter any other digit to exit) : ")) except ValueError as err: print(err) break if(action == 1): file_to_open.edit_already_existing_addressbook(address_list) addressbook_operation(file_to_open) else: mainmenu_operation()
def get_name_from_address_book(phone_or_email): book = AddressBook() try: name = book.query_number(AddressBook.clean_number(phone_or_email)) if not name: # try getting rid of the 1 name = book.query_number(AddressBook.clean_number(phone_or_email)[1:]) except KeyError: name = book.query_email(phone_or_email.lower()) if not name: return phone_or_email return name
def new_addressbook(): Addressbook_name = str(input("Enter a name for your new addressbook : ")) Addressbook_name = AddressBook(Addressbook_name) addressbook_operation(Addressbook_name)
def __init__(self, *args, **kwargs): self.addressbook = AddressBook() self.current_book_filename = "" Cmd.__init__(self, *args, **kwargs)
class TestAddressBookApiMethods(unittest.TestCase): def setUp(self): self.address_book = AddressBook() def test_add_person(self): self.assertRaises(ValueError, self.address_book.add_person, {}) person_dict = { 'first_name': 'John', 'last_name': 'Deer', 'email_list': ['*****@*****.**'], 'phone_list': ['999-999-9999'], 'address_list': ['Anthony Benoit 490 E, Main Street Norwich, CT 06360, US'], 'group_list': ['general'], } result = self.address_book.add_person(person_dict) self.assertTrue(result) def test_add_group(self): self.assertRaises(ValueError, self.address_book.add_group, None) result = self.address_book.add_group('general') self.assertTrue(result) def test_find(self): address_book = self.address_book john_dict = { 'first_name': 'John', 'last_name': 'Deer', 'email_list': ['*****@*****.**'], 'phone_list': ['999-999-9999'], 'address_list': ['Anthony Benoit 490 E, Main Street Norwich, CT 06360, US'], 'group_list': ['general'], } address_book.add_person(john_dict) bob_dict = { 'first_name': 'Bob', 'last_name': 'Dilan', 'email_list': ['*****@*****.**', '*****@*****.**'], 'phone_list': ['999-888-9999'], 'address_list': ['Green 490 E, Main Street Norwich, CT 06360, US'], 'group_list': ['general', 'friends'], } address_book.add_person(bob_dict) result1 = address_book.find_persons(first_name='Bob') self.assertEqual(result1, [bob_dict]) result2 = address_book.find_persons(first_name='Bob', last_name='Dilan') self.assertEqual(result2, [bob_dict]) result3 = address_book.find_persons(email='*****@*****.**') self.assertEqual(result3, [bob_dict]) result4 = address_book.find_persons(email='john') self.assertEqual(result4, [john_dict]) result5 = address_book.find_persons(group='general') self.assertEqual(result5, [john_dict, bob_dict])
def main(): """ Main """ # fileConfig( # 'logging.conf', # defaults={'logfilename': ''}) log_format = ("[%(asctime)s] [%(levelname)s] " "[%(name)s] [%(funcName)s():%(lineno)s] " "[PID:%(process)d TID:%(thread)d] %(message)s") # logging.basicConfig( # level=logging.INFO, # format=log_format, # datefmt="%d/%m/%Y %H:%M:%S", # filename=LOG_FILENAME, # filemode='a') # # logger = logging.getLogger(__name__) # handler = logging.handlers.RotatingFileHandler( # LOG_FILENAME, # maxBytes=200, # backupCount=5) # # TODO: # IN COLORED LOGS: # ADD OPTION FOR ERRSTREAM/FILE # ADD OPTION FOR LOGROTATE # CHANGE LEVEL NAME COLOUR ACCORDING TO CURRENT LEVEL coloredlogs.install(level=logging.INFO, fmt=log_format, isatty=True, datefmt="%d/%m/%Y %H:%M:%S", stream=open(LOG_FILENAME, "w+")) # error_handler = logging.FileHandler(ERROR_FILENAME) # error_handler.setLevel(logging.ERROR) # Initializes a thread lock which can be used to serialize access # to underlying I/O functionality which may not be threadsafe. # handler.createLock() # error_handler.createLock() # Acquires the thread lock created with createLock(). # handler.acquire() # error_handler.acquire() # Add the log message handlers to the logger # logger.addHandler(handler) # logger.addHandler(error_handler) addr_book = AddressBook() person_args = { "lastname": "Mario", "name": "Luigi", "address": ["via alfa", "via beta"], "email": ["*****@*****.**", "*****@*****.**"], "phone": ["012345678", "8765431"], "groups": ["Alfa", "Gamma"] } person1 = Person(person_args) person_args = { "lastname": "Mario", "name": "Mario", "address": ["via alfa", "via beta"], "email": ["*****@*****.**", "*****@*****.**"], "phone": ["012345678", "8765431"], "groups": ["Alfa", "Gamma"] } person2 = Person(person_args) # Add a group to the address book. addr_book.add_group("Alfa") addr_book.add_group("Gamma") # Add a group to the address book. # Add a person to the address book. addr_book.add_person(person1) addr_book.add_person(person2) print("Persons:\n%s" % (addr_book.get_persons_str())) print("Groups:\n%s" % (addr_book.get_groups_str())) # Given a group we want to easily find its members for person in addr_book.get_group_members("Alfa"): print("Person: %s" % person.get_full_name()) # Given a person we want to easily find the groups the person belongs to. print("Person: " + str(addr_book.get_person_group(person1))) # Find person by name (can supply either first name, last name, or both). persons_found = addr_book.find_person("Mario", "Luigi") if persons_found is not None: for person in persons_found: print(person.get_fmt_str()) print("--------------------------") persons_found = addr_book.find_person("Mario") if persons_found is not None: for person in persons_found: print(person.get_fmt_str()) print("--------------------------") persons_found = addr_book.find_person(None, "Luigi") if persons_found is not None: for person in persons_found: print(person.get_fmt_str()) print("--------------------------") # Find person by email address (can supply either the exact string # or a prefix string, # ie. both "*****@*****.**" and "alex" should work). person_found = addr_book.find_person_by_email("*****@*****.**") if person_found is not None: print("Found:\n%s" % person_found.get_fmt_str()) person = addr_book.find_person_by_email("luigi") if person is not None: print("Person found")
class CommandLineInterface(Cmd): """ VALID COMMANDS: | add | edit | delete | display | display_mail | sort | open | save | save_as | import | export | options | help | quit VALID FLAGS (used with keywords edit, delete, display, sort): NOTE all flags must be followed by a single argument in quotes (eg: -a "123 Easy St"; -fn "Kevin"). -fn (first name) -ln (last name) -a (address) -a2 (address line 2) -c (city) -s (state) -z (ZIP Code) -p (phone number) -e (email) """ intro = WELCOME_MESSAGE prompt = "> " def __init__(self, *args, **kwargs): self.addressbook = AddressBook() self.current_book_filename = "" Cmd.__init__(self, *args, **kwargs) def do_add(self, line): """ Add a new contact to the Address Book. """ new_contact = Contact() print "* denotes a required field.\nPress ctrl-c to cancel." for field in CONTACT_FIELDS: valid = FIELD_VALIDATORS[field[0]] try: data = None if field[0] in REQUIRED_FIELDS: while getattr(new_contact, field[0], '')=='' or not valid(data)[0]: data = raw_input("{0}: ".format(field[1])) setattr(new_contact, field[0], data) if not valid(data)[0]: print valid(data)[1] else: while not valid(data)[0]: data = raw_input("{0}: ".format(field[1])) setattr(new_contact, field[0], data) if not valid(data)[0]: print valid(data)[1] except KeyboardInterrupt: print "\nCancelling New Contact\n" return self.addressbook.add(new_contact) print "Your entry was successfully added." def do_edit(self, line): """ Edit an existing contact in the Address Book. It actually deletes the "edited " contact and adds a new contact with the changed (or not) fields """ if line == "": print EDIT_AND_DELETE_NEED_ARGS return # Get list of tuples of (field, arg) fields_args = get_field_and_args_from_input(line) if not fields_args: return # Search address book for specified contact lists = [] # a list of lists returned by search, each list the result of a search on one field for field_arg in fields_args: lists.append(self.addressbook.search(field_arg[0], field_arg[1])) # Intersect all resulting lists from field queries # This is a list of tuples (index, contact) search_results = list(set(lists[0]).intersection(*lists[1:])) # if more than one such contact, display list of said contacts # if exacly one such contact, that is the one we want # if no such contact, inform user if len(search_results) > 1: print NARROW_SEARCH_MESSAGE for result in search_results: print "-------------------------" print result[1] elif len(search_results) == 1: print "\nPress ctrl-c to cancel." contact = search_results[0] #tuple (index, contact) # TODO, create temp contact to hold edits, replace old contact with new temp = Contact() for field in CONTACT_FIELDS: valid = FIELD_VALIDATORS[field[0]] try: new_data = None old_data = getattr(contact[1], field[0], '') while not valid(new_data)[0]: user_input = raw_input("{0}: {1}".format(field[1], old_data) + chr(8)*len(old_data)) # NOTE: 8 is the ASCII value of backspace if not user_input: user_input = old_data # My hacky way of having the newly added chars replace those of the old_data # for cases where user replaces some, but not all, of the old data's chars user_input = list(user_input) new_data = list(old_data) for i in range(len(user_input)): try: new_data[i] = user_input[i] except IndexError: new_data.extend(user_input[i:]) break new_data = ''.join(new_data) if not valid(new_data)[0]: print valid(new_data)[1] # Update the temp Contact Info setattr(temp, field[0], new_data) except KeyboardInterrupt: print "\nCancelling Contact Edit, reverting to original.\n" return confirm = raw_input("Are you sure you want to make these changes to this entry? (y/n): ") if confirm in ('yes', 'y'): self.addressbook.delete(contact[0]) self.addressbook.add(temp) print "Edit complete" return else: print "\nCancelling Contact Edit, reverting to original.\n" else: print "There were no contacts that met your specification. Please generalize your request." def do_delete(self, line): """ Deletes a contact from the Address Book. Or if more than one contact meets the user's specification, presents a list of said contacts. If no contacts meet the user's specification, then does nothing. **User can only delete one contact at a time.** """ if line == "": print EDIT_AND_DELETE_NEED_ARGS return # Get list of tuples of (field, arg) fields_args = get_field_and_args_from_input(line) if not fields_args: return # Search address book for specified contact lists = [] # a list of lists returned by search, each list the result of a search on one field for field_arg in fields_args: lists.append(self.addressbook.search(field_arg[0], field_arg[1])) # Intersect all resulting lists from field queries # This is a list of tuples (index, contact) search_results = list(set(lists[0]).intersection(*lists[1:])) # if more than one such contact, display list of said contacts # if exacly one such contact, that is the one we want # if no such contact, inform user if len(search_results) > 1: print NARROW_SEARCH_MESSAGE for result in search_results: print "-------------------------" print result[1] elif len(search_results) == 1: contact = search_results[0] #tuple (index, contact) print contact[1] yes_delete = raw_input("Is this the entry you want to delete? (y/n): ") if yes_delete in ('yes', 'y'): yes_delete = raw_input("*** Are you sure? (y/n): ") if yes_delete in ('yes', 'y'): try: self.addressbook.delete(contact[0]) print "Contact deleted." except: print "*** Encountered an error while trying to delete, please make sure your input is correct." return print "No deletions." else: print "There were no contacts that met your specification. Please generalize your request." def do_display(self, line): """ If no flags are given, displays all contacts in the Address Book. If flags are present, then displays only contacts that meet all of the specifications given by flags. """ if self.addressbook.total == 0: print "The addressbook is empty." return # if no flags, arguments: # Print entire address book if line == '': print "\n{0}".format(self.addressbook) # else find subset of addressbook else: # Get list of tuples (field, arg) fields_args = get_field_and_args_from_input(line) if not fields_args: return # Search address book for specified contacts lists = [] # a list of lists returned by search, each list the result of a search on one field for field_arg in fields_args: lists.append(self.addressbook.search(field_arg[0], field_arg[1])) # Intersect all resulting lists from field queries # This is a list of tuples (index, contact) search_results = list(set(lists[0]).intersection(*lists[1:])) if len(search_results) > 0: for result in search_results: print "\n{0}".format(result[1]) print "\n" else: print "There were no contacts that met your specification. Please generalize/check your request." def do_display_mail(self, line): """ Same as 'display', but displays contact in mailing label format, as opposed to full contact. If no flags are given, displays all contacts in the Address Book. If flags are present, then displays only contacts that meet all of the specifications given by flags. """ if self.addressbook.total == 0: print "The addressbook is empty." return # if no flags, arguments: # Print entire address book if line == '': print "\n{0}".format(self.addressbook.print_all_mailing()) # else find subset of addressbook else: # Get list of tuples (field, arg) fields_args = get_field_and_args_from_input(line) if not fields_args: return # Search address book for specified contacts lists = [] # a list of lists returned by search, each list the result of a search on one field for field_arg in fields_args: lists.append(self.addressbook.search(field_arg[0], field_arg[1])) # Intersect all resulting lists from field queries # This is a list of tuples (index, contact) search_results = list(set(lists[0]).intersection(*lists[1:])) if len(search_results) > 0: for result in search_results: print "\n{0}".format(result[1].print_mailing()) print "\n" else: print "There were no contacts that met your specification. Please generalize/check your request." def do_sort(self, line): """ Sort the address book by the given attributes (flags). The first attribute is used and ties are broken using the following attributes in the list. Defaults to sorting by last name. Ties are broken by last name. """ # convert flags to list of attributes. flags = line.split() attrs = [] for flag in flags: if flag not in VALID_FLAGS: print "'{0}' is not a valid flag. Enter \"options\" to view available flag options.\n".format(flag) return for field in CONTACT_FIELDS: if flag == field[2]: attrs.append(field[0]) # Ties are broken by name attrs.extend(['lname', 'fname']) # Apply sort function try: self.addressbook.sort(attributes=attrs) print "Address book is now sorted.\n" except: print "*** Encountered an error while trying to sort, please make sure your input is correct." def do_open(self, line): """ Open an address book from file. """ confirm = raw_input("Are you sure you want to open a new address book? Changes since your last save will be lost. ('open' / 'cancel'): ") if confirm != 'open': return file_name = raw_input("Please give the name/path of the file containing the address book you want to open (or leave blank to cancel): ") if file_name: try: self.addressbook = utils.open_addressbook(file_name) self.current_book_filename = file_name print "Now using address book from file '{0}'".format(file_name) except: print "*** Encountered an error while trying to open that file. Please make sure you provided a good file name." def do_save(self, line): """ Save an address book as an object to a file, to the file name inferred from current address book. """ if self.current_book_filename == '': print "This seems to be a new address book." return self.do_save_as(line) file_name = self.current_book_filename confirm = raw_input("Are you sure you want to overwrite the existing file '{0}'? (y/n): ".format(file_name)) if confirm in ['yes', 'y']: try: utils.save_addressbook(self.addressbook, file_name) print "Successfully saved your address book to the file '{0}'".format(file_name) except: print "in save" print "*** Encountered an error while trying to save. Sorry..." else: print "Cancelling save." return def do_save_as(self, line): """ Save an address book as an object to a file. """ file_name = raw_input("What do you want to call your file? (or leave blank to cancel): ") # "WARNING, I can't tell if you're overwritting a file that already exists, so choose the name carefully: ") if not file_name: return try: with open(file_name): overwrite = raw_input("There already seems to be a file with that name. Do you want to overwrite that file? (y/n): ") except IOError: overwrite = 'yes' if overwrite in ['yes', 'y']: try: utils.save_addressbook(self.addressbook, file_name) self.current_book_filename = file_name print "Successfully saved your address book to the file '{0}'".format(file_name) except: print "in save as" print "*** Encountered an error while trying to save. Sorry..." def do_import(self, line): """ Import a contacts list from a tsv file and add the contacts to the current book. The format of the file is as follows: | Last<tab>Delivery<tab>Second<tab>Recipient<tab>Phone<NL> | followed by a list of contacts with the same format. """ file_name = raw_input("Please give the name/path of the file from which you want to import contacts (or leave blank to cancel): ") if file_name: confirm = raw_input("Are you sure you want to try to merge the contacts from this file into your current book? (y/n): ") if confirm not in ['yes', 'y']: print "Cancelling import."; return try: self.addressbook.import_contacts(file_name) print "Successfully imported contacts from '{0}'".format(file_name) except: print "*** Encountered an error while trying to import from that file. " + \ "Please make sure you provided a good file name and that the file matches the format described in 'help import'." else: print "Cancelling import." def do_export(self, line): """ Export the contacts of the current AddressBook to a tsv file. The format of the file is as follows: | Last<tab>Delivery<tab>Second<tab>Recipient<tab>Phone<NL> | followed by a list of contacts with the same format. """ file_name = raw_input("Please give the name of the file to which you want to export contacts (or leave blank to cancel): ") if file_name: try: self.addressbook.export_contacts(file_name) print "Successfully exported your address book to the file '{0}'".format(file_name) except: print "*** Encountered an error while trying to export. Sorry..." else: print "Cancelling export." def do_options(self, line): """ Displays the available keyword commands and flags used by the applet. """ print OPTIONS_MESSAGE def do_quit(self, line): """ quit the applet """ confirm = raw_input("Are you sure you want to quit? Changes since your last save will be lost. ('quit' to quit; anything else will cancel): ") if confirm != 'quit': return else: sys.exit() def default(self, line): """ Method called on an input line when the command prefix is not recognized. """ if line != "": print "*** Invalid command. Type \"options\" to view available command options." def emptyline(self): """ Method called when an empty line is entered in response to the prompt. If this method is not overridden, it repeats the last nonempty command entered. """ pass
def __init__(self, parent): """Initiates the window, with the appropriate grid layout, buttons, Canvas and scroll bar. All class-wide variables defined here.""" Frame.__init__(self, parent, background="#ffffff") self.parent = parent #Temporary Variables - remember to reset! self.searchState = None #Currently displaying search results? self.tempvars = [] #Temporary values for add and editing fields self.tempID = -1 #TemporaryID number for contact self.tempFile = None #Temporary File for import/export self.mL = 0 #Mailing label flag # Some formatting for the grid layout self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=2) self.grid_columnconfigure(1, weight=0) self.grid_columnconfigure(2, weight=0) self.grid_columnconfigure(3, weight=0) self.grid_columnconfigure(4, weight=7) #Logo Column logo = PhotoImage(file="logo.gif") imgCan = Label(self, image=logo) imgCan.image = logo imgCan.grid(sticky = N+W, row = 0, column = 0, padx=0) # Add Button img1 = PhotoImage(file="add.gif") addBtn = Button(self, image= img1, command = self.clickAdd) addBtn.image = img1 addBtn.grid(sticky = N+W, row = 0, column = 1, ipadx=5, padx=3, ipady=5, pady=8) # Edit Button img2 = PhotoImage(file="edit.gif") editBtn = Button(self, image=img2, command = self.clickEdit) editBtn.image = img2 editBtn.grid(sticky = N+W, row = 0, column = 2, ipadx=5, padx=3, ipady=5, pady=8) #Delete Button img3 = PhotoImage(file="delete.gif") delBtn = Button(self, image=img3, command = self.clickDelete) delBtn.image = img3 delBtn.grid(sticky = N+W, row = 0, column = 3, ipadx=5, padx=3, ipady=5, pady=8) #Search Box Input self.searchBox = Entry(self, relief=SUNKEN, border=2, width=20) self.searchBox.grid(sticky = N+E, row = 0, column = 4, padx=2, pady=20) #Search Button searchBtn = Button(self, text="Search", command = self.clickSearch) searchBtn.grid(sticky = N+E, row = 0, column = 5, padx=0, pady=20) #Return to view the full book Button search2Btn = Button(self, text="View Full Book", command = self.update) search2Btn.grid(sticky = N+E, row = 0, column = 6, padx=0, pady=20) #Main addressbook! self.addressbook = AddressBook() #Used for initial testing """for i in range (0, 5): new_contact = Contact() setattr(new_contact, 'lname', "Yablok" + str(i)) self.addressbook.add(new_contact)""" #Canvas and scrollbar configuration self.canvas=Canvas(self, relief=SUNKEN, border=2, bg='#c6c6c6') self.vbar=Scrollbar(self, command=self.canvas.yview) self.vbar.config(command=self.canvas.yview) self.vbar.grid(sticky=NS, columnspan=8, row = 1, column = 8) self.canvas.config(width=980,height=700) self.canvas.config(yscrollcommand=self.vbar.set) self.canvas.grid(sticky=N+E+S+W, columnspan=7, row = 1, column = 0) self.fill_book(self.addressbook.contacts) self.initUI()
def setUp(self): self.address_book = AddressBook()
class GUI(Frame): """Main class which represents our parent window for the GUI.""" def __init__(self, parent): """Initiates the window, with the appropriate grid layout, buttons, Canvas and scroll bar. All class-wide variables defined here.""" Frame.__init__(self, parent, background="#ffffff") self.parent = parent #Temporary Variables - remember to reset! self.searchState = None #Currently displaying search results? self.tempvars = [] #Temporary values for add and editing fields self.tempID = -1 #TemporaryID number for contact self.tempFile = None #Temporary File for import/export self.mL = 0 #Mailing label flag # Some formatting for the grid layout self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=2) self.grid_columnconfigure(1, weight=0) self.grid_columnconfigure(2, weight=0) self.grid_columnconfigure(3, weight=0) self.grid_columnconfigure(4, weight=7) #Logo Column logo = PhotoImage(file="logo.gif") imgCan = Label(self, image=logo) imgCan.image = logo imgCan.grid(sticky = N+W, row = 0, column = 0, padx=0) # Add Button img1 = PhotoImage(file="add.gif") addBtn = Button(self, image= img1, command = self.clickAdd) addBtn.image = img1 addBtn.grid(sticky = N+W, row = 0, column = 1, ipadx=5, padx=3, ipady=5, pady=8) # Edit Button img2 = PhotoImage(file="edit.gif") editBtn = Button(self, image=img2, command = self.clickEdit) editBtn.image = img2 editBtn.grid(sticky = N+W, row = 0, column = 2, ipadx=5, padx=3, ipady=5, pady=8) #Delete Button img3 = PhotoImage(file="delete.gif") delBtn = Button(self, image=img3, command = self.clickDelete) delBtn.image = img3 delBtn.grid(sticky = N+W, row = 0, column = 3, ipadx=5, padx=3, ipady=5, pady=8) #Search Box Input self.searchBox = Entry(self, relief=SUNKEN, border=2, width=20) self.searchBox.grid(sticky = N+E, row = 0, column = 4, padx=2, pady=20) #Search Button searchBtn = Button(self, text="Search", command = self.clickSearch) searchBtn.grid(sticky = N+E, row = 0, column = 5, padx=0, pady=20) #Return to view the full book Button search2Btn = Button(self, text="View Full Book", command = self.update) search2Btn.grid(sticky = N+E, row = 0, column = 6, padx=0, pady=20) #Main addressbook! self.addressbook = AddressBook() #Used for initial testing """for i in range (0, 5): new_contact = Contact() setattr(new_contact, 'lname', "Yablok" + str(i)) self.addressbook.add(new_contact)""" #Canvas and scrollbar configuration self.canvas=Canvas(self, relief=SUNKEN, border=2, bg='#c6c6c6') self.vbar=Scrollbar(self, command=self.canvas.yview) self.vbar.config(command=self.canvas.yview) self.vbar.grid(sticky=NS, columnspan=8, row = 1, column = 8) self.canvas.config(width=980,height=700) self.canvas.config(yscrollcommand=self.vbar.set) self.canvas.grid(sticky=N+E+S+W, columnspan=7, row = 1, column = 0) self.fill_book(self.addressbook.contacts) self.initUI() def initUI(self): """Setup the window and display it.""" self.parent.title("Blue Book") self.pack(fill=BOTH, expand=1) def get_tagged_box(self, tag): """Search through Canvas objects by tags, return the rectangle with the given tag.""" id, = self.canvas.find_withtag(tag) tags = self.canvas.gettags(tag) if 'text' in tags: #if the current item is text id = self.canvas.find_withtag(self.canvas.itemcget(id,'text')) return id; def boxClicked(self, event): """Give the rectangle the 'select' tag when clicked, and the highlight properties (outline and white background.""" id = self.get_tagged_box('current') tags = self.canvas.gettags(id) for t in tags: if (t == "select"): #The box is already selected, clicking should deselect self.canvas.dtag(id, 'select') self.canvas.itemconfigure(id, width=0, fill=BOX_COLOR) return for en in self.canvas.find_withtag("select"): if (en != id): #Cycle through and make sure only one is selected at a time. self.canvas.dtag(en, 'select') self.canvas.itemconfigure(en, width=0, fill=BOX_COLOR) #Select the current box. self.canvas.addtag('select', 'withtag', id) self.canvas.itemconfigure(id, width=2, outline="#58a4cd", fill=BOX_HIGHLIGHT) def hover(self, event): """Binding for when the user hovers over a rectangle. Changes the cursor.""" id = self.get_tagged_box('current') self.config(cursor="pointinghand") def no_hover(self,event): """Binding for when the user stops hovering over a rectangle. Changes the cursor.""" id = self.get_tagged_box('current') self.config(cursor="") def fill_book(self, subset): """Fill the Canvas with the info from adresses from the subset provided. for each entry add a rectangle to the canvas, and the associated address text within the rectangle. Bind these boxes to the boxClicked() function.""" SCROLLAREA_HEIGHT = (BOX_HEIGHT + ROW_PAD)*int(math.ceil(len(subset)/3.0)) + ROW_PAD self.canvas.config(scrollregion=(0,0,SCROLLAREA_WIDTH, SCROLLAREA_HEIGHT)) if len(subset) == 0 and self.searchState is None: self.canvas.create_text(980/2, 700/2, anchor='c', tags='text', text="The addressbook is currently empty.", ) elif len(subset) == 0: self.canvas.create_text(980/2, 700/2, anchor='c', tags='text', text="No existing entries match your search.", ) else: idn = 0 #ID Number r = 0 #row count y = COLUMN_PAD for c in range(0, len(subset)): if (idn%3 == 0 and idn != 0): #If in a new row #Update row count, x, and y. r += 1 y = COLUMN_PAD+r*(BOX_HEIGHT+COLUMN_PAD) x = ROW_PAD else : x = ROW_PAD+((c%3)*(BOX_WIDTH+ROW_PAD)) y = COLUMN_PAD+r*(BOX_HEIGHT+COLUMN_PAD) #If the index number exists in the addressbook if (idn < self.addressbook.total): #Create rectangle id = self.canvas.create_rectangle(x, y, x+BOX_WIDTH, y+BOX_HEIGHT, fill=BOX_COLOR, width=0, tags=('box', idn)) con = "" #Generate Text for field in CONTACT_FIELDS: #Check for subset if (self.searchState is None): #default entry = self.addressbook.contacts[idn] else: #Data has been searched, get from tuple entry = subset[idn][1] if (getattr(entry, field[0], '') != ""): con += field[1] + ": " + str(getattr(entry, field[0], '') + "\n") #Check for Mailing Label Display if (self.mL): self.canvas.create_text(x+20, y+20, anchor='nw', text=entry.print_mailing(), tags='text', width=TEXT_WIDTH) else: self.canvas.create_text(x+20, y+20, anchor='nw', text=con, tags='text', width=TEXT_WIDTH) idn+= 1 #Increment ID Number self.canvas.tag_bind('all', '<1>', self.boxClicked) #Check when box is clicked self.canvas.tag_bind('all', '<Any-Enter>', self.hover) #Check when box is hovered over self.canvas.tag_bind('all', '<Any-Leave>', self.no_hover) #Check when box is not hovered over def update(self): """Update the canvas once a change has been made to the addressbook, to display changes in the addressbook we delete all entries and and re-fill the canvas via the fill_book() function.""" self.searchState = None #reset searchState self.searchBox.delete(0, 10000) self.canvas.delete("all") self.fill_book(self.addressbook.contacts) def updateSearchState(self): """Special update function which uses a subset of the full address book - the self.searchState which is the current search results.""" self.canvas.delete("all") self.fill_book(self.searchState) def sort(self, att): """Sort the addresses by last name. This search function only works on the full addressbook, not a subset, sort before searching.""" #If we are not viewing search results, sort entire book if (self.addressbook.total == 0): return attrs = [] attrs.append(att) attrs.extend(['lname', 'fname']) self.addressbook.sort(attributes=attrs) self.update() def clickSearch(self): """General search for all of the fields and all of the contacts in the address book. It will look for exact matches in fields. For example search will accurately find '123 Easy St' but not just 'Easy St'. (Needs full search field.""" results = [] val = self.searchBox.get() if val == "": return for field in (CONTACT_FIELDS): results += self.addressbook.search(field[0], val) self.searchState = results self.updateSearchState() def clickAdd(self): """Add an entry to the address book. Creates a new top level window with inputs, and refers to the addEntry function to add the new contact object.""" self.tempvars = [] self.searchState = None toplevel = Toplevel() toplevel.title("Add an Entry") count = 1 tex = Label(toplevel, text="Please enter your new contact information in the inputs below.") tex.grid(row=0, column=0, columnspan=4, sticky=W+E+N+S, padx=10, pady=10) for label in (CONTACT_FIELDS): va = StringVar() l = Label(toplevel, text=label[1]) l.grid(row=count,column=0, columnspan=1, sticky=W, padx=10, pady=3) e = Entry(toplevel, relief=SUNKEN, border=2, width=40, textvariable=va) e.grid(row=count,column=1, columnspan=3, sticky=W, padx=10, pady=3) self.tempvars.append(e) count += 1 ok = Button(toplevel, text="Submit", command= lambda: self.addEntry(toplevel)) can = Button(toplevel, text="Cancel", command=toplevel.destroy) can.grid(row=count,column=2, sticky=W, padx=0, pady=10) ok.grid(row=count,column=1, sticky=W, padx=0, pady=10) def addEntry(self, top): """Get all of the fields entered by the user in the toplevel window Add the new contact object to the address book and update the canvas.""" new_contact = Contact() count = 0 go = 1 for field in CONTACT_FIELDS: valid = FIELD_VALIDATORS[field[0]] if field[0] in REQUIRED_FIELDS: data = self.tempvars[count].get() if data == "": tkMessageBox.showinfo("Required Field Error", field[1] + " is a required field, please enter this info into the input box.") go = 0 break if(go): setattr(new_contact, field[0], data) else: data = self.tempvars[count].get() if data != "" and not valid(data)[0]: tkMessageBox.showinfo("Input Error", valid(data)[1]) go = 0 break if(go): setattr(new_contact, field[0], data) count += 1 if(go): self.addressbook.add(new_contact) self.update() top.destroy() def clickEdit(self): """Edit an existing entry in the address book. Creates a new top level window with inputs which automatically gets the existing fields from the contact selected by the user. It refers to the editEntry() function to make the edits in to the actual contact in the addressbook.""" self.tempvars = [] #Temp values to get from the entry boxes self.tempID = -1 #Temp ID of the Contact being edited #ERROR: no contact selected by the user if (len(self.canvas.find_withtag('select')) == 0 or self.addressbook.total == 0): tkMessageBox.showinfo("Error: No Selection", "Please select an address to edit by clicking on its surrounding box. Selected addresses will have a blue outline.") return toplevel = Toplevel() toplevel.title("Edit an Entry") #Get the global ID of the contact based on #based on what is selected. id = self.get_tagged_box('select') tags=self.canvas.gettags(id) for t in tags: if t not in ('current','box', 'select'): box_name=t try: i = int(box_name) except ValueError: tkMessageBox.showinfo("Input error", "The ID of the contact is invalid, please delete and re-add this contact.") return self.tempID = i; #global ID #Layout configuration for the new toplevel window count = 1 tex = Label(toplevel, text="Please edit the contact information in the inputs below.") tex.grid(row=0, column=0, columnspan=4, sticky=W+E+N+S, padx=10, pady=10) for label in (CONTACT_FIELDS): #Get all of the fields va = StringVar() l = Label(toplevel, text=label[1]) l.grid(row=count,column=0, columnspan=1, sticky=W, padx=10, pady=3) #Special case for if searchState is activated. if (self.searchState is None): s = str(getattr(self.addressbook.contacts[i], label[0], '')) else: s = str(getattr(self.searchState[i][1], label[0], '')) va.set(s) e = Entry(toplevel, relief=SUNKEN, border=2, width=40, text=s, textvariable=va) e.grid(row=count,column=1, columnspan=3, sticky=W, padx=10, pady=3) self.tempvars.append(e) # add the values so we can .get() them later count += 1 ok = Button(toplevel, text="Submit", command=lambda: self.editEntry(toplevel)) can = Button(toplevel, text="Cancel", command=toplevel.destroy) can.grid(row=count,column=2, sticky=W, padx=0, pady=10) ok.grid(row=count,column=1, sticky=W, padx=0, pady=10) def editEntry(self, top): """Edit the entry id in self.tempID. Update all of the fields, and update the Canvas to reflect these changes.""" if (self.tempID >= 0): #special case for searchState subset if (self.searchState is None): en = self.addressbook.contacts[self.tempID] else: en = self.searchState[self.tempID][1] count = 0 go = 1 for field in CONTACT_FIELDS: valid = FIELD_VALIDATORS[field[0]] if field[0] in REQUIRED_FIELDS: data = self.tempvars[count].get() if data == "": tkMessageBox.showinfo("Required Field Error", field[1] + " is a required field, please enter this info into the input box.") go = 0 break if(go): setattr(en, field[0], data) else: data = self.tempvars[count].get() if data != "" and not valid(data)[0]: tkMessageBox.showinfo("Input Error", valid(data)[1]) go = 0 break if(go): setattr(en, field[0], data) count += 1 if (go): if (self.searchState is None): self.update() else: self.updateSearchState() top.destroy() else: tkMessageBox.showinfo("Input error", "The ID of the contact is invalid, please delete and re-add this contact") return def clickDelete(self): """Delete a contact from the address book, prompt a confirmation box which reviews the contact information, and proceeds to delete the entry from the addressbook, and update the Canvas""" self.tempvars = [] #Temp values to get from the entry boxes self.tempID = -1 #Temp ID of the Contact being edited #ERROR: no contact selected by the user if (len(self.canvas.find_withtag('select')) == 0 or self.addressbook.total == 0): tkMessageBox.showinfo("Error: No Selection", "Please select an address to delete by clicking on its surrounding box. Selected addresses will have a blue outline.") return #Get the global id of the selected contacts #through the tags options id = self.get_tagged_box('select') tags = self.canvas.gettags(id) for t in tags: if t not in ('current','box', 'select'): box_name=t try: i = int(box_name) except ValueError: tkMessageBox.showinfo("Input error", "Something was invalid") return self.tempID = i; #Gloabl ID con = "" for field in CONTACT_FIELDS: #Special case for searchState subset if (self.searchState is None): con += field[1] + ": " + getattr(self.addressbook.contacts[i], field[0], '') + "\n" else: con += field[1] + ": " + str(getattr(self.searchState[i][1], field[0], '')) + "\n" #Confirmation box if tkMessageBox.askyesno("Delete an Entry", "Are you sure you want to delete this entry?\n" + con): if (self.searchState is None): self.addressbook.delete(self.tempID) else: self.addressbook.delete(self.searchState[self.tempID][0]) self.searchState.pop(self.tempID) #Update the Canvas if (self.searchState is None): self.update() else: self.updateSearchState() def mailLabels(self): """View the contact information in a mailing label format. Update the canvas to display new format""" self.mL = 1 if (self.searchState is None): self.update() else: self.updateSearchState() def noMailLabels(self): """View the contact information in the regular format. Update the canvas to display new format""" self.mL = 0 if (self.searchState is None): self.update() else: self.updateSearchState() def new(self): """Create a new blank address book, ask to save before creating new book. Update the Canvas with the new book.""" self.tempFile = None if tkMessageBox.askyesno("New Address Book", "Do you want to save your current file first?"): self.save() self.addressbook.contacts = [] self.addressbook.total = 0 self.update() def open(self): """Open a new address book and prompt the user to save changes.""" if self.addressbook.contacts != []: if tkMessageBox.askyesno("Open Address Book", "Do you want to save your current file first?"): self.save() dlg = tkFileDialog.Open(self) fl = dlg.show() if fl != '': self.addressbook = utils.open_addressbook(fl) self.tempFile = fl self.update() def close(self): """Close an address book and prompt the user to save changes. If no address book is open, quit the program""" if self.addressbook.contacts == []: root.quit() return if tkMessageBox.askyesno("Close Address Book", "Do you want to save your current file first?"): self.save() self.tempFile = None self.addressbook.contacts = [] self.addressbook.total = 0 self.update() def imp(self): """Import a new address from a .tsv file. Prompt the user to select a file, update the Canvas with the imported addressbook""" ftypes = [('TSV files', '*.tsv'), ('All files', '*')] dlg = tkFileDialog.Open(self, filetypes = ftypes) fl = dlg.show() if fl != '': self.addressbook.import_contacts(fl) if tkMessageBox.askyesno("Import Addresses", "Do you want to merge addresses with the same first and last name?"): self.addressbook.merge_addressbook() self.update() def export(self): """Export the current address book to a .tsv file. Prompt the user to select a file, update the Canvas with the imported addressbook""" self.tempFile = None f = tkFileDialog.asksaveasfile(mode='w', defaultextension=".tsv") if f is None: return self.tempFile = f.name self.addressbook.export_contacts(f.name) def save(self): """Save an address book to the disk.""" if (self.tempFile is None): f = tkFileDialog.asksaveasfile() if f != None and self.addressbook != None: utils.save_addressbook(self.addressbook, f.name) self.tempFile = f.name else: utils.save_addressbook(self.addressbook, self.tempFile) def save_as(self): """Save an address book to the disk prompting for a file name.""" f = tkFileDialog.asksaveasfile() if f != None and self.addressbook != None: utils.save_addressbook(self.addressbook, f.name) def quit(self): """Quit the program but make sure to save changes first.""" self.close() root.quit()
# -*- coding: utf-8 -*- # Copyright (C) 2013 Sylvain Boily <*****@*****.**> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from flask import Blueprint from addressbook import AddressBook bp_addressbook = Blueprint('addressbook', __name__, template_folder='templates/addressbook', static_folder='static', static_url_path='/%s' % __name__) addressbook = AddressBook()
def setUp(self): self.book = AddressBook()
class TestAddressBookSearchByName(TestCase): def setUp(self): self.book = AddressBook() self.book.create_person(first_name="Test", last_name="Dog") self.book.create_person(first_name="Mike", last_name="Dog") def test_find_person_by_first_name(self): person = self.book.create_person(first_name="Test", last_name="Man") result = self.book.person_by_name(first_name="Test") assert len(result) == 2 assert person in result def test_find_person_by_last_name(self): person = self.book.create_person(first_name="Test", last_name="Man") result = self.book.person_by_name(last_name="Man") assert len(result) == 1 assert person in result def test_find_person_by_both(self): person = self.book.create_person(first_name="Test", last_name="Man") result = self.book.person_by_name(first_name="Test", last_name="Man") assert len(result) == 1 assert person in result def test_find_person_no_result(self): self.book.create_person(first_name="Test", last_name="Man") result = self.book.person_by_name(first_name="George", last_name="Man") assert len(result) == 0 def test_find_person_wrong_parameters(self): with pytest.raises(AssertionError): self.book.person_by_name(first_name=None, last_name=None)
def setUp(self): self.book = AddressBook() self.book.create_person(first_name="Test", last_name="Dog") self.book.create_person(first_name="Mike", last_name="Dog")