Настроил динамическую загрузку Ветро проектов (Tasks) из Clockify и добавил новый аргумент для обновления базы данных с серверов
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
from .classes import Activity, ActivityType, Tag # noqa: F401
|
from .classes import Activity, ActivityType, Tag, VetroProject # noqa: F401
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
12
db/db.py
12
db/db.py
@@ -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)
|
||||||
|
|||||||
2
enums.py
2
enums.py
@@ -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
|
||||||
|
|||||||
7
track.py
7
track.py
@@ -47,8 +47,12 @@ def main():
|
|||||||
)
|
)
|
||||||
red = RedmineAPI(config.REDMINE_TOKEN)
|
red = RedmineAPI(config.REDMINE_TOKEN)
|
||||||
|
|
||||||
|
if args.init_db:
|
||||||
|
logger.info("Обновление базы данных...")
|
||||||
db.insert_tags(clock.get_tags())
|
db.insert_tags(clock.get_tags())
|
||||||
db.insert_vetro_projects(config.VETRO_PROJECTS)
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user