diff --git a/backend/migrations/versions/063b15fc4ca4_.py b/backend/migrations/versions/063b15fc4ca4_.py new file mode 100644 index 0000000..ed0c33d --- /dev/null +++ b/backend/migrations/versions/063b15fc4ca4_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 063b15fc4ca4 +Revises: b60bb67d1758 +Create Date: 2021-05-02 16:32:52.230489 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '063b15fc4ca4' +down_revision = 'b60bb67d1758' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('data', sa.Column('is_deleted', sa.DateTime(), nullable=True)) + op.add_column('label', sa.Column('is_deleted', sa.DateTime(), nullable=True)) + op.add_column('user', sa.Column('is_deleted', sa.DateTime(), nullable=True)) + op.add_column('user_project', sa.Column('is_deleted', sa.DateTime(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('user_project', 'is_deleted') + op.drop_column('user', 'is_deleted') + op.drop_column('label', 'is_deleted') + op.drop_column('data', 'is_deleted') + # ### end Alembic commands ### diff --git a/backend/migrations/versions/e1e28decd983_.py b/backend/migrations/versions/e1e28decd983_.py new file mode 100644 index 0000000..6b760ab --- /dev/null +++ b/backend/migrations/versions/e1e28decd983_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: e1e28decd983 +Revises: 063b15fc4ca4 +Create Date: 2021-05-02 16:46:55.760634 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'e1e28decd983' +down_revision = '063b15fc4ca4' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('project', sa.Column('is_deleted', sa.DateTime(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('project', 'is_deleted') + # ### end Alembic commands ### diff --git a/backend/migrations/versions/ffc98d2cb07f_.py b/backend/migrations/versions/ffc98d2cb07f_.py new file mode 100644 index 0000000..c49aa3c --- /dev/null +++ b/backend/migrations/versions/ffc98d2cb07f_.py @@ -0,0 +1,30 @@ +"""empty message + +Revision ID: ffc98d2cb07f +Revises: e1e28decd983 +Create Date: 2021-05-02 16:58:36.146492 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ffc98d2cb07f' +down_revision = 'e1e28decd983' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('label_type', sa.Column('is_deleted', sa.DateTime(), nullable=True)) + op.add_column('label_value', sa.Column('is_deleted', sa.DateTime(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('label_value', 'is_deleted') + op.drop_column('label_type', 'is_deleted') + # ### end Alembic commands ### diff --git a/backend/models.py b/backend/models.py index 710a880..bdc06da 100644 --- a/backend/models.py +++ b/backend/models.py @@ -1,6 +1,52 @@ from werkzeug.security import generate_password_hash, check_password_hash +from flask_sqlalchemy import BaseQuery +from backend import db, app + + +class QueryWithSoftDelete(BaseQuery): + _with_deleted = False + + def __new__(cls, *args, **kwargs): + obj = super(QueryWithSoftDelete, cls).__new__(cls) + obj._with_deleted = kwargs.pop("_with_deleted", False) + if len(args) > 0: + super(QueryWithSoftDelete, obj).__init__(*args, **kwargs) + return obj.filter_by(is_deleted=None) if obj._with_deleted is None else obj + return obj + + def __init__(self, *args, **kwargs): + pass + + def with_deleted(self): + return self.__class__( + db.class_mapper(self._mapper_zero().class_), + session=db.session(), + _with_deleted=True, + ) + + def _get(self, *args, **kwargs): + # this calls the original query.get function from the base class + return super(QueryWithSoftDelete, self).get(*args, **kwargs) + + def get(self, *args, **kwargs): + # the query.get method does not like it if there is a filter clause + # pre-loaded, so we need to implement it using a workaround + obj = self.with_deleted()._get(*args, **kwargs) + return ( + obj if obj is None or self._with_deleted or obj.is_deleted is None else None + ) + + def _all(self, *args, **kwargs): + return super(QueryWithSoftDelete, self).all(*args, **kwargs) + + def all(self, *args, **kwargs): + objs = self.with_deleted()._all(*args, **kwargs) + ret_vals = [] + for obj in objs: + if obj and obj.is_deleted is None: + ret_vals.append(obj) + return ret_vals -from backend import db annotation_table = db.Table( "annotation", @@ -39,14 +85,22 @@ default=db.func.now(), onupdate=db.func.utc_timestamp(), ), + db.Column( + "is_deleted", + db.DateTime(), + nullable=True, + default=None, + ), ) class Data(db.Model): __tablename__ = "data" + query_class = QueryWithSoftDelete id = db.Column("id", db.Integer(), primary_key=True) + is_deleted = db.Column("is_deleted", db.DateTime(), nullable=True, default=None) project_id = db.Column( "project_id", db.Integer(), db.ForeignKey("project.id"), nullable=False ) @@ -108,8 +162,10 @@ def to_dict(self): class Label(db.Model): __tablename__ = "label" + query_class = QueryWithSoftDelete id = db.Column("id", db.Integer(), primary_key=True) + is_deleted = db.Column("is_deleted", db.DateTime(), nullable=True, default=None) name = db.Column("name", db.String(32), nullable=False) @@ -162,6 +218,7 @@ class LabelType(db.Model): default=db.func.now(), onupdate=db.func.utc_timestamp(), ) + is_deleted = db.Column("is_deleted", db.DateTime(), nullable=True, default=None) class LabelValue(db.Model): @@ -169,6 +226,8 @@ class LabelValue(db.Model): id = db.Column("id", db.Integer(), primary_key=True) + is_deleted = db.Column("is_deleted", db.DateTime(), nullable=True, default=None) + label_id = db.Column( "label_id", db.Integer(), db.ForeignKey("label.id"), nullable=False ) @@ -225,6 +284,8 @@ class Project(db.Model): onupdate=db.func.utc_timestamp(), ) + is_deleted = db.Column("is_deleted", db.DateTime(), nullable=True, default=None) + users = db.relationship( "User", secondary=user_project_table, back_populates="projects" ) @@ -281,7 +342,9 @@ class Segmentation(db.Model): ) values = db.relationship( - "LabelValue", secondary=annotation_table, back_populates="segmentations", + "LabelValue", + secondary=annotation_table, + back_populates="segmentations", ) def set_start_time(self, start_time): @@ -305,6 +368,7 @@ def to_dict(self): class User(db.Model): __tablename__ = "user" + query_class = QueryWithSoftDelete id = db.Column("id", db.Integer(), primary_key=True) @@ -334,6 +398,7 @@ class User(db.Model): projects = db.relationship( "Project", secondary=user_project_table, back_populates="users" ) + is_deleted = db.Column("is_deleted", db.DateTime(), nullable=True, default=None) def set_role(self, role_id): self.role_id = role_id diff --git a/backend/routes/current_user.py b/backend/routes/current_user.py index 1c9da34..2d6b4af 100644 --- a/backend/routes/current_user.py +++ b/backend/routes/current_user.py @@ -59,6 +59,7 @@ def fetch_data_for_project(project_id): data["pending"] = ( db.session.query(Data) + .filter(Data.is_deleted == None) .filter(Data.assigned_user_id == request_user.id) .filter(Data.project_id == project_id) .filter(Data.id.notin_(segmentations)) @@ -68,6 +69,7 @@ def fetch_data_for_project(project_id): data["completed"] = ( db.session.query(Data) + .filter(Data.is_deleted == None) .filter(Data.assigned_user_id == request_user.id) .filter(Data.project_id == project_id) .filter(Data.id.in_(segmentations)) @@ -77,12 +79,13 @@ def fetch_data_for_project(project_id): data["marked_review"] = Data.query.filter_by( assigned_user_id=request_user.id, + is_deleted=None, project_id=project_id, is_marked_for_review=True, ).order_by(Data.last_modified.desc()) data["all"] = Data.query.filter_by( - assigned_user_id=request_user.id, project_id=project_id + assigned_user_id=request_user.id, project_id=project_id, is_deleted=None ).order_by(Data.last_modified.desc()) paginated_data = data[active].paginate(page, 10, False) diff --git a/backend/routes/data.py b/backend/routes/data.py index 1d65ef8..94020cd 100644 --- a/backend/routes/data.py +++ b/backend/routes/data.py @@ -1,3 +1,4 @@ +import os import json import sqlalchemy as sa import uuid @@ -190,3 +191,43 @@ def add_data(): ), 201, ) + + +@api.route("/rmdata", methods=["POST"]) +@jwt_required +def remove_data(): + identity = get_jwt_identity() + request_user = User.query.filter_by(username=identity["username"]).first() + is_admin = True if request_user.role.role == "admin" else False + if is_admin == False: + return jsonify(message="Unauthorized access!"), 401 + + if not request.is_json: + return jsonify(message="Missing JSON in request"), 400 + + data_id = request.json['dataId'] + project_id = request.json['projectId'] + + app.logger.info(f"This is something: {data_id} : {project_id}") + + try: + data = Data.query.filter_by(id=data_id, project_id=project_id).first() + data.is_deleted = sa.func.now() + + # for segment in Segmentation.query.filter_by(data_id=data.id): + # segment.is_deleted = sa.func.now() + + except Exception as e: + app.logger.error("Error deleting datapoint") + app.logger.error(e) + return jsonify(message="Error deleting datapoint!"), 500 + + db.session.commit() + + return ( + jsonify( + message=f"Data deleted", + type="DATA_DELETED", + ), + 201, + ) diff --git a/backend/routes/labels.py b/backend/routes/labels.py index 46eb412..2f1f1b8 100644 --- a/backend/routes/labels.py +++ b/backend/routes/labels.py @@ -34,6 +34,7 @@ def add_value_to_label(label_id): try: label_value = LabelValue(value=value, label_id=label_id) + app.logger.info(f"soemthing was done here: {label_value}") db.session.add(label_value) db.session.commit() db.session.refresh(label_value) @@ -78,7 +79,7 @@ def get_values_for_label(label_id): return jsonify(message="Unauthorized access!"), 401 try: - values = LabelValue.query.filter_by(label_id=label_id).all() + values = LabelValue.query.filter_by(label_id=label_id, is_deleted=None).all() response = [ { "value_id": value.id, @@ -95,6 +96,32 @@ def get_values_for_label(label_id): return (jsonify(values=response), 200) +@api.route("/labels//values/", methods=["DELETE"]) +@jwt_required +def remove_values_from_label(label_id, label_value_id): + app.logger.info("We are deleint something") + identity = get_jwt_identity() + request_user = User.query.filter_by(username=identity["username"]).first() + is_admin = True if request_user.role.role == "admin" else False + + if is_admin == False: + return jsonify(message="Unauthorized access!"), 401 + + try: + value = LabelValue.query.get(label_value_id) + value.is_deleted = db.func.now() + db.session.commit() + except Exception as e: + app.logger.error(f"No values exists for label with id: {label_id}") + app.logger.error(e) + return (jsonify(message=f"No values exists for label with id: {label_id}"), 404) + + return ( + jsonify(label_value_id=label_value_id, message=f"LabelValue was deleted"), + 200, + ) + + @api.route("/labels//values/", methods=["GET"]) @jwt_required def fetch_label_value(label_id, label_value_id): diff --git a/backend/routes/projects.py b/backend/routes/projects.py index a21f6c9..bae1e97 100644 --- a/backend/routes/projects.py +++ b/backend/routes/projects.py @@ -60,6 +60,37 @@ def create_project(): return jsonify(project_id=project.id, message="Project has been created!"), 201 +@api.route("/rmprojects", methods=["POST"]) +@jwt_required +def remove_project(): + identity = get_jwt_identity() + request_user = User.query.filter_by(username=identity["username"]).first() + is_admin = True if request_user.role.role == "admin" else False + + if is_admin == False: + return jsonify(message="Unauthorized access!"), 401 + + if not request.is_json: + return jsonify(message="Missing JSON in request"), 400 + + project_id = request.json.get("projectId", None) + + try: + project = Project.query.filter_by(id=project_id).first() + for data in Data.query.filter_by(project_id=project.id): + for segment in Segmentation.query.filter_by(data_id=data.id): + segment.is_deleted = sa.func.now() + data.is_deleted = sa.func.now() + project.is_deleted = sa.func.now() + db.session.commit() + except Exception as e: + app.logger.error("Error deleting project") + app.logger.error(e) + return jsonify(message="Error deleting project!"), 500 + + return jsonify(project_id=project_id, message="Project has been deleted!"), 201 + + @api.route("/projects", methods=["GET"]) @jwt_required def fetch_all_projects(): @@ -70,7 +101,8 @@ def fetch_all_projects(): if is_admin == False: return jsonify(message="Unauthorized access!"), 401 try: - projects = Project.query.all() + projects = Project.query.filter_by(is_deleted=None).all() + app.logger.info(f"All the projects are : {projects}") response = list( [ { @@ -115,6 +147,7 @@ def fetch_project(project_id): "created_on": label.created_at.strftime("%B %d, %Y"), } for label in project.labels + if label.is_deleted is None ] except Exception as e: app.logger.error(f"No project exists with Project ID: {project_id}") @@ -276,6 +309,49 @@ def add_label_to_project(project_id): ) +@api.route("/projects//rmlabels", methods=["DELETE"]) +@jwt_required +def remove_label_to_project(project_id): + identity = get_jwt_identity() + request_user = User.query.filter_by(username=identity["username"]).first() + is_admin = True if request_user.role.role == "admin" else False + + if is_admin == False: + return jsonify(message="Unauthorized access!"), 401 + + if not request.is_json: + return jsonify(message="Missing JSON in request"), 400 + + label_id = request.json.get("labelId", None) + + try: + project = Project.query.get(project_id) + label = Label.query.get(label_id) + label.is_deleted = db.func.now() + + db.session.commit() + except Exception as e: + app.logger.error(f"Error adding label to project: {project_id}") + app.logger.error(e) + return ( + jsonify( + message=f"Error deleting label to project: {project_id}", + type="LABEL_DELETION_FAILED", + ), + 500, + ) + + return ( + jsonify( + project_id=project.id, + label_id=label.id, + message=f"Label deleted from project: {project.name}", + type="LABEL_DELETED_FROM_PROJECT", + ), + 201, + ) + + @api.route("/projects//labels/", methods=["GET"]) @jwt_required def get_label_for_project(project_id, label_id): diff --git a/backend/routes/users.py b/backend/routes/users.py index 106eb70..666cb19 100644 --- a/backend/routes/users.py +++ b/backend/routes/users.py @@ -65,6 +65,40 @@ def create_user(): return jsonify(user_id=user.id, message="User has been created!"), 201 +@api.route("/rmusers", methods=["POST"]) +@jwt_required +def remove_user(): + identity = get_jwt_identity() + request_user = User.query.filter_by(username=identity["username"]).first() + is_admin = True if request_user.role.role == "admin" else False + if is_admin == False: + return jsonify(message="Unauthorized access!"), 401 + + if not request.is_json: + return jsonify(message="Missing JSON in request"), 400 + + try: + del_user_id = request.json['rmuserId'] + del_user = User.query.filter_by(id=del_user_id).first() + if del_user.id == request_user.id: + app.logger.error("Cannot delete self") + return jsonify(message="Cannot delete self!"), 500 + + if del_user.is_deleted: + app.logger.error("This user had already been deleted") + return jsonify(message="User not found"), 404 + + del_user.is_deleted = sa.func.now() + db.session.commit() + except Exception as e: + app.logger.error("Error deleting user") + app.logger.error(e) + return jsonify(message="Error deleting user!"), 500 + + return jsonify(user_id=request_user.id, name=request_user.username, message="User has been deleted!"), 201 + + + @api.route("/users/", methods=["GET"]) @jwt_required def fetch_user(user_id): @@ -77,6 +111,9 @@ def fetch_user(user_id): try: user = User.query.get(user_id) + # user = User.query.filter(User.id == user_id) + # user = User.query.filter(User.id == user_id, User.is_deleted != None) + except Exception as e: app.logger.error(f"No user exists with user_id: {user_id}") app.logger.error(e) @@ -159,6 +196,7 @@ def fetch_all_users(): try: users = User.query.all() + # users = User.query.not_deleted().all() response = list( [ { diff --git a/frontend/src/containers/forms/deleteDataForm.js b/frontend/src/containers/forms/deleteDataForm.js new file mode 100644 index 0000000..aa32aa3 --- /dev/null +++ b/frontend/src/containers/forms/deleteDataForm.js @@ -0,0 +1,114 @@ +import React from "react"; +import axios from "axios"; +import { withRouter } from "react-router"; +import { withStore } from "@spyna/react-store"; + +import Alert from "../../components/alert"; +import { Button } from "../../components/button"; + +class DeleteDataform extends React.Component { + constructor(props) { + super(props); + + const dataId = this.props.dataId; + const projectId = this.props.projectId; + + this.initialState = { + dataId, + projectId, + message: `Are you sure you want to delete the datapoint ${dataId}`, + refreshPath: `/projects/${projectId}/data`, + errorMessage: "", + successMessage: "", + isSubmitting: false, + }; + + this.state = Object.assign({}, this.initialState); + } + + resetState() { + this.setState(this.initialState); + } + + refreshPage() { + const { history } = this.props; + const { refreshPath } = this.state; + history.replace({ pathname: "/empty" }); + setTimeout(() => { + history.replace({ + pathname: refreshPath, + }); + }); + } + + handleUserDelete(e, dataId, projectId) { + console.log("The data to be deleted will be:", dataId, " from the project: ", projectId); + axios({ + method: "post", + url: "/api/rmdata", + data: { + dataId, + projectId + }, + }) + .then((response) => { + if (response.status === 201) { + this.setState({ successMessage: response.data.message }); + } + this.refreshPage() + }) + .catch((error) => { + console.log(error.response); + this.setState({ + errorMessage: error.response.data.message, + successMessage: "", + isSubmitting: false, + }); + }); + + } + + render() { + const { + dataId, + projectId, + message, + isSubmitting, + errorMessage, + successMessage, + } = this.state; + return ( +
+
+
(this.form = el)} + > + {errorMessage ? ( + + ) : null} + {successMessage ? ( + + ) : null} +
{message}
+
+
+
+
+ +
+
+ ); + } +} + +export default withStore(withRouter(DeleteDataform)); diff --git a/frontend/src/containers/forms/deleteLabelFrom.js b/frontend/src/containers/forms/deleteLabelFrom.js new file mode 100644 index 0000000..da417bf --- /dev/null +++ b/frontend/src/containers/forms/deleteLabelFrom.js @@ -0,0 +1,113 @@ +import React from "react"; +import axios from "axios"; +import { withRouter } from "react-router"; +import { withStore } from "@spyna/react-store"; + +import Alert from "../../components/alert"; +import { Button } from "../../components/button"; + +class DeleteLabelForm extends React.Component { + constructor(props) { + super(props); + + const labelId = this.props.labelId; + const projectId = this.props.projectId; + + this.initialState = { + labelId, + projectId, + message: `Are you sure you want to delete the label: ${labelId}`, + refreshPath: `/projects/${projectId}/labels`, + deleteLabelUrl: `/api/projects/${projectId}/rmlabels`, + errorMessage: "", + successMessage: "", + isSubmitting: false, + }; + + this.state = Object.assign({}, this.initialState); + } + + resetState() { + this.setState(this.initialState); + } + + refreshPage() { + const { history } = this.props; + const { refreshPath } = this.state; + history.replace({ pathname: "/empty" }); + setTimeout(() => { + history.replace({ + pathname: refreshPath, + }); + }); + } + + handleLabelDelete(e) { + const { labelId, deleteLabelUrl } = this.state; + axios({ + method: "DELETE", + url: deleteLabelUrl, + data: { + labelId, + }, + }) + .then((response) => { + if (response.status === 201) { + this.setState({ successMessage: response.data.message }); + } + this.refreshPage() + }) + .catch((error) => { + console.log(error.response); + this.setState({ + errorMessage: error.response.data.message, + successMessage: "", + isSubmitting: false, + }); + }); + + } + + render() { + const { + labelId, + message, + isSubmitting, + errorMessage, + successMessage, + } = this.state; + return ( +
+
+
(this.form = el)} + > + {errorMessage ? ( + + ) : null} + {successMessage ? ( + + ) : null} +
{message}
+
+
+
+
+ +
+
+ ); + } +} + +export default withStore(withRouter(DeleteLabelForm)); diff --git a/frontend/src/containers/forms/deleteLabelValuesFrom.js b/frontend/src/containers/forms/deleteLabelValuesFrom.js new file mode 100644 index 0000000..b680a04 --- /dev/null +++ b/frontend/src/containers/forms/deleteLabelValuesFrom.js @@ -0,0 +1,110 @@ +import React from "react"; +import axios from "axios"; +import { withRouter } from "react-router"; +import { withStore } from "@spyna/react-store"; + +import Alert from "../../components/alert"; +import { Button } from "../../components/button"; + +class DeleteLabelValuesForm extends React.Component { + constructor(props) { + super(props); + + const labelId = this.props.labelId; + const projectId = this.props.projectId; + const labelValueId = this.props.labelValueId; + this.initialState = { + labelId, + projectId, + labelValueId, + message: `Are you sure you want to delete the label: ${labelId}`, + refreshPath: `/labels/${labelId}/values`, + deleteLabelValueUrl: `/api/labels/${labelId}/values/${labelValueId}`, + errorMessage: "", + successMessage: "", + isSubmitting: false, + }; + + this.state = Object.assign({}, this.initialState); + } + + resetState() { + this.setState(this.initialState); + } + + refreshPage() { + const { history } = this.props; + const { refreshPath } = this.state; + history.replace({ pathname: "/empty" }); + setTimeout(() => { + history.replace({ + pathname: refreshPath, + }); + }); + } + + handleLabelDelete(e) { + const { deleteLabelValueUrl } = this.state; + axios({ + method: "DELETE", + url: deleteLabelValueUrl, + }) + .then((response) => { + if (response.status === 201) { + this.setState({ successMessage: response.data.message }); + } + this.refreshPage(); + }) + .catch((error) => { + console.log(error.response); + this.setState({ + errorMessage: error.response.data.message, + successMessage: "", + isSubmitting: false, + }); + }); + } + + render() { + const { + labelId, + message, + isSubmitting, + errorMessage, + successMessage, + } = this.state; + return ( +
+
+
(this.form = el)} + > + {errorMessage ? ( + + ) : null} + {successMessage ? ( + + ) : null} +
{message}
+
+
+
+
+ +
+
+ ); + } +} + +export default withStore(withRouter(DeleteLabelValuesForm)); diff --git a/frontend/src/containers/forms/deleteUserForm.js b/frontend/src/containers/forms/deleteUserForm.js new file mode 100644 index 0000000..ec887d9 --- /dev/null +++ b/frontend/src/containers/forms/deleteUserForm.js @@ -0,0 +1,101 @@ +import React from "react"; +import axios from "axios"; +import { withRouter } from "react-router"; +import { withStore } from "@spyna/react-store"; + +import Alert from "../../components/alert"; +import { Button } from "../../components/button"; + +class DeleteUserForm extends React.Component { + constructor(props) { + super(props); + + const user = this.props.user; + + const userId = user["user_id"]; + const username = user["username"]; + this.initialState = { + userId, + username, + message: `The User to be deleted is "${username}"`, + errorMessage: "", + successMessage: "", + isSubmitting: false, + }; + + this.state = Object.assign({}, this.initialState); + } + + resetState() { + this.setState(this.initialState); + } + + handleUserDelete(e, userId) { + axios({ + method: "post", + url: "/api/rmusers", + data: { + rmuserId: userId, + }, + }) + .then((response) => { + if (response.status === 201) { + console.log("YES"); + this.setState({ successMessage: response.data.message }); + } + // TODO: After the opration revert to the /admin page + // this.refreshPage(); + }) + .catch((error) => { + console.log(error.response); + this.setState({ + errorMessage: error.response.data.message, + successMessage: "", + isSubmitting: false, + }); + }); + } + + render() { + const { + userId, + message, + isSubmitting, + errorMessage, + successMessage, + } = this.state; + return ( +
+
+
(this.form = el)} + > + {errorMessage ? ( + + ) : null} + {successMessage ? ( + + ) : null} +
{message}
+
+
+
+
+ +
+
+ ); + } +} + +export default withStore(withRouter(DeleteUserForm)); diff --git a/frontend/src/containers/modal.js b/frontend/src/containers/modal.js index bab258f..9d8058f 100644 --- a/frontend/src/containers/modal.js +++ b/frontend/src/containers/modal.js @@ -9,6 +9,11 @@ import EditLabelForm from "./forms/editLabelForm"; import ManageUsersProjectForm from "./forms/manageUsersProjectForm"; import CreateLabelValueForm from "./forms/createLabelValuelForm"; import EditLabelValueForm from "./forms/editLabelValueForm"; +import DeleteDataForm from "./forms/deleteDataForm"; +import DeleteUserForm from "./forms/deleteUserForm"; +import DeleteLabelForm from "./forms/deleteLabelFrom"; +import DeleteLabelValuesForm from "./forms/deleteLabelValuesFrom"; + const FormModal = (props) => { return ( @@ -27,6 +32,25 @@ const FormModal = (props) => { {props.formType === "NEW_USER" ? : null} + {props.formType === "DELETE_LABEL" ? ( + + ) : null} + {props.formType === "DELETE_LABEL_VALUE" ? ( + + ) : null} + {props.formType === "DELETE_USER" ? ( + + ) : null} + {props.formType === "DELETE_DATA" ? ( + + ) : null} {props.formType === "NEW_PROJECT" ? : null} {props.formType === "EDIT_USER" ? ( diff --git a/frontend/src/pages/admin.js b/frontend/src/pages/admin.js index 6d65c13..60d1a85 100644 --- a/frontend/src/pages/admin.js +++ b/frontend/src/pages/admin.js @@ -9,6 +9,7 @@ import { faUserPlus, faTags, faDownload, + faTrash, } from "@fortawesome/free-solid-svg-icons"; import { IconButton } from "../components/button"; import Loader from "../components/loader"; @@ -86,6 +87,17 @@ class Admin extends React.Component { this.setState({ formType: "EDIT_USER", title: "Edit User", userId }); } + handleRemoveUser(e, user) { + console.log("We are removing the user: ", user); + this.setModalShow(true); + this.setState({ + formType: "DELETE_USER", + title: "Deleting user", + user: user, + }); + } + + handleAddLabelsToProject(e, projectId) { const { history } = this.props; history.push(`/projects/${projectId}/labels`); @@ -165,6 +177,31 @@ class Admin extends React.Component { }); } + handleDeleteProject(e, projectId) { + console.log("will delete: ", projectId); + axios({ + method: "post", + url: "/api/rmprojects", + data: { + projectId, + }, + }) + .then((response) => { + if (response.status === 201) { + this.setState({ successMessage: response.data.message }); + } + this.refreshPage(); + }) + .catch((error) => { + console.log(error.response); + this.setState({ + errorMessage: error.response.data.message, + successMessage: "", + isSubmitting: false, + }); + }); + } + setModalShow(modalShow) { this.setState({ modalShow }); } @@ -180,6 +217,7 @@ class Admin extends React.Component { formType, userId, projectId, + user, } = this.state; return ( @@ -194,6 +232,7 @@ class Admin extends React.Component { title={title} show={modalShow} userId={userId} + user={user} projectId={projectId} onHide={() => this.setModalShow(false)} /> @@ -272,6 +311,17 @@ class Admin extends React.Component { ) } /> + + this.handleDeleteProject( + e, + project["project_id"] + ) + } + /> ); @@ -330,6 +380,14 @@ class Admin extends React.Component { this.handleEditUser(e, user["user_id"]) } /> + + this.handleRemoveUser(e, user) + } + /> {/* { @@ -347,11 +354,18 @@ class Annotate extends React.Component { }); } + handleDeleteData(e, dataId) { + this.setModalShow(true); + } + render() { const { zoom, isPlaying, labels, + modalShow, + dataId, + projectId, isDataLoading, isMarkedForReview, referenceTranscription, @@ -433,6 +447,14 @@ class Annotate extends React.Component { }} /> +
+ this.handleDeleteData(e, dataId)} + /> +
@@ -510,7 +532,7 @@ class Annotate extends React.Component { selectedSegment.data.annotations && selectedSegment.data.annotations[key] && selectedSegment.data.annotations[key][ - "values" + "values" ]) || (value["type"] === "multiselect" ? [] : "") } @@ -579,6 +601,17 @@ class Annotate extends React.Component {
) : null} + {modalShow ? ( + this.refreshPage()} + formType="DELETE_DATA" + title="Deleting data" + dataId={dataId} + projectId={projectId} + show={modalShow} + onHide={() => this.setModalShow(false)} + /> + ) : null} diff --git a/frontend/src/pages/labelValues.js b/frontend/src/pages/labelValues.js index bbed176..140c8ae 100644 --- a/frontend/src/pages/labelValues.js +++ b/frontend/src/pages/labelValues.js @@ -3,7 +3,7 @@ import React from "react"; import { withRouter } from "react-router-dom"; import { Helmet } from "react-helmet"; -import { faPlusSquare, faEdit } from "@fortawesome/free-solid-svg-icons"; +import { faPlusSquare, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons"; import { IconButton } from "../components/button"; import Loader from "../components/loader"; @@ -66,6 +66,18 @@ class LabelValues extends React.Component { }); } + handleDeleteLabelValue(e, labelId, labelValueId) { + const { projectId } = this.state; + console.log("will delete: ", labelId, labelValueId, projectId); + this.setModalShow(true); + this.setState({ + formType: "DELETE_LABEL_VALUE", + title: "Deleting Lavel Value", + labelId, + labelValueId + }); + } + refreshPage() { const { history } = this.props; const { labelValuesUrl } = this.state; @@ -160,6 +172,18 @@ class LabelValues extends React.Component { ) } /> + + this.handleDeleteLabelValue( + e, + labelId, + labelValue["value_id"] + ) + } + /> ); diff --git a/frontend/src/pages/labels.js b/frontend/src/pages/labels.js index 8cc1f64..f87a143 100644 --- a/frontend/src/pages/labels.js +++ b/frontend/src/pages/labels.js @@ -7,6 +7,7 @@ import { faPlusSquare, faEdit, faList, + faTrashAlt, } from "@fortawesome/free-solid-svg-icons"; import { IconButton } from "../components/button"; @@ -74,6 +75,16 @@ class Labels extends React.Component { history.push(`/labels/${labelId}/values`); } + handleDeleteLabel(e, labelId) { + console.log("We are removing the user: ", labelId); + this.setModalShow(true); + this.setState({ + formType: "DELETE_LABEL", + title: "Deleting label", + labelId, + }); + } + refreshPage() { const { history } = this.props; const { labelsUrl } = this.state; @@ -173,6 +184,14 @@ class Labels extends React.Component { ) } /> + + this.handleDeleteLabel(e, label["label_id"]) + } + /> );