Reformatting 🧹
This commit is contained in:
parent
05928b1244
commit
cb909540fa
@ -18,7 +18,7 @@ from .db import (
|
|||||||
KimaiProject,
|
KimaiProject,
|
||||||
KimaiActivity,
|
KimaiActivity,
|
||||||
HamsterActivityKimaiMapping,
|
HamsterActivityKimaiMapping,
|
||||||
HamsterFactKimaiImport
|
HamsterFactKimaiImport,
|
||||||
)
|
)
|
||||||
|
|
||||||
HAMSTER_DIR = Path.home() / ".local/share/hamster"
|
HAMSTER_DIR = Path.home() / ".local/share/hamster"
|
||||||
@ -631,8 +631,15 @@ def db_():
|
|||||||
|
|
||||||
@db_.command()
|
@db_.command()
|
||||||
def init():
|
def init():
|
||||||
db.create_tables([KimaiCustomer, KimaiProject, KimaiActivity,
|
db.create_tables(
|
||||||
HamsterActivityKimaiMapping, HamsterFactKimaiImport])
|
[
|
||||||
|
KimaiCustomer,
|
||||||
|
KimaiProject,
|
||||||
|
KimaiActivity,
|
||||||
|
HamsterActivityKimaiMapping,
|
||||||
|
HamsterFactKimaiImport,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@db_.command()
|
@db_.command()
|
||||||
@ -665,10 +672,7 @@ def mapping2db(mapping_path=None, global_=False):
|
|||||||
try:
|
try:
|
||||||
kimai_activity = KimaiActivity.get(name=row[4], project_id=kimai_project.id)
|
kimai_activity = KimaiActivity.get(name=row[4], project_id=kimai_project.id)
|
||||||
except KimaiActivity.DoesNotExist:
|
except KimaiActivity.DoesNotExist:
|
||||||
kimai_activity = KimaiActivity.get(
|
kimai_activity = KimaiActivity.get(name=row[4], project_id=None)
|
||||||
name=row[4],
|
|
||||||
project_id=None
|
|
||||||
)
|
|
||||||
|
|
||||||
HamsterActivityKimaiMapping.create(
|
HamsterActivityKimaiMapping.create(
|
||||||
hamster_activity=hamster_activity,
|
hamster_activity=hamster_activity,
|
||||||
|
@ -38,9 +38,15 @@ class ListScreen(Screen):
|
|||||||
with Vertical():
|
with Vertical():
|
||||||
yield DataTable()
|
yield DataTable()
|
||||||
with Horizontal(id="filter"):
|
with Horizontal(id="filter"):
|
||||||
yield Input(id="search", placeholder="Category/activity name contains text")
|
yield Input(
|
||||||
yield Input(id="date",
|
id="search", placeholder="Category/activity name contains text"
|
||||||
placeholder='After date, in {0} format'.format(datetime.now().strftime('%Y-%m-%d')))
|
)
|
||||||
|
yield Input(
|
||||||
|
id="date",
|
||||||
|
placeholder="After date, in {0} format".format(
|
||||||
|
datetime.now().strftime("%Y-%m-%d")
|
||||||
|
),
|
||||||
|
)
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
def action_refresh(self) -> None:
|
def action_refresh(self) -> None:
|
||||||
@ -69,7 +75,7 @@ class ListScreen(Screen):
|
|||||||
self.table.focus()
|
self.table.focus()
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
@on(Input.Changed, '#filter Input')
|
@on(Input.Changed, "#filter Input")
|
||||||
def filter(self, event):
|
def filter(self, event):
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
@ -81,7 +87,7 @@ class ActivityEditScreen(ModalScreen):
|
|||||||
]
|
]
|
||||||
|
|
||||||
category_id = None
|
category_id = None
|
||||||
category_name = ''
|
category_name = ""
|
||||||
|
|
||||||
def _get_categories(self, input_state):
|
def _get_categories(self, input_state):
|
||||||
categories = [DropdownItem(c.name, str(c.id)) for c in HamsterCategory.select()]
|
categories = [DropdownItem(c.name, str(c.id)) for c in HamsterCategory.select()]
|
||||||
@ -96,15 +102,20 @@ class ActivityEditScreen(ModalScreen):
|
|||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Vertical(
|
yield Vertical(
|
||||||
Horizontal(Label("Category:"),
|
Horizontal(
|
||||||
|
Label("Category:"),
|
||||||
AutoComplete(
|
AutoComplete(
|
||||||
Input(placeholder="Type to search...", id="category",
|
Input(
|
||||||
value=self.category_name),
|
placeholder="Type to search...",
|
||||||
|
id="category",
|
||||||
|
value=self.category_name,
|
||||||
|
),
|
||||||
Dropdown(items=self._get_categories),
|
Dropdown(items=self._get_categories),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Horizontal(Label("Activity:"), Input(value=self.activity_name,
|
Horizontal(
|
||||||
id='activity')),
|
Label("Activity:"), Input(value=self.activity_name, id="activity")
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@on(Input.Submitted, "#category")
|
@on(Input.Submitted, "#category")
|
||||||
@ -129,7 +140,7 @@ class ActivityEditScreen(ModalScreen):
|
|||||||
self.dismiss(
|
self.dismiss(
|
||||||
{
|
{
|
||||||
"category": self.category_id,
|
"category": self.category_id,
|
||||||
"activity": self.query_one('#activity').value,
|
"activity": self.query_one("#activity").value,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -340,18 +351,18 @@ class ActivityListScreen(ListScreen):
|
|||||||
.group_by(HamsterActivity)
|
.group_by(HamsterActivity)
|
||||||
)
|
)
|
||||||
|
|
||||||
filter_search = self.query_one('#filter #search').value
|
filter_search = self.query_one("#filter #search").value
|
||||||
if filter_search is not None:
|
if filter_search is not None:
|
||||||
activities = activities.where(
|
activities = activities.where(
|
||||||
HamsterActivity.name.contains(filter_search)
|
HamsterActivity.name.contains(filter_search)
|
||||||
| HamsterCategory.name.contains(filter_search)
|
| HamsterCategory.name.contains(filter_search)
|
||||||
)
|
)
|
||||||
|
|
||||||
filter_date = self.query_one('#filter #date').value
|
filter_date = self.query_one("#filter #date").value
|
||||||
if filter_date is not None:
|
if filter_date is not None:
|
||||||
try:
|
try:
|
||||||
activities = activities.where(
|
activities = activities.where(
|
||||||
HamsterFact.start_time > datetime.strptime(filter_date, '%Y-%m-%d')
|
HamsterFact.start_time > datetime.strptime(filter_date, "%Y-%m-%d")
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
@ -431,15 +442,14 @@ class ActivityListScreen(ListScreen):
|
|||||||
def handle_edit(properties):
|
def handle_edit(properties):
|
||||||
if properties is None:
|
if properties is None:
|
||||||
return
|
return
|
||||||
activity.name = properties['activity']
|
activity.name = properties["activity"]
|
||||||
activity.category_id = properties['category']
|
activity.category_id = properties["category"]
|
||||||
activity.save()
|
activity.save()
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
self.app.push_screen(ActivityEditScreen(
|
self.app.push_screen(
|
||||||
category=category,
|
ActivityEditScreen(category=category, activity=activity), handle_edit
|
||||||
activity=activity
|
)
|
||||||
), handle_edit)
|
|
||||||
|
|
||||||
def action_mapping(self):
|
def action_mapping(self):
|
||||||
selected_activity = (
|
selected_activity = (
|
||||||
@ -490,26 +500,24 @@ class CategoryListScreen(ListScreen):
|
|||||||
|
|
||||||
categories = (
|
categories = (
|
||||||
HamsterCategory.select(
|
HamsterCategory.select(
|
||||||
HamsterCategory,
|
HamsterCategory,
|
||||||
fn.Count(HamsterActivity.id).alias("activities_count"),
|
fn.Count(HamsterActivity.id).alias("activities_count"),
|
||||||
HamsterFact.start_time
|
HamsterFact.start_time,
|
||||||
)
|
)
|
||||||
.join(HamsterActivity, JOIN.LEFT_OUTER)
|
.join(HamsterActivity, JOIN.LEFT_OUTER)
|
||||||
.join(HamsterFact, JOIN.LEFT_OUTER)
|
.join(HamsterFact, JOIN.LEFT_OUTER)
|
||||||
.group_by(HamsterCategory)
|
.group_by(HamsterCategory)
|
||||||
)
|
)
|
||||||
|
|
||||||
filter_search = self.query_one('#filter #search').value
|
filter_search = self.query_one("#filter #search").value
|
||||||
if filter_search is not None:
|
if filter_search is not None:
|
||||||
categories = categories.where(
|
categories = categories.where(HamsterCategory.name.contains(filter_search))
|
||||||
HamsterCategory.name.contains(filter_search)
|
|
||||||
)
|
|
||||||
|
|
||||||
filter_date = self.query_one('#filter #date').value
|
filter_date = self.query_one("#filter #date").value
|
||||||
if filter_date is not None:
|
if filter_date is not None:
|
||||||
try:
|
try:
|
||||||
categories = categories.where(
|
categories = categories.where(
|
||||||
HamsterFact.start_time > datetime.strptime(filter_date, '%Y-%m-%d')
|
HamsterFact.start_time > datetime.strptime(filter_date, "%Y-%m-%d")
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
@ -613,7 +621,7 @@ class KimaiProjectListScreen(ListScreen):
|
|||||||
"id": project.id,
|
"id": project.id,
|
||||||
"name": project.name,
|
"name": project.name,
|
||||||
"customer_id": project.customer.id,
|
"customer_id": project.customer.id,
|
||||||
"allow_global_activities": project.allow_global_activities
|
"allow_global_activities": project.allow_global_activities,
|
||||||
}
|
}
|
||||||
for project in projects
|
for project in projects
|
||||||
]
|
]
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
from peewee import SqliteDatabase, Model, CharField, ForeignKeyField, DateTimeField, SmallIntegerField, BooleanField
|
from peewee import (
|
||||||
|
SqliteDatabase,
|
||||||
|
Model,
|
||||||
|
CharField,
|
||||||
|
ForeignKeyField,
|
||||||
|
DateTimeField,
|
||||||
|
SmallIntegerField,
|
||||||
|
BooleanField,
|
||||||
|
)
|
||||||
|
|
||||||
from textual.logging import TextualHandler
|
from textual.logging import TextualHandler
|
||||||
|
|
||||||
logger = logging.getLogger("peewee")
|
# logger = logging.getLogger("peewee")
|
||||||
logger.addHandler(TextualHandler())
|
# logger.addHandler(TextualHandler())
|
||||||
logger.setLevel(logging.DEBUG)
|
# logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
db = SqliteDatabase(None)
|
db = SqliteDatabase(None)
|
||||||
|
|
||||||
|
@ -28,12 +28,14 @@ class KimaiAPI(object):
|
|||||||
self.user_json = self.get("users/me")
|
self.user_json = self.get("users/me")
|
||||||
|
|
||||||
def get(self, endpoint, params=None):
|
def get(self, endpoint, params=None):
|
||||||
return requests.get(f"{self.KIMAI_API_URL}/{endpoint}",
|
return requests.get(
|
||||||
params=params, headers=self.auth_headers).json()
|
f"{self.KIMAI_API_URL}/{endpoint}", params=params, headers=self.auth_headers
|
||||||
|
).json()
|
||||||
|
|
||||||
def post(self, endpoint, data):
|
def post(self, endpoint, data):
|
||||||
return requests.post(f"{self.KIMAI_API_URL}/{endpoint}",
|
return requests.post(
|
||||||
json=data, headers=self.auth_headers)
|
f"{self.KIMAI_API_URL}/{endpoint}", json=data, headers=self.auth_headers
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseAPI(object):
|
class BaseAPI(object):
|
||||||
@ -72,9 +74,13 @@ class Project(BaseAPI):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def list(api):
|
def list(api):
|
||||||
return [
|
return [
|
||||||
Project(api, p["id"], p["name"], Customer.get_by_id(api,
|
Project(
|
||||||
p["customer"]),
|
api,
|
||||||
p["globalActivities"])
|
p["id"],
|
||||||
|
p["name"],
|
||||||
|
Customer.get_by_id(api, p["customer"]),
|
||||||
|
p["globalActivities"],
|
||||||
|
)
|
||||||
for p in api.projects_json
|
for p in api.projects_json
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -87,7 +93,7 @@ class Project(BaseAPI):
|
|||||||
value["id"],
|
value["id"],
|
||||||
value["name"],
|
value["name"],
|
||||||
Customer.get_by_id(api, value["customer"]),
|
Customer.get_by_id(api, value["customer"]),
|
||||||
value["globalActivities"]
|
value["globalActivities"],
|
||||||
)
|
)
|
||||||
if not none:
|
if not none:
|
||||||
raise NotFound()
|
raise NotFound()
|
||||||
@ -143,19 +149,19 @@ class Timesheet(BaseAPI):
|
|||||||
Project.get_by_id(api, t["project"], none=True),
|
Project.get_by_id(api, t["project"], none=True),
|
||||||
t["begin"],
|
t["begin"],
|
||||||
t["end"],
|
t["end"],
|
||||||
t["id"],
|
t["id"],
|
||||||
t["description"],
|
t["description"],
|
||||||
t["tags"],
|
t["tags"],
|
||||||
)
|
)
|
||||||
for t in api.get(
|
for t in api.get(
|
||||||
'timesheets',
|
"timesheets",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_id(api, id, none=False):
|
def get_by_id(api, id, none=False):
|
||||||
t = api.get(
|
t = api.get(
|
||||||
f'timesheets/{id}',
|
f"timesheets/{id}",
|
||||||
)
|
)
|
||||||
return Timesheet(
|
return Timesheet(
|
||||||
api,
|
api,
|
||||||
@ -163,19 +169,22 @@ class Timesheet(BaseAPI):
|
|||||||
Project.get_by_id(api, t["project"], none=True),
|
Project.get_by_id(api, t["project"], none=True),
|
||||||
t["begin"],
|
t["begin"],
|
||||||
t["end"],
|
t["end"],
|
||||||
t["id"],
|
t["id"],
|
||||||
t["description"],
|
t["description"],
|
||||||
t["tags"],
|
t["tags"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def upload(self):
|
def upload(self):
|
||||||
return self.api.post('timesheets', {
|
return self.api.post(
|
||||||
"begin": self.begin.isoformat(),
|
"timesheets",
|
||||||
"end": self.end.isoformat(),
|
{
|
||||||
"project": self.project.id,
|
"begin": self.begin.isoformat(),
|
||||||
"activity": self.activity.id,
|
"end": self.end.isoformat(),
|
||||||
"description": self.description,
|
"project": self.project.id,
|
||||||
# FIXME: support multiple users
|
"activity": self.activity.id,
|
||||||
# "user": self.,
|
"description": self.description,
|
||||||
"tags": self.tags,
|
# FIXME: support multiple users
|
||||||
})
|
# "user": self.,
|
||||||
|
"tags": self.tags,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@ -17,7 +18,8 @@ api = KimaiAPI()
|
|||||||
|
|
||||||
# print(Timesheet.list(api))
|
# print(Timesheet.list(api))
|
||||||
|
|
||||||
t = Timesheet(api,
|
t = Timesheet(
|
||||||
|
api,
|
||||||
activity=Activity.get_by_id(api, 613),
|
activity=Activity.get_by_id(api, 613),
|
||||||
project=Project.get_by_id(api, 233),
|
project=Project.get_by_id(api, 233),
|
||||||
begin=datetime.now() - timedelta(minutes=10),
|
begin=datetime.now() - timedelta(minutes=10),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -11,22 +12,19 @@ from hamstertools.kimai import *
|
|||||||
|
|
||||||
api = KimaiAPI()
|
api = KimaiAPI()
|
||||||
|
|
||||||
DATE_FROM='2023-10-01'
|
DATE_FROM = "2023-10-01"
|
||||||
DATE_TO='2023-11-01'
|
DATE_TO = "2023-11-01"
|
||||||
SEARCH='auto'
|
SEARCH = "auto"
|
||||||
|
|
||||||
facts = HamsterFact.select(
|
facts = (
|
||||||
HamsterFact,
|
HamsterFact.select(HamsterFact, HamsterActivity, HamsterCategory)
|
||||||
HamsterActivity,
|
.join(HamsterActivity, JOIN.LEFT_OUTER)
|
||||||
HamsterCategory
|
.join(HamsterCategory, JOIN.LEFT_OUTER)
|
||||||
).join(
|
.where(
|
||||||
HamsterActivity, JOIN.LEFT_OUTER
|
(HamsterFact.start_time > datetime.strptime(DATE_FROM, "%Y-%m-%d"))
|
||||||
).join(
|
& (HamsterFact.start_time < datetime.strptime(DATE_TO, "%Y-%m-%d"))
|
||||||
HamsterCategory, JOIN.LEFT_OUTER
|
& HamsterCategory.name.contains(SEARCH)
|
||||||
).where(
|
)
|
||||||
(HamsterFact.start_time > datetime.strptime(DATE_FROM, "%Y-%m-%d"))
|
|
||||||
& (HamsterFact.start_time < datetime.strptime(DATE_TO, "%Y-%m-%d"))
|
|
||||||
& HamsterCategory.name.contains(SEARCH)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
has_errors = False
|
has_errors = False
|
||||||
@ -35,15 +33,24 @@ has_errors = False
|
|||||||
for f in facts:
|
for f in facts:
|
||||||
mappings = f.activity.mappings
|
mappings = f.activity.mappings
|
||||||
if len(mappings) == 0:
|
if len(mappings) == 0:
|
||||||
print(f'fact {f.id}: @{f.activity.category.id} {f.activity.category.name} » @{f.activity.id} {f.activity.name} has no mapping')
|
print(
|
||||||
|
f"fact {f.id}: @{f.activity.category.id} {f.activity.category.name} » @{f.activity.id} {f.activity.name} has no mapping"
|
||||||
|
)
|
||||||
has_errors = True
|
has_errors = True
|
||||||
continue
|
continue
|
||||||
if len(mappings) > 1:
|
if len(mappings) > 1:
|
||||||
print(f'fact {f.id}: activity @{f.activity.id} {f.activity.name} has multiple mappings')
|
print(
|
||||||
|
f"fact {f.id}: activity @{f.activity.id} {f.activity.name} has multiple mappings"
|
||||||
|
)
|
||||||
has_errors = True
|
has_errors = True
|
||||||
continue
|
continue
|
||||||
if mappings[0].kimai_activity.project is None and not mappings[0].kimai_project.allow_global_activities:
|
if (
|
||||||
print(f'fact {f.id}: project @{mappings[0].kimai_project.id} {mappings[0].kimai_project.name} does not allow global activity {mappings[0].kimai_activity}')
|
mappings[0].kimai_activity.project is None
|
||||||
|
and not mappings[0].kimai_project.allow_global_activities
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
f"fact {f.id}: project @{mappings[0].kimai_project.id} {mappings[0].kimai_project.name} does not allow global activity {mappings[0].kimai_activity}"
|
||||||
|
)
|
||||||
has_errors = True
|
has_errors = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -55,25 +62,27 @@ for f in facts:
|
|||||||
try:
|
try:
|
||||||
mapping = f.activity.mappings[0]
|
mapping = f.activity.mappings[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print(f"no mapping, skipping {f.id} ({f.activity.category.name} » {f.activity.name})")
|
print(
|
||||||
|
f"no mapping, skipping {f.id} ({f.activity.category.name} » {f.activity.name})"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
t = Timesheet(api,
|
t = Timesheet(
|
||||||
|
api,
|
||||||
activity=mapping.kimai_activity,
|
activity=mapping.kimai_activity,
|
||||||
project=mapping.kimai_project,
|
project=mapping.kimai_project,
|
||||||
begin=f.start_time,
|
begin=f.start_time,
|
||||||
end=f.end_time,
|
end=f.end_time,
|
||||||
description=f.description if f.description != '' else mapping.kimai_description,
|
description=f.description if f.description != "" else mapping.kimai_description,
|
||||||
# tags=f.tags if f.tags != '' else mapping.kimai_tags
|
# tags=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()
|
from pdb import set_trace
|
||||||
|
|
||||||
|
set_trace()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
HamsterFactKimaiImport.create(
|
HamsterFactKimaiImport.create(hamster_fact=f, kimai_id=r["id"]).save()
|
||||||
hamster_fact=f,
|
|
||||||
kimai_id=r['id']
|
|
||||||
).save()
|
|
||||||
print(f'Created Kimai timesheet {r["id"]}')
|
print(f'Created Kimai timesheet {r["id"]}')
|
||||||
|
Loading…
Reference in New Issue
Block a user