Skip to content
This repository was archived by the owner on Apr 3, 2025. It is now read-only.

Commit 5686c28

Browse files
authored
Merge pull request #1100 from facebookresearch/v1.2.1-dev
2 parents aac76d7 + e56b5c0 commit 5686c28

File tree

19 files changed

+266
-87
lines changed

19 files changed

+266
-87
lines changed

examples/README.md

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,71 @@
55
-->
66

77
# Examples
8-
The Mephisto example folders within contain some sample starter code and tasks to demonstrate potential workflows for setting up and working on new tasks.
8+
The Mephisto example folders within contain some sample starter code and tasks to demonstrate potential workflows for setting up and working on new tasks.
9+
10+
Mephisto Tasks can be launched (each run is called TaskRun) with a single `docker-compose` command (you will need to have Docker [installed](https://docs.docker.com/engine/install/).)
11+
12+
Let's launch Mephisto example tasks, starting from the easiest one
13+
14+
---
15+
16+
#### 1. Simple HTML-based task
17+
18+
A simple project with HTML-based UI task template [simple_static_task](/examples/simple_static_task)
19+
20+
- Default config file: [/examples/simple_static_task/hydra_configs/conf/example.yaml]
21+
- Launch command:
22+
```shell
23+
docker-compose -f docker/docker-compose.dev.yml run \
24+
--build \
25+
--publish 3001:3000 \
26+
--rm mephisto_dc \
27+
python /mephisto/examples/simple_static_task/static_test_script.py
28+
```
29+
- Browser page (for the first task unit): [http://localhost:3001/?worker_id=x&assignment_id=1](http://localhost:3001/?worker_id=x&assignment_id=1)
30+
- Browser page should display an image, instruction, select and file inputs, and a submit button.
31+
32+
---
33+
34+
#### 2. Simple React-based task
35+
36+
A simple project with React-based UI task template [static_react_task](/examples/static_react_task)
37+
38+
- Default config file: [example.yaml](/examples/static_react_task/hydra_configs/conf/example.yaml).
39+
- Launch command:
40+
```shell
41+
docker-compose -f docker/docker-compose.dev.yml run \
42+
--build \
43+
--publish 3001:3000 \
44+
--rm mephisto_dc \
45+
python /mephisto/examples/static_react_task/run_task.py
46+
```
47+
- Browser page (for the first task unit): [http://localhost:3001/?worker_id=x&assignment_id=1](http://localhost:3001/?worker_id=x&assignment_id=1).
48+
- Browser page should display an instruction line and two buttons (green and red).
49+
50+
---
51+
52+
#### 3. Task with dynamic input
53+
54+
A more complex example featuring worker-generated dynamic input: [mnist](/examples/remote_procedure/mnist).
55+
56+
- Default config file: [launch_with_local.yaml](/examples/remote_procedure/mnist/hydra_configs/conf/launch_with_local.yaml).
57+
- Launch command:
58+
```shell
59+
docker-compose -f docker/docker-compose.dev.yml run \
60+
--build \
61+
--publish 3001:3000 \
62+
--rm mephisto_dc \
63+
apt install curl && \
64+
pip install grafana torch pillow numpy && \
65+
mephisto metrics install && \
66+
python /mephisto/examples/remote_procedure/mnist/run_task.py
67+
```
68+
- Browser page (for the first task unit): [http://localhost:3001/?worker_id=x&assignment_id=1](http://localhost:3001/?worker_id=x&assignment_id=1).
69+
- Browser page should display instructions and a layout with 3 rectangle fields for drawing numbers with a mouse, each field having inputs at the bottom.
70+
71+
---
72+
73+
# Your Mephisto project
74+
75+
To read on steps for creating your own custom Mephisto task, please refer to README in the main Mephisto repo.

mephisto/abstractions/providers/mock/mock_agent.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@
44
# This source code is licensed under the MIT license found in the
55
# LICENSE file in the root directory of this source tree.
66

7-
from mephisto.data_model.agent import Agent
7+
from typing import Any
8+
from typing import Dict
9+
from typing import Mapping
10+
from typing import Optional
11+
from typing import TYPE_CHECKING
12+
from typing import Union
13+
814
from mephisto.abstractions.blueprint import AgentState
915
from mephisto.abstractions.providers.mock.provider_type import PROVIDER_TYPE
10-
11-
from typing import List, Optional, Tuple, Dict, Mapping, Any, TYPE_CHECKING
16+
from mephisto.data_model.agent import Agent
1217

1318
if TYPE_CHECKING:
1419
from mephisto.data_model.unit import Unit
1520
from mephisto.abstractions.database import MephistoDB
1621
from mephisto.data_model.worker import Worker
17-
from mephisto.data_model.packet import Packet
1822
from mephisto.abstractions.providers.mock.mock_datastore import MockDatastore
1923

2024

@@ -76,7 +80,7 @@ def get_live_update(self, timeout=None) -> Optional[Dict[str, Any]]:
7680
def approve_work(
7781
self,
7882
review_note: Optional[str] = None,
79-
bonus: Optional[str] = None,
83+
bonus: Optional[Union[int, float]] = None,
8084
skip_unit_review: bool = False,
8185
) -> None:
8286
"""

mephisto/abstractions/providers/mock/mock_worker.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,30 @@
44
# This source code is licensed under the MIT license found in the
55
# LICENSE file in the root directory of this source tree.
66

7-
from mephisto.data_model.worker import Worker
7+
from typing import Any
8+
from typing import Mapping
9+
from typing import Optional
10+
from typing import Tuple
11+
from typing import TYPE_CHECKING
12+
813
from mephisto.abstractions.providers.mock.provider_type import PROVIDER_TYPE
9-
from typing import List, Optional, Tuple, Dict, Mapping, Type, Any, TYPE_CHECKING
14+
from mephisto.data_model.worker import Worker
15+
from mephisto.utils.logger_core import get_logger
1016

1117
if TYPE_CHECKING:
1218
from mephisto.abstractions.database import MephistoDB
1319
from mephisto.data_model.task_run import TaskRun
1420
from mephisto.data_model.unit import Unit
15-
from mephisto.data_model.agent import Agent
1621
from mephisto.data_model.requester import Requester
1722
from mephisto.abstractions.providers.mock.mock_datastore import MockDatastore
1823

24+
logger = get_logger(name=__name__)
25+
1926

2027
class MockWorker(Worker):
2128
"""
22-
This class represents an individual - namely a person. It maintains components of ongoing identity for a user.
29+
This class represents an individual - namely a person.
30+
It maintains components of ongoing identity for a user.
2331
"""
2432

2533
def __init__(
@@ -36,6 +44,7 @@ def bonus_worker(
3644
self, amount: float, reason: str, unit: Optional["Unit"] = None
3745
) -> Tuple[bool, str]:
3846
"""Bonus this worker for work any reason. Return success of bonus"""
47+
logger.debug(f"Mock paying bonus to worker. Amount: {amount}. Reason: '{reason}")
3948
return True, ""
4049

4150
def block_worker(
@@ -61,6 +70,11 @@ def is_eligible(self, task_run: "TaskRun") -> bool:
6170
"""Determine if this worker is eligible for the given task run"""
6271
return True
6372

73+
def send_feedback_message(self, text: str, unit: "Unit") -> bool:
74+
"""Send feedback message to a worker"""
75+
logger.debug(f"Mock sending feedback message to worker: '{text}'. Unit: {unit}")
76+
return True
77+
6478
@staticmethod
6579
def new(db: "MephistoDB", worker_id: str) -> "Worker":
6680
return MockWorker._register_worker(db, worker_id + "_sandbox", PROVIDER_TYPE)

mephisto/abstractions/providers/mturk/mturk_agent.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@
44
# This source code is licensed under the MIT license found in the
55
# LICENSE file in the root directory of this source tree.
66

7-
from mephisto.data_model.agent import Agent
8-
from mephisto.abstractions.blueprint import AgentState
9-
from mephisto.abstractions.providers.mturk.provider_type import PROVIDER_TYPE
10-
from mephisto.abstractions.providers.mturk.mturk_utils import (
11-
approve_work,
12-
reject_work,
13-
get_assignment,
14-
get_assignments_for_hit,
15-
)
16-
17-
import xmltodict # type: ignore
187
import json
8+
from typing import Any
9+
from typing import cast
10+
from typing import Dict
11+
from typing import Mapping
12+
from typing import Optional
13+
from typing import TYPE_CHECKING
14+
from typing import Union
1915

20-
from typing import List, Optional, Tuple, Dict, Mapping, Any, cast, TYPE_CHECKING
16+
import xmltodict # type: ignore
2117

18+
from mephisto.abstractions.blueprint import AgentState
19+
from mephisto.abstractions.providers.mturk.mturk_utils import approve_work
20+
from mephisto.abstractions.providers.mturk.mturk_utils import get_assignments_for_hit
21+
from mephisto.abstractions.providers.mturk.mturk_utils import reject_work
22+
from mephisto.abstractions.providers.mturk.provider_type import PROVIDER_TYPE
23+
from mephisto.data_model.agent import Agent
2224
from mephisto.utils.logger_core import get_logger
2325

24-
logger = get_logger(name=__name__)
25-
2626
if TYPE_CHECKING:
2727
from mephisto.data_model.unit import Unit
2828
from mephisto.abstractions.database import MephistoDB
@@ -31,6 +31,8 @@
3131
from mephisto.abstractions.providers.mturk.mturk_unit import MTurkUnit
3232
from mephisto.abstractions.providers.mturk.mturk_datastore import MTurkDatastore
3333

34+
logger = get_logger(name=__name__)
35+
3436

3537
class MTurkAgent(Agent):
3638
"""
@@ -104,7 +106,7 @@ def attempt_to_reconcile_submitted_data(self, mturk_hit_id: str):
104106
def approve_work(
105107
self,
106108
review_note: Optional[str] = None,
107-
bonus: Optional[str] = None,
109+
bonus: Optional[Union[int, float]] = None,
108110
skip_unit_review: bool = False,
109111
) -> None:
110112
"""Approve the work done on this specific Unit"""

mephisto/abstractions/providers/mturk/mturk_worker.py

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,33 @@
44
# This source code is licensed under the MIT license found in the
55
# LICENSE file in the root directory of this source tree.
66

7-
from mephisto.data_model.worker import Worker
8-
from mephisto.data_model.requester import Requester
9-
from mephisto.abstractions.providers.mturk.provider_type import PROVIDER_TYPE
10-
from mephisto.abstractions.providers.mturk.mturk_utils import (
11-
pay_bonus,
12-
block_worker,
13-
unblock_worker,
14-
is_worker_blocked,
15-
give_worker_qualification,
16-
remove_worker_qualification,
17-
)
18-
from mephisto.abstractions.providers.mturk.mturk_requester import MTurkRequester
19-
7+
from typing import Any
8+
from typing import cast
9+
from typing import Mapping
10+
from typing import Optional
11+
from typing import Tuple
12+
from typing import TYPE_CHECKING
2013
from uuid import uuid4
2114

22-
from typing import List, Optional, Tuple, Dict, Mapping, Any, cast, TYPE_CHECKING
15+
from mephisto.abstractions.providers.mturk.mturk_utils import block_worker
16+
from mephisto.abstractions.providers.mturk.mturk_utils import email_worker
17+
from mephisto.abstractions.providers.mturk.mturk_utils import give_worker_qualification
18+
from mephisto.abstractions.providers.mturk.mturk_utils import is_worker_blocked
19+
from mephisto.abstractions.providers.mturk.mturk_utils import pay_bonus
20+
from mephisto.abstractions.providers.mturk.mturk_utils import remove_worker_qualification
21+
from mephisto.abstractions.providers.mturk.mturk_utils import unblock_worker
22+
from mephisto.abstractions.providers.mturk.provider_type import PROVIDER_TYPE
23+
from mephisto.data_model.requester import Requester
24+
from mephisto.data_model.worker import Worker
25+
from mephisto.utils.logger_core import get_logger
2326

2427
if TYPE_CHECKING:
2528
from mephisto.abstractions.providers.mturk.mturk_datastore import MTurkDatastore
2629
from mephisto.abstractions.database import MephistoDB
2730
from mephisto.data_model.task_run import TaskRun
2831
from mephisto.data_model.unit import Unit
29-
from mephisto.abstractions.providers.mturk.mturk_unit import MTurkUnit
3032
from mephisto.abstractions.providers.mturk.mturk_requester import MTurkRequester
3133

32-
from mephisto.utils.logger_core import get_logger
3334

3435
logger = get_logger(name=__name__)
3536

@@ -179,6 +180,27 @@ def is_eligible(self, task_run: "TaskRun") -> bool:
179180
"""
180181
return True
181182

183+
def send_feedback_message(self, text: str, unit: "Unit") -> bool:
184+
"""Send feedback message to a worker"""
185+
requester = cast(
186+
"MTurkRequester",
187+
self.db.find_requesters(provider_type=self.provider_type)[-1],
188+
)
189+
190+
assert isinstance(requester, MTurkRequester), "Must be an MTurk requester"
191+
192+
client = self._get_client(requester._requester_name)
193+
task_name = unit.get_task_run().get_task().task_name
194+
195+
email_worker(
196+
client=client,
197+
worker_id=self.get_mturk_worker_id(),
198+
subject=f'Feedback for your Mturk task "{task_name}"',
199+
message_text=text,
200+
)
201+
202+
return True
203+
182204
@staticmethod
183205
def new(db: "MephistoDB", worker_id: str) -> "Worker":
184206
return MTurkWorker._register_worker(db, worker_id, PROVIDER_TYPE)

mephisto/abstractions/providers/prolific/api/messages.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,13 @@ def list_unread(cls) -> List[Message]:
5252
return messages
5353

5454
@classmethod
55-
def send(cls, **data) -> Message:
55+
def send(cls, study_id: str, recipient_id: str, text: str) -> Message:
5656
"""Send a message to a participant or another researcher"""
57-
message = Message(**data)
57+
message = Message(
58+
body=text,
59+
recipient_id=recipient_id,
60+
study_id=study_id,
61+
)
5862
message.validate()
5963
response_json = cls.post(cls.list_api_endpoint, params=message.to_dict())
6064
return Message(**response_json)

mephisto/abstractions/providers/prolific/prolific_agent.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
from typing import Mapping
1111
from typing import Optional
1212
from typing import TYPE_CHECKING
13+
from typing import Union
1314

1415
from mephisto.abstractions.blueprint import AgentState
1516
from mephisto.abstractions.providers.prolific import prolific_utils
17+
from mephisto.abstractions.providers.prolific.api.client import ProlificClient
18+
from mephisto.abstractions.providers.prolific.api.constants import SubmissionStatus
1619
from mephisto.abstractions.providers.prolific.provider_type import PROVIDER_TYPE
1720
from mephisto.data_model.agent import Agent
1821
from mephisto.utils.logger_core import get_logger
19-
from mephisto.abstractions.providers.prolific.api.client import ProlificClient
20-
from mephisto.abstractions.providers.prolific.api.constants import SubmissionStatus
2122

2223
if TYPE_CHECKING:
2324
from mephisto.abstractions.providers.prolific.prolific_datastore import ProlificDatastore
@@ -103,7 +104,7 @@ def new_from_provider_data(
103104
def approve_work(
104105
self,
105106
review_note: Optional[str] = None,
106-
bonus: Optional[str] = None,
107+
bonus: Optional[Union[int, float]] = None,
107108
skip_unit_review: bool = False,
108109
) -> None:
109110
"""Approve the work done on this specific Unit"""
@@ -139,7 +140,7 @@ def approve_work(
139140
worker_id=unit.worker_id,
140141
status=AgentState.STATUS_APPROVED,
141142
review_note=review_note,
142-
bonus=bonus,
143+
bonus=str(bonus),
143144
)
144145

145146
def soft_reject_work(self, review_note: Optional[str] = None) -> None:

mephisto/abstractions/providers/prolific/prolific_utils.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from .api.base_api_resource import CREDENTIALS_CONFIG_PATH
2929
from .api.client import ProlificClient
3030
from .api.data_models import BonusPayments
31+
from .api.data_models import Message
3132
from .api.data_models import Participant
3233
from .api.data_models import ParticipantGroup
3334
from .api.data_models import Project
@@ -596,7 +597,7 @@ def pay_bonus(
596597
client: ProlificClient,
597598
task_run_config: "DictConfig",
598599
worker_id: str,
599-
bonus_amount: int, # in cents
600+
bonus_amount: Union[int, float], # in cents
600601
study_id: str,
601602
*args,
602603
**kwargs,
@@ -798,3 +799,12 @@ def reject_work(
798799
)
799800

800801
return None
802+
803+
804+
def send_message(client: ProlificClient, study_id: str, participant_id: str, text: str) -> Message:
805+
try:
806+
message: Message = client.Messages.send(study_id, participant_id, text)
807+
except (ProlificException, ValidationError):
808+
logger.exception(f'Could not send message to participant "{participant_id}"')
809+
raise
810+
return message

0 commit comments

Comments
 (0)