From 2e6062f41004f9808f8010b9486c0a8e9f0be601 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Wed, 25 Nov 2015 15:08:50 +0200 Subject: [PATCH 01/14] Class Contacts added --- console_controller.py | 9 ++++-- console_wrappers.py | 18 ++++++------ pickle_model.py | 67 +++++++++++++++++++++---------------------- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/console_controller.py b/console_controller.py index 562ba39..7296d05 100644 --- a/console_controller.py +++ b/console_controller.py @@ -1,6 +1,9 @@ +import functools + from console_wrappers import ask_create_contacts, ask_find_contact, ask_delete_contact, ask_update_contact -from pickle_model import save_contacts +from pickle_model import Contacts +contacts = Contacts() controller = { 'c': ask_create_contacts, @@ -9,6 +12,8 @@ 'u': ask_update_contact, } +controller = {key: functools.partial(value, contacts) for key, value in controller.items()} + def default(): print "Invalid action" @@ -21,4 +26,4 @@ def default(): break controller.get(action.lower(), default)() finally: - save_contacts() \ No newline at end of file + contacts.save_contacts() \ No newline at end of file diff --git a/console_wrappers.py b/console_wrappers.py index 3b681c9..d582b6c 100644 --- a/console_wrappers.py +++ b/console_wrappers.py @@ -1,38 +1,36 @@ -from pickle_model import create_contact, find_contact, delete_contact, update_contact - def ask_name(): return raw_input('name?') def ask_phone(): return raw_input('phone?') -def ask_create_contacts(): +def ask_create_contacts(contacts): name = ask_name() phone = ask_phone() try: - create_contact(name, phone) + contacts.create_contact(name, phone) except ValueError as e: print e -def ask_find_contact(): +def ask_find_contact(contacts): name = name = ask_name() try: - print name, find_contact(name) + print name, contacts.find_contact(name) except ValueError as e: print e -def ask_delete_contact(): +def ask_delete_contact(contacts): name = ask_name() try: - delete_contact(name) + contacts.delete_contact(name) except ValueError as e: print e -def ask_update_contact(): +def ask_update_contact(contacts): name = ask_name() phone = ask_phone() try: - update_contact(name, phone) + contacts.update_contact(name, phone) except ValueError as e: print e diff --git a/pickle_model.py b/pickle_model.py index 26ab915..b2fdfa4 100644 --- a/pickle_model.py +++ b/pickle_model.py @@ -1,48 +1,45 @@ import pickle -FILENAME = 'contacts.dat' +def key_exists(f): + def wrapper(self, name, *args): + if name not in self.contacts: + raise ValueError("Name doesn't exist") + return f(self, name, *args) + return wrapper -def load_contacts(): - try: - with open(FILENAME, 'r') as f: - return pickle.load(f) - except IOError, EOFError: - return {} - +class Contacts(object): + FILENAME = 'contacts.dat' -def save_contacts(): - with open(FILENAME, 'w') as f: - pickle.dump(contacts, f) + def __init__(self): + self.contacts = self.load_contacts() + def load_contacts(self): + try: + with open(self.FILENAME, 'r') as f: + return pickle.load(f) + except (IOError, EOFError): + return {} -def create_contact(name, phone): - if name in contacts: - raise ValueError("Name exists") - contacts[name] = phone - + def save_contacts(self): + with open(self.FILENAME, 'w') as f: + pickle.dump(self.contacts, f) -def key_exists(f): - def wrapper(name, *args): - if name not in contacts: - raise ValueError("Name doesn't exist") - return f(name, *args) - return wrapper - + def create_contact(self, name, phone): + if name in self.contacts: + raise ValueError("Name exists") + self.contacts[name] = phone -@key_exists -def find_contact(name): - return contacts[name] - + @key_exists + def find_contact(self, name): + return self.contacts[name] -@key_exists -def delete_contact(name): - del contacts[name] - -@key_exists -def update_contact(name, phone): - contacts[name] = phone + @key_exists + def delete_contact(self, name): + del self.contacts[name] -contacts = load_contacts() \ No newline at end of file + @key_exists + def update_contact(self, name, phone): + self.contacts[name] = phone From 6269b487e14c66d91717f868f3fa90a3609ad851 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Thu, 26 Nov 2015 10:27:59 +0200 Subject: [PATCH 02/14] FileReader and settings added --- console_controller.py | 16 ++++++++++++---- console_reader.py | 16 ++++++++++++++++ console_wrappers.py | 36 +++++++++++++++++------------------- contacts.dat | 8 ++++++++ file_reader.py | 21 +++++++++++++++++++++ session.txt | 5 +++++ settings.py | 3 +++ 7 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 console_reader.py create mode 100644 file_reader.py create mode 100644 session.txt create mode 100644 settings.py diff --git a/console_controller.py b/console_controller.py index 7296d05..4e45be3 100644 --- a/console_controller.py +++ b/console_controller.py @@ -2,8 +2,14 @@ from console_wrappers import ask_create_contacts, ask_find_contact, ask_delete_contact, ask_update_contact from pickle_model import Contacts +import settings +if settings.READER == 'Console': + from console_reader import ConsoleReader as DefaultReader +else: + from file_reader import FileReader as DefaultReader contacts = Contacts() +reader = DefaultReader() controller = { 'c': ask_create_contacts, @@ -12,18 +18,20 @@ 'u': ask_update_contact, } -controller = {key: functools.partial(value, contacts) for key, value in controller.items()} +controller = {key: functools.partial(value, contacts, reader) for key, value in controller.items()} def default(): print "Invalid action" - + + if __name__ == '__main__': try: while True: - action = raw_input("Action?") + action = reader.ask_action() if action in "Qq": break controller.get(action.lower(), default)() finally: - contacts.save_contacts() \ No newline at end of file + contacts.save_contacts() + reader.close() \ No newline at end of file diff --git a/console_reader.py b/console_reader.py new file mode 100644 index 0000000..7608ab2 --- /dev/null +++ b/console_reader.py @@ -0,0 +1,16 @@ +# coding=utf-8 +class ConsoleReader(object): + @staticmethod + def ask_name(): + return raw_input('name?') + + @staticmethod + def ask_phone(): + return raw_input('phone?') + + @staticmethod + def ask_action(): + return raw_input("Action?") + + def close(self): + pass diff --git a/console_wrappers.py b/console_wrappers.py index d582b6c..afbcb54 100644 --- a/console_wrappers.py +++ b/console_wrappers.py @@ -1,34 +1,32 @@ -def ask_name(): - return raw_input('name?') - -def ask_phone(): - return raw_input('phone?') - -def ask_create_contacts(contacts): - name = ask_name() - phone = ask_phone() + +def ask_create_contacts(contacts, reader): + name = reader.ask_name() + phone = reader.ask_phone() try: - contacts.create_contact(name, phone) + contacts.create_contact(name, phone) except ValueError as e: - print e + print e + -def ask_find_contact(contacts): - name = name = ask_name() +def ask_find_contact(contacts, reader): + name = reader.ask_name() try: print name, contacts.find_contact(name) except ValueError as e: print e -def ask_delete_contact(contacts): - name = ask_name() + +def ask_delete_contact(contacts, reader): + name = reader.ask_name() try: contacts.delete_contact(name) except ValueError as e: print e - -def ask_update_contact(contacts): - name = ask_name() - phone = ask_phone() + + +def ask_update_contact(contacts, reader): + name = reader.ask_name() + phone = reader.ask_phone() try: contacts.update_contact(name, phone) except ValueError as e: diff --git a/contacts.dat b/contacts.dat index f67ed38..687d1c9 100644 --- a/contacts.dat +++ b/contacts.dat @@ -3,4 +3,12 @@ S'Bob' p1 S'111' p2 +sS'Bill' +p3 +S'222' +p4 +sS'John' +p5 +S'111' +p6 s. \ No newline at end of file diff --git a/file_reader.py b/file_reader.py new file mode 100644 index 0000000..442e71d --- /dev/null +++ b/file_reader.py @@ -0,0 +1,21 @@ +# coding=utf-8 +class FileReader(object): + def __init__(self, filename='session.txt'): + try: + self.file = open(filename, 'r') + except IOError: + raise + + def read_action(self): + command = self.file.readline()[:-1] + if not command: + command = 'q' + print command + return command + + def __getattr__(self, item): + assert item in ('ask_action', 'ask_name', 'ask_phone') + return self.read_action + + def close(self): + self.file.close() \ No newline at end of file diff --git a/session.txt b/session.txt new file mode 100644 index 0000000..1d3a1cc --- /dev/null +++ b/session.txt @@ -0,0 +1,5 @@ +f +Bob +c +John +111 diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..4e1771e --- /dev/null +++ b/settings.py @@ -0,0 +1,3 @@ +# coding=utf-8 + +READER = "File" \ No newline at end of file From fde0c4e79e03e2cba34dd6be19361f1e80002128 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Thu, 26 Nov 2015 11:37:55 +0200 Subject: [PATCH 03/14] Redis database --- abstract_contacts.py | 30 +++++++++++++++++++++++ console_controller.py | 6 ++++- pickle_model.py => pickle_contacts.py | 5 +++- redis_contacts.py | 34 +++++++++++++++++++++++++++ requirements.txt | 2 ++ session.txt | 4 ++++ settings.py | 4 +++- 7 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 abstract_contacts.py rename pickle_model.py => pickle_contacts.py (92%) create mode 100644 redis_contacts.py create mode 100644 requirements.txt diff --git a/abstract_contacts.py b/abstract_contacts.py new file mode 100644 index 0000000..d573eef --- /dev/null +++ b/abstract_contacts.py @@ -0,0 +1,30 @@ +# coding=utf-8 +import abc + + +class AbstractModel(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def load_contacts(self): + pass + + @abc.abstractmethod + def save_contacts(self): + pass + + @abc.abstractmethod + def create_contact(self, name, phone): + pass + + @abc.abstractmethod + def find_contact(self, name): + pass + + @abc.abstractmethod + def delete_contact(self, name): + pass + + @abc.abstractmethod + def update_contact(self, name, phone): + pass diff --git a/console_controller.py b/console_controller.py index 4e45be3..a457145 100644 --- a/console_controller.py +++ b/console_controller.py @@ -1,8 +1,12 @@ import functools from console_wrappers import ask_create_contacts, ask_find_contact, ask_delete_contact, ask_update_contact -from pickle_model import Contacts import settings +if settings.DATABASE == "Pickle": + from pickle_contacts import PickleContacts as Contacts +else: + from redis_contacts import RedisContacts as Contacts + if settings.READER == 'Console': from console_reader import ConsoleReader as DefaultReader else: diff --git a/pickle_model.py b/pickle_contacts.py similarity index 92% rename from pickle_model.py rename to pickle_contacts.py index b2fdfa4..4a9d96a 100644 --- a/pickle_model.py +++ b/pickle_contacts.py @@ -1,5 +1,7 @@ import pickle +from abstract_contacts import AbstractModel + def key_exists(f): def wrapper(self, name, *args): @@ -8,7 +10,8 @@ def wrapper(self, name, *args): return f(self, name, *args) return wrapper -class Contacts(object): + +class PickleContacts(AbstractModel): FILENAME = 'contacts.dat' def __init__(self): diff --git a/redis_contacts.py b/redis_contacts.py new file mode 100644 index 0000000..785aeaa --- /dev/null +++ b/redis_contacts.py @@ -0,0 +1,34 @@ +# coding=utf-8 +import redis + +from abstract_contacts import AbstractModel + + +class RedisContacts(AbstractModel): + NOT_EXIST_MESSAGE = "Name doesn't exist" + + rc = redis.StrictRedis() + + def load_contacts(self): + pass + + def find_contact(self, name): + phone = self.rc.get(name) + if phone is None: + raise ValueError(self.NOT_EXIST_MESSAGE) + return phone + + def delete_contact(self, name): + if not self.rc.delete(name): + raise ValueError(self.NOT_EXIST_MESSAGE) + + def save_contacts(self): + pass + + def update_contact(self, name, phone): + if not self.rc.exists(name): + raise ValueError(self.NOT_EXIST_MESSAGE) + self.rc.set(name, phone) + + def create_contact(self, name, phone): + self.rc.set(name, phone) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b2fb29a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +redis==2.10.5 +wheel==0.24.0 diff --git a/session.txt b/session.txt index 1d3a1cc..99c13ea 100644 --- a/session.txt +++ b/session.txt @@ -1,5 +1,9 @@ f Bob +d +John c John 111 +f +John diff --git a/settings.py b/settings.py index 4e1771e..4c73d82 100644 --- a/settings.py +++ b/settings.py @@ -1,3 +1,5 @@ # coding=utf-8 -READER = "File" \ No newline at end of file +READER = "File" # "Console" + +DATABASE = "Redis" # "Pickle" From df89839aadebc1ff5b5bff6ab02e06a6467ef344 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Thu, 26 Nov 2015 12:44:27 +0200 Subject: [PATCH 04/14] MySQL database --- abstract_contacts.py | 1 + console_controller.py | 7 ++++++- console_reader.py | 10 ++++++++-- mysql_contacts.py | 41 +++++++++++++++++++++++++++++++++++++++++ redis_contacts.py | 1 - settings.py | 4 ++-- 6 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 mysql_contacts.py diff --git a/abstract_contacts.py b/abstract_contacts.py index d573eef..6ddd325 100644 --- a/abstract_contacts.py +++ b/abstract_contacts.py @@ -4,6 +4,7 @@ class AbstractModel(object): __metaclass__ = abc.ABCMeta + NOT_EXIST_MESSAGE = "Name doesn't exist" @abc.abstractmethod def load_contacts(self): diff --git a/console_controller.py b/console_controller.py index a457145..057becb 100644 --- a/console_controller.py +++ b/console_controller.py @@ -4,8 +4,13 @@ import settings if settings.DATABASE == "Pickle": from pickle_contacts import PickleContacts as Contacts -else: +elif settings.DATABASE == "Redis": from redis_contacts import RedisContacts as Contacts +elif settings.DATABASE == "MySQL": + from mysql_contacts import MysqlContacts as Contacts +else: + print "Invalid database" + exit() if settings.READER == 'Console': from console_reader import ConsoleReader as DefaultReader diff --git a/console_reader.py b/console_reader.py index 7608ab2..b28ff44 100644 --- a/console_reader.py +++ b/console_reader.py @@ -1,12 +1,18 @@ # coding=utf-8 class ConsoleReader(object): + @staticmethod + def validate(message): + if "'" in message or " " in message: + raise ValueError("SQL Injection") + return message + @staticmethod def ask_name(): - return raw_input('name?') + return ConsoleReader.validate(raw_input('name?')) @staticmethod def ask_phone(): - return raw_input('phone?') + return ConsoleReader.validate(raw_input('phone?')) @staticmethod def ask_action(): diff --git a/mysql_contacts.py b/mysql_contacts.py new file mode 100644 index 0000000..b32d81d --- /dev/null +++ b/mysql_contacts.py @@ -0,0 +1,41 @@ +# coding=utf-8 +import MySQLdb +from abstract_contacts import AbstractModel + + +class MysqlContacts(AbstractModel): + db = MySQLdb.connect(host='localhost', user='root', passwd='1', db='phones') + + def __init__(self): + self.cursor = self.db.cursor() + + def load_contacts(self): + pass + + def find_contact(self, name): + if not self.cursor.execute("select phone from contacts where name='{}'".format(name)): + raise ValueError(self.NOT_EXIST_MESSAGE) + return self.cursor.fetchone()[0] + + def delete_contact(self, name): + if not self.cursor.execute("delete from contacts where name='{}'".format(name)): + raise ValueError(self.NOT_EXIST_MESSAGE) + self.cursor.execute("commit") + + def save_contacts(self): + pass + + def update_contact(self, name, phone): + if not self.cursor.execute("update contacts set phone='{}' where name='{}'".format(phone, name)): + raise ValueError(self.NOT_EXIST_MESSAGE) + self.cursor.execute("commit") + + def create_contact(self, name, phone): + try: + self.find_contact(name) + except ValueError: + pass + else: + raise ValueError("Name exists") + self.cursor.execute("insert into contacts (name, phone) values ('{}', '{}')".format(name, phone)) + self.cursor.execute("commit") diff --git a/redis_contacts.py b/redis_contacts.py index 785aeaa..40432f0 100644 --- a/redis_contacts.py +++ b/redis_contacts.py @@ -5,7 +5,6 @@ class RedisContacts(AbstractModel): - NOT_EXIST_MESSAGE = "Name doesn't exist" rc = redis.StrictRedis() diff --git a/settings.py b/settings.py index 4c73d82..74e77aa 100644 --- a/settings.py +++ b/settings.py @@ -1,5 +1,5 @@ # coding=utf-8 -READER = "File" # "Console" +READER = "Console" # "File" # "Console" -DATABASE = "Redis" # "Pickle" +DATABASE = "MySQL" #"Redis" # "Pickle" From fe12b529c31861b4cfba9fd98a902b2d3404174c Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Thu, 26 Nov 2015 12:46:57 +0200 Subject: [PATCH 05/14] Packages added --- console_controller.py | 13 +++++++------ contacts/__init__.py | 0 .../abstract_contacts.py | 0 mysql_contacts.py => contacts/mysql_contacts.py | 0 pickle_contacts.py => contacts/pickle_contacts.py | 0 redis_contacts.py => contacts/redis_contacts.py | 0 reader/__init__.py | 0 console_reader.py => reader/console_reader.py | 0 file_reader.py => reader/file_reader.py | 0 settings.py | 2 +- 10 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 contacts/__init__.py rename abstract_contacts.py => contacts/abstract_contacts.py (100%) rename mysql_contacts.py => contacts/mysql_contacts.py (100%) rename pickle_contacts.py => contacts/pickle_contacts.py (100%) rename redis_contacts.py => contacts/redis_contacts.py (100%) create mode 100644 reader/__init__.py rename console_reader.py => reader/console_reader.py (100%) rename file_reader.py => reader/file_reader.py (100%) diff --git a/console_controller.py b/console_controller.py index 057becb..78394ea 100644 --- a/console_controller.py +++ b/console_controller.py @@ -1,21 +1,22 @@ import functools -from console_wrappers import ask_create_contacts, ask_find_contact, ask_delete_contact, ask_update_contact import settings +from console_wrappers import ask_create_contacts, ask_find_contact, ask_delete_contact, ask_update_contact + if settings.DATABASE == "Pickle": - from pickle_contacts import PickleContacts as Contacts + from contacts.pickle_contacts import PickleContacts as Contacts elif settings.DATABASE == "Redis": - from redis_contacts import RedisContacts as Contacts + from contacts.redis_contacts import RedisContacts as Contacts elif settings.DATABASE == "MySQL": - from mysql_contacts import MysqlContacts as Contacts + from contacts.mysql_contacts import MysqlContacts as Contacts else: print "Invalid database" exit() if settings.READER == 'Console': - from console_reader import ConsoleReader as DefaultReader + from reader.console_reader import ConsoleReader as DefaultReader else: - from file_reader import FileReader as DefaultReader + from reader.file_reader import FileReader as DefaultReader contacts = Contacts() reader = DefaultReader() diff --git a/contacts/__init__.py b/contacts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/abstract_contacts.py b/contacts/abstract_contacts.py similarity index 100% rename from abstract_contacts.py rename to contacts/abstract_contacts.py diff --git a/mysql_contacts.py b/contacts/mysql_contacts.py similarity index 100% rename from mysql_contacts.py rename to contacts/mysql_contacts.py diff --git a/pickle_contacts.py b/contacts/pickle_contacts.py similarity index 100% rename from pickle_contacts.py rename to contacts/pickle_contacts.py diff --git a/redis_contacts.py b/contacts/redis_contacts.py similarity index 100% rename from redis_contacts.py rename to contacts/redis_contacts.py diff --git a/reader/__init__.py b/reader/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/console_reader.py b/reader/console_reader.py similarity index 100% rename from console_reader.py rename to reader/console_reader.py diff --git a/file_reader.py b/reader/file_reader.py similarity index 100% rename from file_reader.py rename to reader/file_reader.py diff --git a/settings.py b/settings.py index 74e77aa..a7a4642 100644 --- a/settings.py +++ b/settings.py @@ -1,5 +1,5 @@ # coding=utf-8 -READER = "Console" # "File" # "Console" +READER = "File" # "Console" DATABASE = "MySQL" #"Redis" # "Pickle" From 414ace9d66b128a39cf029788d3bb1a90adb6b97 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Thu, 26 Nov 2015 12:49:19 +0200 Subject: [PATCH 06/14] Requirements fix --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index b2fb29a..b61d2b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ +MySQL-python==1.2.5 redis==2.10.5 wheel==0.24.0 From c1772c641932ac67cb7082386f56d2da071aec25 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Thu, 26 Nov 2015 14:35:05 +0200 Subject: [PATCH 07/14] SQLAlchemy supported --- console_controller.py | 2 ++ contacts/mysql_contacts.py | 3 ++- contacts/sqlalchemy_contacts.py | 42 +++++++++++++++++++++++++++++++++ requirements.txt | 1 + settings.py | 4 ++-- 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 contacts/sqlalchemy_contacts.py diff --git a/console_controller.py b/console_controller.py index 78394ea..7f17f71 100644 --- a/console_controller.py +++ b/console_controller.py @@ -9,6 +9,8 @@ from contacts.redis_contacts import RedisContacts as Contacts elif settings.DATABASE == "MySQL": from contacts.mysql_contacts import MysqlContacts as Contacts +elif settings.DATABASE == "SQLAlchemy": + from contacts.sqlalchemy_contacts import SQLAlchemyContacts as Contacts else: print "Invalid database" exit() diff --git a/contacts/mysql_contacts.py b/contacts/mysql_contacts.py index b32d81d..e03afad 100644 --- a/contacts/mysql_contacts.py +++ b/contacts/mysql_contacts.py @@ -23,7 +23,8 @@ def delete_contact(self, name): self.cursor.execute("commit") def save_contacts(self): - pass + self.cursor.close() + self.db.close() def update_contact(self, name, phone): if not self.cursor.execute("update contacts set phone='{}' where name='{}'".format(phone, name)): diff --git a/contacts/sqlalchemy_contacts.py b/contacts/sqlalchemy_contacts.py new file mode 100644 index 0000000..d884414 --- /dev/null +++ b/contacts/sqlalchemy_contacts.py @@ -0,0 +1,42 @@ +# coding=utf-8 +from abstract_contacts import AbstractModel + +from sqlalchemy import create_engine +from sqlalchemy.ext.automap import automap_base +from sqlalchemy.orm import Session + + +class SQLAlchemyContacts(AbstractModel): + engine = create_engine('mysql://root:1@localhost/phones') + + def __init__(self): + self.session = Session(self.engine) + Base = automap_base() + Base.prepare(self.engine, reflect=True) + self.Contacts = Base.classes.contacts + + def create_contact(self, name, phone): + self.session.add(self.Contacts(name=name, phone=phone)) + self.session.commit() + + def delete_contact(self, name): + if not self.session.query(self.Contacts).filter_by(name=name).delete(): + raise ValueError(self.NOT_EXIST_MESSAGE) + self.session.commit() + + def find_contact(self, name): + contacts = self.session.query(self.Contacts).filter_by(name=name).all() + if contacts: + return contacts[0].phone + raise ValueError(self.NOT_EXIST_MESSAGE) + + def save_contacts(self): + pass + + def load_contacts(self): + pass + + def update_contact(self, name, phone): + if not self.session.query(self.Contacts).filter_by(name=name).update({'phone': phone}): + raise ValueError(self.NOT_EXIST_MESSAGE) + self.session.commit() diff --git a/requirements.txt b/requirements.txt index b61d2b4..0b93d77 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ MySQL-python==1.2.5 redis==2.10.5 +SQLAlchemy==1.0.9 wheel==0.24.0 diff --git a/settings.py b/settings.py index a7a4642..cb62b03 100644 --- a/settings.py +++ b/settings.py @@ -1,5 +1,5 @@ # coding=utf-8 -READER = "File" # "Console" +READER = "Console" # "File" # "Console" -DATABASE = "MySQL" #"Redis" # "Pickle" +DATABASE = "SQLAlchemy" #"MySQL" #"Redis" # "Pickle" From 3a4660c99af4b884b5f2896c4a9d6f061825f5b4 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Thu, 26 Nov 2015 16:24:39 +0200 Subject: [PATCH 08/14] XML config --- configurer.py | 36 ++++++++++++++++++++++++++++++++++++ console_controller.py | 22 +--------------------- settings.py | 2 +- 3 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 configurer.py diff --git a/configurer.py b/configurer.py new file mode 100644 index 0000000..9da8547 --- /dev/null +++ b/configurer.py @@ -0,0 +1,36 @@ +# coding=utf-8 +import os +from xml.dom.minidom import parse + + +def parse_xml_config(): + print "XML Configuration..." + dom = parse('settings.xml') + database = dom.getElementsByTagName('database')[0].firstChild.nodeValue + reader = dom.getElementsByTagName('reader')[0].firstChild.nodeValue + return database.strip(), reader.strip() + +if os.path.isfile('settings.xml'): + DATABASE, READER = parse_xml_config() +else: + from settings import DATABASE, READER + +if DATABASE == "Pickle": + from contacts.pickle_contacts import PickleContacts as Contacts +elif DATABASE == "Redis": + from contacts.redis_contacts import RedisContacts as Contacts +elif DATABASE == "MySQL": + from contacts.mysql_contacts import MysqlContacts as Contacts +elif DATABASE == "SQLAlchemy": + from contacts.sqlalchemy_contacts import SQLAlchemyContacts as Contacts +else: + print "Invalid database" + exit() + +if READER == 'Console': + from reader.console_reader import ConsoleReader as DefaultReader +else: + from reader.file_reader import FileReader as DefaultReader + +contacts = Contacts() +reader = DefaultReader() diff --git a/console_controller.py b/console_controller.py index 7f17f71..28cc575 100644 --- a/console_controller.py +++ b/console_controller.py @@ -1,27 +1,7 @@ import functools -import settings from console_wrappers import ask_create_contacts, ask_find_contact, ask_delete_contact, ask_update_contact - -if settings.DATABASE == "Pickle": - from contacts.pickle_contacts import PickleContacts as Contacts -elif settings.DATABASE == "Redis": - from contacts.redis_contacts import RedisContacts as Contacts -elif settings.DATABASE == "MySQL": - from contacts.mysql_contacts import MysqlContacts as Contacts -elif settings.DATABASE == "SQLAlchemy": - from contacts.sqlalchemy_contacts import SQLAlchemyContacts as Contacts -else: - print "Invalid database" - exit() - -if settings.READER == 'Console': - from reader.console_reader import ConsoleReader as DefaultReader -else: - from reader.file_reader import FileReader as DefaultReader - -contacts = Contacts() -reader = DefaultReader() +from configurer import contacts, reader controller = { 'c': ask_create_contacts, diff --git a/settings.py b/settings.py index cb62b03..a1a31bf 100644 --- a/settings.py +++ b/settings.py @@ -1,5 +1,5 @@ # coding=utf-8 -READER = "Console" # "File" # "Console" +READER = "File" # "Console" DATABASE = "SQLAlchemy" #"MySQL" #"Redis" # "Pickle" From 3af108badb7e880bb02cb4eb633e8e5046269b6b Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Fri, 27 Nov 2015 11:04:33 +0200 Subject: [PATCH 09/14] XML config --- desktop_controller.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 desktop_controller.py diff --git a/desktop_controller.py b/desktop_controller.py new file mode 100644 index 0000000..5d0436c --- /dev/null +++ b/desktop_controller.py @@ -0,0 +1,3 @@ +# coding=utf-8 +from Tkinter import * + From fe8ba8ef8d428f650449054b5291c44d3f40fa96 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Fri, 27 Nov 2015 11:07:14 +0200 Subject: [PATCH 10/14] settings.xml --- settings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 settings.xml diff --git a/settings.xml b/settings.xml new file mode 100644 index 0000000..3400312 --- /dev/null +++ b/settings.xml @@ -0,0 +1,8 @@ + + + MySQL + + + File + + \ No newline at end of file From d3cec2c7a75b56131834c6d8a835a422299e44ba Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Fri, 27 Nov 2015 12:52:41 +0200 Subject: [PATCH 11/14] GUI --- contacts.dat | 4 +- contacts/abstract_contacts.py | 4 ++ contacts/mysql_contacts.py | 6 +++ contacts/pickle_contacts.py | 6 ++- contacts/redis_contacts.py | 7 +++ contacts/sqlalchemy_contacts.py | 5 ++ desktop_controller.py | 91 +++++++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 4 deletions(-) diff --git a/contacts.dat b/contacts.dat index 687d1c9..5acebab 100644 --- a/contacts.dat +++ b/contacts.dat @@ -1,11 +1,11 @@ (dp0 S'Bob' p1 -S'111' +S'123' p2 sS'Bill' p3 -S'222' +S'223' p4 sS'John' p5 diff --git a/contacts/abstract_contacts.py b/contacts/abstract_contacts.py index 6ddd325..f069df2 100644 --- a/contacts/abstract_contacts.py +++ b/contacts/abstract_contacts.py @@ -29,3 +29,7 @@ def delete_contact(self, name): @abc.abstractmethod def update_contact(self, name, phone): pass + + @abc.abstractmethod + def list_contacts(self): + pass diff --git a/contacts/mysql_contacts.py b/contacts/mysql_contacts.py index e03afad..2154668 100644 --- a/contacts/mysql_contacts.py +++ b/contacts/mysql_contacts.py @@ -40,3 +40,9 @@ def create_contact(self, name, phone): raise ValueError("Name exists") self.cursor.execute("insert into contacts (name, phone) values ('{}', '{}')".format(name, phone)) self.cursor.execute("commit") + + def list_contacts(self): + self.cursor.execute("select name, phone from contacts") + return tuple( + [(row[0], row[1]) for row in self.cursor.fetchall()] + ) diff --git a/contacts/pickle_contacts.py b/contacts/pickle_contacts.py index 4a9d96a..1c41465 100644 --- a/contacts/pickle_contacts.py +++ b/contacts/pickle_contacts.py @@ -27,6 +27,7 @@ def load_contacts(self): def save_contacts(self): with open(self.FILENAME, 'w') as f: pickle.dump(self.contacts, f) + print "Contacts saved" def create_contact(self, name, phone): if name in self.contacts: @@ -37,12 +38,13 @@ def create_contact(self, name, phone): def find_contact(self, name): return self.contacts[name] - @key_exists def delete_contact(self, name): del self.contacts[name] - @key_exists def update_contact(self, name, phone): self.contacts[name] = phone + + def list_contacts(self): + return tuple(self.contacts.items()) diff --git a/contacts/redis_contacts.py b/contacts/redis_contacts.py index 40432f0..e508234 100644 --- a/contacts/redis_contacts.py +++ b/contacts/redis_contacts.py @@ -30,4 +30,11 @@ def update_contact(self, name, phone): self.rc.set(name, phone) def create_contact(self, name, phone): + if self.rc.exists(name): + raise ValueError("Name exists") self.rc.set(name, phone) + + def list_contacts(self): + names = self.rc.keys("*") + contacts = [(name, self.rc.get(name)) for name in names] + return tuple(contacts) diff --git a/contacts/sqlalchemy_contacts.py b/contacts/sqlalchemy_contacts.py index d884414..bee3cb8 100644 --- a/contacts/sqlalchemy_contacts.py +++ b/contacts/sqlalchemy_contacts.py @@ -40,3 +40,8 @@ def update_contact(self, name, phone): if not self.session.query(self.Contacts).filter_by(name=name).update({'phone': phone}): raise ValueError(self.NOT_EXIST_MESSAGE) self.session.commit() + + def list_contacts(self): + return tuple( + [(c.name, c.phone) for c in self.session.query(self.Contacts).all()] + ) diff --git a/desktop_controller.py b/desktop_controller.py index 5d0436c..656a1a9 100644 --- a/desktop_controller.py +++ b/desktop_controller.py @@ -1,3 +1,94 @@ # coding=utf-8 from Tkinter import * +import tkMessageBox +from configurer import contacts + + +root = Tk() +tl = None + +l_name = Label(root, text="Name") +l_name.grid(row=0, column=0) +e_name = Entry(root) +e_name.grid(row=0, column=1) +l_phone = Label(root, text="Phone") +l_phone.grid(row=1, column=0) +e_phone = Entry(root) +e_phone.grid(row=1, column=1) + + +def clear_entries(): + e_name.delete(0, END) + e_phone.delete(0, END) + + +def ask_create_contact(): + try: + contacts.create_contact(e_name.get(), e_phone.get()) + clear_entries() + tkMessageBox.showinfo("Success", "Contact added") + except ValueError as e: + tkMessageBox.showerror("Error", e) + + +def ask_find_contact(): + try: + phone = contacts.find_contact(e_name.get()) + e_phone.delete(0, END) + e_phone.insert(0, phone) + except ValueError as e: + tkMessageBox.showerror("Error", e) + + +def ask_update_contact(): + try: + contacts.update_contact(e_name.get(), e_phone.get()) + clear_entries() + tkMessageBox.showinfo("Success", "Contact updated") + except ValueError as e: + tkMessageBox.showerror("Error", e) + + +def ask_delete_contact(): + try: + contacts.delete_contact(e_name.get()) + clear_entries() + tkMessageBox.showinfo("Success", "Contact deleted") + except ValueError as e: + tkMessageBox.showerror("Error", e) + + +def ask_list_contacts(): + global tl + b_l.config(text="Close List", command=on_tl_exit) + tl = Toplevel(root) + tl.bind('', on_tl_exit) + scrollbar = Scrollbar(tl) + scrollbar.pack(side=RIGHT, fill=Y) + lb = Listbox(tl) + lb.pack() + lb.config(yscrollcommand=scrollbar.set) + scrollbar.config(command=lb.yview) + for name, phone in contacts.list_contacts(): + lb.insert(0, "{}:{}".format(name, phone)) + + +def on_tl_exit(_=None): + b_l.config(text="Show All", command=ask_list_contacts) + tl.destroy() + + +def on_exit(): + contacts.save_contacts() + root.quit() + +Button(root, text="Add", width=10, command=ask_create_contact).grid(row=0, column=2) +Button(root, text="Find", width=10, command=ask_find_contact).grid(row=1, column=2) +Button(root, text="Update", width=10, command=ask_update_contact).grid(row=2, column=2) +Button(root, text="Delete", width=10, command=ask_delete_contact).grid(row=3, column=2) +b_l = Button(root, text="Show All", width=10, command=ask_list_contacts) +b_l.grid(row=3, column=0) + +root.protocol("WM_DELETE_WINDOW", on_exit) +root.mainloop() From f80ce00b43c136f3b035b256e4a22d6553145678 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Fri, 27 Nov 2015 15:33:01 +0200 Subject: [PATCH 12/14] Web find --- requirements.txt | 5 +++++ settings.xml | 2 +- static/main.css | 3 +++ templates/find.html | 14 ++++++++++++++ templates/index.html | 30 ++++++++++++++++++++++++++++++ web_controller.py | 22 ++++++++++++++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 static/main.css create mode 100644 templates/find.html create mode 100644 templates/index.html create mode 100644 web_controller.py diff --git a/requirements.txt b/requirements.txt index 0b93d77..d6eba05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,9 @@ +Flask==0.10.1 +itsdangerous==0.24 +Jinja2==2.8 +MarkupSafe==0.23 MySQL-python==1.2.5 redis==2.10.5 SQLAlchemy==1.0.9 +Werkzeug==0.11.2 wheel==0.24.0 diff --git a/settings.xml b/settings.xml index 3400312..1e3f032 100644 --- a/settings.xml +++ b/settings.xml @@ -1,6 +1,6 @@ - MySQL + Redis File diff --git a/static/main.css b/static/main.css new file mode 100644 index 0000000..e1391ea --- /dev/null +++ b/static/main.css @@ -0,0 +1,3 @@ +h1 { + color:red +} \ No newline at end of file diff --git a/templates/find.html b/templates/find.html new file mode 100644 index 0000000..62104d0 --- /dev/null +++ b/templates/find.html @@ -0,0 +1,14 @@ +{% extends 'index.html' %} +{% block content %} +

