"""Dataset requests."""
import flask
import user
import utils
blueprint = flask.Blueprint("dataset", __name__) # pylint: disable=invalid-name
[docs]@blueprint.route("", methods=["GET"])
def list_datasets():
"""Provide a simplified list of all available datasets."""
results = list(
flask.g.db["datasets"].find(projection={"title": 1, "_id": 1, "tags": 1, "properties": 1})
)
return utils.response_json({"datasets": results})
[docs]@blueprint.route("/user", methods=["GET"])
@user.login_required
def list_user_data():
"""List all datasets belonging to current user."""
user_orders = list(
flask.g.db["orders"].find({"editors": flask.session["user_id"]}, {"datasets": 1})
)
uuids = list(ds for entry in user_orders for ds in entry["datasets"])
user_datasets = list(flask.g.db["datasets"].find({"_id": {"$in": uuids}}))
return utils.response_json({"datasets": user_datasets})
[docs]@blueprint.route("/<identifier>", methods=["GET"])
def get_dataset(identifier):
"""
Retrieve the dataset with uuid <identifier>.
Args:
identifier (str): uuid for the wanted dataset
Returns:
flask.Response: json structure for the dataset
"""
result = build_dataset_info(identifier)
if not result:
return flask.Response(status=404)
return utils.response_json({"dataset": result})
[docs]@blueprint.route("/<identifier>", methods=["DELETE"])
@user.login_required
def delete_dataset(identifier: str):
"""
Delete a dataset.
Can be deleted only by editors or user with DATA_MANAGEMENT permissions.
Args:
identifier (str): The dataset uuid.
"""
perm_status = utils.req_check_permissions(["DATA_EDIT"])
if perm_status != 200:
flask.abort(status=perm_status)
ds = utils.req_get_entry("datasets", identifier)
if not ds:
flask.abort(status=404)
# permission check
order = flask.g.db["orders"].find_one({"datasets": ds["_id"]})
if not order:
flask.current_app.logger.error("Dataset without parent order: %s", ds["_id"])
flask.current_app.logger.error(ds)
# permission check
if (
not utils.req_has_permission("DATA_MANAGEMENT")
and flask.g.current_user["_id"] not in order["editors"]
):
flask.abort(status=403)
result = utils.req_commit_to_db("datasets", "delete", {"_id": ds["_id"]})
if not result.log or not result.data:
flask.abort(status=500)
collections = list(flask.g.db["collections"].find({"datasets": ds["_id"]}))
flask.g.db["collections"].update_many({}, {"$pull": {"datasets": ds["_id"]}})
for collection in collections:
collection["datasets"] = [collection["datasets"].remove(ds["_id"])]
utils.req_make_log_new(
data_type="collection",
action="edit",
comment="Dataset deleted",
data=collection,
)
flask.g.db["orders"].update_many({}, {"$pull": {"datasets": ds["_id"]}})
order["datasets"].remove(ds["_id"])
utils.req_make_log_new(
data_type="order",
action="edit",
comment="Dataset deleted",
data=order,
)
return flask.Response(status=200)
[docs]@blueprint.route("/<identifier>", methods=["PATCH"])
@user.login_required
def update_dataset(identifier):
"""
Update a dataset with new values.
Args:
identifier (str): uuid for the wanted dataset
Returns:
flask.Response: success: 200, failure: 400
"""
perm_status = utils.req_check_permissions(["DATA_EDIT"])
if perm_status != 200:
flask.abort(status=perm_status)
dataset = utils.req_get_entry("datasets", identifier)
if not dataset:
flask.abort(status=404)
# permissions
order = flask.g.db["orders"].find_one({"datasets": dataset["_id"]})
if (
not utils.req_has_permission("DATA_MANAGEMENT")
and flask.g.current_user["_id"] not in order["editors"]
):
flask.abort(status=403)
jsondata = flask.request.json
if not jsondata or "dataset" not in jsondata or not isinstance(jsondata["dataset"], dict):
flask.abort(status=400)
indata = jsondata["dataset"]
validation = utils.basic_check_indata(indata, dataset, prohibited=("_id"))
if not validation.result:
flask.abort(status=validation.status)
indata = utils.prepare_for_db(indata)
is_different = False
for field in indata:
if indata[field] != dataset[field]:
is_different = True
break
dataset.update(indata)
if indata and is_different:
result = utils.req_commit_to_db("datasets", "edit", dataset)
if not result.log or not result.data:
flask.abort(status=500)
return flask.Response(status=200)
[docs]@blueprint.route("/<identifier>/log", methods=["GET"])
@user.login_required
def get_dataset_log(identifier: str = None):
"""
Get change logs for the user entry with uuid ``identifier``.
Can be accessed by editors with DATA_EDIT and admin (DATA_MANAGEMENT).
Logs for deleted datasets cannot be accessed.
Args:
identifier (str): The uuid of the dataset.
Returns:
flask.Response: Logs as json.
"""
perm_status = utils.req_check_permissions(["DATA_EDIT"])
if perm_status != 200:
flask.abort(status=perm_status)
dataset = utils.req_get_entry("datasets", identifier)
if not dataset:
flask.abort(status=404)
order_data = flask.g.db["orders"].find_one({"datasets": dataset["_id"]})
if not order_data:
flask.current_app.logger.error("Dataset without parent order: %s", dataset["_id"])
flask.abort(500)
if (
not utils.req_has_permission("DATA_MANAGEMENT")
and flask.g.current_user["_id"] not in order_data["editors"]
):
flask.abort(403)
dataset_logs = list(
flask.g.db["logs"].find({"data_type": "dataset", "data._id": dataset["_id"]})
)
for log in dataset_logs:
del log["data_type"]
utils.incremental_logs(dataset_logs)
return utils.response_json(
{"entry_id": dataset["_id"], "data_type": "dataset", "logs": dataset_logs}
)
[docs]@blueprint.route("", methods=["POST"])
@user.login_required
def info_add_dataset():
"""Return information about the correct endpoint for adding datasets."""
return flask.Response(
f"Use {flask.url_for('order.add_dataset', identifier='-identifier-', _external=True)} instead",
status=400,
)
# helper functions
[docs]def build_dataset_info(identifier: str):
"""
Query for a dataset from the database.
Args:
identifier (str): The uuid of the dataset.
Returns:
dict: The prepared dataset entry.
"""
dataset = utils.req_get_entry("datasets", identifier)
if not dataset:
return None
order = flask.g.db["orders"].find_one({"datasets": dataset["_id"]})
if flask.g.current_user:
curr_user = flask.g.current_user["_id"]
else:
curr_user = None
if utils.req_has_permission("DATA_MANAGEMENT") or curr_user in order["editors"]:
dataset["order"] = {"_id": order["_id"], "title": order["title"]}
dataset["related"] = list(
flask.g.db["datasets"].find({"_id": {"$in": order["datasets"]}}, {"title": 1})
)
dataset["related"].remove({"_id": dataset["_id"], "title": dataset["title"]})
dataset["collections"] = list(
flask.g.db["collections"].find({"datasets": dataset["_id"]}, {"title": 1})
)
for field in ("editors", "generators", "authors"):
if field == "editors" and (
not utils.req_has_permission("DATA_MANAGEMENT") and curr_user not in order[field]
):
continue
dataset[field] = utils.user_uuid_data(order[field], flask.g.db)
dataset["organisation"] = utils.user_uuid_data(order["organisation"], flask.g.db)
dataset["organisation"] = dataset["organisation"][0] if dataset["organisation"] else ""
return dataset