1KA_F2F/admin/survey/classes/surveyData/class.SurveyDataFile.php
2020-08-14 15:08:59 +02:00

540 lines
19 KiB
PHP

<?php
/**
*
* Created on 17.12.2019
*
* @author: Peter Hrvatin
*
* Class za upravljanje z datotekami za podatke (preveri status datoteke, zaklepanje datoteke, klice kreiranje, updatanje...)
*
*/
class SurveyDataFile {
private static $instance;
private $anketa;
private $checked;
private $header_file_name; // Ime header datoteke
private $data_file_name; // Ime datoteke s podatki
private $head_file_time = 0; // Cas zadnjega kreiranja header datoteke
private $data_file_time = 0; // Cas zadnjega kreiranja datoteke s podatki
private $last_update = 0; // Cas zadnjega kreiranja datoteke
private $collect_all_status = 1; // Ali zbiramo vse ali samo ustrezne
private $file_status = FILE_STATUS_NO_DATA; // Status datoteke
private $has_test_data = false; // Ali vnosi vsebujejo testne podatke
private $last_response_time = null; // Ali vnosi vsebujejo testne podatke
private $all_user_cnt = null; // Stevilo vseh responsov
private $HEADER = NULL;
public static function get_instance() {
if(!is_object(self::$instance)) {
self::$instance = new SurveyDataFile();
}
return self::$instance;
}
public function init($anketa) {
global $site_path;
// Nastavimo id ankete
$this->anketa = $anketa;
// Nastavimo folder kjer se nahajajo datoteke
$folder = $site_path . EXPORT_FOLDER.'/';
SurveySetting::getInstance()->Init($this->anketa);
// Nastavimo imena datotek
$this->header_file_name = $folder . 'export_header_'.$this->anketa.'.dat';
$this->data_file_name = $folder . 'export_data_'.$this->anketa.'.dat';
// Vedno ob inicializaciji preverimo status datoteke
if($this->checked == false) {
$this->checkFile();
}
}
/**** GETTERJI ****/
// Vrnemo status datoteke s podatki
public function getStatus() {
return $this->file_status;
}
// Vrnemo cas zadnjega updata datoteke
public function getFileUpdated() {
return $this->last_update;
}
// Vrnemo ime header datoteke
public function getHeaderFileName() {
return $this->header_file_name;
}
// Vrnemo ime datoteke s podatki
public function getDataFileName() {
return $this->data_file_name;
}
// Vrnemo cas kreiranja datoteke s podatki
public function getDataFileTime() {
return $this->data_file_time;
}
// Vrnemo status kreiranja (ce pridobivamo vse ali samo ustrezne)
public function getCollectAllStatus() {
return $this->collect_all_status;
}
// Vrnemo status kreiranja (ce pridobivamo vse ali samo ustrezne)
public function getHasTestData() {
return $this->has_test_data;
}
// Vrnemo cas zadnjega responsa
public function getLastResponseTime() {
return $this->last_response_time;
}
// Vrnemo header datoteko
public function getHeader() {
if (!isset($this->HEADER) || $this->HEADER == null) {
if ($this->header_file_name != null && file_exists($this->header_file_name)) {
$this->HEADER = unserialize(file_get_contents($this->header_file_name));
}
}
return $this->HEADER;
}
public function getHeaderVariable($variable) {
$this->getHeader();
return isset($this->HEADER[$variable]) ? $this->HEADER[$variable] : null;
}
public function getSurveyVariables($filterTip = NULL) {
$result = array();
$this->getHeader();
if (is_countable($this->HEADER) && count($this->HEADER) > 0) {
foreach ($this->HEADER AS $_vkey => $variable) {
# dodamo samo tiste variable, ki imajo numerični tip (navadne spremenljvke)
if (is_numeric($variable['tip'] )
# ne dodamo sistemskih email, telefon, ime, priimek, naziv
&& !( (int)$variable['hide_system'] == 1
&& in_array($variable['variable'],unserialize (SYSTEM_VARIABLES)) )# unserialize (SYSTEM_VARIABLES) -> definition.php = array('email','telefon','ime','priimek','naziv','drugo')
) {
# če filter ni setiran dodamo vse variable
if ( $filterTip == NULL ) {
$result[$_vkey] = $variable['sequences'];
}
else if ( is_array($filterTip) && in_array($variable['tip'],$filterTip) ) {
$result[$_vkey] = $variable['sequences'];
}
else if ( is_string($filterTip) && (string)$variable['tip'] == (string)$filterTip ) {
$result[$_vkey] = $variable['sequences'];
}
else if ( is_numeric($filterTip) && (int)$variable['tip'] == (int)$filterTip ) {
$result[$_vkey] = $variable['sequences'];
}
}
}
}
return $result;
}
public function getVariableName($variable) {
$result = '';
$this->getHeader();
if (isset($this->HEADER[$variable])) {
$result = '(' . $this->HEADER[$variable]['variable'] .') - '.$this->HEADER[$variable]['naslov'];
}
return $result;
}
/**** GETTERJI - KONEC ****/
// Preverimo status datotek s podatki
public function checkFile () {
// Najprej nastavimo status ni podatkov
$this->file_status = FILE_STATUS_NO_DATA;
// Preverimo ce ima anketa kaj responsov - če je respondentov več kot ONLY_VALID_LIMIT (5000) lovimo samo ustrezne
$sql_cnt_user = sisplet_query("SELECT count(*) FROM srv_user AS u WHERE u.ank_id = '".$this->anketa."' AND u.preview='0' AND u.deleted='0'");
list($this->all_user_cnt) = mysqli_fetch_row($sql_cnt_user);
// Imamo response
if((int)$this->all_user_cnt > 0) {
// Nastavimo zapise v bazi za datoteko
$sql = sisplet_query("SELECT UNIX_TIMESTAMP(head_file_time), UNIX_TIMESTAMP(data_file_time), DATE_FORMAT(last_update,'%d.%m.%Y %H:%i:%s'), collect_all_status
FROM srv_data_files
WHERE sid='".$this->anketa."'");
if(mysqli_num_rows($sql) > 0) {
list($this->head_file_time, $this->data_file_time, $this->last_update, $this->collect_all_status) = mysqli_fetch_row($sql);
}
// Ce je collect_all_status = 2 je admin nastavil da se lovijo vsi statusi, popravimo tudi čas, da se datoteka zgenerira na novo
if ((int)$this->all_user_cnt > ONLY_VALID_LIMIT && (int)$this->collect_all_status == 1) {
$this->collect_all_status = 0;
$this->data_file_time = 0;
$updated = sisplet_query("INSERT INTO srv_data_files (sid, collect_all_status, data_file_time)
VALUES ('".$this->anketa."','".(int)$this->collect_all_status."', '0000-00-00')
ON DUPLICATE KEY UPDATE collect_all_status = '".(int)$this->collect_all_status."', data_file_time = '0000-00-00'");
}
// Nastavimo, ce imamo testne vnose
$_qry_cnt_testdata = sisplet_query("SELECT count(*)
FROM srv_user AS u
WHERE u.ank_id = '".$this->anketa."' AND preview='0' AND (u.testdata = '1' OR u.testdata = '2') AND u.deleted = '0'");
list($this->has_test_data) = mysqli_fetch_row($_qry_cnt_testdata);
// Preverimo ce imamo usability stolpec v header datoteki ali ce imamo na novo testne podatke - potem pobrisemo vse datoteke, ker moramo vse generirati na novo
if($this->checkUsability() || $this->checkTestData()){
$this->clearFiles();
$this->file_status = FILE_STATUS_NO_FILE;
}
else{
// Datoteka obstaja
if(file_exists($this->getHeaderFileName()) && file_exists($this->data_file_name)) {
// Preverimo ce je datoteka up2date
$this->file_status = $this->isFileUp2Date();
}
// Ni datoteke
else {
$this->file_status = FILE_STATUS_NO_FILE;
}
}
}
// Konmcali smo preverjanje statusa datotek
$this->checked = true;
return $this->file_status;
}
// Preverimo ce je datoteka s podatki up to date
private function isFileUp2Date() {
// Cas zadnjega editiranja ankete (vse razen active=-1 - pobrisana)
$sql_survey = sisplet_query("SELECT active, UNIX_TIMESTAMP(edit_time) AS srv_edit_time FROM srv_anketa WHERE id='".$this->anketa."'");
list($anketa_active, $anketa_edit_time) = mysqli_fetch_row($sql_survey);
// Ce je anketa slucajno pobrisana nastavimo status na pobrisano
if($anketa_active != '0' && $anketa_active != '1'){
return FILE_STATUS_SRV_DELETED;
}
// Ce se cas zadnjega urejanja ankete ne ujema s tistim v bazi, moram vse kreirati na novo
if((int)$anketa_edit_time != (int)$this->head_file_time || (int)$anketa_edit_time <= 0) {
$this->clearFiles();
return FILE_STATUS_NO_FILE;
}
// Polovimo datum zadnjega respondenta
$this->setLastResponseTime();
// Preberemo max time iz datoteke s podatki in uporabimo manjšega
if(IS_WINDOWS) {
$command = 'awk -F"|" "BEGIN {max = 0} {if ($6 > max && $6 > 0) max=$6 } END {print max}" '.$this->data_file_name;
}
else {
$command = 'awk -F\'|\' \'BEGIN {max = 0} {if ($6 > max && $6 > 0) max=$6 } END {print max}\' '.$this->data_file_name;
}
$response_time_from_file = shell_exec($command);
if((int)$response_time_from_file > 0) {
$data_file_time = min((int)$this->data_file_time, (int)$response_time_from_file);
}
else{
$data_file_time = $this->data_file_time;
}
// Ce se cas dejanskega zadnjega responsa in cas zadnjega responsa v datoteki ne ujemata je datoteka zastarela
if((int)$this->last_response_time != (int)$data_file_time || (int)$this->last_response_time <= 0) {
return FILE_STATUS_OLD;
}
return FILE_STATUS_OK;
}
// Preverimo ce imamo vklopljen filter na uporabnost in ce imamo v headerju zapisan stolpec za usability (drugace moramo generirati datoteko na novo)
private function checkUsability(){
// Preverimo ce imamo vklopljen filter na usability
if(SurveyStatusProfiles::usabilitySettings()){
// Preverimo ce imamo usability stolpec v header datoteki
$usability = $this->getHeaderVariable('usability');
if($usability == null){
return true;
}
}
return false;
}
// Preverimo ce imamo testne podatke na novo -> potem moramo generirati datoteko na novo
private function checkTestData(){
// Preverimo ce prej nismo imeli testnih podatkov
$settings = $this->getHeaderVariable('_settings');
if($settings['hasTestData'] != 1 && $this->has_test_data > 0){
return true;
}
return false;
}
// Vrnemo text z info o datoteki
public function getDataFileInfo() {
global $admin_type, $lang;
$result = null;
if ($admin_type == '0') {
// Delamo inkremental
$inkremental_user_limit = '';
if ($this->file_status == FILE_STATUS_OLD) {
$inkremental_user_limit = " AND u.time_edit > FROM_UNIXTIME('".(int)$this->data_file_time."') ";
}
// Zbiramo samo ustrezne
$is_valid_user_limit = '';
if ((int)$this->collect_all_status == 0) {
$is_valid_user_limit = " AND u.last_status IN (5,6) ";
}
$qry_usr_cnt = sisplet_query("SELECT count(u.id)
FROM srv_user AS u
WHERE u.ank_id = '".$this->anketa."' AND u.preview='0' AND u.deleted='0' " . $is_valid_user_limit . $inkremental_user_limit);
list($user_cnt) = mysqli_fetch_row($qry_usr_cnt);
// Preverimo ce imamo izklopljeno belezenje parapodatka datum - potem moramo vedno generirati datoteko
$no_paradata_date = SurveySetting::getInstance()->getSurveyMiscSetting('survey_date');
$result = '&nbsp;&nbsp;<span class="leftSpace">';
if ($this->file_status == FILE_STATUS_NO_DATA) {
$result .= $lang['srv_dashboard_no_data'];
}
else if ($this->file_status == FILE_STATUS_NO_FILE) {
$result .= $lang['srv_dashboard_no_file'].$lang['srv_dashboard_new_units'].(int)$user_cnt;
}
else if ($this->file_status == FILE_STATUS_OLD && $no_paradata_date == 0) {
$result .= $lang['srv_dashboard_not_up_to_date'].'('.$this->last_update.')';
$result .= $lang['srv_dashboard_new_units'].(int) $user_cnt;
}
else if ($this->file_status == FILE_STATUS_OLD && $no_paradata_date == 1) {
$result .= $lang['srv_dashboard_up_to_date'].' ('.$this->last_update.')';
}
else if ($this->file_status == FILE_STATUS_OK) {
$result .= $lang['srv_dashboard_up_to_date'].' ('.$this->last_update.')';
}
$result .= '</span>';
}
return $result;
}
// Preverimo v bazi in nastavimo cas zadnjega respondenta
public function setLastResponseTime() {
global $admin_type, $lang;
// Zbiramo samo ustrezne
$is_valid_user_limit = '';
if ((int)$this->collect_all_status == 0) {
$is_valid_user_limit = " AND u.last_status IN (5,6) ";
}
// Polovimo datum zadnjega respondenta
$sql_usr_time = sisplet_query("SELECT max(UNIX_TIMESTAMP(GREATEST(u.time_insert,u.time_edit)))
FROM srv_user AS u
WHERE u.ank_id='".$this->anketa."' AND u.preview='0' AND u.deleted='0' ".$is_valid_user_limit."");
list($this->last_response_time) = mysqli_fetch_row($sql_usr_time);
// Ce ne belezimo parapodatka datum responsa moramo vedno generirati na novo - nastavimo, da je vedno zadnji response zdaj
$no_paradata_date = SurveySetting::getInstance()->getSurveyMiscSetting('survey_date');
if($no_paradata_date == 1)
$this->last_response_time = time();
return $this->last_response_time;
}
// Pripravimo datoteke s podatki
public function prepareFiles($show_loading=false) {
global $lang;
// File status ok, no data ali deleted survey - ne rabimo generirati ničesar
if(in_array($this->file_status, array(FILE_STATUS_OK, FILE_STATUS_NO_DATA, FILE_STATUS_SRV_DELETED))){
return $this->file_status;
}
// Ce imamo to vklopljeno in imamo vec kot 300 responsov prikazemo popup za loading in z ajaxem poklicemo generiranje
if($show_loading && $this->all_user_cnt > 300 && !isDebug()){
// Popup div z loading obvestilom
echo '<div id="collect_data_popup" class="divPopUp"><span class="faicon spinner fa-spin spaceRight"></span> '.$lang['srv_collectdata_in_progress'].' ('.$this->all_user_cnt.'). '.$lang['srv_collectdata_in_progress2'].'</div>';
echo '<script>
collectDataAjax();
</script>';
return $this->file_status;
}
// Najprej poskusimo zakleniti anekto za kreiranje
$lock = new ExclusiveLock( "survey_lock_".$this->anketa);
$locked_success = $lock->lock();
// Ce je nismo ustrezno zaklenili ne moremo poklicati generiranje datotek
if(!$locked_success){
return $this->file_status;
}
// Ce smo jo ustrezno zaklenili lahko poklicemo generiranje datotek
$collectData = new SurveyDataCollect($this->anketa);
// File status old - inkrementalno dodamo nove response
if($this->file_status == FILE_STATUS_OLD){
$sdc = new SurveyDataCollect($this->anketa);
$createdNewFile = (int)$sdc->updateFilesIncremental();
}
// Drugace generiramo vse na novo
else{
$sdc = new SurveyDataCollect($this->anketa);
$createdNewFile = (int)$sdc->createFiles();
}
// Na koncu se enkrat preverimo in nastavimo stanje datotek
$this->checkFile();
// Anketo odklenemo
$lock->unlock();
// Vrnemo zadnji status datotek
return $this->file_status;
}
// Popolnoma pobrisemo datoteke s podatki in ponastavimo vrednosti generiranja v bazi
public function clearFiles() {
// Najprej pobrisemo vse datoteke
$this->deleteFile();
// Pobrisemo vrstico za datoteke v bazi
$sql = sisplet_query("DELETE FROM srv_data_files WHERE sid='".$this->anketa."'");
// Pobrisemo lock ce ga imamo na datoteki
$key = 'survey_lock_'.$this->anketa;
$sqlUnlock = sisplet_query("DELETE FROM srv_lock WHERE lock_key='".$key."'");
// Na koncu ponastavimo statuse
$this->file_status = FILE_STATUS_NO_FILE;
$this->last_update = 0;
}
// Pobrisemo datoteke s podatki
public function deleteFile($filename=null) {
global $site_path;
// Nastavimo folder kjer se nahajajo datoteke
$folder = $site_path . EXPORT_FOLDER.'/';
// Brišemo specifično datoteko
if ($filename !== null && trim($filename) != '') {
if (file_exists($filename)) {
unlink($filename);
}
}
// Brisemo vse datoteke za doloceno anketo
else {
// Pobrišemo header datoteke
if (file_exists($folder . 'export_header_'.$this->anketa.'.dat')) {
unlink($folder . 'export_header_'.$this->anketa.'.dat');
}
// Pobrišemo data datoteko
if (file_exists($folder . 'export_data_'.$this->anketa.'.dat')) {
unlink($folder . 'export_data_'.$this->anketa.'.dat');
}
// Pobrišemo tmp file
if (file_exists($folder . 'export_data_'.$this->anketa.'.tmp')) {
unlink($folder . 'export_data_'.$this->anketa.'.tmp');
}
if (file_exists($folder . 'export_data_'.$this->anketa.'.dat_data1.tmp')) {
unlink($folder . 'export_data_'.$this->anketa.'.dat_data1.tmp');
}
if (file_exists($folder . 'export_data_'.$this->anketa.'.dat_data1_1.tmp')) {
unlink($folder . 'export_data_'.$this->anketa.'.dat_data1_1.tmp');
}
// Pobrisemo morebitne SN datoteke - header
$files = glob($folder.'export_sn_header_'.$this->anketa.'_*.dat');
if(count($files ) > 0) {
foreach ($files AS $file) {
unlink($file);
}
}
// Pobrisemo morebitne SN datoteke - data
$files = glob($folder.'export_sn_data_'.$this->anketa.'_*.dat');
if(count($files ) > 0) {
foreach ($files AS $file) {
unlink($file);
}
}
}
}
// Ajax klici
public function ajax() {
// Poklicemo generiranje datoteke s podatki
if($_GET['a'] == 'prepareFiles') {
$file_status = $this->prepareFiles($show_loading = false);
echo $file_status;
}
}
}