Skip to content

Commit

Permalink
added paging functionality to the user_api (get_user_transactions, ge…
Browse files Browse the repository at this point in the history
…t_user_friends_list, and others).
  • Loading branch information
mmohades committed Jan 8, 2021
1 parent eb74da7 commit a80660f
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 86 deletions.
3 changes: 2 additions & 1 deletion venmo_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .models.transaction import Transaction
from .models.payment import Payment, PaymentStatus
from .models.payment_method import (PaymentMethod, PaymentRole, PaymentPrivacy)
from .models.page import Page
from .utils.api_util import (deserialize, wrap_callback, warn, get_user_id, confirm, validate_access_token)
from .utils.api_client import ApiClient
from .apis.auth_api import AuthenticationApi
Expand All @@ -19,7 +20,7 @@
"GeneralPaymentError",
"get_phone_model_from_json", "random_device_id", "string_to_timestamp",
"deserialize", "wrap_callback", "warn", "confirm", "get_user_id", "validate_access_token",
"JSONSchema", "User", "Transaction", "Payment", "PaymentStatus", "PaymentMethod", "PaymentRole",
"JSONSchema", "User", "Transaction", "Payment", "PaymentStatus", "PaymentMethod", "PaymentRole", "Page",
"BaseModel", "PaymentPrivacy", "ApiClient", "AuthenticationApi", "UserApi", "PaymentApi",
"Client"
]
4 changes: 1 addition & 3 deletions venmo_api/apis/auth_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from venmo_api import random_device_id, warn, confirm
from venmo_api import AuthenticationFailedError
from venmo_api import ApiClient
from venmo_api import random_device_id, warn, confirm, AuthenticationFailedError, ApiClient


class AuthenticationApi(object):
Expand Down
5 changes: 2 additions & 3 deletions venmo_api/apis/payment_api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from venmo_api import ApiClient, Payment, ArgumentMissingError, AlreadyRemindedPaymentError, \
NoPendingPaymentToUpdateError, NoPaymentMethodFoundError, NotEnoughBalanceError, GeneralPaymentError
from venmo_api import User, PaymentMethod, PaymentRole, PaymentPrivacy
from venmo_api import deserialize, wrap_callback, get_user_id
NoPendingPaymentToUpdateError, NoPaymentMethodFoundError, NotEnoughBalanceError, GeneralPaymentError, \
User, PaymentMethod, PaymentRole, PaymentPrivacy, deserialize, wrap_callback, get_user_id
from typing import List, Union


Expand Down
99 changes: 37 additions & 62 deletions venmo_api/apis/user_api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import re
from venmo_api import User
from venmo_api import Transaction
from venmo_api import InvalidArgumentError
from venmo_api import deserialize, wrap_callback, get_user_id
from venmo_api import User, Page, Transaction, deserialize, wrap_callback, get_user_id
from typing import List, Union


Expand Down Expand Up @@ -38,13 +34,13 @@ def get_my_profile(self, callback=None, force_update=False) -> Union[User, None]
return self.__profile

