def delete_talk(semid, semctr): try: talk = WebTalk(semid, semctr) except ValueError as err: flash_error(str(err)) return redirect(url_for(".edit_seminar_schedule", shortname=semid), 302) def failure(): return render_template( "edit_talk.html", talk=talk, seminar=talk.seminar, title="Edit talk", section="Manage", subsection="edittalk", institutions=institutions(), timezones=timezones, ) if not talk.user_can_delete(): flash_error("Only the organizers of a seminar can delete talks in it") return failure() else: if talk.delete(): flash("Talk deleted") return redirect( url_for(".edit_seminar_schedule", shortname=talk.seminar_id), 302) else: flash_error( "Only the organizers of a seminar can delete talks in it") return failure()
def save_talk(): raw_data = request.form resp, seminar, talk = can_edit_talk(raw_data.get("seminar_id", ""), raw_data.get("seminar_ctr", ""), raw_data.get("token", "")) if resp is not None: return resp data = { 'seminar_id': talk.seminar_id, 'token': talk.token, 'display': talk.display, # could be being edited by anonymous user } if talk.new: curmax = talks_max('seminar_ctr', {'seminar_id': talk.seminar_id}) if curmax is None: curmax = 0 data['seminar_ctr'] = curmax + 1 else: data['seminar_ctr'] = talk.seminar_ctr for col in db.talks.search_cols: if col in data: continue try: val = raw_data.get(col) if not val: data[col] = None else: data[col] = process_user_input(val, db.talks.col_type[col]) if col == 'speaker_homepage' and val and not val.startswith( "http"): data[col] = "http://" + data[col] if col == "access" and val not in ["open", "users", "endorsed"]: raise ValueError("Invalid access type") except Exception as err: flash_error("Error processing %s: %s", [col, err]) talk = WebTalk(talk.seminar_id, talk.seminar_ctr, data=raw_data) title = "Create talk" if talk.new else "Edit talk" return render_template("edit_talk.html", talk=talk, seminar=seminar, title=title, top_menu=basic_top_menu(), categories=categories(), institutions=institutions(), timezones=timezones) new_version = WebTalk(talk.seminar_id, data['seminar_ctr'], data=data) if new_version == talk: flash("No changes made to talk.") else: new_version.save() edittype = "created" if talk.new else "edited" flash("Talk successfully %s!" % edittype) return redirect( url_for("show_talk", semid=new_version.seminar_id, talkid=new_version.seminar_ctr), 301)
def deleted_talk(semid, semctr): try: talk = WebTalk(semid, semctr, deleted=True) except ValueError as err: flash_error(str(err)) return redirect(url_for(".edit_seminar_schedule", shortname=semid), 302) return render_template("deleted_talk.html", talk=talk, title="Deleted")
def save_talk(version=0, user=None): if version != 0: raise APIError({"code": "invalid_version", "description": "Unknown API version: %s" % version}) raw_data = get_request_json() # Temporary measure while we rename seminar_id series_id = raw_data.pop("series_id", None) raw_data["seminar_id"] = series_id if series_id is None: raise APIError({"code": "unspecified_series_id", "description": "You must specify series_id when saving a talk"}) series = seminars_lookup(series_id) if series is None: raise APIError({"code": "no_series", "description": "The series %s does not exist (or is deleted)" % series_id}) else: # Make sure user has permission to edit if not series.user_can_edit(user): raise APIError({"code": "unauthorized_user", "description": "You do not have permission to edit %s." % series_id}, 401) # Temporary measure while we rename seminar_ctr series_ctr = raw_data.pop("series_ctr", None) raw_data["seminar_ctr"] = series_ctr if series_ctr is None: # Creating new talk talk = WebTalk(series_id, seminar=series, editing=True) else: talk = talks_lookup(series_id, series_ctr) if talk is None: raise APIError({"code": "no_talk", "description": "The talk %s/%s does not exist (or is deleted)" % (series_id, series_ctr)}) warnings = [] def warn(msg, *args): warnings.append(msg % args) new_version, errmsgs = process_save_talk(talk, raw_data, warn, format_error, format_input_error, incremental_update=True) # doesn't currently use the user if new_version is None: raise APIError({"code": "processing_error", "description": "Error in processing input", "errors": errmsgs}) if talk.new or new_version != talk: # Talks saved by the API are not displayed until user approves new_version.display = False new_version.by_api = True new_version.save(user) else: raise APIError({"code": "no_changes", "description": "No changes detected"}) edittype = "created" if talk.new else "edited" if warnings: response = jsonify({"code": "warning", "series_ctr": new_version.seminar_ctr, # FIXME seminar_ctr -> series_ctr "description": "series successfully %s, but..." % edittype, "warnings": warnings}) else: response = jsonify({"code": "success", "series_ctr": new_version.seminar_ctr, # FIXME seminar_ctr -> series_ctr "description": "series successfully %s" % edittype}) return response
def make_error(talk, col=None, err=None): if err is not None: flash_error("Error processing %s: {0}".format(err), col) talk = WebTalk(talk.seminar_id, talk.seminar_ctr, data=raw_data) title = "Create talk error" if talk.new else "Edit talk error" return render_template( "edit_talk.html", talk=talk, seminar=talk.seminar, title=title, section="Manage", subsection="edittalk", institutions=institutions(), timezones=timezones, )
def talks(self): res = [] for shortname, ctrs in self.talk_subscriptions.items(): for ctr in ctrs: try: res.append(WebTalk(shortname, ctr)) except ValueError: self._data["talk_subscriptions"][shortname].remove(ctr) self._dirty = True if self._dirty: for shortname in self._data["talk_subscriptions"]: if not self._data["talk_subscriptions"]: self._data["talk_subscriptions"].pop("shortname") self.save() res.sort(key=lambda elt: elt.start_time) return res
def save_seminar_schedule(): raw_data = request.form shortname = raw_data["shortname"] resp, seminar = can_edit_seminar(shortname, new=False) if resp is not None: return resp frequency = raw_data.get("frequency") try: frequency = int(frequency) except Exception: pass slots = int(raw_data["slots"]) curmax = talks_max("seminar_ctr", {"seminar_id": shortname}) if curmax is None: curmax = 0 ctr = curmax + 1 updated = 0 warned = False errmsgs = [] tz = seminar.tz to_save = [] for i in list(range(slots)): seminar_ctr = raw_data.get("seminar_ctr%s" % i) speaker = process_user_input(raw_data.get("speaker%s" % i, ""), "speaker", "text", tz) if not speaker: if not warned and any( raw_data.get("%s%s" % (col, i), "").strip() for col in optional_cols): warned = True flash_warning("Talks are only saved if you specify a speaker") elif (not warned and seminar_ctr and not any( raw_data.get("%s%s" % (col, i), "").strip() for col in optional_cols)): warned = True flash_warning( "To delete an existing talk, click Details and then click delete on the Edit talk page" ) continue date = start_time = end_time = None dateval = raw_data.get("date%s" % i).strip() timeval = raw_data.get("time%s" % i).strip() if dateval and timeval: try: date = process_user_input(dateval, "date", "date", tz) except Exception as err: # should only be ValueError's but let's be cautious errmsgs.append(format_input_errmsg(err, dateval, "date")) if date: try: interval = process_user_input(timeval, "time", "daytimes", tz) start_time, end_time = date_and_daytimes_to_times( date, interval, tz) except Exception as err: # should only be ValueError's but let's be cautious errmsgs.append(format_input_errmsg(err, timeval, "time")) if not date or not start_time or not end_time: errmsgs.append( format_errmsg( "You must specify a date and time for the talk by %s", speaker)) # we need to flag date and time errors before we go any further if errmsgs: return show_input_errors(errmsgs) if daytimes_early(interval): flash_warning( "Talk for speaker %s includes early AM hours, please correct if this is not intended (use 24-hour time format).", speaker, ) elif daytimes_long(interval) > 8 * 60: flash_warning( "Time s %s is longer than 8 hours, please correct if this is not intended.", speaker), if seminar_ctr: # existing talk seminar_ctr = int(seminar_ctr) talk = WebTalk(shortname, seminar_ctr, seminar=seminar) else: # new talk talk = WebTalk(shortname, seminar=seminar, editing=True) data = dict(talk.__dict__) data["speaker"] = speaker data["start_time"] = start_time data["end_time"] = end_time for col in optional_cols: typ = db.talks.col_type[col] try: val = raw_data.get("%s%s" % (col, i), "") data[ col] = None # make sure col is present even if process_user_input fails data[col] = process_user_input(val, col, typ, tz) except Exception as err: errmsgs.append(format_input_errmsg(err, val, col)) # Don't try to create new_version using invalid input if errmsgs: return show_input_errors(errmsgs) if seminar_ctr: new_version = WebTalk(talk.seminar_id, data=data) if new_version != talk: updated += 1 to_save.append( new_version) # defer save in case of errors on other talks else: data["seminar_ctr"] = ctr ctr += 1 new_version = WebTalk(talk.seminar_id, data=data) to_save.append( new_version) # defer save in case of errors on other talks for newver in to_save: newver.save() if raw_data.get("detailctr"): return redirect( url_for( ".edit_talk", seminar_id=shortname, seminar_ctr=int(raw_data.get("detailctr")), ), 302, ) else: flash("%s talks updated, %s talks created" % (updated, ctr - curmax - 1)) if warned: return redirect(url_for(".edit_seminar_schedule", **raw_data), 302) else: return redirect( url_for( ".edit_seminar_schedule", shortname=shortname, begin=raw_data.get("begin"), end=raw_data.get("end"), frequency=raw_data.get("frequency"), weekday=raw_data.get("weekday"), ), 302, )
def save_talk(): raw_data = request.form token = raw_data.get("token", "") resp, talk = can_edit_talk(raw_data.get("seminar_id", ""), raw_data.get("seminar_ctr", ""), token) if resp is not None: return resp errmsgs = [] data = { "seminar_id": talk.seminar_id, "token": talk.token, "display": talk.display, # could be being edited by anonymous user } if talk.new: curmax = talks_max("seminar_ctr", {"seminar_id": talk.seminar_id}, include_deleted=True) if curmax is None: curmax = 0 data["seminar_ctr"] = curmax + 1 else: data["seminar_ctr"] = talk.seminar_ctr default_tz = talk.seminar.timezone if not default_tz: default_tz = "UTC" data["timezone"] = tz = raw_data.get("timezone", default_tz) tz = pytz.timezone(tz) for col in db.talks.search_cols: if col in data: continue typ = db.talks.col_type[col] try: val = raw_data.get(col, "") data[ col] = None # make sure col is present even if process_user_input fails data[col] = process_user_input(val, col, typ, tz) if col == "access" and data[col] not in [ "open", "users", "endorsed" ]: errmsgs.append( format_errmsg("Access type %s invalid", data[col])) except Exception as err: # should only be ValueError's but let's be cautious errmsgs.append(format_input_errmsg(err, val, col)) if not data["speaker"]: errmsgs.append( "Speaker name cannot be blank -- use TBA if speaker not chosen.") if data["start_time"] is None or data["end_time"] is None: errmsgs.append("Talks must have both a start and end time.") data["topics"] = clean_topics(data.get("topics")) data["language"] = clean_language(data.get("language")) data["subjects"] = clean_subjects(data.get("subjects")) if not data["subjects"]: errmsgs.append("Please select at least one subject") # Don't try to create new_version using invalid input if errmsgs: return show_input_errors(errmsgs) else: # to make it obvious that these two statements should be together new_version = WebTalk(talk.seminar_id, data=data) # Warnings sanity_check_times(new_version.start_time, new_version.end_time) if "zoom" in data["video_link"] and not "rec" in data["video_link"]: flash_warning( "Recorded video link should not be used for Zoom meeting links; be sure to use Livestream link for meeting links." ) if not data["topics"]: flash_warning( "This talk has no topics, and thus will only be visible to users when they disable their topics filter." ) if new_version == talk: flash("No changes made to talk.") else: new_version.save() edittype = "created" if talk.new else "edited" flash("Talk successfully %s!" % edittype) edit_kwds = dict(seminar_id=new_version.seminar_id, seminar_ctr=new_version.seminar_ctr) if token: edit_kwds["token"] = token else: edit_kwds.pop("token", None) return redirect(url_for(".edit_talk", **edit_kwds), 302)
def index(): # TODO: use a join for the following query seminars = {} conferences = {} deleted_seminars = [] deleted_talks = [] def key(elt): role_key = {"organizer": 0, "curator": 1, "creator": 3} return (role_key[elt[1]], elt[0].name) for rec in db.seminar_organizers.search( {"email": ilike_query(current_user.email)}, ["seminar_id", "curator"]): semid = rec["seminar_id"] role = "curator" if rec["curator"] else "organizer" seminar = WebSeminar(semid) pair = (seminar, role) if seminar.is_conference: conferences[semid] = pair else: seminars[semid] = pair role = "creator" for semid in seminars_search({"owner": ilike_query(current_user.email)}, "shortname", include_deleted=True): if semid not in seminars and semid not in conferences: seminar = WebSeminar(semid, deleted=True) # allow deleted pair = (seminar, role) if seminar.deleted: deleted_seminars.append(seminar) elif seminar.is_conference: conferences[semid] = pair else: seminars[semid] = pair seminars = sorted(seminars.values(), key=key) conferences = sorted(conferences.values(), key=key) deleted_seminars.sort(key=lambda sem: sem.name) for semid, semctr in db._execute( # ~~* is case insensitive amtch SQL(""" SELECT DISTINCT ON ({Ttalks}.{Csemid}, {Ttalks}.{Csemctr}) {Ttalks}.{Csemid}, {Ttalks}.{Csemctr} FROM {Ttalks} INNER JOIN {Tsems} ON {Ttalks}.{Csemid} = {Tsems}.{Csname} WHERE {Tsems}.{Cowner} ~~* %s AND {Ttalks}.{Cdel} = %s AND {Tsems}.{Cdel} = %s """).format( Ttalks=IdentifierWrapper("talks"), Tsems=IdentifierWrapper("seminars"), Csemid=IdentifierWrapper("seminar_id"), Csemctr=IdentifierWrapper("seminar_ctr"), Csname=IdentifierWrapper("shortname"), Cowner=IdentifierWrapper("owner"), Cdel=IdentifierWrapper("deleted"), ), [ilike_escape(current_user.email), True, False], ): talk = WebTalk(semid, semctr, deleted=True) deleted_talks.append(talk) deleted_talks.sort(key=lambda talk: (talk.seminar.name, talk.start_time)) manage = "Manage" if current_user.is_organizer else "Create" return render_template( "create_index.html", seminars=seminars, conferences=conferences, deleted_seminars=deleted_seminars, deleted_talks=deleted_talks, institution_known=institution_known, institutions=institutions(), section=manage, subsection="home", title=manage, user_is_creator=current_user.is_creator, )
def save_seminar_schedule(): raw_data = request.form shortname = raw_data["shortname"] resp, seminar = can_edit_seminar(shortname, new=False) if resp is not None: return resp frequency = raw_data.get("frequency") try: frequency = int(frequency) except Exception: pass schedule_count = int(raw_data["schedule_count"]) # FIXME not being used # update_times = bool(raw_data.get("update_times")) curmax = talks_max("seminar_ctr", {"seminar_id": shortname}) if curmax is None: curmax = 0 ctr = curmax + 1 updated = 0 warned = False for i in list(range(schedule_count)): seminar_ctr = raw_data.get("seminar_ctr%s" % i) speaker = process_user_input(raw_data.get("speaker%s" % i, ""), "text", tz=seminar.timezone) if not speaker: if not warned and any( raw_data.get("%s%s" % (col, i), "").strip() for col in optional_cols): warned = True flash_warning("Talks are only saved if you specify a speaker") continue date = raw_data.get("date%s" % i).strip() if date: try: date = process_user_input(date, "date", tz=seminar.tz) except ValueError as err: flash_error("invalid date %s: {0}".format(err), date) redirect( url_for(".edit_seminar_schedule", shortname=shortname, **raw_data), 301) else: date = None time_input = raw_data.get("time%s" % i, "").strip() if time_input: try: time_split = time_input.split("-") if len(time_split) == 1: raise ValueError("Must specify both start and end times") elif len(time_split) > 2: raise ValueError("More than one hyphen") # TODO: clean this up start_time = process_user_input(time_split[0], "time", seminar.tz).time() end_time = process_user_input(time_split[1], "time", seminar.tz).time() if check_time(start_time, end_time): raise ValueError except ValueError as err: if str(err): flash_error("invalid time range %s: {0}".format(err), time_input) return redirect(url_for(".edit_seminar_schedule", **raw_data), 301) else: start_time = end_time = None if any(X is None for X in [start_time, end_time, date]): flash_error("You must give a date, start and end time for %s" % speaker) return redirect(url_for(".edit_seminar_schedule", **raw_data), 301) if seminar_ctr: # existing talk seminar_ctr = int(seminar_ctr) talk = WebTalk(shortname, seminar_ctr, seminar=seminar) else: # new talk talk = WebTalk(shortname, seminar=seminar, editing=True) data = dict(talk.__dict__) data["speaker"] = speaker for col in optional_cols: data[col] = process_user_input(raw_data.get("%s%s" % (col, i), ""), "text", tz=seminar.timezone) data["start_time"] = localize_time( datetime.datetime.combine(date, start_time), seminar.tz) data["end_time"] = localize_time( datetime.datetime.combine(date, end_time), seminar.tz) if seminar_ctr: new_version = WebTalk(talk.seminar_id, data["seminar_ctr"], data=data) if new_version != talk: updated += 1 new_version.save() else: data["seminar_ctr"] = ctr ctr += 1 new_version = WebTalk(talk.seminar_id, ctr, data=data) new_version.save() if raw_data.get("detailctr"): return redirect( url_for( ".edit_talk", seminar_id=shortname, seminar_ctr=int(raw_data.get("detailctr")), ), 301, ) else: if updated or ctr > curmax + 1: flash("%s talks updated, %s talks created" % (updated, ctr - curmax - 1)) if warned: return redirect(url_for(".edit_seminar_schedule", **raw_data), 301) else: return redirect( url_for(".edit_seminar_schedule", shortname=shortname, begin=raw_data.get('begin'), end=raw_data.get('end'), frequency=raw_data.get('frequency'), weekday=raw_data.get('weekday')), 301)
def save_talk(): raw_data = request.form token = raw_data.get("token", "") resp, talk = can_edit_talk(raw_data.get("seminar_id", ""), raw_data.get("seminar_ctr", ""), token) if resp is not None: return resp def make_error(talk, col=None, err=None): if err is not None: flash_error("Error processing %s: {0}".format(err), col) talk = WebTalk(talk.seminar_id, talk.seminar_ctr, data=raw_data) title = "Create talk error" if talk.new else "Edit talk error" return render_template( "edit_talk.html", talk=talk, seminar=talk.seminar, title=title, section="Manage", subsection="edittalk", institutions=institutions(), timezones=timezones, ) data = { "seminar_id": talk.seminar_id, "token": talk.token, "display": talk.display, # could be being edited by anonymous user } if talk.new: curmax = talks_max("seminar_ctr", {"seminar_id": talk.seminar_id}) if curmax is None: curmax = 0 data["seminar_ctr"] = curmax + 1 else: data["seminar_ctr"] = talk.seminar_ctr default_tz = talk.seminar.timezone if not default_tz: default_tz = "UTC" data["timezone"] = tz = raw_data.get("timezone", default_tz) tz = pytz.timezone(tz) for col in db.talks.search_cols: if col in data: continue try: val = raw_data.get(col, "").strip() if not val: data[col] = None else: data[col] = process_user_input(val, db.talks.col_type[col], tz=tz) if col == "speaker_homepage" and val and not val.startswith( "http"): data[col] = "http://" + data[col] if col == "access" and val not in ["open", "users", "endorsed"]: raise ValueError("Invalid access type") except Exception as err: return make_error(talk, col, err) data["topics"] = clean_topics(data.get("topics")) data["language"] = clean_language(data.get("language")) new_version = WebTalk(talk.seminar_id, data["seminar_ctr"], data=data) if check_time(new_version.start_time, new_version.end_time, check_past=True): return make_error(talk) if new_version == talk: flash("No changes made to talk.") else: new_version.save() edittype = "created" if talk.new else "edited" flash("Talk successfully %s!" % edittype) edit_kwds = dict(seminar_id=new_version.seminar_id, seminar_ctr=new_version.seminar_ctr) if token: edit_kwds["token"] = token else: edit_kwds.pop("token", None) return redirect(url_for(".edit_talk", **edit_kwds), 301)
def save_seminar_schedule(): raw_data = request.form shortname = raw_data["shortname"] resp, seminar = can_edit_seminar(shortname, new=False) if resp is not None: return resp schedule_count = int(raw_data["schedule_count"]) update_times = bool(raw_data.get("update_times")) curmax = talks_max('seminar_ctr', {'seminar_id': shortname}) if curmax is None: curmax = 0 ctr = curmax + 1 try: start_time = datetime.time.fromisoformat(raw_data["start_time"]) end_time = datetime.time.fromisoformat(raw_data["end_time"]) except ValueError as err: flash_error("Invalid time: %s", err) return redirect(url_for(".edit_seminar_schedule", shortname=shortname), 301) for i in range(schedule_count): seminar_ctr = raw_data.get("seminar_ctr%s" % i) date = datetime.date.fromisoformat(raw_data["date%s" % i]) if seminar_ctr: # existing talk seminar_ctr = int(seminar_ctr) talk = WebTalk(shortname, seminar_ctr, seminar=seminar) data = dict(talk.__dict__) for col in [ "speaker", "speaker_affiliation", "speaker_email", "title" ]: data[col] = process_user_input(raw_data["%s%s" % (col, i)], 'text') if update_times: data["start_time"] = datetime.datetime.combine( date, start_time) data["end_time"] = datetime.datetime.combine(date, end_time) new_version = WebTalk(talk.seminar_id, data['seminar_ctr'], data=data) if new_version != talk: print(data) new_version.save() elif raw_data["speaker%s" % i].strip(): # new talk talk = WebTalk(shortname, seminar=seminar, editing=True) data = dict(talk.__dict__) for col in [ "speaker", "speaker_affiliation", "speaker_email", "title" ]: data[col] = process_user_input(raw_data["%s%s" % (col, i)], 'text') data["start_time"] = datetime.datetime.combine(date, start_time) data["end_time"] = datetime.datetime.combine(date, end_time) data["seminar_ctr"] = ctr ctr += 1 new_version = WebTalk(talk.seminar_id, ctr, data=data) print(data) new_version.save() return redirect(url_for(".edit_seminar_schedule", shortname=shortname), 301)