diff --git a/.changeset/cold-chairs-confess.md b/.changeset/cold-chairs-confess.md new file mode 100644 index 00000000..001ffbdc --- /dev/null +++ b/.changeset/cold-chairs-confess.md @@ -0,0 +1,5 @@ +--- +"@lookit/style": patch +--- + +Update consent trial style diff --git a/.changeset/large-rabbits-matter.md b/.changeset/large-rabbits-matter.md new file mode 100644 index 00000000..804b881e --- /dev/null +++ b/.changeset/large-rabbits-matter.md @@ -0,0 +1,6 @@ +--- +"@lookit/templates": patch +"@lookit/record": patch +--- + +Add ability to select specific video consent template. diff --git a/.changeset/strange-bottles-invent.md b/.changeset/strange-bottles-invent.md new file mode 100644 index 00000000..9774fea1 --- /dev/null +++ b/.changeset/strange-bottles-invent.md @@ -0,0 +1,6 @@ +--- +"@lookit/templates": patch +"@lookit/record": patch +--- + +Add Garden's consent template diff --git a/.gitignore b/.gitignore index 007e4da5..9f65f2b1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ Procfile .env site .DS_Store -packages/style/**/*.js \ No newline at end of file +packages/style/**/*.js +.python-version \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..2dbb03a8 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,23 @@ +# Read the Docs configuration file for MkDocs projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: "ubuntu-22.04" + tools: + python: "3.12" + jobs: + post_create_environment: + # Install poetry + # https://python-poetry.org/docs/#installing-manually + - pip install poetry + post_install: + # VIRTUAL_ENV needs to be set manually for now. + # See https://github.com/readthedocs/readthedocs.org/pull/11152/ + - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --no-root --sync + +mkdocs: + configuration: mkdocs.yml diff --git a/makefile b/makefile index f5312c46..185bc301 100644 --- a/makefile +++ b/makefile @@ -1,11 +1,14 @@ serve: poetry - poetry run mkdocs serve -a localhost:8888 + poetry run mkdocs serve --strict -a localhost:8888 build: poetry poetry run mkdocs build --strict poetry: - poetry install --no-root --sync + poetry check + poetry self update + poetry env use 3.12 + poetry update --sync clean: rm -rf ./site $(shell poetry env info -p) diff --git a/mkdocs.yml b/mkdocs.yml index d9aa14e0..43631ee3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,22 +4,34 @@ repo_url: https://github.com/lookit/lookit-jspsych nav: - Home: index.md - - CHS's initJsPsych: lookit-initjspsych/README.md - - Data: data/README.md - - Record: record/README.md - - Surveys: surveys/README.md + - Related projects: + - Lookit API: https://lookit.readthedocs.io + - Ember Lookit Frameplayer: https://lookit.readthedocs.io/projects/frameplayer + - Plugins and Extensions: + - Record: record/README.md + - Surveys: surveys/README.md + - Developers: + - Data: data/README.md + - CHS's initJsPsych: lookit-initjspsych/README.md + - Templates: templates/README.md + validation: omitted_files: warn absolute_links: warn unrecognized_links: warn anchors: warn + exclude_docs: | node_modules - lookit-initjspsych/CHANGELOG.md + CHANGELOG.md + !templates + theme: name: readthedocs + plugins: - search - macros + extra: - jsPsych: https://www.jspsych.org/7.3/ + jsPsych: https://www.jspsych.org/v8/ diff --git a/package-lock.json b/package-lock.json index 90f45c15..1aaba5a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17217,6 +17217,8 @@ }, "node_modules/rollup-plugin-dotenv": { "version": "0.5.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-dotenv/-/rollup-plugin-dotenv-0.5.1.tgz", + "integrity": "sha512-ARUPDmeKAw3niZ2Ajv0qKNRryIWFMW796oJSS1hNdop3HF63Vljio/QRmG6ob0aQzzVUrFq6vW1p4jOE6xDQrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20331,6 +20333,7 @@ "devDependencies": { "@jspsych/config": "^2.0.0", "handlebars": "^4.7.8", + "rollup-plugin-dotenv": "^0.5.1", "rollup-plugin-polyfill-node": "^0.13.0" }, "peerDependencies": { diff --git a/packages/record/README.md b/packages/record/README.md index 798a14c6..c79be745 100644 --- a/packages/record/README.md +++ b/packages/record/README.md @@ -21,6 +21,8 @@ const videoConfig = { type: chsRecord.VideoConfigPlugin }; ### Parameters +#### Optional + **`troubleshooting_intro` [HTML String]** Optional text to add at the start of the "Setup tips and troubleshooting" @@ -48,9 +50,9 @@ const jsPsych = initJsPsych({ }); ``` -Next, create a video configuration trial as described above. Add trial recording -to the extensions parameter of the trial that needs to be recorded. Any trial -you design can be recorded by add this extension. +Next, create a video configuration trial as described above. Then, add the trial +recoding extension parameter to your trial. By adding this extension, you can +record any trial you design. ```javascript const trialRec = { @@ -149,14 +151,74 @@ const videoConsent = { ### Parameters -#### Standard fields +Parameter names are shown below, along with their type and default value. If the +default value is _undefined_, then a value is required for that parameter. + +#### Required + +**`PIName` [String | _undefined_]** + +Name of PI running this study. + +**`institution` [String | _undefined_]** + +Name of institution running this study (if ambiguous, list institution whose IRB +approved the study). + +**`PIContact` [String | _undefined_]** + +Contact information for PI or lab in case of participant questions or concerns. +This will directly follow the phrase “please contact”, so format accordingly: +e.g., “the XYZ lab at xyz@science.edu” or “Mary Smith at 123 456 7890”. + +**`payment` [String | _undefined_]** + +Statement about payment/compensation for participation, including a statement +that there are no additional benefits anticipated to the participant. E.g., +“After you finish the study, we will email you a $5 BabyStore gift card within +approximately three days. To be eligible for the gift card your child must be in +the age range for this study, you need to submit a valid consent statement, and +we need to see that there is a child with you. But we will send a gift card even +if you do not finish the whole study or we are not able to use your child’s +data! There are no other direct benefits to you or your child from +participating, but we hope you will enjoy the experience.” + +This section is by default titled “Are there any benefits to your family?”; it +should only include information about benefits and compensation. If your IRB +prefers to combine risk/benefit information, you can change this to something +like “What are the risks and benefits if you participate?” and include both +here, then omit the risk_statement. + +**`procedures` [String | _undefined_]** + +Brief description of study procedures. For consent templates 001 and 002, this +should include any risks or a statement that there are no anticipated risks. +(For consent template 003, that is included in payment). We add a statement +about the duration (from your study definition) to the start (e.g., “This study +takes about 10 minutes to complete”), so you don’t need to include that. It can +be in third person or addressed to the parent. E.g., “Your child will be shown +pictures of lots of different cats, along with noises that cats make like +meowing and purring. We are interested in which pictures and sounds make your +child smile. We will ask you (the parent) to turn around to avoid influencing +your child’s responses. There are no anticipated risks associated with +participating.” + +**`purpose` [String | _undefined_]** + +Brief description of purpose of study - 1-2 sentences that describe what you are +trying to find out. Language should be as straightforward and accessible as +possible! E.g., “Why do babies love cats? This study will help us find out +whether babies love cats because of their soft fur or their twitchy tails.” + +#### Optional **`locale` [String | "en-us"]** -Set this parameter to the languages 2 letter code. In some cases, a regional -code will have to be provided as well. For example, we currently support english -only from the US region. Therefore, to get the US english translation you would -put "en-US" for the locale. We support the following language codes: +Optional parameter to set a two-letter language code for translation. In some +cases, a regional code will have to be provided as well. For example, we +currently support english only from the US region. Therefore, to get the US +english translation you would put "en-US" for the locale. We support the +following language codes: | Language | Region | Code | | -------------- | ------ | ----- | @@ -170,16 +232,22 @@ put "en-US" for the locale. We support the following language codes: | Portuguese | Brazil | pt-BR | | Portuguese | | pt | -**`additional_video_privacy_statement` [String]** +**`template` [String | "consent_005"]** + +Which consent document template to use. If you are setting up a new study, we +recommend using the most recent (highest number) of these options. Options: +`consent_005` + +**`additional_video_privacy_statement` [String | ""]** Optional additional text for under header “Who can see our webcam recordings”. For cases where researchers ask for other specific permission to share videos, separate from the exit survey, or want to provide more detail or different language about Databrary sharing. -**`datause` [String]** +**`datause` [String | ""]** -Study-specific data use statement (optional). This will follow more general text +Optional study-specific data use statement. This will follow more general text like: “The research group led by [PIName] at [institution] will have access to video and other data collected during this session. We will also have access to your account profile, demographic survey, and the child profile for the child @@ -200,56 +268,23 @@ single planned study). Whether to include a section on GDPR. -**`gdpr_personal_data` [String]** +**`gdpr_personal_data` [String | ""]** List of types of personal information collected, for GDPR section only. Do not include special category information, which is listed separately. -**`gdpr_sensitive_data` [String]** +**`gdpr_sensitive_data` [String | ""]** List of types of special category information collected, for GDPR section only. Include all that apply: racial or ethnic origin; political opinions; religious or philosophical beliefs; trade union membership; processing of genetic data; -biometric data; health data; and/or sex life or sexual orientation information - -**`PIName` [String]** - -Name of PI running this study +biometric data; health data; and/or sex life or sexual orientation information. **`include_databrary` [Boolean | false]** Whether to include a paragraph about Databrary under “Who can see our webcam recordings?”. -**`institution` [String]** - -Name of institution running this study (if ambiguous, list institution whose IRB -approved the study)’ - -**`PIContact` [String]** - -Contact information for PI or lab in case of participant questions or concerns. -This will directly follow the phrase “please contact”, so format accordingly: -e.g., “the XYZ lab at xyz@science.edu” or “Mary Smith at 123 456 7890”. - -**`payment` [String]** - -Statement about payment/compensation for participation, including a statement -that there are no additional benefits anticipated to the participant. E.g., -“After you finish the study, we will email you a $5 BabyStore gift card within -approximately three days. To be eligible for the gift card your child must be in -the age range for this study, you need to submit a valid consent statement, and -we need to see that there is a child with you. But we will send a gift card even -if you do not finish the whole study or we are not able to use your child’s -data! There are no other direct benefits to you or your child from -participating, but we hope you will enjoy the experience.” - -This section is by default titled “Are there any benefits to your family?”; it -should only include information about benefits and compensation. If your IRB -prefers to combine risk/benefit information, you can change this to something -like “What are the risks and benefits if you participate?” and include both -here, then omit the risk_statement. - **`private_level_only` [Boolean | false]** Whether to describe only the “private” video privacy level under the heading @@ -258,28 +293,7 @@ IRB has a hard restriction against even offering participants the option to share their videos more broadly, and in conjunction with the corresponding restriction of options in the exit survey! -**`procedures` [String]** - -Brief description of study procedures. For consent templates 001 and 002, this -should include any risks or a statement that there are no anticipated risks. -(For consent template 003, that is included in payment). We add a statement -about the duration (from your study definition) to the start (e.g., “This study -takes about 10 minutes to complete”), so you don’t need to include that. It can -be in third person or addressed to the parent. E.g., “Your child will be shown -pictures of lots of different cats, along with noises that cats make like -meowing and purring. We are interested in which pictures and sounds make your -child smile. We will ask you (the parent) to turn around to avoid influencing -your child’s responses. There are no anticipated risks associated with -participating.” - -**`purpose` [String]** - -Brief description of purpose of study - 1-2 sentences that describe what you are -trying to find out. Language should be as straightforward and accessible as -possible! E.g., “Why do babies love cats? This study will help us find out -whether babies love cats because of their soft fur or their twitchy tails.” - -**`research_rights_statement` [String]** +**`research_rights_statement` [String | ""]** Statement about rights of research subjects and how to contact IRB. For instance, MIT’s standard language is: You are not waiving any legal claims, @@ -287,12 +301,12 @@ rights or remedies because of your participation in this research study. If you feel you have been treated unfairly, or you have questions regarding your rights as a research subject, you may contact [CONTACT INFO]. -**`risk_statement` [String]** +**`risk_statement` [String | ""]** Optional statement; if provided, it is displayed under a header “Are there any risks if you participate?”. -**`voluntary_participation` [String]** +**`voluntary_participation` [String | ""]** Optional additional text for under header “Participation is voluntary”. E.g., “There are two sessions in this study; you will be invited to complete another @@ -310,27 +324,27 @@ in the Terms of Use! If it really won’t be possible to use Lookit without maki more changes, please let us know before using the following fields to further customize the consent form: -**`purpose_header` [String]** +**`purpose_header` [String | ""]** Custom alternate header for the section on study purpose. -**`procedures_header` [String]** +**`procedures_header` [String | ""]** Custom alternate header for the section on study procedures. -**`participation_header` [String]** +**`participation_header` [String | ""]** Custom alternate header for the section on participation being voluntary. -**`benefits_header` [String]** +**`benefits_header` [String | ""]** Custom alternate header for the section on benefits/compensation. -**`risk_header` [String]** +**`risk_header` [String | ""]** Custom alternate header for risks section. -**`summary_statement` [String]** +**`summary_statement` [String | ""]** Statement inserted at the beginning of the consent form, right after “Researchers led by … are running this study … on Lookit.” Please only use this @@ -366,7 +380,7 @@ Whether to prompt only the adult for consent for themselves to participate, rather than also referencing a child. This is for occasional studies running an adult comparison group. -**`consent_statement_text` [String]** +**`consent_statement_text` [String | ""]** Replace the default spoken consent statement with your custom text. diff --git a/packages/record/scss/consent-video-trial.scss b/packages/record/scss/consent-video-trial.scss index 604a857e..25745525 100644 --- a/packages/record/scss/consent-video-trial.scss +++ b/packages/record/scss/consent-video-trial.scss @@ -35,8 +35,10 @@ div#consent-video-trial { font-size: 15px; } - ol { + ol, + ul { padding-left: 30px; + font-size: 15px; } li { diff --git a/packages/record/src/consentVideo.spec.ts b/packages/record/src/consentVideo.spec.ts index 1ad76c7e..fa048dd2 100644 --- a/packages/record/src/consentVideo.spec.ts +++ b/packages/record/src/consentVideo.spec.ts @@ -42,7 +42,10 @@ test("Trial", () => { const jsPsych = initJsPsych(); const plugin = new VideoConsentPlugin(jsPsych); const display = document.createElement("div"); - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = { + locale: "en-us", + template: "consent-template-5", + } as unknown as TrialType; plugin["recordFeed"] = jest.fn(); plugin["recordButton"] = jest.fn(); @@ -116,7 +119,10 @@ test("onEnded", () => { const play = document.createElement("button"); const next = document.createElement("button"); const record = document.createElement("button"); - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = { + locale: "en-us", + template: "consent-template-5", + } as unknown as TrialType; display.innerHTML = chsTemplates.consentVideo(trial); plugin["recordFeed"] = jest.fn(); @@ -152,7 +158,10 @@ test("getButton", () => { const jsPsych = initJsPsych(); const plugin = new VideoConsentPlugin(jsPsych); const display = document.createElement("div"); - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = { + locale: "en-us", + template: "consent-template-5", + } as unknown as TrialType; display.innerHTML = chsTemplates.consentVideo(trial); @@ -182,7 +191,10 @@ test("recordButton", async () => { const jsPsych = initJsPsych(); const plugin = new VideoConsentPlugin(jsPsych); const display = document.createElement("div"); - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = { + locale: "en-us", + template: "consent-template-5", + } as unknown as TrialType; display.innerHTML = chsTemplates.consentVideo(trial); @@ -228,7 +240,10 @@ test("playButton", () => { const jsPsych = initJsPsych(); const plugin = new VideoConsentPlugin(jsPsych); const display = document.createElement("div"); - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = { + locale: "en-us", + template: "consent-template-5", + } as unknown as TrialType; plugin["playbackFeed"] = jest.fn(); @@ -249,7 +264,10 @@ test("stopButton", async () => { const jsPsych = initJsPsych(); const plugin = new VideoConsentPlugin(jsPsych); const display = document.createElement("div"); - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = { + locale: "en-us", + template: "consent-template-5", + } as unknown as TrialType; display.innerHTML = chsTemplates.consentVideo(trial) + Handlebars.compile(recordFeed)({}); @@ -274,7 +292,10 @@ test("nextButton", () => { const jsPsych = initJsPsych(); const plugin = new VideoConsentPlugin(jsPsych); const display = document.createElement("div"); - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = { + locale: "en-us", + template: "consent-template-5", + } as unknown as TrialType; display.innerHTML = chsTemplates.consentVideo(trial); plugin["endTrial"] = jest.fn(); diff --git a/packages/record/src/consentVideo.ts b/packages/record/src/consentVideo.ts index eeda729e..8d79349c 100644 --- a/packages/record/src/consentVideo.ts +++ b/packages/record/src/consentVideo.ts @@ -16,7 +16,7 @@ const info = { name: "consent-video", version, parameters: { - template: { type: ParameterType.STRING, default: "consent_005" }, + template: { type: ParameterType.STRING, default: "consent-template-5" }, locale: { type: ParameterType.STRING, default: "en-us" }, additional_video_privacy_statement: { type: ParameterType.STRING, @@ -26,14 +26,14 @@ const info = { gdpr: { type: ParameterType.BOOL, default: false }, gdpr_personal_data: { type: ParameterType.STRING, default: "" }, gdpr_sensitive_data: { type: ParameterType.STRING, default: "" }, - PIName: { type: ParameterType.STRING, default: "" }, + PIName: { type: ParameterType.STRING, default: undefined }, include_databrary: { type: ParameterType.BOOL, default: false }, - institution: { type: ParameterType.STRING, default: "" }, - PIContact: { type: ParameterType.STRING, default: "" }, - payment: { type: ParameterType.STRING, default: "" }, + institution: { type: ParameterType.STRING, default: undefined }, + PIContact: { type: ParameterType.STRING, default: undefined }, + payment: { type: ParameterType.STRING, default: undefined }, private_level_only: { type: ParameterType.BOOL, default: false }, - procedures: { type: ParameterType.STRING, default: "" }, - purpose: { type: ParameterType.STRING, default: "" }, + procedures: { type: ParameterType.STRING, default: undefined }, + purpose: { type: ParameterType.STRING, default: undefined }, research_rights_statement: { type: ParameterType.STRING, default: "" }, risk_statement: { type: ParameterType.STRING, default: "" }, voluntary_participation: { type: ParameterType.STRING, default: "" }, diff --git a/packages/style/src/index.css b/packages/style/src/index.css index 85bc1efb..e9f20c68 100644 --- a/packages/style/src/index.css +++ b/packages/style/src/index.css @@ -8937,6 +8937,10 @@ div#consent-video-trial p { div#consent-video-trial ol { padding-left: 30px; } +div#consent-video-trial ol, div#consent-video-trial ul { + font-size: 15px; + margin-top: 0px; +} div#consent-video-trial li { padding-top: 10px; padding-bottom: 10px; diff --git a/packages/surveys/README.md b/packages/surveys/README.md index dcb66fe4..9ccb86c0 100644 --- a/packages/surveys/README.md +++ b/packages/surveys/README.md @@ -31,9 +31,20 @@ const exitSurvey = { type: chsSurvey.ExitSurveyPlugin }; ### Parameters -| Parameter | Type | Default Value | Description | -| ----------------------------- | ------- | ------------- | -------------------------------------------------------- | -| show_databrary_options | boolean | true | Show question about sharing collected data on Databrary. | -| include_withdrawal_example | boolean | true | Include an example in withdrawal question text. | -| private_level_only | boolean | false | Only show "private" on use of media question. | -| additional_video_privacy_text | string | "" | Add custom video privacy text to privacy question. | +#### Optional + +**`show_databrary_options` [Boolean | true]** + +Show question about sharing collected data on Databrary. + +**`include_withdrawal_example` [Boolean | true]** + +Include an example in withdrawal question text. + +**`private_level_only` [Boolean | false]** + +Only show "private" on use of media question. + +**`additional_video_privacy_text` [String | ""]** + +Add custom video privacy text to privacy question. diff --git a/packages/templates/README.md b/packages/templates/README.md new file mode 100644 index 00000000..cd51cd6e --- /dev/null +++ b/packages/templates/README.md @@ -0,0 +1,6 @@ +# Templates + +This package contains the translated templates used in other custom CHS jsPsych +trials. Please see the trial documentation (e.g. +[Video Consent](../record/README.md#video-consent)) for help using these +templates. diff --git a/packages/templates/hbs/consent-garden.hbs b/packages/templates/hbs/consent-garden.hbs new file mode 100644 index 00000000..84b4fa2d --- /dev/null +++ b/packages/templates/hbs/consent-garden.hbs @@ -0,0 +1,307 @@ +

