// Counting completed questionnaires
$finished = statistic('count', 'FINISHED');
// Filter to stop Quota
if ($finished >= 250) {
// Show text
text('QS01');
// go on hiding the button
buttonHide();
// Stop showing new content of this page
pageStop();
}
**Note**:This PHD-Code counts evry case with MODE "Interview" and FINISHED 1.
These can also be cases where a screenout occurs using redirect(). In debug mode, the cases are counted independently of the MODE variable.
Do not use the count of completed cases using 'FINISHED' in addition to a more differentiated quota. Cases that were rejected due to a quota-out using redirect() are counted as completed cases by statistic('count', 'FINISHED'). In such cases, you can work with the frequency of LASTPAGE instead. If the redirect() command is on page 25 and all respondents answer page 24 beforehand, the code could look like this.
// Counting relevant questionnaires
$finished = statistic('count', 'LASTPAGE', 24);
// Filter for quota stop
// (go on just like above)
==== Filtering ====
If you want to ensure that only cases with certain properties are counted, create an [[:en:create:questions:internal|internal variable]] (in the following example "QS02_01") and only give this variable the value 2 if a case fulfills the desired criteria. For example, if the following PHP code is placed on the penultimate page of the questionnaire, then only cases that reach the penultimate page and have taken at least 10 minutes (600 seconds) to get there will be counted.
$time = caseTime('hitherto');
if ($time >= 600) {
put('QS02_01', 2);
}
The actual quote stop then no longer refers to the "FINISHED" variable, but to the internal variable. The following PHP code would then be placed on the first page of the questionnaire, for example.
// Count valid questionnaires
$valid = statistic('count', 'QS02_01', 2);
// Filter just like above
if ($valid >= 250) {
text('QS01');
buttonHide();
pageStop();
}
====== Functionality ======
The quota limits by demographic groups includes the following steps:
- Query the quota characteristics of a person using ''[[:de:create:functions:value]]''.
- Counting how many persons with this characteristic value or with this combination of characteristic values have already been surveyed using ''[[:de:create:functions:statistic]]''.
- Comparison of the count with a quota table showing how many people are needed per demographic cell.
- Rejecting people if there are already enough cases for a demographic cell, using ''[[:de:create:functions:redirect]]''.
The term "demographic cell" or "demographic group" means a combination of the characteristics to be quoted (e.g. "male participants between 15 and 25 years" if age and gender are quoted).
In principle, unrelated and related quota can be examined. In the case of unrelated quota, the characteristics are checked one after the other ("Are there already enough questionnaires from male participants? Are there already enough questionnaires from young participants?"). Here the marginal sums of the distribution are checked. Accordingly, in the unrelated quota system, it would be possible to fill the quota with young women and old men -- without any young men.
In the linked quota system, the demographic cells are checked ("Are there already enough questionnaires from young male participants?"). The linked quota system is thus less susceptible to systematic distortions (conflicting demographic characteristics) -- but more potential participants are also rejected.
Please note the interaction between ''statistic()'' and ''redirect()'': The function ''redirect()'' marks the questionnaires of rejected participants as "completed" (FINISHED=1), so that they are counted by ''statistic()''. In order to avoid wrong counts, the variables that were queried in the questionnaire are not counted directly: Instead, with the Complete-Redirect the quota characteristics are copied to [[:de:create:questions:internal|internal variables]] by means of [[:de:create:functions:put]], which can then be counted. Thus, 2 separate PHP codes are required for the quota.
- After querying the quota characteristics (on the following page), the value specified by the participant is checked against the number of existing cases.
- Directly before the Complete-Redirect, the value of the quota characteristic is copied into an internal variable.
If the quota feature has to be recoded first, this PHP code is used in both parts.
===== Technical implementation (unrelated quotas) ====
The technical implementation is done by [[:en:create:php]] and comprises several steps. These are first explained here for the verification of unrelated quotas.
In the following two characteristics are quoted as examples: Age (open query in variable "SD01_01") and gender (closed query in variable "SD02"). It is advisable to define quote questions as [[:en:create:checks#response compulsive|mandatory questions]].
==== Definition of quota ====
First, a list ([[:en:create:array|Array]]) is required, which specifies the desired number of participants for each characteristic value. For unrelated quota, one quota table per feature is required.
In the following example, the screenout is integrated directly in the quota check. However, an upstream screenout would also be possible.
$quotaAge = [
2 => 200, // 200 persons in age group 2 (18-30 years)
3 => 250, // 250 persons in age group 3 (31-50 years)
4 => 150 // 150 persons in age group 4 (51-68 years)
];
$quotaGender = [
1 => 300, // 300 women (code 1)
2 => 300 // 300 men (code 2)
];
Please note that there are no entries in the table for age groups 1 (persons under 18 years) and 5 (persons older than 68 years). The same applies to gender 3 (persons who assign themselves to another gender). This is represented in the screenout (below) by the function ''[[https://www.php.net/manual/de/function.array-key-exists.php|array_key_exists()]]''.
==== Read out and recode characteristics ====
The characteristic value of the current participant is read out using ''value()'' and, if necessary, recoded with an [[:en:create:php-filters#the_keyword_if|IF construction]]. In this case the age (open input) has to be recoded.
// Gender is read out directly
$gender = value('SD02')
// The age is recoded
$age = value('SD01_01')
if ($age < 18) {
$ageGroup = 1;
} elseif ($age <= 30) {
$ageGroup = 2;
} elseif ($age <= 50) {
$ageGroup = 3;
} elseif ($age <= 68) {
$ageGroup = 4;
} else {
$ageGroup = 5;
}
==== Counting the cases in hand ===
Now the system uses ''statistic()'' to check how many data records with the quota characteristics already exist in the data record. This value is then compared with the quota defined above.
**Important:** For the PHP code to work correctly, all parts must be in the same PHP code block. [[:en:create:variables#php-variables|PHP-Variables]] such as ''$quotaAge'' and ''$ageGroup'' are only valid within one code block.
In the following code __not__ the variables used to query the quota characteristics ("SD01_01" and "SD02") are counted, but internal variables ("SD03_01" and "SD03_02").
**Important:** The [[:en:create:questions:internal|internal variables]] must be created manually in advance in the **question catalogue**.
// Retrieval of the available cases for this characteristic
$casesAge = statistic('count', 'SD03_01', $ageGroup);
$casesGender = statistic('count', 'SD03_02', $gender);
// Screenout
if (!array_key_exists($ageGroup, $quotaAge) || !array_key_exists($gender, $quotaGender)) {
redirect('https://www.panelanbieter.de/?xyz=screenoutGHIJKI&uid=%reference%');
}
// Reading the quota for the specified characteristics
$maxPerAge = $quotaAge [$ageGroup];
$maxPerGender = $quotaGender[$gender];
// quota stop
if (($casesAge >= $maxPerAge) || ($casesGender >= $maxPerGender)) {
redirect('https://www.panelanbieter.de/?xyz=quotaABCDEF&uid=%reference%');
}
The Boolean operator ''||'' is an OR operation between the two conditions. So if there are either enough cases for age __or__ enough cases for gender, a QuotaFull redirect is performed.
You can get the redirect link from the Panel provider. It is important that you submit the participant ID. The example uses the placeholder ''%reference%''. Details can be found here: [[:en:survey:panels]].
**Important:** The participant's response will not be transmitted to the server until the participant has sent it by clicking "Next". If the questions "SD01" and "SD02" are on page 2 of the questionnaire, ''value()'' and thus the PHP code above can be placed on page 3 at the earliest.
==== Copy quota characteristics ====
Finally, the quota characteristics must be copied to the internal variables. This means directly before the Complete-Redirect, which is usually placed on the penultimate page of the questionnaire.
To do this you need __redo__ the PHP code from the section [[#Read_out_and_recode_characteristics|Read out and recode characteristics]] (above) and in the same PHP code block directly below the function ''[[:de:create:functions:put]]'' to set the internal variables.
put('SD03_01', $ageGroup);
put('SD03_02', $gender);
This is usually followed by the Complete redirect.
redirect('https://www.panelanbieter.de/?xyz=completeDEFGHI&uid=%reference%');
===== Related Quota =====
==== Quota check at the beginning ===
If you work with linked quota, you must define a larger number of quota: One threshold per demographic cell. With 3 values for age and 2 values for gender, this makes a total of 6 quota.
In the following PHP code, a demographic cell is defined by a code of age (2-4) and gender (1-2). For the sake of clarity, the 6 quota are noted in 3 lines with 2 quota each -- technically it makes no difference if the comma is followed by a line break.
$quota = [
'2-1' => 100, '2-2' => 100, // 100 persons per gender for age group 1
'3-1' => 125, '3-2' => 125, // 125 persons per gender for age group 2
'4-1' => 75, '4-2' => 75 // 75 persons per gender for age group 3
];
The readout of the quota-relevant characteristics and the recoding is initially carried out in the same way as for the unrelated quota.
// Gender is read out directly
$gender = value('SD02')
// Age is recoded
$age = value('SD01_01')
if ($age < 18) {
$ageGroup = 1;
} elseif ($age <= 30) {
$ageGroup = 2;
} elseif ($age <= 50) {
$ageGroup = 3;
} elseif ($age <= 68) {
$ageGroup = 4;
} else {
$ageGroup = 5;
}
However, an additional variable is defined, which contains the code of the demographic cell. The dot (''.'') is used in PHP to join texts (concatenate strings).
// Definition of a variable with age group and gender
// 3 and 2 become '3-2' here
$demGroup = $ageGroup.'-'.$gender
The quota check is now performed for this (combined) characteristic. Again, an internal variable (SD04_01) is used, which must be defined in advance in the **question catalog**.
// Retrieval of the available cases for this characteristic
$cases = statistic('count', 'SD04_01', $demGroup);
// screenout
if (!array_key_exists($demGroup, $quota)) {
redirect('https://www.panelanbieter.de/?xyz=screenoutGHIJKI&uid=%reference%');
}
// Reading the quota for the demographic group
$maxPerGroup = $quota [$demGroup];
// quota stop
if ($cases >= $maxPerGroup) {
redirect('https://www.panelanbieter.de/?xyz=quotaABCDEF&uid=%reference%');
}
==== Saving the cell code ====
Before the Complete-Redirect, the code for the demographic cell must be saved in the internal variable.
// Saving the group membership in the internal variable SD04_01
put('SD04_01', $demGroup);
In order for this code to work, the quota characteristics must also be retrieved and recoded. And of course they have to be concatenated to the cell code ''$demGroup''. The complete code for the penultimate questionnaire page including Complete-Redirect looks like this
// Gender is read out directly
$gender = value('SD02')
// Age is recoded
$age = value('SD01_01')
if ($age < 18) {
$ageGroup = 1;
} elseif ($age <= 30) {
$ageGroup = 2;
} elseif ($age <= 50) {
$ageGroup = 3;
} elseif ($age <= 68) {
$ageGroup = 4;
} else {
$ageGroup = 5;
}
// Definition of a variable with age group and gender
// 3 and 2 become '3-2' here
$demGroup = $ageGroup.'-'.$gender
// Store the group membership in the internal variable SD04_01
put('SD04_01', $demGroup);
// Complete-Redirect
redirect('https://www.panelanbieter.de/?xyz=completeDEFGHI&uid=%reference%');
===== Searching for mistakes =====
The implementation of quotas is not entirely trivial at the moment (optimization in progress) --
and mistakes keep appearing in the code. For example, cases are rejected even though the quotas have not yet been met. For example, cases are rejected even though quotas have not yet been met.
We expect you to be familiar with the function about Debug-Information (see [[:de:create:debugging]]).
Two modifications are very helpful to show all important information for the search of mistakes.
==== (1) Redirects should be temporary deactivated ====
You should deactivate the redirects so that you can see the Debug-Information. You can comment out the ''redirect()'' with two slashes (''%%//%%'') and thus deactivate it. Add ''[[:de:create:functions:pagestop]]'', instead so that no further content is executed on the page. Using ''html()'' you can see one short information if your IF filter looks like this in productive operation...
// Quotenstopp
if (($casesAge >= $maxPerAge) || ($casesGender >= $maxPerGender)) {
redirect('https://www.panelanbieter.de/?xyz=quotaABCDEF&uid=%reference%');
}
... dann ändern Sie den Code zur Fehlersuche wie folgt.
// Quotenstopp
if (($casesAge >= $maxPerAge) || ($casesGender >= $maxPerGender)) {
// redirect('https://www.panelanbieter.de/?xyz=quotaABCDEF&uid=%reference%');
html('Redirect ausgelöst
');
pageStop();
}
==== (2) Show quota count ====
In the debug information, you can already see which values ''statistic()'' returns. But with the function ''[[:en:create:functions:debug]]'' you can also display which values your quotation (the array with the quotas) returns for the current case.
Simply add a line ''debug()'' for all filter-relevant variables. For the filter above, this would look like this.
// Show additional information
debug($casesAge);
debug($maxPerAge);
debug($casesGender);
debug($maxPerGender);
// Quota stop
if (($casesAge >= $maxPerAge) || ($casesGender >= $maxPerGender)) {
// redirect('https://www.panelanbieter.de/?xyz=quotaABCDEF&uid=%reference%');
html('Redirect ausgelöst
');
pageStop();
}
This allows you to immediately understand why a redirect is triggered. If you are still unable to understand why these (possibly incorrect) values are averaged, display other variables, e.g. where the quota limits are read out. The code in the example above is as follows.
// Read out the quotas for the specified characteristics
$maxPerAge = $quotaAge[$ageGroup];
$maxPerGender = $quotaGender[$gender];
For example, if you do not know where the value for ''$maxPerAge'' comes from, you can display the array and the age category.
// Read out the quotas for the specified characteristics
$maxPerAge = $quotaAge[$ageGroup];
$maxPerGender = $quotaGender[$gender];
// Show additional information
debug($ageGroup);
debug($quotaAge);
debug($maxPerAge);
These information are helping too in [[https://support.soscisurvey.de/|Online-Support]], if you need support with the implementation of your filters.