From ddec6094c291935a0e1f1e3f63d08c2361d99d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AD=D0=B4=D1=83=D0=B0=D1=80=D0=B4?= Date: Wed, 29 Oct 2025 16:07:06 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=B0=D0=B3=D0=B8=D0=BD=D0=B0=D1=86=D0=B8=D1=8E?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B5=D0=B3=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/clockify.py | 25 +++++++++++++++++-------- config/clockify.py | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/api/clockify.py b/api/clockify.py index 44e1e9e..e33aa11 100644 --- a/api/clockify.py +++ b/api/clockify.py @@ -58,7 +58,7 @@ class ClockifyAPI(ClockifyConfig): tag = [tag for tag in tags if tag.id == tag_id] if not tag: - tag = self.get_tags(True, tag_id=tag_id) + tag = self.get_tags(True, tag_id=tag_id, **{"page-size": self.tags_count}) if tag: db.insert_tags([tag] if isinstance(tag, Tag) else tag) from config import red @@ -104,16 +104,25 @@ class ClockifyAPI(ClockifyConfig): name: str - optional archived: bool - optional page-size: int - optional + page: int - optional """ if is_updating: + page = 1 + kwargs["page"] = page tags = requests.get(self.base_url + self.tags_url, params={"sort-column": "name", "sort-order": "descending", **kwargs}, headers={"X-API-KEY": self.token}).json() - if tag_id: - for tag in tags: - if tag_id == tag["id"]: - return Tag( - id=tag["id"], - title=tag["name"] - ) + while len(tags) == self.tags_count: + if tag_id: + for tag in tags: + if tag_id == tag["id"]: + return Tag( + id=tag["id"], + title=tag["name"] + ) + page += 1 + kwargs["page"] = page + tags = requests.get(self.base_url + self.tags_url, params={"sort-column": "name", "sort-order": "descending", **kwargs}, headers={"X-API-KEY": self.token}).json() + else: + break logger.debug(f"{Fore.YELLOW}Получено тегов: {len(tags)}.{Fore.RESET}") return [Tag( id=tag["id"], diff --git a/config/clockify.py b/config/clockify.py index 663f20d..d0a8880 100644 --- a/config/clockify.py +++ b/config/clockify.py @@ -10,7 +10,7 @@ class ClockifyConfig: self.user_url = "/user" self.projects_url = f"{self.workspace_url}/projects" - self.tags_count = 100 + self.tags_count = 20 def update_time_entry(self, activity_id): return f"{self.workspace_url}/time-entries/{activity_id}" From 5dc9c8f0d1a2665261d18a2bcbdc990bcee0660f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AD=D0=B4=D1=83=D0=B0=D1=80=D0=B4?= Date: Thu, 30 Apr 2026 16:34:57 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=91=D0=BE=D1=82=20+=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=BA=D1=81=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes bot/__init__.py | 0 bot/__main__.py | 29 ++++++++++++++++++++++++++++ bot/commands.py | 30 +++++++++++++++++++++++++++++ bot/factory.py | 12 ++++++++++++ config/__init__.py | 3 +++ env.example | 5 ++++- main.py | 18 +++++++++++++++++ requirements.txt | 13 +++++++++++++ track.py => tracker/commands.py | 33 ++++++++++++++++---------------- utils.py | 5 ++++- 11 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 .DS_Store create mode 100644 bot/__init__.py create mode 100644 bot/__main__.py create mode 100644 bot/commands.py create mode 100644 bot/factory.py create mode 100644 main.py rename track.py => tracker/commands.py (72%) diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..71f8971e12d638bafb8535b02bb1d7b69f5b20d6 GIT binary patch literal 6148 zcmeHKF-sgl6n=Ae#v>PS%b*}D#3D`Lnuy&hC!`dvvIr)7XYLO6+zY2dD)%=Cf{-HM zFOXPTXrZJ{s2OrY^bW6pSKuxx!0&F2o>7kmwBx+Lm8CCsY?!uM zagxUH)fcI;|77FSiW~3#F+S>BYr~W*5mTtUM_aT_dv;~Dc|C)ft*Bl5?cvqsqeZFh zWaQOv&#>#O!wdzeL7O;6^o~088m$j5@Yr04mU)#c$+UTWo%;}KHx9evCrHlOE7^m#$wBQtK8=JktFx866dHw{e^)Ewj3qOn^yI4EY=^`D*| ze-B>BeVUlZ=24#I@`;$w1Mo@lw7kKy+Ut%xNt~uhA>h(%?oJN0N%qceNIH|-6w7j4 zg7QFDs7tX;OdJlO+4mJ5#)_X>Cg zyaG20@cIyOFF%QVUmbY?r26W)p+qlqCF@(D09m}!Iu=KlzY48HRU{8t5j E15sn?lmGw# literal 0 HcmV?d00001 diff --git a/bot/__init__.py b/bot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bot/__main__.py b/bot/__main__.py new file mode 100644 index 0000000..57fe2b8 --- /dev/null +++ b/bot/__main__.py @@ -0,0 +1,29 @@ +import logging + +import asyncio +import sys + +from bot.commands import router as command_router +from bot.factory import bot, dp, init_bot_and_dp +from config import config + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s', + handlers=[ + logging.FileHandler('app.log', encoding='utf-8'), + logging.StreamHandler(sys.stdout) + ] +) + + +logger = logging.getLogger(__name__) + +async def main(): + bot, dp = init_bot_and_dp(f"socks5://{config.PROXY_URL}", config.BOT_TOKEN) + dp.include_router(command_router) + await dp.start_polling(bot) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/bot/commands.py b/bot/commands.py new file mode 100644 index 0000000..9e429be --- /dev/null +++ b/bot/commands.py @@ -0,0 +1,30 @@ +from argparse import Namespace + +from aiogram import Router +from aiogram.filters.command import Command, CommandStart +from aiogram.types import Message + +from bot.factory import bot +from tracker.commands import end_workday + + +router = Router() + +@router.message(CommandStart()) +async def start_command(message: Message): + await message.answer(f"Привет, {message.from_user.first_name}!") + + +@router.message(Command("track")) +async def track_command(message: Message): + await message.answer("Эта фича пока в разработке, но скоро она поможет стартовать и останавливать таймер.") + + +@router.message(Command("end")) +async def end_command(message: Message): + args = message.text.replace("/end", "").strip().split() + if "-i" in args or "--init-db" in args: + args = Namespace(init_db=True) + await message.answer("Начинаю процесс занесения трудочасов...") + await end_workday(args, bot, message.from_user.id) + await message.answer("Трудочасы занесены!") diff --git a/bot/factory.py b/bot/factory.py new file mode 100644 index 0000000..61c9591 --- /dev/null +++ b/bot/factory.py @@ -0,0 +1,12 @@ +from aiogram import Bot, Dispatcher +from aiogram.client.session.aiohttp import AiohttpSession + + +bot: Bot = None # type: ignore +dp: Dispatcher = None + +def init_bot_and_dp(proxy_url: str, token: str): + global bot, dp + bot = Bot(token=token, session=AiohttpSession(proxy=proxy_url) if proxy_url else None) + dp = Dispatcher() + return bot, dp diff --git a/config/__init__.py b/config/__init__.py index 83977d4..de5d45b 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -16,6 +16,9 @@ class Config(BaseSettings): REDMINE_TOKEN: str USER_ID: str = None WORKSPACE_ID: str = None + BOT_TOKEN: str + + PROXY_URL: str MODE: str = "prod" diff --git a/env.example b/env.example index 5a5dff9..cf2ef93 100644 --- a/env.example +++ b/env.example @@ -2,4 +2,7 @@ REDMINE_TOKEN= CLOCKIFY_TOKEN= USER_ID= WORKSPACE_ID= -TRIAL_END_DATE= \ No newline at end of file +TRIAL_END_DATE= + +BOT_TOKEN= +PROXY_URL= \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..f2126da --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +import asyncio +import logging +import sys + +from tracker.commands import end_workday + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s', + handlers=[ + logging.FileHandler('app.log', encoding='utf-8'), + logging.StreamHandler(sys.stdout) + ] +) + + +if __name__ == "__main__": + asyncio.run(end_workday()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5020b36..b0e8627 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,28 @@ +aiofiles==25.1.0 +aiogram==3.27.0 +aiohappyeyeballs==2.6.1 +aiohttp==3.13.5 +aiohttp_socks==0.11.0 +aiosignal==1.4.0 annotated-types==0.7.0 asarPy==1.0.1 +attrs==26.1.0 certifi==2025.4.26 charset-normalizer==3.4.2 colorama==0.4.6 +frozenlist==1.8.0 idna==3.10 +magic-filter==1.0.12 +multidict==6.7.1 +propcache==0.4.1 pydantic==2.11.4 pydantic-settings==2.9.1 pydantic_core==2.33.2 python-dotenv==1.1.0 +python-socks==2.8.1 requests==2.32.3 tqdm==4.67.1 typing-inspection==0.4.0 typing_extensions==4.13.2 urllib3==2.4.0 +yarl==1.23.0 diff --git a/track.py b/tracker/commands.py similarity index 72% rename from track.py rename to tracker/commands.py index 64492bc..768225c 100644 --- a/track.py +++ b/tracker/commands.py @@ -1,24 +1,20 @@ import logging -import sys - -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s', - handlers=[ - logging.FileHandler('app.log', encoding='utf-8'), - logging.StreamHandler(sys.stdout) - ] -) - +from argparse import Namespace +from aiogram import Bot from colorama import Fore # noqa: E402 +from bot.factory import bot from config import arg_parser, clock, config, db, red # noqa: E402 from utils import check_envs, db_update, format_date, get_start_end_dates # noqa: E402 -def main(): - args = arg_parser.parse_args() +async def end_workday( + args: Namespace = None, + bot: Bot = None, + user_id: int = None +): + args = args or arg_parser.parse_args() if is_debug := args.debug: config.MODE = "debug" @@ -47,7 +43,7 @@ def main(): } db.insert_user(**clock_user, **red_user) if args.init_db: - db_update() + await db_update(bot) activities = sorted(clock.get_activities( **{ @@ -58,16 +54,19 @@ def main(): ), key=lambda x: x.date_start) if not activities: logger.info(Fore.YELLOW + "Нет задач для занесения трудочасов" + Fore.RESET) + if bot: + await bot.send_message(user_id, "Нет задач для занесения трудочасов") return db.insert_activities(activities) try: tracked_activities = red.track_activities(activities) + if bot: + await bot.send_message(user_id, f"Трудочасы занесены: {len(tracked_activities)}/{len(activities)}.") clock.mark_tracked(tracked_activities) if not is_debug: db.insert_time_entries(tracked_activities) db.insert_activities(tracked_activities) except Exception as e: + if bot: + await bot.send_message(user_id, f"Ошибка при трекинге трудочасов: {e}") logger.error(e) - -if __name__ == "__main__": - main() diff --git a/utils.py b/utils.py index bc12d7f..3f1ed96 100644 --- a/utils.py +++ b/utils.py @@ -5,6 +5,7 @@ import os import re from typing import Optional +from aiogram import Bot from colorama import Fore from config import config @@ -40,9 +41,11 @@ def format_date(date: datetime) -> datetime: def parse_task_id(tag: str) -> int: return int(re.search(r"^(?P\d{5})", tag).group("task_id")) -def db_update(): +async def db_update(bot: Bot = None, user_id: int = None): from config import clock, db, red logger.info(Fore.YELLOW + "Обновление базы данных..." + Fore.RESET) + if bot: + await bot.send_message(user_id, "Обновление базы данных...") try: # Обновление Тегов From 63044be1066d88b7bb7514e7e8d4098f49c40d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AD=D0=B4=D1=83=D0=B0=D1=80=D0=B4?= Date: Thu, 4 Jun 2026 16:00:35 +0300 Subject: [PATCH 3/4] =?UTF-8?q?Revert=20"=D0=91=D0=BE=D1=82=20+=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D1=81=D0=B8"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5dc9c8f0d1a2665261d18a2bcbdc990bcee0660f. --- .DS_Store | Bin 6148 -> 0 bytes bot/__init__.py | 0 bot/__main__.py | 29 ---------------------------- bot/commands.py | 30 ----------------------------- bot/factory.py | 12 ------------ config/__init__.py | 3 --- env.example | 5 +---- main.py | 18 ----------------- requirements.txt | 13 ------------- tracker/commands.py => track.py | 33 ++++++++++++++++---------------- utils.py | 5 +---- 11 files changed, 19 insertions(+), 129 deletions(-) delete mode 100644 .DS_Store delete mode 100644 bot/__init__.py delete mode 100644 bot/__main__.py delete mode 100644 bot/commands.py delete mode 100644 bot/factory.py delete mode 100644 main.py rename tracker/commands.py => track.py (72%) diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 71f8971e12d638bafb8535b02bb1d7b69f5b20d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKF-sgl6n=Ae#v>PS%b*}D#3D`Lnuy&hC!`dvvIr)7XYLO6+zY2dD)%=Cf{-HM zFOXPTXrZJ{s2OrY^bW6pSKuxx!0&F2o>7kmwBx+Lm8CCsY?!uM zagxUH)fcI;|77FSiW~3#F+S>BYr~W*5mTtUM_aT_dv;~Dc|C)ft*Bl5?cvqsqeZFh zWaQOv&#>#O!wdzeL7O;6^o~088m$j5@Yr04mU)#c$+UTWo%;}KHx9evCrHlOE7^m#$wBQtK8=JktFx866dHw{e^)Ewj3qOn^yI4EY=^`D*| ze-B>BeVUlZ=24#I@`;$w1Mo@lw7kKy+Ut%xNt~uhA>h(%?oJN0N%qceNIH|-6w7j4 zg7QFDs7tX;OdJlO+4mJ5#)_X>Cg zyaG20@cIyOFF%QVUmbY?r26W)p+qlqCF@(D09m}!Iu=KlzY48HRU{8t5j E15sn?lmGw# diff --git a/bot/__init__.py b/bot/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/bot/__main__.py b/bot/__main__.py deleted file mode 100644 index 57fe2b8..0000000 --- a/bot/__main__.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging - -import asyncio -import sys - -from bot.commands import router as command_router -from bot.factory import bot, dp, init_bot_and_dp -from config import config - -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s', - handlers=[ - logging.FileHandler('app.log', encoding='utf-8'), - logging.StreamHandler(sys.stdout) - ] -) - - -logger = logging.getLogger(__name__) - -async def main(): - bot, dp = init_bot_and_dp(f"socks5://{config.PROXY_URL}", config.BOT_TOKEN) - dp.include_router(command_router) - await dp.start_polling(bot) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/bot/commands.py b/bot/commands.py deleted file mode 100644 index 9e429be..0000000 --- a/bot/commands.py +++ /dev/null @@ -1,30 +0,0 @@ -from argparse import Namespace - -from aiogram import Router -from aiogram.filters.command import Command, CommandStart -from aiogram.types import Message - -from bot.factory import bot -from tracker.commands import end_workday - - -router = Router() - -@router.message(CommandStart()) -async def start_command(message: Message): - await message.answer(f"Привет, {message.from_user.first_name}!") - - -@router.message(Command("track")) -async def track_command(message: Message): - await message.answer("Эта фича пока в разработке, но скоро она поможет стартовать и останавливать таймер.") - - -@router.message(Command("end")) -async def end_command(message: Message): - args = message.text.replace("/end", "").strip().split() - if "-i" in args or "--init-db" in args: - args = Namespace(init_db=True) - await message.answer("Начинаю процесс занесения трудочасов...") - await end_workday(args, bot, message.from_user.id) - await message.answer("Трудочасы занесены!") diff --git a/bot/factory.py b/bot/factory.py deleted file mode 100644 index 61c9591..0000000 --- a/bot/factory.py +++ /dev/null @@ -1,12 +0,0 @@ -from aiogram import Bot, Dispatcher -from aiogram.client.session.aiohttp import AiohttpSession - - -bot: Bot = None # type: ignore -dp: Dispatcher = None - -def init_bot_and_dp(proxy_url: str, token: str): - global bot, dp - bot = Bot(token=token, session=AiohttpSession(proxy=proxy_url) if proxy_url else None) - dp = Dispatcher() - return bot, dp diff --git a/config/__init__.py b/config/__init__.py index de5d45b..83977d4 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -16,9 +16,6 @@ class Config(BaseSettings): REDMINE_TOKEN: str USER_ID: str = None WORKSPACE_ID: str = None - BOT_TOKEN: str - - PROXY_URL: str MODE: str = "prod" diff --git a/env.example b/env.example index cf2ef93..5a5dff9 100644 --- a/env.example +++ b/env.example @@ -2,7 +2,4 @@ REDMINE_TOKEN= CLOCKIFY_TOKEN= USER_ID= WORKSPACE_ID= -TRIAL_END_DATE= - -BOT_TOKEN= -PROXY_URL= \ No newline at end of file +TRIAL_END_DATE= \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index f2126da..0000000 --- a/main.py +++ /dev/null @@ -1,18 +0,0 @@ -import asyncio -import logging -import sys - -from tracker.commands import end_workday - -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s', - handlers=[ - logging.FileHandler('app.log', encoding='utf-8'), - logging.StreamHandler(sys.stdout) - ] -) - - -if __name__ == "__main__": - asyncio.run(end_workday()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b0e8627..5020b36 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,28 +1,15 @@ -aiofiles==25.1.0 -aiogram==3.27.0 -aiohappyeyeballs==2.6.1 -aiohttp==3.13.5 -aiohttp_socks==0.11.0 -aiosignal==1.4.0 annotated-types==0.7.0 asarPy==1.0.1 -attrs==26.1.0 certifi==2025.4.26 charset-normalizer==3.4.2 colorama==0.4.6 -frozenlist==1.8.0 idna==3.10 -magic-filter==1.0.12 -multidict==6.7.1 -propcache==0.4.1 pydantic==2.11.4 pydantic-settings==2.9.1 pydantic_core==2.33.2 python-dotenv==1.1.0 -python-socks==2.8.1 requests==2.32.3 tqdm==4.67.1 typing-inspection==0.4.0 typing_extensions==4.13.2 urllib3==2.4.0 -yarl==1.23.0 diff --git a/tracker/commands.py b/track.py similarity index 72% rename from tracker/commands.py rename to track.py index 768225c..64492bc 100644 --- a/tracker/commands.py +++ b/track.py @@ -1,20 +1,24 @@ import logging -from argparse import Namespace +import sys + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s', + handlers=[ + logging.FileHandler('app.log', encoding='utf-8'), + logging.StreamHandler(sys.stdout) + ] +) + -from aiogram import Bot from colorama import Fore # noqa: E402 -from bot.factory import bot from config import arg_parser, clock, config, db, red # noqa: E402 from utils import check_envs, db_update, format_date, get_start_end_dates # noqa: E402 -async def end_workday( - args: Namespace = None, - bot: Bot = None, - user_id: int = None -): - args = args or arg_parser.parse_args() +def main(): + args = arg_parser.parse_args() if is_debug := args.debug: config.MODE = "debug" @@ -43,7 +47,7 @@ async def end_workday( } db.insert_user(**clock_user, **red_user) if args.init_db: - await db_update(bot) + db_update() activities = sorted(clock.get_activities( **{ @@ -54,19 +58,16 @@ async def end_workday( ), key=lambda x: x.date_start) if not activities: logger.info(Fore.YELLOW + "Нет задач для занесения трудочасов" + Fore.RESET) - if bot: - await bot.send_message(user_id, "Нет задач для занесения трудочасов") return db.insert_activities(activities) try: tracked_activities = red.track_activities(activities) - if bot: - await bot.send_message(user_id, f"Трудочасы занесены: {len(tracked_activities)}/{len(activities)}.") clock.mark_tracked(tracked_activities) if not is_debug: db.insert_time_entries(tracked_activities) db.insert_activities(tracked_activities) except Exception as e: - if bot: - await bot.send_message(user_id, f"Ошибка при трекинге трудочасов: {e}") logger.error(e) + +if __name__ == "__main__": + main() diff --git a/utils.py b/utils.py index 3f1ed96..bc12d7f 100644 --- a/utils.py +++ b/utils.py @@ -5,7 +5,6 @@ import os import re from typing import Optional -from aiogram import Bot from colorama import Fore from config import config @@ -41,11 +40,9 @@ def format_date(date: datetime) -> datetime: def parse_task_id(tag: str) -> int: return int(re.search(r"^(?P\d{5})", tag).group("task_id")) -async def db_update(bot: Bot = None, user_id: int = None): +def db_update(): from config import clock, db, red logger.info(Fore.YELLOW + "Обновление базы данных..." + Fore.RESET) - if bot: - await bot.send_message(user_id, "Обновление базы данных...") try: # Обновление Тегов From 20fa3eb8a1db9abde3616c7f56a12fad0461d074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AD=D0=B4=D1=83=D0=B0=D1=80=D0=B4?= Date: Fri, 5 Jun 2026 16:14:07 +0300 Subject: [PATCH 4/4] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D1=83=20=D1=81=20list?= =?UTF-8?q?=20has=20no=20attribute=20'title'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/clockify.py | 60 +++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/api/clockify.py b/api/clockify.py index e33aa11..3709456 100644 --- a/api/clockify.py +++ b/api/clockify.py @@ -59,8 +59,9 @@ class ClockifyAPI(ClockifyConfig): if not tag: tag = self.get_tags(True, tag_id=tag_id, **{"page-size": self.tags_count}) - if tag: - db.insert_tags([tag] if isinstance(tag, Tag) else tag) + if isinstance(tag, Tag): + db.insert_tags([tag]) + tags.append(tag) from config import red try: task_id = parse_task_id(tag.title) @@ -72,6 +73,7 @@ class ClockifyAPI(ClockifyConfig): db.insert_tasks([task]) else: logger.error(f"{Fore.RED} Тег не найден: {tag_id}{Fore.RESET}") + continue else: tag = tag[0] task_id = parse_task_id(tag.title) @@ -98,7 +100,7 @@ class ClockifyAPI(ClockifyConfig): return result - def get_tags(self, is_updating=False, tag_id="", **kwargs) -> Union[list[Tag], Tag]: + def get_tags(self, is_updating=False, tag_id="", **kwargs) -> Union[list[Tag], Tag, None]: """ Получает с сервера Clockify теги name: str - optional @@ -108,26 +110,40 @@ class ClockifyAPI(ClockifyConfig): """ if is_updating: page = 1 - kwargs["page"] = page - tags = requests.get(self.base_url + self.tags_url, params={"sort-column": "name", "sort-order": "descending", **kwargs}, headers={"X-API-KEY": self.token}).json() - while len(tags) == self.tags_count: - if tag_id: - for tag in tags: - if tag_id == tag["id"]: - return Tag( - id=tag["id"], - title=tag["name"] - ) - page += 1 - kwargs["page"] = page - tags = requests.get(self.base_url + self.tags_url, params={"sort-column": "name", "sort-order": "descending", **kwargs}, headers={"X-API-KEY": self.token}).json() - else: + page_size = int(kwargs.get("page-size", self.tags_count)) + result = [] + while True: + params = { + "sort-column": "name", + "sort-order": "descending", + **kwargs, + "page": page + } + tags = requests.get( + self.base_url + self.tags_url, + params=params, + headers={"X-API-KEY": self.token} + ).json() + + for tag in tags: + clockify_tag = Tag( + id=tag["id"], + title=tag["name"] + ) + if tag_id and tag_id == clockify_tag.id: + return clockify_tag + if not tag_id: + result.append(clockify_tag) + + if len(tags) < page_size: break - logger.debug(f"{Fore.YELLOW}Получено тегов: {len(tags)}.{Fore.RESET}") - return [Tag( - id=tag["id"], - title=tag["name"] - ) for tag in tags] + page += 1 + + if tag_id: + return None + + logger.debug(f"{Fore.YELLOW}Получено тегов: {len(result)}.{Fore.RESET}") + return result return db.get_tags() def get_vetro_projects(self) -> list[VetroProject]: