From 0df9b103162016d412dd98594d5e6edf5d267082 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 17:10:51 -0700 Subject: [PATCH 01/10] added test directory --- test/.gitigore | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/.gitigore diff --git a/test/.gitigore b/test/.gitigore new file mode 100644 index 0000000..e69de29 From 77362f4ced0f323d3d88e4a7772c709999a6cf2a Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 17:12:29 -0700 Subject: [PATCH 02/10] Added database --- requirements.txt | 1 + src/db.py | 150 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 src/db.py diff --git a/requirements.txt b/requirements.txt index 5a9d091..4c28285 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ Flask==0.9 twilio==3.3.10 mock==0.8.0 nose==1.1.2 +sqlalchemy==0.9.1 diff --git a/src/db.py b/src/db.py new file mode 100644 index 0000000..d7e9748 --- /dev/null +++ b/src/db.py @@ -0,0 +1,150 @@ + +import datetime +import re + +import sqlalchemy +from sqlalchemy import Boolean, Column, Integer, String, Text, DateTime, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, Session + +#from phone_directory_actions import PhoneDirectoryActions + + +Base = declarative_base() + +def bind_engine(engine): + Base.metadata.create_all(engine) + return sessionmaker(bind=engine, autoflush=True) + +# def __create_crm_object(SessionMaker, crm_type, crm_class, kwargs, +# site_id=None): +# +# session = SessionMaker() +# +# connection = crm_class(**kwargs) +# connection.site_id = site_id +# session.add(connection) +# session.commit() +# +# crm_id = connection.crm_id +# session.close() +# +# return crm_id + +class User(Base): + __tablename__ = 'users' + + user_id = Column('user_id', Integer, primary_key=True) + phone = Column('crm_type', String(100)) + name = Column('name', String(100)) + + updated_at = Column('updated_at', DateTime, onupdate=datetime.datetime.now, default=datetime.datetime.now, index=True) + created_at = Column('created_at', DateTime, default=datetime.datetime.now, index=True) + +class PhoneHistory(Base): + __tablename__ = 'phone_history' + id = Column('id', Integer, index=True, primary_key=True) + user_id = Column('user_id', Integer, index=True) + phone = Column('phone', String(100)) + updated_at = Column('updated_at', DateTime, onupdate=datetime.datetime.now, default=datetime.datetime.now, index=True) + created_at = Column('created_at', DateTime, default=datetime.datetime.now, index=True) + + +class PhoneDirectoryActions: + pass + +class SqlitePhoneDirectoryActions(PhoneDirectoryActions): + ''' + Data stored in a sqliteDB. + ''' + + def __init__(self, database="sqlite:///test.db"): + engine = sqlalchemy.create_engine( + database # TODO: better path to database + ) + + self._SessionMaker = bind_engine(engine) + + def _get_user_by_id(self, user_id): + session = self._SessionMaker() + res = session.query(User).filter(User.user_id == user_id).all() + session.close() + + if len(res) != 1: + raise KeyError + else: + return res[0] + + def user_exists(self, user_id): + ''' + Return true if user_id exists, false if not + ''' + user = self._get_user_by_id(user_id) + return (user != None) # truthy + + def get_phone_number(self, user_id): + ''' + Return phone number for given user_id. Throw KeyError if user doesn't exist. + ''' + user = self._get_user_by_id(user_id) + return user.phone + + def get_name(self, user_id): + ''' + Return username for given user_id. Throw KeyError if user doesn't exist. + ''' + user = self._get_user_by_id(user_id) + return user.name + + def update_phone_number(self, user_id, new_number): + ''' + If user_id exists, update its phone number and archive the old number. + If user_id does not exist, throw KeyError + If new phone number is not valid, throw + ''' + user = self._get_user_by_id(user_id) + self._validate_phone_number(new_number) + old_number = user.phone + user.phone = new_number + + session = self._SessionMaker() + + history = PhoneHistory() + history.user_id = user.user_id + history.phone = old_number + + session.add(user) + session.add(history) + session.commit() + + def _add_a_user(self, user_id, phone, name): + session = self._SessionMaker() + + user= User() + user.name = name + user.user_id = user_id + user.phone = phone + + session.add(user) + session.commit() + + def _validate_phone_number(self, number): + ''' + Raise ValueError if phone number is not a ten digit numeric string + ''' + if not (re.match(r'^\d+$', number) and len(number) == 10): + raise ValueError('Phone number {} is not a ten digit numeric string'.format(number)) + + +if __name__ == '__main__': + sq = SqlitePhoneDirectoryActions(database="sqlite:///:memory:") + sq._add_a_user(user_id=1122, phone='1'*10, name='ben') + + # some quick tests + assert sq.user_exists(1122) + assert sq.get_name(1122) == 'ben' + assert sq.get_phone_number(1122) == '1'*10 + sq.update_phone_number(user_id=1122, new_number='2'*10) + assert sq.get_phone_number(1122) == '2'*10 + + From 6f7084cbb4d4ebdc3a84a4a4ccd8e265e37f8da0 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 17:14:33 -0700 Subject: [PATCH 03/10] cleanup --- src/{db.py => sqlite_phone_directory.py} | 34 +++++------------------- 1 file changed, 7 insertions(+), 27 deletions(-) rename src/{db.py => sqlite_phone_directory.py} (81%) diff --git a/src/db.py b/src/sqlite_phone_directory.py similarity index 81% rename from src/db.py rename to src/sqlite_phone_directory.py index d7e9748..db1f1d3 100644 --- a/src/db.py +++ b/src/sqlite_phone_directory.py @@ -7,7 +7,8 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, Session -#from phone_directory_actions import PhoneDirectoryActions +from phone_directory_actions import PhoneDirectoryActions +from util import validate_phone_number Base = declarative_base() @@ -16,20 +17,6 @@ def bind_engine(engine): Base.metadata.create_all(engine) return sessionmaker(bind=engine, autoflush=True) -# def __create_crm_object(SessionMaker, crm_type, crm_class, kwargs, -# site_id=None): -# -# session = SessionMaker() -# -# connection = crm_class(**kwargs) -# connection.site_id = site_id -# session.add(connection) -# session.commit() -# -# crm_id = connection.crm_id -# session.close() -# -# return crm_id class User(Base): __tablename__ = 'users' @@ -41,6 +28,7 @@ class User(Base): updated_at = Column('updated_at', DateTime, onupdate=datetime.datetime.now, default=datetime.datetime.now, index=True) created_at = Column('created_at', DateTime, default=datetime.datetime.now, index=True) + class PhoneHistory(Base): __tablename__ = 'phone_history' id = Column('id', Integer, index=True, primary_key=True) @@ -50,9 +38,6 @@ class PhoneHistory(Base): created_at = Column('created_at', DateTime, default=datetime.datetime.now, index=True) -class PhoneDirectoryActions: - pass - class SqlitePhoneDirectoryActions(PhoneDirectoryActions): ''' Data stored in a sqliteDB. @@ -103,7 +88,7 @@ def update_phone_number(self, user_id, new_number): If new phone number is not valid, throw ''' user = self._get_user_by_id(user_id) - self._validate_phone_number(new_number) + validate_phone_number(new_number) old_number = user.phone user.phone = new_number @@ -118,9 +103,11 @@ def update_phone_number(self, user_id, new_number): session.commit() def _add_a_user(self, user_id, phone, name): + validate_phone_number(phone) + session = self._SessionMaker() - user= User() + user = User() user.name = name user.user_id = user_id user.phone = phone @@ -128,13 +115,6 @@ def _add_a_user(self, user_id, phone, name): session.add(user) session.commit() - def _validate_phone_number(self, number): - ''' - Raise ValueError if phone number is not a ten digit numeric string - ''' - if not (re.match(r'^\d+$', number) and len(number) == 10): - raise ValueError('Phone number {} is not a ten digit numeric string'.format(number)) - if __name__ == '__main__': sq = SqlitePhoneDirectoryActions(database="sqlite:///:memory:") From de92a95fe4283aa3319313959114f3d5d5a6628b Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 17:56:35 -0700 Subject: [PATCH 04/10] Added .py --- src/sqlite_phone_directory.py | 22 ++++++---------------- test/test_sqlite_phone_directory.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 test/test_sqlite_phone_directory.py diff --git a/src/sqlite_phone_directory.py b/src/sqlite_phone_directory.py index db1f1d3..c69c2d8 100644 --- a/src/sqlite_phone_directory.py +++ b/src/sqlite_phone_directory.py @@ -1,7 +1,6 @@ import datetime import re - import sqlalchemy from sqlalchemy import Boolean, Column, Integer, String, Text, DateTime, ForeignKey from sqlalchemy.ext.declarative import declarative_base @@ -11,25 +10,28 @@ from util import validate_phone_number +# Creating the base class for database mapped objects, +# see: http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/declarative.html Base = declarative_base() +# A helper for binding a database engine for use with declaritive base def bind_engine(engine): Base.metadata.create_all(engine) return sessionmaker(bind=engine, autoflush=True) - class User(Base): + """ User model representing a user with a user_id, name, and phone number """ __tablename__ = 'users' user_id = Column('user_id', Integer, primary_key=True) phone = Column('crm_type', String(100)) name = Column('name', String(100)) - updated_at = Column('updated_at', DateTime, onupdate=datetime.datetime.now, default=datetime.datetime.now, index=True) created_at = Column('created_at', DateTime, default=datetime.datetime.now, index=True) class PhoneHistory(Base): + """ PhoneHistory, we store a PhoneHistory entry for each change """ __tablename__ = 'phone_history' id = Column('id', Integer, index=True, primary_key=True) user_id = Column('user_id', Integer, index=True) @@ -43,7 +45,7 @@ class SqlitePhoneDirectoryActions(PhoneDirectoryActions): Data stored in a sqliteDB. ''' - def __init__(self, database="sqlite:///test.db"): + def __init__(self, database="sqlite:///:memory:"): engine = sqlalchemy.create_engine( database # TODO: better path to database ) @@ -116,15 +118,3 @@ def _add_a_user(self, user_id, phone, name): session.commit() -if __name__ == '__main__': - sq = SqlitePhoneDirectoryActions(database="sqlite:///:memory:") - sq._add_a_user(user_id=1122, phone='1'*10, name='ben') - - # some quick tests - assert sq.user_exists(1122) - assert sq.get_name(1122) == 'ben' - assert sq.get_phone_number(1122) == '1'*10 - sq.update_phone_number(user_id=1122, new_number='2'*10) - assert sq.get_phone_number(1122) == '2'*10 - - diff --git a/test/test_sqlite_phone_directory.py b/test/test_sqlite_phone_directory.py new file mode 100644 index 0000000..ed7e74b --- /dev/null +++ b/test/test_sqlite_phone_directory.py @@ -0,0 +1,28 @@ +import unittest +import sys + +sys.path.append("src") + +from sqlite_phone_directory import SqlitePhoneDirectoryActions + +class TestSqlitePhoneDirectory(unittest.TestCase): + + def setUp(self): + self._db = SqlitePhoneDirectoryActions(database="sqlite:///:memory:") + self._db._add_a_user(user_id=1122, phone='1'*10, name='ben') + + def test_user_exists(self): + assert self._db.user_exists(1122) + + def test_get_name(self): + assert self._db.get_name(1122) == 'ben' + + def test_get_phone_number(self): + assert self._db.get_phone_number(1122) == '1'*10 + + def test_update_phone_number(self): + self._db.update_phone_number(user_id=1122, new_number='2'*10) + assert self._db.get_phone_number(1122) == '2'*10 + + + From 1fbab7b414908f01040987545e8f631852c46844 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 17:58:36 -0700 Subject: [PATCH 05/10] raise --- src/sqlite_phone_directory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sqlite_phone_directory.py b/src/sqlite_phone_directory.py index c69c2d8..cb7680d 100644 --- a/src/sqlite_phone_directory.py +++ b/src/sqlite_phone_directory.py @@ -58,7 +58,7 @@ def _get_user_by_id(self, user_id): session.close() if len(res) != 1: - raise KeyError + raise KeyError("User {} not found".format(user_id)) else: return res[0] From 40c820e236a48a39a7f0e9cd05cd4fb25056cb00 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 17:59:06 -0700 Subject: [PATCH 06/10] close --- src/sqlite_phone_directory.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sqlite_phone_directory.py b/src/sqlite_phone_directory.py index cb7680d..5c46e75 100644 --- a/src/sqlite_phone_directory.py +++ b/src/sqlite_phone_directory.py @@ -103,6 +103,7 @@ def update_phone_number(self, user_id, new_number): session.add(user) session.add(history) session.commit() + session.close() def _add_a_user(self, user_id, phone, name): validate_phone_number(phone) @@ -116,5 +117,6 @@ def _add_a_user(self, user_id, phone, name): session.add(user) session.commit() + session.close() From 6498ecbe61024f12a2b65f888c8b4f79be919782 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 18:00:22 -0700 Subject: [PATCH 07/10] more clear variable name --- src/sqlite_phone_directory.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sqlite_phone_directory.py b/src/sqlite_phone_directory.py index 5c46e75..6090521 100644 --- a/src/sqlite_phone_directory.py +++ b/src/sqlite_phone_directory.py @@ -54,13 +54,13 @@ def __init__(self, database="sqlite:///:memory:"): def _get_user_by_id(self, user_id): session = self._SessionMaker() - res = session.query(User).filter(User.user_id == user_id).all() + users_returned = session.query(User).filter(User.user_id == user_id).all() session.close() - if len(res) != 1: + if len(users_returned) != 1: raise KeyError("User {} not found".format(user_id)) else: - return res[0] + return users_returned[0] def user_exists(self, user_id): ''' From 1f03e6c9b4f67105587e295a72141ef5802361c9 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 18:02:06 -0700 Subject: [PATCH 08/10] cleaned up --- src/sqlite_phone_directory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sqlite_phone_directory.py b/src/sqlite_phone_directory.py index 6090521..7dfed80 100644 --- a/src/sqlite_phone_directory.py +++ b/src/sqlite_phone_directory.py @@ -87,7 +87,7 @@ def update_phone_number(self, user_id, new_number): ''' If user_id exists, update its phone number and archive the old number. If user_id does not exist, throw KeyError - If new phone number is not valid, throw + If new phone number is not valid, throw ValueError ''' user = self._get_user_by_id(user_id) validate_phone_number(new_number) From 9eb455b7709d7dfb41991e92df067012397806d4 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 23:35:22 -0700 Subject: [PATCH 09/10] sqlite cleanup --- README.md | 5 +++++ src/sqlite_phone_directory.py | 7 +++++-- test/test_sqlite_phone_directory.py | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d53db3..de9604d 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,11 @@ It can be quite confusing to get twilio up and running from scratch. There are (Note, we spent a lot of time figuring things out from the ADVANCED_README.md in this directory. So if you run into problems, try looking at that file.) +## Running tests + +Type: ``` nosetests ``` in the root directory to run the test suite. + + ## TODO: 1. Get the basic app and menu working diff --git a/src/sqlite_phone_directory.py b/src/sqlite_phone_directory.py index 7dfed80..4e59286 100644 --- a/src/sqlite_phone_directory.py +++ b/src/sqlite_phone_directory.py @@ -66,8 +66,11 @@ def user_exists(self, user_id): ''' Return true if user_id exists, false if not ''' - user = self._get_user_by_id(user_id) - return (user != None) # truthy + try: + user = self._get_user_by_id(user_id) + return True + except KeyError: + return False def get_phone_number(self, user_id): ''' diff --git a/test/test_sqlite_phone_directory.py b/test/test_sqlite_phone_directory.py index ed7e74b..c702d67 100644 --- a/test/test_sqlite_phone_directory.py +++ b/test/test_sqlite_phone_directory.py @@ -14,6 +14,10 @@ def setUp(self): def test_user_exists(self): assert self._db.user_exists(1122) + + def test_user_does_not_exist(self): + assert not self._db.user_exists(1123) + def test_get_name(self): assert self._db.get_name(1122) == 'ben' @@ -26,3 +30,4 @@ def test_update_phone_number(self): + From 7dffd50bd1537cb49688d58c7f361cc53f121af7 Mon Sep 17 00:00:00 2001 From: Ben Congleton Date: Wed, 8 Oct 2014 23:37:51 -0700 Subject: [PATCH 10/10] whitespace --- test/test_sqlite_phone_directory.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_sqlite_phone_directory.py b/test/test_sqlite_phone_directory.py index c702d67..7c84668 100644 --- a/test/test_sqlite_phone_directory.py +++ b/test/test_sqlite_phone_directory.py @@ -14,7 +14,6 @@ def setUp(self): def test_user_exists(self): assert self._db.user_exists(1122) - def test_user_does_not_exist(self): assert not self._db.user_exists(1123)