def all(): "Display list of graphics." if not flask.g.am_admin: utils.flash_error("Not logged in as admin.") return flask.redirect(flask.url_for("home")) graphics = get_graphics_all(full=True) return flask.render_template("graphics/all.html", graphics=graphics)
def all(): "Display list of datasets." if not flask.g.am_admin: utils.flash_error("Not logged in as admin.") return flask.redirect(flask.url_for("home")) datasets = get_datasets_all(full=True) return flask.render_template("datasets/all.html", datasets=datasets)
def reset(): "Reset the password for a user account and send email." if not flask.current_app.config["MAIL_SERVER"]: utils.flash_error("Cannot reset password; no email server defined.") return flask.redirect(flask.url_for("home")) if utils.http_GET(): email = flask.request.args.get("email") or "" email = email.lower() return flask.render_template("user/reset.html", email=email) elif utils.http_POST(): try: user = get_user(email=flask.request.form["email"]) if user is None: raise KeyError if user["status"] != constants.ENABLED: raise KeyError except KeyError: pass else: with UserSaver(user) as saver: saver.set_password() send_password_code(user, "password reset") utils.get_logger().info(f"reset user {user['username']}") utils.flash_message( "An email has been sent if the user account exists.") return flask.redirect(flask.url_for("home"))
def display(iuid): "Display the graphic." try: graphic = get_graphic(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for("home")) if not allow_view(graphic): utils.flash_error("View access to graphic not allowed.") return flask.redirect(utils.url_referrer()) dataset = get_dataset(graphic) if dataset: other_graphics = [gr for gr in datagraphics.dataset.get_graphics(dataset) if gr["_id"] != graphic["_id"]] else: other_graphics = [] if flask.g.current_user and \ flask.g.current_user["username"] == graphic["owner"] and \ dataset["owner"] != graphic["owner"]: utils.flash_warning("The dataset is not owned by you." " This graphic may become invalid if the owner of" " the dataset deletes it or makes it inaccessible.") return flask.render_template("graphic/display.html", graphic=graphic, slug=utils.slugify(graphic['title']), dataset=dataset, other_graphics=other_graphics, am_owner=am_owner(graphic), allow_edit=allow_edit(graphic), allow_delete=allow_delete(graphic))
def register(): "Register a new user account." if not flask.g.am_admin and not flask.current_app.config["USER_REGISTER"]: utils.flash_error("Only admin can register new user accounts.") return flask.redirect(flask.url_for("home")) if utils.http_GET(): return flask.render_template("user/register.html") elif utils.http_POST(): try: with UserSaver() as saver: saver.set_username(flask.request.form.get("username")) saver.set_email(flask.request.form.get("email")) saver.set_role(constants.USER) if flask.g.am_admin: saver.set_password( flask.request.form.get("password") or None) saver.set_apikey() saver.set_status(constants.ENABLED) else: saver.set_password() user = saver.doc except ValueError as error: utils.flash_error(error) return flask.redirect(flask.url_for(".register")) utils.get_logger().info(f"registered user {user['username']}") if user["status"] == constants.ENABLED: # Directly enabled and code set. Send code to the user. if user["password"][:5] == "code:": send_password_code(user, "registration") utils.get_logger().info(f"enabled user {user['username']}") utils.flash_message("User account created; check your email.") # Directly enabled and password set. No email to anyone. else: utils.get_logger().info(f"enabled user {user['username']}" " and set password") utils.flash_message("User account created and password set.") # Was set to 'pending'; send email to admins. else: admins = get_users(constants.ADMIN, status=constants.ENABLED) emails = [u["email"] for u in admins] message = flask_mail.Message("DataGraphics user account pending", recipients=emails) url = flask.url_for(".display", username=user["username"], _external=True) message.body = f"To enable the user account, go to {url}" utils.mail.send(message) utils.get_logger().info(f"pending user {user['username']}") utils.flash_message("User account created; an email will be sent" " when it has been enabled by the admin.") return flask.redirect(flask.url_for("home"))
def disable(username): "Disable the given user account." user = get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if user["username"] == flask.g.current_user["username"]: utils.flash_error("You cannot disable yourself.") return flask.redirect(flask.url_for("home")) with UserSaver(user) as saver: saver.set_status(constants.DISABLED) utils.get_logger().info(f"disabled user {username}") return flask.redirect(flask.url_for(".display", username=username))
def download(iuid, ext): """Download the content of the dataset as JSON or CSV file. This is for use in the HTML pages, not for API calls. """ try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if not allow_view(dataset): utils.flash_error("View access to dataset is not allowed.") return flask.redirect(utils.url_referrer()) if not dataset.get("_attachments", None): utils.flash_error("Dataset does not contain any data.") return flask.redirect(utils.url_referrer()) if ext == "json": outfile = flask.g.db.get_attachment(dataset, "data.json") response = flask.make_response(outfile.read()) response.headers.set("Content-Type", constants.JSON_MIMETYPE) elif ext == "csv": outfile = flask.g.db.get_attachment(dataset, "data.csv") response = flask.make_response(outfile.read()) response.headers.set("Content-Type", constants.CSV_MIMETYPE) else: utils.flash_error("Invalid file type requested.") return flask.redirect(utils.url_referrer()) slug = utils.slugify(dataset['title']) response.headers.set("Content-Disposition", "attachment", filename=f"{slug}.{ext}") return response
def editor(username): "Display list of the graphics for which the user is editor." user = datagraphics.user.get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if not datagraphics.user.am_admin_or_self(user): utils.flash_error("View access to editor graphics is not allowed.") return flask.redirect(flask.url_for("home")) graphics = get_graphics_editor(username, full=True) return flask.render_template("graphics/editor.html", user=user, graphics=graphics, show_public=True)
def private(iuid): "Set the graphic to private access." try: graphic = get_graphic(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if am_owner(graphic): if graphic["public"]: with GraphicSaver(graphic) as saver: saver.set_public(False) else: utils.flash_error("Only owner may make graphic private.") return flask.redirect(flask.url_for(".display", iuid=iuid))
def user(username): "Display list of user's datasets." user = datagraphics.user.get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if not datagraphics.user.am_admin_or_self(user): utils.flash_error("View access to user datasets is not allowed.") return flask.redirect(flask.url_for("home")) datasets = get_datasets_owner(username, full=True) return flask.render_template("datasets/user.html", user=user, datasets=datasets, show_public=True)
def private(iuid): "Set the dataset to private access." try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if am_owner(dataset): if dataset["public"]: with DatasetSaver(dataset) as saver: saver.set_public(False) else: utils.flash_error("Only owner may make dataset private.") return flask.redirect(flask.url_for(".display", iuid=iuid))
def logs(iuid): "Display the log records of the given dataset." try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if not allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) return flask.render_template( "logs.html", title=f"Dataset {dataset['title'] or 'No title'}", cancel_url=flask.url_for(".display", iuid=iuid), logs=utils.get_logs(iuid))
def logs(iuid): "Display the log records of the given graphic." try: graphic = get_graphic(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if not allow_view(graphic): utils.flash_error("View access to graphic not allowed.") return flask.redirect(utils.url_referrer()) return flask.render_template( "logs.html", title=f"Graphic {graphic['title']}", back_url=flask.url_for(".display", iuid=iuid), logs=utils.get_logs(iuid))
def logs(username): "Display the log records of the given user." user = get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if not am_admin_or_self(user): utils.flash_error("Access not allowed.") return flask.redirect(flask.url_for("home")) return flask.render_template( "logs.html", title=f"User {user['username']}", back_url=flask.url_for(".display", username=user["username"]), api_logs_url=flask.url_for("api_user.logs", username=user["username"]), logs=utils.get_logs(user["_id"]))
def password(): "Set the password for a user account, and login user." if utils.http_GET(): return flask.render_template( "user/password.html", username=flask.request.args.get("username"), code=flask.request.args.get("code")) elif utils.http_POST(): try: code = "" try: username = flask.request.form.get("username") or "" if not username: raise ValueError user = get_user(username=username) if user is None: raise ValueError if flask.g.am_admin and \ flask.g.current_user["username"] != username: pass # No check for either code or current password. elif flask.current_app.config["MAIL_SERVER"]: code = flask.request.form.get("code") or "" if user["password"] != f"code:{code}": raise ValueError else: password = flask.request.form.get("current_password") or "" if not check_password_hash(user["password"], password): raise ValueError except ValueError: if flask.current_app.config["MAIL_SERVER"]: raise ValueError("No such user or wrong code.") else: raise ValueError("No such user or wrong password.") password = flask.request.form.get("password") or "" if len(password) < flask.current_app.config["MIN_PASSWORD_LENGTH"]: raise ValueError("Too short password.") if not flask.current_app.config["MAIL_SERVER"]: if password != flask.request.form.get("confirm_password"): raise ValueError("Wrong password entered; confirm failed.") except ValueError as error: utils.flash_error(str(error)) return flask.redirect( flask.url_for(".password", username=username, code=code)) else: with UserSaver(user) as saver: saver.set_password(password) utils.get_logger().info(f"password user {user['username']}") if not flask.g.current_user: do_login(username, password) return flask.redirect(flask.url_for("home"))
def display(username): "Display the given user." user = get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if not am_admin_or_self(user): utils.flash_error("Access not allowed.") return flask.redirect(flask.url_for("home")) user["count"] = { "datasets": count_datasets_owner(username), "datasets_editor": count_datasets_editor(username), "graphics": count_graphics_owner(username), "graphics_editor": count_graphics_editor(username) } return flask.render_template("user/display.html", user=user)
def enable(username): "Enable the given user account." user = get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if user["username"] == flask.g.current_user["username"]: utils.flash_error("You cannot enable yourself.") return flask.redirect(flask.url_for("home")) with UserSaver(user) as saver: saver.set_status(constants.ENABLED) saver.set_apikey() # Better safety to set/change API key on enable. if user["password"][:5] == "code:" and \ flask.current_app.config["MAIL_SERVER"]: send_password_code(user, "enabled") utils.get_logger().info(f"enabled user {username}") return flask.redirect(flask.url_for(".display", username=username))
def create(): "Create a new dataset, from file or URL." if utils.http_GET(): return flask.render_template("dataset/create.html") elif utils.http_POST(): try: with DatasetSaver() as saver: saver.set_title() saver.set_description() saver.set_public(False) if not saver.upload_file(): saver.get_url_data() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def edit(iuid): "Edit the graphic, or delete it." try: graphic = get_graphic(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if utils.http_GET(): if not allow_edit(graphic): utils.flash_error("Edit access to graphic not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) return flask.render_template("graphic/edit.html", am_owner=am_owner(graphic), graphic=graphic) elif utils.http_POST(): if not allow_edit(graphic): utils.flash_error("Edit access to graphic not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) try: with GraphicSaver(graphic) as saver: saver.set_title() if flask.g.am_admin: saver.change_owner() if am_owner(graphic): saver.set_editors() saver.set_description() saver.set_specification() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"])) elif utils.http_DELETE(): if not allow_delete(graphic): utils.flash_error("Delete access to graphic not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) flask.g.db.delete(graphic) for log in utils.get_logs(graphic["_id"], cleanup=False): flask.g.db.delete(log) utils.flash_message("The graphic was deleted.") return flask.redirect( flask.url_for("dataset.display", iuid=graphic["dataset"]))
def data(iuid): "Display the data contents of the dataset." try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for("home")) if not allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) outfile = flask.g.db.get_attachment(dataset, "data.json") data = json.load(outfile) max_records = flask.current_app.config["MAX_RECORDS_INSPECT"] if len(data) > max_records: data = data[:max_records] utils.flash_message( f"Only the first {max_records} records are displayed.") return flask.render_template("dataset/data.html", dataset=dataset, data=data)
def login(): """Login to a user account. Creates the admin user specified in the settings.json, if not done. """ app = flask.current_app if app.config.get("ADMIN_USER"): user = get_user(username=app.config["ADMIN_USER"]["username"]) if user is None: try: with UserSaver() as saver: saver.set_username(app.config["ADMIN_USER"]["username"]) saver.set_email(app.config["ADMIN_USER"]["email"]) saver.set_role(constants.ADMIN) saver.set_status(constants.ENABLED) saver.set_password(app.config["ADMIN_USER"]["password"]) utils.get_logger().info("Created admin user " + app.config["ADMIN_USER"]["username"]) except ValueError as error: utils.get_logger().error("Could not create admin user;" " misconfiguration.") if utils.http_GET(): return flask.render_template("user/login.html", next=flask.request.args.get("next")) if utils.http_POST(): username = flask.request.form.get("username") password = flask.request.form.get("password") try: if username and password: do_login(username, password) else: raise ValueError try: next = flask.request.form["next"] except KeyError: return flask.redirect(flask.url_for("home")) else: return flask.redirect(next) except ValueError: utils.flash_error("Invalid user or password, or account disabled.") return flask.redirect(flask.url_for(".login"))
def display(iuid): "Display the dataset." try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for("home")) if not allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) storage = sum( [s['length'] for s in dataset.get('_attachments', {}).values()]) return flask.render_template("dataset/display.html", dataset=dataset, graphics=get_graphics(dataset), storage=storage, am_owner=am_owner(dataset), allow_edit=allow_edit(dataset), allow_delete=allow_delete(dataset), possible_delete=possible_delete(dataset), commands=get_commands(dataset))
def download(iuid, ext): "Download the JSON or JavaScript specification of the Vega-Lite graphic." try: graphic = get_graphic(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if not allow_view(graphic): utils.flash_error("View access to graphic not allowed.") return flask.redirect(utils.url_referrer()) dataset = get_dataset(graphic) if not dataset: utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) spec = graphic["specification"] slug = utils.slugify(graphic['title']) id = flask.request.args.get("id") or "graphic" if utils.to_bool(flask.request.args.get("inline")): outfile = flask.g.db.get_attachment(dataset, "data.json") spec["data"] = {"values": json.load(outfile)} if ext == "json": response = flask.jsonify(spec) response.headers.set("Content-Type", constants.JSON_MIMETYPE) elif ext == "js": spec = json.dumps(spec, ensure_ascii=False) response = flask.make_response(f'vegaEmbed("#{id}", {spec},' f' {{downloadFileName: "{slug}"}})' '.then(result=>console.log(result))' '.catch(console.warn);') response.headers.set("Content-Type", constants.JS_MIMETYPE) elif ext == "html": html = flask.render_template("graphic/vega_lite.html", graphic=graphic, id=id, slug=slug) response = flask.make_response(html) response.headers.set("Content-Type", constants.HTML_MIMETYPE) else: utils.flash_error("Invalid file type requested.") return flask.redirect(utils.url_referrer()) response.headers.set("Content-Disposition", "attachment", filename=f"{slug}.{ext}") return response
def stencil(): "Select a stencil for the dataset given as form argument." try: iuid = flask.request.values.get("dataset") if not iuid: raise ValueError("No dataset IUID provided.") dataset = datagraphics.dataset.get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for("home")) if not datagraphics.dataset.allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) if utils.http_GET(): stencils = [] for name in flask.current_app.config["STENCILS"]: header = deepcopy( flask.current_app.config["STENCILS"][name]["header"]) field_variables = [ v for v in header["variables"] if v.get("class") == "field" ] header["combinations"] = combinations(field_variables, dataset["meta"].items()) if header["combinations"]: stencils.append(header) stencils.sort(key=lambda h: (h.get("weight", 0), h["title"])) return flask.render_template("graphic/stencil.html", dataset=dataset, stencils=stencils) elif utils.http_POST(): try: spec = deepcopy(flask.current_app.config["STENCILS"]\ [flask.request.form["stencil"]]) header = spec.pop("header") setfields = SetFields(flask.request.form["combination"]) url = flask.url_for("api_dataset.content", iuid=dataset["_id"], ext="csv", _external=True) for variable in header["variables"]: if variable.get("class") == "dataset": setfields.lookup["/".join(variable["path"])] = url setfields.traverse(spec) with GraphicSaver() as saver: saver.set_dataset(dataset) saver.set_title(header["title"]) saver.set_description( f"Created from stencil {header['name']}.") saver.set_public(False) saver.set_specification(spec) except (KeyError, ValueError) as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def copy(iuid): "Copy the graphic." try: graphic = get_graphic(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if not allow_view(graphic): utils.flash_error("View access to graphic not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) try: with GraphicSaver() as saver: saver.copy(graphic) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def copy(iuid): "Copy the dataset, including its data content." try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if not allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) try: with DatasetSaver() as saver: saver.copy(dataset) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def edit(username): "Edit the user display. Or delete the user." user = get_user(username=username) if user is None: utils.flash_error("No such user.") return flask.redirect(flask.url_for("home")) if not am_admin_or_self(user): utils.flash_error("Access not allowed.") return flask.redirect(flask.url_for("home")) if utils.http_GET(): return flask.render_template("user/edit.html", user=user, deletable=is_empty(user)) elif utils.http_POST(): with UserSaver(user) as saver: if flask.g.am_admin: email = flask.request.form.get("email") if email != user["email"]: saver.set_email(email) if am_admin_and_not_self(user): saver.set_role(flask.request.form.get("role")) if flask.request.form.get("apikey"): saver.set_apikey() return flask.redirect( flask.url_for(".display", username=user["username"])) elif utils.http_DELETE(): if not is_empty(user): utils.flash_error("Cannot delete non-empty user account.") return flask.redirect(flask.url_for(".display", username=username)) for log in utils.get_logs(user["_id"], cleanup=False): flask.g.db.delete(log) flask.g.db.delete(user) utils.flash_message(f"Deleted user {username}.") utils.get_logger().info(f"deleted user {username}") if flask.g.am_admin: return flask.redirect(flask.url_for(".all")) else: return flask.redirect(flask.url_for("home"))
def create(): "Create a new graphic for dataset given as form argument." try: iuid = flask.request.values.get("dataset") if not iuid: raise ValueError("No dataset IUID provided.") dataset = datagraphics.dataset.get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for("home")) if not datagraphics.dataset.allow_view(dataset): utils.flash_error("View access to dataset not allowed.") return flask.redirect(utils.url_referrer()) if utils.http_GET(): graphic = { "$schema": constants.VEGA_LITE_SCHEMA_URL, "data": { "url": flask.url_for("api_dataset.content", iuid=dataset["_id"], ext="csv", _external=True) } } return flask.render_template("graphic/create.html", dataset=dataset, graphic=graphic) elif utils.http_POST(): try: with GraphicSaver() as saver: saver.set_dataset(dataset) saver.set_title() saver.set_description() saver.set_public(False) saver.set_specification() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) return flask.redirect(flask.url_for(".display", iuid=saver.doc["_id"]))
def edit(iuid): "Edit the dataset, or delete it." try: dataset = get_dataset(iuid) except ValueError as error: utils.flash_error(str(error)) return flask.redirect(utils.url_referrer()) if utils.http_GET(): if not allow_edit(dataset): utils.flash_error("Edit access to dataset not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) return flask.render_template("dataset/edit.html", am_owner=am_owner(dataset), dataset=dataset) elif utils.http_POST(): if not allow_edit(dataset): utils.flash_error("Edit access to dataset not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) try: with DatasetSaver(dataset) as saver: saver.set_title() if flask.g.am_admin: saver.change_owner() if am_owner(dataset): saver.set_editors() saver.set_description() saver.upload_file() saver.set_vega_lite_types() except ValueError as error: utils.flash_error(str(error)) return flask.redirect(flask.url_for(".display", iuid=iuid)) elif utils.http_DELETE(): if not possible_delete(dataset): utils.flash_error("Dataset cannot be deleted; use by graphics.") return flask.redirect(flask.url_for(".display", iuid=iuid)) if not allow_delete(dataset): utils.flash_error("Delete access to dataset not allowed.") return flask.redirect(flask.url_for(".display", iuid=iuid)) flask.g.db.delete(dataset) for log in utils.get_logs(dataset["_id"], cleanup=False): flask.g.db.delete(log) utils.flash_message("The dataset was deleted.") return flask.redirect(flask.url_for("datasets.display"))