Find contact

+ {% if name %} +

Found: {{name}} - {{phone}}

+ {% endif %} +
+ + +
+{% endblock %} +{%block footer%} +© 2015 +{%endblock%} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..410daf5 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,30 @@ + + + + + Phone Book + + + + {% block content %} +

Phone Book

+ + {% for contact in contacts %} + + + + + {% endfor %} +
{{contact.0}}{{contact.1}}
+ {% endblock %} + +{%block footer%} +{%endblock%} + + \ No newline at end of file diff --git a/web_controller.py b/web_controller.py new file mode 100644 index 0000000..ee62a1d --- /dev/null +++ b/web_controller.py @@ -0,0 +1,22 @@ +# coding=utf-8 +from flask import Flask, render_template, request +from configurer import contacts + +app = Flask(__name__) + + +@app.route('/') +def index(): + return render_template('index.html', contacts=contacts.list_contacts()) + + +@app.route('/find') +def found(): + name = phone = '' + if 'search' in request.args: + name = request.args['search'] + phone = contacts.find_contact(name) + return render_template('find.html', name=name, phone=phone) + +if __name__ == '__main__': + app.run(debug=True) From b044f50494cc2ce6584c366b9da9bbdbbcd44d49 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Fri, 27 Nov 2015 16:34:07 +0200 Subject: [PATCH 13/14] Web all --- static/main.css | 8 ++++- templates/add.html | 9 +++++ templates/delete.html | 8 +++++ templates/find.html | 4 +-- templates/index.html | 8 ++++- templates/update.html | 9 +++++ web_controller.py | 77 +++++++++++++++++++++++++++++++++++++++---- 7 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 templates/add.html create mode 100644 templates/delete.html create mode 100644 templates/update.html diff --git a/static/main.css b/static/main.css index e1391ea..8f74e10 100644 --- a/static/main.css +++ b/static/main.css @@ -1,3 +1,9 @@ -h1 { +h1, .error { color:red +} + +table, td { + border-style: solid; + border-color: #424242; + border-width: 1px } \ No newline at end of file diff --git a/templates/add.html b/templates/add.html new file mode 100644 index 0000000..a791d0e --- /dev/null +++ b/templates/add.html @@ -0,0 +1,9 @@ +{% extends 'index.html' %} +{% block content %} +

