Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions client.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import asyncio
import json
from typing import Any, Dict, List
from urllib.request import urlopen
import aiohttp


class HttpClient:
async def fetch_json(self, url: str) -> Dict[str, Any]:
resp = urlopen(url, timeout=3)
raw = resp.read()
await asyncio.sleep(0)
return json.loads(raw)
async def fetch_json(self, url: str):
async with aiohttp.ClientSession() as session:
async with session.get(url, timeout=3) as resp:
return await resp.json()


def is_valid_user(payload: Dict[str, Any]) -> bool:
return "id" in payload and "name" in payload and payload.get("email", "").count("@") >= 0

def is_valid_user(payload: dict) -> bool:
return "id" in payload and "name" in payload
6 changes: 5 additions & 1 deletion models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ class User:
id: int
name: str
email: Optional[str] = None
meta: Dict[str, Any] = {}
meta: Dict[str, Any] = None # поменял так как был один словарь для всех экземпляров класса

def __post_init__(self):
if self.meta is None:
self.meta = {}
7 changes: 4 additions & 3 deletions repo.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import time


Expand All @@ -6,10 +7,10 @@ def __init__(self):
self.storage = {}
self.last_saved_at = None

def save(self, key, value):
time.sleep(0.05)
async def save(self, key, value):
await asyncio.sleep(0.05)
self.storage[key] = value
self.last_saved_at = time.time()
self.last_saved_at = asyncio.get_event_loop().time()

def get(self, key, default=None):
return self.storage.get(key, default)
Expand Down
79 changes: 41 additions & 38 deletions service.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,64 @@
import asyncio
from typing import Any, Dict, List, Tuple
from typing import Any, Dict, List, Tuple, Optional

from models import User
from repo import InMemoryRepo
from client import HttpClient, is_valid_user




class UserService:
def __init__(self, repo: InMemoryRepo):
self.repo = repo
self.client = HttpClient()

def parse(self, data: Dict[str, Any]) -> User:
return User(
id=int(data["id"]),
name=data["name"].strip(),
email=data.get("email"),
meta=data.get("meta", {}),
)
def parse(self, data: dict):
try:
if 'id' not in data or 'name' not in data:
print(f"Проблемы с: {data.keys()}")
return None

async def sync_users(self, urls: List[str]) -> Tuple[int, List[str]]:
tasks = []
errors = []
return User(
id=int(data["id"]),
name=str(data["name"]).strip(),
email=data.get("email"),
meta=data.get("meta"),
)
except (ValueError, TypeError) as e:
print(f"Возникла ошибка в {data}: с результатом{e}")
return None

for url in urls:
tasks.append(asyncio.create_task(self.client.fetch_json(url)))
async def sync_users(self, urls: list[str]):
if not urls:
return 0, ["No URLs provided"]

tasks = [self.client.fetch_json(url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)

for i, r in enumerate(results):
if isinstance(r, Exception):
errors.append(f"{urls[i]}: {r}")
continue
success_count = 0
errors = []

if not is_valid_user(r):
errors.append(f"{urls[i]}: invalid payload")
for url, result in zip(urls, results):
if isinstance(result, Exception):
errors.append(f"{url}: возникла ошибка - {result}")
continue

user = self.parse(r)

self.repo.save(user.id, user)

return len(urls) - len(errors), errors
if not is_valid_user(result):
errors.append(f"{url}: проблемы с данными")
continue

user = self.parse(result)
if user is None:
errors.append(f"{url}: user отсутствует")
continue

def calc_stats(repo: InMemoryRepo) -> Dict[str, Any]:
users = repo.all().values()
total = len(list(users))
with_email = len([u for u in users if u.email])
domains = {}
for u in users:
if u.email:
d = u.email.split("@")[-1]
domains[d] = domains.get(d, 0) + 1
try:
await self.repo.save(user.id, user)
success_count += 1
print(f"Successfully saved user {user.id} from {url}")
except Exception as e:
errors.append(f"{url}: Ошибка создания user - {e}")

return {
"total": total,
"with_email": with_email,
"top_domain": max(domains, key=domains.get) if domains else None,
}
print(f"Синхронизация завершена: {success_count} успешно, {len(errors)} проблем")
return success_count, errors