Repository: namaggarwal/splitwise

Issue:
Add support for get_notifications
https://dev.splitwise.com/#tag/notifications

```
{
  "notifications": [
    {
      "id": 32514315,
      "type": 0,
      "created_at": "2019-08-24T14:15:22Z",
      "created_by": 2,
      "source": {
        "type": "Expense",
        "id": 865077,
        "url": "string"
      },
      "image_url": "https://s3.amazonaws.com/splitwise/uploads/notifications/v2/0-venmo.png",
      "image_shape": "square",
      "content": "<strong>You</strong> paid <strong>Jon H.</strong>.<br><font color=\\\"#5bc5a7\\\">You paid $23.45</font>"
    }
  ]
}
```

Enables use cases like:
https://github.com/sriramsv/custom_component_splitwise/issues/4

Discussion:

Patch:
diff --git a/README.md b/README.md
index 41a155f..c80c8fd 100644
--- a/README.md
+++ b/README.md
@@ -395,6 +395,17 @@ print("content:", comment.getContent())
 print("errors:", errors)
 ```
 
+### Get Notifications
+
+You can use ```getNotifications()``` to get recent Notifications. It returns an array of ```Notification``` object.
+
+```python
+sObj = Splitwise(Config.consumer_key,Config.consumer_secret)
+sObj.setAccessToken(session['access_token'])
+id = 982430660
+notifications = sObj.getNotifications()
+```
+
 ## Objects
 
 ### User
@@ -582,7 +593,27 @@ Methods:
 7. getDeletedAt(id) - Returns datetime at which comment was deleted
 8. getUser() - Returns a ```User``` object containing user details
 
+### Notification
+
+Methods:
+
+1. getId() - Returns the id
+2. getContent() - Returns message
+3. getImageShape() - Returns comment type
+4. getImageType() - Returns relation type of the comment
+5. source - Returns source object
+6. getCreatedAt() - Returns datetime at which notification was created
+7. getCreatedBy() - Returns id of user who created notification
+
+### Source
+
+Used with Notifications.
+
+Methods:
 
+1. getId() - Returns the id
+2. getType() - Returns type. Use in combination with ID to fetch structured data
+3. getUrl() - Returns url
 
 ## Sample Application
 
diff --git a/splitwise/__init__.py b/splitwise/__init__.py
index a3d4ed3..b2e1029 100644
--- a/splitwise/__init__.py
+++ b/splitwise/__init__.py
@@ -25,6 +25,7 @@ from splitwise.group import Group
 from splitwise.category import Category
 from splitwise.expense import Expense
 from splitwise.comment import Comment
+from splitwise.notification import Notification
 from splitwise.error import SplitwiseError
 from requests_oauthlib import OAuth1, OAuth2Session, OAuth2
 from requests import Request, sessions
@@ -103,6 +104,8 @@ class Splitwise(object):
         "api/"+SPLITWISE_VERSION+"/get_comments"
     CREATE_COMMENT_URL = SPLITWISE_BASE_URL + \
         "api/"+SPLITWISE_VERSION+"/create_comment"
+    GET_NOTIFICATIONS_URL = SPLITWISE_BASE_URL + \
+        "api/"+SPLITWISE_VERSION+"/get_notifications"
 
     debug = False
 
@@ -867,3 +870,30 @@ class Splitwise(object):
                 errors = SplitwiseError(content['errors'])
 
         return comment, errors
+
+    # TODO: Implement optional args
+    def getNotifications(self, updated_since=None, limit=None):
+        """
+        Get notifications.
+
+        Args:
+            updated_since(string): Optional. ISO8601 Timestamp.
+            limit(long): Optional. Defaults to 0
+
+        Returns:
+            :obj:`splitwise.notification.Notifications`: Object representing Notifications
+        """
+
+        try:
+            content = self.__makeRequest(Splitwise.GET_NOTIFICATIONS_URL)
+        except SplitwiseNotAllowedException as e:
+            e.setMessage("You are not allowed to fetch notifications")
+            raise
+
+        content = json.loads(content)
+        notifications = []
+        if "notifications" in content:
+            for n in content["notifications"]:
+                notifications.append(Notification(n))
+
+        return notifications
diff --git a/splitwise/notification.py b/splitwise/notification.py
new file mode 100644
index 0000000..156dc32
--- /dev/null
+++ b/splitwise/notification.py
@@ -0,0 +1,125 @@
+class Notification(object):
+    """
+    Contains notification details
+    Attributes:
+        id(long): Notification Id
+        content(string): Notification content
+        type(long): Notification type
+        image_url(string): Url
+        image_shape(string): Shape of image
+        created_at(datetime): Notification creation datetime
+        created_by(long): User Id of the notification creator
+        source: The related thing (ie, Expense)
+    """
+    def __init__(self, data=None):
+        """
+          Args:
+              data(:obj:`json`, optional): JSON object representing comment object
+        """
+
+        if data:
+            self.id = data["id"]
+            self.content = data["content"]
+            self.type = data["type"]
+            self.created_at = data["created_at"]
+            self.created_by = data["created_by"]
+            self.image_shape = data["image_shape"]
+            self.image_url = data["image_url"]
+            self.source = Source(data["source"])
+
+    def getId(self):
+        """ Returns Notification's Id
+        Returns:
+            str: Notification's Id
+        """
+
+        return self.id
+
+    def getContent(self):
+        """ Returns message
+        Returns:
+            str: Content of the notification - text and HTML.
+        """
+
+        return self.content
+
+    def getType(self):
+        """ Returns Notification type
+        Returns:
+            long: Notification type
+        """
+
+        return self.type
+
+    def getCreatedBy(self):
+        """ Returns id who triggered Notification was created
+        Returns:
+            long: Notification's creator id
+        """
+
+        return self.created_by
+
+    def getCreatedAt(self):
+        """ Returns datetime at which notification was created
+        Returns:
+            datetime: Notification's creation date
+        """
+
+        return self.created_at
+
+    def getImageShape(self):
+        """ Returns shape of image
+        Returns:
+            string: Image shape, ie square
+        """
+
+        return self.image_shape
+
+    def getImageUrl(self):
+        """ Returns url of image
+        Returns:
+            string: Image url
+        """
+
+        return self.image_url
+
+
+class Source(object):
+    """
+    Contains  made on an expense
+    Attributes:
+        id(long): Notification Source Id
+        type(long): Notification Source type
+        url(string): Url
+    """
+    def __init__(self, data=None):
+        """
+          Args:
+              data(:obj:`json`, optional): JSON object representing source object
+        """
+
+        if data:
+            self.id = data["id"]
+            self.type = data["type"]
+            self.url = data["url"]
+
+    def getType(self):
+        """ Returns Notification Source's Type
+        Returns:
+            str: Notification Source's Type, ie Expense
+        """
+        return self.type
+
+    def getId(self):
+        """ Returns Notification Source's Id
+        Returns:
+            long: Notification Source's Id
+        """
+        return self.id
+
+    def getUrl(self):
+        """ Returns Notification Source's Url
+        Returns:
+            str: Notification Source's Url
+        """
+        return self.url


Test patch:
diff --git a/tests/test_getNotifications.py b/tests/test_getNotifications.py
new file mode 100755
index 0000000..100f426
--- /dev/null
+++ b/tests/test_getNotifications.py
@@ -0,0 +1,40 @@
+from splitwise import Splitwise
+import unittest
+try:
+    from unittest.mock import patch
+except ImportError:  # Python 2
+    from mock import patch
+
+
+@patch('splitwise.Splitwise._Splitwise__makeRequest')
+class GetNotifications(unittest.TestCase):
+
+    def setUp(self):
+        self.sObj = Splitwise('consumerkey', 'consumersecret')
+
+    def test_getNotifications_success(self, mockMakeRequest):
+        mockMakeRequest.return_value = '{"notifications": [{"id": 32514315,"type": 0,"created_at": "2019-08-24T14:15:22Z","created_by": 2,"source": {"type": "Expense","id": 865077,"url": "string"},"image_url": "https://s3.amazonaws.com/splitwise/uploads/notifications/v2/0-venmo.png","image_shape": "square","content": "<strong>You</strong> paid <strong>Jon H.</strong>.<br><font color=\\\"#5bc5a7\\\">You paid $23.45</font>"}]}'  # noqa: E501
+        notifications = self.sObj.getNotifications()  # TODO: Coverage of updated_after, limit: 0 arguments
+        mockMakeRequest.assert_called_with("https://secure.splitwise.com/api/v3.0/get_notifications")
+
+        self.assertEqual(notifications[0].getId(), 32514315)
+        self.assertEqual(notifications[0].getType(), 0)  # TODO: Constants?
+        self.assertEqual(notifications[0].getCreatedAt(), "2019-08-24T14:15:22Z")
+        self.assertEqual(notifications[0].getCreatedBy(), 2)  # TODO: Users?
+        self.assertEqual(notifications[0].getImageUrl(),
+                         "https://s3.amazonaws.com/splitwise/uploads/notifications/v2/0-venmo.png")
+        self.assertEqual(notifications[0].getImageShape(), "square")
+        self.assertEqual(notifications[0].getContent(),
+                         "<strong>You</strong> paid <strong>Jon H.</strong>.<br><font color=\"#5bc5a7\">You paid $23.45</font>")  # noqa: 501
+
+        self.assertEqual(notifications[0].source.getType(), "Expense")
+        self.assertEqual(notifications[0].source.getId(), 865077)
+        self.assertEqual(notifications[0].source.getUrl(), "string")
+
+    def test_getNotifications_exception(self, mockMakeRequest):
+        mockMakeRequest.side_effect = Exception(
+            "Invalid response %s. Please check your consumer key and secret." % 404)
+        with self.assertRaises(Exception):
+            self.sObj.getNotifications()
+        mockMakeRequest.assert_called_with(
+            "https://secure.splitwise.com/api/v3.0/get_notifications")
diff --git a/tests/test_updateExpense.py b/tests/test_updateExpense.py
index 5e64774..8736f78 100644
--- a/tests/test_updateExpense.py
+++ b/tests/test_updateExpense.py
@@ -93,7 +93,8 @@ class UpdateExpenseTestCase(unittest.TestCase):
         expenseRes, errors = self.sObj.updateExpense(expense)
         mockMakeRequest.assert_called_with(
             "https://secure.splitwise.com/api/v3.0/update_expense/1010906976", "POST",
-            {'cost': '13', 'description': 'Testing', 'details': 'Full details of the expense', 'group_id': 19433671, 'split_equally': True}, files=None)
+            {'cost': '13', 'description': 'Testing', 'details': 'Full details of the expense',
+             'group_id': 19433671, 'split_equally': True}, files=None)
         self.assertIsNone(errors)
         self.assertEqual(expenseRes.getId(), 1010906976)
         self.assertEqual(expenseRes.getGroupId(), 19433671)
@@ -318,4 +319,4 @@ Sorry for the trouble!'
         users.append(user2)
         expense.setUsers(users)
         with self.assertRaises(SplitwiseBadRequestException):
-            expenseRes, errors = self.sObj.updateExpense(expense)
\ No newline at end of file
+            expenseRes, errors = self.sObj.updateExpense(expense)