Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 29 additions & 19 deletions cmd/study/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,33 @@ If you are using the CLI in other tooling, you may want to silence the returned
output of the study creation, so you can use the "-s" flag.
$ prolific study create -t /path/to/study.json -p -s

An example of a JSON study file, with an ethnicity screener
An example of a JSON study file, with completion codes and filters

{
"name": "Study with a ethnicity screener",
"internal_name": "Study with a ethnicity screener",
"description": "This study will be published to the participants with the selected ethnicity",
"name": "Study with completion codes",
"internal_name": "Study with completion codes",
"description": "This study uses completion codes and a handedness filter set",
"external_study_url": "https://google.com",
"prolific_id_option": "question",
"completion_code": "COMPLE01",
"completion_option": "code",
"prolific_id_option": "url_parameters",
"completion_codes": [
{
"code": "C1234567",
"code_type": "COMPLETED",
"actions": [{"action": "AUTOMATICALLY_APPROVE"}]
},
{
"code": "C7654321",
"code_type": "FAILED_ATTENTION_CHECK",
"actions": [{"action": "MANUALLY_REVIEW"}]
Comment on lines +60 to +61
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command help example uses completion_codes with code_type "FAILED_ATTENTION_CHECK" and action "MANUALLY_REVIEW", but those values don't appear anywhere else in the repo’s documented examples. To avoid users copying an example that the API may reject, consider using code_type/action values already demonstrated in docs/examples (e.g. COMPLETED / FOLLOW_UP_STUDY with AUTOMATICALLY_APPROVE, or align with the new REJECTED/AUTOMATICALLY_REJECT usage in model/study_test.go).

Suggested change
"code_type": "FAILED_ATTENTION_CHECK",
"actions": [{"action": "MANUALLY_REVIEW"}]
"code_type": "REJECTED",
"actions": [{"action": "AUTOMATICALLY_REJECT"}]

Copilot uses AI. Check for mistakes.
}
],
"total_available_places": 10,
"estimated_completion_time": 10,
"maximum_allowed_time": 10,
"reward": 400,
"device_compatibility": ["desktop", "tablet", "mobile"],
"peripheral_requirements": ["audio", "camera", "download", "microphone"],
"eligibility_requirements": [
{
"attributes": [{ "index": 3, "value": true }],
"query": { "id": "5950c8413e9d730001924f2a" },
"_cls": "web.eligibility.models.SelectAnswerEligibilityRequirement"
}
],
"credential_pool_id": "64a1b2c3d4e5f6a7b8c9d0e1_12345678-1234-11e0-8000-0a1b2c3d4e5f"
"filter_set_id": "handedness"
}

An example of a YAML study file
Expand All @@ -75,10 +78,17 @@ internal_name: Standard sample
description: This is my first standard sample study on the Prolific system.
external_study_url: https://eggs-experriment.com
# Enum: "question", "url_parameters" (Recommended), "not_required"
prolific_id_option: question
completion_code: COMPLE01
# Enum: "url", "code"
completion_option: code
prolific_id_option: url_parameters
# New completion_codes array format (recommended)
completion_codes:
- code: C1234567
code_type: COMPLETED
actions:
- action: AUTOMATICALLY_APPROVE
- code: C7654321
code_type: FAILED_ATTENTION_CHECK
actions:
- action: MANUALLY_REVIEW
total_available_places: 10
# In minutes
estimated_completion_time: 10
Expand Down
32 changes: 22 additions & 10 deletions cmd/study/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,36 @@ import (
)

var studyTemplate = model.CreateStudy{
Name: "My first standard sample",
InternalName: "Standard sample",
Description: "This is my first standard sample study on the Prolific system.",
ExternalStudyURL: "https://eggs-experriment.com?participant={{%PROLIFIC_PID%}}",
ProlificIDOption: "url_parameters",
CompletionCode: "COMPLE01",
Name: "My first standard sample",
InternalName: "Standard sample",
Description: "This is my first standard sample study on the Prolific system.",
ExternalStudyURL: "https://eggs-experriment.com?participant={{%PROLIFIC_PID%}}",
ProlificIDOption: "url_parameters",
CompletionCodes: []model.CompletionCode{
{
Code: "COMPLE01",
CodeType: "COMPLETED",
Actions: []model.CompletionCodeAction{
{
Action: "AUTOMATICALLY_APPROVE",
},
},
},
},
TotalAvailablePlaces: 10,
EstimatedCompletionTime: 10,
MaximumAllowedTime: 10,
MaximumAllowedTime: 100,
Reward: 400,
DeviceCompatibility: []string{"desktop", "tablet", "mobile"},
PeripheralRequirements: []string{"audio", "camera", "download", "microphone"},
SubmissionsConfig: struct {
MaxSubmissionsPerParticipant int `json:"max_submissions_per_participant,omitempty" mapstructure:"max_submissions_per_participant"`
MaxConcurrentSubmissions int `json:"max_concurrent_submissions,omitempty" mapstructure:"max_concurrent_submissions"`
MaxSubmissionsPerParticipant int `json:"max_submissions_per_participant,omitempty" mapstructure:"max_submissions_per_participant"`
MaxConcurrentSubmissions int `json:"max_concurrent_submissions,omitempty" mapstructure:"max_concurrent_submissions"`
AutoRejectionCategories []string `json:"auto_rejection_categories,omitempty" mapstructure:"auto_rejection_categories"`
}{
MaxSubmissionsPerParticipant: -1,
MaxConcurrentSubmissions: 0,
AutoRejectionCategories: nil,
},
}