Add contact

+
+ + + +
+{% endblock %} \ No newline at end of file diff --git a/templates/delete.html b/templates/delete.html new file mode 100644 index 0000000..df6b123 --- /dev/null +++ b/templates/delete.html @@ -0,0 +1,8 @@ +{% extends 'index.html' %} +{% block content %} +

Delete contact

+
+ + +
+{% endblock %} \ No newline at end of file diff --git a/templates/find.html b/templates/find.html index 62104d0..7162253 100644 --- a/templates/find.html +++ b/templates/find.html @@ -5,8 +5,8 @@

Find contact

Found: {{name}} - {{phone}}

{% endif %}
- - + +
{% endblock %} {%block footer%} diff --git a/templates/index.html b/templates/index.html index 410daf5..f699842 100644 --- a/templates/index.html +++ b/templates/index.html @@ -6,22 +6,28 @@ + {%block errors%} + {% if message %}

{{message}}

{%endif%} + {%endblock%} {% block content %}

Phone Book

+ {% for contact in contacts %} + {% endfor %} +
{{contact.0}} {{contact.1}}
{% endblock %} {%block footer%} diff --git a/templates/update.html b/templates/update.html new file mode 100644 index 0000000..1956c84 --- /dev/null +++ b/templates/update.html @@ -0,0 +1,9 @@ +{% extends 'index.html' %} +{% block content %} +

