def show_talk(seminar_id, talkid): token = request.args.get("token", "") # save the token so user can toggle between view and edit talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid}, prequery={}) if talk is None: return abort(404, "Talk not found") if not talk.visible(): # There may be a non-API version of the seminar that can be shown talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid}) if talk is None or not talk.visible(): flash_error("You do not have permission to view %s/%s", seminar_id, talkid) return redirect(url_for("semseries_index")) kwds = dict( title="View talk", talk=talk, seminar=talk.seminar, subsection="viewtalk", token=token ) if token: kwds["section"] = "Manage" # Also want to override top menu from seminars.utils import top_menu menu = top_menu() menu[1] = (url_for("create.index"), "", "Manage") kwds["top_menu"] = menu elif ( current_user.is_subject_admin(talk) or current_user.email_confirmed and ( current_user.email in talk.seminar.editors() or current_user.email == talk.speaker_email ) ): kwds["section"] = "Manage" return render_template("talk.html", **kwds)
def register_for_talk(seminar_id, talkid): from flask import flash talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid}) if talk is None: return abort(404, "Talk not found") # If registration isn't required just send them to the talk page # where the user will see an appropriate livestream link if talk.access_control != 4: return redirect( url_for('show_talk', seminar_id=seminar_id, talkid=talkid)) if current_user.is_anonymous or len(current_user.name) < 2: return redirect( url_for("user.info", next=url_for("register_for_talk", seminar_id=seminar_id, talkid=talkid))) if not current_user.email_confirmed: flash_error("You need to confirm your email before you can register.") return redirect( url_for('show_talk', seminar_id=seminar_id, talkid=talkid)) if not talk.live_link: return abort(404, "Livestream link for talk not found") if talk.register_user(): flash("You have been registered; enjoy the talk!") else: flash("Previous registration confirmed; enjoy the talk!") if talk.is_starting_soon(): return redirect(talk.live_link) else: return redirect( url_for('show_talk', seminar_id=seminar_id, talkid=talkid))
def ics_talk_file(seminar_id, talkid): talk = talks_lucky({"seminar_id": seminar_id, "seminar_ctr": talkid}) if talk is None: return abort(404, "Talk not found") return ics_file([talk], filename="{}_{}.ics".format(seminar_id, talkid), user=current_user)
def show_talk(semid, talkid): token = request.args.get( "token", "") # save the token so user can toggle between view and edit talk = talks_lucky({"seminar_id": semid, "seminar_ctr": talkid}) if talk is None: return not_found_404("Talk not found") kwds = dict(title="View talk", talk=talk, seminar=talk.seminar, subsection="viewtalk", token=token) if token: kwds["section"] = "Manage" # Also want to override top menu from seminars.utils import top_menu menu = top_menu() menu[2] = (url_for("create.index"), "", "Manage") kwds["top_menu"] = menu elif (current_user.is_subject_admin(talk) or current_user.email_confirmed and (current_user.email in talk.seminar.editors() or current_user.email == talk.speaker_email)): kwds["section"] = "Manage" return render_template("talk.html", **kwds)
def show_talk(semid, talkid): talk = talks_lucky({"seminar_id": semid, "seminar_ctr": talkid}) if talk is None: return render_template("404.html", title="Talk not found") utcoffset = int(talk.start_time.utcoffset().total_seconds() / 60) return render_template( "talk.html", title="View talk", talk=talk, utcoffset=utcoffset, top_menu=basic_top_menu(), )
def next_talk(shortname): """ Gets the next talk time in a single seminar. Note that if you need this information for many seminars, the `next_talks` function will be faster. """ from seminars.talk import talks_lucky return talks_lucky( { "seminar_id": shortname, "start_time": { "$gte": datetime.now(pytz.UTC) } }, projection="start_time", sort=["start_time"])
def layout_schedule(seminar, data): """ Returns a list of schedule slots in specified date range (date, daytime-interval, talk) where talk is a WebTalk or none. Picks default dates if none specified """ tz = seminar.tz def parse_date(key): date = data.get(key) if date: try: return process_user_input(date, "date", "date", tz) except ValueError: flash_warning( "Invalid date %s ignored, please use a format like mmm dd, yyyy or dd-mmm-yyyy or mm/dd/yyyy", date) def slot_start_time(s): # put slots with no time specified at the end of the day return date_and_daytimes_to_times(parse_time(s[0]), s[1] if s[1] else "23:59-23:59", tz)[0] begin = parse_date("begin") end = parse_date("end") shortname = seminar.shortname now = datetime.now(tz=tz) today = now.date() day = timedelta(days=1) if seminar.is_conference and (seminar.start_date is None or seminar.end_date is None): flash_warning( "You have not specified the start and end dates of your conference (we chose a date range to layout your schedule)." ) begin = seminar.start_date if begin is None and seminar.is_conference else begin begin = today if begin is None else begin end = seminar.end_date if end is None and seminar.is_conference else end if end is None: if seminar.is_conference: if seminar.per_day: end = begin + day * ceil(SCHEDULE_LEN / seminar.per_day) else: end = begin + 7 * day else: if seminar.frequency: end = begin + day * ceil( SCHEDULE_LEN * seminar.frequency / len(seminar.time_slots)) else: end = begin + 14 * day if end < begin: end = begin data["begin"] = seminar.show_input_date(begin) data["end"] = seminar.show_input_date(end) midnight_begin = midnight(begin, tz) midnight_end = midnight(end, tz) query = {"$gte": midnight_begin, "$lt": midnight_end + day} talks = list( talks_search({ "seminar_id": shortname, "start_time": query }, sort=["start_time"])) slots = [(t.show_date(tz), t.show_daytimes(tz), t) for t in talks] if seminar.is_conference: newslots = [] d = midnight_begin while d < midnight_end + day: newslots += [(seminar.show_schedule_date(d), "", None) for i in range(seminar.per_day)] d += day for t in slots: if (t[0], "", None) in newslots: newslots.remove((t[0], "", None)) slots = sorted(slots + newslots, key=lambda t: slot_start_time(t)) return slots if not seminar.frequency: for i in range(max(SCHEDULE_LEN - len(slots), 3)): slots.append(("", "", None)) else: # figure out week to start in. # use the week of the first seminar after begin if any, otherwise last seminar before begin, if any # otherwise just use the week containing begin t = talks_lucky( { "seminar_id": shortname, "start_time": { "$gte": midnight_begin } }, sort=[("start_time", 1)]) if not t: t = talks_lucky( { "seminar_id": shortname, "start_time": { "$lt": midnight_begin } }, sort=[("start_time", -1)]) if t: t = adapt_datetime(t.start_time, newtz=tz) w = t - t.weekday() * day while w > midnight_begin: w -= day * seminar.frequency while w + day * seminar.frequency < midnight_begin: w += day * seminar.frequency else: w = midnight_begin - midnight_begin.weekday() * day # make a list of all seminar time slots in [begin,end) newslots = [] while w < midnight_end: for i in range(len(seminar.weekdays)): d = w + day * seminar.weekdays[i] if d >= midnight_begin and d < midnight_end + day: newslots.append((seminar.show_schedule_date(d), seminar.time_slots[i], None)) w = w + day * seminar.frequency # remove slots that are (exactly) matched by an existing talk # this should handle slots that occur with multiplicity for t in slots: if (t[0], t[1], None) in newslots: newslots.remove((t[0], t[1], None)) slots = sorted(slots + newslots, key=lambda t: slot_start_time(t)) return slots
def import_talks(csv_file): talks = [] ctr = {} with open(csv_file) as F: for i, line in enumerate(reader(F)): if i == 0: assert line == [ "Timestamp", "Title", "Speaker", "Speaker_inst", "Abstract", "Host", "Seminar", "Site", "In_Charge", "arXiv", "Date", "Start_Time", "End_Time", "Timezone", "Approved", ] continue ( timestamp, title, speaker, speaker_affiliation, abstract, host, seminar_id, site, in_charge, arXiv, date, start_time, end_time, timezone, approved, ) = line # Make sure seminar exists seminar = seminars_lookup(seminar_id) if not seminar: continue if seminar is None: print("Warning: seminar %s does not exist" % seminar_id) continue if seminar_id not in ctr: m = talks_max("seminar_ctr", {"seminar_id": seminar_id}) if m is None: m = -1 ctr[seminar_id] = m + 1 # This time zone info is specific to importing in early April # There is some broken data, where time zones were incrementing the minute. We reset them all to zero. tzdict = { -7: "America/Los_Angeles", -4: "America/New_York", -5: "America/Chicago", -3: "America/Buenos_Aires", 2: "Europe/Paris", 1: "Europe/London", } timezone = tzdict[int(timezone[4:7])] tz = pytz.timezone(timezone) date = parse(date, dayfirst=True).date() start_time = tz.localize( datetime.datetime.combine(date, parse(start_time).time())) end_time = tz.localize( datetime.datetime.combine(date, parse(end_time).time())) # Check to see if a talk at this time in the seminar already exists curtalk = talks_lucky({ "seminar_id": seminar_id, "speaker": speaker }) if curtalk is not None: print( "Talk with speaker %s already exists in seminar %s; continuing" % (speaker, seminar_id)) continue curtalk = talks_lucky({ "seminar_id": seminar_id, "start_time": start_time }) if curtalk is not None: print( "Talk at time %s (speaker %s) already exists in seminar %s; continuing" % (start_time.strftime("%a %b %d %-H:%M"), speaker, seminar_id)) continue topics = (arXiv.replace(" ", "").replace("Math.", "").replace( "math.", "").lower().split(",")) if not topics: topics = [] talks.append( dict( title=title, speaker=speaker, speaker_affiliation=speaker_affiliation, abstract=abstract, topics=topics, timezone=timezone, start_time=start_time, end_time=end_time, display=True, token="%016x" % random.randrange(16**16), online=True, live_link=seminar.live_link, room=seminar.room, access=seminar.access, comments=seminar.comments, seminar_id=seminar_id, seminar_ctr=ctr[seminar_id], )) ctr[seminar_id] += 1 return talks
def cleanse(self): """ This function is used to ensure backward compatibility across changes to the schema and/or validation This is the only place where columns we plan to drop should be referenced """ from seminars.talk import talks_lucky if self.is_conference: self.frequency = None if not self.per_day: self.per_day = 4 if self.frequency is None: self.weekdays = [] self.time_slots = [] else: if self.frequency > 1 and self.frequency <= 7: self.frequency = 7 elif self.frequency > 7 and self.frequency <= 14: self.frequency = 14 elif self.frequency > 14 and self.frequency <= 21: self.frequency = 21 else: self.frequency = None self.weekdays = [] self.time_slots = [] if self.frequency and (not self.weekdays or not self.time_slots): self.weekdays = [] self.time_slots = [] if self.weekday is not None and self.start_time is not None and self.end_time is not None: self.weekdays = [self.weekday] self.time_slots = [self.start_time.strftime("%H:%M") + "-" + self.end_time.strftime("%H:%M")] else: now = datetime.now(tz=self.tz) t = talks_lucky( {"seminar_id": self.shortname, "start_time": {"$gte": now}}, projection=["start_time", "end_time"], sort=[("start_time",1)], objects=False, ) if not t: t = talks_lucky( {"seminar_id": self.shortname, "start_time": {"$lt": now}}, projection=["start_time", "end_time"], sort=[("start_time", -1)], objects=False, ) if t: self.weekdays = [t["start_time"].weekday()] self.time_slots = [t["start_time"].strftime("%H:%M") + "-" + t["end_time"].strftime("%H:%M")] else: # Create a slot with an obviously bogus time in the hope that the user will notice and set it self.weekdays = [0] self.time_slots = ["00:00-01:00"] n = min(len(self.weekdays),len(self.time_slots)) self.weekdays = self.weekdays[0:n] self.time_slots = self.time_slots[0:n] s = self.description self.description = s[0].upper() + s[1:] if s else "" if self.online and self.access_control is None: self.access_control = 0 if self.access == 'open' else self.access_control self.access_control = 3 if self.access in ['users', 'endorsed'] else self.access_control if self.live_link and "comments" in self.live_link: self.live_link = "" if self.homepage: self.access_control = 5 self.access_registration = self.homepage if self.online and self.live_link and "comments" in self.live_link: self.live_link = "" # Port old subjects and topics to the new topic scheme if getattr(self, "subjects", []): def update_topic(topic): if topic in ["math", "physics", "bio"]: return [topic] if topic in ["math_mp", "mp", "physics_math-ph"]: return ["math", "physics", "math-ph"] if topic == "math_na": return ["math", "cs", "math_NA"] if len(topic) == 2: return ["math", "math_" + topic.upper()] if topic.startswith("math_"): return ["math", "math_" + topic[5:].upper()] if topic.startswith("bio_bio_"): return ["bio", "bio_" + topic[8:].upper()] assert topic.startswith("physics_") topic = topic[8:] if topic.startswith("nlin_"): return ["physics", "nlin", topic] if topic.startswith("cond-mat_"): return ["physics", "cond-mat", topic] if topic.startswith("nucl-"): return ["physics", "nucl-ph", topic] if topic.startswith("hep-"): return ["physics", "hep", topic] if topic.startswith("astro-ph_"): return ["physics", "astro-ph", topic] return ["physics", topic] self.topics = sorted(set(sum([update_topic(topic) for topic in self.subjects + self.topics], []))) self.subjects = [] # remove columns we plan to drop for attr in ["start_time","end_time","start_times","end_times","weekday","archived"]: killattr(self, attr) if self.level is None: self.level = 0
def convert_time_to_times(self): from seminars.talk import talks_lucky if self.is_conference: self.frequency = None if self.frequency is None: self.weekdays = [] self.time_slots = [] return if self.frequency > 1 and self.frequency <= 7: self.frequency = 7 elif self.frequency > 7 and self.frequency <= 14: self.frequency = 14 elif self.frequency > 14 and self.frequency <= 21: self.frequency = 21 else: self.frequency = None self.weekdays = [] self.time_slots = [] return if not self.weekdays or not self.time_slots: self.weekdays = [] self.time_slots = [] if self.weekday is not None and self.start_time is not None and self.end_time is not None: self.weekdays = [self.weekday] self.time_slots = [ self.start_time.strftime("%H:%M") + "-" + self.end_time.strftime("%H:%M") ] else: now = datetime.now(tz=self.tz) t = talks_lucky( { "seminar_id": self.shortname, "start_time": { "$gte": now } }, projection=["start_time", "end_time"], sort=[("start_time", 1)], objects=False, ) if not t: t = talks_lucky( { "seminar_id": self.shortname, "start_time": { "$lt": now } }, projection=["start_time", "end_time"], sort=[("start_time", -1)], objects=False, ) if t: self.weekdays = [t["start_time"].weekday()] self.time_slots = [ t["start_time"].strftime("%H:%M") + "-" + t["end_time"].strftime("%H:%M") ] else: # Create a slot with an obviously bogus time in the hope that the user will notice and set it self.weekdays = [0] self.time_slots = ["00:00-01:00"] n = min(len(self.weekdays), len(self.time_slots)) self.weekdays = self.weekdays[0:n] self.time_slots = self.time_slots[0:n]
def make_date_data(seminar, data): tz = seminar.tz def parse_date(key): date = data.get(key) if date: try: return process_user_input(date, "date", tz) except ValueError: pass begin = parse_date("begin") end = parse_date("end") frequency = data.get("frequency") try: frequency = int(frequency) except Exception: frequency = None if not frequency or frequency < 0: frequency = seminar.frequency if not frequency or frequency < 0: frequency = 1 if seminar.is_conference else 7 try: weekday = short_weekdays.index(data.get("weekday", "")[:3]) except ValueError: weekday = None if weekday is None: weekday = seminar.weekday shortname = seminar.shortname day = datetime.timedelta(days=1) now = datetime.datetime.now(tz=tz) today = now.date() midnight_today = now.replace(hour=0, minute=0, second=0, microsecond=0) if begin is None or seminar.start_time is None or seminar.end_time is None: future_talk = talks_lucky( { "seminar_id": shortname, "start_time": { "$exists": True, "$gte": midnight_today } }, sort=["start_time"]) last_talk = talks_lucky( { "seminar_id": shortname, "start_time": { "$exists": True, "$lt": midnight_today } }, sort=[("start_time", -1)], ) if begin is None: if seminar.is_conference: if seminar.start_date: begin = seminar.start_date else: begin = today else: if weekday is not None and frequency == 7: begin = today # Will set to next weekday below else: # Try to figure out a plan from future and past talks if future_talk is None: if last_talk is None: # Give up begin = today else: begin = last_talk.start_time.date() while begin < today: begin += frequency * day else: begin = future_talk.start_time.date() while begin >= today: begin -= frequency * day begin += frequency * day if not seminar.is_conference and seminar.weekday is not None: # Weekly meetings: take the next one while begin.weekday() != weekday: begin += day if end is None: if seminar.is_conference: if seminar.end_date: end = seminar.end_date schedule_len = int((end - begin) / (frequency * day)) + 1 else: end = begin + 6 * day schedule_len = 7 else: end = begin + (SCHEDULE_LEN - 1) * frequency * day schedule_len = SCHEDULE_LEN else: schedule_len = abs(int((end - begin) / (frequency * day))) + 1 seminar.frequency = frequency data["begin"] = seminar.show_input_date(begin) data["end"] = seminar.show_input_date(end) midnight_begin = localize_time( datetime.datetime.combine(begin, datetime.time()), tz) midnight_end = localize_time( datetime.datetime.combine(end, datetime.time()), tz) # add a day since we want to allow talks on the final day if end < begin: # Only possible by user input frequency = -frequency query = {"$gte": midnight_end, "$lt": midnight_begin + day} else: query = {"$gte": midnight_begin, "$lt": midnight_end + day} schedule_days = [begin + i * frequency * day for i in range(schedule_len)] scheduled_talks = list( talks_search({ "seminar_id": shortname, "start_time": query })) by_date = defaultdict(list) for T in scheduled_talks: by_date[adapt_datetime(T.start_time, tz).date()].append(T) all_dates = sorted(set(schedule_days + list(by_date)), reverse=(end < begin)) # Fill in by_date with Nones up to the per_day value for date in all_dates: by_date[date].extend([None] * (seminar.per_day - len(by_date[date]))) if seminar.start_time is None: if future_talk is not None and future_talk.start_time: seminar.start_time = future_talk.start_time.time() elif last_talk is not None and last_talk.start_time: seminar.start_time = last_talk.start_time.time() if seminar.end_time is None: if future_talk is not None and future_talk.start_time: seminar.end_time = future_talk.end_time.time() elif last_talk is not None and last_talk.start_time: seminar.end_time = last_talk.end_time.time() return seminar, all_dates, by_date
def make_date_data(seminar): shortname = seminar.shortname if not seminar.frequency or seminar.frequency < 0 or not seminar.schedule_len or seminar.schedule_len < 0 or seminar.schedule_len > 400: print(seminar.frequency, seminar.schedule_len) flash_error( "You must specify a meeting frequence to use the scheduler") return redirect(url_for("show_seminar", shortname=shortname), 301), None, None, None now = datetime.datetime.now(tz=pytz.utc) today = now.date() day = datetime.timedelta(days=1) last_talk = talks_lucky( { 'seminar_id': shortname, 'start_time': { '$lte': now } }, sort=[('start_time', -1)]) future_talks = list( talks_search({ 'seminar_id': shortname, 'start_time': { '$gte': now } }, sort=['start_time'])) by_date = {T.start_time.date(): T for T in future_talks} if len(by_date) != len(future_talks): flash_error( "Cannot use scheduler when there are multiple talks on the same day" ) return redirect(url_for("show_seminar", shortname=shortname), 301), None, None, None if last_talk is None: if seminar.weekday is None: if not future_talks: flash_error( "You must specify a weekday or add a talk to the seminar") return redirect(url_for("show_seminar", shortname=shortname), 301), None, None, None seminar.weekday = future_talks[0].start_time.date().weekday() next_date = today while next_date.weekday() != seminar.weekday: next_date += day else: next_date = last_talk.start_time.date() today = now.date() while next_date < today: next_date += seminar.frequency * day all_dates = sorted( set([ next_date + i * seminar.frequency * day for i in range(seminar.schedule_len) ] + list(by_date))) if seminar.start_time is None: if future_talks: seminar.start_time = future_talks[0].start_time.time() elif last_talk is not None: seminar.start_time = last_talk.start_time.time() if seminar.end_time is None: if future_talks: seminar.end_time = future_talks[0].end_time.time() elif last_talk is not None: seminar.end_time = last_talk.end_time.time() return None, seminar, all_dates, by_date