Expand All @@ -44,7 +56,7 @@ var actualStudy = model.Study{
ExternalStudyURL: "https://eggs-experriment.com?participant={{%PROLIFIC_PID%}}",
TotalAvailablePlaces: 10,
EstimatedCompletionTime: 10,
MaximumAllowedTime: 10,
MaximumAllowedTime: 100,
Reward: 400,
DeviceCompatibility: []string{"desktop", "tablet", "mobile"},
}
Expand Down
14 changes: 12 additions & 2 deletions docs/examples/standard-sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@
"description": "This is my first standard sample study on the Prolific system.",
"external_study_url": "https://eggs-experriment.com?participant={{%PROLIFIC_PID%}}",
"prolific_id_option": "url_parameters",
"completion_code": "COMPLE01",
"completion_codes": [
{
"code": "COMPLE01",
"code_type": "COMPLETED",
"actions": [
{
"action": "AUTOMATICALLY_APPROVE"
}
]
}
],
"total_available_places": 10,
"estimated_completion_time": 10,
"maximum_allowed_time": 10,
"maximum_allowed_time": 100,
"reward": 400,
"device_compatibility": ["desktop", "tablet", "mobile"],
"peripheral_requirements": ["audio", "camera", "download", "microphone"],
Expand Down
10 changes: 8 additions & 2 deletions docs/examples/standard-sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ internal_name: Standard sample
description: This is my first standard sample study on the Prolific system.
external_study_url: "https://eggs-experriment.com?participant={{%PROLIFIC_PID%}}"
prolific_id_option: url_parameters
completion_code: COMPLE01

completion_codes:
- code: COMPLE01
code_type: COMPLETED
actions:
- action: AUTOMATICALLY_APPROVE

total_available_places: 10
estimated_completion_time: 10
maximum_allowed_time: 10
maximum_allowed_time: 100
reward: 400
device_compatibility:
- desktop
Expand Down
74 changes: 64 additions & 10 deletions model/study.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,25 @@ type Study struct {
CredentialPoolID string `json:"credential_pool_id"`
}

// CompletionCode represents a completion code configuration for a study.
type CompletionCode struct {
Code string `json:"code" mapstructure:"code"`
CodeType string `json:"code_type" mapstructure:"code_type"`
Actions []CompletionCodeAction `json:"actions" mapstructure:"actions"`
}

// CompletionCodeAction represents an action to take when a completion code is used.
type CompletionCodeAction struct {
Action string `json:"action" mapstructure:"action"`
ParticipantGroup string `json:"participant_group,omitempty" mapstructure:"participant_group,omitempty"`
}

// AccessDetail represents a taskflow study URL allocation.
type AccessDetail struct {
ExternalURL string `json:"external_url" mapstructure:"external_url"`
TotalAllocation int `json:"total_allocation" mapstructure:"total_allocation"`
}

