Добавил таймауты для API-запросов, уточнил парсинг тегов и типизацию аргументов
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import Union
|
||||
|
||||
from colorama import Fore
|
||||
import requests
|
||||
@@ -10,6 +9,7 @@ from config import ClockifyConfig, config, db
|
||||
from classes import Activity, Tag, VetroProject
|
||||
from exceptions import InvalidToken
|
||||
from utils import parse_task_id, time_to_hour
|
||||
from .constants import REQUEST_TIMEOUT
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -33,7 +33,12 @@ class ClockifyAPI(ClockifyConfig):
|
||||
page-size: int - Размер страницы (по умолчанию 50)
|
||||
"""
|
||||
time_entries_url = self.time_entries_url
|
||||
activities = requests.get(self.base_url + time_entries_url, params=kwargs, headers={"X-API-KEY": self.token}).json()
|
||||
activities = requests.get(
|
||||
self.base_url + time_entries_url,
|
||||
params=kwargs,
|
||||
headers={"X-API-KEY": self.token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
).json()
|
||||
if not activities:
|
||||
return []
|
||||
logger.info(f"Получено задач: {len(activities)}")
|
||||
@@ -100,7 +105,7 @@ class ClockifyAPI(ClockifyConfig):
|
||||
return result
|
||||
|
||||
|
||||
def get_tags(self, is_updating=False, tag_id="", **kwargs) -> Union[list[Tag], Tag, None]:
|
||||
def get_tags(self, is_updating=False, tag_id="", **kwargs) -> list[Tag] | Tag | None:
|
||||
"""
|
||||
Получает с сервера Clockify теги
|
||||
name: str - optional
|
||||
@@ -122,7 +127,8 @@ class ClockifyAPI(ClockifyConfig):
|
||||
tags = requests.get(
|
||||
self.base_url + self.tags_url,
|
||||
params=params,
|
||||
headers={"X-API-KEY": self.token}
|
||||
headers={"X-API-KEY": self.token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
).json()
|
||||
|
||||
for tag in tags:
|
||||
@@ -150,10 +156,18 @@ class ClockifyAPI(ClockifyConfig):
|
||||
"""
|
||||
Получает с сервера Clockify Проекты (ДО) Ветро
|
||||
"""
|
||||
projects = requests.get(self.base_url + self.projects_url, headers={"X-API-KEY": self.token}).json()
|
||||
projects = requests.get(
|
||||
self.base_url + self.projects_url,
|
||||
headers={"X-API-KEY": self.token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
).json()
|
||||
for project in projects:
|
||||
if not project.get("archived"):
|
||||
vetro_projects = requests.get(self.base_url + self.tasks_url(project.get("id")), headers={"X-API-KEY": self.token}).json()
|
||||
vetro_projects = requests.get(
|
||||
self.base_url + self.tasks_url(project.get("id")),
|
||||
headers={"X-API-KEY": self.token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
).json()
|
||||
return [VetroProject(
|
||||
id=vetro_project["id"],
|
||||
title=vetro_project["name"]
|
||||
@@ -180,7 +194,8 @@ class ClockifyAPI(ClockifyConfig):
|
||||
"projectId": activity.project_id
|
||||
}, headers={
|
||||
"X-API-KEY": self.token
|
||||
}
|
||||
},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
if config.is_debug or resp.ok:
|
||||
logger.info(f"{Fore.GREEN}{activity}{Fore.RESET}".ljust(60) + "✅")
|
||||
@@ -193,7 +208,11 @@ class ClockifyAPI(ClockifyConfig):
|
||||
|
||||
def _check_token(self, token: str) -> str:
|
||||
"""Проверяет токен"""
|
||||
resp = requests.get(self.base_url + self.user_url, headers={"X-API-KEY": token})
|
||||
resp = requests.get(
|
||||
self.base_url + self.user_url,
|
||||
headers={"X-API-KEY": token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
if not resp.ok:
|
||||
logger.error(resp.json())
|
||||
raise InvalidToken
|
||||
@@ -203,7 +222,11 @@ class ClockifyAPI(ClockifyConfig):
|
||||
|
||||
def get_user(self):
|
||||
"""Получает нужные данные пользователя с сервера Clockify"""
|
||||
resp = requests.get(self.base_url + self.user_url, headers={"X-API-KEY": self.token})
|
||||
resp = requests.get(
|
||||
self.base_url + self.user_url,
|
||||
headers={"X-API-KEY": self.token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
if not resp.ok:
|
||||
logger.error(resp.json())
|
||||
raise requests.exceptions.RequestException(Fore.RED + "Ошибка запроса." + Fore.RESET)
|
||||
|
||||
3
api/constants.py
Normal file
3
api/constants.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""Константы API-клиентов."""
|
||||
|
||||
REQUEST_TIMEOUT = 30
|
||||
@@ -8,14 +8,11 @@ from config import RedmineConfig, config
|
||||
from classes import Activity, ActivityType
|
||||
from enums import ActivityTypes
|
||||
from exceptions import InvalidToken
|
||||
from .constants import REQUEST_TIMEOUT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
WORKSPACE_ID = config.WORKSPACE_ID
|
||||
USER_ID = config.USER_ID
|
||||
|
||||
|
||||
class RedmineAPI(RedmineConfig):
|
||||
"""API клиент для взаимодействия с Redmine.
|
||||
|
||||
@@ -52,7 +49,8 @@ class RedmineAPI(RedmineConfig):
|
||||
"comments": activity.description,
|
||||
"spent_on": activity.date_start.strftime(self.datetime_format)
|
||||
}
|
||||
}
|
||||
},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
if config.is_debug or resp.ok:
|
||||
activity.is_tracked = True
|
||||
@@ -84,7 +82,11 @@ class RedmineAPI(RedmineConfig):
|
||||
|
||||
def get_time_entry_activities(self) -> list[ActivityTypes]:
|
||||
"""Получает виды Деятельности с сервера Redmine"""
|
||||
resp = requests.get(self.base_url + self.time_entry_activities, headers={"X-Redmine-API-KEY": self.token})
|
||||
resp = requests.get(
|
||||
self.base_url + self.time_entry_activities,
|
||||
headers={"X-Redmine-API-KEY": self.token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
logger.debug(resp.json())
|
||||
if resp.ok:
|
||||
return [
|
||||
@@ -95,7 +97,11 @@ class RedmineAPI(RedmineConfig):
|
||||
|
||||
def get_user(self):
|
||||
"""Проверяет токен и возвращает данные юзера"""
|
||||
resp = requests.get(self.base_url + self.my_account_url, headers={"X-Redmine-Api-Key": self.token})
|
||||
resp = requests.get(
|
||||
self.base_url + self.my_account_url,
|
||||
headers={"X-Redmine-Api-Key": self.token},
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
if not resp.ok:
|
||||
logger.error("Invalid Token for RedmineAPI")
|
||||
raise InvalidToken
|
||||
@@ -113,7 +119,7 @@ class RedmineAPI(RedmineConfig):
|
||||
resp = requests.get(
|
||||
self.base_url + self.issue.format(**{"id": issue_id}),
|
||||
headers={"X-Redmine-Api-Key": self.token},
|
||||
timeout=None
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
if not resp.ok:
|
||||
logger.error("Ошибка RedmineAPI")
|
||||
@@ -138,7 +144,7 @@ class RedmineAPI(RedmineConfig):
|
||||
self.base_url + self.issues,
|
||||
headers={"X-Redmine-Api-Key": self.token},
|
||||
params={"limit": 100, "offset": 100 * i, "status_id": "*"},
|
||||
timeout=None
|
||||
timeout=REQUEST_TIMEOUT,
|
||||
)
|
||||
if not resp.ok or not resp.json():
|
||||
logger.error("Ошибка RedmineAPI")
|
||||
|
||||
@@ -67,6 +67,7 @@ def configure_argument_parser():
|
||||
parser.add_argument(
|
||||
'-p',
|
||||
'--page-size',
|
||||
type=int,
|
||||
help='Количество задач для вывода'
|
||||
)
|
||||
parser.add_argument(
|
||||
|
||||
9
utils.py
9
utils.py
@@ -3,7 +3,6 @@ from datetime import datetime, timedelta
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
from colorama import Fore
|
||||
|
||||
@@ -29,16 +28,18 @@ def today_end() -> datetime:
|
||||
return datetime(year=year, month=month, day=day, hour=23)
|
||||
|
||||
|
||||
def str_to_date(date: str) -> Optional[datetime]:
|
||||
def str_to_date(date: str) -> datetime | None:
|
||||
if date:
|
||||
return datetime.strptime(date + "T00:00:00Z", config.DATETIME_FORMAT)
|
||||
|
||||
def format_date(date: datetime) -> datetime:
|
||||
def format_date(date: datetime) -> str | None:
|
||||
if date:
|
||||
return date.strftime(config.DATETIME_FORMAT)
|
||||
|
||||
def parse_task_id(tag: str) -> int:
|
||||
return int(re.search(r"^(?P<task_id>\d{5})", tag).group("task_id"))
|
||||
if match := re.search(r"^(?P<task_id>\d{5})", tag):
|
||||
return int(match.group("task_id"))
|
||||
raise ValueError(f'Тег "{tag}" должен начинаться с номера задачи из 5 цифр')
|
||||
|
||||
def db_update():
|
||||
from config import clock, db, red
|
||||
|
||||
Reference in New Issue
Block a user