From d7cd0b371d4e96917fa787bbbd16fa939ed2be18 Mon Sep 17 00:00:00 2001
From: "Ruben C. Arslan"
Date: Wed, 30 Oct 2013 19:09:08 +0100
Subject: [PATCH 1/8] removed old remnant, reference to `users` table
---
webroot/admin/run/user_overview.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/webroot/admin/run/user_overview.php b/webroot/admin/run/user_overview.php
index aa9759617..f04f6057a 100755
--- a/webroot/admin/run/user_overview.php
+++ b/webroot/admin/run/user_overview.php
@@ -16,7 +16,7 @@
`survey_run_sessions`.created,
`survey_runs`.name AS run_name,
`survey_units`.type AS unit_type,
- `users`.`email`,
+ `survey_users`.`email`,
DATEDIFF(NOW(), `survey_run_sessions`.last_access) AS last_access_days,
(`survey_units`.type IN ('Survey','External') AND DATEDIFF(NOW(), `survey_run_sessions`.last_access) >= 2) AS hang
@@ -32,8 +32,8 @@
LEFT JOIN `survey_runs`
ON `survey_run_sessions`.run_id = `survey_runs`.id
-LEFT JOIN `users`
-ON `survey_run_sessions`.session = `users`.code
+LEFT JOIN `survey_users`
+ON `survey_run_sessions`.session = `survey_users`.user_code
WHERE `survey_runs`.name = :run_name
From 6fc2e0be0a82e01f078c1e653313186d66cb58eb Mon Sep 17 00:00:00 2001
From: "Ruben C. Arslan"
Date: Wed, 30 Oct 2013 19:09:41 +0100
Subject: [PATCH 2/8] branch now uses R :-)
---
Model/Branch.php | 83 +++++++++++++++++++------------------------
Model/OpenCPU.php | 62 ++++++++++++++++++++++++--------
Model/RunUnit.php | 42 ++++++++++++----------
Model/Site.php | 6 ++++
webroot/assets/run.js | 2 +-
5 files changed, 114 insertions(+), 81 deletions(-)
diff --git a/Model/Branch.php b/Model/Branch.php
index 955e4a3e6..525baf7ac 100644
--- a/Model/Branch.php
+++ b/Model/Branch.php
@@ -87,32 +87,35 @@ public function removeFromRun($run_id)
}
public function test()
{
- $join = join_builder($this->dbh, $this->condition);
-$q = "SELECT DISTINCT ( {$this->condition} ) AS test,`survey_run_sessions`.session FROM `survey_run_sessions`
-
-$join
-
-WHERE
- `survey_run_sessions`.run_id = :run_id
+ $q = "SELECT `survey_run_sessions`.session,`survey_run_sessions`.id FROM `survey_run_sessions`
-ORDER BY IF(ISNULL( ( {$this->condition} ) ),1,0), RAND()
+ WHERE
+ `survey_run_sessions`.run_id = :run_id
-LIMIT 20";
+ ORDER BY RAND()
- echo "$q
";
- $evaluate = $this->dbh->prepare($q); // should use readonly
- $evaluate->bindParam(':run_id',$this->run_id);
+ LIMIT 20";
+ $get_sessions = $this->dbh->prepare($q); // should use readonly
+ $get_sessions->bindParam(':run_id',$this->run_id);
- $evaluate->execute() or die(print_r($evaluate->errorInfo(), true));
- if($evaluate->rowCount()>=1):
+ $get_sessions->execute() or die(print_r($get_sessions->errorInfo(), true));
+ if($get_sessions->rowCount()>=1):
$results = array();
- while($temp = $evaluate->fetch())
+ while($temp = $get_sessions->fetch())
$results[] = $temp;
else:
- echo 'Nothing found';
+ echo 'No data to compare to yet.';
return false;
endif;
-
+
+ $openCPU = $this->makeOpenCPU();
+ $this->run_session_id = current($results)['id'];
+
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$this->condition)
+ ));
+ echo $openCPU->isTrueAdmin($this->condition);
+
echo '
Code |
@@ -120,47 +123,33 @@ public function test()
"';
foreach($results AS $row):
+ $openCPU = $this->makeOpenCPU();
+ $this->run_session_id = $row['id'];
+
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$this->condition)
+ ));
+
echo "
{$row['session']} |
- ".h((int)$row['test'])." |
+ ".stringBool($openCPU->isTrue($this->condition) )." |
";
endforeach;
echo '
';
}
public function exec()
{
- $join = join_builder($this->dbh, $this->condition);
-
- $q = "SELECT ( {$this->condition} ) AS test FROM `survey_run_sessions`
- $join
-
- WHERE
- `survey_run_sessions`.`id` = :run_session_id
-
- ORDER BY IF(ISNULL( ( {$this->condition} ) ),1,0), `survey_unit_sessions`.id DESC
-
- LIMIT 1";
-
-# pr($q);
- $evaluate = $this->dbh->prepare($q); // should use readonly
- $evaluate->bindParam(":run_session_id", $this->run_session_id);
+ $openCPU = $this->makeOpenCPU();
- $evaluate->execute() or die(print_r($evaluate->errorInfo(), true));
- if($evaluate->rowCount()===1):
- $temp = $evaluate->fetch();
- $result = (bool)$temp['test'];
- else:
- $result = false;
- endif;
-# pr($temp);
-# pr($this->run_session_id);
-
- // evaluate condition
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$this->condition)
+ ));
+ $result = (bool)$openCPU->isTrue($this->condition);
+ $join = join_builder($this->dbh, $this->condition);
+
$position = $result ? $this->if_true : $this->if_false;
-# $run_to_id = $this->getUnitIdAtPosition( $run_to );
-
-# $run_session = new RunSession($this->dbh, $this->run_id, $this->user_id, $this->session);
+
global $run_session;
if($run_session->session):
$this->end();
diff --git a/Model/OpenCPU.php b/Model/OpenCPU.php
index 6c309f409..b3d64789a 100644
--- a/Model/OpenCPU.php
+++ b/Model/OpenCPU.php
@@ -40,13 +40,39 @@ private function identity($post, $return = '/json',$headers = false)
return $this->r_function('base/R/identity'.$return, $post, $headers);
}
- public function isTrue($source,$return = 'json',$headers = false)
+ public function isTrue($source,$return = '/json',$headers = false)
{
- $post = array('x' => '{ $source }');
- return $this->identity($post,$return,$headers);
+ $post = array('x' => '{
+ (function() {
+ '.$this->user_data.'
+ '.$source.'
+ })() }');
+
+ $result = $this->identity($post,$return,$headers);
+ $parsed = json_decode($result);
+ if($parsed===null):
+ alert($result,'alert-error');
+ alert("".$source."
",'alert-error');
+ return null;
+ elseif(empty($parsed)):
+ return null;
+ else:
+ return $parsed[0];
+ endif;
+ }
+ public function isTrueAdmin($source,$return = '',$headers = true)
+ {
+ $post = array('x' => '{
+ (function() {
+ '.$this->user_data.'
+ '.$source.'
+ })() }');
+
+ $result = $this->identity($post,$return,$headers);
+ return $this->debugCall($result);
}
- public function knit($source,$return,$headers = false)
+ public function knit($source,$return = '/json',$headers = false)
{
$post = array('x' => '{
library(knitr)
@@ -127,26 +153,32 @@ public function selftest()
private function debugCall($results)
{
list($header, $results) = explode("\r\n\r\n", $results, 2);
-
if($this->http_status > 302):
$response = array(
'Response' => ''. htmlspecialchars($results). '
',
'HTTP headers' => ''. htmlspecialchars($header). '
',
);
else:
- list($first) = explode("\n",$results);
+ $available = explode("\n",$results);
- $session = explode('/',$first);
+ $session = explode('/',$available[0]);
$session = '/'.$session[1].'/'.$session[2] .'/'.$session[3] . '/';
// info/text stdout/text console/text R/.val/text
-
- $response = array(
- 'Result' => file_get_contents($this->instance. $session . 'R/.val/text'),
- 'Console' => ''. htmlspecialchars(file_get_contents($this->instance. $session . 'console/text')).'
',
- 'Stdout' => ''. htmlspecialchars(file_get_contents($this->instance. $session . 'stdout/text')). '
',
- 'HTTP headers' => ''. htmlspecialchars($header). '
',
- 'Session info' => ''. htmlspecialchars(file_get_contents($this->instance. $session . 'info/text')). '
'
- );
+
+ $response = array();
+ if(in_array($session . 'R/.val',$available))
+ $response['Result'] = file_get_contents($this->instance. $session . 'R/.val/text');
+
+ if(in_array($session . 'console',$available))
+ $response['Console'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'console/text')).'
';
+ if(in_array($session . 'stdout',$available))
+
+ $response['Stdout'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'stdout/text')). '
';
+
+ $response['HTTP headers'] = ''. htmlspecialchars($header). '
';
+
+ if(in_array($session . 'info',$available))
+ $response['Session info'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'info/text')). '
';
endif;
return $this->ArrayToAccordion($response);
diff --git a/Model/RunUnit.php b/Model/RunUnit.php
index c5f4cfd5c..3cf920c4e 100644
--- a/Model/RunUnit.php
+++ b/Model/RunUnit.php
@@ -171,23 +171,8 @@ public function displayForRun($prepend = '')
{
return parent::runDialog($prepend,'');
}
- private function getUserDataInRun()
+ protected function getUserDataInRun($surveys)
{
- $result_tables = $this->dbh->prepare("SELECT `survey_studies`.name FROM survey_run_units
- LEFT JOIN survey_units
- ON `survey_units`.id = `survey_run_units`.unit_id
- LEFT JOIN `survey_studies`
- ON `survey_units`.id = `survey_studies`.id
-
- WHERE `survey_run_units`.run_id = :run_id
- AND `survey_units`.type = 'Survey'");
-
- $result_tables->bindParam(":run_id",$this->run_id);
- $result_tables->execute();
- $surveys = array();
- while($res = $result_tables->fetch(PDO::FETCH_ASSOC))
- $surveys[] = $res['name'];
-
$results = array();
foreach($surveys AS $survey_name):
$get_results = $this->dbh->prepare("SELECT `survey_run_sessions`.session, `$survey_name`.* FROM `$survey_name`
@@ -213,7 +198,7 @@ private function getUserDataInRun()
endforeach;
return $results;
}
- private function makeOpenCPU()
+ protected function makeOpenCPU()
{
require_once INCLUDE_ROOT . "Model/OpenCPU.php";
@@ -223,7 +208,6 @@ private function makeOpenCPU()
global $settings;
$openCPU = new OpenCPU($settings['opencpu_instance']);
- $openCPU->addUserData($this->getUserDataInRun());
return $openCPU;
}
private function knittingNeeded($source)
@@ -233,6 +217,20 @@ private function knittingNeeded($source)
else
return false;
}
+ protected function dataNeeded($fdb,$q)
+ {
+ $result_tables = $fdb->query("SELECT name FROM `survey_studies`");
+ $tables = array();
+
+ while($res = $result_tables->fetch(PDO::FETCH_ASSOC)):
+ $result = $res['name'];
+ if(preg_match("/($result\\\$|$result\\[)/",$q)):
+ $tables[] = $result;
+ endif;
+ endwhile;
+
+ return $tables;
+ }
public function getParsedBodyAdmin($source)
{
$q = "SELECT id,run_session_id FROM `survey_unit_sessions`
@@ -255,6 +253,11 @@ public function getParsedBodyAdmin($source)
if($this->knittingNeeded($source)):
$openCPU = $this->makeOpenCPU();
+
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$source)
+ ));
+
return $openCPU->knitForAdminDebug($source);
else:
return $this->body_parsed;
@@ -282,6 +285,9 @@ public function getParsedBody($source)
else
{
$openCPU = $this->makeOpenCPU();
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$source)
+ ));
$report = $openCPU->knitForUserDisplay($source);
if($report)
{
diff --git a/Model/Site.php b/Model/Site.php
index 66c2694dd..80d9a9213 100644
--- a/Model/Site.php
+++ b/Model/Site.php
@@ -333,3 +333,9 @@ function makeUnit($dbh, $session, $unit)
function emptyNull(&$x){
$x = ($x=='') ? null : $x;
}
+function stringBool($x)
+{
+ if($x===false) return 'false';
+ elseif($x===true) return 'true';
+ else return 'null';
+}
\ No newline at end of file
diff --git a/webroot/assets/run.js b/webroot/assets/run.js
index 9cf7cac2d..ef4bd5527 100644
--- a/webroot/assets/run.js
+++ b/webroot/assets/run.js
@@ -79,7 +79,7 @@ RunUnit.prototype.test = function(e)
.done($.proxy(function(data)
{
- var $modal = $($.parseHTML(''));
+ var $modal = $($.parseHTML(''));
$modal.modal('show');
$("#opencpu_accordion").collapse({toggle:true});
},this))
From f8757ca6ebe7afe215907cf427b706c7fd1a4848 Mon Sep 17 00:00:00 2001
From: "Ruben C. Arslan"
Date: Thu, 31 Oct 2013 15:04:50 +0100
Subject: [PATCH 3/8] switching to R
---
Model/Branch.php | 15 ++--
Model/Email.php | 83 +++++++++-----------
Model/Item.php | 9 ++-
Model/OpenCPU.php | 11 +--
Model/Pause.php | 45 +++++++----
Model/Site.php | 4 +-
Model/Survey.php | 20 ++++-
documentation/Example files/all_widgets.xls | Bin 49664 -> 50688 bytes
webroot/assets/run.js | 7 +-
9 files changed, 112 insertions(+), 82 deletions(-)
diff --git a/Model/Branch.php b/Model/Branch.php
index 525baf7ac..fd73ccd13 100644
--- a/Model/Branch.php
+++ b/Model/Branch.php
@@ -87,12 +87,12 @@ public function removeFromRun($run_id)
}
public function test()
{
- $q = "SELECT `survey_run_sessions`.session,`survey_run_sessions`.id FROM `survey_run_sessions`
+ $q = "SELECT `survey_run_sessions`.session,`survey_run_sessions`.id,`survey_run_sessions`.position FROM `survey_run_sessions`
WHERE
`survey_run_sessions`.run_id = :run_id
- ORDER BY RAND()
+ ORDER BY `survey_run_sessions`.position DESC,RAND()
LIMIT 20";
$get_sessions = $this->dbh->prepare($q); // should use readonly
@@ -114,11 +114,11 @@ public function test()
$openCPU->addUserData($this->getUserDataInRun(
$this->dataNeeded($this->dbh,$this->condition)
));
- echo $openCPU->isTrueAdmin($this->condition);
+ echo $openCPU->evaluateAdmin($this->condition);
echo '
- Code |
+ Code (Position) |
Test |
"';
@@ -131,11 +131,12 @@ public function test()
));
echo "
- {$row['session']} |
- ".stringBool($openCPU->isTrue($this->condition) )." |
+ {$row['session']} ({$row['position']}) |
+ ".stringBool($openCPU->evaluate($this->condition) )." |
";
endforeach;
echo '
';
+ $this->run_session_id = null;
}
public function exec()
{
@@ -145,7 +146,7 @@ public function exec()
$openCPU->addUserData($this->getUserDataInRun(
$this->dataNeeded($this->dbh,$this->condition)
));
- $result = (bool)$openCPU->isTrue($this->condition);
+ $result = (bool)$openCPU->evaluate($this->condition);
$join = join_builder($this->dbh, $this->condition);
$position = $result ? $this->if_true : $this->if_false;
diff --git a/Model/Email.php b/Model/Email.php
index d6bd345e6..48ffa1a0b 100644
--- a/Model/Email.php
+++ b/Model/Email.php
@@ -140,7 +140,7 @@ public function displayForRun($prepend = '')
@@ -154,35 +154,16 @@ public function displayForRun($prepend = '')
}
public function getRecipientField()
{
- if($this->recipient_field === null OR trim($this->recipient_field)=='')
- $this->recipient_field = '`survey_users`.email';
-
- $join = join_builder($this->dbh, $this->recipient_field);
-
-$q = "SELECT {$this->recipient_field} AS email_field FROM `survey_run_sessions`
-
-$join
+ $openCPU = $this->makeOpenCPU();
-WHERE `survey_run_sessions`.id = :run_session_id
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$this->recipient_field)
+ ));
-ORDER BY IF(ISNULL(email_field),1,0), `survey_unit_sessions`.id DESC
-
-LIMIT 1";
-
-#pr($q);
-#pr($this->run_session_id);
-
- $g_email = $this->dbh->prepare($q); // should use readonly
- $g_email->bindParam(":run_session_id", $this->run_session_id);
+ if($this->recipient_field === null OR trim($this->recipient_field)=='')
+ $this->recipient_field = 'survey_users$email';
- $g_email->execute() or die(print_r($g_email->errorInfo(), true));
- if($g_email->rowCount()===1):
- $temp = $g_email->fetch(PDO::FETCH_ASSOC);
- $email = $temp['email_field'];
- else:
- $email = '';
- endif;
- return $email;
+ return $openCPU->evaluate($this->recipient_field);
}
public function sendMail($who = NULL)
{
@@ -236,47 +217,53 @@ public function test()
echo $this->getBody();
+
if($this->recipient_field === null OR trim($this->recipient_field)=='')
- $this->recipient_field = '`survey_users`.email';
+ $this->recipient_field = 'survey_users$email';
- $join = join_builder($this->dbh, $this->recipient_field);
-
-$q = "SELECT DISTINCT {$this->recipient_field} AS email,`survey_run_sessions`.session FROM `survey_run_sessions`
+ $q = "SELECT `survey_run_sessions`.session,`survey_run_sessions`.id,`survey_run_sessions`.position FROM `survey_run_sessions`
-$join
+ WHERE
+ `survey_run_sessions`.run_id = :run_id
-WHERE `survey_run_sessions`.run_id = :run_id
-AND email IS NOT NULL
+ ORDER BY `survey_run_sessions`.position DESC,RAND()
-ORDER BY RAND()
-LIMIT 20";
-#echo $q;
- $g_email = $this->dbh->prepare($q); // should use readonly
- $g_email->bindParam(':run_id',$this->run_id);
+ LIMIT 20";
+ $get_sessions = $this->dbh->prepare($q); // should use readonly
+ $get_sessions->bindParam(':run_id',$this->run_id);
- $g_email->execute() or die(print_r($g_email->errorInfo(), true));
- if($g_email->rowCount()>=1):
+ $get_sessions->execute() or die(print_r($get_sessions->errorInfo(), true));
+ if($get_sessions->rowCount()>=1):
$results = array();
- while($temp = $g_email->fetch())
+ while($temp = $get_sessions->fetch())
$results[] = $temp;
else:
- echo 'Nothing found';
+ echo 'No data to compare to yet.';
return false;
endif;
-
+
echo '
- Code |
- Email |
+ Code (Position) |
+ Test |
"';
foreach($results AS $row):
+ $openCPU = $this->makeOpenCPU();
+ $this->run_session_id = $row['id'];
+
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$this->recipient_field)
+ ));
+ $email = stringBool($openCPU->evaluate($this->recipient_field) );
+ $good = filter_var( $email, FILTER_VALIDATE_EMAIL) ? '' : 'warning';
echo "
- {$row['session']} |
- ".h($row['email'])." |
+ {$row['session']} ({$row['position']}) |
+ ".$email." |
";
endforeach;
echo '
';
+ $this->run_session_id = null;
}
public function remind($who)
{
diff --git a/Model/Item.php b/Model/Item.php
index 548112a57..b21119cbc 100644
--- a/Model/Item.php
+++ b/Model/Item.php
@@ -4,11 +4,12 @@ class ItemFactory
public $errors;
private $choice_lists = array();
private $used_choice_lists = array();
+ public $skipifs = array();
function __construct($choice_lists)
{
$this->choice_lists = $choice_lists;
}
- function make($item) {
+ public function make($item) {
$type = $item['type'];
if(isset($item['choice_list']) AND $item['choice_list']):
@@ -36,6 +37,11 @@ public function unusedChoiceLists()
array_keys($this->used_choice_lists)
);
}
+ public function skip($openCPU, $skipif)
+ {
+ $this->skipifs[$skipif] = $openCPU->evaluate($skipif);
+ return $this->skipifs[$skipif];
+ }
}
// the default item is a text input, as many browser render any input type they don't understand as 'text'.
@@ -195,6 +201,7 @@ public function getResultField()
public function skip($session_id, $run_session_id, $rdb, $results_table)
{
if($this->skipif!=null):
+
if(
(strpos($this->skipif,'AND')!==false AND strpos($this->skipif,'OR')!==false) // and/or mixed?
OR strpos($this->skipif,'.') !== false // references to other tables (very simplistic check)
diff --git a/Model/OpenCPU.php b/Model/OpenCPU.php
index b3d64789a..ea7c597d7 100644
--- a/Model/OpenCPU.php
+++ b/Model/OpenCPU.php
@@ -40,7 +40,7 @@ private function identity($post, $return = '/json',$headers = false)
return $this->r_function('base/R/identity'.$return, $post, $headers);
}
- public function isTrue($source,$return = '/json',$headers = false)
+ public function evaluate($source,$return = '/json',$headers = false)
{
$post = array('x' => '{
(function() {
@@ -60,7 +60,7 @@ public function isTrue($source,$return = '/json',$headers = false)
return $parsed[0];
endif;
}
- public function isTrueAdmin($source,$return = '',$headers = true)
+ public function evaluateAdmin($source,$return = '',$headers = true)
{
$post = array('x' => '{
(function() {
@@ -170,15 +170,15 @@ private function debugCall($results)
$response['Result'] = file_get_contents($this->instance. $session . 'R/.val/text');
if(in_array($session . 'console',$available))
- $response['Console'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'console/text')).'
';
+ $response['Console'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'console/print')).'
';
if(in_array($session . 'stdout',$available))
- $response['Stdout'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'stdout/text')). '
';
+ $response['Stdout'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'stdout/print')). '
';
$response['HTTP headers'] = ''. htmlspecialchars($header). '
';
if(in_array($session . 'info',$available))
- $response['Session info'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'info/text')). '
';
+ $response['Session info'] = ''. htmlspecialchars(file_get_contents($this->instance. $session . 'info/print')). '
';
endif;
return $this->ArrayToAccordion($response);
@@ -188,6 +188,7 @@ private function ArrayToAccordion($array)
$acc = '';
$first = ' in';
foreach($array AS $title => $content):
+ if($content == null) $content = stringBool($content);
$acc .= '
diff --git a/Model/Pause.php b/Model/Pause.php
index bda1a9303..19e474729 100644
--- a/Model/Pause.php
+++ b/Model/Pause.php
@@ -132,28 +132,33 @@ public function test()
{
if($this->relative_to=== null OR trim($this->relative_to)=='')
{
- $this->relative_to = '`survey_unit_sessions`.created';
+ $this->relative_to = 'survey_unit_sessions$created';
}
- $join = join_builder($this->dbh, $this->relative_to);
+ $openCPU = $this->makeOpenCPU();
- $conditions = array();
-
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$this->relative_to)
+ ));
+ $relative_to = $openCPU->evaluate($this->relative_to);
+
+
+ $conditions = array();
if($this->wait_minutes AND $this->wait_minutes!='')
- $conditions['minute'] = "DATE_ADD({$this->relative_to}, INTERVAL :wait_minutes MINUTE) <= NOW()";
+ $conditions['minute'] = "DATE_ADD(:relative_to, INTERVAL :wait_minutes MINUTE) <= NOW()";
if($this->wait_until_date AND $this->wait_until_date != '0000-00-00')
$conditions['date'] = "CURDATE() >= :wait_date";
if($this->wait_until_time AND $this->wait_until_time != '00:00:00')
$conditions['time'] = "CURTIME() >= :wait_time";
if(isset($conditions['time']) AND !isset($conditions['date']) AND !isset($conditions['minute']))
- $conditions['date'] = "DATE_ADD({$this->relative_to}, INTERVAL 1 DAY) >= CURDATE()";
+ $conditions['date'] = "DATE_ADD(:relative_to, INTERVAL 1 DAY) >= CURDATE()";
if(!empty($conditions)):
$condition = implode($conditions," AND ");
- $order = str_replace(array(':wait_minutes',':wait_date','wait_time'),array(':wait_minutes2',':wait_date2','wait_time2'),$condition);
+ $order = str_replace(array(':wait_minutes',':wait_date',':wait_time',':relative_to'),array(':wait_minutes2',':wait_date2',':wait_time2',':relative_to2'),$condition);
$q = "SELECT DISTINCT ( {$condition} ) AS test,`survey_run_sessions`.session FROM `survey_run_sessions`
@@ -164,7 +169,7 @@ public function test()
ORDER BY IF(ISNULL($order),1,0), RAND()
-LIMIT 20";
+LIMIT 1";
echo "
$q
";
$evaluate = $this->dbh->prepare($q); // should use readonly
@@ -180,6 +185,8 @@ public function test()
$evaluate->bindParam(':wait_time',$this->wait_until_time);
$evaluate->bindParam(':wait_time2',$this->wait_until_time);
endif;
+ $evaluate->bindParam(':relative_to',$relative_to);
+ $evaluate->bindParam(':relative_to2',$relative_to);
$evaluate->bindParam(':run_id',$this->run_id);
$evaluate->execute() or die(print_r($evaluate->errorInfo(), true));
@@ -217,31 +224,34 @@ public function exec()
{
if($this->relative_to=== null OR trim($this->relative_to)=='')
{
- $this->relative_to = '`survey_unit_sessions`.created';
+ $this->relative_to = 'survey_unit_sessions$created';
}
- $join = join_builder($this->dbh, $this->relative_to);
-
+ $openCPU = $this->makeOpenCPU();
+
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh,$this->relative_to)
+ ));
+ $relative_to = $openCPU->evaluate($this->relative_to);
+
$conditions = array();
if($this->wait_minutes AND $this->wait_minutes!='')
- $conditions['minute'] = "DATE_ADD({$this->relative_to}, INTERVAL :wait_minutes MINUTE) <= NOW()";
+ $conditions['minute'] = "DATE_ADD(:relative_to, INTERVAL :wait_minutes MINUTE) <= NOW()";
if($this->wait_until_date AND $this->wait_until_date != '0000-00-00')
$conditions['date'] = "CURDATE() >= :wait_date";
if($this->wait_until_time AND $this->wait_until_time != '00:00:00')
$conditions['time'] = "CURTIME() >= :wait_time";
if(isset($conditions['time']) AND !isset($conditions['date']) AND !isset($conditions['minute']))
- $conditions['date'] = "DATE_ADD({$this->relative_to}, INTERVAL 1 DAY) >= CURDATE()";
+ $conditions['date'] = "DATE_ADD(:relative_to, INTERVAL 1 DAY) >= CURDATE()";
if(!empty($conditions)):
$condition = implode($conditions," AND ");
- $order = str_replace(array(':wait_minutes',':wait_date','wait_time'),array(':wait_minutes2',':wait_date2','wait_time2'),$condition);
+ $order = str_replace(array(':wait_minutes',':wait_date',':wait_time',':relative_to'),array(':wait_minutes2',':wait_date2',':wait_time2',':relative_to2'),$condition);
$q = "SELECT ( {$condition} ) AS test FROM `survey_run_sessions`
- $join
-
WHERE
`survey_run_sessions`.`id` = :run_session_id
@@ -261,7 +271,8 @@ public function exec()
$evaluate->bindParam(':wait_time',$this->wait_until_time);
$evaluate->bindParam(':wait_time2',$this->wait_until_time);
endif;
-
+ $evaluate->bindParam(':relative_to',$relative_to);
+ $evaluate->bindParam(':relative_to2',$relative_to);
$evaluate->bindParam(":run_session_id", $this->run_session_id);
diff --git a/Model/Site.php b/Model/Site.php
index 80d9a9213..727d4d330 100644
--- a/Model/Site.php
+++ b/Model/Site.php
@@ -337,5 +337,7 @@ function stringBool($x)
{
if($x===false) return 'false';
elseif($x===true) return 'true';
- else return 'null';
+ elseif($x===null) return 'null';
+ elseif($x===0) return '0';
+ else return $x;
}
\ No newline at end of file
diff --git a/Model/Survey.php b/Model/Survey.php
index b1844bd76..bc6184813 100644
--- a/Model/Survey.php
+++ b/Model/Survey.php
@@ -234,9 +234,25 @@ protected function getNextItems() {
{
$name = $item['name'];
$this->unanswered_batch[$name] = $item_factory->make($item);
- if($this->unanswered_batch[$name]->skipif !== null)
+ $skipif = $this->unanswered_batch[$name]->skipif;
+ if($skipif !== null)
{
- if($this->unanswered_batch[$name]->skip($this->session_id,$this->run_session_id,$this->dbh,$this->results_table))
+ if(isset($item_factory->skipifs[ $skipif ]))
+ {
+ $skip = $item_factory->skipifs[ $skipif ];
+ }
+ else
+ {
+ $openCPU = $this->makeOpenCPU();
+
+ $openCPU->addUserData($this->getUserDataInRun(
+ $this->dataNeeded($this->dbh, $skipif )
+ ));
+
+ $skip = $item_factory->skip($openCPU, $skipif );
+ }
+
+ if($skip)
{
unset($this->unanswered_batch[$name]); // todo: do something else with this when we want JS?
}
diff --git a/documentation/Example files/all_widgets.xls b/documentation/Example files/all_widgets.xls
index 9135c4eeb9c4d652a23bf2499290da40729ea05c..45782135a6dc6fdd068a107571eb4ac1a9853d7e 100644
GIT binary patch
delta 6689
zcmaJ`Yiu0V6~4P(zt+zCNt~E#yX5Jt6O!2AkgOj#!6`A2ID|)1@9gf_J9u|zGcy}!
z31z}7K!G+eg+dFI7UF~?K$C5Ypok(`sZt-PQY)lZRaF)GhhIek6(QR6J9qEg-6bTk
z?7QEbbH4Mq=iEDXj@(~!
rrzM7>o
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
delta 5877
zcmaJ_TX0n69skZ|Hg`ywQqP|KqdK`*}qb(;5qZ
zs<<=cO;?mxg~cNAm?nhgU94PQ{jms(dWDxpn#0>f#M@HSR#q-nc!-dxSPvI=pX
z_fbu=*IQHXeO6QM$+|E&dqt)9Va-9U#cQukYa6|nYFo9K_ipV8ZBt>et|zQ*^3E?^
z4g!6~__L+(!O|x~;d@#d&K8a}9Edg|*tsY?2;oaUpTCMj{QdQ4t@oZhxq#T&*{O(y
z?5dxJ7EtGl7CNr4&=ycHUAm-Xy(4S?kK(_sePLPs6I23GE}Wcw)Hr@h)QMZ}^IqOL
z5&C_DSGTKSWw{W#HLhC|a5^(qi;rB|HK)_Y*kslmGYT!cKGC#adD*^(ZLX1VWX5!*
zVOmB;-fSBt@D;}~EIEZ|^6(7wIL`6Rw42RH2amkp^mXPFU?-VzeQeT@eSv=2!v^GD
zTc0pk?d*(U$}Y>D$=Ysq+RR#p70(N?X3ZMe*AxA`966FPCT+upc_rogVzSS4T_f$t
zlws%P;Iy804Y_(Otvk+YIrUEqs^yGngZv+|tW*x@xs04Jl5pYGzy6?I2}H
zAIPQwe^{#IGrmn_GZ}reV@&HdEW7bzGu)HBq3OMf)qMXHV(_V|^Fq)yoy
zHd7OZ>&WqJdV;LW!EBlmgc9oDwWMX_IITG=#VN|5(k`A~1{W#lwzD&iVOw%MXH8Mc
zVH%1qs$vc3u8CHVj%lUxRC}2=%&|#V>PZBlTPcGyp)kg*si((bmE-U08R?}ii_2ld
z@?%Ds-SmH}F*HI08Hh
z4}CskdrZSIP!q?sa~z&3ge$F_P4Q&(bPUxq)AQMo89g`7et<-&B5J;<#js+sD+w>m
zv8m17IoHivj^9qh%#9SeISUmD3KR1?C5eipjG$NIQH;YHc_nF^V05BroW!v)BP}x+
zE)0Z@DTG&smCGdUD>QHB5sL@b_6am;JQ;(oJ+UcaQ5iN2Ba5i$M&FyRw7aQmdfZ4C
z$$k3R#&|T^n~_7tF&BiCoYF0%N2YS#gE#l)F(x^8mRc#n&02CHhn%DOeY!mb3-E;M
zgJvU7W1yt4NXTiP{e!6JEcFQ`61ho$ywGQjg((4S8I60_cdW{v(s&j1AL{E)
z49M14>$a`2*62PniPlAk$Qn|KF9&oiB?=%e59^aOPQ`VAjfMQ60qo1xlwWBRIXymY
zSe(?9F>cZ*mG**+_M?!{n=w6iV99zZqchng)+Dkx&5f3U1e(zx%~%z##H7YN*;Rjg
zYg``6_<6uKF^wcTM(#5#J!ei}UAuBNhjOI#9I6+?B6E0KVyxU*jLCIg!+u(PBztkud#XX|-#r#pcg1DybE8}il
zB@t1OD&5z_DdbsnJvs;KIg~UJ=8zYHPqX`Pwt4K
z%_kPFo#5R?F2-aEGr~0r+TbXbQL9YirMUxp){(&q@EK`&(Gc+Vnx`gSCq&Y84ms
z=d{LUyxoTqw+%&yvy$B7A?);;4{XML<4sX1rnU)OiiCI@=M>Jx+l9DY^fX*mX2K1OhVW2GvMZ_NQxI*Qi
zJ)%$r&}oG#fgV+;3h0~2Lts7%_$`I1fxa6c4VT4EeD^suQ3lkaP(*Zy8@UaB&3tOg3=RVxr>TMaz9ass;Dt6|uLi3%e
z;xc_l(J1;m6r$+w3=nl^$DnGH>y;JxIqb$QW^}O76qd65UOT1*c<_(
z2uo{E!`?pKK1zC|I*
z#jOFVW1aMOBLz{%&iFd>1WDfsm&KDFSrOzuwoG%7w3v(V=)qEH3UIfW{LUR9_H=rx6+K(7;8hxu(||IY`+
zHg+VW5IOROH}~-J{2S`oe6wxHMagU%c4^6Mo0xC54YmTa_}0F_YWo7K?VPwD!5-ze
z-4CDMlkJECB
z>>qYK6^Ihs-g&p?5YXc!j8xEn8~sku@V!oa+k5SmrW+#o{ ' + data + '
'));
- $modal.modal('show');
+ $modal.modal('show').on('hidden',function() {
+ $modal.remove();
+ });
$("#opencpu_accordion").collapse({toggle:true});
+ $modal.find('#opencpu_accordion').on('hidden', function (event) {
+ event.stopPropagation()
+ });
},this))
.fail(ajaxErrorHandling);
return false;
From 230764653102f1d24bf97920f214aadd5f75e406 Mon Sep 17 00:00:00 2001
From: "Ruben C. Arslan"
Date: Fri, 1 Nov 2013 18:15:19 +0100
Subject: [PATCH 4/8] - switched Email recipient field to R - made Email play
nice with embedded knitr figures (using a custom image upload function and
PHPmailer), though there is one problem: mailinator which we use for testing,
strips away all images and other funny business. should we, by default, send
the email to the sender? - made skipifs use R. Caching evaluated skipifs in
item_factory (per page) - refactored openCPU and RunUnit models - translated
all_widgets example table to english - added two new fields to DB scheme, to
allow for the day when we use knitr in survey item and choice labels
---
Model/Email.php | 51 +++++++---
Model/EmailAccount.php | 21 +++++
Model/Item.php | 4 +-
Model/OpenCPU.php | 92 +++++++++++++------
Model/Pause.php | 2 -
Model/RunUnit.php | 76 ++++++++++-----
Model/Survey.php | 7 +-
README.md | 2 +-
documentation/Example files/all_widgets.xls | Bin 50688 -> 53248 bytes
formr_scheme.mwb | Bin 0 -> 37840 bytes
survey_db_scheme.mwb => formr_scheme.mwb.bak | Bin
schema.sql | 4 +-
survey_db_scheme.mwb.bak | Bin 37773 -> 0 bytes
webroot/admin/mail/edit.php | 7 +-
webroot/admin/test_opencpu.php | 45 ++++++++-
15 files changed, 241 insertions(+), 70 deletions(-)
create mode 100644 formr_scheme.mwb
rename survey_db_scheme.mwb => formr_scheme.mwb.bak (100%)
delete mode 100644 survey_db_scheme.mwb.bak
diff --git a/Model/Email.php b/Model/Email.php
index 48ffa1a0b..b3c166889 100644
--- a/Model/Email.php
+++ b/Model/Email.php
@@ -13,6 +13,7 @@ class Email extends RunUnit {
private $body = null;
protected $body_parsed = null;
+ private $images = array();
private $subject = null;
private $html = null;
@@ -92,7 +93,7 @@ public function create($options)
return true;
}
- private function getBody()
+ private function getBody($embed_email = true)
{
if(isset($this->run_name))
$login_link = WEBROOT."{$this->run_name}?code={$this->session}";
@@ -100,14 +101,26 @@ private function getBody()
if($this->html):
$login_link = "Login link";
- if($this->session_id)
- $this->body_parsed = $this->getParsedBody($this->body);
- else
- $this->body_parsed = $this->getParsedBodyAdmin($this->body);
+ if($this->session_id):
+ $response = $this->getParsedBody($this->body,true);
+ $this->body_parsed = $response['body'];
+ $this->images = $response['images'];
+ else:
+ $response = $this->getParsedBodyAdmin($this->body,$embed_email);
+ if($embed_email):
+ $this->body_parsed = $response['body'];
+ $this->images = $response['images'];
+ else:
+ $this->body_parsed = $response;
+ endif;
+ endif;
+
$this->body_parsed = str_replace("{{login_link}}", $login_link , $this->body_parsed );
+ $this->body_parsed = str_replace("{{login_code}}", $this->session, $this->body_parsed);
return $this->body_parsed;
else:
$this->body = str_replace("{{login_link}}", $login_link , $this->body);
+ $this->body = str_replace("{{login_code}}", $this->session, $this->body);
return $this->body;
endif;
}
@@ -144,7 +157,7 @@ public function displayForRun($prepend = '')
- {{login_link}}
will be replaced by a personalised link to this run.
+ {{login_link}}
will be replaced by a personalised link to this run, {{login_code}}
will be replaced with this user\'s session code.
';
$dialog .= 'Save.
Test
';
@@ -156,13 +169,13 @@ public function getRecipientField()
{
$openCPU = $this->makeOpenCPU();
+ if($this->recipient_field === null OR trim($this->recipient_field)=='')
+ $this->recipient_field = 'survey_users$email';
+
$openCPU->addUserData($this->getUserDataInRun(
$this->dataNeeded($this->dbh,$this->recipient_field)
));
- if($this->recipient_field === null OR trim($this->recipient_field)=='')
- $this->recipient_field = 'survey_users$email';
-
return $openCPU->evaluate($this->recipient_field);
}
public function sendMail($who = NULL)
@@ -183,7 +196,23 @@ public function sendMail($who = NULL)
$mail->AddAddress($this->recipient);
$mail->Subject = $this->subject;
- $mail->Body = $this->getBody();
+ $mail->Body = $this->getBody();
+
+ foreach($this->images AS $image_id => $image):
+ $local_image = INCLUDE_ROOT . 'tmp/' . uniqid(). $image_id;
+ copy($image,$local_image);
+ register_shutdown_function(create_function('', "unlink('{$local_image}');"));
+
+ if (!$mail->AddEmbeddedImage(
+ $local_image,
+ $image_id,
+ $image_id,
+ 'base64',
+ 'image/png'
+ )) {
+ alert($mail->ErrorInfo,'alert-error');
+ }
+ endforeach;
if(!$mail->Send())
{
@@ -215,7 +244,7 @@ public function test()
echo "{$this->subject}
";
echo "$link
";
- echo $this->getBody();
+ echo $this->getBody(false);
if($this->recipient_field === null OR trim($this->recipient_field)=='')
diff --git a/Model/EmailAccount.php b/Model/EmailAccount.php
index ac607bd27..757cf637d 100644
--- a/Model/EmailAccount.php
+++ b/Model/EmailAccount.php
@@ -56,6 +56,27 @@ public function changeSettings($posted)
$acc->execute() or die(print_r($acc->errorInfo(), true));
return true;
}
+ public function test()
+ {
+ $RandReceiv = bin2hex(openssl_random_pseudo_bytes(5));
+ $receiver = $RandReceiv . '@mailinator.com';
+ $link = "http://{$RandReceiv}.mailinator.com";
+
+ $mail = $this->makeMailer();
+
+ $mail->AddAddress($receiver);
+ $mail->Subject = 'Test';
+ $mail->Body = 'You got mail.';
+
+ if(!$mail->Send())
+ {
+ alert($mail->ErrorInfo,'alert-error');
+ }
+ else
+ {
+ redirect_to($link);
+ }
+ }
public function makeMailer()
{
$mail = new PHPMailer();
diff --git a/Model/Item.php b/Model/Item.php
index b21119cbc..25845ee49 100644
--- a/Model/Item.php
+++ b/Model/Item.php
@@ -37,9 +37,9 @@ public function unusedChoiceLists()
array_keys($this->used_choice_lists)
);
}
- public function skip($openCPU, $skipif)
+ public function skip($results_table, $openCPU, $skipif)
{
- $this->skipifs[$skipif] = $openCPU->evaluate($skipif);
+ $this->skipifs[$skipif] = $openCPU->evaluateWith($results_table, $skipif);
return $this->skipifs[$skipif];
}
}
diff --git a/Model/OpenCPU.php b/Model/OpenCPU.php
index ea7c597d7..99c0f8962 100644
--- a/Model/OpenCPU.php
+++ b/Model/OpenCPU.php
@@ -3,13 +3,11 @@ class OpenCPU {
private $instance;
private $user_data = '';
private $curl_c;
- private $http_status = null;
+ public $http_status = null;
public function __construct($instance)
{
$this->instance = $instance;
$this->curl_c = curl_init();
-# curl_setopt($this->curl_c, CURLOPT_HTTPHEADER,array('Content-Type: application/json'));
- curl_setopt($this->curl_c, CURLOPT_POST, 1); // Method is "POST"
curl_setopt($this->curl_c, CURLOPT_RETURNTRANSFER, 1); // Returns the curl_exec string, rather than just Logical value
}
@@ -18,6 +16,7 @@ public function r_function($function,$post,$headers = false)
curl_setopt($this->curl_c, CURLOPT_URL, $this->instance.'/ocpu/library/'.$function);
if($post !== null):
+ curl_setopt($this->curl_c, CURLOPT_POST, 1); // Method is "POST"
curl_setopt($this->curl_c, CURLOPT_POSTFIELDS, http_build_query($post));
endif;
if($headers):
@@ -35,7 +34,7 @@ public function r_function($function,$post,$headers = false)
return $result;
}
- private function identity($post, $return = '/json',$headers = false)
+ public function identity($post, $return = '/json',$headers = false)
{
return $this->r_function('base/R/identity'.$return, $post, $headers);
}
@@ -60,6 +59,28 @@ public function evaluate($source,$return = '/json',$headers = false)
return $parsed[0];
endif;
}
+ public function evaluateWith($results_table, $source,$return = '/json',$headers = false)
+ {
+ $post = array('x' => '{
+ (function() {
+ '.$this->user_data.'
+ with('.$results_table.', {
+ '.$source.'
+ })
+ })() }');
+
+ $result = $this->identity($post,$return,$headers);
+ $parsed = json_decode($result);
+ if($parsed===null):
+ alert($result,'alert-error');
+ alert("".$source."
",'alert-error');
+ return null;
+ elseif(empty($parsed)):
+ return null;
+ else:
+ return $parsed[0];
+ endif;
+ }
public function evaluateAdmin($source,$return = '',$headers = true)
{
$post = array('x' => '{
@@ -72,12 +93,12 @@ public function evaluateAdmin($source,$return = '',$headers = true)
return $this->debugCall($result);
}
- public function knit($source,$return = '/json',$headers = false)
+ public function knit($source,$return = '/json',$headers = false,$options = '"base64_images","smartypants","highlight_code","mathjax"')
{
$post = array('x' => '{
library(knitr)
knit2html(text = "' . addslashes($source) . '",
- fragment.only = T, options=c("base64_images","smartypants")
+ fragment.only = T, options=c('.$options.')
)
}');
return $this->identity($post,$return,$headers);
@@ -127,30 +148,49 @@ public function knitForAdminDebug($source)
}
- public function selftest()
+
+
+ public function knitEmail($source)
{
- $source = '{
- options(bitmapType = "Xlib");
- library(knitr); library(markdown); library(ggplot2)
- knit2html(text = "__Hello__ World `r 1`
- ```{r}
- qplot(rnorm(10))
- ```
- ",
- fragment.only = T, options=c("base64_images","smartypants")
- )
- }';
- $results = $this->identity(array('x' => $source),'', true);
-
- if($this->http_status > 302) $alert_type = 'alert-error';
- else $alert_type = 'alert-info';
- alert("HTTP status: ".$this->http_status,'alert-success');
+ $source =
+'```{r settings,message=FALSE,warning=F,echo=F}
+email_image = function(x) {
+ cid = gsub("[^a-zA-Z0-9]", "", substring(x,8))
+ structure(paste0("cid:",cid,".png"), link = x)
+}
+opts_chunk$set(warning=F,message=F,echo=F)
+opts_knit$set(upload.fun=email_image)
+'.
+$this->user_data .
+'```
+'.
+ $source;
+ $results = $this->knit($source,'',false,'"smartypants","highlight_code","mathjax"');
- return $this->debugCall($results);
+ $available = explode("\n",$results);
+
+ $response = array();
+ $response['images'] = array();
+
+ foreach($available AS $part):
+ $upto = strpos($part,'/files/figure/');
+ if($upto!==false):
+ $image_id = preg_replace("/[^a-zA-Z0-9]/",'',substr($part,$upto+14)) . '.png';
+ $response['images'][ $image_id ] = $this->instance. $part;
+ endif;
+ endforeach;
+
+ $session = explode('/',$available[0]);
+ $session = '/'.$session[1].'/'.$session[2] .'/'.$session[3] . '/';
+ // info/text stdout/text console/text R/.val/text
+
+ if(in_array($session . 'R/.val',$available))
+ $response['body'] = current( json_decode(file_get_contents($this->instance. $session . 'R/.val/json')) );
+
+ return $response;
}
-
- private function debugCall($results)
+ public function debugCall($results)
{
list($header, $results) = explode("\r\n\r\n", $results, 2);
if($this->http_status > 302):
diff --git a/Model/Pause.php b/Model/Pause.php
index 19e474729..3978103e9 100644
--- a/Model/Pause.php
+++ b/Model/Pause.php
@@ -162,8 +162,6 @@ public function test()
$q = "SELECT DISTINCT ( {$condition} ) AS test,`survey_run_sessions`.session FROM `survey_run_sessions`
-$join
-
WHERE
`survey_run_sessions`.run_id = :run_id
diff --git a/Model/RunUnit.php b/Model/RunUnit.php
index 3cf920c4e..fa4eacd43 100644
--- a/Model/RunUnit.php
+++ b/Model/RunUnit.php
@@ -175,13 +175,31 @@ protected function getUserDataInRun($surveys)
{
$results = array();
foreach($surveys AS $survey_name):
- $get_results = $this->dbh->prepare("SELECT `survey_run_sessions`.session, `$survey_name`.* FROM `$survey_name`
- left join `survey_unit_sessions`
- on `$survey_name`.session_id = `survey_unit_sessions`.id
- left join `survey_run_sessions`
- on `survey_run_sessions`.id = `survey_unit_sessions`.run_session_id
+ $q1 = "SELECT `survey_run_sessions`.session, `$survey_name`.* FROM `$survey_name`
+ ";
+
+ $q4 = "
+ WHERE `survey_run_sessions`.id = :run_session_id;";
+
+ if(!in_array($survey_name,array('survey_users'))):
+ $q2 = "left join `survey_unit_sessions`
+ on `$survey_name`.session_id = `survey_unit_sessions`.id
+ ";
+ $q3 = "left join `survey_run_sessions`
+ on `survey_run_sessions`.id = `survey_unit_sessions`.run_session_id
+ ";
+
+ elseif($survey_name == 'survey_users'):
+ $q2 = '';
+ $q3 = "left join `survey_run_sessions`
+ on `survey_users`.id = `survey_run_sessions`.user_id
+ ";
+ endif;
+
+ $q = $q1 . $q2 . $q3 . $q4;
+
+ $get_results = $this->dbh->prepare($q);
- WHERE `survey_run_sessions`.id = :run_session_id;");
$get_results->bindParam(':run_session_id', $this->run_session_id);
$get_results->execute();
$results[$survey_name] = array();
@@ -219,19 +237,24 @@ private function knittingNeeded($source)
}
protected function dataNeeded($fdb,$q)
{
+ $matches = $tables = array();
$result_tables = $fdb->query("SELECT name FROM `survey_studies`");
- $tables = array();
-
while($res = $result_tables->fetch(PDO::FETCH_ASSOC)):
- $result = $res['name'];
+ $tables[] = $res['name'];
+ endwhile;
+ $tables[] = 'survey_users';
+ $tables[] = 'survey_unit_sessions';
+ $tables[] = 'survey_email_log';
+
+ foreach($tables AS $result):
if(preg_match("/($result\\\$|$result\\[)/",$q)):
- $tables[] = $result;
+ $matches[] = $result;
endif;
- endwhile;
+ endforeach;
- return $tables;
+ return $matches;
}
- public function getParsedBodyAdmin($source)
+ public function getParsedBodyAdmin($source,$email_embed = false)
{
$q = "SELECT id,run_session_id FROM `survey_unit_sessions`
@@ -248,7 +271,6 @@ public function getParsedBodyAdmin($source)
$temp_user = $g_user->fetch(PDO::FETCH_ASSOC);
$this->session_id = $temp_user['id'];
$this->run_session_id = $temp_user['run_session_id'];
-# pr($temp_user);
endif;
if($this->knittingNeeded($source)):
@@ -258,12 +280,18 @@ public function getParsedBodyAdmin($source)
$this->dataNeeded($this->dbh,$source)
));
- return $openCPU->knitForAdminDebug($source);
+ if($email_embed):
+ return $openCPU->knitEmail($source); # currently not caching email reports
+ else:
+ $report = $openCPU->knitForAdminDebug($source);
+ endif;
+ return $report;
+
else:
return $this->body_parsed;
endif;
}
- public function getParsedBody($source)
+ public function getParsedBody($source,$email_embed = false)
{
if(!$this->knittingNeeded($source))
{ // knit if need be
@@ -277,7 +305,8 @@ public function getParsedBody($source)
$get_report->bindParam(":unit_id",$this->id);
$get_report->bindParam(":session_id",$this->session_id);
$get_report->execute();
- if($get_report->rowCount() > 0)
+
+ if(!$email_embed AND $get_report->rowCount() > 0)
{
$report = $get_report->fetch(PDO::FETCH_ASSOC);
return $report['body_knit'];
@@ -288,9 +317,14 @@ public function getParsedBody($source)
$openCPU->addUserData($this->getUserDataInRun(
$this->dataNeeded($this->dbh,$source)
));
- $report = $openCPU->knitForUserDisplay($source);
- if($report)
- {
+
+ if($email_embed):
+ return $openCPU->knitEmail($source); # currently not caching email reports
+ else:
+ $report = $openCPU->knitForUserDisplay($source);
+ endif;
+
+ if($report):
$set_report = $this->dbh->prepare("INSERT INTO `survey_reports`
(`session_id`, `unit_id`, `body_knit`, `created`, `last_viewed`)
VALUES (:session_id, :unit_id, :body_knit, NOW(), NOW() ) ");
@@ -299,7 +333,7 @@ public function getParsedBody($source)
$set_report->bindParam(":session_id",$this->session_id);
$set_report->execute();
return $report;
- }
+ endif;
}
}
}
diff --git a/Model/Survey.php b/Model/Survey.php
index bc6184813..796b881aa 100644
--- a/Model/Survey.php
+++ b/Model/Survey.php
@@ -245,11 +245,14 @@ protected function getNextItems() {
{
$openCPU = $this->makeOpenCPU();
+ $dataNeeded = $this->dataNeeded($this->dbh, $skipif );
+ $dataNeeded[] = $this->results_table; // currently we stupidly at the current results table to every request, because it would be quite bothersome to parse the statement to understand
+ $dataNeeded = array_unique($dataNeeded);
$openCPU->addUserData($this->getUserDataInRun(
- $this->dataNeeded($this->dbh, $skipif )
+ $dataNeeded
));
- $skip = $item_factory->skip($openCPU, $skipif );
+ $skip = $item_factory->skip($this->results_table, $openCPU, $skipif);
}
if($skip)
diff --git a/README.md b/README.md
index cc37c3b2e..475b4d5fc 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ These components are the bastard children of a Pause + Branch: If the user acces
See the **OpenCPU + R + Knitr + Markdown** section to find out how to customise the text shown while waiting.
### Email
-Using an SMTP gateway that you can set up in the admin area, you can send emails to your users. Using the tag `{{login_link}}`, you can send users a personalised link to the run. See the **OpenCPU + R + Knitr + Markdown** section to find out how to personalise email text.
+Using an SMTP gateway that you can set up in the admin area, you can send emails to your users. Using the tag `{{login_link}}`, you can send users a personalised link to the run, you can also use `{{login_code}}` to use the session code to create custom links, e.g. for inviting peers to rate this person. See the **OpenCPU + R + Knitr + Markdown** section to find out how to personalise email text.
### External link
These are simple external links - you can use them to send users to other, specialised data collection modules, such as a social network generator. If you insert the placeholder `%s`, it will be replaced by the users run_session code, allowing you to link data later. You can choose to "end" this component before the user is redirected to the link or by enabling your external module to call our API to close it, when it's done.
diff --git a/documentation/Example files/all_widgets.xls b/documentation/Example files/all_widgets.xls
index 45782135a6dc6fdd068a107571eb4ac1a9853d7e..7e4a58f2468f542a7e5220aa484560b4eb4459e0 100644
GIT binary patch
delta 13393
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
delta 11026
zcmd^EeRLevb$_$^UI|N9{2^>GBY$BVS=P5$VM(^+l`Pwm%}SP?g~-}njaJKkj5@RH
zjkUnIWB9%)a@(ci->cckg}o%}6KDWt}{ob!Mk@__^jEOOh|0x
zc+fn&Gnm)A`rk0{ee>^j4i)4wri5a;FKXW0TwRsJm@g7nH1z6`(2$}P;wGLL3MsLm
zFBZ&ZY+pk2OUIRn)km4UxMq-$+XfZtw!4W0&nz^RsYtlcMXZNo(
zIGY^47>g+}IU0(~LrPTciYw9hiBWk_1GzFNE1DixB9XY9RJ0)_Rw?gSqyA7Vq-b$I
z=Ll{vefUF7?rw*n)p8;>WZl+exP6Jhhzx_}Luy2k{YuOii^b)}45NoM$+1v03KJj|
zj3^D4@ueV1=y2lRjPc#V|{khkRO4u@+l7Xwi4u{N4H$
z3Jh9fce}hrFpC>Y#n#`g<4|S^N1g6a3|2>Bp$^yasoc58RSiqXSOrJ)o{0M4!EjLi=8K
z`~J3rmN8h{M@(9O$grmIInF&HMTgsEkoB>ce3ueMjvztoMFv#ECLqbMrXOPK24DgUh>d2jJRlWZ`c>{*I=MQG5QC#PgfvpLV91X#URf>u
zekDYiNkvORo&+`ZHAr`(v@5cuW-Nh}jE`ZfqJR%8@wjs*V^#nO6=hBZ&R0{+7?t0c
zilWDPX%?~K6bPfN#!Po>XWMpuz{nBGUQLcGpQQ}b?6k1f6_fpmcwEJvQ6mwsVyYZY
zXk)g#dAjw#^1(Um)BK7V-&I};87jz~?t=#ow(suiI@n`AvTH*z?4xdvyTk4AxIN|%
zcHJsfn(m(R)f%!-?(4_GH6&mwj=`mhhVwc2(q6S+mR+dTuPfLN2{__cqCi7(|U5xz*
zE@?vwHo^mc*}~YfaCLbrW2*`ndjjpFXysjut%QRgM4LkUE3`jH`|aI~P2ln69kg$v
zZEj<%29`XH_FRz+;&u?jqtBsz5^d`q#_EY%Z47xMr$q`dc-_AlAmr0h4UJ#Ypd$3DytSgH9L1`iZ9aQEa0D=
z9Jp$$8!8(XK8@xj?Q!#5u+VHBb(*7Ei8+{@T7%Cr~cNP70I<^oIiF
z1O1Ue1wi-VXtmWl0q+;65a=QE%O}?wTkJjwo@ilzW+UTp2_E&-C~UV`voc=mMD=@mRN{Ldj%rW@7bt;8}lWBXx>4A78_H*1Nxj8qtzc0
zh*p0)P$4NK#jw;Yyl<_60>h1P^6AeDMAIqOmb?=v+eU@lr0WDClNQiolL|R!+LkQh
z^YAFmIg5DciZl5@TeF2t+Jnou^Y#lw&O`O%6PNLcD1A1fc_@H3BD38B(W*OaRLnUK
z2xM{E`B2PBJH;T8?iGkgy96TAeKsoLq*VeDX`MjinG%7BvtA(Ezg9k!Fe1%3csX~}
zmu=DI+%zgc6pH2CPdITnD&>+myKO`YR|-T5aVFVgq;Qo$q;EBlL8PU~-Bwm+4}J>m
zZids2Ph7!O+$0dGkOd+YIPSRQN-kM05J_$jh$Nc?B4ha0G@yPZU;R3Ja22OnFA&k(
zED+J$CJ@o!B<1Q?ah8n&(ULX^Q~i*3gSL%PRh1mM
zYz?Bli}$bP6oat!{TWz?Gi;_wT$~sQERv;qX
zWuu$8s-i3U>UiH$
zqIz4-eE#!wMiU21Pubge-@<83OO*NY4?~-UX!%D3B1iSvsGLjo3q+Df1tLkGjn*@V
zfkFDy8Ii8%-uDVbqV(r98lxO*Wyfu_iK|EoL@GWj5Y0Pbqs?6MZh=S=^@nqAM%0WJ
zHfF<{x#&Ftk?6ew5oyXsTezfOAd(CSM3O@`+Quanfk<*#AV?Z4Xv15$=!ifh8WM;|
z@3K(^mkbL;k`aMOGHRpkTyk6>k~9S(N#hY4?%<*^fk;#ph(t#Pq7v{20#RAhY*fWr
zbb*K^E)cOK1mY~2SXA*!LNv7LDQn$4Ms*-`vp=>c)geRN>`#PLwvfuf(eWtyUqtb*
z!=EwT;_Ku*l=E)(O`E5lvpxpIYjHiN{fbRnk3+#yUXKLnWM37Nh;~AtJfOz~$_M(I
zKm|Zw7sv^8&PEN~kS8+8;Fi!nrz~&ahWsfId7(iV(f~u;jQTC9#fCJ%a|?zvpeigF
z(tt|6U`PWrEEv+r4UwKOfB4W^;~TchMxnBitE7HX*~nd*S$pFWm5obOHZD=wxI|?W
z&yE_Kvx#qaj_5>cg2EOYf|kPDxM+nylt@$n$(Y-?WV1kp7^?<)g;lUUcyzs6;-!=q
zSL-hG+ks;1VW;?a8*6ctvKQ&8l$k#|zg5aJ|L6RT%jls9?nfcTU5
zX3h7W-|{afpXC2u$$}((I&mGxgB<;<=1^CA-*T#O4F8cSn!H{bG&fI{<9F}mIwQ69
zC-gF5eFoV{eq*(5@2V*l_P(2(Vrt6zeLFcN1zq~YJG}2=a>}7k1j_V@IrQrj`ovp&
zTuo7{ri^*KKd7k>^a)3DD(C`T}?G#AWy(|=r`s);~mxBnI1hk#EPlmmoarz~y|F}|X|D>h0t;9Ryn${=wd73XYUntLa
zO
zYS%PT39Siht&(?!uNkZI&UnV#O8O_;N<8Dl-al#SO-?mm=+S$0T5(&+z+6Uej~@1x
zy%Z?h9`=lrii}>GO}2ZD8Ct1DCG_s#k)gmKPw@MOF`r`vW&nw9WzUkJta&%=Wynzk
zTA!eB>k~*`>Nb5c>t&5aq9K6UUS3#>CRhBYDB(A2`
zMM^U}?AnB%8B~LoWct+E58tdZG>pUm(ljcXhJdBZBNS2yXz?#Dj7@_fC@`_RDY
z{yPtek1;omYr6aAnZZxjPi*)ltp8eu-+ulSk2zO85C%P5U3IMsN@@Qsdb+;KyI;cd
z$0s`cv&OXzKkdJ38U9&%|8wDS_%d3&i1NK4b|cSCycA=9;FlwkNKU<)d1?$>Q}JdT
zbP>g(H8FE-}sJ?siop^aW3`zPtmN}nK#{>lDHdM|D(u`ch-`}QI(_fI-$9B+sQ
zUPIeT0%e|YW1d=}E0|;HN=^;T=@VD14ePwDrmpkO(3+A{WPG5E%vDp=mzvclpvz$`
z?2?+wr)ANIK4C3NP1%Yz&g1_^VefnROUKgpxBujCaX;g4ap`^k^r-ZWX~+Kpc3){p
diff --git a/formr_scheme.mwb b/formr_scheme.mwb
new file mode 100644
index 0000000000000000000000000000000000000000..2d85668f58fe5d0bb58e0f32f86883aa86375dee
GIT binary patch
literal 37840
zcmX`SWmFpt+ckT%
ztjQ$V*S1lTg@(a~fPg@NPz*2>Rm*KJT6KYdkbgsfK>s*u>R{q#XJ+rpXy*ZB^t7`*
z>(TW9v?X3Vu05+RUUg4aXVC6qwl5vuebu|aB
z26uBmAG@185d3)Bcc?RXy>>`ys@h+={gb-zzCOue*rVg1q1l9@CpUc^^~2v5?AHDm
zUuVFu|9+JsiJx2o6WvNM>jb%44lzebfvFzreP2vrk!hp+gXQu4x41?QIVrVfDB~OC$@IvV8dz;{Z`8nynYV1*-
zX8+fZnjcS3{UlpuB$K${rq+ub{Tj}R>-e$}z(wx)=KD1(Pu-8a>hE?MoTNUzuiyp}
zPLEqpua2>%%Q}aiH?(VbCdQ4;moo6~#~sQ0vGE3<_jcVhG*pKspZ%kbLvXG8`)lA2
z!@ckR3+DT8*S|_Gv+%v2O)j3ilW$q5zK>2^w~1dr3sU2nIUC+T+}Y$w98U|}ure_D
zKi2p547BDvJnY=H+-iCV)JT3lPdiIZnFzl8tPfLG4p}T6kV4Q;OWM6Kj8%A{*Ri8U
z$X)iNsSl&y{V~}*@%%kmz%TeB^^GT2Dn5
zMs_{h{BEt6CO;ug@Cv%`FOYs8XKFoDA&mpChev^j!s9@OhVRR*(0+WWdDII{aX;(n
z1nb@#`P_9XIM4KX0|#X*Oq|-(?s~Z(r>?Bk7kSJQKM!u&{XBv}TQ~%+Z@aiGUK`^4@(1iX0yt4E-G}3G9uf53))rE0Z
zx^yKJophMkV$`z}pws@Mnn>nrB+xY~g}-r@Vk3Z~cJ0&Yasg7P<(P0_GOscx#P<~?
z#aj+1Jv&Vsvu6FnR^>6Zf2MzKkX6QZ2FGCtH)xUP+LYge(<
z;9bqw{N4C40$u^O{(doOjKSn#`<<;&JI(Q~@z$&R%^!8*jH6foM-LCLkEiVZ$7WU8L#Pz&TdUUf5bJ-2&J)VK&7n+-ssRx{4Htc!1bq?R(xP}Z54AxP5
z-F58`erZ@AAOkQmL7Y%&2$iJ}8UH@^cC3h`KiVCoQ7iG}y-kujY$;rx08>7-&5
zuEgHCvxb?tl&is_$+Z6OVv$GAUhbAEa{C8SQRm5)OOqgkgmx5V9T*9Dh+?q-3{-OE
z!r;kzN4Ds6^!;W`pr!D52nsy99Ed>HROE$2n^=@Yl)4lpMovW^
ztPvYxvomD5MW$-1?VH(PyyfxZ*cRCw8xAH8rjPKBbECm~uimZW+FWZ1Mgc`QvcX?G
zav4zwyEo~m@<43>7v1|cK;VW8d^>AmS?HC(ti;zS3>d$!0YFL*;ulVfreo
z*f4R95{IBXz>FadhXqK4;yLK2#1G^pO|YzE-v&q|*M?IB^I~%^-6|E6M7kD9eY?OV
znUY1a2}ugPBJ1~YaL#LnkgS1-5~IF|@Nk|qo~kBELT0SaXd+l%tlu9U$W(8#=b*^~
z*G&zLy&qELuCxqc2#*2MbdF#$(cx)j$$l1xj}|qDp<5LbGbEdbESqEm3eyQTl@7B&
z10>%HR=!SWnTSYUkAQ)<1%{r@HrEe-=QuX6qr#n?5_mIcj=BgP?CZ?!CxbSJvIRGE
zY(%~58hEmNAM&Qc{S6s7rbwh~{EBhQz?ItFKSp~VET;r|9OLA>s=i?Q9FDi)Uhl`n
zEnfnA)7klpe{HaJrc^u5eUYR+?1RbArpv!J6Pv$b}&C?zJYs$|5h+&4;LIb!PcD=2?}CFgt^Ax+Mi72E{>o-`}ETdUXK4A
z7}qT9eMa0X``L@@(eT`s`}uZ#=SLP}!r(>J6w`{RK^o*AL86rr@yAJSdyS-{9ovS-
zS#Xyv{Ou~u^GMw0_f4P+SOE8dv})+l^tK@Cs_*vZd@-&nYRF&T&AT&?s3r9k&(qQW
zQ>BVGV-mU%ASOVK+%i|o;3dJ_e6*bI0Cj`w^6LZ%I)EHB=J>RR5
zxQWeQKK2^foK^QzZMa^C9QryBkH9YFc0=f>C{)9(!bcO6I?kJW+ihpoXSc4rh7pn}
zuxX2SvOLL;Ztv5t+tWD-0vuLvmkn42-1>U9{!fiQ{%_!IyDZ-DgVLA}Jgd~MyZ4{X
z)9GwA@o9DP!+qUuYx|4-)>+}|WrsgupgEF%`*vPO9o6v;KHdql^Fiq^<5(spIQ)yC
zF|oOzk>{NU+tizR-e7)yICK`qGc)-o#tko{2Ev595FtuEje2M3x$yS!!b*i>Z1#-fChVJI-pm|LDnkq*LVe-2U`l$i8^o&}?03Rz$
z9tFbxZ3y&e0)q{n-#K$FzP)c-U#PR{vM0%BBzC|@E1`x!0_yO*ejdfT1aaD>jQw#t
zvu$LgVZ^-r)Mj#v8!+9N+b;bW{?9mG&
zO=Aqgm4Gi@(>WzmFMo|y~J)X}{QD=rn;YFiX%<7_`eO&pCRi
zqlr>g&YS`2U9LfmMq)mORcw;$sOlOfOqtlIAQf>EA|@jKMGFZJ;8tg*ul9Bpzs|bl
zc3erl%B}-z8Lnf-FJm!z{aFgiQD4T7d8W^^RM?B6VquPzW23qqAJ8O|6Q~@XuppBo
zlVegieMeb>cGGNYDfS|qB`q2RYXQWdkVDO$#ndC4SFNjHcOHB>QW2`VI$(bBDz-FQ
zO0V>CybmmV>EfkYZDyqqE1rZ%A%Z_Ig1C_gXh;llmEkD}x+9Ol!@#Nr;So?!S5g#G
z0GY(H88(>|pSHzLN@LMJvv<|}I&H;2qbfP=KW&|!{IULYCnQH}+@q!xBqTJ%hPTf@
zo1cxeVeMp?%bRhyp#3(WKRISN5&GuE@bt7a%}ID+E;+(l*0nMCexl^NV6OO(aN#!F
z^~0-zEo*MDBU?eF+gUhnI+9a_jEMG@=!OAJCbekFT*@&P1)LO-auU)P+yfPMzD
z30m2HeJWkgl$F6lVgIo@6xG4rgT44{blpILzkm09$YuOVm(Q?N__Q~=)12b`{`Bqi
z{@zESa7JTc$t(ftw-Y;mN0#f;$=Fm)#_hg!ujMigLmhd`O%6}>;%7*@HbA1G$Z6FqpQW4DjjR%foa*yru|ty
zTbFMZw0t*rIc?+uC@39`i4p+v&~#!7+;QY7tYsc!?R@F{hLo@w3DN1J%ULCtyfZBM
zcw22T5nbj4%lrrx(&SiL%MxRF0&_Vt9{sNzuYGm;E;%9(Gtg?^Af}Qy&c8bxt>{gM
zrRNei(tSDj>}##dR9}a4(JktkRn_JBq0`vo=6-fedY)UMkrqo4zZ=zjLOk#(cf$<+O#!s
z2Gk*&=>uZV;B=R$39C6)qUozwO-IV*mr=^pv#PC=P_(jaCCpS?#)fO&AYJ~@z5m1KUqdk3VOj3K$mQwN3z6-p4dKzWd*C&dH_LMBPn
z$;BRvBty;ivXEo!o_?usk=M`L9D1-q7g4KOW$CFU9&~>OWOMpVk`v^n$
zFcZ+QA^`OSPh_3AH1xzw8BidG$ovYHB<=a1HU`Es*BC{fMoFNVqWKI6cc5!x0LP#(nE_T!&`w|so=79f^eH5{vlfyxe>RsHrH{5?
zUDgtAF|95Snp5G+E7q6N3GhmRHj6F;lS1Q=*2}VJb)%>WpfD6PU4#wAb`QzS0~KZs
zw??;Tz_q^vis~J;;#b2bVJOwIQnqUN#=uxt>c!#;FW%)I8;7AT)nQ0(mR;44NEg1<
zBeNJNb`GIUkC{jgC27sVm4O*~iAXoGuu>2T_@ie~Au|3;WE`7{g+4BZHp6pG#u5Bm
z{UBnTC=F7zd_KWLTGBmqgg@$_8Hw%(_R;9l($Im9N;7UsG;!9MybR_+6Qq>%>R|#6
z)fI5bYb7$oh|R)CTP78@P?W4E!h%)+Ki?DXK6ZGEjZ&Jz=r1C28fLqhin)(aN18gH
ziDVETMeZE+#BTY}x7F$@`dsCqZm&VyNhqx%l|9>LsJdu#*zR
z&DSe>-uTHv7s$6bm9HW#&jNjc1*`l#bxOqaYH<>uuj(%M8V-fee>Ei#eO8;Q+_u&*
zE~+$NVzJh6ivm`et1nd(G@9dZG+S%L=r_q+aM6@rd9hWc`-+1J>CtMm)w9RxwOZmN
zH_Rb?G^%z69?AGbq{<ABYAxp--s<}u@0y#hwl|6^!aQ|sMf_HHTa
zH`MG;u9xwg9bs!JU4dCctEGSgbL3|_23vV5j3~))JMWC9qNIVIjcB0-Qxxj5D#IZ6
z+bcl`ALC^T>WfnAH
zNf4h~#4X1NU*>)4BEK+CU$OnqjxFx^sjwfvnWK=i^NE6#@!?l#8LL!eW~A5c?D0t5
zR6MfW)RQ_tj;PDvPZ*yul((*6oS;=EJwgFFk()o`XBMecl8Uh}(t?V~;Kr7}>~Q+_d}70|ZLye9wa86F$E
z7p9ZKMp#>{?GCCeUSW~Rbo*mpn`o{+Z#$Z3PEb|6@|$YPQBzI%l`qj;ts8iXLM2IN
z<*qWnQ(3!WVBmeCe5$<`EkTM?0tLh@SR;xw@}K<;BILvHe0VI!@w}s9bd9)V7e8rtF#El3zor%CShXR@6Ihl
z#vpJJsF^LF{mt9TrEOsEz;uUY(rjlZ_8T-TAq@!3$AC1U*fO1iLI||}T#QW-i$~Z=
z0z$UqwXRiPkF6a2{RtnuxvujQ1&
zMq^{T424!Z4Q`RKXo^LZiWG_f1*Nj}K2HUb*bDcN$x-$r4EK?%0FbD^TIN+G02ll5
zWaY7UW2gvp;#$M;WZ7<-fk4Z2Ej?eZpsvPWG)PG#6p^hAF`&=lq@QxcXOl7d^6?H;
znxU{fSXrx}%mAgK)fTJ4#i2yA85~%JJ3AmoksSGDej@WWYjZhCY$+utngD0H0EutR
z&3fNi=Zc!S>+9_Y^V65t%Y=h~mDXCy9(nFFKc3y3I=IfrpKH~5N5Ug?8rn7^dltq_
z9dUf5Mb2|(3XE?W_eQbJ3`K=_P4LtQGC}Y)7NeluwJ#xkW_0a%04+N%=CYbyRAtb*
zY$JPFS$M6*ZwX;z+FRrvzXLmvqUuz
z9MpCTD_J5=fQk|`ix!Ba1Gq|S+PWP2JApoKG1L+u;JN5V{;@W7a0{3DtKHB>8rS_k
zRrcFNOVj1^?2S_3e;UqEkp>wxXXD$8`zz%jH~dzakF{oO5-z5e+~ocY|D_uqJX=7a
zp@jY4DxLnfO3n4%Laf`g8QL3IxI*ykg#F3##S_u>uf()~v_m{q&pAtWn5KqL)wQ)d
z37wIGKVI(){bS+l$lU?g?N-xW+)>S(Lt~%y@#B8{HmB)i@^@13uF$_d&0_u<++tZx
zryFT+S=tAaIx^;sZ(6U1VK}DqIy95&eUFtZvs{pP`#aKQ!wb{N)Q$W8d{?SAe=#xp
zC&L(bj)JxHw8_`8l{3oX)V*pd?r^ivXF#Z3pP
zV5s~URxg^ixAv6MLUds8$JkDy{y-&rFmzO9ayLCv&qN7C^H^|%RDWPM3yFRS*AKGatyN0qH{(H
zkED1Gk>0HeLz9^(U`G)h#!C!p{t1EmRKtj}Azmd~VP8mGmIL@UnfV`6{o+sWA6uz+
zeH$n%2%Dwb#!i7G9;xTENQVC_AP~QvM?z{BL
zFE;Sj#eT5hgs-{d$?$~lC4o?~CRq+MI)z{s21-E-z8_Pu
zF^VvSvSs?Gz8gtr;cjk8QC!)ys7KDF&cSxSj++m49(gZzYE*%nZCzOQ1=8;M1h;
zG)jIk{*P3Abg6W#`b
zn+*bj0`0CA<8+wor#dTx0$Z#_YgU<4{jPMC?67EV;MDiZMAf$04;c9dZ-83{gFFP#U+^}r$i#0@$#YYbQ
z1vgKB!SU|r^HQ}Di#o``HABoaF|{ptu??R^+pc4Dq6(IQc#aHVwy9blr;c@ypHS3!
zrz0x7!gP?uAQtLq{eNE4TC-%Yt?|~fI8`7mGT8A3*G8bCS4ohlYa
ziwUke6{;03YMp=n5s&X`ZMy%62f5kj49%^Yf&g&-<`9{%5EvmK2a`b{2th
z$Qn<>F=+>!_o>r#gfl_t(lEm6PfXr!P!LR^S$Zvp-idOTa`WH*gc
zWvPb|gGI@sE*4xojuoJi$FEu=JaPy_ZSg9~`J1G3iqn0rz4ox(;z%x5l1<^C`xcTP
z4AV?mN2=aTWjD`~L6#6tPtaCo^FPn$b^Lb8wMW~ww)KcO&tOe&3U{hAe6mtNpqz7s
zyWj!B^gymLJup3>(R5sYBsq;k1|g)}d7TsUi;wJ~rVcW50cnk@0e*;O#nc)C6x+zD
zq~}lM1QjlPn`4(B6*G3VTO@ABE*~shHLvPaFC~X?V)>q-oSGD2;vJf8Fy;6w75yD@
zcJsi>?pLpE^2^D#X
zp8HU#q)-&wN-jWHhM7<6BSyed8NFUx)Ly@+ws)~-p`7^SF4@(F
zbl+&}|9q;nyqi5`41DZopImhX@?H97CH>sk@$%ytT>Tnt@qES)Ki(iFkQxvp2hC2K
zw4X_>3CJUu@h1mtK8p3uyOy)@qvAiN(hN%$uy2|eN;-$uBSoTWDyfy1B1MuE0@|rKL}<4JzEl!A_~8^%Gr*b%9dMlT=Fn@OQckc4mVL^B&XGm0X#$1N0>t
zErjO=qh}*%5n4~x(lTp?X;C!@$dk<3&{>g$m5UVM$!Q5dS0Z$fW6p4+rZ8m`=26Am
za2aL%3mOQ!qEzil^vI6MdG;Wc!2ay_aoHo>c%E^mN9+fp|Y`$podBoGm0
zTjjsDhQh+sE6e=fXFd;-@Xx9&B;VFutbWDn>%d~5fJ`@Pv-wy__4O3LI=tt
zA|Tnw@v$Q%95tBZbzqIF#o70DxK(w~)b1dL{^b35t`*dp^~=pF-xd!YGN`v?-1o~b
zRR1;tA4$)=TGv-T$Hf(>%*H2EgzdQ_(+Co(Cl7gH5{W<%mrI+{
zKH9Jn7npcwk+EHAv6LeK=E{yZg9Vte&wfo@ry{YxGa)i>!)QEG6j0;^s76T^;(h$DGAxZvz{cT0=JWp216eAm*$PZ
zwipg*^iA_+a|85GW4}+a>CJ@>l)gwt^I6VR#|7S$eqqf~ge(6dqFtPt5UukWhioi1
zRy`V0mB+FmB3@2IKY`fmI$cIH$~C2E8ON%rwz5_VXR~(aj|n$XFN!*;7$#nyS1yE~
zdAJ#MfQYNcFfH+!Vzo_}bAZxRY@1xF*B0Mrbxp!P7>PiYgdAt=5J!W8RVKARYj(W$
z<*izp@Hnn9P!WwC4q=3<9P8iyB4P;vcPvYggw-`04z$<(XsV}=!!6ipq)g&hG*k`t
zrsgbkYw9g$#^bdLmn;59tJf9zMg*&DAk;!}NL3O$#thq^>VhSO&bnB0$;zkHLpFl)
zxru@3lwu+z8BiGGkD`fCjc7?imM5^|l-V^;?>Nb8oIeYe5fq_!#6C!d)e88MM110m72voZXIV51{
z8XME-7zgY?E9godIFmi8BV|;#Q1t;72O?0@3&^(TLN;}fHKPDRI%r^Zs<1T8g)%yc
z4tZc0TQDsp+4VK^pZSwRAYEVBN=YOt<;0f&E)np83=e5*yik$lQqPL8=?)&uJVB`X
zd6MF9t6g?wg#tt)~qseo~%
z7zvhR-pW-1v@`2<29F!E?r_~Bk^d#Cq8LWdAVi(CI%yZh9yqJs_TV88PyJ!rM5+g7
zdjF_m$f^P7@&B$BJ44q}nnpqJ@n(&-n?)L>{OFc_Yy84NRS|b(1{?T
z+c-su{8(0O!YaH|1eK?b-YK8BW}fu^bQI>Z!l%#XCo6JsiQglw&!L7Ubj+%=Z;7-5
zL7kc*j0_8ifB|<%?x&zH-ljrpGTtCF!J}gR%%=<8YaV@z-Zc_W=ydHD8#CQ&u5`Z5
zhW@oLvZQ>_*P^Y-4qq#yuTe44VC%IRROvx)P0tmAj-THTllcXD+}X^m1Jw>_sAPwL
zsw}8e2uh$uVJo-Yl8^1bey4DB9d>eHp#EP@yM7PKxGiw_nEsBh>d%_6*HKevXQbpY{oBjkAOBZwl0f<`_p;)wGO6cy7~7?|OV3C<
z_rm9EY(jn^MP)kb!ZCZKl3|O|!tv4^9;I@83~D$NF*EC1Y(d@Qbwy8l)$sAf-393G
zXS|(5Y{>hIeJRF5wT^=AaTNAw&5i<-WWr;2Ociig<=SjKIE;X&&RG4;7tn)!Ul{t-=eWY9LlVnx7Z(%uRmfr4T+Ole)zmiQ`-$N2XwI4Ydc
z*afR1kz3Z#Y=YMgrZ+yIn$QcT$`^IBWn6H~QrJ^10ZtfkWX}A8qk6YirDXbAHJpVz
zds1Ixi8xkjz2K#Qm-JsIDZ(Yewlp3bCjmhaX4Dd;^pqrulLmzgpbp`y3Be;E$F5pM
zg$9U8TdaNb8a}kEH(`4??SJdAMfgh!MXp7pu|G33ABd%g``TT;#T$+l?J^^5Nc(b?1_upTzT>RkM~_xjO0rF^cu
zS?ADHd;I!D@1O)m8Q#xX;=Ky)0cWn`
zg~4S-?Zh)E#Diy^x!J}l()nWMxH3-gk6w7_5|7CyG@`qnvVS>Vuuryk`cQEnD=OP|
zPx{Szh&P>Ssz1Q%S>}@@LGUfiEDM~a2?U|ZTo`qx4Q`LTeYd6#y7WK76FmCGjovLb
zf=Bbjd`T8|BNZhEBp5+>QoJja;4}I@;dd1caXJ1ebZsnMVv>}<>II*`|K=#5u_>fH
zo-6mJdSU6R8-A?ob4@e?hO%^}CjvUuEdWQQ@SU_~0x$OP+?9
zn%HR!DCx>XFxzrz5oAQ}*%&GHDM$uUDhXl=1jhivrb9&jKG@M6w4|C`&@aNQZmewF
zfcy2kb}i>R_kW&hXnDLW3W=LU2x`Ts2Y{KtWXG~O5fDogQYlFiEE=-I9_3*aqLrsO
zJsrDcyW`w(42fZ1~E+qy|)@5VHUt!1|{JDi|>NXQd5Q*EIN48OaU|k
z{r;$-5kZo3Se%eh?vgl|%8|oJ(7Cb2b8QC%k#P+}mye3k{fb~*y}S}m^rmEC6Odxf
z2O!E+mLFoAv8L!szh+A{+gXW~Bg|t24T`FyLg)=ptC!to@ck(ALW;llq-
zdiuLHyUql{L*tPOGue^w$V&WQb>>8zR~78LJXr7UCx0Lk28j?PQ1z5l6S>*(Vv(ep
zsq@>)*Z3{uObZn)@H6$J$1KKr4QV~~e>}y^FiImDPn7V$Qk6lm?IL2gC}b4FUUsIi
zJVq0h79~H#7CQM8M>?$TedE9CL8VtsNt0=^`rb`Hb9jbUSN(qUhjinvFOA6+>3U|9
z`3opXE{J^@inXYpWjo+YNktpEqHzHBz!Lksd35u5tQ;JKbOjhT0lLYb==B%!ZTbY>VD)F{-?JBjX=EZXM*TfC^r7
zS|h4v1ma^_9IlFH0SgWLyu(Uqp^4hm|=V{BTRtJzhx!64U3m#@zkd`_j=B%#$E
znNh-{nY@Z_gHnAqB@?fjXr+9li3nzLtflO+_it7E?CLbA$iIftkBwlM8E-byh*9qHkgUuYI9r01R55>_*M-
z^0$qYm|AHia1k8ys?j}V5LBE}gpberH9UEn`q^j@eE}*_0;8H)NF*8@$pibdBQRvp
zS_BXgyWAY`eF3VImmXf>)8V=P2C?ApugTA?A2M*vY5#Gq0V
zu&o8-nn3v}A`!;28X7kSib|Ko047cU!bq8Q
z0663{bZH^m^I_X@)oOdQ%R1Of!hg(mZSTsA#jiaklk+EG3z;RFO*diLC1nKF+TMVf
zc@x_aZk8MY#Z(e3D%rU5V2h*m?D9pJ9tA}2xLDv!hu4M{2`DYTkU=snGH{6=!kBvW
zFk2{7A|Z5iwC{Pj5*$xB47iqp4wX};+PU7s}c8bbz-X1L>ZP!#gb8(nWgD+
zZEbHbLHy6&I#2#oz)zk`4+;riD@%XgvxWJBj`f8^lfdcqjIM$X_k$lNFGfcdBp5f*
zn(d=Tg>BMAO^q!LnLD{VujkZe|8D5V5dP`37YT>BN&{b>4hwM3%buK_B|+@R(A>)f
z){OFVSsHyzHp^t*BT1N;lk*#d7!H(J1cJmXOD$A}5FtoVEUxk`f(kRR7$@io!$whF
zekqrLh;6c2r087uY$*DAXlSWa`yM7bwdNx{qIWcp!4y*an=Cl7!o$1eqGV9rR$
zMV01;A;*4}Uy3FmVvH60X1$aQzSVVU)OM#CMvaL<#8XR9;wb2DLW-Y9MkYkWbHK(F
zwQVn}UZM?P*}Ww*vFuB~^p0m~502iC3m(B;0KbTO$YgV#-0rtR>E}pWt(xA_uu$@{Pwa<~qJ(dr3`8ME
zQ_t<9<}Dwt(sF~ln8!awiu3tAQ+$lvi=ES-MOl4RnXyL2zgX5eXOK6S^?6%N4nAM}
z_b~Py^>;QJQEWjZ12GXGR4}6t8=tP=K@!YSd-I0)9-G=_5%J0u=t0@TqBMrvTja9n
zoKXTt&|8F~hn8XBnak4@7^2_F{kP{@SIGP@#jQg&7wHooQBc%!5c;&vI0i%7E2Q=u
zIn;`{FiAhA$A6ax)sHMbW8$5xP(4(;YU_{)?~8;dT?UA{G;`xt%sOS^h_J=j2ta8JO;IZPDjN0DDWUEp(9
z+1DNqF_94|Z6K*ADVHNHKaEG^Mhz`9Z4?qe9=0p}Fs*9VyWVyj8?h|^@grPy90Sdv
zi*?N8u|Zmtz>%At*UXF>0W@mt6sl4ew=A7>T8hJlBX1w~PEYZIA{LTfro7QfZC7SN
zLKr1292p24q7?900)cbG!NOJ@W6H1q0O}WohH)tr6zsBcdRltgJJ_M4J==9OI-M3*
z-}3UKjn`5i&JYyF^(@%nm2MruApFJQAMuuBjDc`TLDh
zAB&>0D@&)oe9Ol)NWx|a`Xn^AuzcEM$A22Q
zAX}^T=gwPp*B(S5MBqV)^-Fj8_mQnYUv)!y1QlhHAgnfHG96^5X`c8vnQ3XsU$*7y
zhVMRRk)l4#kl&*q1+B7)
z)V2WCHx>}KJ($lLi(=5SZ7vZmcmUeEEbkZ?g{OSQ=T-?Z@{vT1kWZsr^qE_0!PhDQ`Kas?#A<&5rL)!Jnkca%P4tH9
zbgpXH*3&dmO$K<2ttBi&!ebNYb`U!WDV5kndag)XYIG5n2=dn5Fu-CNZIl?HXuh&!
z^%61CWZDY#tRXKKN2;nbV+*um%az%55u93lq}@PoFhuZvzH;Rx%5JnGhh+o+F0_1v
zEf_i!FFq0nt1xlLlM-zr-+(bQibTa;m($I+zWj=^XFMr8G1hDqja)Htci@oD7+qa1
zK*o`oay;>dFoS-G0!Of%F9Ky!R+`Tf6D5D@OTX7}AJ4gH;+RZ{ySf&a8P<18uS5=?
zpP~sgm;b9`CU7CnMT6l{B!(fg65YqXgNcmcew4!$u?Uj1I0*o9xp6z)5x!G5Ioaxb9YGPzwDn{|B
zbPF@X;#bb?S}^Ne<-Ntd&H+9=5EV>m*|-=CN{TLhecy%|ygyNp-LG-m
zWgp5LhkRod3dJadR*6Ps`+Q$u`>0#!yjbTJ$?|6;I=+&*5nx54jAxaE;IfDW_-Ll9
z<5eI5)QwHQ!Vk9He$zc2xP@2eC;W&j=5-uJ`buc8Rk6rL)iDvK6ZnMqoX!8s1odyA
zMO&WF-9wL+3cPKfGVbmANixFB)9hp6N9|hQpP-#b=>5YX8`a5W68Y*IRb%^A>V=D6
zun0($O36fEoqq_O_XheKk$G%t)CtuJeNTM_n;cx7_YjW}cT|pYYl%;*vgWCYPn)Ri
zowmN};c!<`90cHSH)#)ioV6BLcndowJ{UB8B8tpL;_~{)<{43IMjyw1RwrgZw;$I?
zHdx3{b7($D7K}v@z!!|I?62N!YWzDIclLjp$1eQv>FoZTNN2l=wUH|K4OFC+^bAS1#l#^DfZRc(a!!
z=lVj>kwr5NiiH^>8=^=XLwF%A)`KFPA~_mJC@~_L>IGQ^1v^q9_RE@oE`9}ncj*cn
zhCvcVH`~bvzTxVm^f8mWjHpp?aF?8STneBnO_t(di$V`oSLB!H|NexXXF92%fIGF)
zEvet^{y}<(=u{-i<%P6>+yh1><=t3Yv~d(-_ArD_XZX@5N8_`7QHbEEAtEKIMECz8@=`vv74i_eomqzL
zMJx?9X-sC?`6-^BHfWh}<}yF==b{P|!#x?SSwMm;bC~$(Tf~1=JZ*OMTm@7HZeLoI
zOL7_1@Z!Wf7`fcIfJCKw7VCTs42qs8m1BBt-?Y8INoo!8W!Jk2M9cs2{@}?wSzTrp
zCcyt$TN-q4uT$
zR&{~I2>9qS=Y|~KGQweyh>=*teCuBQ^!BLoT^wnDznrx;1Nv#@)!jW4*-I!3r(=a%
zSJrGMs?k&kbgk}fn^k??`I>jf@Tr^n3&r4shKNm1l*EdX6<35g*nl{{FJ!vLY?lG2
zBsMji8NQ2C5T;juxUPE;5$H?c=#uiB9Zz7jR5iTti2?YKBEg_RpsRugWIwUjB`Cn?
z-myC-!b=9Se_GfX2i;d>qni)1|JIJbcUKK%ttbjwaIXgzh}F(2g-~3W6-oa@0jCfk
z6Q~wmt}XQ%*uMR-G7AM{l8Bh=^u4Sf11~9sC4@cqNQ$ukwZH$PU_tH6RTa0^5(rBi
zSsp#Y>bpni1>?;Vd!|D8=H)y$`DSz_K%5pvW(?OF&4q7jT@F7;@haCjhrX!UxISag
zot4RN6k0j&8K0B*5s%fh(8mbsS^2V+%IuOBv3+loYLnU3w0a?z-7VTyC-C2!e~A95
z%kJ%Q7hl3Ey+t-~mUPczNbS~DF}?{Hcz2PXYGdbN)qy69=$b50wX6qptzi%fq8lUV
zWL{~-E^mE#5R_lGC`3~!wd5WH@iIt3pP^h`L5sTL@zI=1^M6ZAbx^-9&6Cv>KCmDB
zh}>yg`}9v1EBq~^^9W-`8bHY9uW}{}P=Rdhqlz04$CZ>L=C4@9CvdyzrhY(+Mppl7
zoA0Rv%BIlltNsoZ`?7q^i0o+437-&`O8e!P;U|vTAZA}
zNA&gEXJ#rCZeIlSk~R(9Iu#o$4x%h+ytl$-ScRk|UmbYD7*SLEx834$vNC^979+VkyW8PuA<6cajsvD{
zCkAxra^84(NwRK@`S4?M%
zwgPJMUzB^_6XO-L%ms2Lzw=us)V%U?|INA3WkwyNr3XS#1wkz`CJobv>~Yg)$VaNY
z_IuKY(>9s0%kq@Gq+g*0sL?Sq3i1T0>_P4-Q|lY+8NaNke7Lam(N&WgDJukj0)>~Z
z@M=@T1*p^~cJW&HI=7pxz0?@u$Zfn1%~e|XBx=Bc4x_c@nR5pVS7Z+9+|8+y+_Dua
zBtG0*H4^m}RB>Xn+JGzc{RG&XpaAmafI9_M<8fsw!qCK#W^UrJMEHIXD~3>R56y=k
zV?qrnov>$h4rAiptf_bDT6lu<%NQ)YXF*+=C@DY6OHCDb>!#&0A6_W=+bOyMY6**)dW;aO`WZAIh#&w&iqI~Zou#FeD^V($RaUliVSk@R3)cBp
z0+CiMLZFZoR6!6T;BQa%l}1aWKmy}j1E|rdQiQs&4*#o=h8~j4#HjgI6eJYtIc@&)
z&Ubfwxec=!pOnz8{>+0Mjd4;m#vsHfQPYEQnEBx12dR=}GO*V?o#v9G+aja-w+l0t
z_Wnjjy*fJiNvuFK{Ihl^)U?c+$X`|;h_TOXhK3C>8AY*=(QPWb>
zI)#ne2X5RH(Xfgssfj0i#}O|r9k_CwDAuRvDSiiA~7K+36yQ6r*XI!1ILfV}(UI$TM0{#T_j`&<3=0_`N%+x;5
z2(pqJ3GTx37InovM*f8VzYL;Ci4G1`fl35F60+j=SAc|+T#)C<%{q@GT&Keg*iYns
z-JZ$qCS^U7esDU4>o?LxO}-c0AqD{V!aUTiw7GzJa7_&u9UJYDR54-}9t`-ut_lgd
z2}~8rjE%uZSH&FInS=Y-ZG}2D!l7wbxwD)azbI8ujB2DcsxeEAhU%5uxZ>r)qV=<;
zJj9nnWIF5U5?$rLj!M){Dpfinok#{14>cAgrV8Q;C;7xsviNnet?eyFkCltO;uJ)4
z$7%?Y-&o+5uM8Y5JVlTCm<~)O)YO~Y7-yRr#4oyC3XGZd4|w8D1ha6*PZxYAG`UH3
zCd(21OAmh)V(P8Ds)rT^lBAwn>ZpSj)awo1=U|cXx0E0v%hYy^cES=Wa1X$4>H$Xt
zFAm7`o80{t7kBw#dEPtWXvF3b_xdICOJ*J$f`C2k#r(wO7FQPgc#NqLTUvkupnVHj
z>LyoffHIv@xP()z)9m!3`oVL
z+LpeDMZ3qit2^t3vDP>p{ka-Z-<{rtx15F5*qt{0I(remK$EK-#u`DF%gt|J?Q~=n
z#!B5sa)7i85@>aL5$wv9N7qvl=yBaybeGFmUhM~Wd>_gmVJ%yva1Q$kH>ce53qp=M
z)Ht-A`t34GN&xysE_c@sbuVj6M`3DdDYKVa=>A1pOE!qBs?vB;*H2}Y#b0CCvK0+EcK+ei#4`y6aivMx_J
z2aahg^|LBV^B-lUg$fk?zu{j^O#oTidK)XnE0;~)Bl3I(sfT(!GkHNEo=oiyna=m7
zN&pZ#O`v<>cqf=u)lNdBkj8=)9YVh@N1YMY1~h)hN%bQ#NPitK+5n?q3r?R6(MUz8
zBNxV!m?k2j2?@snM|&K82RXCtot=+=-Xsjm6v!2rK*g8V%Q=dh8=wZj5^reN{nQ$i
zwbp{;fL3LpSC+WMQ77FB%#l0)H9h~AHEXiEgPTc8&G~2`WDv&TOB6ia7$W>3n^hdL
zl304BN~QESW-2rOD%?GTRWSt`IvTpi;1RIltOT^LzdDCuiwdEf%M`^W0@0k-sxvF8
zcy&mjp=u&%LLi~+|II5vh<6SKuw>0-KXmajfJEwY^N}ovKS_evm9fJZk=K5Mq30nF
z>X_e078xHsd^L-lXL8jjVrH`XA18>DSwN?r;~io*Mv1*F>7aO|1lJiN(+pK1(f3?T
za5iIQC5pshG);Kcu6!ND%f&$}ZAN=_MEr%ay459{wDrz(sLTD^<#;3l!UP-PW&?!e
zUBsmw_~c(wz3n2R9!y+B^5VBcWQDU76?J87&mmZP%m&dLq#&nQ&T)Uz3uS|lY2hNC
z$PIH;L-*fd6a%L+c^WPHAIYj$M*
zO(@4#?#Pr*T{Q^_`$+>8^8J=1X2!RD;u27;rJ3VR
zrdrP(i{Ues43;o6L73S5+0KlNF*=sK?)>!EpFP$5=(zBbMuDMu*DOs%msPQDz5yK0
zV7R;t#b_9F(*HF(_K?mgml%6{ADbr*=~RWmbu(V$a(m&PV^M;q#p!Ysdyphk1u2ud
zm<3|xQoxbLsf1_N^(2FK;H_*YYUf*|8gq@m&~D)C8-}=3d)a@OWD9B>jaz8ake-Jx6Mqb
z0hG#IA~74K%|g@gslK)N#|j@BsT(0re^E}YO3keMS8+F&j)J&!McId0EgE+AD{Zys
zTJ81^yuHWiu~*{8kyI@wSZcSOCM>^IGK?K5qrz5qIyjrS5n$nT4!B4b
zcZqi-I2Sgz1;Vcb7z%u;iek!N<0ooiE@8L-ru)xe4;@-~P_LrOk!xx3TZ00b)k4_D
z>7MfMd`eY30r>z8P0c$g{h5O8fVT$0v5>dy5og+X9ZtjKN7d3&dCAdz+HY|xS{On;
zHq9@Se9OZwXFZAzbSlS6%KhK$4on@vQLdSGH)E#rZ=lyPhQgWd(_tVA338HIHJ8$
zPTrl^7|Bpx+D#w(b5srh5Rl^TEoq#6N22jYJ<%p0skl|0(nxU<4kemk4oj>1Xm0bZ
zpAA}F;}0osZX_*YtyN#!Y9x
z#d^H@e^A8_jEu1S_*h?^y-N3g*`S;8Ws()F!@AzKG}&@HdF|jhI&Sy9b8uzfG>-Or
zTt+#UckMgQ0ocqkX4FXH*T4|`9E0APNzd?wBirJto_UX`>D8QYXKvyncSXS|`W^{J
zW61m}@fZI%^j%ddpzrzM>rDM}&Y^eKUAIe_8tLDDIfgC3GCy2uZ}A?IYB!43l--A<
zYyfbn^I|ajLISJbJ-~7iJI_+hDpE~KXaa+)6jsjO)hO$5_$nle(=Id6jrjd=i*3hcX6I)cutl6_4;B(qx0j+ic4A
zmL=ii{^JyZ?MFERuWzErLsDh?(AA&(=CgQZ)prUZ8CW7=l{051seIarR^{9Llq2Qw
zYJ|y}TAv!|1W65jB^GpTcOCPkG2)u7h~(FWsEU77oEbzJ%*FTcQk@_}mxL38GSei9
ziJ0+wWK{_3zi1O9w2K<|0%-AYQ>w4eZ)VEXrgm7Clq;L3lTrA1-_h7WGVYe)pK$t+
zoSjgrx1lDt|1CN_Cb}>5
zHKBo%7Q1I7d@-giD#nMh&jgprx{vm$y2uPNJ`*0@3H)b0@6{wwkei1;#Qk(C4Yc6D
zkZiNFl1qY;u_0gDQAQP-faPjHhb{ZDG6C(HAoI#46!h@d#HsM7kC(D=6DO4drR8~m
z)UWEYebJG8&K^bv7s|U(KTlU&XAn3Sqsd6>lDc)-Q1ovx
zXt^YVd-s+1MzdTz5%;RYV!Chy!WY@c8auMc*l~#AlB0#}2jdmu_@h~O7?n7$I?fLh
zRalu;v&iuNK19PUySkFY3%L(vk=e|Pr75`&b@NeXk$DRGJ1&;m<>tDBcid4^-dI{E
z^;IhsONkaa+YCj`nIfbp_-?)codd%qu`8SyC5e^D6g`ld_$1Mu_K-X}L!-D3l)3~K
z_lbc*-XJ|$kCo(2Q(lwGuF#<+@~_Dn`h%v6j)~ew3R*ko2z2lY!Nla?^QF@$CEZkI2A*F
zXIC(Dohj-6<4KTP)OM^tB2;)aQe01$5F*)W(C#Fe*#L
zQWLE%N{X6=B-Iq1BonotL`1!CSo;14AG0p!8yaotFFF|#qL;wHAeQ+7+(HSNxItL?
zv1xeyhid3CczGIH8rnx=aluS4<4_`8cpfc;|$Nv#eR@V(F=72o!RcjlJ
zxY0~$9oIcbH`ZoVwQb%ux3I|O6OD(h&!zB8CQ|vV1a_6pYMv7+?*CLI#=a-erI87f
z{)QFC)~1m~%|$2vOm&l^kRmIcRB^|er1HT4J>=5G%}@7j4@E|cwHE}^7#@MSTfaVY_W)t6{=Q%cpiIN`b~2
zU$CPWO1Y={)#+mc+L+eJY`(W)Nq~$DZA{fz5=(TOB?*fat5y_7r!f@8PyvF@GxP2={`5#q8xBokB>b-?57b#)_h4)5LXZ2lQ;O_ro;epSFJIg=Q4oJ4^ojrdJcj#$;52uAXsHEmdYw1_5ljJzOhS3HWW?|H^Z5f!PVu#85Z
zQ{XRzc|8@TQ60x(&W$KES6Evc;9r%yDc78(#rL3_;)CL}N!p0;(FX}PlzC7gF471s
z)q=qaCVt^}1=&C|j!;)+9W#s1J!v>pl}lnsC=@X!7ADr4K6y?pCuKi<_Vydt=!;7q
zmwpeG394#ozZ*n)B$YJbif3Tw^@8pf_J>}J%QW-bdQ>h6=p3PurS&1{}Mh{
zOcKuH_!unK5}`0-ie}1bVkMGhyj8FArmy+fw+cGwBS19_V0SWOqc+=$>XV@KZE(
zG$tCl$@>5G$hnFo8wOD^HWvk(aepym306f5Dm=m|6`gcTdq2yi?zLIYsxM$k=z#I!
zd=gx4T@gEm5wtd3YETGl<-rBHT@_eD=V6jEQYcV6TUT97Q6&hzCjO`3Fk?&fM5`-<
zHDI-y+pdCu;bC}a7homS-f7GFRjA!y@r{^8Q5?OQ5HdJ2(;u#0NHR^n??!-&jZI7w
z?Zq$BEM;G~^hm%*URAwy+t#XoxSLG1RJn_mo08jKjbdqRu
z|GFCSHdM_=IawbiJVLU3_1}Dt5-8szhKa`g75rR`dVPo73Qg?_J1P`Ij^`orDE`3^
zCB#CO)RJ0BNTKTO*HK8`mCGSl92x3We-
z3>tulO*f*pmRrxM>KrXAVyV7Br|l^~R>Z<8^`lV^(EitZz`}8IiTBFU9CD7pN}ax}
zm`ejJIcl3*8JCwddLdRY$}$UP@V@5?KU&-(bk?g)#ESs8Z@=Clk(cMl%*1f3qWaqp
z!SXQ*LA^<1;LMF{jt%{7awMT`F8KCs_*ru_fCJW
za+lYMpmlS~?F%5Wz%!s%F7Q_P@HC^~`dJanv-g^2teFzvk)@uN-71t)Or%RttLmiHW{#)bQKoY82h(kXk11@&sV_;oAM5&;z|{v$UQXbO@&w_{SziKmZ~x}IA@t>4dPnb;XZ2S8
z6>!W75ZwU0Tz|f90N(Wj=8#7KeZaZy%Td75%Y6sWz&RwK9S+z*>hktNTfJ4dx}=)P
zAF<9DI8nE*m_K{ju=VP{^%`%mY#{K?@Uo(rquT3fTX1ZcQLeDX`9iEPt6&ksQFqn#
zb)9|)pCmVH!+qkiX``8a-Z68-UPyNb2T*zCbyJ^lm8n~uz!rVpxZll}N~qtg$moYgv9%f|P|a_5
zSGk;#qV4KCASU@!1wE^J6@u778BGv{o-E%i_1Pr+O}l`TyJIE
z1%6w%Gq%20&l~K4Djx70pb>OccQU{Z!^pi`?J=-?Zw&h>!3n5lpr_~eqXKoDpswy8
zEXxPG=hif~=J7|{ilHnw%SA)eLd#)kL;KNG&G*2kB`sFn1Ijg3+Y$7~#YM*y^@O^I
zpn`S%^S0h*q3aaM&5BN7c9%gOKZjR)kJt9G<_sw(zplbtHu(p;JHHD*hn7VB*bxD`
z<918uR=%}k@9X@%g^*0ca?Zqyx#Fcc;bYJ0y@d!v#z&Kl2C~Lmc3Vu$(OS(#(*QwE
zcCF9F`m2pQPZoPG|L+!*>kMrExegLW0R72EM$uIMd-2?9hkfH5_YeEVr>GzHZQGOq
ztf~G2kEg{AW54zE@}AlhvS+rKo6pLJAO0y$qdDqt`kXTU(#XlFD(KL@D&P9}n1cCu
zF-o6m;ccj}87i+p`m9Mj*&_K!Sv|6GSym|h1S;JW)dlCs6!RAJ2RQd%R5)$}8$ioF
z;Pm
zP(M1h*RO$!GC;!S>spy@;8-ta>X<;qqy!GZc4?Pq$Q5q6+Upg}+d`jeRS&k|U%lUw
zR)trah5pEI_d|16-G9tEPBXZ3nlnUdKHWM#L*J4a`m_4}oDRo3^6Obc8
z(PGGWWGY>ZC{P9XVVn)zty^X)Z;$;26^b&b&3WXgfhHA;XkB{|IdbcD5NpN+yD@o^
zk|*KYfS`Tc^5nP2YjY0f-^LuhdXhb>)vIbli#f*8fCRz5=~cBWdB3hk*IM9$KQ?dv
zOpwd=$IfG}{`SY;N6@Wg26)ov@7qsuHht0mbkJjj
z{SS<;28Uys$7-LhG#xiA#|xD7QTsXSPvrf4L#Z?lk3h}x$?m$()66G4oh|mIKQHyC
zb-d;0Fuysk@ge|Ij_Wql885YI7)jO;dJ$kJ%7Nl+f$-cEgK5d0D;>%y6OcJCW75y-
z@+ByA&4TWc<(e@qFY7gT*4~gpgoXtjF+rc#hiCh`BMR4gdKWLV>aNRj%QFG^2FGQd
zZGDPPr%m2~yIPNI2i|I2{^hWo;kCT27wIpjL7dmRx|BGM%ne!*dYjM5u`m*)tHv%Ni@wB1V=)=m!nmZjULhf|l%&W5~o1V0*H`0xFK+dYp3J~tT-
z{_T6$ukX?kYia$An*9nnF{_LX|Ge)*ttJ5&HX>tk2Ti_J9tpqbZZlZ
zW0uj7(s>XvlOd+sV5qB-C9DSCH#H)iIZRho+FE#9S`NFi1e>uOrzj_-BeKnP+u9-s
z4?}S5c#}H>ix4R!XiZhV=en(y>PieG4&OG2_{xMW++MK)So7ExvWyd|tO0#S6d5Zu
z2Xih0=QB%wzuvWHE1b>(*az3?8?q->k1v5^C>0eWK79^AN0CSS1Hk3hcsx(slY3!j
z=9@s@c)_LH2g3|u&eAZzK$=V0oQ4piGy(?OlbQ@1RwWJ?06!41PN9?e!R8TdFAZ
zLB*VK4huWNnrYg%P?#IDXK4>;Q}^T_cTV5nIrGLMI=N!S;fo^uh^did0R7}4>pPvP
zi}_n4WqVscpPpc`+5dh(FK4jT3(#f*%>BjL4@k+3Q?Q1iNW1;JdJJir7lboq3MYc@
z^J%)eU(ykBcQce^eOZaL3eJEg^cUKV2^^v%*mgASuzo)R5I>*bKCVO5u%!2Xr~T2c
z#aM1RBMPPcL>K*s!e$*$Gm1*WWtyBH!JLT83LU?Z3~5(+!R%sTr@E3408On!txoOk
zBop>ueA;9aG6EWxO3c7Ss*;*aFP#i|0Ua$bo0>RuBpHA!`@Nx53YOWIWt^rgKT&0(
z=ut=aFNOEd%zbKEBp7k+MFwRPegSJG_4~~!fvdW0+6J12_&`kp|M|&7^#PG~v?NPg
zVm$~EIg?QpG?JPgp8x}XewM3aOcXs=QW986*)((0+gu{pR440JVo1z}9v=~dD5<+i
z)faGBdoXdb9g?O*CauY25w(K8Y8uX}$cKjqcc%y$0*gmS*~lSEOCSF6*G!9r&8R(qp!kucn`X73!vM`F
zyMm~sEq5fktOBtF5mTBg&v+P$NEn`jJjY+$9yjJULP`FiosI4-{ZRF&5~hYESdCp;
zaa7;My)#5*<&*|9F6_WR?8X!Ku%(uvNz4{{a&SD6@H7S3HEU=AZ-412Atg<#jI>Y&osM+X
zOdDSdm80u~ro#ZMLyt^A#yAjXT8{`5zRj!YpAYljp8
z?4|rF@qscbNy+|^b?1^D$BSU)c=b4sfLT;#@Z5ax#pI#V6dX!8T>7SsNZih#lXlX>
zU}9RX3))=-LJ@|M8G7tozQ6>=2G|J!ZlVKHRK+B{%N1zLv$4Og*qYW0vov$6pFWsa_~9xlDDlZP!-;rYG&$Y
z_~gx%GOJ(7;8)QtBGz)W9DbbJ)%nFYTA2xJnDsK5K^1(bsTUS=dl^y>hb@9Yd;m}L
zq%x8J8X#k~YNhOiEqva?@{0?eCwY$*cCpFsV7)?I!&N{#S6x3Qy2f-Uja1nQi`z*#
z^({JyA+}3XmHHU|ZI?F$qH5)uS4&EY@6fj>V
zWj(;fA)wnr3aiK!!o|3KeH`*Y+*tr}b98NVE$KMvBMv_~VsW%?$?(8e2Weaq)k(bD
zz)@lGWmV#H+{zoU=H$I^calEn?1d=cZbXoL7(raSNUnV(=hTcH7!_Pcj#qM>G*%?w
zb)jG-qzK9sjDrgry*&2WFK#SvZC6MdM))~ODbqhddi~^ZP{8*MQT{8eyNCp3)?gRs
z^WY;iu6(Wxok@9D3PPMO7_m^q(q`#}
zGseppl&B3W9qf!WjWmt%;>at1sp3>ta~RykD}S1hwg?i!l9M!`8&`Y0$?{JB9^kHl
z3$5FOV$>FHjZ|Dq#qpU4uGfSrxM!MT9rbeK?xH7VXo6(`31taom+Uc{K~j^P;4rNM
zCXEU833o3S=MgH;!|cKp<{T-B0dLg2{A&MvoU7TL62hltY($}(?
zG2YbKEM!dw@)W{lqwfiTHCvn-3xD@^ZA}Mt2mG4RLYM3alJM>}%+A
zgEqf*u}A4~$)8%mNHI}Hagij%ph?6S;AXH=m+1#7XUIL6$wXa-#jU|o$))EeRK7*J
zWHT{ETyb}*lS9)s#UCq8NzJyl3i&6Sv(mKGq@KVAXqM!d9b|FcT
zX@cYsPazZ%aSBzTN@MT+&BDS^&||io0i*lUHd#AaYpP@yy=d7|DeF#_-`my9C(^FA
zYD1QBnue{YZpRY{8Ln71FIIJ-3I9Q?Y&8GXSWR3cGTPcqkuqq2%Q*^aoPT_JkHuV2
zLY4^;10yu+K9Eo~Ybj4#DiU_kl_}|Tn$F>;x8mK`#?`d@ph!v$yRH~*^^Pi$bE(3#
zxFC25vxKc1JA5R{o$p;edd2t2q>3Vs5cE1l>^Pg{@O57@2#L|;OSxOk5$}Wwp@u{$
zuy=7ym0j8Xj5o!b;Tzx^Or;nlDCUW@99vw+Y6I=?+2^O3k4sgnCr!SS^gf@y
zC_ADPTz95pMxs$Sn!y#im7&^BgX8=j_EW)icqeMUDrKo*Io|>OWx~bs0?kAoFaAHp
zlr(L|^GDu^`WWi4s3DHy9x_obdvesg7jZrnvsRXKYEL-PR5CaigUn_s**ud}a!Z6d
zxy8(%`RUqWO8=yhW3*Kn&D8Ah3ELrDb*L?y@y23O-F}gndDmGLkeLP@18jaTBXlPw
z36iQv$abMlxdz*vPPiJH{2g$b3$6R(qhcpskAd{P^04Q`3)7IrW_*9g{s7ltP2h7^
zV}`AMo(I_qH-6A(+5tb~Rflc-$HgJD10+Va*#a7EtAR$XI!Xs}z>h;wu^oEr7
z+6*ic8L5(XG9Qm0esx{aS_AHo9V{jXNu74+q$bjgk47f(-Tz4@KG5tU$o_#}lSNDt
zZX{om@%F%p{+dhYtQDyzt~~G%x-&VzzRSG_Q4{STPmOOcANz7jI^$4|5Pd=ky>jT%
zRu!aixYTqrd1jW$0>?#M9Z3W`&$2!-Zq9+fY6nM3+hCy8v^QD9@9q8+^Azy2M|(~q
zCgNgt##fPnvnBWinyupX~S}}!h
zjWCDt#NHMEQQ`iUdc*>hO;L4HM{n7G
z#V$EViLIH^;u-VFpm>$D>D;4jaAf5znEKh5=@>vg7nLnQuo!6M#*1`{3sKdddB;(a
zzLZP&GnaKw2Al{4MMQRHmhAXH(nPP4s{)MG?8Eskt}9Td<*^r7!(Gu37LaxHe$@Ae
z7||!^`?<|LiqU*r#vs)GKxHOnEpr)Ifo37I(iu__&O!|PiH#FkA8#Pf|N~DEo1ct%Dey2t{UHC$kZhaABxy2@-LoSKARxt-PKECZKu(Y>-?&16h8hvw(V@goE?hE!hb^8_EY39Ka!n?d*M{9
zrDGA6$_am#FPxMVs|m@GfWhnA`Iv}55W``!V@&k?!N1@2KG|zi8)`W{WA+iS_11!^wYGRX=%c#v9iqTvYLQ+}gPy0g@lRYConZ|Gm
zQR^WqJhOK$vJ{^N#KuuE=#c`fe6gEw8bFvdT{1=9^k!^CvmF~gG7h>4fvre)34
z&IUn}4yI0%!@gf>Ys>*UQ&%cxi(2`Fa>`fpEWT$G8Qlzev^44@R0bDUF*v#Anhj8d
zrRLlD2_8)sjQ3vmHeFkE9GZvu2gZ7jSgWKA-Wv1C
zxgkRCu817Rx6+r3#pF(=sh%AEbJ0V4S~}#T$5XifdrWS_ndc(4(gB(GH}+W*XO28@
zK{(bYXR*^}5yjcSrYa--n3|@UdTgB49x2AC)k&O9H75PFYN?WN4A{ja+EZF@#_5>1
zJN2-PVlp3=vVo3HOgvhghCR?kC3LvbsTpde;c1AR>?+pQG0cP7oX7zSJxZhgI4Y%-
zd$MCKPmDj`&NH%$>$X50mfE+>=ynH6tI^H=Psp>UgpC)cDIO!qVgjb!{&$t|YhoRI
ziNxxOUTC_6?k
z1~g*=64%c}XmRf#AEdzI?hB_%F{43qLM2O`p`AL>b$u@l!0uZvFA)b6b)POQk>PC(
z{c%faj)`Xoq*TV9Spnlgg~*ReoQdX~ez93VFiw_d?SaR1
z$!QRL~W)Hy*
zy9LS3T8(TXqNWzp}K|50@g|1qOG;m1ddUFZ9H@yka$
zb3!`Iu2^Tw_;(^%NWFvF4NvJZ3TPVF4$lA?ErRBTM^=L}?1FHn&*wNybL=4WNS!Vc
zbORRhs<54qHo34JF53sYt5#m>a4i}qg1vTpfu6{O+xAT`eCa!#(=(|nGw;dCH!>ox
zc{|B};weNa7P$HU##3=ozt5Bj$YSyeTO`a#_xZm3pV&P3canO7i%_|Ghx=rtTSXv;MTf$fx>mlOgq#m7VE|@`
zAESNkA>>3J3N4uo-46X_pm1g(-5Z>k+CHv))X5;G4oONfF^AZqIj0_Ui4JK11=9iA
zShB8(t&*4I;p0SqOjc4%ji^+dnzi0hJEM!&9z?(XQv0_k`>KMHpiITdCS+ui%1l6C
z;%@2B^_!OQ2hFN
z!&yfO4LA8d9zQq=Uh^*B$1J;hHe?z<%f4J%>^vQA7t9F~#CDvJaEY^WS)&`3jjEh-
z{hQEJC`;;X^k+?m`eQG*nD?{XHM#{(!Le`DrgKA&^R3X-IpfrEKhf{8&$}hR4r`tA
z@1Jf*ewop1>lNx}kY?M1u)V)_U0?c@f*CLGH?kao29;*sPc2I#i9aYvcrp_SzhmmL*3)@*x2}}Gzc5_3QUB~#^`(y%y!vgBvuFBG^TCY5
zhpn=;amDev$gXe2^|}a%?)kW8g?GhaNU*ZFR@TcA%mC^rpEj2pLHTA~5P2+h{SP&WmWiF%
zNIS1qbO~uoUEq}thu@Kt*K2QHY=lY+$dimRA_ZttR{XO?9*jIT8E6Q{0hZZjU
zUj)K34CitdCYDx@`q~|4#!ua3o(hrxhW1$|0Ckdw4+FfTM+YT+uByt=noiteR<4~ESNnQ@Nbx_o#5y2
z-s}<7RcMj#3i08^$@K+$J|P9VIBYd;mLN{5Ku~n=ug)Dqv?_xaON)_Je}6C>9w_j?
zKknaPb}02eum;#XUKzguhq?hD408R3vkkM^oB&6cio59B)gO-8j*Le$gaG{6JRSHN
z^GEOQwwue5E^Yg+_FIQXSyzG65<$=;6a6B93M`<1a~GAl&!gSZC5xwV8}Rd16*y*d
zqs!5Ko#Vg$y6e}}Ns!H={b;kd*$^{zFWAvye(hkl{(KkcHm9+*9eX`t#~Y-GwZi&C
zSNCq|QNgi-18bdnqmAG2KV_c3MBms{5lyxyk7zV6T0GYI(bWu>RYGBhM?^!MM)@$a8M
zTs2$H_2WVQV{1}>=fd0Bndo$T4n%NRpH=rZGURZ=?j-nWNl><8%-zj1Cos~1T``p-
z0|ky@t^iJ(J#j)8tUw{Mdcj2UZSP~#ouoyyh%M_(F@wS-CPDCYV(BQuMeN=*%eUde
z#-F3pieKI$C5N=wb3NkyY^}0Az&DEOgVB31H8L4(l9(4Mx3Bni#&EWf(AwPgO`u%_
zhv_ytiL>V;7H~3r4)oNoPhTN^o&n3X=xC?D(K*Kon7Xri%er^>wRP!*cYSuwqQBT<
zd`irpa$Yy>`!v1n^Y&D2yrxjiR30R8B|>~K*W5ft5eHW(aTCGyEI4V%o=ats@%Q`M
zq1)#Q>nMJ`DhlZT;WRaK#1k!<+VMm#70)NL%Qb^&o{BcgB|OmdE`hj>mFeeX^Kn?x
z_eRwdee@`ZuFOmil0G{h3!;hN%IaBDf4-A7FHnAX^@hBDCwx#fzQypHw{-1=?7}AO
z5Sb*8TQX}}bsscT?CsGBnEafnfV;bS(p^XES2T1qWVG+i_jCU4v29grnbLemWpI5z
zauwT!-0wzswO>K_`EqXONxA)g`3a-+-+4fxLGyqLr5K{Xg698(1}%`F#m>>xmX(=>
znZ+Co?EiYof_mx|*r%lufVzx>fgyn2gv?AB&5YfQ8O%&B{pB0WCEd3?k7d4*qO#zS
zg`yu*fDfYT`?GYL_-oTjg-`ES%8JjA`~Eysa|eHD%$`?UhjBbLO;P@S~#w;P={~
z@OgMDDEQGdwUv!!mbp{TzGuhT>ElApS7tAjIRLtemCkIdK$lI^x{8awCDM!(MwRXa-g^hf!X!s_@j$dX+-KO!I6tNVVXU?qKJ7QI~`H$yIyJa?Fe#-r_6-@Z%8grwt>M
zPEo4h$NAHcr{{lvGo~2e^ci3TIG3ICJFQ}D=LP6%hab-Z^7-!znJpDRuMiq?FLQGF
zJaEH8+>9>^0a$c^_fP;m46a
zxAs0CQs8$o(Bkj?5QewyeRTAY;cz0DPtgBxU$Ol@(NVwM1^ms*xc7n$xZP~t
zOh2UD?zwqqee=i&xV`Og=$}!{_kAhLaM5ny|C4ny|Nj
z%@>@Y=m$m!xD$PT{_A^l(}c3WpF%i1)*uj@Ft(kLU>JZ(c)f)G{Of6VzvyXapH%ba
zMi%rrXd%Llv;a7U-`C!pR`)KFs+h0mZ@2PyMs~%p(|>=NmeZi;X5+F&F^?Z`ewZ<9
z@Y0J-OkKeJ`77k@<5SS%d2Ldo-Gwm~Mv;)0dmBi3{Q8;opMCjF+4lR~Zfy>|w7nS)
zN%;QRdVS;sokPHR8g|3>d)v16=_>-G0Qk-)v*(BDFyWi8;m7jKw)gD_w!mlEHs0sX
zQOtICz#a6*2c?7mQB|TrR)Y#)_uMeRIo)wRxge+i=HM9c!Mp8$yRrZb`ot#Gc>28R
zvo8jniEq;hP;isKsrz=Hc6-3kliG3GHRlhC;g+3=pkfZr*%)Xp2vZ!%iEN_Za#_|m
zjP{UwqjJDTfv!Krk{?O%SxPtfV*{g0==`Sz?R*8YkM5{he>yXjT-dy%9W8$TStJNY
zaz0Hcb?{d#Zwr6LQ6g%K7Gv6q!
za4nqYBWlk&V&rxS?Dg-xi}P>Y%3t6`tdW`Wj6wwj&HkHe@NZ7=~
z0M;(fBoEFtFO`?sroh+jX1fV7+%hINb|zPa
zAr$+{H1cR|H#om0L^|6Mcvk+|0lLU~pN?+;THVf!n@
z6+zL~5&p>U1^)aJ@m|*YwJal?;!B*Y^mD!W5I9vWX0!3M=~<7Zf0Ajoo>vm5K`&st
z3Gh6!|JN_pMJ}(#b}Zi7#X1zXGC%>m&>oTRq6Q~D9B`x~k^N=HY~UDEsKAF^CH$KP
zxCl9fplrpWovH3raKXfW(kzN8R3(QKq;=%MG!C2SzDDkOO;Lw5&SIQp;q%V7L27#vex+hWZ)WnzxnxJPb#sR=i`x1e0C9xtD#Tb)k%%n
zhtIUlna!)CG{8+uYxiyM7k4N$d1<;VG;yNAZgz1Sw)hG3VT~5_M|Ys}YguphTZN&Y
z=NGuTg09j;Vq<-yNdNb)vt(9>>EQw0`XJQBV*Zg@#_30@C8xHIc3^nVS)Jpa-c>8`do1IO-81m~TC9;a39guA(om1Dp?^
z?SHodcDW7CDsW`c5Md=X{K9S~N@1=S9tNvQ8~FcAH4E=Y2n_s+iP&-4k@vS*3oANsS+B^EG+jGl`1K&d
zF37)~4-MbpKCk-x+-K2|YD?A0xcQpTT;(e)hjcy;_qP+@WUm#&6k8@?>N&F1tWFZ@
zRR5;2&)d-pe_brehu-$%$ZmLxiQ3ZrJI;ujlt#$zcht&&;daoIVFqxoo2JK4)Vr$%z>yLh}
z2W&mwwHItZylJ0HN`imdhpYD*(mF{E5y{6okJ&Vu54(SEH+}j$JXLiE28>71Cg6W3
zO>q;-2?EbcyI&%vX*>!vr3>tK&m{Mi%Zt!_b_d;a{vv4Qu6`*^n)0EW7Qb<4?G?h|
zmL@;$dz}l*q%Ef(;gB1L8iF4;|3))vBAV;0jdkHIwP;QWLP!ro;6kZDql>;bri`&5
z&3G616Ey~NyR9`-<1g?@nmV_IPu|S~pLU8q9P$1Xto%F+2#3I}EU11QgI-h=+Q7gQ
zY2?mYAW`h*Vu1TTPt%1gLtGQa#jx14PINo@+BXC}hx8q_ISzA7bPN1BXQJ$gi-_5!
zv(S+*6yC$XHF2e+8qLFP;q8Pvy2ZNflRRsn=(>ECyH=UE}J0)(`wEfLTz5d3mx`pfjy=FY$;
zj7iXqZ2j_x#eg^x>VvVIT^(HuD8c6u{7w0F#gSlDw!&Cj>1Tfc*ey{^Db9#^>lR*1^x6clIsh3?tSph_6O@a|U*gyy49d
zf&-VMC+zFWLx!56c1q{$uPX1_O7Xs8VOO-bAhj(^Ig)$gWlx)DyfvfxH&$US=fU3!
zrMEGK3Y{2PxzGkRLhW_8RKD3HUfT{!)DBK;ayV6NHf?nx=k4b2uI3Z^EIhI5_*&%s
z;Z$OBhR(=zvDm;gbM(HhCT`f!aVUSKn&}0nDlwz{V2TCHLxAJO!ceWo{
zwU&bnclw8>8va%@RP&TdTxOat3`5LplI-)r>K|x9Y|xK)P6O?~X*7%t4zaP6!fYN_
z&)zhw@>2`lz}jeFBFmajj?=};6-VyV7yF=zNg?J@*-Rj_h5ZSzU((9sLMy_leFCbE
zV+KSDMv{rYO*}0r{BfngO_k5-stKf@|1?&gfMC$N){9+#+13EXYBQgXplA5;$7elX
z*U$>a*rMP|gH8GC$K1t?+CthmhEmCj!f|&hR9YhfrrmZH%qcto#fT
zE8Uf?#m5~%wvgT%QZ&f_1TN7!>`*AANP7INBJ34Xs|{tnv1k|_Hq|0*U|C-1BU#>L
zWKgc~#-3&u9Qg|zg(Sjs&Ch_o%CXh?<6{D01k5tYiR2sJ%#7o1ZX1p8?CP9jV*&_x
zYYQ4yB}fQ*SN4#wMnN+MjSxwjoN}F?3ZuT<2xr*lL17{#X`$7H7eA917v<)`RAphB
zV6Z)4TF=+c=lY+{2E?Y+c(J&6h*-a|p3uydqpjw*$QN#a4M;A&q`4O-Si5k~`%*Lt
zh!1$oF$&2Hht#J_)akyr*89O=7>0b=*5HX)GQ-S%GV}U+>S`8SVmPa9P?^mQLt%~GQtjt}s5}V&?M<XSC=gP#(&z
z(_2%=G`jPaSz`(hD_08zNme3g)47|CuhD@Qby;i5;McAWt}M6e6`>4gYUC$@{FXsw
z1lHoT_7@b1Hm4zdI-1iyy#o7-=Rc!p3%Y$TISIUXm}1`>6y9SAhGz586-zq3*fPIG
zI{o>7ZCz(nlgkzj9SsC&5)dIsl`d64DN4ssLKTn}sR`ht6Dbj-h#(3g(h-mjp$maX
zQ9+bWXwu6C(wqUFe
z72i+0z*iB*Oy7f$HCh`3=cN0%aq_88D?sUoertSU!Ij{FReH@dES<06_f$}o&0>v^
z23ml4%lSx;%}8SJ<}?vmryCEZ2|*xN<{G!ZUNhhy>$+9sx=8#fM_<|vS(qq|)pEd86Y~ar-NK1HmYM!UoiLJpPELcWx
z9D*9;65G7+U5VuDarpd#QT{h;=qsKqXwV7QMm7G2v0>MtqTB|q&0!g2TZO;6QlYT>ALpXcfEV4hT<;f=}F`Kxmu(@G%u0sU=Ve|
zM;9#EeFa)0esjrc*cqzU-yLHLf8c66ac{6k;NlxJ7g5F;os)S@FgmI(W1|)#(!1!H2%k{yAy&UA&CPLdgkM=3@Bx|KgeWOkXB
zS9lefeG2My)aV1E@w2BtG`4~0{q-|o_;@gNXjdy9XI<&$w%JvE(`QTwCcYO$S17VI
zC}6o!4|d%&P-<(czWuvvt>n6KcB$_e<<%pWSK<#4@X2L+_?U1qE9PHDYC+-dE{QIqV3)j46M(N6MoP7Rs
z3Fxp`Z@?f&MP}Ai#2ZUQmy@#h_C+;inasS+CADn2cAvxT54tSdTDi;)z9tjMVZAC%
zhw1sZt*++{*gIbNy+&@{zzAI+CyHf~E_-1G>AH~VqA3D#YtT(o6&+2D3z$Sy?q!cs
z2%F6dWeZ7ER6YH|`TR5;?PAf+FFXclHC43X-iDOv>Tmf!z6nu`v*!D(HA9A!k)>|t
zWeb(3{Uuvzo6>Wa+Q6JL6LHzf4`t&!&2I9|v@4z<^tGMYC@%nhU~#Y@l(aEV3157B
z)^T!3Fb^uL($U%FTLWWX=-bqZnVg%*XxRPRc0vzKV%Ee*mk`FdsZ^Uhg%1aR9>#q#8sy3
zCA->SKe{NJF%aJyy`|;Vx%+H%@+rspokwBCGq~jAeW`pAy5Lzi$a&1gQ~uNHEDliJ
z1WXBg@q6@=^0SvK^pAg_qDxY%Lz!MPjurQ-l>QbYWV&JxXyqQs%Y@}r_xXUj$b5^-
zyxHn&ZXJeOI!Jl1NKF`nwg-)XMFgL+;46ib8}dakqpsZGy>~o4#|+pno6c6}o*FJ+
zVub65)bH&rc?^wtVtw|zHzT;&I+hypElQ-T%_9bR;(32G|GJl%>ohkqp|!}%gx}Jk
zx6k`}a(wmE+gy;#gwylAHP~t-vfnpmnoEMhAh&j3XXE%X1!urseb~9$?L-0Tr@{Yn
zs17rf=)c&D$iFhn=q(<=w-cCfR#Vl`qSz*X&b^6hYezJ!+*g(=5^IeS9|vXchQ3>w
zQo?ZaeJ=xN#ILC=xEyIoCg^BlAN|M+0jB}a;@eiEj)lgkw
zUVMx59p8j4=?Q6Ft`CMImr><5jyAEOJd=mtUA4{S5nS<1dRO{qPX+(FeyUJ9Giv5Y
zC&1z1A~guDjR1?B+O|pOv@?~OS$7Z6
z_5Ox{c`L^rjNB(1NjFz}FT{f(t;~lZ5(C1)@`>?#x4^SLjAZAXHo1y-_9+KNem*MD
zdJ+9t=4@2Ill-h0>Z-^#eBPN<=-H2rn@@h6@W40xSwpGCr`a^i1nTFThOSTxJE-G9
ze`;dy*K5IW%Z~idp+(iP^w5f|PbT2W4aoa?eB8!`4pxEIrb^4Smg)*yMYq>cNv#pg
zvCh$F9};#un^##mm0i42uGV0e#&Q*{Bj{RkmH(hJrJV#*@-?_DocuHG91?|{-(9)P
z6_;fv#|MGkliA&jUXJ%4yG>)lob7HbC%QH{@Y4=j+=uDbWHSZ%Se6NuB}mR+73CzH
zLK{bzF3w;Cjh-?v&q313l_@U4D_zf6X8ZHg@gP~wUx6Rd$1&$O3sfhZEfqae_Mwgu)SLVUOcx5riP5Clc&&V7`R5wYzn
zR8HX!vbXBo)v4Sw8m~Q3N)}kGBH_qEE-go34b6_gyw3L!B6aDQ-1e33eVGUXKH3;dkC=9SYg~P{KKas+8QZO2BxPtOBF)DQVTbqtGY_
z$vn<_XDYc@GC()|>l7~Nl7VMgH#^2xF+{~h
zU`ajnsgYqLOB;40*gn1ctO3L7U_QML=RrH}oz$q{ytQPO78j1VrF95CJ2^z~L2Jn8gR{qZ)YXS+FK-HmOkWE=
z>y7KG6V7%VaxV0d`>|UqPWWI-axc!WS7hnR({JcCIAm<*l4>fmjUcAyhe9M63r$jl
z9)&HZ{Oys$70&>4_0mk*&wj
zV>7X)?}ns!3jVjRDO|Fn<>qKV3_=^ZpJY!2XS$(}yKIF3-v|)=W}RC)$pa
z9J^$i6S!nO5bJPf-v^(_cVfo3Q(o7IEa{5d+59B90vNFE0{U|cq91)zH|npaXX|$y
zlkWHAv?{Q==HHX$n8y_8J(0nm_Y$h{j-sbwBCQ))@3?Z8B&-qQZ@~@avtvb#8?7jE#_rpFrjVaR6Law4hT
z#VU3?JtM{9S9|LlH&*4WN#GZkM)6QD^btUfy)_go02qZho6Vh`Elvp@eoxb?>eJ-Z
zaWUiG%ML2XUxXLjZ5*0LDCzXi|4C^EdyG*{3xR?}NNG3m6)0sojvz${@!g`sG?i5Y
z7$Jf9UbM=b5vj7T1y*R28s2|RV?Zz$cE$i*OH79jIt)BxLBbB@UoF0J*I|HVdj{wGx
z3y?%_N-~4Ht!1m|1b_*AzzZR*1O_+;q`lRW@l+pPQQdzsO+I5OJ6MFkt3b7aQxpr#
z6kL#oMYIQp|fu)F^0fgJ-f|AlB4i>P(7ty12xix@>e|MTW
zlFir-OP}=NRs*CM?eHbI`Y5w5CT$|(Jo|PIm!y*P?(0VX;j9qU`OatAS8@v2>VxM6d98c;R^}y0;sg1xO9{J_VXY`AVA;^z@A`1lcMHSbYW=UrGTfz&5QPdiq@cxyB9AOa
zt*GBU8fw_gq6UfJQ#z+jJZZVKmgzK?;!mt@`N9fkQ+x{~{0`0XYd#0RqT0U9>$Z
z+Wo<^p&}-aZ&&w}f-q-H{j|Q%3%MsrH|oGFolbhP7z<-mJe`OXQE1)8bnr!sm3m
zv*QM=vcnJa$v7q~uE={t?GtVzn)8a@uuclz#%)S9W6J!>AR9OX8{DFvfBUjaX*i#%
zQDdz}vvUlYd{MY<;7Y>2*8JNX%|-H2DYf7x`6%j!1KfI*{oqKr>KLqw{*uV>5?}m%
z)`1lK3!sYEG_)WP7geZc2ANUiYpeX$;%|W<5d4f8bb&hizwN$1GT7JMFG$um)J-L)>B4WaUBs256-v9sr
literal 0
HcmV?d00001
diff --git a/survey_db_scheme.mwb b/formr_scheme.mwb.bak
similarity index 100%
rename from survey_db_scheme.mwb
rename to formr_scheme.mwb.bak
diff --git a/schema.sql b/schema.sql
index 6834c2c73..2a0a48eae 100644
--- a/schema.sql
+++ b/schema.sql
@@ -124,6 +124,7 @@ CREATE TABLE IF NOT EXISTS `survey_items` (
`type_options` VARCHAR(100) NULL DEFAULT NULL ,
`name` VARCHAR(100) NOT NULL ,
`label` TEXT NULL DEFAULT NULL ,
+ `label_parsed` TEXT NULL DEFAULT NULL ,
`optional` TINYINT NULL DEFAULT NULL ,
`class` VARCHAR(255) NULL DEFAULT NULL ,
`skipif` TEXT NULL DEFAULT NULL ,
@@ -497,7 +498,8 @@ CREATE TABLE IF NOT EXISTS `survey_item_choices` (
`study_id` INT UNSIGNED NOT NULL ,
`list_name` VARCHAR(20) NULL ,
`name` VARCHAR(20) NULL ,
- `label` TEXT NULL ,
+ `label` TEXT NULL DEFAULT NULL ,
+ `label_parsed` TEXT NULL DEFAULT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_survey_item_choices_survey_studies1_idx` (`study_id` ASC) ,
INDEX `listname` (`list_name` ASC) ,
diff --git a/survey_db_scheme.mwb.bak b/survey_db_scheme.mwb.bak
deleted file mode 100644
index 413f8804ed1f5e2e5284f6058498f700f4c67ef9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 37773
zcmYg$WlY