def get_calendar(self): calendar = ic.Calendar() calendar.add('X-WR-CALNAME', '课程表') for c in self.course_list: calendar.add_component(c.get_event(self.first_week)) return calendar
def ics_feed(): log.debug('handling a query') if conf is None: log.error("No configuration available") return flask.Response(status_code=500, status='Missing configuration') q_contact = flask.request.args.get('contact') cal = icalendar.Calendar() cal.add('prodid', '-//danielpocock.com//NagiosIssueFeed//') cal.add('version', '1.0') s = mk_livestatus.Socket(conf['livestatus_sock']) q = s.services.columns('state', 'host_alias', 'description', 'plugin_output', 'last_state_change', 'last_check', 'action_url_expanded', 'acknowledged').filter('state > 0') q_contact = flask.request.args.get('contact') if q_contact is not None: log.debug("Contact: %s" % q_contact) q = q.filter('contacts >= %s' % q_contact) result = q.call() log.debug("services query got %d row(s)" % (len(result))) for row in result: try: todo = icalendar.Todo() todo['uid'] = make_uid(row) todo['summary'] = make_title(row) todo['description'] = row['plugin_output'] if row['action_url_expanded']: todo['url'] = row['action_url_expanded'] todo.add('created', parse_nagios_ts(row['last_state_change'])) todo['last-modified'] = parse_nagios_ts(row['last_check']) todo['status'] = get_todo_status(row) todo['organizer'] = make_contact(row) todo['priority'] = make_priority(row) cal.add_component(todo) except Exception: log.error("Failed to parse %r", row, exc_info=True) return flask.Response(status_code=500, status='Error parsing Nagios data') q = s.hosts.columns('state', 'alias', 'plugin_output', 'last_state_change', 'last_check', 'action_url_expanded', 'acknowledged').filter('state > 0') if q_contact is not None: log.debug("Contact: %s" % q_contact) q = q.filter('contacts >= %s' % q_contact) result = q.call() log.debug("hosts query got %d row(s)" % (len(result))) for row in result: try: todo = icalendar.Todo() todo['uid'] = make_uid(row) todo['summary'] = make_title(row) if row['action_url_expanded']: todo['url'] = row['action_url_expanded'] todo.add('created', parse_nagios_ts(row['last_state_change'])) todo['last-modified'] = parse_nagios_ts(row['last_check']) todo['status'] = get_todo_status(row) todo['organizer'] = make_contact(row) todo['priority'] = make_priority(row) cal.add_component(todo) except Exception: log.error("Failed to parse %r", row, exc_info=True) return flask.Response(status_code=500, status='Error parsing Nagios data') log.debug("done, writing response to stream") return flask.Response("%s" % display(cal), mimetype='text/calendar')
def _DisplayAppleSusPromoCalendar(self): """Display upcoming Apple SUS updates in iCal format.""" now = datetime.datetime.utcnow().date() query = models.AppleSUSProduct.all().order('-apple_mtime') dates = {} # NOTE(user): the following adds about 700ms onto the request, so we may # want to pre-calculate this in a cron in the future. for p in query: if p.manual_override: continue if not common.UNSTABLE in p.tracks: continue if common.STABLE not in p.tracks: p.stable_promote_date = applesus.GetAutoPromoteDate( common.STABLE, p) if common.TESTING not in p.tracks: p.testing_promote_date = applesus.GetAutoPromoteDate( common.TESTING, p) if hasattr(p, 'stable_promote_date') and p.stable_promote_date >= now: dates.setdefault(p.stable_promote_date, []).append(p) if hasattr( p, 'testing_promote_date') and p.testing_promote_date >= now: dates.setdefault(p.testing_promote_date, []).append(p) dtstamp = datetime.datetime.utcnow() cal = icalendar.Calendar() for d in dates: e = icalendar.Event() e.add('dtstamp', dtstamp) e.add('summary', 'Apple SUS auto-promote') e.add('dtstart', d) e.add('transp', 'TRANSPARENT') products = {common.TESTING: [], common.STABLE: []} for p in dates[d]: track = None if p.stable_promote_date == d: track = common.STABLE elif p.testing_promote_date == d: track = common.TESTING products[track].append(' %s %s (%s)' % (p.name, p.version, p.product_id)) desc = [] for track in [common.STABLE, common.TESTING]: if not products[track]: continue desc.append('Auto-promoting to %s:' % track.upper()) desc.append('\n'.join(products[track])) e.add('description', '\n\n'.join(desc)) e['uid'] = '%s-simian-applesus' % d.strftime('%Y%m%d') cal.add_component(e) self.response.headers['Content-Type'] = 'text/calendar' self.response.out.write(cal.as_string())
def ics_from_list(events, tzs, random_uid=False, default_timezone=None): """convert an iterable of icalendar.Events to an icalendar.Calendar :params events: list of events all with the same uid :type events: list(icalendar.cal.Event) :param random_uid: assign random uids to all events :type random_uid: bool :param tzs: collection of timezones :type tzs: dict(icalendar.cal.Vtimzone """ calendar = icalendar.Calendar() calendar.add('version', '2.0') calendar.add('prodid', '-//PIMUTILS.ORG//NONSGML khal / icalendar //EN') if random_uid: new_uid = generate_random_uid() needed_tz, missing_tz = set(), set() for sub_event in events: sub_event = sanitize(sub_event, default_timezone=default_timezone) if random_uid: sub_event['UID'] = new_uid # icalendar round-trip converts `TZID=a b` to `TZID="a b"` investigate, file bug XXX for prop in [ 'DTSTART', 'DTEND', 'DUE', 'EXDATE', 'RDATE', 'RECURRENCE-ID', 'DUE' ]: if isinstance(sub_event.get(prop), list): items = sub_event.get(prop) else: items = [sub_event.get(prop)] for item in items: if not (hasattr(item, 'dt') or hasattr(item, 'dts')): continue # if prop is a list, all items have the same parameters datetime_ = item.dts[0].dt if hasattr(item, 'dts') else item.dt if not hasattr(datetime_, 'tzinfo'): continue # check for datetimes' timezones which are not understood by # icalendar if datetime_.tzinfo is None and 'TZID' in item.params and \ item.params['TZID'] not in missing_tz: logger.warning( f"Cannot find timezone `{item.params['TZID']}` in .ics file, " "using default timezone. This can lead to erroneous time shifts" ) missing_tz.add(item.params['TZID']) elif datetime_.tzinfo and datetime_.tzinfo != pytz.UTC and \ datetime_.tzinfo not in needed_tz: needed_tz.add(datetime_.tzinfo) for tzid in needed_tz: if str(tzid) in tzs: calendar.add_component(tzs[str(tzid)]) else: logger.warning( f'Cannot find timezone `{tzid}` in .ics file, this could be a bug, ' 'please report this issue at http://github.com/pimutils/khal/.' ) for sub_event in events: calendar.add_component(sub_event) return calendar.to_ical().decode('utf-8')
if 'Quinton' in line: person = 'Quinton' elif 'No ' in line: my_date = line.split(':')[0] if my_date not in pack_lunches: pack_lunches[my_date] = [person] else: pack_lunches[my_date].append(person) import parsedatetime as pdt parser = pdt.Calendar() import icalendar as ical from datetime import datetime, timedelta cal = ical.Calendar() cal.add('prodid', 'foo') cal.add('version', '2.0') for day_str in pack_lunches.keys(): start = parser.parse(day_str)[0] dtstart = datetime(start[0], start[1], start[2]).date() dtend = dtstart + timedelta(days = 1) trigger = datetime(start[0], start[1], start[2], 7, 0) summary = 'Pack lunch for %s' % " & ".join(pack_lunches[day_str]) event = ical.Event() event.add('summary', summary) event.add('dtstart', dtstart) event.add('dtend', dtend)
def construct_icalendar(context, events): """Returns an icalendar.Calendar object. :param context: A content object, which is used for calendar details like Title and Description. Usually a container, collection or the event itself. :param events: The list of event objects, which are included in this calendar. """ cal = icalendar.Calendar() cal.add('prodid', PRODID) cal.add('version', VERSION) cal_tz = default_timezone(context) if cal_tz: cal.add('x-wr-timezone', cal_tz) tzmap = {} if not hasattr(events, '__getslice__'): # LazyMap doesn't have __iter__ events = [events] for event in events: if ICatalogBrain.providedBy(event) or\ IContentListingObject.providedBy(event): event = event.getObject() acc = IEventAccessor(event) tz = acc.timezone # TODO: the standard wants each recurrence to have a valid timezone # definition. sounds decent, but not realizable. if not acc.whole_day: # whole day events are exported as dates without # timezone information if isinstance(tz, tuple): tz_start, tz_end = tz else: tz_start = tz_end = tz tzmap = add_to_zones_map(tzmap, tz_start, acc.start) tzmap = add_to_zones_map(tzmap, tz_end, acc.end) cal.add_component(IICalendarEventComponent(event).to_ical()) for (tzid, transitions) in tzmap.items(): cal_tz = icalendar.Timezone() cal_tz.add('tzid', tzid) cal_tz.add('x-lic-location', tzid) for (transition, tzinfo) in transitions.items(): if tzinfo['dst']: cal_tz_sub = icalendar.TimezoneDaylight() else: cal_tz_sub = icalendar.TimezoneStandard() cal_tz_sub.add('tzname', tzinfo['name']) cal_tz_sub.add('dtstart', transition) cal_tz_sub.add('tzoffsetfrom', tzinfo['tzoffsetfrom']) cal_tz_sub.add('tzoffsetto', tzinfo['tzoffsetto']) # TODO: add rrule # tzi.add('rrule', # {'freq': 'yearly', 'bymonth': 10, 'byday': '-1su'}) cal_tz.add_component(cal_tz_sub) cal.add_component(cal_tz) return cal
def serialize_category_ical(category, user, event_filter): """Export the events in a category to iCal :param category: The category to export :param user: The user who needs to be able to access the events :param event_filter: A SQLalchemy criterion to restrict which events will be returned. Usually something involving the start/end date of the event. """ own_room_strategy = joinedload('own_room') own_room_strategy.load_only('building', 'floor', 'number', 'name') own_room_strategy.lazyload('owner') own_venue_strategy = joinedload('own_venue').load_only('name') query = (Event.query.filter(Event.category_chain_overlaps( category.id), ~Event.is_deleted, event_filter).options( load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'description', 'own_venue_name', 'own_room_name', 'protection_mode', 'access_key'), subqueryload('acl_entries'), joinedload('person_links'), own_room_strategy, own_venue_strategy).order_by(Event.start_dt)) events = [e for e in query if e.can_access(user)] cal = ical.Calendar() cal.add('version', '2.0') cal.add('prodid', '-//CERN//INDICO//EN') now = now_utc(False) for event in events: url = url_for('event.conferenceDisplay', confId=event.id, _external=True) location = ('{} ({})'.format(event.room_name, event.venue_name) if event.venue_name and event.room_name else (event.venue_name or event.room_name)) cal_event = ical.Event() cal_event.add('uid', u'indico-event-{}@cern.ch'.format(event.id)) cal_event.add('dtstamp', now) cal_event.add('dtstart', event.start_dt) cal_event.add('dtend', event.end_dt) cal_event.add('url', url) cal_event.add('summary', event.title) cal_event.add('location', location) description = [] if event.person_links: speakers = [ u'{} ({})'.format(x.full_name, x.affiliation) if x.affiliation else x.full_name for x in event.person_links ] description.append(u'Speakers: {}'.format(u', '.join(speakers))) if event.description: desc_text = unicode( event.description) or u'<p/>' # get rid of RichMarkup try: description.append( unicode(html.fromstring(desc_text).text_content())) except ParserError: # this happens e.g. if desc_text contains only a html comment pass description.append(url) cal_event.add('description', u'\n'.join(description)) cal.add_component(cal_event) return BytesIO(cal.to_ical())
def generate_calendar(filename, campus, term, subjects): ''' Generates an iCalendar (RFC 5545) file for the timetable events for the specified subjects ''' # Retrieve the dates for each of the weeks in the specified term currentYear = arrow.now().date().year termWeeks = KeyDates.get_terms(currentYear)[term-1] # Retrieve the timetable events for the specified subjects events = Timetable.get_events(campus, term, subjects) # Create a calendar to hold our generated calendar events calendar = icalendar.Calendar() calendar.add('prodid', '-//Adam Rehn//CQU Timetable Tools//EN') calendar.add('version', '2.0') # Iterate over the timetable events and generate corresponding calendar events total = 0 for event in events: # Generate a calendar event for each week for week in event['weeks']: # Determine the numeric index of the weekday for the event dayNum = WEEKDAYS.index(event['day'].capitalize()) # Parse the start time and end time for the event and determine the duration startTime = arrow.get(event['start'], 'HH:mm') endTime = arrow.get(event['end'], 'HH:mm') duration = endTime - startTime # Compute the start date/time for the event start = arrow.get(termWeeks[week-1]['start']) start = start.shift(weekday=dayNum, hours=startTime.time().hour, minutes=startTime.time().minute) # Convert the start date/time from the local campus timezone to UTC campusTimezone = Campus.get_timezone(campus) start.tzinfo = arrow.parser.TzinfoParser.parse(campusTimezone) start = start.to('UTC') # Compute the end date/time for the event end = start.shift(seconds=duration.seconds) # Create the event and add it to the calendar calEvent = icalendar.Event() calEvent.add('summary', '{} {} (Week {})'.format(event['code'].upper(), event['type'].capitalize(), week)) calEvent.add('description', '{} {}'.format(event['code'], event['name'])) calEvent.add('location', event['location']) calEvent.add('dtstart', start.datetime) calEvent.add('dtend', end.datetime) calEvent.add('dtstamp', arrow.utcnow().datetime) calendar.add_component(calEvent) # Keep track of the total number of generated events total += 1 # Write the generated calendar events to file with open(filename, 'wb') as f: f.write(calendar.to_ical()) # Return the number of generated events return total
def sendAppointment(attendees, dtstart): #Login to SMTP server s = smtplib.SMTP('smtp.office365.com', 587) s.connect("smtp.office365.com", 587) s.ehlo() s.starttls() s.ehlo() username, password = prompt_creds() try: s.login(username, password) except smtplib.SMTPAuthenticationError: print("Invalid credentials. Please try again.") quit() # Timezone to use for our dates - change as needed tz = pytz.timezone("US/Eastern") reminderHours = 1 description = "wash dishes" start = tz.localize(dtstart) cal = icalendar.Calendar() cal.add('prodid', '-//My calendar application//example.com//') cal.add('version', '2.0') cal.add('method', "REQUEST") event = icalendar.Event() for attendee in attendees: event.add('attendee', attendee) event.add('organizer', username) event.add('status', "confirmed") event.add('category', "Event") event.add('summary', "wash n wait") event.add('description', description) event.add('location', "SigEp Kitchen") event.add('dtstart', start) event.add('dtend', start + dt.timedelta(hours=1)) event.add('dtstamp', tz.localize(dt.datetime.now())) event['uid'] = getUniqueId() # Generate some unique ID event.add('priority', 5) event.add('sequence', 1) event.add('created', tz.localize(dt.datetime.now())) alarm = icalendar.Alarm() alarm.add("action", "DISPLAY") alarm.add('description', "Reminder") alarm.add("trigger", dt.timedelta(hours=-reminderHours)) # The only way to convince Outlook to do it correctly alarm.add("TRIGGER;RELATED=START", "-PT{0}H".format(reminderHours)) event.add_component(alarm) cal.add_component(event) msg = MIMEMultipart("alternative") msg["Subject"] = "Wash 'n' Wait" msg["From"] = username msg['To'] = " ".join(attendees) msg["Content-class"] = "urn:content-classes:calendarmessage" msg.attach(MIMEText(description)) filename = "invite.ics" part = MIMEBase('text', "calendar", method="REQUEST", name=filename) part.set_payload( cal.to_ical() ) encode_base64(part) part.add_header('Content-Description', filename) part.add_header("Content-class", "urn:content-classes:calendarmessage") part.add_header("Filename", filename) part.add_header("Path", filename) msg.attach(part) send = input("Are you sure you want to send invitations? (type \"yes\" to send) ") if send.lower() != "yes": quit() try: s.sendmail(msg["From"], msg["To"], msg.as_string()) # print("would send mail HERE") except smtplib.SMTPDataError: print("SMTP failed to send the invite. SMTPDataError Thrown. You cannot send a calendar invite to the account you have logged in with.") except Exception as e: print("SMTP failed to send the invite:", e) else: print("Outlook invitation successfully sent to", msg['To']) s.quit()
# -*- coding: utf-8 -*- import json from uuid import uuid1 from datetime import date, time, datetime, timedelta, timezone import icalendar TERMWEEKS = 16 with open("class.json", 'r') as f: entry = json.load(f) calendar = icalendar.Calendar() calendar['version'] = '2.0' calendar['prodid'] = '-//THU//Syllabus//CN' for klass in entry: event = icalendar.Event() kl_start = 0 kl_interval = 1 if(klass['ClassWeeks'] == '全'): kl_count = TERMWEEKS elif(klass['ClassWeeks'] == '前八'): kl_count = TERMWEEKS // 2 elif(klass['ClassWeeks'] == '后八'): kl_count = TERMWEEKS // 2 kl_start = TERMWEEKS // 2 elif(klass['ClassWeeks'] == '单'): kl_count = TERMWEEKS // 2 kl_interval = 2
def getCalendarIcsFile(path): return icalendar.Calendar.from_ical(open(path).read()) def getUrl(event): url = str(event) url = url.replace("[Event(","",1) url = url.replace(")]","",1) return url calendars_settings = settings.get_all_calendar_settings() sync_settings = settings.get_all_syncs() for sync in sync_settings: input_calendar = icalendar.Calendar() output_calendar = getCalendarCalDav(sync.target) if sync.source.connectionType == 'ics': getCalendarIcs(sync.source) elif sync.source.connectionType == 'caldav': print('input from caldav not supproted') continue elif sync.source.connectionType == 'icsfile': input_calendar = getCalendarIcsFile(sync.source.url) else : print('type not known') continue empty_input_calendar = icalendar.Calendar() for key in input_calendar.keys(): if key == 'METHOD':
def do_GET(self): self.send_response(200) # Sending an '200 OK' response self.send_header("Content-type", "text/calendar") # Setting the header self.end_headers() # Extract query param query_components = parse_qs(urlparse(self.path).query) if 'token' in query_components: token = query_components['token'][0] else: token = open('./config/todoist_token', 'r').read() if 'default_duration' in query_components: default_duration = query_components['default_duration'][0] else: default_duration = '1h' # Regex task_name_regex = re.compile(r'\[(.*)\]\(.*\)', re.IGNORECASE) # Write iCal cal = ical.Calendar() cal.add('prodid', '-//Todoist/iCal//NONSGML v1.0//EN') cal.add('version', '2.0') cal_stamp = datetime.datetime.today() # Connect to Todoist API api = TodoistAPI(token) api.sync() # Get project dict projects = {project['id']: project['name'] for project in api.projects.all()} # Get label dict labels = {label['id']: label['name'] for label in api.labels.all()} # Get all current tasks with due dates tasks = api.items.all() for task in tasks: if task['due']: # Get labels from task task_label = [labels[label] for label in task['labels']] # Remove links from task name task_name = task_name_regex.sub(r'\1', task['content']) # Calculate duration from labels duration = next((label for label in task_label if re.search(r'\dm|\dh|$', str(label)).group()), default_duration) duration_int = int(re.search('\d+', duration).group()) duration_timedelta = datetime.timedelta(minutes=duration_int) if duration.endswith('m') else datetime.timedelta(hours=duration_int) # Get date from due date date = task['due']['date'].replace('Z', '') # Debug print(f'{task_name}\t|\t{duration}\t|\t{date}') # Summarize task properties event_summary = f'{task_name} [{projects[task["project_id"]]}]' event_start = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S') if 'T' in date else datetime.datetime.strptime(date, '%Y-%m-%d') event_start_uid = re.sub(r'\D', '', str(event_start)) event_end = event_start + duration_timedelta if 'T' in date else event_start + datetime.timedelta(days=1) event = ical.Event() event.add('summary', event_summary) event.add('dtstart', event_start) event.add('dtend', event_end) event.add('dtstamp', cal_stamp) event.add('uid', f'{task["id"]}{event_start_uid}') cal.add_component(event) # Writing the HTML contents with UTF-8 print(cal.to_ical()) self.wfile.write(cal.to_ical()) return
def json_to_ical(address, obj): cal = icalendar.Calendar() cal.add("prodid", "-//grahame.dev//mxm.dk//EN") cal.add("version", "2.0") cal.add("name", "Bayswater Bins") cal.add("dtstamp", datetime.date.today()) cal.add("uid", hashlib.md5(address.encode("utf8")).hexdigest()) # figure out our binday lookup = { "MONDAY": 0, "TUESDAY": 1, "WEDNESDAY": 2, "THURSDAY": 3, "FRIDAY": 4, "SATURDAY": 5, "SUNDAY": 6, } def get_refuse_date(typ): attr = obj[typ].rsplit(" - ", 1)[-1] return datetime.datetime.strptime(attr, "%d/%m/%Y").date() # in reality for now this is general waste; baysie devs might change this though redbin_dt = get_refuse_date("NextGreenWasteCollection") binday = obj["DomesticWasteCollection"] start_dt = next_weekday(datetime.date.today(), lookup[binday]) for week in range(-12, 13): bin_dt = start_dt + datetime.timedelta(days=7 * week) if (bin_dt - redbin_dt).days % 14 == 0: thisbin = "Red" else: thisbin = "Yellow" event_dt = bin_dt + datetime.timedelta(days=-1) # NB hacky timezone conversion, 8 hours offset, timezones in ical are a pain start = datetime.datetime(event_dt.year, event_dt.month, event_dt.day, 12, 0, 0, tzinfo=pytz.utc) end = datetime.datetime(event_dt.year, event_dt.month, event_dt.day, 13, 0, 0, tzinfo=pytz.utc) event = icalendar.Event() event.add("summary", "Put the bins out: Green and {}".format(thisbin)) event.add("dtstart", start) event.add("dtend", end) event.add("dtstamp", start) event.add( "uid", hashlib.md5( (address + ":" + str(bin_dt)).encode("utf8")).hexdigest()) cal.add_component(event) return flask.Response(cal.to_ical(), mimetype="text/calendar", status=200)
def create_calendar(self): cal = icalendar.Calendar() cal["summary"] = "When do the certs expire?" cal["prodid"] = "-//Certficate Expiration//.../" cal["version"] = "1.0" return cal
def write_file(file, contents): with open(file, "w") as text_file: text_file.write(contents) def ical_line(v): if isinstance(v, icalendar.prop.vDDDTypes): return v.dt.isoformat() else: return v r = requests.get(calUrl) if r.status_code == 200: result = icalendar.Calendar().from_ical(r.text) for key, value in result.property_items(): if key == "END" and value == "VEVENT": listening = False events.append(event) if listening: event[key.lower()] = ical_line(value) if key == "BEGIN" and value == "VEVENT": listening = True event = {} write_file(output_dir + "basic.json", json.dumps(events, indent=2))
def create_ical(self, name, summary, description, location, count, st_hr, st_mn, et_hr, et_mn, day, start_date): """ Create iCal file with given values """ cal = ical.Calendar() """ Add Timezone """ timezone = ical.Timezone() timezone.add('TZID', pytz.timezone('US/Eastern')) timezone_standard = ical.TimezoneStandard() timezone_standard.add('DTSTART', dt.datetime(1601, 11, 4, 2, 0, 0)) timezone_standard.add('RRULE', { 'FREQ': 'YEARLY', 'BYDAY': '1SU', 'BYMONTH': '11' }) timezone_standard.add('TZOFFSETFROM', dt.timedelta(hours=-4)) timezone_standard.add('TZOFFSETTO', dt.timedelta(hours=-5)) timezone_daylight = ical.TimezoneDaylight() timezone_daylight.add('DTSTART', dt.datetime(1601, 3, 11, 2, 0, 0)) timezone_daylight.add('RRULE', { 'FREQ': 'YEARLY', 'BYDAY': '2SU', 'BYMONTH': '3' }) timezone_daylight.add('TZOFFSETFROM', dt.timedelta(hours=-5)) timezone_daylight.add('TZOFFSETTO', dt.timedelta(hours=-4)) timezone.add_component(timezone_standard) timezone.add_component(timezone_daylight) cal.add_component(timezone) """ Add Event""" event = ical.Event() event.add( 'DTSTART', dt.datetime(2015, 9, start_date, st_hr, st_mn, 0, tzinfo=pytz.timezone('US/Eastern'))) event.add( 'DTEND', dt.datetime(2015, 9, start_date, et_hr, et_mn, 0, tzinfo=pytz.timezone('US/Eastern'))) event.add('SUMMARY', summary) event.add('DESCRIPTION', description) event.add('LOCATION', location) event.add('RRULE', {'FREQ': 'weekly', 'COUNT': count, 'BYDAY': day}) cal.add_component(event) f = open(name, "wb") f.write(cal.to_ical()) f.close()
def compile_calendar(): # Obtain our stored sequences with open('_compile-calendar-sequences.yml', encoding='utf-8') as f: seminar_calendar_sequences = yaml.safe_load(f)['sequences'] # Iterate over all our seminar files seminar_paths = _get_seminar_paths() # Maintain the sequence field for each seminar for seminar_path_current in seminar_paths: # Get the hash and sequence from the file, to compare against our stored data with open(seminar_path_current, encoding='utf-8') as f: hash = hashlib.md5() for line in f: hash.update(line.strip().encode(encoding='utf-8')) seminar_hash_current = hash.hexdigest() with open(seminar_path_current, encoding='utf-8') as f: seminar_sequence_current = list( yaml.safe_load_all(f))[0]['sequence'] # Regardless of platform we're on, standardize the path we store (e.g., slashes) seminar_path_stored = posixpath.join( *os.path.normpath(seminar_path_current).split(os.sep)) if seminar_path_stored not in seminar_calendar_sequences: # This is a seminar that is new to our sequence tracking, # decrement the initial sequence so it will be hashed and incremented seminar_calendar_sequences[seminar_path_stored] = { 'hash': 'invalid_hash_to_force_update', 'sequence': seminar_sequence_current - 1 } seminar_hash_stored = seminar_calendar_sequences[seminar_path_stored][ 'hash'] seminar_sequence_stored = seminar_calendar_sequences[ seminar_path_stored]['sequence'] if seminar_hash_current != seminar_hash_stored: # # Change detected, we need to bump the sequence # seminar_sequence_current = max(seminar_sequence_current, seminar_sequence_stored) + 1 # # # pyyaml does not preserve comments, so we brute force modification of the seminar yml file # with open(seminar_path_current, encoding='utf-8') as f: # seminar_contents = f.read() # seminar_contents = re.sub( # 'sequence: {}'.format('(\d*)'), # 'sequence: {}'.format(seminar_sequence_current), # seminar_contents # ) # with open(seminar_path_current, 'w', encoding='utf-8') as f: # f.write(seminar_contents) # # # That changed the file, so update our hash, then store the updated sequence # with open(seminar_path_current, 'rb') as f: # seminar_hash_current = hashlib.md5(f.read()).hexdigest() # # seminar_calendar_sequences[seminar_path_stored] = { # 'hash': seminar_hash_current, # 'sequence': seminar_sequence_current # } if seminar_sequence_current > seminar_sequence_stored: seminar_calendar_sequences[seminar_path_stored] = { 'hash': seminar_hash_current, 'sequence': seminar_sequence_current } # Store our updated sequences data = {'sequences': seminar_calendar_sequences} with open('_compile-calendar-sequences.yml', 'w', encoding='utf-8') as f: yaml.dump(data, stream=f, default_flow_style=False) # Now generate the ics file from our seminars and their sequences ics = icalendar.Calendar() ics.add('PRODID', '-//DUB//DUB Calendar') ics.add('VERSION', '2.0') ics.add('CALSCALE', 'GREGORIAN') ics.add('METHOD', 'PUBLISH') ics.add('X-WR-CALNAME', 'DUB Calendar') ics.add('X-WR-TIMEZONE', 'America/Los_Angeles') ics.add('X-WR-CALDESC', 'Calendar of DUB seminars.') for seminar_path_current in seminar_paths: # Parse the seminar with open(seminar_path_current, encoding='utf-8') as f: seminar_contents = list(yaml.safe_load_all(f))[0] # Add seminar as calendar event ics_event = icalendar.Event() # Give it a UID ics_event.add('UID', seminar_contents['date'] + '@dub.washington.edu') # Give it DTSTART and DTEND timezone = pytz.timezone('America/Los_Angeles') seminar_start_time = timezone.localize( datetime.combine( datetime.strptime(seminar_contents['date'], '%Y-%m-%d').date(), datetime.strptime(seminar_contents['time'], '%I:%M %p').time())) seminar_end_time = timezone.localize( datetime.combine( datetime.strptime(seminar_contents['date'], '%Y-%m-%d').date(), datetime.strptime(seminar_contents['time_end'], '%I:%M %p').time())) ics_event.add('DTSTART', seminar_start_time) ics_event.add('DTEND', seminar_end_time) # Generate SUMMARY from applicable components if seminar_contents.get('no_seminar', False): # Flagged as not having a seminar # Title should indicate why there's no seminar seminar_summary = seminar_contents['title'] else: # We have a seminar, but its data may not yet be complete seminar_summary = 'DUB Seminar' if not seminar_contents.get('tbd_speakers', False): # We have speakers # If they all have the same affiliation, we'll collapse it, so let's check speaker_affiliations = [ speaker_current['affiliation'] if not speaker_current.get('affiliation_none', False) else 'affiliation_none' for speaker_current in seminar_contents['speakers'] ] if len(set(speaker_affiliations)) == 1: # Everybody has the same affiliation # But it's still possible that affiliation is affiliation_none if not seminar_contents['speakers'][0].get( 'affiliation_none', False): # We have a legit affiliation seminar_summary = '{} - {} ({})'.format( seminar_summary, ', '.join([ ' '.join(speaker_current['name'][1:] + [speaker_current['name'][0]]) for speaker_current in seminar_contents['speakers'] ]), seminar_contents['speakers'][0]['affiliation']) else: # Everybody has no affiliation seminar_summary = '{} - {}'.format( seminar_summary, ', '.join([ ' '.join(speaker_current['name'][1:] + [speaker_current['name'][0]]) for speaker_current in seminar_contents['speakers'] ])) else: # Distinct affiliations seminar_summary = '{} - {}'.format( seminar_summary, ', '.join([ '{}{}'.format( ' '.join(speaker_current['name'][1:] + [speaker_current['name'][0]]), '({})'.format(speaker_current['affiliation']) if not speaker_current.get( 'affiliation_none', False) else '') for speaker_current in seminar_contents['speakers'] ])) if not seminar_contents.get('tbd_title', False): # We have a title seminar_summary = '{} - "{}"'.format(seminar_summary, seminar_contents['title']) ics_event.add('SUMMARY', seminar_summary) # Add the location unless it has an override if not seminar_contents.get('no_seminar', False): if seminar_contents.get('location_override_calendar', False): ics_event.add('LOCATION', seminar_contents['location_override_calendar']) else: ics_event.add('LOCATION', seminar_contents['location']) # This description generation is still a bit sketchy, should decide what we want # # Generate description string from applicable components if not seminar_contents.get('no_seminar', False): description_string = '' if 'text_override_seminar_page' in seminar_contents: description_string = seminar_contents[ 'text_override_seminar_page'] else: if 'tbd_bio' not in seminar_contents: if 'tbd_abstract' not in seminar_contents: description_string = seminar_contents[ 'abstract'] + '\r\n' + seminar_contents['bio'] else: description_string = seminar_contents['bio'] elif 'tbd_abstract' not in seminar_contents: description_string = seminar_contents['abstract'] # This was crashing, and I couldn't easily determine why, so I've temporarily removed it # # # Parse description as markdown # class SensibleParagraphs(markdown.extensions.Extension): # def extendMarkdown(self, md, md_globals): # br_tag = markdown.inlinepatterns.SubstituteTagPattern(r'\n', None) # md.inlinePatterns.add('nl', br_tag, '_end') # # ics_event.add('DESCRIPTION', markdown.markdown(description_string, extensions=[SensibleParagraphs()])) # That's our complete event ics.add_component(ics_event) # Store the ics file output with open('calendar.ics', 'wb') as f: f.write(ics.to_ical())
def generateIcal(self): ical = ic.Calendar() for dataId, classData in enumerate(self.timetable): loopIsLunch = 2 if (classData["classTimeDuration"] > 1) & ( (classData["classTime"] <= self.lunchBreakAfter) & ((classData["classTime"] + classData["classTimeDuration"] - 1) > self.lunchBreakAfter)) else 1 for loopIdLunch in range(loopIsLunch): # Event Init event = ic.Event() event.add( 'uid', urllib.parse.quote( "mu-timetable-" + self.univName + "_" + str(self.termStart[classData["classTerm"] - 1].strftime("%Y%m%dT%H%M%S")) + "_" + str(dataId) + "_" + str(loopIdLunch)) + '@dev.386.jp') event.add('dtstamp', dt.datetime.now()) # Event Summary (Title) event.add( 'summary', '[' + classData["classNumber"] + '] ' + classData["className"]) # Event Location locationText = "Online" if classData["onlineURL"] == None: locationText = self.univName + " " + classData["classPlace"] event.add('location', locationText) # Event Description descriptionText = 'Teacher: ' + classData["classTeacher"] if classData["onlineURL"] != None: descriptionText += "\nLink for Online Conference: " + classData[ "onlineURL"] event.add('description', descriptionText) # Event Time Start ## Date week = [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ] termStartWeekday = self.termStart[classData["classTerm"] - 1].weekday() classStartWeekday = [ wid for wid, w in enumerate(week) if w == classData["classWeekday"] ][0] dateDelta = classStartWeekday - termStartWeekday if termStartWeekday > classStartWeekday: dateDelta = len( week) - termStartWeekday + classStartWeekday classDateStart = self.termStart[classData["classTerm"] - 1] + dt.timedelta( days=dateDelta) ## Time classStartTime = self.classTime[classData["classTime"] - 1]["start"] classEndTime = self.classTime[classData["classTime"] + classData["classTimeDuration"] - 2]["end"] if loopIsLunch == 2: if loopIdLunch == 0: classEndTime = self.classTime[self.lunchBreakAfter - 1]["end"] if loopIdLunch == 1: classStartTime = self.classTime[ self.lunchBreakAfter]["start"] event.add('dtstart', classDateStart + classStartTime) event.add('dtend', classDateStart + classEndTime) event.add( 'rrule', { 'freq': 'weekly', 'until': self.termStart[classData["classTerm"] + classData["classTermDuration"] - 1] - dt.timedelta(days=1) }) for skipDate in [ d for d in self.skipDates if d.weekday() == classDateStart.weekday() ]: event.add('exdate', skipDate + classStartTime) ical.add_component(event) ical.add('version', '2.0') ical.add('prodid', '-//386JP//MuSchoolTimetable//JP') return ical
import datetime import pytz import urllib2 CALENDARS = { '[CF] ': 'https://www.google.com/calendar/ical/br1o1n70iqgrrbc875vcehacjg%40group.calendar.google.com/public/basic.ics', '[TC] ': 'https://www.google.com/calendar/ical/appirio.com_bhga3musitat85mhdrng9035jg%40group.calendar.google.com/public/basic.ics', '[GCJ] ': 'https://www.google.com/calendar/ical/google.com_jqv7qt9iifsaj94cuknckrabd8%40group.calendar.google.com/public/basic.ics', '': 'https://www.google.com/calendar/ical/iqe3kkmf7vltagnjs7fd2sv4a8%40group.calendar.google.com/public/basic.ics' } merged = icalendar.Calendar() merged.add('PRODID', '-//Algospot Calendar//algospot.com//EN') merged.add('METHOD', 'PUBLISH') thresh = datetime.date.today() + datetime.timedelta(days=-30) def recent_enough(c): if 'DTSTART' not in c: return True dt_start = c['DTSTART'].dt if isinstance(dt_start, datetime.datetime): dt_start = dt_start.date() return dt_start >= thresh for prefix, url in CALENDARS.items():
def siakng_ics(): if request.method == 'GET': return render_template('index.html') try: raw = request.form.get('tabel-jadwal') raw = raw[raw.index(prefix):raw.index(suffix) + len(suffix)] raw = raw.replace('<br>', delimiter) rows = pandas.read_html(raw)[0].to_dict('records') cal = icalendar.Calendar() cal.add('prodid', 'PRODID:-//faisalhanif.id//NONSGML SIAKNG.ics//ID') cal.add('version', '2.0') tzc = icalendar.Timezone() tzc.add('tzid', 'Asia/Jakarta') tzc.add('x-lic-location', 'Asia/Jakarta') tzs = icalendar.TimezoneStandard() tzs.add('tzname', 'WIB') tzs.add('dtstart', datetime.datetime(1970, 1, 1, 0, 0, 0)) tzs.add('TZOFFSETFROM', datetime.timedelta(hours=7)) tzs.add('TZOFFSETTO', datetime.timedelta(hours=7)) tzc.add_component(tzs) cal.add_component(tzc) tz = pytz.timezone('Asia/Jakarta') for row in rows: row = list(row.values()) periode_raw = row[4] kelas_raw = row[2] jadwal_raw = row[5] ruangan_raw = row[6] periodes = periode_raw.split(delimiter) kelas = kelas_raw[:kelas_raw.index(delimiter)] jadwals = jadwal_raw.split(delimiter) ruangans = ruangan_raw.split(delimiter) assert len(periodes) == len(jadwals) == len(ruangans) for periode, jadwal, ruangan in zip(periodes, jadwals, ruangans): event = icalendar.Event() jadwal_by, jadwal = jadwal.split(', ') periode_op, periode_ed = periode.split(' - ') op, ed = [ datetime.datetime.strptime(periode_op + ' ' + jam, '%d/%m/%Y %H.%M') for jam in jadwal.split('-') ] op = onDay(op, days_i[jadwal_by]) ed = onDay(ed, days_i[jadwal_by]) true_ed = datetime.datetime.strptime( periode_ed + ' ' + '23.59.59', '%d/%m/%Y %H.%M.%S') event.add('dtstart', tz.localize(op)) event.add('dtend', tz.localize(ed)) event.add('dtstamp', tz.localize(datetime.datetime.today())) event.add('rrule', {'freq': 'weekly', 'until': true_ed}) event.add('summary', kelas) event.add('location', ruangan) status = request.form.get('status') event.add('status', status_s[status]) event_hash = hashlib.sha1( (kelas + str(op)).encode()).hexdigest() event.add('uid', uuid.uuid5(uuid.NAMESPACE_URL, event_hash)) cal.add_component(event) return Response( cal.to_ical(), mimetype='text/calendar', headers={'Content-disposition': 'attachment; filename=SIAKNG.ics'}) except: return Response( 'ERROR', mimetype='text/plain', headers={'Content-disposition': 'attachment; filename=ERROR'})
def setUp(self): self._icalendar = icalendar.Calendar() self._icalendar['prodid'] = '-//My calendar product//mxm.dk//' self._icalendar['version'] = '2.0' self._calendar = None
def __init__(self, icalfilename): """open/init icalfilename""" super(NotesToIcalConverter, self).__init__() self.cal = icalendar.Calendar() self.filedescriptor = open(icalfilename, 'wb')
def test_create_to_ical(self): cal = icalendar.Calendar() cal.add('prodid', u"-//Plone.org//NONSGML plone.app.event//EN") cal.add('version', u"2.0") cal.add('x-wr-calname', u"test create calendar") cal.add('x-wr-caldesc', u"icalendar tests") cal.add('x-wr-relcalid', u"12345") cal.add('x-wr-timezone', u"Europe/Vienna") tzc = icalendar.Timezone() tzc.add('tzid', 'Europe/Vienna') tzc.add('x-lic-location', 'Europe/Vienna') tzs = icalendar.TimezoneStandard() tzs.add('tzname', 'CET') tzs.add('dtstart', datetime.datetime(1970, 10, 25, 3, 0, 0)) tzs.add('rrule', {'freq': 'yearly', 'bymonth': 10, 'byday': '-1su'}) tzs.add('TZOFFSETFROM', datetime.timedelta(hours=2)) tzs.add('TZOFFSETTO', datetime.timedelta(hours=1)) tzd = icalendar.TimezoneDaylight() tzd.add('tzname', 'CEST') tzd.add('dtstart', datetime.datetime(1970, 3, 29, 2, 0, 0)) tzs.add('rrule', {'freq': 'yearly', 'bymonth': 3, 'byday': '-1su'}) tzd.add('TZOFFSETFROM', datetime.timedelta(hours=1)) tzd.add('TZOFFSETTO', datetime.timedelta(hours=2)) tzc.add_component(tzs) tzc.add_component(tzd) cal.add_component(tzc) event = icalendar.Event() tz = pytz.timezone("Europe/Vienna") event.add( 'dtstart', tz.localize(datetime.datetime(2012, 2, 13, 10, 00, 00))) event.add( 'dtend', tz.localize(datetime.datetime(2012, 2, 17, 18, 00, 00))) event.add( 'dtstamp', tz.localize(datetime.datetime(2010, 10, 10, 10, 10, 10))) event.add( 'created', tz.localize(datetime.datetime(2010, 10, 10, 10, 10, 10))) event.add('uid', u'123456') event.add( 'last-modified', tz.localize(datetime.datetime(2010, 10, 10, 10, 10, 10))) event.add('summary', u'artsprint 2012') # event.add('rrule', u'FREQ=YEARLY;INTERVAL=1;COUNT=10') event.add('description', u'sprinting at the artsprint') event.add('location', u'aka bild, wien') event.add('categories', u'first subject') event.add('categories', u'second subject') event.add('attendee', u'häns') event.add('attendee', u'franz') event.add('attendee', u'sepp') event.add('contact', u'Max Mustermann, 1010 Wien') event.add('url', u'http://plone.org') cal.add_component(event) test_out = b'|'.join(cal.to_ical().splitlines()) test_out = test_out.decode('utf-8') vtimezone_lines = "BEGIN:VTIMEZONE|TZID:Europe/Vienna|X-LIC-LOCATION:" "Europe/Vienna|BEGIN:STANDARD|DTSTART;VALUE=DATE-TIME:19701025T03" "0000|RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10|RRULE:FREQ=YEARLY;B" "YDAY=-1SU;BYMONTH=3|TZNAME:CET|TZOFFSETFROM:+0200|TZOFFSETTO:+01" "00|END:STANDARD|BEGIN:DAYLIGHT|DTSTART;VALUE=DATE-TIME:19700329T" "020000|TZNAME:CEST|TZOFFSETFROM:+0100|TZOFFSETTO:+0200|END:DAYLI" "GHT|END:VTIMEZONE" self.assertTrue(vtimezone_lines in test_out) test_str = "DTSTART;TZID=Europe/Vienna;VALUE=DATE-TIME:20120213T100000" self.assertTrue(test_str in test_out) self.assertTrue("ATTENDEE:sepp" in test_out) # ical standard expects DTSTAMP and CREATED in UTC self.assertTrue("DTSTAMP;VALUE=DATE-TIME:20101010T081010Z" in test_out) self.assertTrue("CREATED;VALUE=DATE-TIME:20101010T081010Z" in test_out)
def test_create_to_ical(self): cal = icalendar.Calendar() cal.add('X-SOMETIME', datetime.time(17, 20, 10)) self.assertTrue( b'X-SOMETIME;VALUE=TIME:172010' in cal.to_ical().splitlines())
def serialize_categories_ical(category_ids, user, event_filter=True, event_filter_fn=None, update_query=None): """Export the events in a category to iCal :param category_ids: Category IDs to export :param user: The user who needs to be able to access the events :param event_filter: A SQLalchemy criterion to restrict which events will be returned. Usually something involving the start/end date of the event. :param event_filter_fn: A callable that determines which events to include (after querying) :param update_query: A callable that can update the query used to retrieve the events. Must return the updated query object. """ own_room_strategy = joinedload('own_room') own_room_strategy.load_only('building', 'floor', 'number', 'name') own_room_strategy.lazyload('owner') own_venue_strategy = joinedload('own_venue').load_only('name') query = (Event.query .filter(Event.category_chain_overlaps(category_ids), ~Event.is_deleted, event_filter) .options(load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'description', 'own_venue_name', 'own_room_name', 'protection_mode', 'access_key'), subqueryload('acl_entries'), joinedload('person_links'), own_room_strategy, own_venue_strategy) .order_by(Event.start_dt)) if update_query: query = update_query(query) it = iter(query) if event_filter_fn: it = ifilter(event_filter_fn, it) events = list(it) # make sure the parent categories are in sqlalchemy's identity cache. # this avoids query spam from `protection_parent` lookups _parent_categs = (Category._get_chain_query(Category.id.in_({e.category_id for e in events})) .options(load_only('id', 'parent_id', 'protection_mode'), joinedload('acl_entries')) .all()) cal = ical.Calendar() cal.add('version', '2.0') cal.add('prodid', '-//CERN//INDICO//EN') now = now_utc(False) for event in events: if not event.can_access(user): continue url = url_for('event.conferenceDisplay', confId=event.id, _external=True) location = ('{} ({})'.format(event.room_name, event.venue_name) if event.venue_name and event.room_name else (event.venue_name or event.room_name)) cal_event = ical.Event() cal_event.add('uid', u'indico-event-{}@cern.ch'.format(event.id)) cal_event.add('dtstamp', now) cal_event.add('dtstart', event.start_dt) cal_event.add('dtend', event.end_dt) cal_event.add('url', url) cal_event.add('summary', event.title) cal_event.add('location', location) description = [] if event.person_links: speakers = [u'{} ({})'.format(x.full_name, x.affiliation) if x.affiliation else x.full_name for x in event.person_links] description.append(u'Speakers: {}'.format(u', '.join(speakers))) if event.description: desc_text = unicode(event.description) or u'<p/>' # get rid of RichMarkup try: description.append(unicode(html.fromstring(desc_text).text_content())) except ParserError: # this happens e.g. if desc_text contains only a html comment pass description.append(url) cal_event.add('description', u'\n'.join(description)) cal.add_component(cal_event) return BytesIO(cal.to_ical())
def render_schedule_ical_feed(profile_user_secret_id): profile_user = (m.User.objects(secret_id=profile_user_secret_id.upper()) .first()) if profile_user is None: logging.error("No profile user with secret id '%s'" % profile_user_secret_id) return '' exams = profile_user.get_current_term_exams() schedule_item_dict_list = profile_user.get_schedule_item_dicts( exam_objs=exams) course_ids = set([sid['course_id'] for sid in schedule_item_dict_list]) limited_course_list = (m.Course.objects(id__in=course_ids) .only('id', 'department_id', 'number', 'name')) humanized_course_id = {} for limited_course in limited_course_list: humanized_course_id[limited_course.id] = ( limited_course.department_id.upper() + ' ' + limited_course.number ) course_name_map = {c['id']: c['name'] for c in limited_course_list} cal = icalendar.Calendar() cal.add('x-wr-calname', 'uwflow.com schedule') cal.add('x-wr-caldesc', 'Schedule exported from https://uwflow.com') for schedule_item_dict in schedule_item_dict_list: event = icalendar.Event() course_id = schedule_item_dict['course_id'] summary_fmt = ('%(course_id)s - %(section_type)s %(section_num)s' ' - %(course_name)s') summary = summary_fmt % { 'course_id': humanized_course_id.get(course_id, course_id), 'section_type': schedule_item_dict['section_type'], 'section_num': schedule_item_dict['section_num'], 'course_name': course_name_map.get(course_id, ''), } event.add('summary', summary) # We need to make sure we explicitly specify a timezone in the # datetimes we emit to the ICS file, otherwise, some calendars (eg. # Calendar.app) will interpret the time as local time (instead of UTC, # which is what we store our datetime as). to_utc = pytz.utc.localize # TODO(jlfwong): DTSTAMP is actually supposed to be when the event was # created, not when it is. Not sure what to put here event.add('dtstamp', to_utc(schedule_item_dict['start_date'])) event.add('dtstart', to_utc(schedule_item_dict['start_date'])) event.add('dtend', to_utc(schedule_item_dict['end_date'])) # TODO(david): There should be a method on a ScheduleItem to return # a location string, except we have this stupid pattern of # returning dicts from functions instead of objects. event.add('location', '%s %s' % (schedule_item_dict['building'], schedule_item_dict['room'])) cal.add_component(event) response = flask.make_response(cal.to_ical()) response.headers["Content-type"] = "text/calendar" return response
"day" : get_next_weekday(now, link[0].string) }) return timetables # # # # timestamp to give to the generated ICS file scriptruntime = datetime.datetime.now() # create the new icalendar cal = icalendar.Calendar() cal.add("prodid", "-//Swimming Timetable//" + WEBSITENAME + "//") cal.add("version", "2.0") # get the list of timetables to download and parse - one for each day timetables = get_days(WEBSITEURL) # for each timetable... for timetable in timetables: # download and parse the timetable and get the session info for timetableinfo in get_schedule(timetable['url'], timetable['day']): # create an event to represent the session event = icalendar.Event() event.add("summary", timetableinfo['type']) event.add("dtstart", timetableinfo['start']) event.add("dtend", timetableinfo['end'])
def send_notify_email(body, start_time, title, attendees, event_uid, patching_duartion_in_min): """Function for send Outlook-notifications and save notification to disk""" subject = 'Linux Monthly Patching {month} | RFC {rfc_number} | {project}'.format( month=datetime.datetime.now().strftime("%B %Y"), rfc_number=rfc_number, project=title) start_time_utc=return_utc(start_time) # create calendar cal = icalendar.Calendar() cal.add('prodid', '-//My calendar application//example.com//') cal.add('version', '2.0') cal.add('method', 'REQUEST') # create event event = icalendar.Event() event.add('summary', subject) event.add('dtstart', datetime.datetime.strptime(start_time_utc, "%d-%m-%Y %H:%M")) event.add('dtend', datetime.datetime.strptime(start_time_utc, "%d-%m-%Y %H:%M")+datetime.timedelta(minutes=patching_duartion_in_min)) event.add('dtstamp', datetime.datetime.now().utcnow()) event['uid'] = event_uid event.add('TZOFFSETFROM', datetime.timedelta(hours=3)) event.add('TZOFFSETTO', datetime.timedelta(hours=3)) event.add('TZID', 'Russian Standard Time') event.add('priority', 5) event.add('organizer', settings['organizer']) event.add('status', "confirmed") event.add('category', "Event") event.add('sequence', 1) event.add('X-MICROSOFT-DISALLOW-COUNTER', "TRUE") event.add('X-MICROSOFT-CDO-BUSYSTATUS', 'FREE') for current_attendee in attendees.split(","): event.add('attendee', current_attendee) # create alarm alarm = icalendar.Alarm() alarm.add("action", "DISPLAY") alarm.add('description', "Reminder") alarm.add("TRIGGER;RELATED=START", "-PT15M") alarm.add('X-MICROSOFT-CDO-BUSYSTATUS', 'FREE') event.add_component(alarm) cal.add_component(event) filename = "invite.ics" msg = MIMEMultipart("mixed") msg["Subject"] = subject msg["From"] = settings['e_mail_from'] msg["To"] = attendees msg_for_cancel = copy.deepcopy(msg) cursor_hashes_db.execute('INSERT INTO "HASHES" (HASH,EMAILS) VALUES (?,?)', (str(event_uid), attendees)) connect_hashes_db.commit() msg_a = MIMEMultipart('alternative') msg.attach(msg_a) part_calendar = MIMEMultipart('text', "calendar", method="REQUEST", name=filename) part_calendar.set_type('text/calendar; charset=UTF-8; method=REQUEST; component = VEVENT') part_calendar.add_header('Content-Type', 'text/calendar') part_calendar.add_header('charset', 'UTF-8') part_calendar.add_header('component', 'VEVENT') part_calendar.add_header('method', 'REQUEST') part_calendar.add_header('Content-Description', filename) part_calendar.add_header('Content-ID', 'calendar_message') part_calendar.add_header("Content-class", "urn:content-classes:appointment") part_calendar.add_header("Filename", filename) part_calendar.add_header("Path", filename) part_calendar_before_encode=copy.deepcopy(part_calendar) part_calendar.set_payload(cal.to_ical()) encode_base64(part_calendar) msg_a.attach(MIMEText(body, 'html')) msg_a.attach(part_calendar) recept_list=attendees.split(",") try: s = smtplib.SMTP(settings['smtp_server']) s.sendmail(msg["From"], recept_list, msg.as_string()) s.quit() print("e-mail with '{title}' title has been sent successfully!".format(title=title)) except Exception as e: termcolor.cprint("Error during sending an-email, second try...", color="white", on_color="on_red") try: s = smtplib.SMTP(settings['smtp_server']) print(recept_list) s.sendmail(msg["From"], recept_list, msg.as_string()) s.quit() print("e-mail with '{title}' title has been sent successfully!".format(title=title)) except Exception as e: termcolor.cprint("Can not send outlook-notofocation for this {prj} project to {start_date}".format(start_date=start_time, prj=title), color="white", on_color="on_red") print("Exception: {e}".format(e=str(e))) cal.update({'method' : 'CANCEL'}) event.update({'summary' : "[CANCELLED] " + subject}) event.update({'status': "cancelled"}) msg_for_cancel.replace_header('Subject', "[CANCELLED] " + subject) msg_a_for_cancel = MIMEMultipart('alternative') msg_for_cancel.attach(msg_a_for_cancel) msg_a_for_cancel.attach(MIMEText(body.replace("please, perform this patching", "<font size=12 color='red'>DO NOT DO IT</font>"), 'html')) part_calendar_before_encode.replace_header('Content-Type', 'text/calendar; charset="UTF-8"; method="CANCEL"; component = "VEVENT"; name="invite.ics"; boundary="calendar"') part_calendar_before_encode.replace_header('method', 'CANCEL') part_calendar_before_encode.set_payload(cal.to_ical()) encode_base64(part_calendar_before_encode) msg_a_for_cancel.attach(part_calendar_before_encode) save_notification_to_disk=open("./archive/" + event_uid + ".msg", 'wb') save_notification_to_disk.write(msg_for_cancel.as_bytes()) save_notification_to_disk.close() input("Enter any symbol to proceed...")
def render(self, request): cal = icalendar.Calendar() cal.add('version', '2.0') cal.add('prodid', 'openstates') for obj in self.construct(): if not isinstance(obj, dict): continue if obj.get('_type') != 'event': # We can only serialize events continue event = icalendar.Event() if obj.get('all_day', False): event.add('dtstart', obj['when'].date()) event['X-FUNAMBOL-ALLDAY'] = 1 event['X-MICROSOFT-CDO-ALLDAYEVENT'] = 1 else: event['dtstart'] = _vDatetime(obj['when']) end = obj.get('end') if not end: end = obj['when'] + datetime.timedelta(hours=1) event['dtend'] = _vDatetime(end) if obj['type'] == 'committee:meeting': part = obj['participants'][0] comm = part['participant'] chamber = part.get('chamber') if chamber: comm = "%s %s" % (chamber_name(obj['state'], chamber), comm) summary = "%s Committee Meeting" % comm elif obj['type'] == 'bill:action': summary = obj['description'] else: continue event.add('summary', summary) event.add('location', obj.get('location', 'Unknown')) event['uid'] = obj['_id'] status = obj.get('status') if status: event.add('status', status.upper()) notes = obj.get('notes') if notes: event.add('description', notes) link = obj.get('link') if link: event.add('attach', link) for participant in obj['participants']: addr = icalendar.vCalAddress('MAILTO:[email protected]') chamber = participant.get('chamber') if chamber: cn = chamber_name(obj['state'], chamber) + " " else: cn = "" cn += participant['participant'] if participant['type'] == 'committee': cn += ' Committee' addr.params['cn'] = icalendar.vText(cn) #addr.params['ROLE'] = icalendar.vText('COMMITTEE') event.add('attendee', addr, encode=0) event['organizer'] = addr cal.add_component(event) return cal.as_string()
def send_attendee_email(start, finish, name, booking_type, notes, uid, mail_address): cal = icalendar.Calendar() cal.add('prodid', '-//pyappointment//pyappointment//') cal.add('version', '2.0') cal.add('method', "REQUEST") tz = start.tzinfo mail_recip = '%s <%s>' % (name, mail_address) event = icalendar.Event() event.add('attendee', 'MAILTO:%s' % mail_address) event.add('organizer', 'MAILTO:%s' % ORGANIZER_EMAIL) event.add('status', "confirmed") event.add('category', "Event") event.add('summary', BOOKING_TYPES[booking_type]['description']) event.add('description', 'Additional notes: ' + notes) event.add('location', BOOKING_TYPES[booking_type]['location']) event.add('dtstart', start) event.add('dtend', finish) event.add('dtstamp', tz.localize(dt.datetime.now())) event.add('created', tz.localize(dt.datetime.now())) event.add('priority', 5) event.add('sequence', 1) event['uid'] = uid # Generate some unique ID cal.add_component(event) template_params = { 'date': start, 'booking_info': BOOKING_TYPES[booking_type], 'recipient': name, 'organizer': ORGANIZER_GREETING } email_msg = EmailMultiAlternatives( 'Booking confirmation', render_to_string('emails/to-attendee.txt', template_params), EMAIL_ADDRESS, [mail_recip], headers={'Content-class': "urn:content-classes:calendarmessage"}, ) email_msg.attach_alternative( render_to_string('emails/to-attendee.html', template_params), "text/html") filename = "invite.ics" part = email.mime.base.MIMEBase('text', "calendar", method="REQUEST", name=filename) part.set_payload(cal.to_ical()) email.encoders.encode_base64(part) part.add_header('Content-Description', filename) part.add_header("Content-class", "urn:content-classes:calendarmessage") part.add_header("Filename", filename) part.add_header("Path", filename) email_msg.attach(part) email_msg.send()