utils.py (3217B)
1 # Copyright 2021 Matthias Balk 2 # 3 # This file is part of ya3 (yet another appointment application). 4 # 5 # ya3 is free software: you can redistribute it and/or modify it under the 6 # terms of the GNU Affero General Public License as published by the Free 7 # Software Foundation, either version 3 of the License, or (at your option) any 8 # later version. 9 # 10 # ya3 is distributed in the hope that it will be useful, but WITHOUT ANY 11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more 13 # details. 14 # 15 # You should have received a copy of the GNU Affero General Public License 16 # along with ya3. If not, see <http://www.gnu.org/licenses/>. 17 18 from datetime import datetime 19 20 import vobject 21 from dateutil.tz import gettz 22 23 import config 24 25 26 def get_ya3_version(): 27 return '0.2' 28 29 30 def parse2local(dt, force_datetime=False): 31 32 if isinstance(dt, datetime): 33 if dt.tzinfo is None: 34 return datetime(dt.year, dt.month, dt.day, 35 dt.hour, dt.minute, dt.second, 36 tzinfo=gettz()) 37 return dt.astimezone(gettz()) 38 39 # date only 40 else: 41 if force_datetime: 42 return datetime(dt.year, dt.month, dt.day, 43 0, 0, 0, tzinfo=gettz()) 44 else: 45 return dt 46 47 48 def normalize(event): 49 if 'dtend' not in event.contents: 50 event.add('dtend') 51 if 'duration' in event.contents: 52 event.dtend.value = event.dtstart.value + event.duration.value 53 else: 54 event.dtend.value = event.dtstart.value + config.DEFAULT_DURATION 55 56 return event 57 58 59 def get_events(icsfilenames, ignore_configured_range=False): 60 61 def _is_in_range(event): 62 if ignore_configured_range: 63 return True 64 start = parse2local(event.dtstart.value, force_datetime=True) 65 end = parse2local(event.dtend.value, force_datetime=True) 66 return (end >= datetime.now(gettz()) - config.MAX_AGE and 67 start < datetime.now(gettz()) + config.MAX_AHEAD) 68 69 events = [] 70 for filename in icsfilenames: 71 with open(filename) as f: 72 for cal in vobject.readComponents(f): 73 events.extend(filter(_is_in_range, 74 map(normalize, cal.vevent_list))) 75 76 return sorted(events, key=lambda ev: parse2local(ev.dtstart.value, 77 force_datetime=True)) 78 79 80 def event2string(event): 81 start = parse2local(event.dtstart.value) 82 end = parse2local(event.dtend.value) 83 84 try: 85 location = " (" + event.location.value + ")" 86 except AttributeError: 87 location = "" 88 89 if isinstance(start, datetime): 90 return ('%s - %s %s%s' % (start.strftime('%Y-%m-%d %H:%M'), 91 end.strftime('%H:%M'), 92 event.summary.value, 93 location)) 94 95 # date only 96 else: 97 return '{start} {summary}{location}'.format( 98 start=start.strftime('%Y-%m-%d'), 99 summary=event.summary.value, 100 location=location)