Добавил новый датакласс для Деятельности трудочасов и настроил их динамическую подгрузку из Redmine

This commit is contained in:
2025-05-21 12:22:48 +03:00
parent aae6d8309a
commit 70232f15a0
8 changed files with 39 additions and 24 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, Tag from classes import Activity, ActivityType, Tag
from exceptions import InvalidToken from exceptions import InvalidToken
from utils import time_to_hour from utils import time_to_hour
@@ -106,9 +106,7 @@ class ClockifyAPI(ClockifyConfig):
except Exception as e: except Exception as e:
logger.error(e) logger.error(e)
return return
def _check_token(self, token: str) -> str: 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})
if not resp.ok: if not resp.ok:

View File

@@ -4,8 +4,8 @@ import requests
from tqdm import tqdm from tqdm import tqdm
from config import RedmineConfig, config from config import RedmineConfig, config
from classes import Activity from classes import Activity, ActivityType
from enums import ActivityType from enums import ActivityType as ActivityTypeEnum
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": ActivityType.DEVELOPMENT, "activity_id": ActivityTypeEnum.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)
} }
@@ -55,7 +55,16 @@ class RedmineAPI(RedmineConfig):
else: else:
logger.info(f"Не все задачи были занесены в Redmine. Занесено {len(tracked)}") logger.info(f"Не все задачи были занесены в Redmine. Занесено {len(tracked)}")
return tracked return tracked
def get_time_entry_activities(self):
resp = requests.get(self.base_url + self.time_entry_activities, headers={"X-Redmine-API-KEY": self.token})
logging.debug(resp.json())
if resp.ok:
return [
ActivityType(id=act.get("id"), title=act.get("name"))
for act in resp.json().get("time_entry_activities")
if act.get("active")
]
def _check_token(self, token): def _check_token(self, token):
resp = requests.get(self.base_url + self.my_account_url, headers={"X-Redmine-Api-Key": token}) resp = requests.get(self.base_url + self.my_account_url, headers={"X-Redmine-Api-Key": token})

View File

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

View File

@@ -30,3 +30,12 @@ class Tag:
def __str__(self): def __str__(self):
return self.title return self.title
@dataclass
class ActivityType:
id: str
title: str
def __str__(self):
return self.title

View File

@@ -24,4 +24,4 @@ class ClockifyConfig:
return "%Y-%m-%dT%H:%M:%SZ" return "%Y-%m-%dT%H:%M:%SZ"
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}"

View File

@@ -13,4 +13,8 @@ class RedmineConfig:
@property @property
def datetime_format(self): def datetime_format(self):
return "%Y-%m-%d" return "%Y-%m-%d"
@property
def time_entry_activities(self):
return "/enumerations/time_entry_activities.json"

View File

@@ -1,8 +1,8 @@
import logging import logging
import sqlite3 as sq import sqlite3 as sq
from classes import Activity, Tag from classes import Activity, ActivityType, Tag
from enums import ActivityType from enums import ActivityType as ActivityTypeEnum
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,
ActivityType.DEVELOPMENT ActivityTypeEnum.DEVELOPMENT
) )
) )
self.con.commit() self.con.commit()
@@ -93,11 +93,11 @@ class Database:
except Exception as e: except Exception as e:
logger.debug(e) logger.debug(e)
def insert_activity_types(self, activity_types: dict): def insert_activity_types(self, activity_types: list[ActivityType]):
cur = self.con.cursor() cur = self.con.cursor()
for k,v in activity_types.items(): for activity_type in activity_types:
try: try:
cur.execute("INSERT INTO activity_types VALUES(?, ?)", (k, v)) cur.execute("INSERT INTO activity_types VALUES(?, ?)", (activity_type.id, activity_type.title))
self.con.commit() self.con.commit()
except Exception as e: except Exception as e:
logger.debug(e) logger.debug(e)

View File

@@ -45,6 +45,8 @@ def main():
workspace_id=WORKSPACE_ID, workspace_id=WORKSPACE_ID,
user_id=USER_ID user_id=USER_ID
) )
red = RedmineAPI(config.REDMINE_TOKEN)
db.insert_tags(clock.get_tags()) db.insert_tags(clock.get_tags())
db.insert_vetro_projects(config.VETRO_PROJECTS) db.insert_vetro_projects(config.VETRO_PROJECTS)
activities = clock.get_activities( activities = clock.get_activities(
@@ -55,18 +57,11 @@ def main():
}, },
) )
db.insert_activities(activities) db.insert_activities(activities)
db.insert_activity_types({ db.insert_activity_types(red.get_time_entry_activities())
9: "Разработка",
8: "Бизнес-анализ",
57: "Code review",
58: "Внутренние консультации",
60: "Поддержка",
})
if args.debug: if args.debug:
logger.debug("Пропускаем этап загрузки трудочасов") logger.debug("Пропускаем этап загрузки трудочасов")
return return
try: try:
red = RedmineAPI(config.REDMINE_TOKEN)
tracked_activities = red.track_activities(activities) tracked_activities = red.track_activities(activities)
clock.mark_tracked(tracked_activities) clock.mark_tracked(tracked_activities)
db.insert_time_entries(tracked_activities) db.insert_time_entries(tracked_activities)