Forráskód Böngészése

Merge branch 'master' of bn.git:plabudda/se

Piotr Labudda 7 éve
szülő
commit
1335810a26
3 módosított fájl, 400 hozzáadás és 20 törlés
  1. 1 1
      SE/bash_sync_perms.php
  2. 133 0
      SE/se-lib/Benford.php
  3. 266 19
      SE/se-lib/Route/UrlAction/BiAuditGenerate.php

+ 1 - 1
SE/bash_sync_perms.php

@@ -545,7 +545,7 @@ class SyncPerms {
 		echo "DBG: UrlAction_WmsGenerate->doGenerate() END\n";
 
 		echo "DBG: UrlAction_BiAuditGenerate->doGenerate() START...\n";
-		//Router::getRoute('UrlAction_BiAuditGenerate')->doGenerate(); //musze przetestowac raport!
+		Router::getRoute('UrlAction_BiAuditGenerate')->doGenerate(); //musze przetestowac raport!
 		echo "DBG: UrlAction_BiAuditGenerate->doGenerate() END\n";
 	}
 

+ 133 - 0
SE/se-lib/Benford.php

@@ -0,0 +1,133 @@
+<?php
+
+class Benford {
+	private $array = null;
+	private $benford = null;
+
+	public function __construct($array = null) {
+		if ($array !== null) $this->addArray($array);
+	}
+
+	public function addArray($array = null) {
+		if ($array === null) throw new Exception("Missing parameter");
+		if (!is_array($array)) throw new Exception("Bad parameter - non array");
+		foreach ($array as $k => $v) $this->addItem($k, $v);
+	}
+
+	public function addItem($k = null, $v = null) {
+		if (!is_int($k)) throw new Exception("Bad key value - non integer ({$k})");
+		if ($k < 1) throw new Exception("Bad key value - less than 1 ({$k})");
+		if (isset($this->array[$k])) throw new Exception("Key value already exists ({$k})");
+		if (!is_numeric($v)) throw new Exception("Bad value - non numeric ({$v})");
+		$v = round(abs($v * 100));
+		if ($v == 0) return;
+		$this->array[$k] = $v;
+		$this->benford = null;
+	}
+
+	public static function benford($k = null) {
+		if ($k === null) throw new Exception("Missing parameter");
+		if (!is_int($k)) throw new Exception("Bad parameter - non integer ({$k})");
+		if ($k < 1) throw new Exceptrion("Bad parameter - less than 1 ({$k})");
+		return log10(1 + 1 / $k);
+	}
+
+	public function calculate($secondStage = true) {
+		return $this->calculateFirstStage($secondStage);
+	}
+
+	public function calculateFirstStage($secondStage = false) {
+		if ($this->array === null) return false;
+		$this->benford = [];
+
+		foreach ($this->array as $k => $v) {
+			$s = (string) $v;
+			$this->benford['firstStage'][$s[0]]['keys'][] = $k;
+		}
+
+		foreach ($this->benford['firstStage'] as $k => $v) {
+			$this->benford['firstStage'][$k]['count'] = count($v['keys']);
+			$this->benford['firstStage'][$k]['value'] = count($v['keys']) / count($this->array);
+			if ($this->benford['firstStage'][$k]['correct'] = ($this->benford['firstStage'][$k]['value'] <= self::benford($k))) {
+				unset($this->benford['firstStage'][$k]['keys']);
+			} elseif ($secondStage) {
+				$this->calculateSecondStage($k);
+			}
+		}
+		ksort($this->benford['firstStage']);
+
+		return true;
+	}
+
+	public function calculateSecondStage($a = null) {
+		if ($a === null) {
+			if (!isset($this->benford['firstStage'])) $this->calculate(false);
+			foreach ($this->benford['firstStage'] as $k => $v) {
+				if (!$v['correct']) $this->_calculateSecondStage($k);
+			}
+		} else $this->_calculateSecondStage($a);
+	}
+
+	private function _calculateSecondStage($a) {
+		if (!isset($this->benford['firstStage'][$a]['keys'])) throw new Exception("Wrong data ({$a})");
+		foreach ($this->benford['firstStage'][$a]['keys'] as $k) {
+			$s = (string) $this->array[$k];
+			if (strlen($s) < 2) continue;
+			$this->benford['secondStage'][$a][$s[1]]['keys'][] = $k;
+		}
+
+		foreach ($this->benford['secondStage'][$a] as $k => $v) {
+			$this->benford['secondStage'][$a][$k]['count'] = count($v['keys']);
+			$this->benford['secondStage'][$a][$k]['value'] = count($v['keys']) / count($this->array);
+			if ($this->benford['secondStage'][$a][$k]['correct'] = ($this->benford['secondStage'][$a][$k]['value'] <= self::benford($a * 10 + $k))) {
+				unset($this->benford['secondStage'][$a][$k]['keys']);
+			}
+		}
+
+		ksort($this->benford['secondStage'][$a]);
+	}
+
+	private function getResult($type = null, $k = null) {
+		if ($type === null) throw new Exception("Wrong usage");
+		if ($this->benford === null) $this->calculateFirstStage();
+		switch ($type) {
+			case 'all':
+			case 'secondStage':
+				if (!isset($this->benford['secondStage'])) $this->calculateSecondStage();
+			case 'firstStage':
+				if ($type == 'all') return $this->benford;
+				else return $this->benford[$type];
+			case 'secondStageItem':
+				if ($k === null) throw new Exception("Missing parameter");
+				if (!is_int($k)) throw new Exception("Bad parameter - not integer ({$k})");
+				if ($k < 1 || $k > 9) throw new Exception("Bad parameter - not between 1 and 9 ({$k})");
+				if (!isset($this->benford['secondStage'][$k])) throw new Exception("Wrong data");
+				return $this->benford['secondStage'][$k];
+			case 'badFirstStage':
+				return array_keys(array_filter($this->benford['firstStage'], function($v) {
+					if (!$v['correct']) return true;
+				}));
+			default: throw new Exception('Wrong usage');
+		}
+	}
+
+	public function getAllResult() {
+		return $this->getResult('all');
+	}
+
+	public function getFirstStage() {
+		return $this->getResult('firstStage');
+	}
+
+	public function getSecondStage() {
+		return $this->getResult('secondStage');
+	}
+
+	public function getSecondStageItem($k = null) {
+		return $this->getResult('secondStageItem', $k);
+	}
+
+	public function getBadFirstStage() {
+		return $this->getResult('badFirstStage');
+	}
+}

