Настроил динамическую загрузку Ветро проектов (Tasks) из Clockify и добавил новый аргумент для обновления базы данных с серверов

This commit is contained in:
2025-05-21 12:55:27 +03:00
parent 70232f15a0
commit 38a8461070
9 changed files with 49 additions and 15 deletions

View File

@@ -6,7 +6,7 @@ import requests
from tqdm import tqdm from tqdm import tqdm
from config import ClockifyConfig, config from config import ClockifyConfig, config
from classes import Activity, ActivityType, Tag from classes import Activity, Tag, VetroProject
from exceptions import InvalidToken from exceptions import InvalidToken
from utils import time_to_hour from utils import time_to_hour
@@ -79,6 +79,16 @@ class ClockifyAPI(ClockifyConfig):
title=tag["name"] title=tag["name"]
) for tag in tags] ) for tag in tags]
def get_vetro_projects(self) -> list[VetroProject]:
projects = requests.get(self.base_url + self.projects_url, headers={"X-API-KEY": self.token}).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()
return [VetroProject(
id=vetro_project["id"],
title=vetro_project["name"]
) for vetro_project in vetro_projects]
def mark_tracked(self, activities: list[Activity]): def mark_tracked(self, activities: list[Activity]):
for activity in activities: for activity in activities:
try: try:

View File

@@ -5,7 +5,7 @@ from tqdm import tqdm
from config import RedmineConfig, config from config import RedmineConfig, config
from classes import Activity, ActivityType from classes import Activity, ActivityType
from enums import ActivityType as ActivityTypeEnum from enums import ActivityTypes
from exceptions import InvalidToken from exceptions import InvalidToken
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -34,7 +34,7 @@ class RedmineAPI(RedmineConfig):
"time_entry": { "time_entry": {
"issue_id": activity.task_id, "issue_id": activity.task_id,
"hours": activity.time_spent, "hours": activity.time_spent,
"activity_id": ActivityTypeEnum.DEVELOPMENT, "activity_id": ActivityTypes.DEVELOPMENT,
"comments": activity.description, "comments": activity.description,
"spent_on": activity.date_start.strftime(self.datetime_format) "spent_on": activity.date_start.strftime(self.datetime_format)
} }
@@ -56,7 +56,7 @@ class RedmineAPI(RedmineConfig):
logger.info(f"Не все задачи были занесены в Redmine. Занесено {len(tracked)}") logger.info(f"Не все задачи были занесены в Redmine. Занесено {len(tracked)}")
return tracked return tracked
def get_time_entry_activities(self): def get_time_entry_activities(self) -> list[ActivityTypes]:
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})
logging.debug(resp.json()) logging.debug(resp.json())
if resp.ok: if resp.ok:

View File

@@ -1 +1 @@
from .classes import Activity, ActivityType, Tag # noqa: F401 from .classes import Activity, ActivityType, Tag, VetroProject # noqa: F401

View File

@@ -34,6 +34,14 @@ class Tag:
@dataclass @dataclass
class ActivityType: class ActivityType:
id: int
title: str
def __str__(self):
return self.title
@dataclass
class VetroProject:
id: str id: str
title: str title: str

View File

@@ -66,6 +66,12 @@ def configure_argument_parser():
action="store_true", action="store_true",
help='Режим отладки. Не загружает трудочасы в Redmine' help='Режим отладки. Не загружает трудочасы в Redmine'
) )
parser.add_argument(
'-i',
'--init-db',
action="store_true",
help='Режим инициализации бд.\nПодгружает с серверов теги, виды деятельности и проекты Ветро'
)
return parser return parser
arg_parser = configure_argument_parser() arg_parser = configure_argument_parser()

View File

@@ -25,3 +25,10 @@ class ClockifyConfig:
def update_time_entry(self, activity_id): def update_time_entry(self, activity_id):
return f"/workspaces/{self.workspace_id}/time-entries/{activity_id}" return f"/workspaces/{self.workspace_id}/time-entries/{activity_id}"
@property
def projects_url(self):
return f"/workspaces/{self.workspace_id}/projects"
def tasks_url(self, project_id: str):
return f"{self.projects_url}/{project_id}/tasks"

View File

@@ -1,8 +1,8 @@
import logging import logging
import sqlite3 as sq import sqlite3 as sq
from classes import Activity, ActivityType, Tag from classes import Activity, ActivityType, Tag, VetroProject
from enums import ActivityType as ActivityTypeEnum from enums import ActivityTypes
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -60,7 +60,7 @@ class Database:
act.id, act.id,
act.time_spent, act.time_spent,
act.task_id, act.task_id,
ActivityTypeEnum.DEVELOPMENT ActivityTypes.DEVELOPMENT
) )
) )
self.con.commit() self.con.commit()
@@ -84,11 +84,11 @@ class Database:
logger.debug(e) logger.debug(e)
def insert_vetro_projects(self, vetro_projects: dict): def insert_vetro_projects(self, vetro_projects: list[VetroProject]):
cur = self.con.cursor() cur = self.con.cursor()
for k, v in vetro_projects.items(): for vetro_project in vetro_projects:
try: try:
cur.execute("INSERT INTO vetro_projects VALUES(?, ?)", (k, v)) cur.execute("INSERT INTO vetro_projects VALUES(?, ?)", (vetro_project.id, vetro_project.title))
self.con.commit() self.con.commit()
except Exception as e: except Exception as e:
logger.debug(e) logger.debug(e)

View File

@@ -1,7 +1,7 @@
from enum import IntEnum, StrEnum from enum import IntEnum, StrEnum
class ActivityType(IntEnum): class ActivityTypes(IntEnum):
DEVELOPMENT = 9 DEVELOPMENT = 9
BUSINESS_ANALYZE = 8 BUSINESS_ANALYZE = 8
CODE_REVIEW = 57 CODE_REVIEW = 57

View File

@@ -47,8 +47,12 @@ def main():
) )
red = RedmineAPI(config.REDMINE_TOKEN) red = RedmineAPI(config.REDMINE_TOKEN)
db.insert_tags(clock.get_tags()) if args.init_db:
db.insert_vetro_projects(config.VETRO_PROJECTS) logger.info("Обновление базы данных...")
db.insert_tags(clock.get_tags())
db.insert_vetro_projects(clock.get_vetro_projects())
db.insert_activity_types(red.get_time_entry_activities())
activities = clock.get_activities( activities = clock.get_activities(
**{ **{
"start": format_date(start), "start": format_date(start),
@@ -57,7 +61,6 @@ def main():
}, },
) )
db.insert_activities(activities) db.insert_activities(activities)
db.insert_activity_types(red.get_time_entry_activities())
if args.debug: if args.debug:
logger.debug("Пропускаем этап загрузки трудочасов") logger.debug("Пропускаем этап загрузки трудочасов")
return return