Remove ancient kimai csv command, add Clockify mapping2db

This commit is contained in:
3wc
2025-08-07 14:28:12 +01:00
parent fb49a24ae1
commit 68998be917
3 changed files with 36 additions and 154 deletions

View File

@ -513,149 +513,6 @@ def check(username, api_key, just_errors, ignore_activities, mapping_path=None):
found_activities.append(activity_str)
@kimai.command("csv")
@click.option(
"--mapping-path",
help="Mapping file (default ~/.local/share/hamster/mapping.kimai.csv)",
multiple=True,
)
@click.option("--output", help="Output file (default kimai.csv)")
@click.option("--category-search", help="Category search string")
@click.option("--after", help="Only show time entries after this date")
@click.option("--show-missing", help="Just report on the missing entries", is_flag=True)
@click.argument("username")
def _csv(
username,
mapping_path=None,
output=None,
category_search=None,
after=None,
show_missing=False,
):
"""
Export time tracking data in Kimai format
"""
if mapping_path is None:
mapping_path = HAMSTER_DIR / "mapping.kimai.csv"
if output is None:
timestamp = datetime.now().strftime("%F")
output = f"kimai_{timestamp}.csv"
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)
next(mapping_file)
mapping_files.append(mapping_file)
mapping_reader = csv.reader(chain(*mapping_files))
else:
mapping_file = _get_kimai_mapping_file(mapping_path, category_search)
next(mapping_file)
mapping_reader = csv.reader(mapping_file)
mapping = {
"{0}:{1}".format(row[0], row[1]): [row[2], row[3], row[4], row[5]]
for row in mapping_reader
}
if isinstance(mapping_path, tuple):
for mapping_file in mapping_files:
mapping_file.close()
else:
mapping_file.close()
output_file = open(output, "w")
output_writer = csv.writer(output_file)
facts = (
HamsterFact.select(HamsterFact, HamsterActivity, HamsterCategory)
.join(HamsterActivity)
.join(HamsterCategory, JOIN.LEFT_OUTER)
)
if category_search is not None:
facts = facts.where(HamsterCategory.name.contains(category_search))
if after is not None:
facts = facts.where(HamsterFact.start_time >= after)
if not show_missing:
output_writer.writerow(
[
"Date",
"From",
"To",
"Duration",
"Rate",
"User",
"Customer",
"Project",
"Activity",
"Description",
"Exported",
"Tags",
"HourlyRate",
"FixedRate",
"InternalRate",
]
)
for fact in facts:
k = f"{fact.activity.category.name}:{fact.activity.name}"
try:
mapping_ = mapping[k]
except KeyError:
if show_missing:
output_writer.writerow(
[fact.activity.category.name, fact.activity.name]
)
click.secho("Can't find mapping for '{0}', skipping".format(k), fg="yellow")
continue
if show_missing:
continue
if fact.start_time is None or fact.end_time is None:
click.secho(
f"Missing duration data '{fact.start_time}-{fact.end_time}', skipping",
fg="yellow",
)
continue
if len(mapping_) < 5:
mapping_.append(None)
date_start, date_end = (
datetime.strptime(fact.start_time.split(".")[0], "%Y-%m-%d %H:%M:%S"),
datetime.strptime(fact.end_time.split(".")[0], "%Y-%m-%d %H:%M:%S"),
)
duration = (date_start - date_end).seconds / 3600
output_writer.writerow(
[
date_start.strftime("%Y-%m-%d"),
date_start.strftime("%H:%M"),
"", # To (time)
duration,
"", # Rate
username,
mapping_[0],
mapping_[1],
mapping_[2],
fact.description or mapping_[4] or "",
"0", # Exported
mapping_[3],
"", # Hourly rate
"", # Fixed rate
]
)
output_file.close()
@kimai.command("import")
@click.argument("search")
@ -788,7 +645,7 @@ def kimai_db_sync(ctx):
)
@kimai_db.command()
@kimai_db.command("mapping2db")
@click.option(
"-g",
"--global",
@ -797,7 +654,7 @@ def kimai_db_sync(ctx):
is_flag=True,
)
@click.option("--mapping-path", help="Mapping file")
def mapping2db(mapping_path=None, global_=False):
def kimai_mapping2db(mapping_path=None, global_=False):
mapping_file = _get_kimai_mapping_file(mapping_path, None)
next(mapping_file)
mapping_reader = csv.reader(mapping_file)
@ -825,6 +682,15 @@ def mapping2db(mapping_path=None, global_=False):
)
@kimai.command("app")
@click.pass_context
def kimai_app(ctx):
from .app import HamsterToolsAppKimai
app = HamsterToolsAppKimai(ctx.obj['kimai'])
app.run()
@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")
@ -860,6 +726,30 @@ def clockify_db_init():
]
)
@clockify_db.command("reset")
def clockify_db_reset():
HamsterClockifyMapping.delete().execute()
@clockify_db.command("mapping2db")
@click.argument("mapping-path")
def clockify_mapping2db(mapping_path):
mapping_file = open(mapping_path, "r")
next(mapping_file)
mapping_reader = csv.reader(mapping_file)
for row in mapping_reader:
hamster_category = HamsterCategory.get(name=row[0])
hamster_activity = HamsterActivity.get(
name=row[1], category_id=hamster_category.id
)
clockify_project = ClockifyProject.get(clockify_id=row[2])
HamsterClockifyMapping.create(
hamster_activity=hamster_activity,
clockify_project=clockify_project,
clockify_description=row[3],
)
@clockify.command("app")
@click.pass_context
def clockify_app(ctx):
@ -868,14 +758,6 @@ def clockify_app(ctx):
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()
def app():