Update contact

+
+ + + +
+{% endblock %} \ No newline at end of file diff --git a/web_controller.py b/web_controller.py index ee62a1d..2ab8d5d 100644 --- a/web_controller.py +++ b/web_controller.py @@ -1,5 +1,5 @@ # coding=utf-8 -from flask import Flask, render_template, request +from flask import Flask, render_template, request, redirect, session from configurer import contacts app = Flask(__name__) @@ -7,16 +7,81 @@ @app.route('/') def index(): - return render_template('index.html', contacts=contacts.list_contacts()) + message = session.pop('message', '') + return render_template( + 'index.html', + contacts=contacts.list_contacts(), + message=message + ) @app.route('/find') def found(): - name = phone = '' if 'search' in request.args: - name = request.args['search'] - phone = contacts.find_contact(name) - return render_template('find.html', name=name, phone=phone) + try: + name = request.args['search'] + phone = contacts.find_contact(name) + return render_template('find.html', name=name, phone=phone) + except ValueError as e: + return render_template('find.html', message=e) + return render_template('find.html') + +@app.route('/add', methods=['GET', 'POST']) +def add(): + if request.method == 'POST': + try: + contacts.create_contact(request.form['name'], request.form['phone']) + session['message'] = "Contact added" + return redirect('/') + except ValueError as e: + return render_template( + 'add.html', + message=e, + name=request.form['name'], + phone=request.form['phone'] + ) + return render_template('add.html') + + +@app.route('/update', methods=['GET', 'POST']) +def update(): + if request.method == 'POST': + try: + contacts.update_contact(request.form['name'], request.form['phone']) + session['message'] = "Contact updated: {}".format(request.form['name']) + return redirect('/') + except ValueError as e: + return render_template( + 'update.html', + message=e, + name=request.form['name'], + phone=request.form['phone'] + ) + return render_template('update.html') + + +@app.route('/delete', methods=['GET', 'POST']) +def delete(): + if request.method == 'POST': + try: + contacts.delete_contact(request.form['name']) + session['message'] = "Contact deleted: {}".format(request.form['name']) + return redirect('/') + except ValueError as e: + return render_template( + 'delete.html', + message=e, + name=request.form['name'], + ) + return render_template('delete.html') + + +@app.route('/do') +def do(): + return "a" + +app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' if __name__ == '__main__': app.run(debug=True) + From 2366899059c6582ff3d288ac896ec0a20ea31d02 Mon Sep 17 00:00:00 2001 From: Nativeborn Date: Fri, 27 Nov 2015 17:05:44 +0200 Subject: [PATCH 14/14] Selenium tests --- requirements.txt | 3 +++ test/__init__.py | 0 test/test_web.py | 16 ++++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 test/__init__.py create mode 100644 test/test_web.py diff --git a/requirements.txt b/requirements.txt index d6eba05..f99b88f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,10 @@ itsdangerous==0.24 Jinja2==2.8 MarkupSafe==0.23 MySQL-python==1.2.5 +py==1.4.30 +pytest==2.8.3 redis==2.10.5 +selenium==2.48.0 SQLAlchemy==1.0.9 Werkzeug==0.11.2 wheel==0.24.0 diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_web.py b/test/test_web.py new file mode 100644 index 0000000..89977b1 --- /dev/null +++ b/test/test_web.py @@ -0,0 +1,16 @@ +# coding=utf-8 +from selenium import webdriver + +class TestWeb(object): + def setup_class(self): + self.driver = webdriver.Firefox() + self.driver.get('http://localhost:5000') + + def teardown_class(self): + self.driver.close() + + def test_A(self): + assert 1 == 1 + + def test_B(self): + assert 1 == 2 \ No newline at end of file