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
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/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/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/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
new file mode 100644
index 0000000..52bff97
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1673962365428
+
+
+ 1673962365428
+
+
+
+
\ No newline at end of file
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/