index.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <?php
  2. if (cfr('COVID')) {
  3. $altCfg = $ubillingConfig->getAlter();
  4. if (@$altCfg['COVID19_ENABLED']) {
  5. class Covid19 {
  6. /**
  7. * Contains system alter config as key=>value
  8. *
  9. * @var array
  10. */
  11. protected $altCfg = array();
  12. /**
  13. * System caching object placeholder
  14. *
  15. * @var object
  16. */
  17. protected $cache = '';
  18. /**
  19. * Contains system remote URL abstraction instance
  20. *
  21. * @var object
  22. */
  23. protected $omaeUrl = '';
  24. /**
  25. * Contains system message helper
  26. *
  27. * @var object
  28. */
  29. protected $messages = '';
  30. /**
  31. * Contains default country name to display
  32. *
  33. * @var string
  34. */
  35. protected $country = 'Ukraine';
  36. /**
  37. * Contains raw data array from cache or remote data source
  38. *
  39. * @var array
  40. */
  41. protected $rawData = array();
  42. /**
  43. * Default raw data caching timeout in seconds
  44. */
  45. const CACHE_TIMEOUT = 21600;
  46. /**
  47. * Default data source URL
  48. */
  49. const DATA_SOURCE = 'http://ubilling.net.ua/covid19/';
  50. /**
  51. * Default module route
  52. */
  53. const URL_ME = '?module=covid19';
  54. /**
  55. * Charts coloring
  56. */
  57. const COLOR_CONFIRMED = 'f68500';
  58. const COLOR_DEATHS = 'd20009';
  59. const COLOR_RECOVERED = '009b04';
  60. /**
  61. * Creates new instance of COVID-19 :P
  62. */
  63. public function __construct() {
  64. $this->initMessages();
  65. $this->loadAlter();
  66. $this->initCache();
  67. $this->initOmae();
  68. $this->loadData();
  69. }
  70. /**
  71. * Loads alter config into protected prop for further usage
  72. *
  73. * @global object $ubillingConfig
  74. *
  75. * @return void
  76. */
  77. protected function loadAlter() {
  78. global $ubillingConfig;
  79. $this->altCfg = $ubillingConfig->getAlter();
  80. }
  81. /**
  82. * Inits message helper instance
  83. *
  84. * @return void
  85. */
  86. protected function initMessages() {
  87. $this->messages = new UbillingMessageHelper();
  88. }
  89. /**
  90. * Inits system caching engine
  91. *
  92. * @return void
  93. */
  94. protected function initCache() {
  95. $cacheEngine = $this->altCfg['UBCACHE_STORAGE'];
  96. // force files storage due big data size and preventing cache keys misses.
  97. if ($cacheEngine == 'memcached') {
  98. $cacheEngine = 'files';
  99. }
  100. $this->cache = new UbillingCache($cacheEngine);
  101. }
  102. /**
  103. * Inits remote URL abstraction layer
  104. *
  105. * @return void
  106. */
  107. protected function initOmae() {
  108. $this->omaeUrl = new OmaeUrl();
  109. $this->omaeUrl->setUserAgent('Ubilling COVID-19 Info');
  110. }
  111. /**
  112. * Loads data from cache or from remote data source
  113. *
  114. * @return void
  115. */
  116. protected function loadData() {
  117. $this->rawData = $this->cache->get('COVID19', self::CACHE_TIMEOUT);
  118. if (empty($this->rawData)) {
  119. $remoteData = $this->omaeUrl->response(self::DATA_SOURCE);
  120. if (!empty($remoteData)) {
  121. $this->rawData = json_decode($remoteData, true);
  122. $this->cache->set('COVID19', $this->rawData, self::CACHE_TIMEOUT);
  123. }
  124. }
  125. }
  126. /**
  127. * Returns default chart options
  128. *
  129. * @return string
  130. */
  131. protected function getChartOptions() {
  132. $result = "'focusTarget': 'category',
  133. 'hAxis': {
  134. 'color': 'none',
  135. 'baselineColor': 'none',
  136. },
  137. 'vAxis': {
  138. 'color': 'none',
  139. 'baselineColor': 'none',
  140. },
  141. 'curveType': 'function',
  142. 'pointSize': 5,
  143. 'crosshair': {
  144. trigger: 'none'
  145. },
  146. series: {
  147. 0: { color: '#" . self::COLOR_CONFIRMED . "' },
  148. 1: { color: '#" . self::COLOR_DEATHS . "' },
  149. 2: { color: '#" . self::COLOR_RECOVERED . "' },
  150. },
  151. ";
  152. return($result);
  153. }
  154. /**
  155. * Returns country selection form
  156. *
  157. * @return string
  158. */
  159. protected function countrySelector() {
  160. $result = '';
  161. //country preset override
  162. if (ubRouting::checkPost('showcountry')) {
  163. $this->country = ubRouting::post('showcountry');
  164. }
  165. $dataTmp = array();
  166. if (!empty($this->rawData)) {
  167. foreach ($this->rawData as $eachCountry => $someData) {
  168. $dataTmp[$eachCountry] = $eachCountry;
  169. }
  170. $inputs = wf_Selector('showcountry', $dataTmp, __('Country'), $this->country, false) . ' ';
  171. $inputs .= wf_Submit(__('Show'));
  172. $result .= wf_Form('', 'POST', $inputs, 'glamour');
  173. }
  174. return($result);
  175. }
  176. /**
  177. * Renders COVID-19 causes report by some country
  178. *
  179. * @return string
  180. */
  181. public function renderCountry() {
  182. $result = '';
  183. $curMonthCount = 0;
  184. if (!empty($this->rawData)) {
  185. if (isset($this->rawData[$this->altCfg['COVID19_ENABLED']])) {
  186. //valid country name
  187. $this->country = $this->altCfg['COVID19_ENABLED'];
  188. }
  189. $result .= $this->countrySelector();
  190. if (isset($this->rawData[$this->country])) {
  191. /**
  192. * Struct:
  193. * [0] => Array
  194. * (
  195. * [date] => 2020-1-22
  196. * [confirmed] => 0
  197. * [deaths] => 0
  198. * [recovered] => 0
  199. * )
  200. * [1]=> Array....
  201. */
  202. $countryTimeline = $this->rawData[$this->country];
  203. if (!empty($countryTimeline)) {
  204. $chartsOptions = $this->getChartOptions();
  205. $charsDataTotal[] = array(__('Date'), __('Confirmed'), __('Deaths'), __('Recovered'));
  206. $charsDataMonth[] = array(__('Date'), __('Confirmed'), __('Deaths'), __('Recovered'));
  207. $charsDataPeaksAll[] = array(__('Date'), __('Confirmed'), __('Deaths'));
  208. $charsDataPeaksYear[] = array(__('Date'), __('Confirmed'), __('Deaths'));
  209. $charsDataPeaksMonth[] = array(__('Date'), __('Confirmed'), __('Deaths'));
  210. $curMonth = curmonth() . '-';
  211. $curYear = curyear() . '-';
  212. $prevConf = 0;
  213. $prevDeaths = 0;
  214. foreach ($countryTimeline as $io => $each) {
  215. $timeStamp = strtotime($each['date']); //need to be transformed to Y-m-d
  216. $date = date("Y-m-d", $timeStamp);
  217. if (ispos($date, $curMonth)) {
  218. $charsDataMonth[] = array($date, $each['confirmed'], $each['deaths'], $each['recovered']);
  219. $curMonthCount++;
  220. }
  221. $charsDataTotal[] = array($date, $each['confirmed'], $each['deaths'], $each['recovered']);
  222. $peakConfirmed = $each['confirmed'] - $prevConf;
  223. $peakDeaths = $each['deaths'] - $prevDeaths;
  224. $charsDataPeaksAll[] = array($date, $peakConfirmed, $peakDeaths);
  225. if (ispos($date, $curYear)) {
  226. $charsDataPeaksYear[] = array($date, $peakConfirmed, $peakDeaths);
  227. }
  228. if (ispos($date, $curMonth)) {
  229. $charsDataPeaksMonth[] = array($date, $peakConfirmed, $peakDeaths);
  230. }
  231. $lastData = $each;
  232. $lastConf = $lastData['confirmed'] - $prevConf;
  233. $lastDeath = $lastData['deaths'] - $prevDeaths;
  234. $prevConf = $each['confirmed'];
  235. $prevDeaths = $each['deaths'];
  236. }
  237. $countryDeathPercent = zb_PercentValue($lastData['confirmed'], $lastData['deaths']);
  238. $result .= $this->messages->getStyledMessage(__('Confirmed') . ' ' . $lastData['confirmed'], 'warning');
  239. $result .= $this->messages->getStyledMessage(__('Deaths') . ' ' . $lastData['deaths'] . ' (' . $countryDeathPercent . '%)', 'error');
  240. $result .= $this->messages->getStyledMessage(__('Recovered') . ' ' . $lastData['recovered'], 'success');
  241. $result .= $this->messages->getStyledMessage(__('For the last day') . ' ' . $date . ' (' . __('Confirmed') . '/' . __('Deaths') . ') ' . $lastConf . '/' . $lastDeath, 'info');
  242. if ($curMonthCount > 0) {
  243. $result .= wf_gchartsLine($charsDataPeaksMonth, __('By date') . ' (' . __('Current month') . ')', '100%', '300px;', $chartsOptions);
  244. }
  245. $result .= wf_gchartsLine($charsDataPeaksYear, __('By date') . ' (' . __('Current year') . ')', '100%', '300px;', $chartsOptions);
  246. $result .= wf_gchartsLine($charsDataPeaksAll, __('By date') . ' (' . __('All time') . ')', '100%', '300px;', $chartsOptions);
  247. if ($curMonthCount > 0) {
  248. $result .= wf_gchartsLine($charsDataMonth, __('Month'), '100%', '300px;', $chartsOptions);
  249. }
  250. $result .= wf_gchartsLine($charsDataTotal, __('All time'), '100%', '300px;', $chartsOptions);
  251. } else {
  252. $result .= $this->messages->getStyledMessage(__('Something went wrong') . ': ' . __('Nothing to show'), 'warning');
  253. }
  254. } else {
  255. $result .= $this->messages->getStyledMessage(__('Something went wrong') . ': EX_WRONG_COUNTRY', 'warning');
  256. }
  257. } else {
  258. $result .= $this->messages->getStyledMessage(__('Something went wrong') . ': ' . __('Empty reply received'), 'error');
  259. }
  260. return($result);
  261. }
  262. /**
  263. * Renders COVID-19 world causes report
  264. *
  265. * @return string
  266. */
  267. public function renderWorld() {
  268. $result = '';
  269. $curMonthCount = 0;
  270. if (!empty($this->rawData)) {
  271. $chartsOptions = $this->getChartOptions();
  272. $curMonth = curmonth() . '-';
  273. $totalTmp = array();
  274. $prevConf = 0;
  275. $prevDeaths = 0;
  276. $charsDataTotal[] = array(__('Date'), __('Confirmed'), __('Deaths'), __('Recovered'));
  277. $charsDataMonth[] = array(__('Date'), __('Confirmed'), __('Deaths'), __('Recovered'));
  278. $charsDataPeaks[] = array(__('Date'), __('Confirmed'), __('Deaths'));
  279. foreach ($this->rawData as $eachCountry => $eachTimeline) {
  280. if (!empty($eachTimeline)) {
  281. foreach ($eachTimeline as $io => $each) {
  282. $timeStamp = strtotime($each['date']); //need to be transformed to Y-m-d
  283. $date = date("Y-m-d", $timeStamp);
  284. if (isset($totalTmp[$date])) {
  285. $totalTmp[$date]['confirmed'] += $each['confirmed'];
  286. $totalTmp[$date]['deaths'] += $each['deaths'];
  287. $totalTmp[$date]['recovered'] += $each['recovered'];
  288. } else {
  289. $totalTmp[$date]['confirmed'] = $each['confirmed'];
  290. $totalTmp[$date]['deaths'] = $each['deaths'];
  291. $totalTmp[$date]['recovered'] = $each['recovered'];
  292. }
  293. }
  294. }
  295. }
  296. if (!empty($totalTmp)) {
  297. foreach ($totalTmp as $date => $each) {
  298. if (ispos($date, $curMonth)) {
  299. $charsDataMonth[] = array($date, $each['confirmed'], $each['deaths'], $each['recovered']);
  300. $curMonthCount++;
  301. }
  302. $charsDataTotal[] = array($date, $each['confirmed'], $each['deaths'], $each['recovered']);
  303. $charsDataPeaks[] = array($date, ($each['confirmed'] - $prevConf), ($each['deaths'] - $prevDeaths));
  304. $lastData = $each;
  305. $prevConf = $each['confirmed'];
  306. $prevDeaths = $each['deaths'];
  307. }
  308. $worldDeathPercent = zb_PercentValue($lastData['confirmed'], $lastData['deaths']);
  309. $result .= $this->messages->getStyledMessage(__('Confirmed') . ' ' . $lastData['confirmed'], 'warning');
  310. $result .= $this->messages->getStyledMessage(__('Deaths') . ' ' . $lastData['deaths'] . ' (' . $worldDeathPercent . '%)', 'error');
  311. $result .= $this->messages->getStyledMessage(__('Recovered') . ' ' . $lastData['recovered'], 'success');
  312. $result .= wf_gchartsLine($charsDataPeaks, __('By date'), '100%', '300px;', $chartsOptions);
  313. if ($curMonthCount > 0) {
  314. $result .= wf_gchartsLine($charsDataMonth, __('Month'), '100%', '300px;', $chartsOptions);
  315. }
  316. $result .= wf_gchartsLine($charsDataTotal, __('All time'), '100%', '300px;', $chartsOptions);
  317. }
  318. } else {
  319. $result .= $this->messages->getStyledMessage(__('Something went wrong') . ': ' . __('Empty reply received'), 'error');
  320. }
  321. return($result);
  322. }
  323. /**
  324. * Renders default module controls
  325. *
  326. * @return string
  327. */
  328. public function panel() {
  329. $result = '';
  330. $result .= wf_Link(self::URL_ME, wf_img('skins/country.png') . ' ' . __('Country'), false, 'ubButton');
  331. $result .= wf_Link(self::URL_ME . '&world=true', wf_img('skins/ymaps/globe.png') . ' ' . __('World'), false, 'ubButton');
  332. return($result);
  333. }
  334. }
  335. $covid = new Covid19();
  336. show_window('', $covid->panel());
  337. if (ubRouting::checkGet('world')) {
  338. show_window(__('COVID-19'), $covid->renderWorld());
  339. } else {
  340. show_window(__('COVID-19'), $covid->renderCountry());
  341. }
  342. zb_BillingStats(true);
  343. } else {
  344. show_error(__('This module is disabled'));
  345. }
  346. } else {
  347. show_error(__('Access denied'));
  348. }