This repository has been archived by the owner on Jun 15, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
303 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from datetime import date | ||
|
||
from cornice import Service | ||
from sqlalchemy.exc import DBAPIError, StatementError | ||
|
||
from mamasbm.models import DBSession, Profile | ||
from mamasbm.service import validators | ||
|
||
|
||
message = Service( | ||
name='message', | ||
path='/api/message.json', | ||
description="Public facing messages api" | ||
) | ||
|
||
|
||
#helper method to allow testing (mockable) | ||
def get_ref_date(): | ||
return date.today() | ||
|
||
|
||
def get_message_counter(index, date, max_weeks): | ||
if index: | ||
return int(index) | ||
|
||
today = get_ref_date() | ||
|
||
prenatal = True if today < date else False | ||
print today, date, prenatal, max_weeks, (date - today).days / 7, (max_weeks - 1) - ((today - date).days / 7) | ||
if prenatal: | ||
return (date - today).days / 7 | ||
return (max_weeks - 1) - ((today - date).days / 7) | ||
|
||
|
||
@message.get(validators=validators.validate_get_message) | ||
def get_message(request): | ||
uuid = request.validated['uuid'] | ||
day = int(request.validated['day']) | ||
|
||
index = request.validated.get('index') | ||
date = request.validated.get('date') | ||
|
||
try: | ||
profile = DBSession.query(Profile).get(uuid) | ||
if not profile: | ||
request.errors.add('request', 'uuid', 'Profile not found.') | ||
return | ||
|
||
msg_profile = profile.message_profiles.filter_by(send_day=day).first() | ||
if not msg_profile: | ||
request.errors.add( | ||
'request', 'day', | ||
'This profile doesn\'t have messages for day %s.' % day) | ||
return | ||
|
||
messages = msg_profile.messages.order_by('week') | ||
num_messages = messages.count() | ||
if not num_messages: | ||
request.errors.add( | ||
'request', 'profile', | ||
'No messages available for this profile') | ||
return | ||
|
||
msg_counter = get_message_counter(index, date, num_messages) | ||
if msg_counter >= num_messages or msg_counter < 0: | ||
request.errors.add('request', 'index', 'Index out of bounds') | ||
return | ||
|
||
return messages[msg_counter].to_dict() | ||
except DBAPIError: | ||
request.errors.add( | ||
'db', 'DBAPIError', 'Could not connect to the database.') | ||
except StatementError: | ||
request.errors.add('db', 'ValueError', 'uuid is not valid.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import os | ||
import transaction | ||
import uuid | ||
|
||
from datetime import date | ||
from mock import patch | ||
from pyramid import testing | ||
from unittest import TestCase | ||
from webtest import TestApp | ||
|
||
from mamasbm import main | ||
from mamasbm.models import DBSession, Base, Profile, MessageProfile | ||
from mamasbm.web import factory | ||
from mamasbm.service import api | ||
|
||
|
||
class TestApi(TestCase): | ||
def setUp(self): | ||
self.config = testing.setUp() | ||
connection_string = os.environ.get( | ||
"MAMASBM_TEST_CONNECTION_STRING", "sqlite://") | ||
self.app = TestApp(main({}, **{'sqlalchemy.url': connection_string})) | ||
Base.metadata.create_all() | ||
|
||
def tearDown(self): | ||
DBSession.remove() | ||
testing.tearDown() | ||
Base.metadata.drop_all() | ||
|
||
def test_get_profiles_db_error(self): | ||
# drop all the tables | ||
Base.metadata.drop_all() | ||
|
||
data = {'uuid': uuid.uuid4(), 'day': 1, 'index': 0} | ||
resp = self.app.get('/api/message.json', data, status=400) | ||
self.assertEquals(resp.json['status'], 'error') | ||
self.assertEquals( | ||
resp.json['errors'][0]['description'], | ||
'Could not connect to the database.' | ||
) | ||
|
||
def import_pregnancy_messages(self): | ||
data = {'title': 'Mama basic', 'send_days': [1, 4]} | ||
self.app.put_json('/web/api/profiles.json', data, status=200) | ||
sample_file = os.path.join( | ||
os.path.dirname(__file__), "../web/sample/english_pregnant.csv") | ||
|
||
resp = self.app.get('/web/api/profiles.json', status=200) | ||
profile_uuid = resp.json[0]['uuid'], | ||
factory.build_message_profiles( | ||
'English', sample_file, profile_uuid) | ||
return profile_uuid | ||
|
||
def test_get_message(self): | ||
profile_uuid = self.import_pregnancy_messages() | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'index': 0} | ||
resp = self.app.get('/api/message.json', data) | ||
self.assertTrue( | ||
resp.json['text'].startswith('Congrats on your pregnancy')) | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'index': 5} | ||
resp = self.app.get('/api/message.json', data) | ||
self.assertTrue( | ||
resp.json['text'].startswith('Hello from MAMA. Most women start')) | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'index': 35} | ||
resp = self.app.get('/api/message.json', data) | ||
self.assertTrue( | ||
resp.json['text'].startswith('Keep this SMS as your labour guide')) | ||
|
||
data = {'uuid': profile_uuid, 'day': 4, 'index': 35} | ||
resp = self.app.get('/api/message.json', data) | ||
self.assertTrue(resp.json['text'].startswith('A tip from MAMA')) | ||
|
||
def test_error_messages(self): | ||
profile_uuid = self.import_pregnancy_messages() | ||
|
||
data = {'uuid': str(uuid.uuid4()), 'day': 1, 'index': 0} | ||
resp = self.app.get('/api/message.json', data, status=400) | ||
self.assertEqual( | ||
resp.json['errors'][0]['description'], 'Profile not found.') | ||
|
||
data = {'uuid': profile_uuid, 'day': 5, 'index': 0} | ||
resp = self.app.get('/api/message.json', data, status=400) | ||
self.assertEqual( | ||
resp.json['errors'][0]['description'], | ||
'This profile doesn\'t have messages for day 5.') | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'index': 77} | ||
resp = self.app.get('/api/message.json', data, status=400) | ||
self.assertEqual( | ||
resp.json['errors'][0]['description'], | ||
'Index out of bounds') | ||
|
||
data = {'uuid': 'xxx', 'day': 1, 'index': 77} | ||
resp = self.app.get('/api/message.json', data, status=400) | ||
self.assertEqual( | ||
resp.json['errors'][0]['description'], 'uuid is not valid.') | ||
|
||
# create a blank profile | ||
data = {'title': 'Mama blank profile', 'send_days': [1, 2]} | ||
self.app.put_json('/web/api/profiles.json', data, status=200) | ||
resp = self.app.get('/web/api/profiles.json', status=200) | ||
profile_uuid = resp.json[1]['uuid'] | ||
with transaction.manager: | ||
profile = DBSession.query(Profile).get(profile_uuid) | ||
msg_profile = MessageProfile(name='Blank - Tuesday') | ||
msg_profile.send_day = 1 | ||
profile.message_profiles.append(msg_profile) | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'index': 0} | ||
resp = self.app.get('/api/message.json', data, status=400) | ||
self.assertEqual( | ||
resp.json['errors'][0]['description'], | ||
'No messages available for this profile') | ||
|
||
def test_required_fields(self): | ||
resp = self.app.get('/api/message.json', status=400) | ||
self.assertEqual( | ||
resp.json['errors'][0]['description'], | ||
'uuid is a required field.') | ||
self.assertEqual( | ||
resp.json['errors'][1]['description'], | ||
'day is a required field.') | ||
self.assertEqual( | ||
resp.json['errors'][2]['description'], | ||
'Either `index` or `date` is required.') | ||
|
||
@patch('mamasbm.service.api.get_ref_date') | ||
def test_get_message_by_date(self, mock_get_ref_date): | ||
mock_get_ref_date.return_value = date(2014, 1, 1) | ||
profile_uuid = self.import_pregnancy_messages() | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'date': '20140907'} | ||
resp = self.app.get('/api/message.json', data) | ||
self.assertEqual(resp.json['week'], 40) | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'date': '20141201'} | ||
resp = self.app.get('/api/message.json', data, status=400) | ||
self.assertEqual( | ||
resp.json['errors'][0]['description'], | ||
'Index out of bounds') | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'date': '20140301'} | ||
resp = self.app.get('/api/message.json', data) | ||
self.assertEqual(resp.json['week'], 13) | ||
|
||
data = {'uuid': profile_uuid, 'day': 1, 'date': '20130601'} | ||
resp = self.app.get('/api/message.json', data) | ||
self.assertEqual(resp.json['week'], 10) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from datetime import datetime | ||
|
||
|
||
def update_validated_field(request, data, key): | ||
if key in data and data[key] is not None: | ||
request.validated[key] = data[key] | ||
|
||
|
||
def validate_required_field(request, data, key): | ||
if key in data and data[key] is not None: | ||
update_validated_field(request, data, key) | ||
else: | ||
request.errors.add( | ||
'body', 'RequiredFieldError', '%s is a required field.' % key) | ||
|
||
|
||
def validate_index_or_date_field(request, data): | ||
has_index = has_date = False | ||
|
||
if 'index' in data and data['index'] is not None: | ||
update_validated_field(request, data, 'index') | ||
has_index = True | ||
|
||
if 'date' in data and data['date'] is not None: | ||
try: | ||
date = datetime.strptime(data['date'], "%Y%m%d").date() | ||
request.validated['date'] = date | ||
has_date = True | ||
except ValueError: | ||
request.errors.add( | ||
'body', 'InvalidDateError', | ||
'`date` must be in the format `yyyymmdd`') | ||
return | ||
|
||
if not (has_index or has_date): | ||
request.errors.add( | ||
'body', 'RequiredFieldError', | ||
'Either `index` or `date` is required.') | ||
|
||
|
||
def validate_get_message(request): | ||
validate_required_field(request, request.GET, 'uuid') | ||
validate_required_field(request, request.GET, 'day') | ||
validate_index_or_date_field(request, request.GET) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
mamasbm/migrations/versions/209040d06873_add_send_day_index.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
"""add send_day index | ||
Revision ID: 209040d06873 | ||
Revises: 1c9e829abd8e | ||
Create Date: 2014-07-16 16:13:09.723638 | ||
""" | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = '209040d06873' | ||
down_revision = '1c9e829abd8e' | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
def upgrade(): | ||
### commands auto generated by Alembic - please adjust! ### | ||
op.create_index('message_profile_send_day_index', 'message_profiles', ['send_day'], unique=False) | ||
### end Alembic commands ### | ||
|
||
|
||
def downgrade(): | ||
### commands auto generated by Alembic - please adjust! ### | ||
op.drop_index('message_profile_send_day_index', table_name='message_profiles') | ||
### end Alembic commands ### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters