WIP: config file support

This commit is contained in:
3wc
2024-12-06 21:30:34 -05:00
parent 48905953ca
commit 4fcf972e17
6 changed files with 77 additions and 29 deletions

View File

@ -1,16 +1,21 @@
#!/usr/bin/env python3.7 #!/usr/bin/env python3.7
import os
import csv import csv
import logging import logging
from datetime import datetime from datetime import datetime
from itertools import chain from itertools import chain
from pathlib import Path from pathlib import Path
import sys import sys
import tomllib
import click import click
import requests import requests
from peewee import fn, JOIN from peewee import fn, JOIN
from textual.logging import TextualHandler from textual.logging import TextualHandler
from xdg.BaseDirectory import xdg_config_home
from .db import ( from .db import (
db, db,
HamsterCategory, HamsterCategory,
@ -21,19 +26,30 @@ from .db import (
KimaiActivity, KimaiActivity,
HamsterActivityKimaiMapping, HamsterActivityKimaiMapping,
HamsterFactKimaiImport, HamsterFactKimaiImport,
HamsterFactTag,
) )
from .kimaiapi import KimaiAPI, Timesheet from .kimaiapi import KimaiAPI, Timesheet
from .sync import sync from .sync import sync
CONFIG_FILE = Path(xdg_config_home) / "hamstertools.toml"
HAMSTER_DIR = Path.home() / ".local/share/hamster" HAMSTER_DIR = Path.home() / ".local/share/hamster"
HAMSTER_FILE = HAMSTER_DIR / "hamster.db" HAMSTER_FILE = HAMSTER_DIR / "hamster.db"
db.init(HAMSTER_FILE) db.init(HAMSTER_FILE)
@click.group() @click.group(context_settings={"auto_envvar_prefix": "HAMSTERTOOL"})
@click.option("-d", "--debug", is_flag=True) @click.option("-d", "--debug", is_flag=True)
def cli(debug): @click.option("--config", default=CONFIG_FILE, type=click.Path())
@click.option("--kimai-api-key", envvar="KIMAI_API_KEY")
@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, kimai_username, kimai_api_key):
if os.path.exists(config):
with open(config, "rb") as f:
ctx.default_map = tomllib.load(f)
if debug: if debug:
logging.basicConfig() logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
@ -310,8 +326,12 @@ def find_duplicates():
@cli.group() @cli.group()
def kimai(): @click.pass_context
pass def kimai(ctx, kimai_username=None, kimai_api_key=None):
ctx.ensure_object(dict)
ctx.obj['KIMAI_USERNAME'] = kimai_username
ctx.obj['KIMAI_API_KEY'] = kimai_api_key
def _get_kimai_mapping_file(path, category_search=None): def _get_kimai_mapping_file(path, category_search=None):
@ -354,7 +374,6 @@ def _get_kimai_mapping_file(path, category_search=None):
multiple=True, multiple=True,
) )
@click.argument("username") @click.argument("username")
@click.argument("api_key", envvar="KIMAI_API_KEY")
@click.option("--just-errors", "just_errors", is_flag=True, help="Only display errors") @click.option("--just-errors", "just_errors", is_flag=True, help="Only display errors")
@click.option("--ignore-activities", is_flag=True, help="Ignore missing activities") @click.option("--ignore-activities", is_flag=True, help="Ignore missing activities")
def check(username, api_key, just_errors, ignore_activities, mapping_path=None): def check(username, api_key, just_errors, ignore_activities, mapping_path=None):
@ -643,7 +662,7 @@ def _csv(
@click.argument("after") @click.argument("after")
@click.argument("before") @click.argument("before")
def _import(search, after, before): def _import(search, after, before):
api = KimaiAPI() api = KimaiAPI(username=KIMAI_USERNAME, api_key=KIMAI_API_KEY)
SEARCH = "auto" SEARCH = "auto"
@ -651,6 +670,8 @@ def _import(search, after, before):
HamsterFact.select(HamsterFact, HamsterActivity, HamsterCategory) HamsterFact.select(HamsterFact, HamsterActivity, HamsterCategory)
.join(HamsterActivity, JOIN.LEFT_OUTER) .join(HamsterActivity, JOIN.LEFT_OUTER)
.join(HamsterCategory, JOIN.LEFT_OUTER) .join(HamsterCategory, JOIN.LEFT_OUTER)
.switch(HamsterFact)
.join(HamsterFactTag, JOIN.LEFT_OUTER)
.where( .where(
(HamsterFact.start_time > datetime.strptime(after, "%Y-%m-%d")) (HamsterFact.start_time > datetime.strptime(after, "%Y-%m-%d"))
& (HamsterFact.start_time < datetime.strptime(before, "%Y-%m-%d")) & (HamsterFact.start_time < datetime.strptime(before, "%Y-%m-%d"))
@ -719,15 +740,12 @@ def _import(search, after, before):
description=f.description description=f.description
if f.description != "" if f.description != ""
else mapping.kimai_description, else mapping.kimai_description,
# tags=f.tags if f.tags != '' else mapping.kimai_tags tags=" ".join([t.tag.name for t in f.tags]) if f.tags != '' else mapping.kimai_tags
) )
r = t.upload().json() r = t.upload().json()
if r.get("code", 200) != 200: if r.get("code", 200) != 200:
print(r) print(r)
print(f"{f.id} ({f.activity.category.name} » {f.activity.name})") print(f"{f.id} ({f.activity.category.name} » {f.activity.name})")
from pdb import set_trace
set_trace()
else: else:
HamsterFactKimaiImport.create(hamster_fact=f, kimai_id=r["id"]).save() HamsterFactKimaiImport.create(hamster_fact=f, kimai_id=r["id"]).save()
@ -758,8 +776,12 @@ def reset():
@db_.command("sync") @db_.command("sync")
def kimai_db_sync(): @click.pass_context
sync() def kimai_db_sync(ctx):
sync(
username=ctx.obj['KIMAI_USERNAME'],
api_key=ctx.obj['KIMAI_API_KEY']
)
@db_.command() @db_.command()
@ -800,10 +822,15 @@ def mapping2db(mapping_path=None, global_=False):
@cli.command() @cli.command()
def app(): @click.pass_context
def app(ctx):
from .app import HamsterToolsApp from .app import HamsterToolsApp
app = HamsterToolsApp() app = HamsterToolsApp(
kimai_api_url=ctx.obj["KIMAI_API_URL"],
kimai_username=ctx.obj["KIMAI_USERNAME"],
kimai_api_key=ctx.obj["KIMAI_API_KEY"]
)
app.run() app.run()

View File

@ -21,20 +21,23 @@ class HamsterToolsApp(App):
@property @property
def api(self) -> KimaiAPI: def api(self) -> KimaiAPI:
if self.api_ is None: if self.api_ is None:
self.api_ = KimaiAPI() self.api_ = KimaiAPI(self.kimai_api_key)
return self.api_ return self.api_
def __init__(self): def __init__(self, kimai_api_key):
self.MODES = { self.kimai_api_key = kimai_api_key
"hamster": HamsterScreen(), self.add_mode("hamster", HamsterScreen())
"kimai": KimaiScreen(), self.add_mode("kimai", KimaiScreen())
} # self.mode MODES = {
# "hamster": HamsterScreen(),
# "kimai": KimaiScreen(),
# }
super().__init__() super().__init__()
def on_mount(self) -> None: def on_mount(self) -> None:
self.switch_mode("hamster") self.switch_mode("hamster")
def action_quit(self) -> None: async def action_quit(self) -> None:
db.close() db.close()
self.exit() self.exit()

View File

@ -1,6 +1,7 @@
from datetime import datetime from datetime import datetime
from peewee import ( from peewee import (
CompositeKey,
SqliteDatabase, SqliteDatabase,
Model, Model,
CharField, CharField,
@ -31,6 +32,14 @@ class HamsterActivity(Model):
table_name = "activities" table_name = "activities"
class HamsterTag(Model):
name = CharField()
class Meta:
database = db
table_name = "tags"
class HamsterFact(Model): class HamsterFact(Model):
activity = ForeignKeyField(HamsterActivity, backref="facts") activity = ForeignKeyField(HamsterActivity, backref="facts")
start_time = DateTimeField() start_time = DateTimeField()
@ -42,6 +51,16 @@ class HamsterFact(Model):
table_name = "facts" table_name = "facts"
class HamsterFactTag(Model):
fact = ForeignKeyField(HamsterFact, backref="tags")
tag = ForeignKeyField(HamsterTag, backref="facts")
class Meta:
database = db
table_name = "fact_tags"
primary_key = CompositeKey('fact', 'tag')
class KimaiCustomer(Model): class KimaiCustomer(Model):
visible = BooleanField(default=True) visible = BooleanField(default=True)
name = CharField() name = CharField()

View File

@ -15,12 +15,10 @@ class KimaiAPI(object):
KIMAI_API_URL = "https://kimai.autonomic.zone/api" KIMAI_API_URL = "https://kimai.autonomic.zone/api"
# KIMAI_API_URL = "https://kimaitest.autonomic.zone/api" # KIMAI_API_URL = "https://kimaitest.autonomic.zone/api"
KIMAI_USERNAME = "3wordchant" def __init__(self, username=None, api_key=None):
KIMAI_API_KEY = os.environ["KIMAI_API_KEY"] self.auth_headers = {"X-AUTH-USER": username, "X-AUTH-TOKEN": api_key}
# NOTE: Uncomment the following line to enable requests_cache, which can make development a *lot* faster
auth_headers = {"X-AUTH-USER": KIMAI_USERNAME, "X-AUTH-TOKEN": KIMAI_API_KEY} # TODO: Add a config option or something for this
def __init__(self):
# requests_cache.install_cache("kimai", backend="sqlite", expire_after=1800) # requests_cache.install_cache("kimai", backend="sqlite", expire_after=1800)
self.customers_json = self.get("customers", {"visible": 3}) self.customers_json = self.get("customers", {"visible": 3})
self.projects_json = self.get("projects", {"visible": 3, "ignoreDates": 1}) self.projects_json = self.get("projects", {"visible": 3, "ignoreDates": 1})

View File

@ -12,8 +12,8 @@ from .db import (
) )
def sync() -> None: def sync(username, api_key) -> None:
api = KimaiAPI() api = KimaiAPI(username, api_key)
KimaiCustomer.delete().execute() KimaiCustomer.delete().execute()
KimaiProject.delete().execute() KimaiProject.delete().execute()

View File

@ -4,3 +4,4 @@ requests-cache==1.1.1
textual==0.44.1 textual==0.44.1
textual-autocomplete==2.1.0b0 textual-autocomplete==2.1.0b0
textual-dev==1.2.1 textual-dev==1.2.1
xdg==6.0.0