Preliminary clockify support

This commit is contained in:
3wc
2025-08-07 12:02:35 +01:00
parent db6d7ac640
commit fb49a24ae1
10 changed files with 273 additions and 56 deletions

View File

@ -9,6 +9,7 @@ import sys
import tomllib
import click
from clockify_sdk import Clockify
import requests
from peewee import fn, JOIN
from textual.logging import TextualHandler
@ -28,6 +29,9 @@ from .db import (
HamsterActivityKimaiMapping,
HamsterFactKimaiImport,
HamsterFactTag,
ClockifyProject,
HamsterClockifyMapping,
HamsterFactClockifyImport
)
from .kimaiapi import KimaiAPI, Timesheet
from .sync import sync
@ -43,11 +47,8 @@ db.init(HAMSTER_FILE)
@click.group(context_settings={"auto_envvar_prefix": "HAMSTERTOOL"})
@click.option("-d", "--debug", is_flag=True)
@click.option("--config", default=CONFIG_FILE, type=click.Path())
@click.option("--kimai-api-url", envvar="KIMAI_API_URL")
@click.option("--kimai-username", envvar="KIMAI_USERNAME")
@click.option("--kimai-api-key", envvar="KIMAI_API_KEY")
@click.pass_context
def cli(ctx, config, debug, kimai_api_url=None, kimai_username=None, kimai_api_key=None):
def cli(ctx, config, debug):
file_config = {}
if os.path.exists(config):
with open(config, "rb") as f:
@ -64,11 +65,8 @@ def cli(ctx, config, debug, kimai_api_url=None, kimai_username=None, kimai_api_k
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
ctx.obj = KimaiAPI(
kimai_username if kimai_username is not None else file_config.get("kimai_username"),
kimai_api_key if kimai_api_key is not None else file_config.get("kimai_api_key"),
kimai_api_url if kimai_api_url is not None else file_config.get("kimai_api_url"),
)
ctx.ensure_object(dict)
ctx.obj['config'] = file_config
@cli.group()
@ -334,7 +332,17 @@ def find_duplicates():
@cli.group()
def kimai():
@click.option("--kimai-api-url", envvar="KIMAI_API_URL")
@click.option("--kimai-username", envvar="KIMAI_USERNAME")
@click.option("--kimai-api-key", envvar="KIMAI_API_KEY")
@click.pass_context
def kimai(ctx, kimai_api_url, kimai_username, kimai_api_key):
file_config = ctx.obj['config']
ctx.obj['kimai'] = KimaiAPI(
kimai_username if kimai_username is not None else file_config.get("kimai_username"),
kimai_api_key if kimai_api_key is not None else file_config.get("kimai_api_key"),
kimai_api_url if kimai_api_url is not None else file_config.get("kimai_api_url"),
)
pass
@ -535,7 +543,7 @@ def _csv(
timestamp = datetime.now().strftime("%F")
output = f"kimai_{timestamp}.csv"
if type(mapping_path) == tuple:
if isinstance(mapping_path, tuple):
mapping_files = []
for mapping_path_item in mapping_path:
mapping_file = _get_kimai_mapping_file(mapping_path_item, category_search)
@ -552,7 +560,7 @@ def _csv(
for row in mapping_reader
}
if type(mapping_path) == tuple:
if isinstance(mapping_path, tuple):
for mapping_file in mapping_files:
mapping_file.close()
else:
@ -561,25 +569,13 @@ def _csv(
output_file = open(output, "w")
output_writer = csv.writer(output_file)
args = []
facts = (
HamsterFact.select(HamsterFact, HamsterActivity, HamsterCategory)
.join(HamsterActivity)
.join(HamsterCategory, JOIN.LEFT_OUTER)
)
sql = """
SELECT
facts.id, facts.start_time, facts.end_time, facts.description,
activities.id, activities.name,
categories.name, categories.id
FROM
facts
LEFT JOIN
activities ON facts.activity_id = activities.id
LEFT JOIN
categories ON activities.category_id = categories.id """
if category_search is not None:
facts = facts.where(HamsterCategory.name.contains(category_search))
@ -665,8 +661,9 @@ def _csv(
@click.argument("search")
@click.argument("after")
@click.argument("before")
def _import(search, after, before):
api = KimaiAPI(username=KIMAI_USERNAME, api_key=KIMAI_API_KEY)
@click.pass_context
def _import(ctx, search, after, before):
api = ctx.obj
SEARCH = "auto"
@ -760,11 +757,11 @@ def _import(search, after, before):
@kimai.group("db")
def db_():
def kimai_db():
pass
@db_.command()
@kimai_db.command()
def init():
db.create_tables(
[
@ -778,20 +775,20 @@ def init():
)
@db_.command()
@kimai_db.command()
def reset():
HamsterActivityKimaiMapping.delete().execute()
@db_.command("sync")
@click.pass_obj
def kimai_db_sync(api):
@kimai_db.command("sync")
@click.pass_context
def kimai_db_sync(ctx):
sync(
api
ctx.obj['kimai']
)
@db_.command()
@kimai_db.command()
@click.option(
"-g",
"--global",
@ -828,14 +825,63 @@ def mapping2db(mapping_path=None, global_=False):
)
@cli.group()
@click.option("--api-key", envvar="CLOCKIFY_API_KEY", help="Clockify API key")
@click.option("--workspace-id", envvar="CLOCKIFY_WORKSPACE_ID", help="Clockify workspace ID")
@click.pass_context
def clockify(ctx, api_key, workspace_id):
file_config = ctx.obj['config']
ctx.obj['clockify'] = Clockify(
api_key=api_key if api_key is not None else file_config.get("clockify_api_key"),
workspace_id=workspace_id if workspace_id is not None else file_config.get("clockify_workspace_id")
)
@clockify.group("db")
def clockify_db():
pass
@clockify_db.command("sync")
@click.pass_context
def clockify_db_sync(ctx):
from .clockify import sync_projects
count = sync_projects(ctx.obj['clockify'], db)
click.echo(f"Synced {count} Clockify projects")
@clockify_db.command("init")
def clockify_db_init():
db.create_tables(
[
ClockifyProject,
HamsterClockifyMapping,
HamsterFactClockifyImport,
]
)
@clockify.command("app")
@click.pass_context
def clockify_app(ctx):
from .app import HamsterToolsAppClockify
app = HamsterToolsAppClockify(ctx.obj['clockify'])
app.run()
@kimai.command("app")
@click.pass_context
def kimai_app(ctx):
from .app import HamsterToolsAppKimai
app = HamsterToolsAppKimai(ctx.obj['kimai'])
app.run()
@cli.command()
@click.pass_obj
def app(kimai_api):
def app():
from .app import HamsterToolsApp
app = HamsterToolsApp(
kimai_api
)
app = HamsterToolsApp()
app.run()