// CreateStudy is responsible for capturing what fields we need to send
// to Prolific to create a study. The `mapstructure` is so we can take a viper
// configuration file.
Expand All @@ -121,10 +140,17 @@ type CreateStudy struct {
ExternalStudyURL string `json:"external_study_url,omitempty" mapstructure:"external_study_url"`
// Enum "question", "url_parameters" (Recommended), "not_required"
ProlificIDOption string `json:"prolific_id_option" mapstructure:"prolific_id_option"`
CompletionCode string `json:"completion_code" mapstructure:"completion_code"`

// New: Array of completion code configurations (replaces completion_code and completion_option)
CompletionCodes []CompletionCode `json:"completion_codes,omitempty" mapstructure:"completion_codes"`

// DEPRECATED: Use CompletionCodes instead. Kept for backward compatibility.
CompletionCode string `json:"completion_code,omitempty" mapstructure:"completion_code"`
// DEPRECATED: Use CompletionCodes instead. Kept for backward compatibility.
// Enum: "url", "code"
CompletionOption string `json:"completion_option,omitempty" mapstructure:"completion_option"`
TotalAvailablePlaces int `json:"total_available_places" mapstructure:"total_available_places"`
CompletionOption string `json:"completion_option,omitempty" mapstructure:"completion_option"`

TotalAvailablePlaces int `json:"total_available_places" mapstructure:"total_available_places"`
// Minutes
EstimatedCompletionTime int `json:"estimated_completion_time" mapstructure:"estimated_completion_time"`
MaximumAllowedTime int `json:"maximum_allowed_time,omitempty" mapstructure:"maximum_allowed_time"`
Expand All @@ -135,18 +161,45 @@ type CreateStudy struct {
PeripheralRequirements []string `json:"peripheral_requirements,omitempty" mapstructure:"peripheral_requirements"`
// Study labels for categorization (e.g., "ai_annotation")
StudyLabels []string `json:"study_labels,omitempty" mapstructure:"study_labels"`

// New: Array of access details for taskflow studies with multiple URLs (replaces access_details_collection_id)
AccessDetails []AccessDetail `json:"access_details,omitempty" mapstructure:"access_details"`

// DEPRECATED: Use AccessDetails instead. Kept for backward compatibility.
// Access details collection ID: ID of the collection to attach to the study (for Taskflow studies)
AccessDetailsCollectionID string `json:"access_details_collection_id,omitempty" mapstructure:"access_details_collection_id"`
// Data collection method: "AI_TASK_BUILDER", "DC_TOOL", or "HUMAN_SIGNAL"

// Data collection method: "AI_TASK_BUILDER"
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment says the only valid data_collection_method is "AI_TASK_BUILDER", but the repo already includes examples using other values (e.g. docs/examples/standard-sample-aitaskbuilder.json uses "DC_TOOL"). Please update the comment to reflect the supported enum values so it doesn't mislead users.

Suggested change
// Data collection method: "AI_TASK_BUILDER"
// Data collection method. Enum: "AI_TASK_BUILDER", "DC_TOOL"

Copilot uses AI. Check for mistakes.
DataCollectionMethod string `json:"data_collection_method,omitempty" mapstructure:"data_collection_method"`
// Data collection ID: Project/collection/batch ID for data collection
DataCollectionID string `json:"data_collection_id,omitempty" mapstructure:"data_collection_id"`
// Data collection metadata: Configuration parameters (optional dict)
DataCollectionMetadata map[string]interface{} `json:"data_collection_metadata,omitempty" mapstructure:"data_collection_metadata"`
SubmissionsConfig struct {
MaxSubmissionsPerParticipant int `json:"max_submissions_per_participant,omitempty" mapstructure:"max_submissions_per_participant"`
MaxConcurrentSubmissions int `json:"max_concurrent_submissions,omitempty" mapstructure:"max_concurrent_submissions"`
DataCollectionMetadata map[string]any `json:"data_collection_metadata,omitempty" mapstructure:"data_collection_metadata"`

// New: Predefined filter set configuration
FilterSetID string `json:"filter_set_id,omitempty" mapstructure:"filter_set_id"`
FilterSetVersion int `json:"filter_set_version,omitempty" mapstructure:"filter_set_version"`

// New: Custom screening flag
IsCustomScreening bool `json:"is_custom_screening,omitempty" mapstructure:"is_custom_screening"`

// New: Content warnings
ContentWarnings []string `json:"content_warnings,omitempty" mapstructure:"content_warnings"`
ContentWarningDetails string `json:"content_warning_details,omitempty" mapstructure:"content_warning_details"`

// New: Custom metadata
Metadata map[string]any `json:"metadata,omitempty" mapstructure:"metadata"`

// New: JWT security flag for external study URLs
IsExternalStudyURLSecure bool `json:"is_external_study_url_secure,omitempty" mapstructure:"is_external_study_url_secure"`

SubmissionsConfig struct {
MaxSubmissionsPerParticipant int `json:"max_submissions_per_participant,omitempty" mapstructure:"max_submissions_per_participant"`
MaxConcurrentSubmissions int `json:"max_concurrent_submissions,omitempty" mapstructure:"max_concurrent_submissions"`
AutoRejectionCategories []string `json:"auto_rejection_categories,omitempty" mapstructure:"auto_rejection_categories"`
} `json:"submissions_config,omitempty" mapstructure:"submissions_config"`

// DEPRECATED: Use Filters or FilterSetID instead. Kept for backward compatibility.
EligibilityRequirements []struct {
Attributes []struct {
ID string `json:"id" mapstructure:"id"`
Expand All @@ -172,8 +225,9 @@ type UpdateStudy struct {

// SubmissionsConfig represents configuration around submission gathering
type SubmissionsConfig struct {
MaxSubmissionsPerParticipant int `json:"max_submissions_per_participant"`
MaxConcurrentSubmissions int `json:"max_concurrent_submissions"`
MaxSubmissionsPerParticipant int `json:"max_submissions_per_participant"`
MaxConcurrentSubmissions int `json:"max_concurrent_submissions"`
AutoRejectionCategories []string `json:"auto_rejection_categories,omitempty"`
}

// FilterValue will help the bubbletea views run
Expand Down
Loading
Loading