From 1b7d323665c6f2ff06b8ebd132ae2a9e3af54b75 Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 16:35:38 +0300 Subject: [PATCH 1/3] s --- .idea/Chzh.iml | 14 ++++++++++++++ .idea/misc.xml | 4 ++++ .idea/workspace.xml | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 .idea/Chzh.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/Chzh.iml b/.idea/Chzh.iml new file mode 100644 index 0000000..8e5446a --- /dev/null +++ b/.idea/Chzh.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..cc2518d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..73eaf47 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + 1673962365428 + + + + \ No newline at end of file From ec56f2194b40ed51701642181b9301b3314c2a5d Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 16:51:31 +0300 Subject: [PATCH 2/3] s --- .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/vcs.xml | 6 ++ .idea/workspace.xml | 23 ++++++ homework05/requirements.txt | 23 +++--- homework05/research/age.py | 19 ++++- homework05/research/network.py | 14 ++-- homework05/research/topic_modeling.py | 1 - homework05/tests/tests_api/test_friends.py | 1 - homework05/tests/tests_api/test_session.py | 1 - homework05/tests/tests_api/test_wall.py | 1 - homework05/tests/tests_research/test_age.py | 1 - .../tests/tests_research/test_network.py | 1 - homework05/vkapi/config.py | 2 +- homework05/vkapi/friends.py | 79 +++++++++++++++++-- homework05/vkapi/session.py | 19 ++++- homework05/vkapi/wall.py | 42 +++++++++- main.py | 16 ++++ 17 files changed, 216 insertions(+), 39 deletions(-) create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/vcs.xml create mode 100644 main.py diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 73eaf47..52bff97 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -22,6 +22,29 @@ + + + + diff --git a/homework05/requirements.txt b/homework05/requirements.txt index dabffee..644c5ef 100644 --- a/homework05/requirements.txt +++ b/homework05/requirements.txt @@ -1,11 +1,12 @@ -responses==0.12.1 -requests==2.24.0 -httpretty==1.0.2 -tqdm==4.52.0 -networkx==2.5 -pandas==1.1.4 -matplotlib==3.3.3 -python-louvain==0.14 -gensim==3.8.3 -textacy==0.10.1 -pyLDAvis==2.1.2 +responses +requests +httpretty +tqdm +networkx +pandas +matplotlib +python-louvain +gensim +textacy +pyLDAvis +types-requests \ No newline at end of file diff --git a/homework05/research/age.py b/homework05/research/age.py index 492ae28..6fb9bc2 100644 --- a/homework05/research/age.py +++ b/homework05/research/age.py @@ -8,10 +8,23 @@ def age_predict(user_id: int) -> tp.Optional[float]: """ Наивный прогноз возраста пользователя по возрасту его друзей. - Возраст считается как медиана среди возраста всех друзей пользователя - :param user_id: Идентификатор пользователя. :return: Медианный возраст пользователя. """ - pass + friends = get_friends(user_id, fields=["bdate"]).items + ages = [] + for friend in friends: + if "bdate" in friend: + bdate = friend["bdate"].split(".") + if len(bdate) == 3: + age = dt.datetime.now().year - int(bdate[2]) + if dt.datetime.now().month < int(bdate[1]) and dt.datetime.now().day < int( + bdate[0] + ): + age -= 1 + ages.append(age) + + if len(ages) == 0: + return None + return statistics.median(ages) diff --git a/homework05/research/network.py b/homework05/research/network.py index 6b6db7c..226c3f7 100644 --- a/homework05/research/network.py +++ b/homework05/research/network.py @@ -5,20 +5,24 @@ import matplotlib.pyplot as plt import networkx as nx import pandas as pd - -from vkapi.friends import get_friends, get_mutual +from vkapi.friends import get_friends, get_mutual_with_class def ego_network( - user_id: tp.Optional[int] = None, friends: tp.Optional[tp.List[int]] = None + friends: tp.List[int], + user_id: tp.Optional[int] = None, ) -> tp.List[tp.Tuple[int, int]]: """ Построить эгоцентричный граф друзей. - :param user_id: Идентификатор пользователя, для которого строится граф друзей. :param friends: Идентификаторы друзей, между которыми устанавливаются связи. """ - pass + net = [] + mutual = get_mutual_with_class(source_uid=friends[0], target_uids=friends) + for friend in mutual: + for uid in friend["common_friends"]: + net.append((friend["id"], uid)) + return net def plot_ego_network(net: tp.List[tp.Tuple[int, int]]) -> None: diff --git a/homework05/research/topic_modeling.py b/homework05/research/topic_modeling.py index 7be95e5..d46c011 100644 --- a/homework05/research/topic_modeling.py +++ b/homework05/research/topic_modeling.py @@ -3,7 +3,6 @@ from gensim.corpora import Dictionary from textacy import preprocessing from tqdm import tqdm - from vkapi.wall import get_wall_execute diff --git a/homework05/tests/tests_api/test_friends.py b/homework05/tests/tests_api/test_friends.py index d5493aa..96133f2 100644 --- a/homework05/tests/tests_api/test_friends.py +++ b/homework05/tests/tests_api/test_friends.py @@ -3,7 +3,6 @@ import unittest import responses - from vkapi.friends import FriendsResponse, get_friends, get_mutual diff --git a/homework05/tests/tests_api/test_session.py b/homework05/tests/tests_api/test_session.py index 895028b..9eebccb 100644 --- a/homework05/tests/tests_api/test_session.py +++ b/homework05/tests/tests_api/test_session.py @@ -4,7 +4,6 @@ import httpretty import responses from requests.exceptions import ConnectionError, HTTPError, ReadTimeout, RetryError - from vkapi.session import Session diff --git a/homework05/tests/tests_api/test_wall.py b/homework05/tests/tests_api/test_wall.py index 6a56a8d..8ab2c26 100644 --- a/homework05/tests/tests_api/test_wall.py +++ b/homework05/tests/tests_api/test_wall.py @@ -5,7 +5,6 @@ import pandas as pd import responses - from vkapi.wall import get_wall_execute diff --git a/homework05/tests/tests_research/test_age.py b/homework05/tests/tests_research/test_age.py index 404fd0a..53b941c 100644 --- a/homework05/tests/tests_research/test_age.py +++ b/homework05/tests/tests_research/test_age.py @@ -2,7 +2,6 @@ import unittest import responses - from research.age import age_predict diff --git a/homework05/tests/tests_research/test_network.py b/homework05/tests/tests_research/test_network.py index 3608565..e375473 100644 --- a/homework05/tests/tests_research/test_network.py +++ b/homework05/tests/tests_research/test_network.py @@ -2,7 +2,6 @@ import unittest import responses - from research.network import ego_network diff --git a/homework05/vkapi/config.py b/homework05/vkapi/config.py index 8459497..cfb4193 100644 --- a/homework05/vkapi/config.py +++ b/homework05/vkapi/config.py @@ -2,6 +2,6 @@ VK_CONFIG = { "domain": "https://api.vk.com/method", - "access_token": "", + "access_token": "vk1.a.jEhY_ByNYk_8nfSc4c3IOQM155w7sKxDJfCulh0P23TdsbWTHzBhNWIauYLefFi8TeKrKKt0f2eQNEfpG-YT7mrxH-BcOpjPz1rXowjzhHe1WJ70b1uN_SCPyhZ9-PihsVKzBOvcfJUjPvjHyBdwYNCyCzUIIYjikZ6wyoeHLkrqa6I0bYrsALHQwxPp05ag", "version": "5.126", } diff --git a/homework05/vkapi/friends.py b/homework05/vkapi/friends.py index dad6a5c..9aaad65 100644 --- a/homework05/vkapi/friends.py +++ b/homework05/vkapi/friends.py @@ -12,7 +12,7 @@ @dataclasses.dataclass(frozen=True) class FriendsResponse: count: int - items: tp.Union[tp.List[int], tp.List[tp.Dict[str, tp.Any]]] + items: tp.List[tp.Dict[str, tp.Any]] def get_friends( @@ -21,14 +21,23 @@ def get_friends( """ Получить список идентификаторов друзей пользователя или расширенную информацию о друзьях пользователя (при использовании параметра fields). - :param user_id: Идентификатор пользователя, список друзей для которого нужно получить. :param count: Количество друзей, которое нужно вернуть. :param offset: Смещение, необходимое для выборки определенного подмножества друзей. :param fields: Список полей, которые нужно получить для каждого пользователя. :return: Список идентификаторов друзей пользователя или список пользователей. """ - pass + friends = session.get( + "friends.get", + user_id=user_id, + count=count, + offset=offset, + fields=fields, + v=config.VK_CONFIG["version"], + access_token=config.VK_CONFIG["access_token"], + ).json()["response"] + + return FriendsResponse(friends["count"], friends["items"]) class MutualFriends(tp.TypedDict): @@ -40,7 +49,7 @@ class MutualFriends(tp.TypedDict): def get_mutual( source_uid: tp.Optional[int] = None, target_uid: tp.Optional[int] = None, - target_uids: tp.Optional[tp.List[int]] = None, + target_uids: tp.Optional[tp.List[tp.Optional[int]]] = None, order: str = "", count: tp.Optional[int] = None, offset: int = 0, @@ -48,7 +57,50 @@ def get_mutual( ) -> tp.Union[tp.List[int], tp.List[MutualFriends]]: """ Получить список идентификаторов общих друзей между парой пользователей. + :param source_uid: Идентификатор пользователя, чьи друзья пересекаются с друзьями пользователя с идентификатором target_uid. + :param target_uid: Идентификатор пользователя, с которым необходимо искать общих друзей. + :param target_uids: Cписок идентификаторов пользователей, с которыми необходимо искать общих друзей. + :param order: Порядок, в котором нужно вернуть список общих друзей. + :param count: Количество общих друзей, которое нужно вернуть. + :param offset: Смещение, необходимое для выборки определенного подмножества общих друзей. + :param progress: Callback для отображения прогресса. + """ + if target_uids is None: + target_uids = [target_uid] + + result = [] + + for i in range(0, len(target_uids), 100): + result += session.get( + "friends.getMutual", + source_uid=source_uid, + target_uids=target_uids, + order=order, + count=count, + offset=offset + i, + v=config.VK_CONFIG["version"], + access_token=config.VK_CONFIG["access_token"], + ).json()["response"] + + if i % 200 == 0: + time.sleep(1) + if target_uid is not None: + return result[0]["common_friends"] + + return result + + +def get_mutual_with_class( + target_uids: tp.List[int], + source_uid: tp.Optional[int] = None, + order: str = "", + count: tp.Optional[int] = None, + offset: int = 0, + progress=None, +) -> tp.List[MutualFriends]: + """ + Получить список идентификаторов общих друзей между парой пользователей. :param source_uid: Идентификатор пользователя, чьи друзья пересекаются с друзьями пользователя с идентификатором target_uid. :param target_uid: Идентификатор пользователя, с которым необходимо искать общих друзей. :param target_uids: Cписок идентификаторов пользователей, с которыми необходимо искать общих друзей. @@ -57,4 +109,21 @@ def get_mutual( :param offset: Смещение, необходимое для выборки определенного подмножества общих друзей. :param progress: Callback для отображения прогресса. """ - pass + result = [] + + for i in range(0, len(target_uids), 100): + result += session.get( + "friends.getMutual", + source_uid=source_uid, + target_uids=target_uids, + order=order, + count=count, + offset=offset + i, + v=config.VK_CONFIG["version"], + access_token=config.VK_CONFIG["access_token"], + ).json()["response"] + + if i % 200 == 0: + time.sleep(1) + + return result diff --git a/homework05/vkapi/session.py b/homework05/vkapi/session.py index 0643389..aa2ce6e 100644 --- a/homework05/vkapi/session.py +++ b/homework05/vkapi/session.py @@ -8,7 +8,6 @@ class Session: """ Сессия. - :param base_url: Базовый адрес, на который будут выполняться запросы. :param timeout: Максимальное время ожидания ответа от сервера. :param max_retries: Максимальное число повторных запросов. @@ -22,10 +21,22 @@ def __init__( max_retries: int = 3, backoff_factor: float = 0.3, ) -> None: - pass + self._session = requests.Session() + self._session.mount( + "https://", + HTTPAdapter( + max_retries=Retry( + total=max_retries, + backoff_factor=backoff_factor, + status_forcelist=[429, 500, 502, 503, 504], + ) + ), + ) + self._base_url = base_url + self._timeout = timeout def get(self, url: str, *args: tp.Any, **kwargs: tp.Any) -> requests.Response: - pass + return self._session.get(self._base_url + "/" + url, params=kwargs, timeout=self._timeout) def post(self, url: str, *args: tp.Any, **kwargs: tp.Any) -> requests.Response: - pass + return self._session.post(self._base_url + "/" + url, data=kwargs) diff --git a/homework05/vkapi/wall.py b/homework05/vkapi/wall.py index a045c6c..25d118a 100644 --- a/homework05/vkapi/wall.py +++ b/homework05/vkapi/wall.py @@ -1,3 +1,4 @@ +import math import textwrap import time import typing as tp @@ -5,10 +6,11 @@ import pandas as pd from pandas import json_normalize - -from vkapi import config, session from vkapi.exceptions import APIError +from .config import VK_CONFIG +from .session import Session + def get_posts_2500( owner_id: str = "", @@ -20,7 +22,7 @@ def get_posts_2500( extended: int = 0, fields: tp.Optional[tp.List[str]] = None, ) -> tp.Dict[str, tp.Any]: - pass + return {} def get_wall_execute( @@ -49,4 +51,36 @@ def get_wall_execute( :param fields: Список дополнительных полей для профилей и сообществ, которые необходимо вернуть. :param progress: Callback для отображения прогресса. """ - pass + ses = Session(VK_CONFIG["domain"]) + code = """return API.wall.get({ + "owner_id": "%s", + "domain": "%s", + "offset": %d, + "count": "%d", + "filter": "%s", + "extended": %d, + "fields": "%s", + "v": "%s" + });""" + res = [] + for i in range(math.ceil(count / 100)): + exec_code = code % ( + owner_id, + domain, + 100 * i, + 100 * (i + 1) if count > 100 else count, + filter, + extended, + fields, + VK_CONFIG["version"], + ) + response = ses.post( + "execute", + code=exec_code, + access_token=VK_CONFIG["access_token"], + v=VK_CONFIG["version"], + ).json() + res.extend(response["response"]["items"]) + if i % 2 == 0: + time.sleep(1) + return json_normalize(res) diff --git a/main.py b/main.py new file mode 100644 index 0000000..5596b44 --- /dev/null +++ b/main.py @@ -0,0 +1,16 @@ +# This is a sample Python script. + +# Press Shift+F10 to execute it or replace it with your code. +# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. + + +def print_hi(name): + # Use a breakpoint in the code line below to debug your script. + print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint. + + +# Press the green button in the gutter to run the script. +if __name__ == '__main__': + print_hi('PyCharm') + +# See PyCharm help at https://www.jetbrains.com/help/pycharm/ From d33626e17373cc26e68619872a159359910d82dd Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 16:52:25 +0300 Subject: [PATCH 3/3] s --- .github/workflows/cs102.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cs102.yml b/.github/workflows/cs102.yml index 4509ac4..8e1e8be 100644 --- a/.github/workflows/cs102.yml +++ b/.github/workflows/cs102.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up Python 3.8.6 + - name: Set up Python 3.10.7 uses: actions/setup-python@v2 with: - python-version: '3.8.6' + python-version: '3.10.7' - name: Install dependencies run: | python -m pip install --upgrade pip