diff --git a/core/tracker.py b/core/tracker.py index abdaaf6..9673b44 100644 --- a/core/tracker.py +++ b/core/tracker.py @@ -1,28 +1,20 @@ import json -from datetime import datetime -from json.encoder import JSONEncoder +from datetime import datetime, timedelta +from fastapi.encoders import jsonable_encoder from pathlib import Path DATA_DIR = Path.home().joinpath(".tracker_api/trackers") def object_hook(dct): - if "date_str" in dct: + if "datetime" in dct: + return TrackerPoint(dct["datetime"], dct["value"]) + if "date_str" in dct: # old format return TrackerPoint(dct["date_str"], dct["value"]) if "name" in dct: return Tracker(dct["name"], dct["points"]) -class TrackerEncoder(JSONEncoder): - def default(self, o): - if isinstance(o, Tracker): - return dict(name=o.name, points=o.points) - elif isinstance(o, TrackerPoint): - return dict(date_str=o.datetime.isoformat(), value=o.value) - else: - return json.JSONEncoder.default(self, o) - - class TrackerPoint: def __init__(self, date_str: str, value: int) -> None: self.datetime = datetime.fromisoformat(date_str) @@ -46,16 +38,14 @@ class Tracker: filepath = DATA_DIR.joinpath(f"{self.name}.json") filepath.unlink() - def to_json(self): - """Convert Tracker object to JSON.""" - return json.dumps(self, indent=4, cls=TrackerEncoder) - def save(self): """Save the tracker to JSON.""" DATA_DIR.mkdir(exist_ok=True) filepath = DATA_DIR.joinpath(f"{self.name}.json") filepath.touch(exist_ok=True) - filepath.write_text(self.to_json()) + with filepath.open("w+") as fp: + data = jsonable_encoder(self) + json.dump(data, fp, indent=4) def modify_point(self, date_str: str, value: int): """Modify a point. Change its assigned value to the one given.""" @@ -81,6 +71,32 @@ class Tracker: break self.save() + def list_points(self, start_date: datetime = None, end_date: datetime = None) -> list: + """ + Return a list of all points that fall in-between the given start_date and end_date. + + :param start_date: the date the data returned should start at. + If this is not given, it'll be a week from the given end_date. + + :param end_date: the date the data returned should end at. (included in output) + If this is not given, it'll be today. + + :return: list of all points that fall in-between the given start_date and end_date. + + :raises ValueError: if either date parameter is set in the future, or if the end_date is before the start_date. + """ + point_list = [] + if end_date is None: + end_date = datetime.now().date() + if start_date is None: + start_date = (end_date - timedelta(days=7)) # assume we'll start a week prior to today. + if end_date > datetime.now().date() or start_date > datetime.now().date() or end_date < start_date: + raise ValueError(end_date) + for point in self.points: + if start_date <= point.datetime.date() <= end_date: + point_list.append(point) + return point_list + @classmethod def from_data(cls, name): """Load a tracker from the DATA_DIR.""" diff --git a/routers/system.py b/routers/system.py new file mode 100644 index 0000000..eb5a9c7 --- /dev/null +++ b/routers/system.py @@ -0,0 +1,15 @@ +import sys + +from fastapi import APIRouter, Depends +from fastapi.security.api_key import APIKey + +import subprocess +from security import get_system_key + +router = APIRouter() + + +@router.post("/system/update") +def update(access_token: APIKey = Depends(get_system_key)): + output = subprocess.check_output(['git', 'pull'], encoding='utf-8') + sys.exit(26) # exit with restart code diff --git a/routers/tracker.py b/routers/tracker.py index 322f124..c78012d 100644 --- a/routers/tracker.py +++ b/routers/tracker.py @@ -1,4 +1,5 @@ from datetime import datetime +from functools import wraps from fastapi import APIRouter, Depends from fastapi.security.api_key import APIKey @@ -9,16 +10,31 @@ from core.tracker import Tracker router = APIRouter() +def check_name(router_fn): + @wraps(router_fn) + def _check_name_fn(name: str, *args, **kwargs): + name += f"-{kwargs['access_token']}" + return router_fn(name, *args, **kwargs) + return _check_name_fn + + @router.get("/tracker/points") -def list_points(name: str, access_token: APIKey = Depends(get_api_key)): +@check_name +def list_points(name: str, start_date=None, end_date=None, access_token: APIKey = Depends(get_api_key)): + if start_date is not None: + start_date = datetime.fromisoformat(start_date) + if end_date is not None: + end_date = datetime.fromisoformat(end_date) try: tracker = Tracker.from_data(name) - return tracker.points + print(name, start_date, end_date) + return tracker.list_points(start_date, end_date) except FileNotFoundError: return {} @router.get("/tracker/points/add") +@check_name def add_point(name: str, value: int, date_str: str = None, access_token: APIKey = Depends(get_api_key)): if date_str is None: date_str = datetime.now().isoformat() @@ -27,10 +43,11 @@ def add_point(name: str, value: int, date_str: str = None, access_token: APIKey except FileNotFoundError: tracker = Tracker(name, []) tracker.add_point(date_str, value) - return tracker.to_json() + return tracker.list_points() @router.put("/tracker/points/modify") +@check_name def modify_point(name: str, date_str: str, value: int, access_token: APIKey = Depends(get_api_key)): tracker = Tracker.from_data(name) tracker.modify_point(date_str, value) @@ -38,6 +55,7 @@ def modify_point(name: str, date_str: str, value: int, access_token: APIKey = De @router.delete("/tracker/points/delete") +@check_name def delete_point(name: str, date_str: str, access_token: APIKey = Depends(get_api_key)): tracker = Tracker.from_data(name) tracker.delete_point(date_str) @@ -45,6 +63,7 @@ def delete_point(name: str, date_str: str, access_token: APIKey = Depends(get_ap @router.get("/tracker/rename") +@check_name def rename_tracker(name: str, new_name: str, access_token: APIKey = Depends(get_api_key)): tracker = Tracker.from_data(name) tracker.rename(new_name) @@ -52,6 +71,7 @@ def rename_tracker(name: str, new_name: str, access_token: APIKey = Depends(get_ @router.delete("/tracker/delete") +@check_name def delete_tracker(name: str, access_token: APIKey = Depends(get_api_key)): tracker = Tracker.from_data(name) - tracker.delete() \ No newline at end of file + tracker.delete()