Много изменений в бд
This commit is contained in:
@@ -5,7 +5,7 @@ from colorama import Fore
|
||||
import requests
|
||||
from tqdm import tqdm
|
||||
|
||||
from config import ClockifyConfig, config
|
||||
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
|
||||
@@ -51,6 +51,9 @@ class ClockifyAPI(ClockifyConfig):
|
||||
except ValueError:
|
||||
logger.info(f'{activity["timeInterval"]["start"]}: {activity["description"]} - Неправильный формат тега {tag}')
|
||||
continue
|
||||
|
||||
author = db.get_user_by("clockify_user_id", activity["userId"])
|
||||
|
||||
result.append(
|
||||
Activity(
|
||||
id=activity["id"],
|
||||
@@ -62,7 +65,8 @@ class ClockifyAPI(ClockifyConfig):
|
||||
time_spent=time_to_hour((end if end else datetime.utcnow()) - start),
|
||||
date_start=start,
|
||||
date_end=end,
|
||||
is_tracked=activity["billable"]
|
||||
is_tracked=activity["billable"],
|
||||
author_id=author["id"]
|
||||
)
|
||||
)
|
||||
logger.info(f"Сохранено задач: {len(result)}")
|
||||
@@ -135,16 +139,27 @@ class ClockifyAPI(ClockifyConfig):
|
||||
resp = resp.json()
|
||||
logger.debug({"id": resp["id"], "email": resp["email"], "name": resp["name"]})
|
||||
return token
|
||||
|
||||
def getenvs(self):
|
||||
"""Получает нужные USER_ID и WORKSPACE_ID с сервера Clockify"""
|
||||
|
||||
def get_user(self):
|
||||
"""Получает нужные данные пользователя с сервера Clockify"""
|
||||
resp = requests.get(self.base_url + self.user_url, headers={"X-API-KEY": self.token})
|
||||
if not resp.ok:
|
||||
logger.error(resp.json())
|
||||
raise requests.exceptions.RequestException(Fore.RED + "Ошибка запроса." + Fore.RESET)
|
||||
json = resp.json()
|
||||
return {
|
||||
"id": json['id'],
|
||||
"workspace_id": json['activeWorkspace'],
|
||||
"email": json["email"],
|
||||
"username": json["name"]
|
||||
}
|
||||
|
||||
def getenvs(self):
|
||||
"""Получает нужные USER_ID и WORKSPACE_ID с сервера Clockify"""
|
||||
user = self.get_user()
|
||||
with open(config.ENV_TXT, "w") as file:
|
||||
try:
|
||||
file.write(f"USER_ID={resp.json()['id']}\nWORKSPACE_ID={resp.json()['activeWorkspace']}")
|
||||
file.write(f"USER_ID={user['id']}\nWORKSPACE_ID={user['workspace_id']}")
|
||||
logger.info(f"{Fore.GREEN}Необходимые переменные были сохранены в файл {config.ENV_TXT}{Fore.RESET}")
|
||||
except Exception as e:
|
||||
logger.error(f"{Fore.RED}Не удалось сохранить необходимые переменные.{Fore.RESET}")
|
||||
|
||||
@@ -23,7 +23,8 @@ class RedmineAPI(RedmineConfig):
|
||||
"""
|
||||
def __init__(self, token):
|
||||
super().__init__()
|
||||
self.token = self._check_token(token)
|
||||
self.token = token
|
||||
self._check_token()
|
||||
|
||||
def track_activities(self, activities: list[Activity]) -> list[Activity]:
|
||||
"""Заносит трудочасы на сервер Redmine"""
|
||||
@@ -75,12 +76,21 @@ class RedmineAPI(RedmineConfig):
|
||||
if act.get("active")
|
||||
]
|
||||
|
||||
def _check_token(self, token):
|
||||
"""Проверяет токен"""
|
||||
resp = requests.get(self.base_url + self.my_account_url, headers={"X-Redmine-Api-Key": token})
|
||||
def get_user(self):
|
||||
"""Проверяет токен и возвращает данные юзера"""
|
||||
resp = requests.get(self.base_url + self.my_account_url, headers={"X-Redmine-Api-Key": self.token})
|
||||
if not resp.ok:
|
||||
logger.error("Invalid Token for RedmineAPI")
|
||||
raise InvalidToken
|
||||
resp = resp.json()['user']
|
||||
logger.debug({"login": resp["login"], "email": resp["mail"], "full_name": f'{resp["firstname"]} {resp["lastname"]}'})
|
||||
return token
|
||||
json = resp.json()["user"]
|
||||
return {
|
||||
"id": json["id"],
|
||||
"first_name": json["firstname"],
|
||||
"last_name": json["lastname"],
|
||||
"email": json["mail"],
|
||||
"username": json["login"]
|
||||
}
|
||||
|
||||
def _check_token(self):
|
||||
user = self.get_user()
|
||||
logging.debug(user)
|
||||
|
||||
@@ -7,6 +7,7 @@ from enums import ClockifyProjects, VetroProjects
|
||||
@dataclass
|
||||
class Activity:
|
||||
id: str
|
||||
author_id: int
|
||||
vetro_project_id: VetroProjects
|
||||
task_id: int
|
||||
tag_id: str
|
||||
|
||||
@@ -1,44 +1,60 @@
|
||||
CREATE TABLE IF NOT EXISTS "activities" (
|
||||
"id" VARCHAR NOT NULL UNIQUE,
|
||||
"description" VARCHAR NOT NULL,
|
||||
"time_spent" NUMERIC(3, 3) NOT NULL,
|
||||
"date_start" DATETIME NOT NULL,
|
||||
"date_end" DATETIME,
|
||||
"is_tracked" BOOLEAN NOT NULL DEFAULT false,
|
||||
"vetro_project_id" VARCHAR NOT NULL,
|
||||
"tag_id" VARCHAR NOT NULL,
|
||||
"project_id" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id"),
|
||||
FOREIGN KEY ("tag_id") REFERENCES "tags"("id") ON UPDATE NO ACTION ON DELETE NO ACTION,
|
||||
FOREIGN KEY ("vetro_project_id") REFERENCES "vetro_projects"("id") ON UPDATE NO ACTION ON DELETE NO ACTION
|
||||
CREATE TABLE IF NOT EXISTS "activity" (
|
||||
"id" VARCHAR NOT NULL UNIQUE,
|
||||
"description" VARCHAR NOT NULL,
|
||||
"author_id" INTEGER NOT NULL,
|
||||
"time_spent" NUMERIC NOT NULL,
|
||||
"date_start" DATETIME NOT NULL,
|
||||
"date_end" DATETIME,
|
||||
"is_tracked" BOOLEAN NOT NULL DEFAULT false,
|
||||
"vetro_project_id" VARCHAR NOT NULL,
|
||||
"tag_id" VARCHAR NOT NULL,
|
||||
"project_id" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id"),
|
||||
FOREIGN KEY ("author_id") REFERENCES "user"("id")
|
||||
ON UPDATE NO ACTION ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "tags" (
|
||||
"id" VARCHAR NOT NULL UNIQUE,
|
||||
"title" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id")
|
||||
CREATE TABLE IF NOT EXISTS "tag" (
|
||||
"id" VARCHAR NOT NULL UNIQUE,
|
||||
"title" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "vetro_projects" (
|
||||
"id" VARCHAR NOT NULL UNIQUE,
|
||||
"title" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id")
|
||||
CREATE TABLE IF NOT EXISTS "vetro_project" (
|
||||
"id" VARCHAR NOT NULL UNIQUE,
|
||||
"title" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "activity_types" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"title" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id")
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "activity_type" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"title" VARCHAR NOT NULL,
|
||||
PRIMARY KEY("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "time_entry" (
|
||||
"id" INTEGER PRIMARY KEY,
|
||||
"date" DATETIME NOT NULL,
|
||||
"description" VARCHAR NOT NULL,
|
||||
"activity_id" VARCHAR NOT NULL,
|
||||
"time_spent" NUMERIC(3, 3) NOT NULL,
|
||||
"task_id" INTEGER NOT NULL,
|
||||
"activity_type_id" INTEGER NOT NULL,
|
||||
FOREIGN KEY ("activity_id") REFERENCES "activities"("id") ON UPDATE NO ACTION ON DELETE NO ACTION,
|
||||
FOREIGN KEY ("activity_type_id") REFERENCES "activity_types"("id") ON UPDATE NO ACTION ON DELETE NO ACTION
|
||||
);
|
||||
"id" INTEGER,
|
||||
"author_id" INTEGER NOT NULL,
|
||||
"description" VARCHAR NOT NULL,
|
||||
"date" DATETIME NOT NULL,
|
||||
"activity_id" VARCHAR NOT NULL,
|
||||
"time_spent" NUMERIC NOT NULL,
|
||||
"task_id" INTEGER NOT NULL,
|
||||
"activity_type_id" INTEGER NOT NULL,
|
||||
PRIMARY KEY("id"),
|
||||
FOREIGN KEY ("author_id") REFERENCES "user"("id")
|
||||
ON UPDATE NO ACTION ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "user" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"first_name" VARCHAR,
|
||||
"last_name" VARCHAR,
|
||||
"clockify_user_id" VARCHAR NOT NULL UNIQUE,
|
||||
"redmine_user_id" VARCHAR NOT NULL UNIQUE,
|
||||
"clockify_email" VARCHAR UNIQUE,
|
||||
"clockify_username" VARCHAR UNIQUE,
|
||||
"redmine_email" VARCHAR UNIQUE,
|
||||
"redmine_username" VARCHAR UNIQUE,
|
||||
PRIMARY KEY("id")
|
||||
);
|
||||
|
||||
78
db/db.py
78
db/db.py
@@ -34,16 +34,17 @@ class Database:
|
||||
is_updated = True
|
||||
for act in activities:
|
||||
if is_updated:
|
||||
tags = self.cursor.execute("SELECT * FROM tags WHERE id = ?", (act.tag_id, )).fetchall()
|
||||
vetro_projects = self.cursor.execute("SELECT * FROM vetro_projects WHERE id = ?", (act.vetro_project_id, )).fetchall()
|
||||
tags = self.cursor.execute("SELECT * FROM tag WHERE id = ?", (act.tag_id, )).fetchall()
|
||||
vetro_projects = self.cursor.execute("SELECT * FROM vetro_project WHERE id = ?", (act.vetro_project_id, )).fetchall()
|
||||
if not tags or not vetro_projects:
|
||||
is_updated = False
|
||||
try:
|
||||
self.cursor.execute(
|
||||
"INSERT INTO activities VALUES(?,?,?,?,?,?,?,?,?)",
|
||||
"INSERT INTO activity VALUES(?,?,?,?,?,?,?,?,?,?)",
|
||||
(
|
||||
act.id,
|
||||
act.description,
|
||||
act.author_id,
|
||||
act.time_spent,
|
||||
act.date_start,
|
||||
act.date_end,
|
||||
@@ -55,7 +56,7 @@ class Database:
|
||||
)
|
||||
except sq.IntegrityError:
|
||||
self.cursor.execute(
|
||||
"""UPDATE activities
|
||||
"""UPDATE activity
|
||||
SET description = ?, time_spent = ?, date_start = ?, date_end = ?, is_tracked = ?, vetro_project_id = ?, tag_id = ?, project_id = ?
|
||||
WHERE id = ?""",
|
||||
(
|
||||
@@ -81,15 +82,16 @@ class Database:
|
||||
try:
|
||||
self.cursor.execute(
|
||||
"""INSERT INTO time_entry
|
||||
(date, description, activity_id, time_spent, task_id, activity_type_id)
|
||||
VALUES(?,?,?,?,?,?)""",
|
||||
(date, description, activity_id, time_spent, task_id, activity_type_id, author_id)
|
||||
VALUES(?,?,?,?,?,?,?)""",
|
||||
(
|
||||
act.date_start.date(),
|
||||
act.description,
|
||||
act.id,
|
||||
act.time_spent,
|
||||
act.task_id,
|
||||
ActivityTypes.DEVELOPMENT
|
||||
ActivityTypes.DEVELOPMENT,
|
||||
act.author_id
|
||||
)
|
||||
)
|
||||
self.con.commit()
|
||||
@@ -101,10 +103,10 @@ class Database:
|
||||
"""Сохраняет в базу данных теги Clockify"""
|
||||
for tag in tags:
|
||||
try:
|
||||
self.cursor.execute("INSERT INTO tags VALUES(?, ?)",(tag.id,tag.title))
|
||||
self.cursor.execute("INSERT INTO tag VALUES(?, ?)",(tag.id,tag.title))
|
||||
except sq.IntegrityError:
|
||||
self.cursor.execute(
|
||||
"""UPDATE tags
|
||||
"""UPDATE tag
|
||||
SET title = ?
|
||||
WHERE id = ?""",
|
||||
(tag.title, tag.id)
|
||||
@@ -117,10 +119,10 @@ class Database:
|
||||
"""Сохраняет в базу данных Проекты (ДО) Ветро Clockify"""
|
||||
for vetro_project in vetro_projects:
|
||||
try:
|
||||
self.cursor.execute("INSERT INTO vetro_projects VALUES(?, ?)", (vetro_project.id, vetro_project.title))
|
||||
self.cursor.execute("INSERT INTO vetro_project VALUES(?, ?)", (vetro_project.id, vetro_project.title))
|
||||
except sq.IntegrityError:
|
||||
self.cursor.execute("""
|
||||
UPDATE vetro_projects
|
||||
UPDATE vetro_project
|
||||
SET title = ?
|
||||
WHERE id = ?""",
|
||||
(vetro_project.title, vetro_project.id)
|
||||
@@ -132,13 +134,63 @@ class Database:
|
||||
"""Сохраняет в базу данных Деятельности Redmine"""
|
||||
for activity_type in activity_types:
|
||||
try:
|
||||
self.cursor.execute("INSERT INTO activity_types VALUES(?, ?)", (activity_type.id, activity_type.title))
|
||||
self.cursor.execute("INSERT INTO activity_type VALUES(?, ?)", (activity_type.id, activity_type.title))
|
||||
except sq.IntegrityError:
|
||||
self.cursor.execute("""
|
||||
UPDATE activity_types
|
||||
UPDATE activity_type
|
||||
SET title = ?
|
||||
WHERE id = ?""",
|
||||
(activity_type.title, activity_type.id)
|
||||
)
|
||||
finally:
|
||||
self.con.commit()
|
||||
|
||||
def insert_user(self, **kwargs):
|
||||
"""Сохраняет в базу данных пользователя"""
|
||||
print(kwargs.values())
|
||||
try:
|
||||
self.cursor.execute("""INSERT INTO user(
|
||||
clockify_user_id, clockify_email, clockify_username,
|
||||
redmine_user_id, redmine_email, first_name, last_name, redmine_username)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?)""", [v for k, v in kwargs.items()])
|
||||
except sq.IntegrityError:
|
||||
self.cursor.execute("""
|
||||
UPDATE user
|
||||
SET clockify_email = ?,
|
||||
clockify_username = ?,
|
||||
redmine_email = ?,
|
||||
first_name = ?,
|
||||
last_name = ?,
|
||||
redmine_username = ?
|
||||
WHERE redmine_user_id = ? AND clockify_user_id = ?""",
|
||||
(
|
||||
kwargs["clockify_email"],
|
||||
kwargs["clockify_username"],
|
||||
kwargs["redmine_email"],
|
||||
kwargs["first_name"],
|
||||
kwargs["last_name"],
|
||||
kwargs["redmine_username"],
|
||||
kwargs["redmine_user_id"],
|
||||
kwargs["clockify_user_id"]
|
||||
)
|
||||
)
|
||||
finally:
|
||||
self.con.commit()
|
||||
|
||||
def get_user_by(self, by_field: str, value: str):
|
||||
"""Получает пользователя из бд"""
|
||||
try:
|
||||
user = self.cursor.execute(f"SELECT * FROM user WHERE {by_field} = ?", (value,)).fetchone()
|
||||
return {
|
||||
"id": user[0],
|
||||
"first_name": user[1],
|
||||
"last_name": user[2],
|
||||
"clockify_user_id": user[3],
|
||||
"redmine_user_id": user[4],
|
||||
"clockify_email": user[5],
|
||||
"clockify_username": user[6],
|
||||
"redmine_email": user[7],
|
||||
"redmine_username": user[8]
|
||||
}
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
|
||||
20
track.py
20
track.py
@@ -62,7 +62,21 @@ def main():
|
||||
logger.info(f'{Fore.YELLOW}Проверьте файл "{config.ENV_TXT}". Если необходимых переменных в нём нет, то просто удалите его.{Fore.RESET}')
|
||||
return
|
||||
red = RedmineAPI(config.REDMINE_TOKEN)
|
||||
|
||||
user = clock.get_user()
|
||||
clock_user = {
|
||||
"clockify_user_id": user["id"],
|
||||
"clockify_email": user["email"],
|
||||
"clockify_username": user["username"],
|
||||
}
|
||||
user = red.get_user()
|
||||
red_user = {
|
||||
"redmine_user_id": user["id"],
|
||||
"redmine_email": user["email"],
|
||||
"first_name": user["first_name"],
|
||||
"last_name": user["last_name"],
|
||||
"redmine_username": user["username"]
|
||||
}
|
||||
db.insert_user(**clock_user, **red_user)
|
||||
if args.init_db:
|
||||
logger.info(Fore.YELLOW + "Обновление базы данных..." + Fore.RESET)
|
||||
try:
|
||||
@@ -74,13 +88,13 @@ def main():
|
||||
else:
|
||||
logger.info(Fore.GREEN + "База данных теперь актуальна." + Fore.RESET)
|
||||
|
||||
activities = clock.get_activities(
|
||||
activities = sorted(clock.get_activities(
|
||||
**{
|
||||
"start": format_date(start),
|
||||
"end": format_date(end),
|
||||
"page-size": args.page_size,
|
||||
},
|
||||
)
|
||||
), key=lambda x: x.date_start)
|
||||
db.insert_activities(activities)
|
||||
if args.debug:
|
||||
logger.debug("Пропускаем этап загрузки трудочасов")
|
||||
|
||||
Reference in New Issue
Block a user