get_user_activity.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php /** Created by Anton on 05.03.2020. */
  2. require __DIR__ . '/src/classes/PDO.php';
  3. require 'tmc/yiiInit.php';
  4. use app\models\entity\Jobtypes;
  5. use app\models\entity\Tasks;
  6. const DATE_FORMAT = 'Y-m-d H:i:s';
  7. const STEP = 6; //Количество делений в одном часе
  8. const LENGTH_SHIFT = 13; //Продолжительность смены в отчёте
  9. //Получение данных запроса
  10. try {
  11. $date = new DateTime($_GET['date']);
  12. } catch (Exception $e) {
  13. $date = new DateTime();
  14. }
  15. $shift = (int)$_GET['shift'];
  16. $company = (int)$_GET['company'];
  17. if (!$date) exit;
  18. //Временные интервалы
  19. $twelveHour = new DateInterval('PT12H');
  20. $threeHour = new DateInterval('PT3H');
  21. $oneHour = new DateInterval('PT1H');
  22. $tenMin = new DateInterval('PT10M');
  23. //Установка времени начала смены
  24. $date->add($threeHour);//Смена начинается в 3 ночи
  25. if ($shift) {
  26. $date->add($twelveHour);//Вторая смена через 12 часов
  27. }
  28. //Даты начала и конца смены
  29. $startShift = clone $date;
  30. $date->add($twelveHour);
  31. $endShift = clone $date;
  32. $endShift->add($oneHour);//Захватываем лишний час по просьбам ***
  33. //Получаем всех сотрудников попадающих во временной интервал
  34. $pdo = \classes\PDO::getInsatance();
  35. $stmt = $pdo->query('' .
  36. 'select tu.id, ta.id, ta.name, ta.jobtypes, ta.overplan_mode, tu.start, tu.end '
  37. . 'from user_activity tu '
  38. . 'left join accounts_internal ta on tu.employee = ta.id '
  39. . 'where '
  40. . 'ta.company = ' . $company . ' '
  41. . 'and ('
  42. //Если активность попадает в интервал смены
  43. . ' (tu.start >= \'' . $startShift->format(DATE_FORMAT) . '\' '
  44. . ' and tu.end <= \'' . $endShift->format(DATE_FORMAT) . '\') '
  45. //Если активность больше смены и длится всю смену
  46. . ' or (tu.start <= \'' . $startShift->format(DATE_FORMAT) . '\' '
  47. . ' and tu.end >= \'' . $endShift->format(DATE_FORMAT) . '\') '
  48. //Если начало или конец активности попадает в смену
  49. . ' or ('
  50. . ' (tu.start between \'' . $startShift->format(DATE_FORMAT) . '\' and \'' . $endShift->format(DATE_FORMAT) . '\') '
  51. . ' or '
  52. . ' (tu.end between \'' . $startShift->format(DATE_FORMAT) . '\' and \'' . $endShift->format(DATE_FORMAT) . '\') '
  53. . ' ) '
  54. . ')'
  55. );
  56. if (!$stmt->rowCount()) {
  57. echo '<h3>Нет данных за этот период</h3>';
  58. exit;
  59. }
  60. //Ключ - пользователь, значение - массив его интервалов
  61. //['name' => 0 => ['start' => time, 'end' => time], 1 =>..
  62. $usersWithIntervals = [];
  63. //[<имя пользователя> => <id пользователя>, ...]
  64. $users = [];
  65. foreach ($stmt->fetchAll() as $item) {
  66. $usersWithIntervals[$item['name']]['id'] = $item['id'];
  67. $users[$item['name']] = $item['id'];
  68. if ($item['overplan_mode']) {
  69. $usersWithIntervals[$item['name']]['overplan'] = 1;
  70. }
  71. if (!isset($usersWithIntervals[$item['name']]['specialty'])) {
  72. $specialtyIds = explode(',', $item['jobtypes']);
  73. $specialtyIds = array_diff($specialtyIds, ['']);
  74. foreach ($specialtyIds as $specialtyId) {
  75. $specialty = Jobtypes::findOne($specialtyId);
  76. if ($specialty) {
  77. $usersWithIntervals[$item['name']]['specialty'][] = $specialty->name;
  78. }
  79. }
  80. }
  81. $usersWithIntervals[$item['name']]['list'][] = [
  82. 'start' => $item['start'],
  83. 'end' => $item['end']
  84. ];
  85. }
  86. //Количетво задач пользователя
  87. $tasksByUser = [];
  88. foreach ($users as $userName => $userId) {
  89. $assignedTasks = Tasks::find()
  90. ->where([
  91. 'assignees_arr' => $userId,
  92. 'parent_id' => 0
  93. ])
  94. ->andWhere(['between', 'created', $startShift->format(DATE_FORMAT), $endShift->format(DATE_FORMAT)])
  95. ->all()
  96. ;
  97. //Количество назначенных сотруднику задач
  98. $tasksByUser[$userName]['assigned'] = count($assignedTasks);
  99. $finished = 0;
  100. foreach ($assignedTasks as $task) {
  101. /** @var Tasks $task */
  102. foreach ($task->getCommands() as $command) {
  103. if ($command->finished_time) {
  104. $finished++;
  105. break;
  106. }
  107. }
  108. }
  109. //Количество задач завершённых через МП
  110. $tasksByUser[$userName]['finished'] = $finished;
  111. }
  112. //Данные подготовленные для вставки в таблицу
  113. $dataForTable = [];
  114. foreach ($usersWithIntervals as $user => $data) {
  115. $userTasks = Tasks::findAll(['assignees_arr' => $data['id']]);//, 'accepted_time is not null'
  116. //Забиваем сотруднику по STEP нулей за каждый час временного интервала
  117. $dataForTable[$user] = [];
  118. for ($i = 0; $i < LENGTH_SHIFT * STEP; $i++) {
  119. $dataForTable[$user][] = 0;
  120. }
  121. //Время, к которому будем добавлять по 10 минут
  122. $date = clone $startShift;
  123. $date->add(new DateInterval('PT5M'));
  124. //Сравниваем каждые 10 минут попадает ли это время в интервалы. Если попадает меняем 0 на 1
  125. foreach ($dataForTable[$user] as $key => $value) {
  126. $timeString = $date->format(DATE_FORMAT);
  127. foreach ($data['list'] as $interval) {
  128. if ($interval['start'] <= $timeString && $timeString <= $interval['end']) {
  129. $dataForTable[$user][$key] = 1;
  130. //Внеплан?
  131. if ($data['overplan']) {
  132. $dataForTable[$user][$key] = 3;
  133. } else {
  134. //Проверка попадает ли во время исполнения задачи
  135. /** @var Tasks $task */
  136. foreach ($userTasks as $task) {
  137. if (
  138. $task->accepted_time <= $timeString
  139. && ($timeString <= $task->finished_time)
  140. ) {
  141. $dataForTable[$user][$key] = 2;
  142. } else {
  143. //Попадает ли принятие или завершение задачи в предыдущие 10 минут
  144. //Костыль, ибо таблица создавалась как "активность пользователей в МП"
  145. $previousDateInterval = clone $date;
  146. $previousDateInterval->sub($tenMin);
  147. $prevTimeString = $previousDateInterval->format(DATE_FORMAT);
  148. if (
  149. ($prevTimeString <= $task->accepted_time && $task->accepted_time <= $timeString)
  150. || ($prevTimeString <= $task->finished_time && $task->finished_time <= $timeString)
  151. ) {
  152. $dataForTable[$user][$key] = 2;
  153. }
  154. }
  155. }
  156. }
  157. $date->add($tenMin);
  158. continue 2;
  159. }
  160. }
  161. $date->add($tenMin);
  162. }
  163. if (isset($data['specialty']) && $data['specialty'] !== 1) {
  164. $dataForTable[$user]['specialty'] = $data['specialty'];
  165. } else {
  166. $dataForTable[$user]['specialty'] = [];
  167. }
  168. if (isset($data['overplan'])) {
  169. array_pop($dataForTable[$user]);
  170. $dataForTable[$user]['overplan'] = 1;
  171. }
  172. }
  173. $tmpDate = clone $startShift;
  174. ?>
  175. <table class="table text-center" border="2">
  176. <thead>
  177. <tr>
  178. <td colspan="<?= LENGTH_SHIFT * STEP + 1?>">
  179. <h3>Начало смены - <?= $startShift->format('d.m.Y H:i') ?> МСК</h3>
  180. </td>
  181. </tr>
  182. </thead>
  183. <tr>
  184. <th>Сотрудник</th>
  185. <?php for ($i = 0; $i < LENGTH_SHIFT; $i++): ?>
  186. <th colspan="6"><?= $tmpDate->format('H:i') ?></th>
  187. <?php $tmpDate->add($oneHour) ?>
  188. <?php endfor; ?>
  189. </tr>
  190. <?php ksort($dataForTable); ?>
  191. <?php foreach ($dataForTable as $user => $values): ?>
  192. <tr>
  193. <th>
  194. <?= $user ?>
  195. <span style="color: black; white-space:nowrap">
  196. (<?= $tasksByUser[$user]['assigned'] ?>, <?= $tasksByUser[$user]['finished'] ?>)
  197. </span>
  198. <br>
  199. <span style="color: grey; font-size: 12px">(<?= implode(', ', $values['specialty']) ?>)</span>
  200. </th>
  201. <?php for ($i = 0; $i < count($values) - 1; $i++): ?>
  202. <?php switch ($values[$i]):
  203. case 1: ?>
  204. <td class="black"></td>
  205. <?php break;
  206. case 2: ?>
  207. <td class="blue"></td>
  208. <?php break;
  209. case 3: ?>
  210. <td class="red"></td>
  211. <?php break;
  212. default: ?>
  213. <td></td>
  214. <?php break; ?>
  215. <?php endswitch; ?>
  216. <?php endfor; ?>
  217. </tr>
  218. <?php endforeach; ?>
  219. </table>
  220. <div class="legend">
  221. <p><span class="white">&nbsp;&nbsp;&nbsp;&nbsp;</span> - не в сети</p>
  222. <p><span class="black">&nbsp;&nbsp;&nbsp;&nbsp;</span> - в сети</p>
  223. <p><span class="blue">&nbsp;&nbsp;&nbsp;&nbsp;</span> - в сети и выполнял задачу</p>
  224. <p><span class="red">&nbsp;&nbsp;&nbsp;&nbsp;</span> - внеплан</p>
  225. </div>
  226. <style>
  227. :root {
  228. --border-style: 2px solid #000;
  229. }
  230. #content {
  231. /*margin-top: 60px;*/
  232. }
  233. @media screen and (max-width: 1024px) {
  234. #content {
  235. max-width: 920px;
  236. }
  237. }
  238. .table {
  239. margin-bottom: 5px;
  240. }
  241. .table td {
  242. padding: 0;
  243. }
  244. .table td:nth-child(6n+1) {
  245. border-right: var(--border-style);
  246. }
  247. .table tr:nth-child(1) > th {
  248. border: var(--border-style);
  249. }
  250. .table th {
  251. border-right: var(--border-style);
  252. border-left: var(--border-style);
  253. }
  254. td.black {
  255. background-color: black!important;
  256. }
  257. td.blue {
  258. background-color: blue!important;
  259. }
  260. td.red {
  261. background-color: red!important;
  262. }
  263. .table > tbody > tr > td {
  264. padding: 5px;
  265. line-height: 1;
  266. }
  267. .legend {
  268. display: flex;
  269. }
  270. .legend p {
  271. margin-left: 5px;
  272. }
  273. .legend span {
  274. border: 1px solid black;
  275. }
  276. .legend .white {
  277. background-color: white;
  278. }
  279. .legend .black {
  280. background-color: black!important;
  281. }
  282. .legend .blue {
  283. background-color: blue!important;
  284. }
  285. .legend .red {
  286. background-color: red!important;
  287. }
  288. @media print {
  289. td.blue {
  290. background-color: #ccc !important;
  291. }
  292. td.red {
  293. background-color: #777 !important;
  294. }
  295. .legend span {
  296. padding: 0 10px 5px 0;
  297. background-color: #cccccc;
  298. }
  299. .legend .blue {
  300. background-color: #ccc !important;
  301. }
  302. .legend .red {
  303. background-color: #777 !important;
  304. }
  305. }
  306. </style>