def show_seminar(shortname): seminar = seminars_lucky({"shortname": shortname}) if seminar is None: return render_template("404.html", title="Seminar not found") organizers = list(db.seminar_organizers.search({"seminar_id": shortname})) talks = talks_search({"display": True, "seminar_id": shortname}, projection=3) now = get_now() future = [] past = [] for talk in talks: if talk.end_time >= now: future.append(talk) else: past.append(talk) future.sort(key=lambda talk: talk.start_time) past.sort(key=lambda talk: talk.start_time, reverse=True) return render_template( "seminar.html", title="View seminar", future=future, past=past, seminar=seminar, top_menu=basic_top_menu(), bread=None, )
def talks_search_api(shortname, projection=1): query = { "seminar_id": shortname, "seminar_ctr": { "$gt": 0 }, "display": True, "hidden": { "$or": [False, { "$exists": False }] } } reverse_sort = False if 'daterange' in request.args: if request.args.get('daterange') == 'past': query["start_time"] = {'$lte': get_now()} elif request.args.get('daterange') == 'future': query["end_time"] = {'$gte': get_now()} else: parse_daterange(request.args, query, time=True) elif 'past' in request.args and 'future' in request.args: # no restriction on date pass elif 'past' in request.args: query["start_time"] = {'$lte': get_now()} reverse_sort = True elif 'future' in request.args: query["end_time"] = {'$gte': get_now()} talks = list(talks_search(query, projection=3)) talks.sort(key=lambda talk: talk.start_time, reverse=reverse_sort) return talks
def talks(self, projection=1): from seminars.talk import talks_search # avoid import loop query = {"seminar_id": self.shortname, "display": True} if self.user_can_edit(): query.pop("display") return talks_search(query, projection=projection)
def dosearch(limit=limit, limitbuffer=limitbuffer): if limit and not getcounters: # we fetch extra talks to account for filtering talks = talks_search(query, sort=sort, seminar_dict=all_seminars(), more=more, limit=limit + limitbuffer) else: talks = talks_search(query, sort=sort, seminar_dict=all_seminars(), more=more) # Filtering on display and hidden isn't sufficient since the seminar could be private talks = [talk for talk in talks if talk.searchable()] return talks
def lookup_series(version=0, user=None): if version != 0: raise version_error(version) if request.method == "POST": raw_data = get_request_json() else: raw_data = get_request_args_json() series_id = _get_col("series_id", raw_data, "looking up a series") try: if user is None: sanitized = True else: sem = seminars_lookup(series_id) sanitized = not sem.user_can_edit(user=user) result = seminars_lookup(series_id, objects=False, sanitized=sanitized) except Exception as err: raise APIError({ "code": "lookup_error", "description": "an error occurred looking up the series", "error": str(err) }) talks = list( talks_search({"seminar_id": series_id}, sort=["start_time"], sanitized=sanitized, objects=False)) # tz = pytz.timezone(raw_data.get("timezone", result.get("timezone", "UTC"))) # TODO: adapt the times, support daterange, sort ans = {"code": "success", "properties": result, "talks": talks} callback = raw_data.get("callback", False) return str_jsonify(ans, callback)
def search_talks(): info = to_dict( request.args, search_array=TalkSearchArray() ) if "search_type" not in info: info["talk_online"] = True info["daterange"] = info.get("daterange", datetime.now(current_user.tz).strftime("%B %d, %Y -") ) try: talk_count = int(info["talk_count"]) talk_start = int(info["talk_start"]) if talk_start < 0: talk_start += (1 - (talk_start + 1) // talk_count) * talk_count except (KeyError, ValueError): talk_count = info["talk_count"] = 50 talk_start = info["talk_start"] = 0 talk_query = {} talks_parser(info, talk_query) talks = talks_search( talk_query, sort=["start_time", "speaker"], seminar_dict=all_seminars() ) # limit=talk_count, offset=talk_start # The talk query isn't sufficient since we don't do a join so don't have access to whether the seminar is private talks = [talk for talk in talks if talk.searchable()] info["results"] = talks return render_template( "search_talks.html", title="Search talks", info=info, section="Search", subsection="talks", bread=None, )
def search_talks(version=0): if version != 0: raise version_error(version) if request.method == "POST": raw_data = get_request_json() query = raw_data.pop("query", {}) projection = raw_data.pop("projection", 1) # FIXME # tz = raw_data.pop("timezone", "UTC") else: query = get_request_args_json() projection = 1 raw_data = {} query["hidden"] = False visible_series = set(seminars_search({"visibility": 2}, "shortname")) # TODO: Need to check visibility on the seminar try: results = talks_search(query, projection, objects=False, **raw_data) except Exception as err: raise APIError({ "code": "search_error", "description": "error in executing search", "error": str(err) }) results = [rec for rec in results if rec["seminar_id"] in visible_series] ans = {"code": "success", "results": results} callback = raw_data.get("callback", False) return str_jsonify(ans, callback)
def talks(self, projection=1): from seminars.talk import talks_search # avoid import loop query = {"seminar_id": self.shortname, "display": True, "hidden": {"$or": [False, {"$exists": False}]}} if self.user_can_edit(): query.pop("display") return talks_search(query, projection=projection)
def talks(self): if not self.talks_query: # searching on the empty gives us everything return [] query = { '$or': self.talks_query, 'hidden': { "$or": [False, { "$exists": False }] } } res = [ t for t in talks_search( query, sort=["start_time"], seminar_dict=all_seminars()) if t.searchable() or t.user_can_edit(user=self) ] talk_subscriptions = defaultdict(list) for t in res: bisect.insort(talk_subscriptions[t.seminar_id], t.seminar_ctr) if talk_subscriptions != self.talk_subscriptions: self._data["talk_subscriptions"] = talk_subscriptions self.save() return res
def index(): # Eventually want some kind of cutoff on which talks are included. # Deal with time zone right talks = talks_search( {"display": True, "end_time": {"$gte": datetime.datetime.now()}}, sort=["start_time"], ) menu = basic_top_menu() menu[0] = ("#", "$('#filter-menu').slideToggle(400); return false;", "Filter") return render_template( "browse.html", title="Math Seminars", info={}, talks=talks, top_menu=menu, bread=None, )
def ics_talks(self): query = self.talks_query[:] for shortname in self.seminar_subscriptions: query.append({'seminar_id': shortname}) query = {'$or': query, 'hidden': {"$or": [False, {"$exists": False}]}} query_st = {} now = datetime.now(tz=UTC) if self.ics_limit_past: query_st['$gte'] = now - timedelta(days=14) if self.ics_limit_future: query_st['$lte'] = now + timedelta(days=31) if query_st: query['start_time'] = query_st res = [t for t in talks_search(query, seminar_dict=all_seminars()) if t.searchable() or t.user_can_edit(user=self)] res.sort(key=lambda elt: now - elt.start_time if now > elt.start_time else elt.start_time - now) return res
def _talks_index(query={}, sort=None, subsection=None, past=False): # Eventually want some kind of cutoff on which talks are included. query = dict(query) subs = subject_pairs() hide_filters = [] if "subjects" in query: subject = query["subjects"]["$contains"] hide_filters = ["subject"] subs = ((subject, subject.capitalize()), ) elif "topics" in query: hide_filters = ["subject", "topic"] elif topdomain() == "mathseminars.org": query["subjects"] = ["math"] query["display"] = True query["hidden"] = {"$or": [False, {"$exists": False}]} if past: query["end_time"] = {"$lt": datetime.now()} if sort is None: sort = [("start_time", -1), "seminar_id"] else: query["end_time"] = {"$gte": datetime.now()} if sort is None: sort = ["start_time", "seminar_id"] talks = list(talks_search(query, sort=sort, seminar_dict=all_seminars())) # Filtering on display and hidden isn't sufficient since the seminar could be private talks = [talk for talk in talks if talk.searchable()] counters = _get_counters(talks) row_attributes = _get_row_attributes(talks) return render_template("browse_talks.html", title="Browse talks", hide_filters=hide_filters, subjects=subs, section="Browse", subsection=subsection, talk_row_attributes=zip(talks, row_attributes), past=past, **counters)
def _talks_index(query={}, sort=None, subsection=None, past=False): # Eventually want some kind of cutoff on which talks are included. search_array = TalkSearchArray(past=past) info = to_dict(read_search_cookie(search_array), search_array=search_array) info.update(request.args) query = dict(query) parse_substring(info, query, "keywords", ["title", "abstract", "speaker", "speaker_affiliation", "seminar_id", "comments", "speaker_homepage", "paper_link"]) more = {} # we will be selecting talks satsifying the query and recording whether they satisfy the "more" query # Note that talks_parser ignores the "time" field at the moment; see below for workaround talks_parser(info, more) if topdomain() == "mathseminars.org": query["topics"] = {"$contains": "math"} query["hidden"] = {"$or": [False, {"$exists": False}]} if past: query["end_time"] = {"$lt": datetime.now()} if sort is None: sort = [("start_time", -1), "seminar_id"] else: query["end_time"] = {"$gte": datetime.now()} if sort is None: sort = ["start_time", "seminar_id"] talks = list(talks_search(query, sort=sort, seminar_dict=all_seminars(), more=more)) # Filtering on display and hidden isn't sufficient since the seminar could be private talks = [talk for talk in talks if talk.searchable()] # While we may be able to write a query specifying inequalities on the timestamp in the user's timezone, it's not easily supported by talks_search. So we filter afterward timerange = info.get("timerange", "").strip() if timerange: tz = current_user.tz try: timerange = process_user_input(timerange, col="search", typ="daytimes") except ValueError: try: onetime = process_user_input(timerange, col="search", typ="daytime") except ValueError: flash_error("Invalid time range input: %s", timerange) else: for talk in talks: if talk.more: talkstart = adapt_datetime(talk.start_time, tz) t = date_and_daytime_to_time(talkstart.date(), onetime, tz) talk.more = (t == talkstart) else: for talk in talks: if talk.more: talkstart = adapt_datetime(talk.start_time, tz) talkend = adapt_datetime(talk.end_time, tz) t0, t1 = date_and_daytimes_to_times(talkstart.date(), timerange, tz) talk.more = (t0 <= talkstart) and (talkend <= t1) counters = _get_counters(talks) row_attributes = _get_row_attributes(talks) response = make_response(render_template( "browse_talks.html", title="Browse talks", section="Browse", info=info, subsection=subsection, talk_row_attributes=zip(talks, row_attributes), past=past, **counters )) if request.cookies.get("topics", ""): # TODO: when we move cookie data to server with ajax calls, this will need to get updated again # For now we set the max_age to 30 years response.set_cookie("topics_dict", topic_dag.port_cookie(), max_age=60*60*24*365*30) response.set_cookie("topics", "", max_age=0) return response
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 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