From 040ca140c8cffe304f28abac7dcc689caf2e3c4c Mon Sep 17 00:00:00 2001 From: Eduard Date: Tue, 20 May 2025 17:29:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=91=D0=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + __init__.py | 0 classes/__init__.py | 1 + classes.py => classes/classes.py | 2 +- config/__init__.py | 5 ++ db/__init__.py | 0 db/db.py | 126 +++++++++++++++++++++++++++++++ track.py | 3 +- 8 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 __init__.py create mode 100644 classes/__init__.py rename classes.py => classes/classes.py (100%) create mode 100644 db/__init__.py create mode 100644 db/db.py diff --git a/.gitignore b/.gitignore index 5e49b42..1afcbe2 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,9 @@ cover/ # Django stuff: *.log +*.db +*.sqlite3 +*.sql local_settings.py db.sqlite3 db.sqlite3-journal diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/classes/__init__.py b/classes/__init__.py new file mode 100644 index 0000000..9c2db31 --- /dev/null +++ b/classes/__init__.py @@ -0,0 +1 @@ +from .classes import Activity, Tag diff --git a/classes.py b/classes/classes.py similarity index 100% rename from classes.py rename to classes/classes.py index 8c3bc27..8a78b35 100644 --- a/classes.py +++ b/classes/classes.py @@ -7,9 +7,9 @@ from enums import ClockifyProjects, VetroProjects @dataclass class Activity: id: str - task_id: int vetro_project_id: VetroProjects vetro_project_title: str + task_id: int task_title: str tag_id: str description: str diff --git a/config/__init__.py b/config/__init__.py index 8a61855..62ff352 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -1,6 +1,8 @@ import argparse from pydantic_settings import BaseSettings +from db.db import Database + from .clockify import ClockifyConfig # noqa: F401 from .redmine import RedmineConfig # noqa: F401 @@ -12,6 +14,8 @@ class Config(BaseSettings): CLOCKIFY_USER_ID: str CLOCKIFY_WORKSPACE_ID: str + DB_NAME: str = "RedmineTracker.db" + DATETIME_FORMAT: str = "%Y-%m-%dT%H:%M:%SZ" VETRO_PROJECTS: dict = { @@ -57,3 +61,4 @@ def configure_argument_parser(): arg_parser = configure_argument_parser() config = Config() +db = Database(config.DB_NAME) diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/db/db.py b/db/db.py new file mode 100644 index 0000000..f7c5d21 --- /dev/null +++ b/db/db.py @@ -0,0 +1,126 @@ +import logging +import sqlite3 as sq + +from classes import Activity, Tag + + +class Database: + def __init__(self, db_name: str): + try: + self.con = sq.connect(db_name) + self.con.cursor().executescript('''CREATE TABLE IF NOT EXISTS "activities" ( + "id" VARCHAR NOT NULL UNIQUE, + "vetro_project_id" VARCHAR NOT NULL, + "vetro_project_title" VARCHAR NOT NULL, + "task_id" VARCHAR NOT NULL, + "description" VARCHAR NOT NULL, + "task_title" VARCHAR NOT NULL, + "tag_id" VARCHAR NOT NULL, + "project_id" VARCHAR NOT NULL, + "time_spent" NUMERIC NOT NULL, + "date_start" DATETIME NOT NULL, + "date_end" DATETIME, + "is_tracked" BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY("id"), + FOREIGN KEY ("tag_id") REFERENCES "tags"("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 "vetro_projects" ( + "id" VARCHAR NOT NULL UNIQUE, + "title" VARCHAR NOT NULL, + PRIMARY KEY("id"), + FOREIGN KEY ("id") REFERENCES "activities"("vetro_project_id") + ON UPDATE NO ACTION ON DELETE NO ACTION +); + +CREATE TABLE IF NOT EXISTS "time_entry" ( + "id" INTEGER NOT NULL UNIQUE, + "activity_id" VARCHAR NOT NULL, + "task_id" INTEGER NOT NULL, + "date" DATETIME NOT NULL, + "time_spent" NUMERIC NOT NULL, + "description" VARCHAR NOT NULL, + "activity_type" INTEGER NOT NULL CHECK(activity_type in (9, 8, 57, 58, 60)), + PRIMARY KEY("id"), + FOREIGN KEY ("activity_id") REFERENCES "activities"("id") + ON UPDATE NO ACTION ON DELETE NO ACTION, + FOREIGN KEY ("task_id") REFERENCES "activities"("task_id") + ON UPDATE NO ACTION ON DELETE NO ACTION +); +''') + except Exception as e: + raise(e) + + def insert_activities(self, activities: list[Activity]): + cur = self.con.cursor() + for act in activities: + try: + cur.execute( + "INSERT INTO activities VALUES(?, ?, ? ,? ,? ,? ,? ,? ,? ,? ,? ,?)", + ( + act.id,act.vetro_project_id, + act.vetro_project_title, + act.task_id, + act.description, + act.task_title, + act.tag_id, + act.project_id, + act.time_spent, + act.date_start, + act.date_end, + act.is_tracked + ) + ) + self.con.commit() + except Exception as e: + logging.error(e) + + # Доделать + def insert_time_entries(self, activities: list[Activity]): + cur = self.con.cursor() + for act in activities: + try: + cur.execute( + "INSERT INTO time_entry VALUES(?, ?, ? ,? ,? ,? ,? ,? ,? ,? ,? ,?)", + ( + act.id,act.vetro_project_id, + act.vetro_project_title, + act.task_id, + act.description, + act.task_title, + act.tag_id, + act.project_id, + act.time_spent, + act.date_start, + act.date_end, + act.is_tracked + ) + ) + self.con.commit() + except Exception as e: + logging.error(e) + + + def insert_tags(self, tags: list[Tag]): + cur = self.con.cursor() + for tag in tags: + try: + cur.execute( + "INSERT INTO tags VALUES(?, ?)", + ( + tag.id, + tag.title + ) + ) + self.con.commit() + except Exception as e: + logging.error(e) + + diff --git a/track.py b/track.py index d37118d..4056fc8 100644 --- a/track.py +++ b/track.py @@ -2,7 +2,7 @@ import logging import sys from api import ClockifyAPI, RedmineAPI -from config import arg_parser, config +from config import arg_parser, config, db from utils import format_date, str_to_date, today_start, today_end @@ -37,6 +37,7 @@ def main(): "page-size": args.page_size, }, ) + db.insert_activities(activities) # tracked_activities = red.track_activities(activities) # clock.mark_tracked(tracked_activities) for activity in activities: