добавил систему миграций и связь одной записи Redmine с несколькими активностями Clockify и запретил отправку трудочасов при инициализации базы данных
This commit is contained in:
91
db/db.py
91
db/db.py
@@ -1,4 +1,6 @@
|
||||
import logging
|
||||
from importlib.util import module_from_spec, spec_from_file_location
|
||||
from pathlib import Path
|
||||
import sqlite3 as sq
|
||||
|
||||
from colorama import Fore
|
||||
@@ -31,6 +33,40 @@ class Database:
|
||||
logger.error("Ошибка при инициализации БД: ", e)
|
||||
self.con.rollback()
|
||||
|
||||
def run_migrations(self, migrations_path: str):
|
||||
"""Выполняет новые миграции и сохраняет время их применения."""
|
||||
self.cursor.execute(
|
||||
"""CREATE TABLE IF NOT EXISTS migration (
|
||||
name TEXT PRIMARY KEY,
|
||||
executed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)"""
|
||||
)
|
||||
self.con.commit()
|
||||
applied = {
|
||||
row[0]
|
||||
for row in self.cursor.execute("SELECT name FROM migration").fetchall()
|
||||
}
|
||||
for path in sorted(Path(migrations_path).glob("*.py")):
|
||||
if path.name.startswith("_") or path.name in applied:
|
||||
continue
|
||||
try:
|
||||
spec = spec_from_file_location(f"migration_{path.stem}", path)
|
||||
if not spec or not spec.loader:
|
||||
raise ImportError(f"Не удалось загрузить миграцию {path.name}")
|
||||
module = module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
module.upgrade(self.con)
|
||||
self.cursor.execute(
|
||||
"INSERT INTO migration(name) VALUES(?)",
|
||||
(path.name,),
|
||||
)
|
||||
self.con.commit()
|
||||
except Exception:
|
||||
self.con.rollback()
|
||||
logger.exception("Не удалось выполнить миграцию %s", path.name)
|
||||
raise
|
||||
logger.info("Миграция %s успешно выполнена", path.name)
|
||||
|
||||
def insert_activities(self, activities: list[Activity]):
|
||||
"""Сохраняет в базу данных активности Clockify"""
|
||||
try:
|
||||
@@ -80,24 +116,42 @@ class Database:
|
||||
else:
|
||||
self.con.commit()
|
||||
|
||||
def insert_time_entries(self, activities: list[Activity]):
|
||||
"""Сохраняет в базу данных трудочасы Redmine"""
|
||||
def insert_time_entry(
|
||||
self,
|
||||
activity: Activity,
|
||||
source_activities: list[Activity],
|
||||
redmine_time_entry_id: int,
|
||||
):
|
||||
"""Сохраняет запись Redmine и связывает с ней активности Clockify."""
|
||||
try:
|
||||
for act in activities:
|
||||
self.cursor.execute(
|
||||
"""INSERT INTO time_entry
|
||||
(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,
|
||||
act.author_id
|
||||
)
|
||||
self.cursor.execute(
|
||||
"""INSERT INTO time_entry
|
||||
(id, date, description, time_spent, task_id, activity_type_id, author_id)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
date = excluded.date,
|
||||
description = excluded.description,
|
||||
time_spent = excluded.time_spent,
|
||||
task_id = excluded.task_id,
|
||||
activity_type_id = excluded.activity_type_id,
|
||||
author_id = excluded.author_id""",
|
||||
(
|
||||
redmine_time_entry_id,
|
||||
activity.date_start.date(),
|
||||
activity.description,
|
||||
activity.time_spent,
|
||||
activity.task_id,
|
||||
ActivityTypes.DEVELOPMENT,
|
||||
activity.author_id,
|
||||
)
|
||||
)
|
||||
self.cursor.executemany(
|
||||
"UPDATE activity SET time_entry_id = ? WHERE id = ?",
|
||||
[
|
||||
(redmine_time_entry_id, source_activity.id)
|
||||
for source_activity in source_activities
|
||||
],
|
||||
)
|
||||
except sq.IntegrityError as e:
|
||||
self.con.rollback()
|
||||
logger.error(f"Не удалось сохранить трудочасы: {e}")
|
||||
@@ -306,7 +360,10 @@ class Database:
|
||||
def has_time_entry(self, activity_id: str) -> bool:
|
||||
"""Проверяет, есть ли запись о времени для активности"""
|
||||
try:
|
||||
result = self.cursor.execute("SELECT * FROM time_entry WHERE activity_id = ?", (activity_id,)).fetchone()
|
||||
result = self.cursor.execute(
|
||||
"SELECT time_entry_id FROM activity WHERE id = ? AND time_entry_id IS NOT NULL",
|
||||
(activity_id,),
|
||||
).fetchone()
|
||||
return bool(result)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
Reference in New Issue
Block a user