1
0

Compare commits

..

25 Commits

Author SHA1 Message Date
b5c15f5d9a 🤖 Bumped version to 0.1.19
Some checks failed
Package Python / Package (push) Successful in 24s
Test Python / Test (push) Failing after 23s
This commit was automatically generated by a script: https://gitfub.space/Jmaa/repo-manager
2024-09-27 00:07:09 +02:00
d775b4ad0e
Support non-timezone datetimes 2024-09-27 00:03:41 +02:00
d8f6c6bf65 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/repo-manager
2024-09-27 00:02:43 +02:00
907faf4f99 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/repo-manager
2024-09-27 00:01:31 +02:00
9b0142eae8 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/repo-manager
2024-09-26 23:13:09 +02:00
1624b1d04d 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/repo-manager
2024-09-26 17:49:16 +02:00
c9bfad20ce 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/repo-manager
2024-09-26 17:48:31 +02:00
c746afa926 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-21 14:40:27 +02:00
e7dec35e2f 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-21 14:40:08 +02:00
b3dd253f47 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-21 14:39:57 +02:00
54fdb537be 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-21 14:39:04 +02:00
5eb44f0e71 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-21 14:38:50 +02:00
06dd687a98
Ruff 2024-09-21 00:32:19 +02:00
66ba20d629 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 19:19:58 +02:00
f118ff5d99 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 19:10:16 +02:00
a5f3945d78 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 19:08:11 +02:00
261501de09 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 19:07:11 +02:00
a1aa629566 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:35:16 +02:00
4eade4216f 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:31:19 +02:00
614aa18027 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:30:51 +02:00
a8bb4aacf1 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:30:21 +02:00
ec560cbba6 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:29:45 +02:00
b486d05658 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:28:33 +02:00
5eac80c5aa 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:27:37 +02:00
20cb6f38c9 🤖 Repository layout updated to latest version
This commit was automatically generated by a script: https://gitfub.space/Jmaa/python-omni
2024-09-19 18:26:38 +02:00
6 changed files with 134 additions and 57 deletions

View File

