taskcheck 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: taskcheck
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Automatic scheduler for taskwarrior
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taskcheck"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "Automatic scheduler for taskwarrior"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -77,6 +77,12 @@ def get_available_hours(time_map, date, calendars):
77
77
  schedule_end = _hours_to_time(schedule_end)
78
78
  for calendar in calendars:
79
79
  for event in calendar:
80
+ # we use str to make object serializable as jsons
81
+ if isinstance(event["start"], str):
82
+ event["start"] = datetime.fromisoformat(event["start"])
83
+ if isinstance(event["end"], str):
84
+ event["end"] = datetime.fromisoformat(event["end"])
85
+
80
86
  if event["start"].date() > date:
81
87
  break
82
88
  elif event["end"].date() < date:
@@ -141,7 +147,7 @@ def schedule_task_on_day(
141
147
  # we can schedule task on this day
142
148
  employable_hours = task_time_map[day_offset] - used_hours[day_offset]
143
149
  current_date = today + timedelta(days=day_offset)
144
- if wait and current_date < wait:
150
+ if wait and current_date <= wait:
145
151
  if args.verbose:
146
152
  print(f"Skipping date {current_date} because of wait date {wait}")
147
153
  return start_date, end_date, task_remaining_hours, is_starting
@@ -243,10 +249,10 @@ def check_tasks_sequentially(tasks, config):
243
249
  f"Task {task['id']} ('{task['description']}') has no estimated time or time map: {estimated_hours}, {time_map_names}"
244
250
  )
245
251
  continue
246
- # if args.verbose:
247
- # print(
248
- # f"Checking task {task['id']} ('{task['description']}') with estimated hours: {estimated_hours} and wait date: {wait_date}"
249
- # )
252
+ if args.verbose:
253
+ print(
254
+ f"Checking task {task['id']} ('{task['description']}') with estimated hours: {estimated_hours} and wait date: {wait_date}"
255
+ )
250
256
 
251
257
  task_remaining_hours = estimated_hours
252
258
  task_time_map = get_long_range_time_map(
@@ -1,12 +1,18 @@
1
+ import hashlib
2
+ import json
1
3
  from icalendar import Calendar
2
4
  from datetime import datetime, timedelta
3
5
  from dateutil.rrule import rruleset, rrulestr
4
6
  from requests_cache import CachedSession
7
+ from pathlib import Path
8
+ import appdirs
5
9
 
6
10
  import time
7
11
 
12
+ CACHE = Path(appdirs.user_cache_dir("taskcheck"))
8
13
 
9
- def fetch_ical_data(url, expiration, verbose):
14
+
15
+ def fetch_ical_data(url, expiration):
10
16
  session = CachedSession(
11
17
  "taskcheck",
12
18
  use_cache_dir=True, # Save files in the default user cache dir
@@ -18,10 +24,7 @@ def fetch_ical_data(url, expiration, verbose):
18
24
  allowable_methods=["GET", "POST"], # Cache whatever HTTP methods you want
19
25
  stale_if_error=True, # In case of request errors, use stale cache data if possible
20
26
  )
21
- ttt = time.time()
22
27
  response = session.get(url)
23
- if verbose:
24
- print("Time taken to fetch ical data: ", time.time() - ttt)
25
28
  response.raise_for_status()
26
29
  return response.text
27
30
 
@@ -62,8 +65,18 @@ def parse_ical_events(ical_text, days_ahead, all_day):
62
65
 
63
66
  event_dates = rruleset()
64
67
  if recurrence_rule:
68
+ # avoid handling rules that are already ended
69
+ # and don't create events beyond end_date
70
+ until = recurrence_rule.get("UNTIL")
71
+ if until is not None:
72
+ until = until[0].date()
73
+ if until < today:
74
+ continue
75
+ elif until > end_date:
76
+ recurrence_rule["UNTIL"] = end_date
65
77
  rrule = rrulestr(
66
- str(recurrence_rule.to_ical(), "utf-8"), dtstart=event_start
78
+ str(recurrence_rule.to_ical(), "utf-8"),
79
+ dtstart=event_start,
67
80
  )
68
81
  event_dates.rrule(rrule) # type: ignore
69
82
  else:
@@ -74,15 +87,46 @@ def parse_ical_events(ical_text, days_ahead, all_day):
74
87
  events.append(
75
88
  {
76
89
  "summary": component.get("summary"),
77
- "start": event_date,
78
- "end": event_end,
90
+ "start": event_date.isoformat(),
91
+ "end": event_end.isoformat(),
79
92
  }
80
93
  )
81
94
 
82
95
  return events
83
96
 
84
97
 
98
+ def get_cache_filename(url):
99
+ hash_object = hashlib.sha256(url.encode())
100
+ return CACHE / f"{hash_object.hexdigest()}.json"
101
+
102
+
85
103
  def ical_to_dict(url, days_ahead=7, all_day=False, expiration=0.25, verbose=False):
86
- ical_text = fetch_ical_data(url, expiration * 60, verbose)
104
+ CACHE.mkdir(exist_ok=True, parents=True)
105
+ cache_file = CACHE / get_cache_filename(url)
106
+ current_time = time.time()
107
+
108
+ # Check if cache file exists and is not expired
109
+ if cache_file.exists():
110
+ cache_mtime = cache_file.stat().st_mtime
111
+ if current_time - cache_mtime < expiration * 3600:
112
+ with open(cache_file, "r") as f:
113
+ events = json.load(f)
114
+ if verbose:
115
+ print("Loaded events from cache")
116
+ return events
117
+
118
+ # Fetch and parse iCal data
119
+ ttt = time.time()
120
+ ical_text = fetch_ical_data(url, expiration * 3600)
121
+ if verbose:
122
+ print("Time taken to fetch ical data: ", time.time() - ttt)
123
+ ttt = time.time()
87
124
  events = parse_ical_events(ical_text, days_ahead, all_day)
125
+ if verbose:
126
+ print("Time taken to parse ical data: ", time.time() - ttt)
127
+
128
+ # Save events to cache
129
+ with open(cache_file, "w") as f:
130
+ json.dump(events, f)
131
+
88
132
  return events
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: taskcheck
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Automatic scheduler for taskwarrior
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes