-
+ {!props.loading ? (
+
+
+
+ ) : (
-
+ )}
{wStats && tStats && (
@@ -70,44 +103,19 @@ function TaskHeader(props: PropsType) {
)}
- {wStats.total_count !== null ? (
- <>
- {wStats.approved_count} (
- {toPercent(wStats.total_count, wStats.approved_count)}%)
- >
- ) : (
- <>
- --
- >
- )}
+
|
- {wStats.total_count !== null ? (
- <>
- {wStats.soft_rejected_count} (
- {toPercent(
- wStats.total_count,
- wStats.soft_rejected_count
- )}
- %)
- >
- ) : (
- <>
- --
- >
- )}
+
|
- {wStats.total_count !== null ? (
- <>
- {wStats.rejected_count} (
- {toPercent(wStats.total_count, wStats.rejected_count)}%)
- >
- ) : (
- <>
- --
- >
- )}
+
|
@@ -126,44 +134,19 @@ function TaskHeader(props: PropsType) {
)}
- {tStats.total_count !== null ? (
- <>
- {tStats.approved_count} (
- {toPercent(tStats.total_count, tStats.approved_count)}%)
- >
- ) : (
- <>
- --
- >
- )}
+
|
- {tStats.total_count !== null ? (
- <>
- {tStats.soft_rejected_count} (
- {toPercent(
- tStats.total_count,
- tStats.soft_rejected_count
- )}
- %)
- >
- ) : (
- <>
- --
- >
- )}
+
|
- {tStats.total_count !== null ? (
- <>
- {tStats.rejected_count} (
- {toPercent(tStats.total_count, tStats.rejected_count)}%)
- >
- ) : (
- <>
- --
- >
- )}
+
|
diff --git a/mephisto/review_app/client/src/pages/TaskPage/TaskPage.css b/mephisto/review_app/client/src/pages/TaskPage/TaskPage.css
index ea1b44afe..5b3b71c37 100644
--- a/mephisto/review_app/client/src/pages/TaskPage/TaskPage.css
+++ b/mephisto/review_app/client/src/pages/TaskPage/TaskPage.css
@@ -5,28 +5,33 @@
*/
/* Buttons */
-.task .buttons {
+.task .review-board {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
- padding: 10px;
+ padding: 10px 20px;
background-color: #ecdadf;
display: flex;
- gap: 10px;
}
-/* Content */
-.task .content {
+.task .review-board .left-side {
+ flex: 0 50%;
+}
+
+.task .review-board .left-side .review-controls {
display: flex;
- flex-direction: column;
- padding: 20px;
+ flex-direction: row;
+ gap: 10px;
}
-.task .content pre {
- white-space: normal;
+.task .review-board .right-side {
+ flex: 0 50%;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
}
-.task .content .info {
+.task .review-board .right-side .info {
display: flex;
flex-direction: row;
gap: 20px;
@@ -34,12 +39,26 @@
color: #ccc;
}
-.task .content .info .info-title {
- color: #ccc;
+.task .review-board .right-side .info .grey {
+ color: #999;
}
-.task .content .question {
- margin-top: 20px;
+.task .review-board .right-side .info .black {
+ color: black;
+}
+
+/* Content */
+.task .content {
+ display: flex;
+ flex-direction: column;
+ padding: 20px;
+}
+
+.task .content pre {
+ white-space: normal;
+}
+
+.task .content .unit-preview-container {
position: relative;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
@@ -47,29 +66,15 @@
cursor: default;
}
-.task .content .question .images {
- position: absolute;
- display: flex;
- flex-direction: row;
- bottom: 29px;
- left: 0;
- background: none;
- z-index: 999;
- height: 393px;
-}
-
-.task .content .question .images img {
- float: left;
- padding: 6px;
- width: 262px;
- height: 262px;
- cursor: default;
-}
-
.task .content .results {
- margin-top: 20px;
+ margin-bottom: 20px;
background-color: #f5f5f5;
padding: 10px 30px;
+ border-radius: 5px;
+}
+
+.task .content .results:hover {
+ background-color: #eeeeee;
}
.task .content .results .results-header {
@@ -80,8 +85,9 @@
display: inline-block;
margin-left: 10px;
font-style: normal;
- font-size: 50px;
- line-height: 1;
+ font-size: 40px;
+ line-height: 0.5;
+
}
.task .content .results .results-closed{
@@ -113,12 +119,14 @@
justify-content: center;
}
-.task-iframe {
+.unit-preview-iframe {
width: 100%;
}
.json-pretty .__json-pretty__ {
white-space: pre !important;
+ border-top: 1px solid #cccccc;
+ padding-top: 15px;
}
.json-pretty .__json-pretty__ .__json-key__ {
diff --git a/mephisto/review_app/client/src/pages/TaskPage/TaskPage.tsx b/mephisto/review_app/client/src/pages/TaskPage/TaskPage.tsx
index af0ef8be6..35e3e0af3 100644
--- a/mephisto/review_app/client/src/pages/TaskPage/TaskPage.tsx
+++ b/mephisto/review_app/client/src/pages/TaskPage/TaskPage.tsx
@@ -104,8 +104,9 @@ function TaskPage(props: PropsType) {
const [unitInputsIsJSON, setUnitInputsIsJSON] = React.useState(false);
const [unitResultsIsJSON, setUnitResultsIsJSON] = React.useState(false);
- const [inputsVisibility, setInputsVisibility] = React.useState(false);
- const [resultsVisibility, setResultsVisibility] = React.useState(false);
+ // Allow `null` state so that non-null values persist between task units
+ const [inputsVisibility, setInputsVisibility] = React.useState(null);
+ const [resultsVisibility, setResultsVisibility] = React.useState(null);
window.onmessage = function (e) {
if (
@@ -118,9 +119,7 @@ function TaskPage(props: PropsType) {
}
};
- const onGetTaskWorkerUnitsIdsSuccess = (
- workerUnitsIds: WorkerUnitIdType[]
- ) => {
+ const onGetTaskWorkerUnitsIdsSuccess = (workerUnitsIds: WorkerUnitIdType[]) => {
setWorkerUnits(() => {
const workerUnitsMap = {};
@@ -355,22 +354,22 @@ function TaskPage(props: PropsType) {
// [RECEIVING WIDGET DATA]
// ---
- const sendDataToTaskIframe = (data: object) => {
+ const sendDataToUnitIframe = (data: object) => {
const reviewData = {
REVIEW_DATA: {
inputs: data["prepared_inputs"],
outputs: data["outputs"],
},
};
- const taskIframe = iframeRef.current;
- taskIframe.contentWindow.postMessage(JSON.stringify(reviewData), "*");
+ const unitIframe = iframeRef.current;
+ unitIframe.contentWindow.postMessage(JSON.stringify(reviewData), "*");
};
// ---
// Effects
useEffect(() => {
// Set default title
- setPageTitle("Mephisto - Task Review - Task");
+ setPageTitle("Mephisto - Task Review - Current Task");
setFinishedTask(false);
if (task === null) {
@@ -466,7 +465,7 @@ function TaskPage(props: PropsType) {
// ---
useEffect(() => {
if (iframeLoaded && currentUnitDetails?.has_task_source_review) {
- sendDataToTaskIframe(currentUnitDetails);
+ sendDataToUnitIframe(currentUnitDetails);
}
}, [currentUnitDetails, iframeLoaded]);
// ---
@@ -483,6 +482,18 @@ function TaskPage(props: PropsType) {
if (typeof unitOutputs === "object") {
setUnitResultsIsJSON(true);
}
+
+ // If Task expressly does not provide a preview template,
+ // we just simply show JSON data for the Unit.
+ // Change values only one time on loading page to save user choice
+ if (currentUnitDetails.has_task_source_review === false) {
+ if (inputsVisibility === null) {
+ setInputsVisibility(false);
+ }
+ if (resultsVisibility === null) {
+ setResultsVisibility(true);
+ }
+ }
}
}, [currentUnitDetails]);
@@ -496,26 +507,66 @@ function TaskPage(props: PropsType) {
taskStats={taskStats}
workerStats={workerStats}
workerId={unitsOnReview ? currentWorkerOnReview : null}
+ loading={loading}
/>
-
- {!finishedTask ? (
- <>
-
-
-
- >
- ) : (
-
- No units left for this task. Redirecting to the list of tasks.
-
- )}
+
+
+ {!finishedTask ? (
+
+
+
+
+
+ ) : (
+
+ No unreviewed units left for this task.
+ Redirecting to the list of tasks.
+
+ )}
+
+
+
+ {/* Unit info */}
+ {unitDetails && currentUnitOnReview && (
+
+ {currentUnitDetails && (
+ <>
+
+ Task ID: {currentUnitDetails.task_id}
+
+
+ Worker ID: {currentUnitDetails.worker_id}
+
+
+ Unit ID: {currentUnitDetails.id}
+
+ >
+ )}
+
+ )}
+
@@ -528,29 +579,20 @@ function TaskPage(props: PropsType) {
)}
- {/* Unit info */}
- {unitDetails && currentUnitOnReview && (
-
- {currentUnitDetails && (
- <>
-
Unit ID: {currentUnitDetails.id}
-
Task ID: {currentUnitDetails.task_id}
-
Worker ID: {currentUnitDetails.worker_id}
- >
- )}
-
- )}
-
{currentUnitDetails?.inputs && (
<>
- {/* Initial parameters */}
+ {/* Initial Unit parameters */}
-
setInputsVisibility(!inputsVisibility)}>
- Initial Parameters
+ setInputsVisibility(!inputsVisibility)}
+ title={"Toggle initial Unit parameters data"}
+ >
+ Initial Parameters
{inputsVisibility ? <>▾> : <>▸>}
-
+
{unitInputsIsJSON ? (
@@ -573,12 +615,16 @@ function TaskPage(props: PropsType) {
<>
{/* Results */}
-
setResultsVisibility(!resultsVisibility)}>
- Results
+ setResultsVisibility(!resultsVisibility)}
+ title={"Toggle Unit results data"}
+ >
+ Results
{resultsVisibility ? <>▾> : <>▸>}
-
+
{unitResultsIsJSON ? (
@@ -595,23 +641,18 @@ function TaskPage(props: PropsType) {
- {/* Task info */}
-
e.preventDefault()}>
- {currentUnitDetails.has_task_source_review ? (
+ {/* Completed Unit preview */}
+
e.preventDefault()}>
+ {currentUnitDetails.has_task_source_review && (
>
diff --git a/mephisto/review_app/client/src/pages/TasksPage/TasksPage.tsx b/mephisto/review_app/client/src/pages/TasksPage/TasksPage.tsx
index b5194fed2..b385dbbf9 100644
--- a/mephisto/review_app/client/src/pages/TasksPage/TasksPage.tsx
+++ b/mephisto/review_app/client/src/pages/TasksPage/TasksPage.tsx
@@ -100,7 +100,7 @@ function TasksPage(props: PropsType) {
Date
- Export task results
+ Export results
|
|
@@ -109,7 +109,7 @@ function TasksPage(props: PropsType) {
{tasks &&
tasks.map((task: TaskType, index) => {
const date = moment(task.created_at).format("MMM D, YYYY");
- const nonClickable = task.is_reviewed || task.unit_count == 0;
+ const nonClickable = task.is_reviewed || task.unit_count === 0;
return (
dict:
"""Approve worker's input"""
- data: dict = request.json
- unit_ids: Optional[str] = data.get("unit_ids") if data else None
- review_note: Optional[str] = data.get("review_note") if data else None
- bonus: Optional[Union[int, float]] = data.get("bonus") if data else None
- send_to_worker: Optional[bool] = data.get("send_to_worker", False) if data else False
+ data: dict = request.json or {}
+ unit_ids: Optional[str] = data.get("unit_ids")
+ review_note: Optional[str] = data.get("review_note")
+ bonus: Optional[Union[int, float]] = data.get("bonus")
+ send_to_worker: Optional[bool] = data.get("send_to_worker", False)
# Validate params
if not unit_ids:
diff --git a/mephisto/review_app/server/api/views/units_reject_view.py b/mephisto/review_app/server/api/views/units_reject_view.py
index 1c8c4d1c4..f03a87bff 100644
--- a/mephisto/review_app/server/api/views/units_reject_view.py
+++ b/mephisto/review_app/server/api/views/units_reject_view.py
@@ -12,15 +12,17 @@
from werkzeug.exceptions import BadRequest
from mephisto.data_model.unit import Unit
+from mephisto.data_model.worker import Worker
class UnitsRejectView(MethodView):
def post(self) -> dict:
"""Reject worker's result"""
- data: dict = request.json
- unit_ids: Optional[str] = data and data.get("unit_ids")
- review_note: Optional[str] = data.get("review_note") if data else None
+ data: dict = request.json or {}
+ unit_ids: Optional[str] = data.get("unit_ids")
+ review_note: Optional[str] = data.get("review_note")
+ send_to_worker: Optional[bool] = data.get("send_to_worker", False)
# Validate params
if not unit_ids:
@@ -29,6 +31,7 @@ def post(self) -> dict:
# Reject units
for unit_id in unit_ids:
unit: Unit = Unit.get(app.db, str(unit_id))
+ worker: Worker = Worker.get(app.db, str(unit.worker_id))
agent = unit.get_assigned_agent()
if not agent:
@@ -41,4 +44,7 @@ def post(self) -> dict:
unit.get_status() # Update status immediately for other EPs as this method affects DB
+ if review_note and send_to_worker:
+ worker.send_feedback_message(text=review_note, unit=unit)
+
return {}
diff --git a/mephisto/review_app/server/api/views/units_soft_reject_view.py b/mephisto/review_app/server/api/views/units_soft_reject_view.py
index f80feddfd..05b151c99 100644
--- a/mephisto/review_app/server/api/views/units_soft_reject_view.py
+++ b/mephisto/review_app/server/api/views/units_soft_reject_view.py
@@ -12,15 +12,17 @@
from werkzeug.exceptions import BadRequest
from mephisto.data_model.unit import Unit
+from mephisto.data_model.worker import Worker
class UnitsSoftRejectView(MethodView):
def post(self) -> dict:
"""Soft-reject worker's result"""
- data: dict = request.json
- unit_ids: Optional[str] = data and data.get("unit_ids")
- review_note: Optional[str] = data.get("review_note") if data else None
+ data: dict = request.json or {}
+ unit_ids: Optional[str] = data.get("unit_ids")
+ review_note: Optional[str] = data.get("review_note")
+ send_to_worker: Optional[bool] = data.get("send_to_worker", False)
# Validate params
if not unit_ids:
@@ -29,6 +31,7 @@ def post(self) -> dict:
# Soft Reject units
for unit_id in unit_ids:
unit: Unit = Unit.get(app.db, str(unit_id))
+ worker: Worker = Worker.get(app.db, str(unit.worker_id))
agent = unit.get_assigned_agent()
if not agent:
@@ -41,4 +44,7 @@ def post(self) -> dict:
unit.get_status() # Update status immediately for other EPs as this method affects DB
+ if review_note and send_to_worker:
+ worker.send_feedback_message(text=review_note, unit=unit)
+
return {}
diff --git a/packages/react-form-composer/src/FormComposer/FormComposer.css b/packages/react-form-composer/src/FormComposer/FormComposer.css
index abc696a50..fb70cd2d4 100644
--- a/packages/react-form-composer/src/FormComposer/FormComposer.css
+++ b/packages/react-form-composer/src/FormComposer/FormComposer.css
@@ -4,6 +4,14 @@
* LICENSE file in the root directory of this source tree.
*/
+/* --- Default content styles --- */
+img,
+video {
+ max-width: 100%;
+ max-height: 500px;
+ display: block;
+ margin: 20px 0;
+}
/* --- Form --- */
.form-composer {