@ -31,7 +31,20 @@ And the ([Hamster](https://github.com/projecthamster/hamster)) manual time track
![](docs/obligatory-hamster.png)
# License
## Dependencies
All requirements can be installed easily using:
```bash
pip install -r requirements.txt
```
Full list of requirements:
- [GitPython](https://pypi.org/project/GitPython/)
- [icalendar](https://pypi.org/project/icalendar/)
## License
```
MIT License

View File

@ -39,7 +39,7 @@ from .data import (
WorkSample,
)
from .format import cli, icalendar
from .source import git_repo, csv_file
from .source import csv_file, git_repo
logger = logging.getLogger(__name__)
@ -51,7 +51,8 @@ MINUTE = datetime.timedelta(minutes=1)
def filter_samples(
samples: list[WorkSample], sample_filter: set[str],
samples: list[WorkSample],
sample_filter: set[str],
) -> list[WorkSample]:
assert len(sample_filter) > 0
return [s for s in samples if set(s.labels).intersection(sample_filter)]
@ -66,10 +67,16 @@ def heuristically_realize_samples(
* No samples overlap.
"""
previous_sample_end = datetime.datetime.fromtimestamp(0, datetime.UTC)
previous_sample_end = None
for sample in samples:
end_at = sample.end_at
if previous_sample_end is None:
if end_at.tzinfo:
previous_sample_end = datetime.datetime.fromtimestamp(0, datetime.UTC)
else:
previous_sample_end = datetime.datetime.fromtimestamp(0)
assert previous_sample_end <= end_at, 'Iterating in incorrect order'
# TODO: Allow end_at is None
@ -120,9 +127,17 @@ def parse_arguments():
default='cli_report',
choices=['cli_report', 'icalendar'],
)
parser.add_argument(
'--out',
action='store',
type=Path,
dest='output_file',
default='output/samples.ics',
)
return parser.parse_args()
def load_samples(args):
def load_samples(args) -> set[WorkSample]:
shared_time_stamps_set: set[WorkSample] = set()
# Git repositories
@ -141,9 +156,9 @@ def load_samples(args):
)
del csv_path
return shared_time_stamps_set
def main():
logging.basicConfig()
@ -173,5 +188,6 @@ def main():
sys.stdout.write(t)
elif args.format_mode == 'icalendar':
icalendar.generate_icalendar_file(
shared_time_stamps, file='./output/samples.ics',
shared_time_stamps,
file=args.output_file,
)

View File

@ -1 +1 @@
__version__ = '0.1.18'
__version__ = '0.1.19'

View File

@ -9,7 +9,7 @@ HOUR = datetime.timedelta(hours=1)
MINUTE = datetime.timedelta(minutes=1)
def create_title(sample: RealizedWorkSample) -> tuple[str,str]:
def create_title(sample: RealizedWorkSample) -> tuple[str, str]:
ls = []
desc = []
for label_and_type in sample.labels:
@ -47,7 +47,8 @@ def generate_calendar(
for label_and_type in sample.labels:
if label_and_type.startswith('author:'):
event.add(
'organizer', 'mailto:' + label_and_type.removeprefix('author:'),
'organizer',
'mailto:' + label_and_type.removeprefix('author:'),
)
cal.add_component(event)

View File

@ -1,60 +1,102 @@
import argparse
from collections.abc import Iterator
from decimal import Decimal
import datetime
import urllib.parse
from typing import Any
from collections.abc import Iterator
from decimal import Decimal
from pathlib import Path
import dataclasses
from personal_data.util import load_csv_file
from ..data import WorkSample
def iterate_samples_from_dicts(rows: list[dict]) -> Iterator[WorkSample]:
max_title_parts = 2
for event_data in rows:
@dataclasses.dataclass
class PossibleKeys:
time_start: list[str]
time_end: list[str]
duration: list[str]
name: list[str]
image: list[str]
misc: list[str]
def determine_possible_keys(event_data: dict[str, Any]) -> PossibleKeys:
# Select data
possible_time_keys = [
time_keys = [
k for k, v in event_data.items() if isinstance(v, datetime.date)
]
possible_duration_keys = [
k for k, v in event_data.items() if isinstance(v, Decimal) and 'duration_seconds' in k
duration_keys = [
k
for k, v in event_data.items()
if isinstance(v, Decimal) and 'duration_seconds' in k
]
possible_name_keys = [k for k, v in event_data.items() if isinstance(v, str)]
possible_image_keys = [
name_keys = [k for k, v in event_data.items() if isinstance(v, str)]
image_keys = [
k for k, v in event_data.items() if isinstance(v, urllib.parse.ParseResult)
]
possible_misc_keys = list(event_data.keys())
for k in possible_image_keys:
if k in possible_misc_keys:
possible_misc_keys.remove(k)
misc_keys = list(event_data.keys())
for k in image_keys:
if k in misc_keys:
misc_keys.remove(k)
del k
for k in possible_time_keys :
if k in possible_misc_keys:
possible_misc_keys.remove(k)
for k in time_keys:
if k in misc_keys:
misc_keys.remove(k)
del k
date = event_data[possible_time_keys[0]] if possible_time_keys else None
image = event_data[possible_image_keys[0]] if possible_image_keys else None
time_start_keys = [k for k in time_keys if 'start' in k.lower() ]
time_end_keys = [k for k in time_keys if 'end' in k.lower() or 'stop' in k.lower() ]
if date is None:
continue
return PossibleKeys(
time_start = time_start_keys,
time_end = time_end_keys,
duration = duration_keys,
name = name_keys,
image = image_keys,
misc = misc_keys,
)
if len(possible_duration_keys) > 0:
start_at = date
seconds = event_data[possible_duration_keys[0]]
end_at = date + datetime.timedelta(seconds = float(seconds))
del seconds
else:
start_at = None
end_at = date
def start_end(sample: dict[str,Any], keys: PossibleKeys) -> tuple[datetime.datetime | None, datetime.datetime | None]:
if keys.time_start and keys.time_end:
return (sample[keys.time_start[0]], sample[keys.time_end[0]])
if keys.time_start and keys.duration:
start = sample[keys.time_start[0]]
duration = datetime.timedelta(seconds=float(sample[keys.duration[0]]))
return (start, start + duration)
if keys.time_start:
start = sample[keys.time_start[0]]
return (start, None)
if keys.time_end:
return (None, sample[keys.time_end[0]])
return (None, None)
def iterate_samples_from_dicts(rows: list[dict[str,Any]]) -> Iterator[WorkSample]:
assert len(rows) > 0
max_title_parts = 2
if True:
event_data = rows[len(rows)//2] # Hopefully select a useful representative.
possible_keys = determine_possible_keys(event_data)
del event_data
assert len(possible_keys.time_start) + len(possible_keys.time_end) >= 1
assert len(possible_keys.image) >= 0
for event_data in rows:
'''
title = ': '.join(event_data[k] for k in possible_name_keys[:max_title_parts])
description = '\n\n'.join(event_data[k] for k in possible_name_keys[max_title_parts:])
description = '\n\n'.join(
event_data[k] for k in possible_name_keys[max_title_parts:]
)
image = event_data[possible_keys.image[0]] if possible_keys.image else None
'''
labels = [f'{k}:{event_data[k]}' for k in possible_misc_keys]
(start_at, end_at) = start_end(event_data, possible_keys)
labels = [f'{k}:{event_data[k]}' for k in possible_keys.misc]
# Create event
yield WorkSample(
@ -65,6 +107,9 @@ def iterate_samples_from_dicts(rows: list[dict]) -> Iterator[WorkSample]:
del event_data
def iterate_samples_from_csv_file(file_path: Path) -> Iterator[WorkSample]:
dicts = load_csv_file(file_path)
yield from iterate_samples_from_dicts(dicts)
samples = list(iterate_samples_from_dicts(dicts))
assert len(samples) > 0, 'Did not found any samples'
yield from samples

View File

@ -39,10 +39,12 @@ def get_samples_from_project(repo: git.Repo) -> Iterator[WorkSample]:
labels.append('author:' + commit.author.email)
authored_date = datetime.datetime.fromtimestamp(
commit.authored_date, tz=datetime.UTC,
commit.authored_date,
tz=datetime.UTC,
)
committed_date = datetime.datetime.fromtimestamp(
commit.committed_date, tz=datetime.UTC,
commit.committed_date,
tz=datetime.UTC,
)
yield WorkSample(