def search_for_users(self, query: str, callback=None,
page: int = 1, count: int = 50, username=False) -> Union[List[User], None]:
offset: int = 0, limit: int = 50, username=False) -> Union[List[User], None]:
"""
search for [query] in users
:param query:
:param callback:
:param page:
:param count:
:param offset:
:param limit:
:param username: default: False; Pass True if search is by username
:return users_list: <list> A list of <User> objects or empty
"""
Expand All @@ -53,22 +49,23 @@ def search_for_users(self, query: str, callback=None,
wrapped_callback = wrap_callback(callback=callback,
data_type=User)

offset_limit_params = self.__prepare_offset_limit_params(page_number=page,
max_number_per_page=50,
max_offset=9900,
count=count)
params = {'query': query}
params = {'query': query, 'limit': limit, 'offset': offset}
# update params for querying by username
if username or '@' in query:
params = {'query': query.replace('@', ''), 'type': 'username'}
params.update(offset_limit_params)
params.update({'query': query.replace('@', ''), 'type': 'username'})

response = self.__api_client.call_api(resource_path=resource_path, params=params,
method='GET', callback=wrapped_callback)
# Return None if threaded
if callback:
return

return deserialize(response=response, data_type=User)
return deserialize(response=response,
data_type=User).set_method(method=self.search_for_users,
kwargs={"query": query, "limit": limit},
current_offset=offset
)


def get_user(self, user_id: str, callback=None) -> Union[User, None]:
"""
Expand Down Expand Up @@ -109,17 +106,14 @@ def get_user_by_username(self, username: str) -> Union[User, None]:
def get_user_friends_list(self, user_id: str = None,
user: User = None,
callback=None,
page: int = 1,
count: int = 1337) -> Union[User, None]:
offset: int = 0,
limit: int = 3337) -> Union[Page, None]:
"""
Get ([user_id]'s or [user]'s) friends list as a list of <User>s
:return users_list: <list> A list of <User> objects or empty
"""
user_id = get_user_id(user, user_id)
params = self.__prepare_offset_limit_params(page_number=page,
max_number_per_page=1337,
max_offset=9999999999999999999,
count=count)
params = {"limit": limit, "offset": offset}

# Prepare the request
resource_path = f'/users/{user_id}/friends'
Expand All @@ -133,24 +127,29 @@ def get_user_friends_list(self, user_id: str = None,
if callback:
return

return deserialize(response=response, data_type=User)
return deserialize(
response=response,
data_type=User).set_method(method=self.get_user_friends_list,
kwargs={"user_id": user_id, "limit": limit},
current_offset=offset
)

def get_user_transactions(self, user_id: str = None, user: User = None,
callback=None,
count: int = 50,
before_id=None) -> Union[Transaction, None]:
limit: int = 50,
before_id=None) -> Union[Page, None]:
"""
Get ([user_id]'s or [user]'s) transactions visible to yourself as a list of <Transaction>s
:param user_id:
:param user:
:param callback:
:param count:
:param limit:
:param before_id:
:return:
"""
user_id = get_user_id(user, user_id)

params = {'limit': count}
params = {'limit': limit}
if before_id:
params['before_id'] = before_id

Expand All @@ -167,15 +166,17 @@ def get_user_transactions(self, user_id: str = None, user: User = None,
if callback:
return

return deserialize(response=response, data_type=Transaction)
return deserialize(response=response,
data_type=Transaction).set_method(method=self.get_user_transactions,
kwargs={"user_id": user_id})

def get_transaction_between_two_users(self, user_id_one: str = None,
user_id_two: str = None,
user_one: User = None,
user_two: User = None,
callback=None,
count: int = 50,
before_id=None) -> Union[Transaction, None]:
limit: int = 50,
before_id=None) -> Union[Page, None]:
"""
Get the transactions between two users. Note that user_one must be the owner of the access token.
Otherwise it raises an unauthorized error.
Expand All @@ -184,14 +185,14 @@ def get_transaction_between_two_users(self, user_id_one: str = None,
:param user_one:
:param user_two:
:param callback:
:param count:
:param limit:
:param before_id:
:return:
"""
user_id_one = get_user_id(user_one, user_id_one)
user_id_two = get_user_id(user_two, user_id_two)

params = {'limit': count}
params = {'limit': limit}
if before_id:
params['before_id'] = before_id

Expand All @@ -208,33 +209,7 @@ def get_transaction_between_two_users(self, user_id_one: str = None,
if callback:
return

return deserialize(response=response, data_type=Transaction)

@staticmethod
def __prepare_offset_limit_params(page_number, max_number_per_page, max_offset, count):
"""Get the offset for going to that page."""
max_page = max_offset//max_number_per_page + 1
if page_number == 0 or page_number > max_page:
raise InvalidArgumentError(argument_name="'page number'",
reason=f"Page number must be an int bigger than 1 smaller than {max_page}.")

offset = (page_number - 1) * max_number_per_page

return {'offset': offset, 'limit': count}

@staticmethod
def __last_transaction_id(body):
pagination = body.get("pagination")
if not pagination:
return

next_link = pagination.get("next")
pattern = r'before_id=(\d*)'
if not next_link:
return

match = re.search(pattern, next_link)
if not match.groups():
return

return {"before_id": match.groups()[0]}
return deserialize(response=response,
data_type=Transaction).set_method(method=self.get_transaction_between_two_users,
kwargs={"user_id_one": user_id_one,
"user_id_two": user_id_two})
1 change: 0 additions & 1 deletion venmo_api/models/json_schema.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

class JSONSchema:

@staticmethod
Expand Down
36 changes: 36 additions & 0 deletions venmo_api/models/page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class Page(list):

def __init__(self):
super().__init__()
self.method = None
self.kwargs = {}
self.current_offset = -1

def set_method(self, method, kwargs, current_offset=-1):
"""
set the method and kwargs for paging. current_offset is provided for routes that require offset.
:param method:
:param kwargs:
:param current_offset:
:return:
"""
self.method = method
self.kwargs = kwargs
self.current_offset = current_offset
return self

def get_next_page(self):
"""
Get the next page of data. Returns empty Page if none exists
:return:
"""
if not self.kwargs or not self.method or len(self) == 0:
return self.__init__()

# use offset or before_id for paging, depending on the route
if self.current_offset > -1:
self.kwargs['offset'] = self.current_offset + len(self)
else:
self.kwargs['before_id'] = self[-1].id

return self.method(**self.kwargs)
4 changes: 1 addition & 3 deletions venmo_api/models/payment.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from venmo_api import string_to_timestamp, User, BaseModel, JSONSchema
from enum import Enum

from venmo_api import string_to_timestamp, User, BaseModel
from venmo_api import JSONSchema


class Payment(BaseModel):

Expand Down
2 changes: 1 addition & 1 deletion venmo_api/models/payment_method.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from venmo_api import JSONSchema, BaseModel
from typing import Dict
from enum import Enum
from venmo_api import JSONSchema, BaseModel
import logging


Expand Down
5 changes: 1 addition & 4 deletions venmo_api/models/transaction.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from venmo_api import string_to_timestamp, BaseModel, User, get_phone_model_from_json, JSONSchema
from enum import Enum
from venmo_api import string_to_timestamp, BaseModel
from venmo_api import User
from venmo_api import get_phone_model_from_json
from venmo_api import JSONSchema


class Transaction(BaseModel):
Expand Down
3 changes: 1 addition & 2 deletions venmo_api/models/user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from venmo_api import string_to_timestamp, BaseModel
from venmo_api import JSONSchema
from venmo_api import string_to_timestamp, BaseModel, JSONSchema


class User(BaseModel):
Expand Down
2 changes: 1 addition & 1 deletion venmo_api/utils/api_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from venmo_api import ResourceNotFoundError, InvalidHttpMethodError, HttpCodeError, validate_access_token
from json import JSONDecodeError
from typing import List
from venmo_api import ResourceNotFoundError, InvalidHttpMethodError, HttpCodeError, validate_access_token
import requests
import threading

Expand Down
10 changes: 5 additions & 5 deletions venmo_api/utils/api_util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from venmo_api import ArgumentMissingError, User, Page
from enum import Enum
from typing import Dict, List
from venmo_api import ArgumentMissingError, User
import re


Expand All @@ -24,7 +24,7 @@ def deserialize(response: Dict, data_type, nested_response: List[str] = None):
:param response: <Dict>
:param data_type: <Generic>
:param nested_response: <List[str]> Optional. Loop through the body
:return:
:return: a single <Object> or a <Page> of objects (Objects can be User/Transaction/Payment/PaymentMethod)
"""

body = response.get('body')
Expand Down Expand Up @@ -70,10 +70,10 @@ def wrapper(response):
def __get_objs_from_json_list(json_list, data_type):
"""Process JSON for User/Transaction
:param json_list: <list> a list of objs
:param data_type: <class> Either User/Transaction
:return: <list> a list of <User>
:param data_type: <class> User/Transaction/Payment/PaymentMethod
:return: <page>
"""
result = []
result = Page()
for obj in json_list:
data_obj = data_type.from_json(obj)
if not data_obj:
Expand Down

0 comments on commit a80660f

Please sign in to comment.