diff --git a/backend/migrations/versions/7726477fb5da_.py b/backend/migrations/versions/7726477fb5da_.py new file mode 100644 index 0000000..6dd045e --- /dev/null +++ b/backend/migrations/versions/7726477fb5da_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: 7726477fb5da +Revises: b60bb67d1758 +Create Date: 2021-04-25 20:46:35.627535 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '7726477fb5da' +down_revision = 'b60bb67d1758' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('data', sa.Column('tracked_time', sa.Integer(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('data', 'tracked_time') + # ### end Alembic commands ### diff --git a/backend/models.py b/backend/models.py index 710a880..5c9bf95 100644 --- a/backend/models.py +++ b/backend/models.py @@ -46,6 +46,7 @@ class Data(db.Model): __tablename__ = "data" id = db.Column("id", db.Integer(), primary_key=True) + tracked_time = db.Column("tracked_time", db.Integer(), default=0) project_id = db.Column( "project_id", db.Integer(), db.ForeignKey("project.id"), nullable=False @@ -281,7 +282,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): diff --git a/backend/routes/projects.py b/backend/routes/projects.py index a21f6c9..6c675df 100644 --- a/backend/routes/projects.py +++ b/backend/routes/projects.py @@ -455,6 +455,7 @@ def get_segmentations_for_data(project_id, data_id): response = { "filename": data.filename, + "tracked_time": data.tracked_time, "original_filename": data.original_filename, "reference_transcription": data.reference_transcription, "is_marked_for_review": data.is_marked_for_review, @@ -478,6 +479,7 @@ def update_data(project_id, data_id): return jsonify(message="Missing JSON in request"), 400 is_marked_for_review = bool(request.json.get("is_marked_for_review", False)) + tracked_time = int(request.json.get("tracked_time", 0)) try: request_user = User.query.filter_by(username=identity["username"]).first() @@ -492,7 +494,8 @@ def update_data(project_id, data_id): return jsonify(message="Unauthorized access!"), 401 data.update_marked_review(is_marked_for_review) - + if tracked_time: + data.tracked_time = tracked_time db.session.add(data) db.session.commit() db.session.refresh(data) diff --git a/examples/upload_data/upload_data.py b/examples/upload_data/upload_data.py index ff7f07f..a62d77d 100644 --- a/examples/upload_data/upload_data.py +++ b/examples/upload_data/upload_data.py @@ -65,6 +65,7 @@ "reference_transcription": reference_transcription, "username": username, "segmentations": segmentations, + "tracked_time": 0, "is_marked_for_review": is_marked_for_review, } diff --git a/frontend/src/pages/annotate.js b/frontend/src/pages/annotate.js index b887110..29e8f76 100644 --- a/frontend/src/pages/annotate.js +++ b/frontend/src/pages/annotate.js @@ -29,6 +29,7 @@ class Annotate extends React.Component { this.state = { isPlaying: false, projectId, + localTrackedTime: 0, dataId, labels: {}, labelsUrl: `/api/projects/${projectId}/labels`, @@ -49,7 +50,47 @@ class Annotate extends React.Component { this.transcription = null; } + handleTrackTimeChange() { + axios({ + method: "patch", + url: this.state.dataUrl, + data: { + tracked_time: this.state.localTrackedTime, + }, + }) + .then(() => { + this.setState({ + successMessage: "Tracked Time updated succesfully", + }); + }) + .catch((error) => { + console.log(error); + this.setState({ + errorMessage: "Tracked Time not updated", + }); + }); + } + + // fired, when changing to an internal page + componentWillUnmount() { + clearInterval(this.interval); + this.handleTrackTimeChange(); + } + componentDidMount() { + // anytime changed to another page + window.onbeforeunload = function () { + this.handleTrackTimeChange(); + }.bind(this); + + this.interval = setInterval( + () => + this.setState({ + localTrackedTime: this.state.localTrackedTime + 1000, + }), + 1000 + ); + const { labelsUrl, dataUrl } = this.state; this.setState({ isDataLoading: true }); const wavesurfer = WaveSurfer.create({ @@ -108,6 +149,7 @@ class Annotate extends React.Component { is_marked_for_review, segmentations, filename, + tracked_time, } = response[1].data; const regions = segmentations.map((segmentation) => { @@ -121,11 +163,16 @@ class Annotate extends React.Component { }, }; }); - + if (tracked_time) { + this.setState({ + localTrackedTime: this.state.localTrackedTime + tracked_time, + }); + } this.setState({ isDataLoading: false, referenceTranscription: reference_transcription, isMarkedForReview: is_marked_for_review, + localTrackedTime: tracked_time, filename, }); @@ -352,6 +399,7 @@ class Annotate extends React.Component { zoom, isPlaying, labels, + localTrackedTime, isDataLoading, isMarkedForReview, referenceTranscription, @@ -510,7 +558,7 @@ class Annotate extends React.Component { selectedSegment.data.annotations && selectedSegment.data.annotations[key] && selectedSegment.data.annotations[key][ - "values" + "values" ]) || (value["type"] === "multiselect" ? [] : "") } @@ -575,6 +623,10 @@ class Annotate extends React.Component { > Mark for review +

+