Compare commits
5 Commits
7d8c37f75c
...
4b85921b3e
Author | SHA1 | Date |
---|---|---|
3wc | 4b85921b3e | |
3wc | a5eca9960e | |
3wc | d88098dd30 | |
3wc | 8908290c4d | |
3wc | 23e90a4413 |
|
@ -704,7 +704,6 @@ def _import(username, mapping_path=None, output=None, category_search=None, afte
|
||||||
@cli.command()
|
@cli.command()
|
||||||
def app():
|
def app():
|
||||||
from .app import HamsterToolsApp
|
from .app import HamsterToolsApp
|
||||||
#app = HamsterToolsApp(db_cursor=c, db_connection=conn)
|
|
||||||
app = HamsterToolsApp()
|
app = HamsterToolsApp()
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ from textual.containers import Horizontal, Vertical
|
||||||
from textual.coordinate import Coordinate
|
from textual.coordinate import Coordinate
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
|
|
||||||
from .db import DatabaseManager, Category, Activity
|
from .db import DatabaseManager, Category, Activity, KimaiProject, KimaiCustomer
|
||||||
|
from .kimai import KimaiAPI, Customer as KimaiAPICustomer, Project as KimaiAPIProject, Activity as KimaiAPIActivity
|
||||||
|
|
||||||
|
|
||||||
class ListScreen(Screen):
|
class ListScreen(Screen):
|
||||||
|
@ -55,7 +56,6 @@ class ListScreen(Screen):
|
||||||
|
|
||||||
class ActivitiesScreen(ListScreen):
|
class ActivitiesScreen(ListScreen):
|
||||||
BINDINGS = [
|
BINDINGS = [
|
||||||
("q", "quit", "Quit"),
|
|
||||||
("s", "sort", "Sort"),
|
("s", "sort", "Sort"),
|
||||||
("r", "refresh", "Refresh"),
|
("r", "refresh", "Refresh"),
|
||||||
("/", "filter", "Search"),
|
("/", "filter", "Search"),
|
||||||
|
@ -68,7 +68,7 @@ class ActivitiesScreen(ListScreen):
|
||||||
self.table.clear()
|
self.table.clear()
|
||||||
|
|
||||||
# List activities with the count of facts
|
# List activities with the count of facts
|
||||||
activities = Activity.list_activities(self.db_manager, filter_query)
|
activities = Activity.list(self.db_manager, filter_query)
|
||||||
|
|
||||||
self.table.add_rows(
|
self.table.add_rows(
|
||||||
[
|
[
|
||||||
|
@ -133,7 +133,6 @@ class ActivitiesScreen(ListScreen):
|
||||||
|
|
||||||
class CategoriesScreen(ListScreen):
|
class CategoriesScreen(ListScreen):
|
||||||
BINDINGS = [
|
BINDINGS = [
|
||||||
("q", "quit", "Quit"),
|
|
||||||
("s", "sort", "Sort"),
|
("s", "sort", "Sort"),
|
||||||
("r", "refresh", "Refresh"),
|
("r", "refresh", "Refresh"),
|
||||||
("/", "filter", "Search"),
|
("/", "filter", "Search"),
|
||||||
|
@ -144,7 +143,7 @@ class CategoriesScreen(ListScreen):
|
||||||
def _refresh(self, filter_query=None):
|
def _refresh(self, filter_query=None):
|
||||||
self.table.clear()
|
self.table.clear()
|
||||||
|
|
||||||
categories = Category.list_categories(
|
categories = Category.list(
|
||||||
self.db_manager, filter_query=filter_query
|
self.db_manager, filter_query=filter_query
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -182,11 +181,66 @@ class CategoriesScreen(ListScreen):
|
||||||
self.table.remove_row(row_key)
|
self.table.remove_row(row_key)
|
||||||
|
|
||||||
|
|
||||||
|
class KimaiScreen(ListScreen):
|
||||||
|
BINDINGS = [
|
||||||
|
("s", "sort", "Sort"),
|
||||||
|
("r", "refresh", "Refresh"),
|
||||||
|
("g", "get", "Get data"),
|
||||||
|
("/", "filter", "Search"),
|
||||||
|
Binding(key="escape", action="cancelfilter", show=False),
|
||||||
|
]
|
||||||
|
|
||||||
|
def _refresh(self, filter_query=None):
|
||||||
|
self.table.clear()
|
||||||
|
|
||||||
|
projects = KimaiProject.list(
|
||||||
|
self.db_manager
|
||||||
|
)
|
||||||
|
|
||||||
|
self.table.add_rows(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
project.customer_id,
|
||||||
|
project.customer_name,
|
||||||
|
project.id,
|
||||||
|
project.name,
|
||||||
|
]
|
||||||
|
for project in projects
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.table.sort(self.sort)
|
||||||
|
|
||||||
|
def action_get(self) -> None:
|
||||||
|
api = KimaiAPI()
|
||||||
|
|
||||||
|
customers = KimaiAPICustomer.list(api)
|
||||||
|
for customer in customers:
|
||||||
|
KimaiCustomer(self.db_manager, id=customer.id, name=customer.name).save()
|
||||||
|
|
||||||
|
projects = KimaiAPIProject.list(api)
|
||||||
|
for project in projects:
|
||||||
|
KimaiProject(self.db_manager, id=project.id, name=project.name,
|
||||||
|
customer_id=project.customer.id, customer_name="").save()
|
||||||
|
|
||||||
|
self._refresh()
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self.table = self.query_one(DataTable)
|
||||||
|
self.table.cursor_type = "row"
|
||||||
|
self.columns = self.table.add_columns("customer id", "customer",
|
||||||
|
"project id", "project")
|
||||||
|
# self.sort = (self.columns[1], self.columns[3])
|
||||||
|
self.sort = self.columns[1]
|
||||||
|
self._refresh()
|
||||||
|
|
||||||
|
|
||||||
class HamsterToolsApp(App):
|
class HamsterToolsApp(App):
|
||||||
CSS_PATH = "app.tcss"
|
CSS_PATH = "app.tcss"
|
||||||
BINDINGS = [
|
BINDINGS = [
|
||||||
("a", "switch_mode('activities')", "Activities"),
|
("a", "switch_mode('activities')", "Activities"),
|
||||||
("c", "switch_mode('categories')", "Categories"),
|
("c", "switch_mode('categories')", "Categories"),
|
||||||
|
("k", "switch_mode('kimai')", "Kimai"),
|
||||||
("q", "quit", "Quit"),
|
("q", "quit", "Quit"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -195,7 +249,8 @@ class HamsterToolsApp(App):
|
||||||
|
|
||||||
self.MODES = {
|
self.MODES = {
|
||||||
"categories": CategoriesScreen(self.db_manager),
|
"categories": CategoriesScreen(self.db_manager),
|
||||||
"activities": ActivitiesScreen(self.db_manager)
|
"activities": ActivitiesScreen(self.db_manager),
|
||||||
|
"kimai": KimaiScreen(self.db_manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
class DatabaseManager:
|
class DatabaseManager:
|
||||||
def __init__(self, database_name):
|
def __init__(self, database_name):
|
||||||
self.conn = sqlite3.connect(database_name)
|
self.conn = sqlite3.connect(database_name)
|
||||||
|
@ -14,6 +15,7 @@ class DatabaseManager:
|
||||||
def close(self):
|
def close(self):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
|
|
||||||
class BaseORM:
|
class BaseORM:
|
||||||
def __init__(self, db_manager, table_name, id, **kwargs):
|
def __init__(self, db_manager, table_name, id, **kwargs):
|
||||||
self.db_manager = db_manager
|
self.db_manager = db_manager
|
||||||
|
@ -31,11 +33,12 @@ class BaseORM:
|
||||||
|
|
||||||
class Category(BaseORM):
|
class Category(BaseORM):
|
||||||
def __init__(self, db_manager, id, name, activity_count):
|
def __init__(self, db_manager, id, name, activity_count):
|
||||||
super().__init__(db_manager, "categories", id, name=name,
|
super().__init__(
|
||||||
activity_count=activity_count)
|
db_manager, "categories", id, name=name, activity_count=activity_count
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_categories(db_manager, filter_query=None):
|
def list(db_manager, filter_query=None):
|
||||||
cursor = db_manager.get_cursor()
|
cursor = db_manager.get_cursor()
|
||||||
where = ""
|
where = ""
|
||||||
if filter_query is not None:
|
if filter_query is not None:
|
||||||
|
@ -65,7 +68,8 @@ class Category(BaseORM):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_id(db_manager, category_id):
|
def get_by_id(db_manager, category_id):
|
||||||
cursor = db_manager.get_cursor()
|
cursor = db_manager.get_cursor()
|
||||||
cursor.execute("""
|
cursor.execute(
|
||||||
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
categories.id,
|
categories.id,
|
||||||
categories.name,
|
categories.name,
|
||||||
|
@ -78,7 +82,9 @@ class Category(BaseORM):
|
||||||
categories.id = activities.category_id
|
categories.id = activities.category_id
|
||||||
WHERE
|
WHERE
|
||||||
categories.id = ?
|
categories.id = ?
|
||||||
""", (category_id,))
|
""",
|
||||||
|
(category_id,),
|
||||||
|
)
|
||||||
|
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
if row:
|
if row:
|
||||||
|
@ -88,7 +94,9 @@ class Category(BaseORM):
|
||||||
|
|
||||||
class Activity(BaseORM):
|
class Activity(BaseORM):
|
||||||
def __init__(self, db_manager, id, name, category_id, category_name, facts_count):
|
def __init__(self, db_manager, id, name, category_id, category_name, facts_count):
|
||||||
super().__init__(db_manager, "activities", id, name=name, category_id=category_id)
|
super().__init__(
|
||||||
|
db_manager, "activities", id, name=name, category_id=category_id
|
||||||
|
)
|
||||||
self.category_name = category_name
|
self.category_name = category_name
|
||||||
self.facts_count = facts_count
|
self.facts_count = facts_count
|
||||||
|
|
||||||
|
@ -97,19 +105,22 @@ class Activity(BaseORM):
|
||||||
|
|
||||||
print(f"moving from {self.id} to {to_activity.id}")
|
print(f"moving from {self.id} to {to_activity.id}")
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute(
|
||||||
|
"""
|
||||||
UPDATE
|
UPDATE
|
||||||
facts
|
facts
|
||||||
SET
|
SET
|
||||||
activity_id = ?
|
activity_id = ?
|
||||||
WHERE
|
WHERE
|
||||||
activity_id = ?
|
activity_id = ?
|
||||||
""", (to_activity.id, self.id))
|
""",
|
||||||
|
(to_activity.id, self.id),
|
||||||
|
)
|
||||||
|
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_activities(db_manager, filter_query=None):
|
def list(db_manager, filter_query=None):
|
||||||
cursor = db_manager.get_cursor()
|
cursor = db_manager.get_cursor()
|
||||||
where = ""
|
where = ""
|
||||||
if filter_query is not None:
|
if filter_query is not None:
|
||||||
|
@ -142,12 +153,15 @@ class Activity(BaseORM):
|
||||||
cursor.execute(sql)
|
cursor.execute(sql)
|
||||||
|
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
return [Activity(db_manager, row[0], row[1], row[2], row[3], row[4]) for row in rows]
|
return [
|
||||||
|
Activity(db_manager, row[0], row[1], row[2], row[3], row[4]) for row in rows
|
||||||
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_id(db_manager, activity_id):
|
def get_by_id(db_manager, activity_id):
|
||||||
cursor = db_manager.get_cursor()
|
cursor = db_manager.get_cursor()
|
||||||
cursor.execute("""
|
cursor.execute(
|
||||||
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
activities.id,
|
activities.id,
|
||||||
activities.name,
|
activities.name,
|
||||||
|
@ -166,7 +180,9 @@ class Activity(BaseORM):
|
||||||
activities.id = facts.activity_id
|
activities.id = facts.activity_id
|
||||||
WHERE
|
WHERE
|
||||||
activities.id = ?
|
activities.id = ?
|
||||||
""", (activity_id,))
|
""",
|
||||||
|
(activity_id,),
|
||||||
|
)
|
||||||
|
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
if row:
|
if row:
|
||||||
|
@ -179,8 +195,74 @@ class Fact(BaseORM):
|
||||||
super().__init__(db_manager, "facts", id, activity_id=activity_id)
|
super().__init__(db_manager, "facts", id, activity_id=activity_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_facts(db_manager):
|
def list(db_manager):
|
||||||
cursor = db_manager.get_cursor()
|
cursor = db_manager.get_cursor()
|
||||||
cursor.execute("SELECT * FROM facts")
|
cursor.execute("SELECT * FROM facts")
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
return [Fact(db_manager, row[0], row[1]) for row in rows]
|
return [Fact(db_manager, row[0], row[1]) for row in rows]
|
||||||
|
|
||||||
|
|
||||||
|
class KimaiCustomer(BaseORM):
|
||||||
|
def __init__(self, db_manager, id, name):
|
||||||
|
super().__init__(db_manager, "kimai_customers", id, name=name)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
cursor = self.db_manager.get_cursor()
|
||||||
|
cursor.execute("SELECT id FROM kimai_customers WHERE id = ?", (self.id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE kimai_customers SET name = ? WHERE id = ?
|
||||||
|
""", (self.name, self.id))
|
||||||
|
else:
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO kimai_customers (id, name) VALUES (?, ?)
|
||||||
|
""", (self.id, self.name))
|
||||||
|
self.db_manager.get_conn().commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class KimaiProject(BaseORM):
|
||||||
|
def __init__(self, db_manager, id, name, customer_id, customer_name):
|
||||||
|
super().__init__(db_manager, "kimai_projects", id, name=name,
|
||||||
|
customer_id=customer_id, customer_name=customer_name)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
cursor = self.db_manager.get_cursor()
|
||||||
|
cursor.execute("SELECT id FROM kimai_projects WHERE id = ?", (self.id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE kimai_projects SET name = ?, customer_id = ? WHERE id = ?
|
||||||
|
""", (self.name, self.customer_id, self.id))
|
||||||
|
else:
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO kimai_projects (id, name, customer_id) VALUES (?, ?, ?)
|
||||||
|
""", (self.id, self.name, self.customer_id))
|
||||||
|
self.db_manager.get_conn().commit()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list(db_manager):
|
||||||
|
cursor = db_manager.get_cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT
|
||||||
|
kimai_projects.id,
|
||||||
|
COALESCE(kimai_projects.name, ""),
|
||||||
|
kimai_customers.id,
|
||||||
|
COALESCE(kimai_customers.name, "")
|
||||||
|
FROM
|
||||||
|
kimai_projects
|
||||||
|
LEFT JOIN
|
||||||
|
kimai_customers
|
||||||
|
ON
|
||||||
|
kimai_customers.id = kimai_projects.customer_id
|
||||||
|
GROUP BY
|
||||||
|
kimai_customers.id
|
||||||
|
""")
|
||||||
|
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
return [KimaiProject(db_manager, row[0], row[1], row[2], row[3]) for row in rows]
|
||||||
|
|
||||||
|
|
||||||
|
class KimaiACtivity(BaseORM):
|
||||||
|
pass
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import requests
|
||||||
|
import requests_cache
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class NotFound(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class KimaiAPI(object):
|
||||||
|
# temporary hardcoded config
|
||||||
|
KIMAI_API_URL = 'https://kimai.autonomic.zone/api'
|
||||||
|
|
||||||
|
KIMAI_USERNAME = '3wordchant'
|
||||||
|
KIMAI_API_KEY = os.environ['KIMAI_API_KEY']
|
||||||
|
|
||||||
|
auth_headers = {
|
||||||
|
'X-AUTH-USER': KIMAI_USERNAME,
|
||||||
|
'X-AUTH-TOKEN': KIMAI_API_KEY
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
requests_cache.install_cache('kimai', backend='sqlite', expire_after=1800)
|
||||||
|
self.customers_json = requests.get(
|
||||||
|
f'{self.KIMAI_API_URL}/customers?visible=3', headers=self.auth_headers).json()
|
||||||
|
self.projects_json = requests.get(
|
||||||
|
f'{self.KIMAI_API_URL}/projects?visible=3', headers=self.auth_headers).json()
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAPI(object):
|
||||||
|
def __init__(self, api, **kwargs):
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
|
||||||
|
class Customer(BaseAPI):
|
||||||
|
def __init__(self, api, id, name):
|
||||||
|
super().__init__(api, id=id, name=name)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list(api):
|
||||||
|
return [
|
||||||
|
Customer(api, c['id'], c['name']) for c in api.customers_json
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_by_id(api, id):
|
||||||
|
for value in api.customers_json:
|
||||||
|
if value['id'] == id:
|
||||||
|
return Customer(api, value['id'], value['name'])
|
||||||
|
raise NotFound()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'Customer (id={self.id}, name={self.name})'
|
||||||
|
|
||||||
|
|
||||||
|
class Project(BaseAPI):
|
||||||
|
def __init__(self, api, id, name, customer):
|
||||||
|
super().__init__(api, id=id, name=name, customer=customer)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list(api):
|
||||||
|
return [
|
||||||
|
Project(api, p['id'], p['name'], Customer.get_by_id(api, p['customer'])) for p in api.projects_json
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_by_id(api, id):
|
||||||
|
for value in api.projects_json:
|
||||||
|
if value['id'] == id:
|
||||||
|
return Project(api, value['id'], value['name'],
|
||||||
|
Customer.get_by_id(api, value['customer']))
|
||||||
|
raise NotFound()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'Project (id={self.id}, name={self.name}, customer={self.customer})'
|
||||||
|
|
||||||
|
|
||||||
|
class Activity():
|
||||||
|
pass
|
Loading…
Reference in New Issue