+ {{#if header}} {{exp-format header}} {{else}} + {{{t "consent-garden.header" experiment=experiment.name}}} + {{/if}} +

+ +{{! intro sentence }} +

{{#if intro_sentence}} {{exp-format intro_sentence}} {{else}} + {{t + "consent-garden.intro-sentence" + name=PIName + institution=institution + experiment=experiment.name + htmlSafe=true + }} + {{/if}} + {{exp-format summary_statement}} +

+ +{{! overview }} +

+ {{#if overview_header}} {{exp-format overview_header}} {{else}} + {{t "consent-garden.overview-header" htmlSafe=true}} + {{/if}} +

+ +

+ {{#if overview_content}} {{exp-format overview_content}} {{else}} + {{t + "consent-garden.overview-content" + contact=PIContact + experiment=experiment.name + htmlSafe=true + }} + {{/if}} +

+ +{{! study info }} +

+ {{#if study_info_header}} {{exp-format study_info_header}} {{else}} + {{t "consent-garden.study-info-header" htmlSafe=true}} + {{/if}} +

+

+ {{#if study_info_content}} {{exp-format study_info_content}} {{else}} + {{t + "consent-garden.study-info-content" + description=study_info_description + lab=lab + institution=institution + experiment=experiment.name + htmlSafe=true + }} + {{/if}} +

+ +{{! purpose }} +

+ {{#if purpose_header}} {{exp-format purpose_header}} {{else}} + {{t "consent-garden.purpose-header" htmlSafe=true}} + {{/if}} +

+

+ {{exp-format purpose_content}} +

+ +{{! eligibility }} +

+ {{#if eligibility_header}} {{exp-format eligibility_header}} {{else}} + {{t "consent-garden.eligibility-header" htmlSafe=true}} + {{/if}}

+

{{exp-format eligibility_content}}

+ +{{! procedures }} +

{{#if procedures_header}} {{exp-format procedures_header}} {{else}} + {{t "consent-garden.procedures-header" htmlSafe=true}} + {{/if}}

+

+ {{#if include_duration_in_procedures}} + {{#if duration_statment}} {{exp-format duration_statment}} {{else}} + {{t + "consent-garden.duration-statement" + duration=experiment.duration + htmlSafe=true + }} + {{exp-format procedures_content}} + {{/if}} + {{else}} + {{exp-format procedures_content}} + {{/if}} +

+ +{{! payment }} +

{{#if payment_header}} {{exp-format payment_header}} {{else}} + {{t "consent-garden.payment-header" htmlSafe=true}} + {{/if}}

+

{{#if payment_content}} {{exp-format payment_content}} {{else}} + {{t "consent-garden.payment-content" htmlSafe=true}} + {{/if}}

+ +{{! benefits }} +

{{#if benefits_header}} {{exp-format benefits_header}} {{else}} + {{t "consent-garden.benefits-header" htmlSafe=true}} + {{/if}}

+

{{exp-format benefits_content}}

+ +{{! risks }} +

{{#if risk_header}} {{exp-format risk_header}} {{else}} + {{t "consent-garden.risk-header" htmlSafe=true}} + {{/if}}

+

+ {{#if risk_content}} + {{exp-format risk_content}} + {{else}} + {{t + "consent-garden.risk-content-1" + duration=experiment.duration + experiment=experiment.name + htmlSafe=true + }} + {{#if risk_content_additional}} + {{exp-format risk_content_additional}} + {{/if}} + {{t + "consent-garden.risk-content-2" + risk_content_discontinue_options=risk_content_discontinue_options + htmlSafe=true + }} + {{#if risk_content_breach_of_confidentiality}} + {{exp-format risk_content_breach_of_confidentiality}} + {{/if}} + {{t "consent-garden.risk-content-3" htmlSafe=true}} + {{/if}} +

+ +{{! data collection }} +

{{#if data_collection_header}} + {{exp-format data_collection_header}} + {{else}} + {{t "consent-garden.data-collection-header" htmlSafe=true}} + {{/if}}

+

{{#if data_collection_content}} + {{exp-format data_collection_content}} + {{else}} + {{t + "consent-garden.data-collection-content" + institution=institution + omit_video=data_collection_omit_video + htmlSafe=true + }} + {{/if}}

+ +{{! data use }} +

{{#if data_use_header}} {{exp-format data_use_header}} {{else}} + {{t "consent-garden.data-use-researchers-header" htmlSafe=true}} + {{/if}}

+

{{#if data_use_content}} {{exp-format data_use_content}} {{else}} + {{t "consent-garden.data-use-researchers-content" htmlSafe=true}} + {{/if}}

+ +{{! data access }} +

{{#if data_access_header}} {{exp-format data_access_header}} {{else}} + {{t "consent-garden.data-access-header" htmlSafe=true}} + {{/if}}

+

{{#if data_access_content}} {{exp-format data_access_content}} {{else}} + {{t + "consent-garden.data-access-content" + institution=institution + htmlSafe=true + }} + {{/if}}

+ +{{! data management }} +

{{#if data_management_header}} + {{exp-format data_management_header}} + {{else}} + {{t "consent-garden.data-management-header" htmlSafe=true}} + {{/if}}

+

{{#if data_management_content}} + {{exp-format data_management_content}} + {{else}} + {{t "consent-garden.data-management-content" htmlSafe=true}} + {{/if}}

+ +{{! data sharing }} +

{{#if data_sharing_header}} {{exp-format data_sharing_header}} {{else}} + {{t "consent-garden.data-sharing-header" htmlSafe=true}} + {{/if}}

+

{{#if data_sharing_content}} {{exp-format data_sharing_content}} {{else}} + {{t + "consent-garden.data-sharing-content" + data_sharing_learn=data_sharing_learn + experiment=experiment.name + htmlSafe=true + }} + {{/if}}

+ +{{! research rights and irb info }} +

{{#if research_rights_irb_header}} + {{exp-format research_rights_irb_header}} + {{else}} + {{t + "consent-garden.research-rights-irb-header" + institution=institution + htmlSafe=true + }} + {{/if}}

+

{{#if research_rights_irb_content}} + {{exp-format research_rights_irb_content}} + {{else}} + {{t + "consent-garden.research-rights-irb-content" + institution=institution + include_irb_contact_statement=include_irb_contact_statement + irb_contact=irb_contact + irb_extra=irb_extra + htmlSafe=true + }} + {{/if}}

+ +{{! lookit info }} +

{{#if lookit_info_header}} {{exp-format lookit_info_header}} {{else}} + {{t "consent-garden.lookit-info-header" htmlSafe=true}} + {{/if}}

+

{{#if lookit_info_content}} {{exp-format lookit_info_content}} {{else}} + {{t "consent-garden.lookit-info-content" htmlSafe=true}} + {{/if}}

+ +{{! voluntary participation }} +

{{#if voluntary_participation_header}} + {{exp-format voluntary_participation_header}} + {{else}} + {{t "consent-garden.voluntary-participation-header" htmlSafe=true}} + {{/if}}

+

{{#if voluntary_participation_content}} + {{exp-format voluntary_participation_content}} + {{else}} + {{t "consent-garden.voluntary-participation-content" htmlSafe=true}} + {{/if}}

+ +{{! video sharing }} +

{{#if video_sharing_header}} {{exp-format video_sharing_header}} {{else}} + {{t "consent-garden.video-sharing-header" htmlSafe=true}} + {{/if}}

+

+ {{#if video_sharing_consent}} {{exp-format video_sharing_consent}} {{else}} + {{t "consent-garden.video-sharing-consent" htmlSafe=true}} + {{/if}} + {{#unless data_collection_omit_video}} + + {{#if video_sharing_study}} + {{exp-format video_sharing_study}} + {{else}} + {{#if private_level_only}} + {{t "consent-garden.video-sharing-study-private" htmlSafe=true}} + {{else}} + {{t "consent-garden.video-sharing-study-all-1" htmlSafe=true}} + {{#if include_databrary}} + {{t "consent-garden.video-sharing-study-all-databrary" htmlSafe=true}} + {{/if}} + {{t "consent-garden.video-sharing-study-all-2" htmlSafe=true}} + {{/if}} + {{/if}} + {{/unless}} + + {{! additional video privacy statement }} + {{#if additional_video_privacy_statement}} + {{exp-format additional_video_privacy_statement}} + {{/if}} +

+ +{{! databrary sharing }} +{{#if include_databrary}} +

{{#if databrary_header}} {{exp-format databrary_header}} {{else}} + {{t "consent-garden.databrary-header" htmlSafe=true}} + {{/if}}

+

+ {{#if databrary_content}} {{exp-format databrary_content}} {{else}} + {{t "consent-garden.databrary-content" htmlSafe=true}} + {{/if}} +

+{{/if}} + +{{! additional segments }} +{{#if additional_segments}} + {{#each additional_segments as |segment|}} +

{{exp-format segment.title}}

+

{{exp-format segment.text}}

+ {{/each}} +{{/if}} + +{{! contact }} +

+ {{#if contact_header}} {{exp-format contact_header}} {{else}} + {{{t "consent-garden.contact-header" htmlSafe=true}}} + {{/if}}

+

+ {{#if contact_content}} {{exp-format contact_content}} {{else}} + {{t + "consent-garden.contact-content" + name=PIName + institution=institution + contact=PIContact + htmlSafe=true + }} + {{/if}} +

\ No newline at end of file diff --git a/packages/templates/hbs/consent-document.hbs b/packages/templates/hbs/consent-template-5.hbs similarity index 100% rename from packages/templates/hbs/consent-document.hbs rename to packages/templates/hbs/consent-template-5.hbs diff --git a/packages/templates/package.json b/packages/templates/package.json index 7cd013a8..eb3c558d 100644 --- a/packages/templates/package.json +++ b/packages/templates/package.json @@ -27,6 +27,7 @@ "devDependencies": { "@jspsych/config": "^2.0.0", "handlebars": "^4.7.8", + "rollup-plugin-dotenv": "^0.5.1", "rollup-plugin-polyfill-node": "^0.13.0" }, "peerDependencies": { diff --git a/packages/templates/rollup.config.mjs b/packages/templates/rollup.config.mjs index b0ac4b28..63119b11 100644 --- a/packages/templates/rollup.config.mjs +++ b/packages/templates/rollup.config.mjs @@ -1,3 +1,4 @@ +import dotenv from "rollup-plugin-dotenv"; import nodePolyfills from "rollup-plugin-polyfill-node"; import { importAsString } from "rollup-plugin-string-import"; import { makeRollupConfig } from "../../rollup.mjs"; @@ -7,7 +8,8 @@ export default makeRollupConfig("chsTemplates").map((config) => { ...config, plugins: [ ...config.plugins, - + // Add support for .env files + dotenv(), // Add support to import yaml and handlebars files as strings importAsString({ include: ["**/*.yaml", "**/*.hbs"], diff --git a/packages/templates/src/consentVideo.ts b/packages/templates/src/consentVideoTemplate.ts similarity index 55% rename from packages/templates/src/consentVideo.ts rename to packages/templates/src/consentVideoTemplate.ts index 13d71412..1166261d 100644 --- a/packages/templates/src/consentVideo.ts +++ b/packages/templates/src/consentVideoTemplate.ts @@ -1,9 +1,11 @@ import { LookitWindow } from "@lookit/data/dist/types"; import Handlebars from "handlebars"; import { PluginInfo, TrialType } from "jspsych"; -import consentDocumentTemplate from "../hbs/consent-document.hbs"; +import consent_garden from "../hbs/consent-garden.hbs"; +import consent_template_5 from "../hbs/consent-template-5.hbs"; import consentVideoTrialTemplate from "../hbs/consent-video-trial.hbs"; -import { initI18nAndTemplates } from "./utils"; +import { ConsentTemplateNotFound } from "./errors"; +import { setLocale } from "./utils"; declare const window: LookitWindow; @@ -19,7 +21,9 @@ export const consentVideo = (trial: TrialType) => { const experiment = window.chs.study.attributes; const { PIName, PIContact } = trial; - initI18nAndTemplates(trial); + setLocale(trial); + + const consentDocumentTemplate = consentDocument(trial); const consent = Handlebars.compile(consentDocumentTemplate)({ ...trial, @@ -34,3 +38,20 @@ export const consentVideo = (trial: TrialType) => { video_container_id, }); }; + +/** + * Get consent template by name. + * + * @param trial - Trial data including user supplied parameters. + * @returns Consent template + */ +const consentDocument = (trial: TrialType) => { + switch (trial.template) { + case "consent-template-5": + return consent_template_5; + case "consent-garden": + return consent_garden; + default: + throw new ConsentTemplateNotFound(trial.template); + } +}; diff --git a/packages/templates/src/environment.d.ts b/packages/templates/src/environment.d.ts new file mode 100644 index 00000000..013eed40 --- /dev/null +++ b/packages/templates/src/environment.d.ts @@ -0,0 +1,9 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + DEBUG?: string; + } + } +} + +export {}; diff --git a/packages/templates/src/errors.ts b/packages/templates/src/errors.ts index c748365f..07ddaa58 100644 --- a/packages/templates/src/errors.ts +++ b/packages/templates/src/errors.ts @@ -1,11 +1,22 @@ -/** Error throw what specified language isn't found */ -export class TranslationNotFoundError extends Error { +/** Error thrown when specified language isn't found */ +export class LocaleNotFoundError extends Error { /** * This will be thrown when attempting to init i18n * * @param baseName - Language a2code with region */ public constructor(baseName: string) { - super(`"${baseName}" translation not found.`); + super(`"${baseName}" locale not found.`); + } +} +/** Error thrown when researcher selects template that isn't available. */ +export class ConsentTemplateNotFound extends Error { + /** + * This will let the researcher know that their template isn't found. + * + * @param template - Supplied name of consent template. + */ + public constructor(template: string) { + super(`Consent template "${template}" not found.`); } } diff --git a/packages/templates/src/index.spec.ts b/packages/templates/src/index.spec.ts index 807d9db8..6dc70064 100644 --- a/packages/templates/src/index.spec.ts +++ b/packages/templates/src/index.spec.ts @@ -1,11 +1,26 @@ import { LookitWindow } from "@lookit/data/dist/types"; import { PluginInfo, TrialType } from "jspsych"; +import { ConsentTemplateNotFound } from "./errors"; import chsTemplate from "./index"; declare const window: LookitWindow; +/** + * Test helper function to create trial object. + * + * @param values - Object to replace default trial values + * @returns Trial object + */ +const getTrial = (values: Record = {}) => { + return { + locale: "en-us", + template: "consent-template-5", + ...values, + } as unknown as TrialType; +}; + test("consent video", () => { - const trial = { locale: "en-us" } as unknown as TrialType; + const trial = getTrial(); const name = "some name"; window.chs = { study: { @@ -25,7 +40,7 @@ test("consent video", () => { }); test("consent video in French", () => { - const trial = { locale: "fr" } as unknown as TrialType; + const trial = getTrial({ locale: "fr" }); const name = "some name"; window.chs = { study: { @@ -43,3 +58,17 @@ test("consent video in French", () => { `Consentement à participer à la recherche:\n ${name}`, ); }); + +test("consent video with unknown template", () => { + const trial = getTrial({ + template: "not a real template name", + }); + expect(() => chsTemplate.consentVideo(trial)).toThrow( + ConsentTemplateNotFound, + ); +}); + +test("consent garden template", () => { + const trial = getTrial({ template: "consent-garden" }); + expect(chsTemplate.consentVideo(trial)).toContain("Project GARDEN"); +}); diff --git a/packages/templates/src/index.ts b/packages/templates/src/index.ts index d3e03529..c0531b8c 100644 --- a/packages/templates/src/index.ts +++ b/packages/templates/src/index.ts @@ -1,3 +1,3 @@ -import { consentVideo } from "./consentVideo"; +import { consentVideo } from "./consentVideoTemplate"; export default { consentVideo }; diff --git a/packages/templates/src/utils.spec.ts b/packages/templates/src/utils.spec.ts index 6bd4e4ab..9747b3f0 100644 --- a/packages/templates/src/utils.spec.ts +++ b/packages/templates/src/utils.spec.ts @@ -1,15 +1,6 @@ -import Yaml from "js-yaml"; -import en_us from "../i18n/en-us.yaml"; -import eu from "../i18n/eu.yaml"; -import fr from "../i18n/fr.yaml"; -import hu from "../i18n/hu.yaml"; -import it from "../i18n/it.yaml"; -import ja from "../i18n/ja.yaml"; -import nl from "../i18n/nl.yaml"; -import pt_br from "../i18n/pt-br.yaml"; -import pt from "../i18n/pt.yaml"; -import { TranslationNotFoundError } from "./errors"; -import { expFormat, getTranslation } from "./utils"; +import { PluginInfo, TrialType } from "jspsych"; +import { LocaleNotFoundError } from "./errors"; +import { expFormat, setLocale } from "./utils"; test("expFormat convert written text to format well in HTML", () => { expect(expFormat("abcdefg")).toStrictEqual("abcdefg"); @@ -30,26 +21,7 @@ test("expFormat convert written text to format well in HTML", () => { ); }); -test("Get translation file for specified locale", () => { - const translations = { - ja, - pt, - eu, - fr, - hu, - it, - nl, - "en-us": en_us, - "pt-br": pt_br, - }; - - for (const [k, v] of Object.entries(translations)) { - expect(getTranslation(new Intl.Locale(k))).toStrictEqual(Yaml.load(v)); - } - - expect(pt_br).not.toStrictEqual(pt); - - expect(() => getTranslation(new Intl.Locale("not-a2code"))).toThrow( - TranslationNotFoundError, - ); +test("setLocale throw error with non-existing locale", () => { + const trial = { locale: "non-existing" } as unknown as TrialType; + expect(() => setLocale(trial)).toThrow(LocaleNotFoundError); }); diff --git a/packages/templates/src/utils.ts b/packages/templates/src/utils.ts index db894ddf..e8842558 100644 --- a/packages/templates/src/utils.ts +++ b/packages/templates/src/utils.ts @@ -12,7 +12,7 @@ import ja from "../i18n/ja.yaml"; import nl from "../i18n/nl.yaml"; import pt_br from "../i18n/pt-br.yaml"; import pt from "../i18n/pt.yaml"; -import { TranslationNotFoundError } from "./errors"; +import { LocaleNotFoundError } from "./errors"; /** * Pulled from EFP. Function to convert researcher's text to HTML. @@ -35,45 +35,32 @@ export const expFormat = (text?: string | string[]) => { }; /** - * Get a translation file based on selected language. + * Get a translation resources from yaml files. * - * @param lcl - Locale object with locale - * @returns Translations from i18next + * @returns Resources for i18next */ -export const getTranslation = (lcl: Intl.Locale) => { - /** - * Switch case to find language from a string. Will throw error is language - * not found. - * - * @param baseName - Base name from locale (en-us) - * @returns Language yaml file - */ - const getYaml = (baseName: string) => { - switch (baseName) { - case "en-US": - return en_us; - case "eu": - return eu; - case "fr": - return fr; - case "hu": - return hu; - case "it": - return it; - case "ja": - return ja; - case "nl": - return nl; - case "pt-BR": - return pt_br; - case "pt": - return pt; - default: - throw new TranslationNotFoundError(baseName); - } +const resources = () => { + const translations = { + "en-us": en_us, + eu, + fr, + hu, + it, + ja, + nl, + "pt-br": pt_br, + pt, }; - return Yaml.load(getYaml(lcl.baseName)) as Record; + return Object.entries(translations).reduce((prev, [locale, translation]) => { + const lcl = new Intl.Locale(locale); + return { + ...prev, + [lcl.baseName]: { + translation: Yaml.load(translation) as Record, + }, + }; + }, {}); }; /** @@ -81,39 +68,27 @@ export const getTranslation = (lcl: Intl.Locale) => { * * @param trial - Trial data including user supplied parameters. */ -const initI18next = (trial: TrialType) => { - const debug = process.env.DEBUG === "true"; +export const setLocale = (trial: TrialType) => { const lcl = new Intl.Locale(trial.locale); - const translation = getTranslation(lcl); - i18next.use(ICU).init({ - lng: lcl.baseName, - debug, - resources: { - [lcl.language]: { - translation, - }, - }, - }); -}; + if (!i18next.hasResourceBundle(lcl.baseName, "translation")) { + throw new LocaleNotFoundError(trial.locale); + } -/** - * Initialize handlebars helpers. This could be done globally, but it does go - * hand in hand with initializing i18n. - */ -const initHandlebars = () => { - Handlebars.registerHelper("t", (context, { hash }) => - i18next.t(context, hash), - ); - Handlebars.registerHelper("exp-format", (context) => expFormat(context)); + if (i18next.language !== lcl.baseName) { + i18next.changeLanguage(lcl.baseName); + } }; -/** - * Initialize both i18next and Handlebars. - * - * @param trial - Yup - */ -export const initI18nAndTemplates = (trial: TrialType) => { - initI18next(trial); - initHandlebars(); -}; +// Initialize translations +i18next.use(ICU).init({ + debug: process.env.DEBUG === "true", + resources: resources(), +}); + +// Setup Handlebars' helpers +Handlebars.registerHelper("exp-format", (context) => expFormat(context)); +Handlebars.registerHelper("t", (context, { hash }) => { + const txt = String(i18next.t(context, hash)); + return hash.htmlSafe ? new Handlebars.SafeString(txt) : txt; +}); diff --git a/poetry.lock b/poetry.lock index fe20bc9b..0b9e44db 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "click" @@ -87,72 +87,72 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "3.0.1" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" files = [ - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, - {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] @@ -214,13 +214,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-macros-plugin" -version = "1.3.4" +version = "1.3.6" description = "Unleash the power of MkDocs with macros and variables" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs-macros-plugin-1.3.4.tar.gz", hash = "sha256:de6ea32729bf2ffffed18b23966e27b4915512370e5cbc37a388f32ff75b97b9"}, - {file = "mkdocs_macros_plugin-1.3.4-py3-none-any.whl", hash = "sha256:6f99dc10420a82afa4835db7896f0a242d3b8f593c090c66f3b427ceebbc8767"}, + {file = "mkdocs_macros_plugin-1.3.6-py3-none-any.whl", hash = "sha256:74fd418c8e1f9f021b7a45bb1c7d7461d8ae09ccec241787f3971e733374da8c"}, + {file = "mkdocs_macros_plugin-1.3.6.tar.gz", hash = "sha256:074bc072cac14c28724037b6988743cd9425752bdd35ae05fbf96b1b2457f3b7"}, ] [package.dependencies] @@ -378,18 +378,21 @@ files = [ [[package]] name = "super-collections" -version = "0.5.0" +version = "0.5.3" description = "file: README.md" optional = false python-versions = ">=3.8" files = [ - {file = "super_collections-0.5.0-py3-none-any.whl", hash = "sha256:95db6b400fc16e29087ba1188eada9669fb8b8a4c4c4e6b4c63037e2e8166798"}, - {file = "super_collections-0.5.0.tar.gz", hash = "sha256:d4a38a0eefa7d02a46d65594437cff94b54cf84c8ee62f9cc4067eca38462ebb"}, + {file = "super_collections-0.5.3-py3-none-any.whl", hash = "sha256:907d35b25dc4070910e8254bf2f5c928348af1cf8a1f1e8259e06c666e902cff"}, + {file = "super_collections-0.5.3.tar.gz", hash = "sha256:94c1ec96c0a0d5e8e7d389ed8cde6882ac246940507c5e6b86e91945c2968d46"}, ] [package.dependencies] hjson = "*" +[package.extras] +test = ["pytest (>=7.0)"] + [[package]] name = "termcolor" version = "2.5.0" diff --git a/pyproject.toml b/pyproject.toml index 271c9f9c..d6b87976 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "lookit-jspsych" -version = "0.1.0" +version = "0.0.0" description = "" authors = ["Your Name "] readme = "README.md" @@ -10,7 +10,6 @@ python = "^3.12" mkdocs = "^1.6.0" mkdocs-macros-plugin = "^1.0.5" - [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/exit_survey_parameters.js b/tests/exit_survey_parameters.js new file mode 100644 index 00000000..5d7baa7d --- /dev/null +++ b/tests/exit_survey_parameters.js @@ -0,0 +1,55 @@ +const jsPsych = initJsPsych(); + +const consentSurvey = { + type: chsSurvey.ConsentSurveyPlugin, + survey_json: { + elements: [ + { + isRequired: true, + name: "ParticipateBoolean", + title: "Would you like to participate in this study?", + type: "boolean", + valueFalse: "no", + valueTrue: "yes", + validators: [ + { + type: "expression", + text: "You must first agree to participate in order to continue with the study.", + expression: "{ParticipateBoolean}='yes'", + }, + ], + }, + { + type: "checkbox", + name: "UnderstandVoluntary", + title: "Please check the box to confirm.", + choices: { + value: "yes", + text: "I understand that my participation in this study is voluntary.", + }, + isRequired: true, + showTitle: true, + }, + ], + showQuestionNumbers: false, + checkErrorsMode: "onValueChanged", + textUpdateMode: "onTyping", + }, +}; + +const trial = { + type: jsPsychHtmlKeyboardResponse, + stimulus: + "

This is a jsPsych study running on CHS!

Press any key to end the study.

", +}; + +// Parameter variations for the exit survey +const exitSurvey = { + type: chsSurvey.ExitSurveyPlugin, + show_databrary_options: false, + include_withdrawal_example: false, + private_level_only: true, + additional_video_privacy_text: "Here is some additional text!", +}; + +jsPsych.run([consentSurvey, trial, exitSurvey]); diff --git a/tests/survey_consent_no_recording.js b/tests/survey_consent_no_recording.js new file mode 100644 index 00000000..db6928d7 --- /dev/null +++ b/tests/survey_consent_no_recording.js @@ -0,0 +1,55 @@ +const jsPsych = initJsPsych(); + +// This is a custom consent survey. It uses the jsPsych survey plugin +// which creates a survey using 'survey_json' and/or 'survey_function' parameters. +// The plugin uses SurveyJS. See the jsPsych docs and SurveyJS docs for all question types and options. +// https://www.jspsych.org/latest/plugins/survey/ +// https://surveyjs.io/form-library/documentation/overview +const consentSurvey = { + type: chsSurvey.ConsentSurveyPlugin, + survey_json: { + elements: [ + { + isRequired: true, + name: "ParticipateBoolean", + title: "Would you like to participate in this study?", + type: "boolean", + valueFalse: "no", + valueTrue: "yes", + validators: [ + { + type: "expression", + text: "You must first agree to participate in order to continue with the study.", + expression: "{ParticipateBoolean}='yes'", + }, + ], + }, + { + type: "checkbox", + name: "UnderstandVoluntary", + title: "Please check the box to confirm.", + choices: { + value: "yes", + text: "I understand that my participation in this study is voluntary.", + }, + isRequired: true, + showTitle: true, + }, + ], + showQuestionNumbers: false, + checkErrorsMode: "onValueChanged", + textUpdateMode: "onTyping", + }, +}; + +const trial = { + type: jsPsychHtmlKeyboardResponse, + stimulus: + "

This is a jsPsych study running on CHS!

Press any key to end the study.

", +}; + +const exitSurvey = { + type: chsSurvey.ExitSurveyPlugin, +}; + +jsPsych.run([consentSurvey, trial, exitSurvey]); diff --git a/tests/video_consent_session_recording.js b/tests/video_consent_session_recording.js new file mode 100644 index 00000000..351cfe53 --- /dev/null +++ b/tests/video_consent_session_recording.js @@ -0,0 +1,57 @@ +const jsPsych = initJsPsych(); + +const videoConfig = { + type: chsRecord.VideoConfigPlugin, + troubleshooting_intro: "Contact Becky Gilbert if you're having problems!", +}; + +const videoConsent = { + type: chsRecord.VideoConsentPlugin, + PIName: "Jane Smith", + institution: "Science University", + PIContact: "Jane Smith at 123 456 7890", + purpose: + "Why do babies love cats? This study will help us find out whether babies love cats because of their soft fur or their twitchy tails.", + procedures: + "Your child will be shown pictures of lots of different cats, along with noises that cats make like meowing and purring. We are interested in which pictures and sounds make your child smile. We will ask you (the parent) to turn around to avoid influencing your child's responses.", + risk_statement: + "There are no expected risks if you participate in the study. (This is optional, but should typically be included. If you leave it out there's no 'risks' section and you should include risk information elsewhere.)", + voluntary_participation: + "There are two sessions in this study; you will be invited to complete another session next month. It is okay not to do both sessions! (This is optional; leave it out if you don't need to say anything besides participation in this session being voluntary.)", + payment: + "After you finish the study, we will email you a $5 BabyStore gift card within approximately three days. To be eligible for the gift card your child must be in the age range for this study, you need to submit a valid consent statement, and we need to see that there is a child with you. But we will send a gift card even if you do not finish the whole study or we are not able to use your child's data! There are no other direct benefits to you or your child from participating, but we hope you will enjoy the experience.", + datause: + "We are primarily interested in your child's emotional reactions to the images and sounds. A research assistant will watch your video to measure the precise amount of delight in your child's face as he or she sees each cat picture.", + include_databrary: true, + additional_video_privacy_statement: + "We will also ask your permission to use your videos as stimuli for other parents. (This is optional; leave it out if there aren't additional ways you'll share video beyond as described in the participant's video privacy level and Databrary selections.)", + gdpr: false, + research_rights_statement: + "You are not waiving any legal claims, rights or remedies because of your participation in this research study. If you feel you have been treated unfairly, or you have questions regarding your rights as a research subject, you may contact the [IRB NAME], [INSTITUTION], [ADDRESS/CONTACT]", + additional_segments: [ + { + title: "US Patriot Act Disclosure", + text: "[EXAMPLE ONLY, PLEASE REMOVE ADDITIONAL_SEGMENTS UNLESS YOU NEED THEM.] Lookit is a U.S. organization and all information gathered from the website is stored on servers based in the U.S. Therefore, your video recordings are subject to U.S. laws, such as the US Patriot Act. This act allows authorities access to the records of internet service providers. If you choose to participate in this study, you understand that your video recording will be stored and accessed in the USA. The security and privacy policy for Lookit can be found at the following link: https://lookit.mit.edu/privacy/.", + }, + ], +}; + +const startRec = { + type: chsRecord.StartRecordPlugin, +}; + +const trial = { + type: jsPsychHtmlKeyboardResponse, + stimulus: + "

This is a jsPsych study running on CHS!

Press any key to end the study.

", +}; + +const stopRec = { + type: chsRecord.StopRecordPlugin, +}; + +const exitSurvey = { + type: chsSurvey.ExitSurveyPlugin, +}; + +jsPsych.run([videoConfig, videoConsent, startRec, trial, stopRec, exitSurvey]); diff --git a/tests/video_consent_trial_recording.js b/tests/video_consent_trial_recording.js new file mode 100644 index 00000000..a253c696 --- /dev/null +++ b/tests/video_consent_trial_recording.js @@ -0,0 +1,60 @@ +const jsPsych = initJsPsych({ + extensions: [ + { + type: chsRecord.TrialRecordExtension, + }, + ], +}); + +const videoConfig = { + type: chsRecord.VideoConfigPlugin, + troubleshooting_intro: "Contact Becky Gilbert if you're having problems!", +}; + +const videoConsent = { + type: chsRecord.VideoConsentPlugin, + PIName: "Jane Smith", + institution: "Science University", + PIContact: "Jane Smith at 123 456 7890", + purpose: + "Why do babies love cats? This study will help us find out whether babies love cats because of their soft fur or their twitchy tails.", + procedures: + "Your child will be shown pictures of lots of different cats, along with noises that cats make like meowing and purring. We are interested in which pictures and sounds make your child smile. We will ask you (the parent) to turn around to avoid influencing your child's responses.", + risk_statement: + "There are no expected risks if you participate in the study. (This is optional, but should typically be included. If you leave it out there's no 'risks' section and you should include risk information elsewhere.)", + voluntary_participation: + "There are two sessions in this study; you will be invited to complete another session next month. It is okay not to do both sessions! (This is optional; leave it out if you don't need to say anything besides participation in this session being voluntary.)", + payment: + "After you finish the study, we will email you a $5 BabyStore gift card within approximately three days. To be eligible for the gift card your child must be in the age range for this study, you need to submit a valid consent statement, and we need to see that there is a child with you. But we will send a gift card even if you do not finish the whole study or we are not able to use your child's data! There are no other direct benefits to you or your child from participating, but we hope you will enjoy the experience.", + datause: + "We are primarily interested in your child's emotional reactions to the images and sounds. A research assistant will watch your video to measure the precise amount of delight in your child's face as he or she sees each cat picture.", + include_databrary: true, + additional_video_privacy_statement: + "We will also ask your permission to use your videos as stimuli for other parents. (This is optional; leave it out if there aren't additional ways you'll share video beyond as described in the participant's video privacy level and Databrary selections.)", + gdpr: false, + research_rights_statement: + "You are not waiving any legal claims, rights or remedies because of your participation in this research study. If you feel you have been treated unfairly, or you have questions regarding your rights as a research subject, you may contact the [IRB NAME], [INSTITUTION], [ADDRESS/CONTACT]", + additional_segments: [ + { + title: "US Patriot Act Disclosure", + text: "[EXAMPLE ONLY, PLEASE REMOVE ADDITIONAL_SEGMENTS UNLESS YOU NEED THEM.] Lookit is a U.S. organization and all information gathered from the website is stored on servers based in the U.S. Therefore, your video recordings are subject to U.S. laws, such as the US Patriot Act. This act allows authorities access to the records of internet service providers. If you choose to participate in this study, you understand that your video recording will be stored and accessed in the USA. The security and privacy policy for Lookit can be found at the following link: https://lookit.mit.edu/privacy/.", + }, + ], +}; + +const trial = { + type: jsPsychHtmlKeyboardResponse, + stimulus: + "

This is a jsPsych study running on CHS!

Press any key to end the study.

", + extensions: [ + { + type: chsRecord.TrialRecordExtension, + }, + ], +}; + +const exitSurvey = { + type: chsSurvey.ExitSurveyPlugin, +}; + +jsPsych.run([videoConfig, videoConsent, trial, exitSurvey]);