Skip to content

Commit

Permalink
[Form Builder] Fixes for Task Review app
Browse files Browse the repository at this point in the history
  • Loading branch information
meta-paul committed Feb 15, 2024
1 parent c6932b2 commit f963a9d
Show file tree
Hide file tree
Showing 18 changed files with 298 additions and 218 deletions.
17 changes: 9 additions & 8 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,16 @@ This example builds further upon the Dynamic form example. Here we use presigned
Putting it altogether, let's prepare and launch a task featuring a form containing one embedded file plus a few other fields. Here we'll assume working in directory `/mephisto/examples/form_composer_demo/data/dynamic_presigned_urls`.

- Adjust `dynamic_presigned_urls_example_ec2_prolific.yaml` task config as needed
- Create `form_config.json` file to determine your form fields and layout
- Create `form_config.json` file to define your form fields and layout
- it should contain a token named `file_location`
- for more details see `mephisto/generators/form_composer/README.md`
- Create `separate_token_values_config.json` with desired token values (except embedded files)
- Remove content of folder `/tmp` (if you didn't shut the previous Task run correctly)
- Create `separate_token_values_config.json` with desired token values
- Specify your AWS credentials
- Create file `docker/aws_credentials` and populate it with AWS keys info
- Populate your AWS credentials into `docker/envs/env.local` file
- Stand up docker containers: `docker-compose -f docker/docker-compose.local.vscode.yml up`
- Create file `docker/aws_credentials` and populate it with AWS keys info (for infrastructure and Mturk)
- Populate your AWS credentials into `docker/envs/env.local` file (for S3 access)
- Clone file `docker/docker-compose.dev.yml` as `docker/docker-compose.local.yml`, and point its `env_file` to `envs/env.local`
- Remove content of folder `/tmp` (if you didn't shut the previous Task run correctly)
- Launch docker containers: `docker-compose -f docker/docker-compose.local.yml up`
- SSH into the running container: `docker exec -it mephisto_dc bash`
- Generate your task data config with these commands:
```shell
Expand All @@ -176,9 +177,9 @@ Putting it altogether, let's prepare and launch a task featuring a form containi
```
- Launch your task:
```shell
cd /mephisto/examples/form_composer_demo && python run_task_dynamic_ec2_prolific.py
cd /mephisto/examples/form_composer_demo && python run_task_dynamic_presigned_urls_ec2_prolific.py
```
- After the Task is completed by all workers, launch task review app (for more details see `mephisto/review_app/README.md`):
- After the Task is completed by all workers, launch task review app and acces it at [http://localhost:8081](http://localhost:8081) (for more details see `mephisto/review_app/README.md`):
```shell
mephisto review_app -h 0.0.0.0 -p 8000 -d True -f True
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ def generate_preview_html():
try:
with open(data_config_path) as data_config_file:
data_config_data = json.load(data_config_file)
except (JSONDecodeError, TypeError):
print(f"Could not read JSON from '{data_config_path}' file")
except (JSONDecodeError, TypeError) as e:
print(f"Could not read JSON from '{data_config_path}' file: {e}")
raise

first_form_version = data_config_data[0]["form"]
Expand Down
4 changes: 4 additions & 0 deletions examples/form_composer_demo/webapp/src/reviewapp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ function ReviewApp() {
}
window.addEventListener("resize", updateSize);
updateSize();
// HACK: Catch-all resize, if normal resizes failed (e.g. acync long loading images)
setTimeout(() => {
updateSize();
}, 3000);
return () => window.removeEventListener("resize", updateSize);
}, []);

Expand Down
4 changes: 4 additions & 0 deletions examples/remote_procedure/mnist/webapp/src/reviewapp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ function ReviewApp() {
}
window.addEventListener("resize", updateSize);
updateSize();
// HACK: Catch-all resize, if normal resizes failed (e.g. acync long loading images)
setTimeout(() => {
updateSize();
}, 3000);
return () => window.removeEventListener("resize", updateSize);
}, []);

Expand Down
4 changes: 2 additions & 2 deletions mephisto/abstractions/providers/prolific/api/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ def send(cls, study_id: str, recipient_id: str, text: str) -> Message:
study_id=study_id,
)
message.validate()
response_json = cls.post(cls.list_api_endpoint, params=message.to_dict())
return Message(**response_json)
cls.post(cls.list_api_endpoint, params=message.to_dict())
return message
11 changes: 5 additions & 6 deletions mephisto/abstractions/providers/prolific/prolific_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def unblock_worker(self, reason: str, requester: "Requester") -> Tuple[bool, str
logger.debug(f"{self.log_prefix}Task Run: {task_run}")

task_run_args = task_run.args
requester = cast("ProlificRequester", requester)
requester: "ProlificRequester" = cast("ProlificRequester", requester)
client = self._get_client(requester.requester_name)
prolific_utils.unblock_worker(client, task_run_args, self.worker_name, reason)
self.datastore.set_worker_blocked(self.worker_name, is_blocked=False)
Expand All @@ -182,7 +182,7 @@ def unblock_worker(self, reason: str, requester: "Requester") -> Tuple[bool, str
def is_blocked(self, requester: "Requester") -> bool:
"""Determine if a worker is blocked"""
task_run = self._get_first_task_run(requester)
requester = cast("ProlificRequester", requester)
requester: "ProlificRequester" = cast("ProlificRequester", requester)
is_blocked = self.datastore.get_worker_blocked(self.get_prolific_participant_id())

logger.debug(
Expand Down Expand Up @@ -256,7 +256,7 @@ def grant_crowd_qualification(
"""Grant qualification by the given name to this worker"""
logger.debug(f"{self.log_prefix}Granting crowd qualification: {qualification_name}")

requester = cast(
requester: "ProlificRequester" = cast(
"ProlificRequester",
self.db.find_requesters(provider_type=self.provider_type)[-1],
)
Expand Down Expand Up @@ -298,12 +298,11 @@ def revoke_crowd_qualification(self, qualification_name: str) -> None:

def send_feedback_message(self, text: str, unit: "Unit") -> bool:
"""Send feedback message to a worker"""
requester = cast(
requester: "ProlificRequester" = cast(
"ProlificRequester",
self.db.find_requesters(provider_type=self.provider_type)[-1],
)

assert isinstance(requester, ProlificRequester), "Must be an Prolific requester"
# assert isinstance(requester, ProlificRequester), "Must be an Prolific requester"

client = self._get_client(requester.requester_name)
datastore_unit = self.datastore.get_unit(unit.db_id)
Expand Down
4 changes: 2 additions & 2 deletions mephisto/generators/form_composer/config_validation/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ def read_config_file(
try:
with open(config_path) as config_file:
config_data = json.load(config_file)
except (JSONDecodeError, TypeError):
print(f"[red]Could not read JSON from file: '{config_path}'.[/red]")
except (JSONDecodeError, TypeError) as e:
print(f"[red]Could not read JSON from file: '{config_path}': {e}.[/red]")
exit()

return config_data
Expand Down
4 changes: 4 additions & 0 deletions mephisto/generators/form_composer/webapp/src/reviewapp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ function ReviewApp() {
}
window.addEventListener("resize", updateSize);
updateSize();
// HACK: Catch-all resize, if normal resizes failed (e.g. acync long loading images)
setTimeout(() => {
updateSize();
}, 3000);
return () => window.removeEventListener("resize", updateSize);
}, []);

Expand Down
26 changes: 15 additions & 11 deletions mephisto/review_app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,22 @@ docker-compose -f docker/docker-compose.dev.yml run \
```

where
- `--build` - build image before starting container
- `--publish 8081:8000` - docker port mapping, with `8000` being same port as in `-p` param
- `--rm` - automatically remove container when it already exits

- `--build` - builds image before starting container
- `--publish 8081:8000` - maps docker ports, with `8000` being same port as in `-p` option
- `--rm` - automatically removes the previous container if it already exits
- `mephisto_dc` - container name in `docker-compose.dev.yml` file
- `mephisto review_app -h 0.0.0.0 -p 8000 -d True` - launch Mephisto service inside container, where
- `-h/--host` - host where TaskReview app is going to be served
- `-p/--port` - port where TaskReview app is going to be served
- `-d/--debug` - debug mode
- `-f/--force-rebuild` - force rebuild React bundle (use if client code was updated between runs)
- `-s/--skip-build` - skip all installation and building steps for the UI, and directly launch the server

Now open TaskReview app in your browser at [http://localhost:8081](http://localhost:8081).
- `mephisto review_app -h 0.0.0.0 -p 8000 -d True` - launches Mephisto's TaskReview app service inside the container

Command `mephisto review_app` supports the following options:

- `-h/--host` - host where TaskReview app will be served
- `-p/--port` - port where TaskReview app will be served
- `-d/--debug` - run in debug mode (with extra logging)
- `-f/--force-rebuild` - force rebuild React bundle (use if your Task client code has been updated)
- `-s/--skip-build` - skip all installation and building steps for the UI, and directly launch the server (use if no code has been changed)

Now you can access TaskReview app in your browser at [http://localhost:8081](http://localhost:8081).

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
display: flex;
align-items: center;
min-height: 120px;
padding-bottom: 10px;
padding-left: 20px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}

.task-header .logo:hover {
opacity: 0.7;
}

.task-header .logo img {
max-width: 100%;
}
Expand All @@ -32,7 +36,7 @@
}

.task-header .table tr.total td {
color: #a6a6a6;
color: #aaaaaa;
}

.task-header .table tr th.center,
Expand All @@ -46,6 +50,14 @@
line-height: 0.8;
}

.task-header .table td .percentage {
font-size: 85%;
}

.task-header .table tr td:nth-child(1) {
border-right: 1px solid grey;
}

.task-header .table .title b {
display: inline-block;
line-height: 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,60 @@ import logo from "static/images/logo.svg";
import urls from "urls";
import "./TaskHeader.css";

interface PropsType {
interface TaskHeaderPropsType {
loading: boolean;
taskStats?: TaskStatsType;
workerId?: number;
workerStats?: WorkerStatsType;
}

function TaskHeader(props: PropsType) {
const wStats = props.workerStats;
const tStats = props.taskStats;
interface StatCountWithPercentagePropsType {
count: number;
totalCount: number;
}

function StatCountWithPercentage(props: StatCountWithPercentagePropsType) {
const { totalCount, count } = props;

const toPercent = (total: number, value: number): number => {
function toPercent(total: number, value: number): number {
return total !== 0 ? Math.round((value * 100) / total) : 0;
};
}

return (
totalCount !== null ? (
<>
<b>{count}</b>
{" "}
<span className={"percentage"}>
({toPercent(totalCount, count)}%)
</span>
</>
) : (
<>
<b>--</b>
</>
)
);
}

function TaskHeader(props: TaskHeaderPropsType) {
const wStats = props.workerStats;
const tStats = props.taskStats;

return (
<Container className={"task-header"}>
<Row>
<Col className={"logo"} sm={3}>
<Link to={urls.client.tasks}>
{!props.loading ? (
<Link
to={urls.client.tasks}
title={"Go to Tasks list"}
>
<img src={logo} alt="logo" />
</Link>
) : (
<img src={logo} alt="logo" />
</Link>
)}
</Col>
<Col />
{wStats && tStats && (
Expand Down Expand Up @@ -70,44 +103,19 @@ function TaskHeader(props: PropsType) {
)}
</td>
<td className={"center"}>
{wStats.total_count !== null ? (
<>
<b>{wStats.approved_count}</b> (
{toPercent(wStats.total_count, wStats.approved_count)}%)
</>
) : (
<>
<b>--</b>
</>
)}
<StatCountWithPercentage
totalCount={wStats.total_count} count={wStats.approved_count}
/>
</td>
<td className={"center"}>
{wStats.total_count !== null ? (
<>
<b>{wStats.soft_rejected_count}</b> (
{toPercent(
wStats.total_count,
wStats.soft_rejected_count
)}
%)
</>
) : (
<>
<b>--</b>
</>
)}
<StatCountWithPercentage
totalCount={wStats.total_count} count={wStats.soft_rejected_count}
/>
</td>
<td className={"center"}>
{wStats.total_count !== null ? (
<>
<b>{wStats.rejected_count}</b> (
{toPercent(wStats.total_count, wStats.rejected_count)}%)
</>
) : (
<>
<b>--</b>
</>
)}
<StatCountWithPercentage
totalCount={wStats.total_count} count={wStats.rejected_count}
/>
</td>
</tr>

Expand All @@ -126,44 +134,19 @@ function TaskHeader(props: PropsType) {
)}
</td>
<td className={"center"}>
{tStats.total_count !== null ? (
<>
<b>{tStats.approved_count}</b> (
{toPercent(tStats.total_count, tStats.approved_count)}%)
</>
) : (
<>
<b>--</b>
</>
)}
<StatCountWithPercentage
totalCount={tStats.total_count} count={tStats.approved_count}
/>
</td>
<td className={"center"}>
{tStats.total_count !== null ? (
<>
<b>{tStats.soft_rejected_count}</b> (
{toPercent(
tStats.total_count,
tStats.soft_rejected_count
)}
%)
</>
) : (
<>
<b>--</b>
</>
)}
<StatCountWithPercentage
totalCount={tStats.total_count} count={tStats.soft_rejected_count}
/>
</td>
<td className={"center"}>
{tStats.total_count !== null ? (
<>
<b>{tStats.rejected_count}</b> (
{toPercent(tStats.total_count, tStats.rejected_count)}%)
</>
) : (
<>
<b>--</b>
</>
)}
<StatCountWithPercentage
totalCount={tStats.total_count} count={tStats.rejected_count}
/>
</td>
</tr>
</tbody>
Expand Down
Loading

0 comments on commit f963a9d

Please sign in to comment.