def __init__(self, component: CalEvent) -> None: """ Initialize Event object :param component: calendar event component """ self.name: str = "" self.type: Optional[str] = None self.teacher: Optional[str] = None # Get location from the calendar event replace @ with CNTI, set as None if no location self.location: Optional[str] = str(component.get("location")).strip() self.location = self.location.replace("@", "CNTI") self.location = None if self.location == "brak lokalizacji brak sali" else self.location # Normalize start and end time to Europe/Warsaw timezone tz = timezone("Europe/Warsaw") self.start: datetime = tz.normalize( tz.localize(component.get("dtstart").dt)) self.end: datetime = tz.normalize( tz.localize(component.get("dtend").dt)) # fix the finish time of 1.5h or 2.25h events # subtract 10 min break pointlessly included in original schedule duration = self.end - self.start if duration == timedelta(minutes=100) or duration == timedelta( minutes=155): self.end -= timedelta(minutes=10) self.parseSummary(str(component.get("summary")).strip())
def write_calendar(options, sources, google_calendar): """Create and write ics file""" cal = Calendar() google_events = [] timezones_cache = [] for key, value in options.items(): cal.add(key, value) for source_id, category in sources.items(): for timezone in in_cals[source_id].walk('VTIMEZONE'): if timezone['tzid'] not in timezones_cache: timezones_cache.append(timezone['tzid']) cal.add_component(timezone) for event in in_cals[source_id].walk('VEVENT'): event_copy = Event(event) event_copy['SUMMARY'] = f'{category}: ' + event['SUMMARY'] event_copy.add('categories', category) cal.add_component(event_copy) # dt_utc = event_copy['DTSTART'].dt.astimezone(pytz.utc) start = event_copy['DTSTART'].dt start = (start if isinstance(start, datetime.datetime) else datetime.datetime.combine( start, datetime.time( hour=0, minute=0, second=0, tzinfo=dateutil.tz.tzutc()))) end = event_copy['DTEND'].dt if event_copy.get('DTEND') else start end = (end if isinstance(end, datetime.datetime) else datetime.datetime.combine( end, datetime.time( hour=0, minute=0, second=0, tzinfo=dateutil.tz.tzutc()))) google_event = { 'id': convert_osm_uid_to_valid_google_id( str(event_copy['UID'])), 'start': start, 'end': end, 'summary': str(event_copy['SUMMARY']), 'description': str(event_copy.get('DESCRIPTION', '')), 'location': str(event_copy.get('LOCATION', ''))} google_events.append(google_event) sync_google_calendar(google_calendar, google_events)
def groupme_json_to_ics(groupme_json, static_name=None): cal = Calendar() cal['prodid'] = '-//Andrew Mussey//GroupMe-to-ICS 0.1//EN' cal['version'] = '2.0' cal['calscale'] = 'GREGORIAN' cal['method'] = 'PUBLISH' cal['x-wr-calname'] = 'GroupMe: {}'.format( current_app.groupme_calendar_name) cal['x-wr-timezone'] = 'America/Los_Angeles' for json_blob in groupme_json['response']['events']: if 'deleted_at' not in json_blob: event = Event() event['uid'] = json_blob['event_id'] event.add('dtstart', dateutil.parser.parse(json_blob['start_at'])) if json_blob.get('end_at'): event.add('dtend', dateutil.parser.parse(json_blob['end_at'])) event['summary'] = json_blob['name'] event['description'] = json_blob.get('description', '') if json_blob.get('location'): location = json_blob.get('location', {}) if json_blob.get('description'): event['description'] += '\n\n' event['description'] += 'Location:\n' if location.get('name') and location.get('address'): event['location'] = "{}, {}".format( location.get('name'), location.get('address').strip().replace("\n", ", ")) event['description'] += location.get('name') event['description'] += '\n' event['description'] += location.get('address') elif location.get('name'): event['location'] = location.get('name') event['description'] += location.get('name') elif location.get('address'): event['location'] = location.get( 'address').strip().replace("\n", ", ") event['description'] += location.get('address') if location.get('lat') and location.get('lng'): location_url = 'https://www.google.com/maps?q={},{}'.format( location.get('lat'), location.get('lng')) if not event.get('location'): event['location'] = location_url else: event['description'] += '\n' event['description'] += location_url if json_blob.get('updated_at'): event['last-modified'] = dateutil.parser.parse( json_blob.get('updated_at')) cal.add_component(event) return cal.to_ical()
def to_ical_event(self) -> Event: """ Returns this event as an iCalendar Event object """ event = Event() event.add("uid", self._event["iCalUID"]) event.add("summary", self.summary) event.add("location", self.location) event.add("description", self.description) event.add("dtstart", self.start_datetime) event.add("dtend", self.end_datetime) if event.get("reccurence"): event.add("sequence", event["sequence"]) for i in event.get("reccurence"): prop_name, v = i.split(":", max_split=1) event[prop_name] = v return event
def populate_content_with_event( self, content: Content, event: iCalendarEvent, event_name: str, ) -> None: """ Populate Content content instance from iCalendarEvent event attributes. :param content: content to populate :param event: event with data to insert in content :param event_name: Event name (ID) like 20160602T083511Z-18100-1001-1-71_Bastien-20160602T083516Z.ics :return: given content """ content.label = event.get('summary') content.description = event.get('description') content.properties = { 'name': event_name, 'location': event.get('location'), 'raw': event.to_ical().decode("utf-8"), 'start': event.get('dtend').dt.strftime('%Y-%m-%d %H:%M:%S%z'), 'end': event.get('dtstart').dt.strftime('%Y-%m-%d %H:%M:%S%z'), }
def groupme_json_to_ics(groupme_json, static_name=None): cal = Calendar() cal['prodid'] = '-//Andrew Mussey//GroupMe-to-ICS 0.1//EN' cal['version'] = '2.0' cal['calscale'] = 'GREGORIAN' cal['method'] = 'PUBLISH' cal['x-wr-calname'] = 'GroupMe: {}'.format(current_app.groupme_calendar_name) cal['x-wr-timezone'] = 'America/Los_Angeles' for json_blob in groupme_json['response']['events']: if 'deleted_at' not in json_blob: event = Event() event['uid'] = json_blob['event_id'] event.add('dtstart', dateutil.parser.parse(json_blob['start_at'])) if json_blob.get('end_at'): event.add('dtend', dateutil.parser.parse(json_blob['end_at'])) event['summary'] = json_blob['name'] event['description'] = json_blob.get('description', '') if json_blob.get('location'): location = json_blob.get('location', {}) if json_blob.get('description'): event['description'] += '\n\n' event['description'] += 'Location:\n' if location.get('name') and location.get('address'): event['location'] = "{}, {}".format(location.get('name'), location.get('address').strip().replace("\n", ", ")) event['description'] += location.get('name') event['description'] += '\n' event['description'] += location.get('address') elif location.get('name'): event['location'] = location.get('name') event['description'] += location.get('name') elif location.get('address'): event['location'] = location.get('address').strip().replace("\n", ", ") event['description'] += location.get('address') if location.get('lat') and location.get('lng'): location_url = 'https://www.google.com/maps?q={},{}'.format(location.get('lat'), location.get('lng')) if not event.get('location'): event['location'] = location_url else: event['description'] += '\n' event['description'] += location_url if json_blob.get('updated_at'): event['last-modified'] = dateutil.parser.parse(json_blob.get('updated_at')) cal.add_component(event) return cal.to_ical()
def get_ics_file(self, events_exported, partner): """ Returns iCalendar file for the event invitation. @param event: event object (browse record) @return: .ics file content """ ics = Event() event = self[0] #~ raise Warning(self.env.cr.dbname) #~ The method below needs som proper rewriting to avoid overusing libraries. def ics_datetime(idate, allday=False): if idate: if allday: return str(vDatetime(datetime.fromtimestamp(mktime(strptime(idate, DEFAULT_SERVER_DATETIME_FORMAT)))).to_ical())[:8] else: return vDatetime(datetime.fromtimestamp(mktime(strptime(idate, DEFAULT_SERVER_DATETIME_FORMAT)))).to_ical() + 'Z' return False #~ try: #~ # FIXME: why isn't this in CalDAV? #~ import vobject #~ except ImportError: #~ return res #~ cal = vobject.iCalendar() #~ event = cal.add('vevent') if not event.start or not event.stop: raise osv.except_osv(_('Warning!'), _("First you have to specify the date of the invitation.")) ics['summary'] = event.name if event.description: ics['description'] = event.description if event.location: ics['location'] = event.location if event.rrule: ics['rrule'] = event.rrule #~ ics.add('rrule', str(event.rrule), encode=0) #~ raise Warning(ics['rrule']) if event.alarm_ids: for alarm in event.alarm_ids: if alarm.type == 'notification': valarm = Alarm() valarm.add('ACTION', 'DISPLAY') if alarm.interval == 'days': delta = timedelta(days=alarm.duration) elif alarm.interval == 'hours': delta = timedelta(hours=alarm.duration) elif alarm.interval == 'minutes': delta = timedelta(minutes=alarm.duration) trigger = valarm.add('TRIGGER', -delta) #fields.Datetime.from_string(event.start) - valarm.add('DESCRIPTION', event.name) ics.add_component(valarm) if event.attendee_ids: for attendee in event.attendee_ids: attendee_add = ics.get('attendee') attendee_add = attendee.cn and ('CN=' + attendee.cn) or '' if attendee.cn and attendee.email: attendee_add += ':' attendee_add += attendee.email and ('MAILTO:' + attendee.email) or '' ics.add('attendee', attendee_add, encode=0) if events_exported: event_not_found = True for event_comparison in events_exported: #~ raise Warning('event_comparison = %s ics = %s' % (event_comparison, ics)) if str(ics) == event_comparison: event_not_found = False break if event_not_found: events_exported.append(str(ics)) ics['uid'] = '%s@%s-%s' % (event.id, self.env.cr.dbname, partner.id) ics['created'] = ics_datetime(strftime(DEFAULT_SERVER_DATETIME_FORMAT)) tmpStart = ics_datetime(event.start, event.allday) tmpEnd = ics_datetime(event.stop, event.allday) if event.allday: ics['dtstart;value=date'] = tmpStart else: ics['dtstart'] = tmpStart if tmpStart != tmpEnd or not event.allday: if event.allday: ics['dtend;value=date'] = str(vDatetime(datetime.fromtimestamp(mktime(strptime(event.stop, DEFAULT_SERVER_DATETIME_FORMAT))) + timedelta(hours=24)).to_ical())[:8] else: ics['dtend'] = tmpEnd return [ics, events_exported] else: events_exported.append(str(ics)) ics['uid'] = '%s@%s-%s' % (event.id, self.env.cr.dbname, partner.id) ics['created'] = ics_datetime(strftime(DEFAULT_SERVER_DATETIME_FORMAT)) tmpStart = ics_datetime(event.start, event.allday) tmpEnd = ics_datetime(event.stop, event.allday) if event.allday: ics['dtstart;value=date'] = tmpStart else: ics['dtstart'] = tmpStart if tmpStart != tmpEnd or not event.allday: if event.allday: ics['dtend;value=date'] = str(vDatetime(datetime.fromtimestamp(mktime(strptime(event.stop, DEFAULT_SERVER_DATETIME_FORMAT))) + timedelta(hours=24)).to_ical())[:8] else: ics['dtend'] = tmpEnd return [ics, events_exported]
def create_school_calendar(details, timetables, hours, name, timetable=True, substitutions=True): logger = logging.getLogger(__name__) calendar = Calendar() calendar.add("prodid", "gimvicurnik") calendar.add("version", "2.0") calendar.add("X-WR-TIMEZONE", "Europe/Ljubljana") calendar.add("X-WR-CALNAME", name) calendar.add("X-WR-CALDESC", name) calendar.add("NAME", name) calendar.add("X-PUBLISHED-TTL", vDuration(timedelta(hours=1))) calendar.add("REFRESH-INTERVAL", vDuration(timedelta(hours=1))) year = datetime.now().year if datetime.now().date() >= date( datetime.now().year, 9, 1) else datetime.now().year - 1 weekdays = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"] weektable = [[None for i in range(10)] for j in range(6)] if timetable: for subject in timetables: with start_span(op="event") as span: span.set_tag("event.type", "timetable") span.set_tag("event.day", subject["day"]) span.set_tag("event.time", subject["time"]) span.set_data("event.source", subject) logger.info("Preparing iCalendar event", extra={ "type": "timetable", "source": subject }) event = Event() event.add("dtstamp", datetime.now()) event.add("CATEGORIES", vText("NORMAL")) event.add("COLOR", vText("green")) event.add( "UID", sha256((str(subject["day"]) + str(subject["time"]) + str(subject["subject"]) + str(subject["class"]) + str(subject["teacher"])).encode()).hexdigest(), ) start = datetime(year, 8, 31) + hours[subject["time"]]["hour"]["start"] event.add("dtstart", start) event["EXDATE"] = vDatetime(start).to_ical().decode("utf-8") event["DURATION"] = "PT45M" event["RRULE"] = ( "FREQ=WEEKLY;BYDAY=" + weekdays[subject["day"]] + ";UNTIL=" + vDatetime( datetime(year + 1, 6, 25)).to_ical().decode("utf-8")) event.add("summary", subject["subject"]) event["organizer"] = vText(subject["teacher"]) event["location"] = vText(subject["classroom"]) weektable[subject["day"]][subject["time"]] = event if substitutions: for subject in details: with start_span(op="event") as span: span.set_tag("event.type", "substitution") span.set_tag("event.date", subject["date"]) span.set_tag("event.day", subject["day"]) span.set_tag("event.time", subject["time"]) span.set_data("event.source", subject) logger.info("Preparing iCalendar event", extra={ "type": "substitution", "source": subject }) event = Event() event.add("dtstamp", datetime.now()) event.add("CATEGORIES", vText("SUBSTITUTION")) event.add("COLOR", vText("darkred")) event.add( "UID", sha256((str(subject["date"]) + str(subject["day"]) + str(subject["time"]) + str(subject["subject"]) + str(subject["class"]) + str(subject["teacher"])).encode()).hexdigest(), ) date_ = datetime.strptime(subject["date"], "%Y-%m-%d") event.add("dtstart", date_ + hours[subject["time"]]["hour"]["start"]) event.add("dtend", date_ + hours[subject["time"]]["hour"]["end"]) event.add("summary", subject["subject"]) event["organizer"] = vText(subject["teacher"]) event["location"] = vText(subject["classroom"]) if weektable[datetime.strptime( subject["date"], "%Y-%m-%d").isoweekday()][subject["time"]]: original = weektable[datetime.strptime( subject["date"], "%Y-%m-%d").isoweekday()][subject["time"]] original["EXDATE"] += "," + event.get( "dtstart").to_ical().decode("utf-8") calendar.add_component(event) for i in range(len(weektable)): for j in range(len(weektable[0])): if weektable[i][j]: calendar.add_component(weektable[i][j]) response = make_response(calendar.to_ical().decode("utf-8").replace( "\\", "")) response.headers[ "Content-Disposition"] = "attachment; filename=calendar.ics" response.headers["Content-Type"] = "text/calendar; charset=utf-8" return response
def get_caldav_event(self, events_exported, partner): """ Returns iCalendar file for the event invitation. @param event: event object (browse record) @return: .ics file content """ ics = Event() event = self[0] def ics_datetime(idate, allday=False): if idate: if allday: return str(vDatetime(datetime.fromtimestamp(mktime(strptime(idate, DEFAULT_SERVER_DATETIME_FORMAT)))).to_ical())[:8] else: return vDatetime(datetime.fromtimestamp(mktime(strptime(idate, DEFAULT_SERVER_DATETIME_FORMAT)))).to_ical() + 'Z' return False raise osv.except_osv(_('Warning!'), _("First you have to specify the date of the invitation.")) ics['summary'] = event.name if event.description: ics['description'] = event.description if event.location: ics['location'] = event.location if event.rrule: ics['rrule'] = event.rrule if event.alarm_ids: for alarm in event.alarm_ids: valarm = ics.add('valarm') interval = alarm.interval duration = alarm.duration trigger = valarm.add('TRIGGER') trigger.params['related'] = ["START"] if interval == 'days': delta = timedelta(days=duration) elif interval == 'hours': delta = timedelta(hours=duration) elif interval == 'minutes': delta = timedelta(minutes=duration) trigger.value = delta valarm.add('DESCRIPTION').value = alarm.name or 'Odoo' if event.attendee_ids: for attendee in event.attendee_ids: attendee_add = ics.get('attendee') attendee_add = attendee.cn and ('CN=' + attendee.cn) or '' if attendee.cn and attendee.email: attendee_add += ':' attendee_add += attendee.email and ('MAILTO:' + attendee.email) or '' ics.add('attendee', attendee_add, encode=0) if events_exported: event_not_found = True for event_comparison in events_exported: if str(ics) == event_comparison: event_not_found = False break if event_not_found: events_exported.append(str(ics)) ics['uid'] = '%s@%s-%s' % (event.id, self.env.cr.dbname, partner.id) ics['created'] = ics_datetime(strftime(DEFAULT_SERVER_DATETIME_FORMAT)) tmpStart = ics_datetime(event.start, event.allday) tmpEnd = ics_datetime(event.stop, event.allday) if event.allday: ics['dtstart;value=date'] = tmpStart else: ics['dtstart'] = tmpStart if tmpStart != tmpEnd or not event.allday: if event.allday: ics['dtend;value=date'] = str(vDatetime(datetime.fromtimestamp(mktime(strptime(event.stop, DEFAULT_SERVER_DATETIME_FORMAT))) + timedelta(hours=24)).to_ical())[:8] else: ics['dtend'] = tmpEnd return [ics, events_exported] else: events_exported.append(str(ics)) ics['uid'] = '%s@%s-%s' % (event.id, self.env.cr.dbname, partner.id) ics['created'] = ics_datetime(strftime(DEFAULT_SERVER_DATETIME_FORMAT)) tmpStart = ics_datetime(event.start, event.allday) tmpEnd = ics_datetime(event.stop, event.allday) if event.allday: ics['dtstart;value=date'] = tmpStart else: ics['dtstart'] = tmpStart if tmpStart != tmpEnd or not event.allday: if event.allday: ics['dtend;value=date'] = str(vDatetime(datetime.fromtimestamp(mktime(strptime(event.stop, DEFAULT_SERVER_DATETIME_FORMAT))) + timedelta(hours=24)).to_ical())[:8] else: ics['dtend'] = tmpEnd return [ics, events_exported]
def from_calendar(cls, component: CalEvent) -> "Event": """ Initialize Event object :param component: calendar event component """ # Get location from the calendar event replace @ with CNTI, set as None if no location location: Optional[str] = str(component.get("location")).strip() if location: location = location.replace("@", "CNTI") location = None if location == "brak lokalizacji brak sali" else location # Normalize start and end time to Europe/Warsaw timezone tz = timezone("Europe/Warsaw") start: datetime = tz.normalize(tz.localize( component.get("dtstart").dt)) end: datetime = tz.normalize(tz.localize(component.get("dtend").dt)) # fix the finish time of 1.5h or 2.25h events # subtract 10 min break pointlessly included in original schedule duration = end - start if duration == timedelta(minutes=100) or duration == timedelta( minutes=155): end -= timedelta(minutes=10) # remove groups from summary summary = re.sub(r"\w{1,}_K-ce.*(,)?", "", str(component.get("summary")).strip()) # split summary into name and teacher split_summary = summary.strip().split(" ") teacher: Optional[str] if len(split_summary) > 1: teacher = split_summary.pop().strip() if teacher.startswith("_K-ce"): teacher = split_summary.pop().strip() # set teacher to none if not specified teacher = None if teacher == "brak nauczyciela" else teacher name = (" ".join(split_summary)).strip() else: teacher = None name = split_summary[0] name = name.replace("brak nauczyciela", "").replace("brak lokalizacji brak sali", "") # split out event type from name split_name = name.strip().split(" - ") type: Optional[str] if len(split_name) > 1: name = split_name[0].strip() type = split_name[1].strip() else: type = None return cls(name, type, teacher, location, start, end)