From 08f32d0ea1dfceb466c0d7990944523f1c253d16 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Wed, 15 Jun 2016 19:46:36 +0300 Subject: move API wrappers to a package --- api.py | 139 ------------------------------------------------------ mutual_friends.py | 9 ++-- track_status.py | 10 ++-- vk/__init__.py | 0 vk/api.py | 76 +++++++++++++++++++++++++++++ vk/error.py | 16 +++++++ vk/user.py | 70 +++++++++++++++++++++++++++ 7 files changed, 173 insertions(+), 147 deletions(-) delete mode 100644 api.py create mode 100644 vk/__init__.py create mode 100644 vk/api.py create mode 100644 vk/error.py create mode 100644 vk/user.py diff --git a/api.py b/api.py deleted file mode 100644 index 0f1333d..0000000 --- a/api.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 2015 Egor Tensin -# This file is licensed under the terms of the MIT License. -# See LICENSE.txt for details. - -import collections -from datetime import datetime -from enum import Enum -import json -import sys -from urllib.error import URLError -import urllib.request - -class Language(Enum): - DEFAULT = None - EN = 'en' - - def __str__(self): - return self.value - -class Method(Enum): - USERS_GET = 'users.get' - FRIENDS_GET = 'friends.get' - - def __str__(self): - return self.value - -class User: - def __init__(self, impl): - self._impl = impl - - def get_uid(self): - return self._impl[self.Field.UID.value] - - def get_first_name(self): - return self._impl[self.Field.FIRST_NAME.value] - - def get_last_name(self): - return self._impl[self.Field.LAST_NAME.value] - - def has_last_name(self): - return self.Field.LAST_NAME.value in self._impl and self.get_last_name() - - def has_screen_name(self): - return self.Field.SCREEN_NAME.value in self._impl - - def get_screen_name(self): - if self.has_screen_name(): - return self._impl[self.Field.SCREEN_NAME.value] - else: - return 'id' + str(self.get_uid()) - - def is_online(self): - return self._impl[self.Field.ONLINE.value] - - def get_last_seen(self): - return datetime.fromtimestamp(self._impl[self.Field.LAST_SEEN.value]['time']) - - def __str__(self): - return repr(self._impl) - - def __hash__(self): - return hash(self.get_uid()) - - def __eq__(self, other): - return self.get_uid() == other.get_uid() - - class Field(Enum): - UID = 'uid' - FIRST_NAME = 'first_name' - LAST_NAME = 'last_name' - SCREEN_NAME = 'screen_name' - ONLINE = 'online' - LAST_SEEN = 'last_seen' - - def __str__(self): - return self.value - -class APIError(RuntimeError): - pass - -class InvalidAPIResponseError(APIError): - def __init__(self, response): - self.response = response - - def __str__(self): - return str(self.response) - -class APIConnectionError(APIError): - pass - -class API: - def __init__(self, lang=Language.DEFAULT): - self.lang = lang - - def _lang_is_specified(self): - return self.lang != Language.DEFAULT - - def _format_method_params(self, **kwargs): - params = '&'.join(map(lambda k: '{}={}'.format(k, kwargs[k]), kwargs)) - if self._lang_is_specified(): - if params: - params += '&' - params += 'lang={}'.format(self.lang) - return params - - def _build_method_url(self, method, **kwargs): - return 'https://api.vk.com/method/{}?{}'.format( - method, self._format_method_params(**kwargs)) - - def _call_method(self, method, **kwargs): - url = self._build_method_url(method, **kwargs) - try: - with urllib.request.urlopen(url) as request: - response = json.loads(request.read().decode()) - if 'response' not in response: - raise InvalidAPIResponseError(response) - return response['response'] - except URLError: - raise APIConnectionError() - - @staticmethod - def _format_param_values(xs): - if isinstance(xs, str): - return xs - if isinstance(xs, collections.Iterable): - return ','.join(map(str, xs)) - return str(xs) - - def users_get(self, user_ids, fields=()): - return map(User, self._call_method( - Method.USERS_GET, - user_ids=self._format_param_values(user_ids), - fields=self._format_param_values(fields))) - - def friends_get(self, user_id, fields=()): - return map(User, self._call_method( - Method.FRIENDS_GET, - user_id=str(user_id), - fields=self._format_param_values(fields))) diff --git a/mutual_friends.py b/mutual_friends.py index 20bb32a..2f32038 100644 --- a/mutual_friends.py +++ b/mutual_friends.py @@ -2,10 +2,11 @@ # This file is licensed under the terms of the MIT License. # See LICENSE.txt for details. -from api import * +import vk.api +import vk.user def query_friends(api, user): - return api.friends_get(user.get_uid(), fields=User.Field.SCREEN_NAME) + return api.friends_get(user.get_uid(), fields=vk.user.Field.SCREEN_NAME) def format_user(user): if user.has_last_name(): @@ -23,8 +24,8 @@ if __name__ == '__main__': help='user IDs or "screen names"') args = parser.parse_args() - api = API(Language.EN) - users = api.users_get(args.uids, fields=User.Field.SCREEN_NAME) + api = vk.api.API(vk.api.Language.EN) + users = api.users_get(args.uids, fields=vk.user.Field.SCREEN_NAME) friend_lists = map(lambda user: frozenset(query_friends(api, user)), users) mutual_friends = frozenset.intersection(*friend_lists) diff --git a/track_status.py b/track_status.py index 78c0dce..b8b747c 100644 --- a/track_status.py +++ b/track_status.py @@ -4,7 +4,9 @@ import logging, time -from api import * +import vk.api +import vk.error +import vk.user def format_user(user): if user.has_last_name(): @@ -52,7 +54,7 @@ def print_status_update(user): else: user_went_offline(user) -USER_FIELDS = User.Field.ONLINE, User.Field.LAST_SEEN +USER_FIELDS = vk.user.Field.ONLINE, vk.user.Field.LAST_SEEN def update_status(api, uids): return {user.get_uid(): user for user in api.users_get(uids, USER_FIELDS)} @@ -67,7 +69,7 @@ def loop_update_status(api, uids, timeout=DEFAULT_TIMEOUT): time.sleep(timeout) try: updated_users = update_status(api, uids) - except APIConnectionError: + except vk.error.ConnectionError: continue for uid in updated_users: if users[uid].is_online() != updated_users[uid].is_online(): @@ -101,7 +103,7 @@ if __name__ == '__main__': level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') - api = API(Language.EN) + api = vk.api.API(vk.api.Language.EN) try: loop_update_status(api, args.uids, timeout=args.timeout) diff --git a/vk/__init__.py b/vk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vk/api.py b/vk/api.py new file mode 100644 index 0000000..192b707 --- /dev/null +++ b/vk/api.py @@ -0,0 +1,76 @@ +# Copyright 2015 Egor Tensin +# This file is licensed under the terms of the MIT License. +# See LICENSE.txt for details. + +from collections import Iterable +from enum import Enum +import json +from urllib.error import URLError +import urllib.request + +import vk.error +from vk.user import User + +class Language(Enum): + DEFAULT = None + EN = 'en' + + def __str__(self): + return self.value + +class Method(Enum): + USERS_GET = 'users.get' + FRIENDS_GET = 'friends.get' + + def __str__(self): + return self.value + +class API: + def __init__(self, lang=Language.DEFAULT): + self.lang = lang + + def _lang_is_specified(self): + return self.lang != Language.DEFAULT + + def _format_method_params(self, **kwargs): + params = '&'.join(map(lambda k: '{}={}'.format(k, kwargs[k]), kwargs)) + if self._lang_is_specified(): + if params: + params += '&' + params += 'lang={}'.format(self.lang) + return params + + def _build_method_url(self, method, **kwargs): + return 'https://api.vk.com/method/{}?{}'.format( + method, self._format_method_params(**kwargs)) + + def _call_method(self, method, **kwargs): + url = self._build_method_url(method, **kwargs) + try: + with urllib.request.urlopen(url) as request: + response = json.loads(request.read().decode()) + if 'response' not in response: + raise vk.error.InvalidResponseError(response) + return response['response'] + except URLError: + raise vk.error.ConnectionError() + + @staticmethod + def _format_param_values(xs): + if isinstance(xs, str): + return xs + if isinstance(xs, Iterable): + return ','.join(map(str, xs)) + return str(xs) + + def users_get(self, user_ids, fields=()): + return map(User, self._call_method( + Method.USERS_GET, + user_ids=self._format_param_values(user_ids), + fields=self._format_param_values(fields))) + + def friends_get(self, user_id, fields=()): + return map(User, self._call_method( + Method.FRIENDS_GET, + user_id=str(user_id), + fields=self._format_param_values(fields))) diff --git a/vk/error.py b/vk/error.py new file mode 100644 index 0000000..b80db4e --- /dev/null +++ b/vk/error.py @@ -0,0 +1,16 @@ +# Copyright 2016 Egor Tensin +# This file is licensed under the terms of the MIT License. +# See LICENSE.txt for details. + +class APIError(RuntimeError): + pass + +class InvalidResponseError(APIError): + def __init__(self, response): + self.response = response + + def __str__(self): + return str(self.response) + +class ConnectionError(APIError): + pass diff --git a/vk/user.py b/vk/user.py new file mode 100644 index 0000000..397d902 --- /dev/null +++ b/vk/user.py @@ -0,0 +1,70 @@ +# Copyright 2016 Egor Tensin +# This file is licensed under the terms of the MIT License. +# See LICENSE.txt for details. + +from datetime import datetime +from enum import Enum + +class Field(Enum): + UID = 'uid' + FIRST_NAME = 'first_name' + LAST_NAME = 'last_name' + SCREEN_NAME = 'screen_name' + ONLINE = 'online' + LAST_SEEN = 'last_seen' + + def __str__(self): + return self.value + +class User: + def __init__(self, impl): + self._impl = impl + + def __iter__(self): + return iter(self._impl) + + def __getitem__(self, field): + if isinstance(field, Field): + field = field.value + return self._impl[field] + + def __contains__(self, field): + if isinstance(field, Field): + field = field.value + return field in self._impl + + def get_uid(self): + return self._impl[Field.UID.value] + + def get_first_name(self): + return self._impl[Field.FIRST_NAME.value] + + def get_last_name(self): + return self._impl[Field.LAST_NAME.value] + + def has_last_name(self): + return Field.LAST_NAME.value in self._impl and self.get_last_name() + + def has_screen_name(self): + return Field.SCREEN_NAME.value in self._impl + + def get_screen_name(self): + if self.has_screen_name(): + return self._impl[Field.SCREEN_NAME.value] + else: + return 'id' + str(self.get_uid()) + + def is_online(self): + return self._impl[Field.ONLINE.value] + + def get_last_seen(self): + return datetime.fromtimestamp(self._impl[Field.LAST_SEEN.value]['time']) + + def __str__(self): + return repr(self._impl) + + def __hash__(self): + return hash(self.get_uid()) + + def __eq__(self, other): + return self.get_uid() == other.get_uid() -- cgit v1.2.3