+ 266 - 19
SE/se-lib/Route/UrlAction/BiAuditGenerate.php

@@ -1103,6 +1103,258 @@ function validateCompany(source) {
 		}
 	}
 
+	private function benford_form($edit = false) {
+		if (!($field = V::get('_benfordField', '', $_GET))) throw new Exception ("Błąd konfiguracji UrlAction - brak parametru _benfordField (DANE )");
+		if (!($namespace = V::get('_fromNamespace', '', $_GET))) throw new Exception("Błąd formularza");
+
+		Lib::loadClass('FeatureAttrSelected');
+		$count = FeatureAttrSelected::getTotalSelected($namespace);
+		$selectedTable = FeatureAttrSelected::getAttributeTableName($namespace, User::getID());
+		$query = "select `t`.`ID`, `{$field}` from `{$this->SOURCE['TABLE']}` `t` join `{$selectedTable}` `s` on `t`.`ID` = `s`.`primaryKey` where `t`.`{$field}` != 0 order by `s`.`primaryKey`";
+		$values = [];
+		try {
+			$result = DB::getPDO()->fetchAll($query);
+			foreach ($result as $row) $values[$row['ID']] = $row[$field];
+		} catch (Exception $e) {
+			echo $e->getMessage();
+		}
+		if (!$values) throw new Exception("Nie wybrano żadnych obiektów lub wszystkie wybrane obiekty mają zerową wartość");
+
+		Lib::loadClass('Benford');
+		try {
+			$benfordObj = new Benford($values);
+			$benford = $benfordObj->getAllResult();
+		} catch (Exception $e) {
+			die($e->getMessage());
+		}
+?>
+<div class="container" style="margin-top:20px">
+  <form method="post">
+    <legend>
+      Analiza rozkładu Benford'a
+      <span class="pull-right">Tabela: <?=$this->SOURCE['TABLE']?></span>
+    </legend>
+    <div class="form-group">
+      <div class="col-sm-12">
+        <h5>Analiza na podstawie kolumny <?=$field?>. Liczba znalezionych obiektów z niezerową wartością: <?=count($values)?> (wybrano obiektów: <?=$count?>)</h5>
+      </div>
+    </div>
+<?php if($edit):?>
+    <div class="form-group">
+      <div class="col-sm-12">
+        <center>
+          <h3>Generowanie raportu analizy rozkładu Benford'a</h3>
+          <table>
+            <tr>
+              <td>
+                <select name="stage" class="form-control" onChange="toggleStage(this)">
+                  <option value="first" selected>Pierwszy stopień analizy</option>
+                  <option value="second">Drugi stopień analizy</option>
+                </select>
+              </td>
+              <td>&nbsp;&nbsp;</td>
+              <td><input type="text" class="form-control" name="L_APPOITMENT_INFO" title="Tytuł raportu" placeholder="Tytuł raportu" required/ size="50"></td>
+              <td>&nbsp;&nbsp;</td>
+              <td><button type="submit" class="btn btn-primary" name="action" value="generate" style="width: 80px;">Generuj</button></td>
+            </tr>
+          </table>
+        </center>
+        <br/><br/>
+      </div>
+    </div>
+<?php endif;?>
+    <div class="form-group">
+      <div class="col-sm-offset-2 col-sm-8">
+        <table class="table table-bordered table-hover table-striped table-condensed">
+          <thead style="text-align:center; font-weight:bold; background-color:lightgray">
+            <tr>
+              <td style="vertical-align:middle;" rowspan="2">Pierwsze cyfry</td>
+              <td style="vertical-align:middle;" rowspan="2">Stopień analizy</td>
+              <td style="vertical-align:middle;" rowspan="2">Liczba wystąpień</td>
+              <td style="vertical-align:middle;" rowspan="2">Procent występowania</td>
+              <td colspan="3">Rozkład Benford'a</td>
+<?php if($edit):?>
+              <td style="vertical-align:middle;" rowspan="2">Załącz obiekty<br/>do&nbsp;raportu</td>
+<?php endif;?>
+            </tr>
+            <tr>
+              <td nowrap>Norma (%)</td>
+              <td nowrap>Odchylenie od&nbsp;normy (%)</td>
+              <td>Zgodność</td>
+            </tr>
+          </thead>
+          <tbody>
+<?php
+	$number_format = function($n) {
+		return number_format($n, 3, ',', '');
+	};
+
+	for ($ka = 1; $ka < 10; $ka++) {
+		if (!isset($benford['firstStage'][$ka])) $benford['firstStage'][$ka] = ['count' => 0, 'value' => 0, 'correct' => true];
+		$va = $benford['firstStage'][$ka];
+		$odchylenie = round(($va['value'] / Benford::benford($ka) - 1) * 100, 3);
+		if ($odchylenie > 0) $odchylenie = "+" . $number_format($odchylenie);
+		else $odchylenie = $number_format($odchylenie);
+?>
+            <tr style="text-align:center; font-weight:bold;" class="tr-stage1-<?=$va['correct'] ? 'green' : 'red" onClick="toggleSecondStage(' . $ka . ');'?>">
+              <td style="text-align:left"><?=$ka?></td>
+              <td>I</td>
+              <td style="text-align:right"><?=$va['count']?></td>
+              <td style="text-align:right"><?=$number_format(round($va['value'] * 100, 3))?></td>
+              <td style="text-align:right"><?=$number_format(round(Benford::benford($ka) * 100, 3))?></td>
+              <td style="text-align:right"><?=$odchylenie?></td>
+              <td<?=$va['correct'] ? '' : ' name="firstStageDesc" data-ka="' . $ka . '"'?> nowrap><?=$va['correct'] ? 'Tak' : 'Nie (rozwiń)'?></td>
+<?php if($edit):?>
+              <td><input type="checkbox" <?=$va['correct'] ? 'disabled' : 'checked name="checkFirstStage[]" value="' . $ka . '" onClick="toggleSecondStage(' . $ka . '); toggleCheckFirst(this)"'?>></td>
+<?php endif;?>
+            </tr>
+<?php
+		if (!$va['correct']) {
+			for ($kb = 0; $kb < 10; $kb++) {
+				if (!isset($benford['secondStage'][$ka][$kb])) $benford['secondStage'][$ka][$kb] = ['count' => 0, 'value' => 0, 'correct' => true];
+				$vb = $benford['secondStage'][$ka][$kb];
+				$odchylenie = round(($vb['value'] / Benford::benford($ka * 10 + $kb) - 1) * 100, 3);
+				if ($odchylenie > 0) $odchylenie = "+" . $number_format($odchylenie);
+				else $odchylenie = $number_format($odchylenie);
+?>
+            <tr style="text-align:center;" class="tr-stage2-<?=$vb['correct'] ? 'green' : 'red'?>" name="secondStage" data-ka="<?=$ka?>" hidden>
+              <td style="text-align:left"><?=$ka . $kb?></td>
+              <td>II</td>
+              <td style="text-align:right"><?=$vb['count']?></td>
+              <td style="text-align:right"><?=$number_format(round($vb['value'] * 100, 3))?></td>
+              <td style="text-align:right"><?=$number_format(round(Benford::benford($ka * 10 + $kb) * 100, 3))?></td>
+              <td style="text-align:right"><?=$odchylenie?></td>
+              <td><?=$vb['correct'] ? 'Tak' : 'Nie'?></td>
+<?php if($edit):?>
+              <td onClick=""><input type="checkbox"<?=$vb['correct'] ?: ' checked name="checkSecondStage[]" value="' . $ka . $kb . '" onClick="toggleCheckSecond(this)"'?> disabled></td>
+<?php endif;?>
+            </tr>
+<?php
+			}
+		}
+	}
+?>
+          </tbody>
+        </table>
+      </div>
+    </div>
+  </form>
+</div>
+<style type="text/css">
+<!--
+.tr-stage1-green,.tr-stage1-red {font-weight:bold;}
+.tr-stage1-green,.tr-stage2-green {color:#008800;}
+.tr-stage1-red,.tr-stage2-red {color:#ff0000;}
+body { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}
+::selection {background: transparent;}
+-->
+</style>
+<script language="JavaScript">
+<!--
+function toggleSecondStage(ka) {
+  var rows = document.getElementsByName('secondStage');
+  var hidden = false;
+  for(var i = 0, n = rows.length; i < n; i++) {
+    if (rows[i].dataset.ka == ka) {
+      rows[i].hidden = !rows[i].hidden;
+      hidden = rows[i].hidden;
+    }
+  }
+  var descs = document.getElementsByName('firstStageDesc');
+  for(var i = 0, n = descs.length; i < n; i++) {
+    if (descs[i].dataset.ka == ka) {
+      descs[i].innerHTML = (hidden ? 'Nie (rozwiń)' : 'Nie (zwiń)');
+    }
+  }
+}
+function toggleStage(option) {
+  if (option.value == 'first') first = true;
+  else if (option.value == 'second') first = false;
+  else {
+    alert('Wystąpił nieznany błąd formularza');
+    return;
+  }
+  var checks = document.getElementsByName('checkSecondStage[]');
+  var all = [];
+  for(var i = 0, n = checks.length; i < n; i++) {
+    checks[i].disabled = first;
+    ka = checks[i].value / 10 | 0;
+    if (all[ka] === 'undefined') all[ka] = checks[i].checked;
+    else if (checks[i].checked) all[ka] = true;
+  }
+  var checks = document.getElementsByName('checkFirstStage[]');
+  for(var i = 0, n = checks.length; i < n; i++) {
+    checks[i].disabled = !(first);
+    if (checks[i].checked != all[checks[i].value]) {
+      checks[i].checked = all[checks[i].value];
+      toggleCheckFirst(checks[i]);
+    }
+  }
+  var descs = document.getElementsByName('firstStageDesc');
+  for(var i = 0, n = descs.length; i < n; i++) {
+    descs[i].innerHTML = (first ? 'Nie (rozwiń)' : 'Nie (zwiń)');
+  }
+  var rows = document.getElementsByName('secondStage');
+  for(var i = 0, n = rows.length; i < n; i++) {
+    rows[i].hidden = first;
+  }
+}
+function toggleCheckFirst(check) {
+  var checks = document.getElementsByName('checkSecondStage[]');
+  for(var i = 0, n = checks.length; i < n; i++) {
+    if ((checks[i].value / 10 | 0) == check.value) checks[i].checked = check.checked;
+  }
+}
+function toggleCheckSecond(check) {
+  var checks = document.getElementsByName('checkSecondStage[]');
+  var all = true;
+  var ka = check.value / 10 | 0;
+  for(var i = 0, n = checks.length; i < n; i++) {
+    if ((checks[i].value / 10 | 0) == ka) {
+      if (!checks[i].checked) {
+        all = false;
+        break;
+      }
+    }
+  }
+  var checks = document.getElementsByName('checkFirstStage[]');
+  for(var i = 0, n = checks.length; i < n; i++) {
+    if (checks[i].value == ka) {
+      checks[i].checked = all;
+      break;
+    }
+  }
+}
+-->
+</script>
+<?php
+	}
+
+	private function benford_generate() {
+		SE_Layout::alert('warning', 'Not implemented yet');
+?>
+<br><br>
+<div style="text-align:center">
+  <a href="<?=$this->REFERER?>" class="btn btn-default" style="width: 80px;">Powrót</a>
+</div>
+<?php
+	}
+
+	private function benford() {
+		try {
+			$action = V::get('action', '', $_POST);
+			switch ($action) {
+				case "generate":
+					$this->benford_generate();
+					break;
+				default:
+					$this->benford_form(true);
+			}
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', $e->getMessage());
+		}
+	}
+
 	public function defaultAction() {
 		SE_Layout::gora();
 		SE_Layout::menu();
@@ -1121,11 +1373,10 @@ function validateCompany(source) {
 			$this->powiazania();
 		}
 		elseif ($TABLE = V::get('_fromNamespace', '', $_GET)) {
+			$this->SOURCE['TABLE'] = ACL::getAclByTypeName($TABLE)->getRootTableName();
 			$_subUrlAction = V::get('_subUrlAction', '', $_GET);
-			switch ($TABLE) {
-				case "default_db/BI_audit_ENERGA_PRACOWNICY":
-				case "default_db/BI_audit_ENERGA_PRACOWNICY/BI_audit_ENERGA_PRACOWNICY":
-					$this->SOURCE['TABLE'] = 'BI_audit_ENERGA_PRACOWNICY';
+			switch ($this->SOURCE['TABLE']) {
+				case "BI_audit_ENERGA_PRACOWNICY":
 					switch ($_subUrlAction) {
 						case "clearTable":
 							$this->clearTable();
@@ -1137,9 +1388,7 @@ function validateCompany(source) {
 						default: SE_Layout::alert('danger', 'Błąd parametru #21');
 					}
 					break;
-				case "default_db/BI_audit_ENERGA_PRACOWNICY_import":
-				case "default_db/BI_audit_ENERGA_PRACOWNICY_import/BI_audit_ENERGA_PRACOWNICY_import":
-					$this->SOURCE['TABLE'] = 'BI_audit_ENERGA_PRACOWNICY_import';
+				case "BI_audit_ENERGA_PRACOWNICY_import":
 					switch ($_subUrlAction) {
 						case "clearTable":
 							$this->clearTable();
@@ -1147,9 +1396,7 @@ function validateCompany(source) {
 						default: SE_Layout::alert('danger', 'Błąd parametru #22');
 					}
 					break;
-				case "default_db/BI_audit_ENERGA_RUM_KONTRAHENCI":
-				case "default_db/BI_audit_ENERGA_RUM_KONTRAHENCI/BI_audit_ENERGA_RUM_KONTRAHENCI":
-					$this->SOURCE['TABLE'] = 'BI_audit_ENERGA_RUM_KONTRAHENCI';
+				case "BI_audit_ENERGA_RUM_KONTRAHENCI":
 					switch ($_subUrlAction) {
 						case "clearTable":
 							$this->clearTable();
@@ -1157,29 +1404,29 @@ function validateCompany(source) {
 						default: SE_Layout::alert('danger', 'Błąd parametru #23');
 					}
 					break;
-				case "default_db/BI_audit_ENERGA_RUM_UMOWY":
-				case "default_db/BI_audit_ENERGA_RUM_UMOWY/BI_audit_ENERGA_RUM_UMOWY":
-					$this->SOURCE['TABLE'] = 'BI_audit_ENERGA_RUM_UMOWY';
+				case "BI_audit_ENERGA_RUM_UMOWY":
 					switch ($_subUrlAction) {
 						case "clearTable":
 							$this->clearTable();
 							break;
+						case "benford":
+							$this->benford();
+							break;
 						default: SE_Layout::alert('danger', 'Błąd parametru #24');
 					}
 					break;
-				case "default_db/BI_audit_ENERGA_FAKTURY":
-				case "default_db/BI_audit_ENERGA_FAKTURY/BI_audit_ENERGA_FAKTURY":
-					$this->SOURCE['TABLE'] = 'BI_audit_ENERGA_FAKTURY';
+				case "BI_audit_ENERGA_FAKTURY":
 					switch ($_subUrlAction) {
 						case "clearTable":
 							$this->clearTable();
 							break;
+						case "benford":
+							$this->benford();
+							break;
 						default: SE_Layout::alert('danger', 'Błąd parametru #25');
 					}
 					break;
-				case "default_db/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA":
-				case "default_db/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA":
-					$this->SOURCE['TABLE'] = 'BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA';
+				case "BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA":
 					switch ($_subUrlAction) {
 						case "clearTable":
 							$this->clearTable();