PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/phpical/functions/parse/overlapping_events.php

https://gitlab.com/endomorphosis/fusenews
PHP | 274 lines | 178 code | 26 blank | 70 comment | 50 complexity | 720b9d11b0695d34d46c36b4de567a42 MD5 | raw file
  1. <?php
  2. // function to determine maximum necessary columns per day
  3. // actually an algorithm to get the smallest multiple for two numbers
  4. function kgv($a, $b) {
  5. $x = $a;
  6. $y = $b;
  7. while ($x != $y) {
  8. if ($x < $y) $x += $a;
  9. else $y += $b;
  10. }
  11. return $x;
  12. }
  13. // merge a given range into $ol_ranges. Returns the merged $ol_ranges.
  14. // if count = -2, treat as a "delete" call (for removeOverlap)
  15. // Why -2? That way, there's less fudging of the math in the code.
  16. function merge_range($ol_ranges, $start, $end, $count = 0) {
  17. foreach ($ol_ranges as $loop_range_key => $loop_range) {
  18. if ($start < $end) {
  19. // handle ranges between $start and $loop_range['start']
  20. if ($start < $loop_range['start']) {
  21. $new_ol_ranges[] = array('count' => $count, 'start' => $start, 'end' => min($loop_range['start'], $end));
  22. $start = $loop_range['start'];
  23. }
  24. // $start is always >= $loop_range['start'] at this point.
  25. // handles ranges between $loop_range['start'] and $loop_range['end']
  26. if ($loop_range['start'] < $end && $start < $loop_range['end']) {
  27. // handles ranges between $loop_range['start'] and $start
  28. if ($loop_range['start'] < $start) {
  29. $new_ol_ranges[] = array('count' => $loop_range['count'], 'start' => $loop_range['start'], 'end' => $start);
  30. }
  31. // handles ranges between $start and $end (where they're between $loop_range['start'] and $loop_range['end'])
  32. $new_count = $loop_range['count'] + $count + 1;
  33. if ($new_count >= 0) {
  34. $new_ol_ranges[] = array('count' => $new_count, 'start' => $start, 'end' => min($loop_range['end'], $end));
  35. }
  36. // handles ranges between $end and $loop_range['end']
  37. if ($loop_range['end'] > $end) {
  38. $new_ol_ranges[] = array('count' => $loop_range['count'], 'start' => $end, 'end' => $loop_range['end']);
  39. }
  40. $start = $loop_range['end'];
  41. } else {
  42. $new_ol_ranges[] = $loop_range;
  43. }
  44. } else {
  45. $new_ol_ranges[] = $loop_range;
  46. }
  47. }
  48. // Catches anything left over.
  49. if ($start < $end) {
  50. $new_ol_ranges[] = array('count' => $count, 'start' => $start, 'end' => $end);
  51. }
  52. return $new_ol_ranges;
  53. }
  54. // Finds the highest value of 'count' in $ol_ranges
  55. function find_max_overlap($ol_ranges) {
  56. $count = 0;
  57. foreach ($ol_ranges as $loop_range) {
  58. if ($count < $loop_range['count'])
  59. $count = $loop_range['count'];
  60. }
  61. return $count;
  62. }
  63. // Merges overlapping blocks
  64. function flatten_ol_blocks($event_date, $ol_blocks, $new_block_key) {
  65. global $master_array;
  66. // Loop block = each other block in the array, the ones we're merging into new block.
  67. // New block = the changed block that caused the flatten_ol_blocks call. Everything gets merged into this.
  68. $new_block = $ol_blocks[$new_block_key];
  69. reset($ol_blocks);
  70. while ($loop_block_array = each($ol_blocks)) {
  71. $loop_block_key = $loop_block_array['key'];
  72. $loop_block = $loop_block_array['value'];
  73. // only compare with other blocks
  74. if ($loop_block_key != $new_block_key) {
  75. // check if blocks overlap
  76. if (($loop_block['blockStart'] < $new_block['blockEnd']) && ($loop_block['blockEnd'] > $new_block['blockStart'])) {
  77. // define start and end of merged overlap block
  78. if ($new_block['blockStart'] > $loop_block['blockStart']) $ol_blocks[$new_block_key]['blockStart'] = $loop_block['blockStart'];
  79. if ($new_block['blockEnd'] < $loop_block['blockEnd']) $ol_blocks[$new_block_key]['blockEnd'] = $loop_block['blockEnd'];
  80. $ol_blocks[$new_block_key]['events'] = array_merge($new_block['events'], $loop_block['events']);
  81. $new_block['events'] = $ol_blocks[$new_block_key]['events'];
  82. foreach ($loop_block['overlapRanges'] as $ol_range) {
  83. $new_block['overlapRanges'] = merge_range($new_block['overlapRanges'], $ol_range['start'], $ol_range['end'], $ol_range['count']);
  84. }
  85. $ol_blocks[$new_block_key]['overlapRanges'] = $new_block['overlapRanges'];
  86. $ol_blocks[$new_block_key]['maxOverlaps'] = find_max_overlap($new_block['overlapRanges']);
  87. foreach ($ol_blocks[$new_block_key]['events'] as $event) {
  88. $master_array[$event_date][$event['time']][$event['key']]['event_overlap'] = $ol_blocks[$new_block_key]['maxOverlaps'];
  89. }
  90. unset($ol_blocks[$loop_block_key]);
  91. reset($ol_blocks);
  92. }
  93. }
  94. }
  95. return $ol_blocks;
  96. }
  97. // Builds $overlap_array structure, and updates event_overlap in $master_array for the given events.
  98. // For a given date,
  99. // - check to see if the event's already in a block, and if so, add it.
  100. // - make sure the new block doesn't overlap another block, and if so, merge the blocks.
  101. // - check that there aren't any events we already passed that we should handle.
  102. // - "flatten" the structure again, merging the blocks.
  103. // $overlap_array structure:
  104. // array of ($event_dates)
  105. // array of unique overlap blocks (no index) -
  106. // $overlap_block structure
  107. // 'blockStart' - $start_time of block - earliest $start_time of the events in the block.
  108. // Shouldn't be any overlap w/ a different overlap block in that day (as if they overlap, they get merged).
  109. // 'blockEnd' - $end_time of block - latest $end_time of the events in the block.
  110. // 'maxOverlaps' - max number of overlaps for the whole block (highest 'count' in overlapRanges)
  111. // 'events' - array of event "pointers" (no index) - each event in the block.
  112. // 'time' - $start_time of event in the block
  113. // 'key' - $uid of event
  114. // 'overlapRanges' - array of time ranges + overlap counts (no index) - the specific overlap info.
  115. // Shouldn't be any overlap w/ the overlap ranges in a given overlap_block - if there is overlap, the block should be split.
  116. // 'count' - number of overlaps that time range (can be zero if that range has no overlaps).
  117. // 'start' - start_time for the overlap block.
  118. // 'end' - end_time for the overlap block.
  119. function checkOverlap($event_date, $event_time, $uid) {
  120. global $master_array, $overlap_array;
  121. if (!isset($event_date)) return;
  122. $event = $master_array[$event_date][$event_time][$uid];
  123. // Copy out the array - we replace this at the end.
  124. $ol_day_array = @$overlap_array[$event_date];
  125. $draw_end = $event['event_end'];
  126. if (isset($event['display_end'])) $draw_end = $event['display_end'];
  127. $drawTimes = drawEventTimes($event['event_start'], $draw_end);
  128. // Track if $event has been merged in, so we don't re-add the details to 'event' or 'overlapRanges' multiple times.
  129. $already_merged_once = false;
  130. // First, check the existing overlap blocks, see if the event overlaps with any.
  131. if (isset($ol_day_array)) {
  132. foreach ($ol_day_array as $loop_block_key => $loop_ol_block) {
  133. // Should $event be in this $ol_block? If so, add it.
  134. if ($loop_ol_block['blockStart'] < $drawTimes['draw_end'] && $loop_ol_block['blockEnd'] > $drawTimes['draw_start']) {
  135. // ... unless it's already in the $ol_block
  136. if (!in_array(array('time' => $drawTimes['draw_start'], 'key' => $uid), $loop_ol_block['events'])) {
  137. $loop_ol_block['events'][] = array('time' => $drawTimes['draw_start'], 'key' => $uid);
  138. if ($loop_ol_block['blockStart'] > $drawTimes['draw_start']) $loop_ol_block['blockStart'] = $drawTimes['draw_start'];
  139. if ($loop_ol_block['blockEnd'] < $drawTimes['draw_end']) $loop_ol_block['blockEnd'] = $drawTimes['draw_end'];
  140. // Merge in the new overlap range
  141. $loop_ol_block['overlapRanges'] = merge_range($loop_ol_block['overlapRanges'], $drawTimes['draw_start'], $drawTimes['draw_end']);
  142. $loop_ol_block['maxOverlaps'] = find_max_overlap($loop_ol_block['overlapRanges']);
  143. foreach ($loop_ol_block['events'] as $max_overlap_event) {
  144. $master_array[$event_date][$max_overlap_event['time']][$max_overlap_event['key']]['event_overlap'] = $loop_ol_block['maxOverlaps'];
  145. }
  146. $ol_day_array[$loop_block_key] = $loop_ol_block;
  147. $ol_day_array = flatten_ol_blocks($event_date, $ol_day_array, $loop_block_key);
  148. $already_merged_once = true;
  149. break;
  150. // Handle repeat calls to checkOverlap - semi-bogus since the event shouldn't be created more than once, but this makes sure we don't get an invalid event_overlap.
  151. } else {
  152. $master_array[$event_date][$event_time][$uid]['event_overlap'] = $loop_ol_block['maxOverlaps'];
  153. }
  154. }
  155. }
  156. }
  157. // Then, check all the events, make sure there isn't a new overlap that we need to create.
  158. foreach ($master_array[$event_date] as $time_key => $time) {
  159. // Skip all-day events for overlap purposes.
  160. if ($time_key != '-1') {
  161. foreach ($time as $loop_event_key => $loop_event) {
  162. // Make sure we haven't already dealt with the event, and we're not checking against ourself.
  163. if ($loop_event['event_overlap'] == 0 && $loop_event_key != $uid) {
  164. $loopDrawTimes = drawEventTimes($loop_event['event_start'], $loop_event['display_end']);
  165. if ($loopDrawTimes['draw_start'] < $drawTimes['draw_end'] && $loopDrawTimes['draw_end'] > $drawTimes['draw_start']) {
  166. if ($loopDrawTimes['draw_start'] < $drawTimes['draw_start']) {
  167. $block_start = $loopDrawTimes['draw_start'];
  168. } else {
  169. $block_start = $drawTimes['draw_start'];
  170. }
  171. if ($loopDrawTimes['draw_end'] > $drawTimes['draw_end']) {
  172. $block_end = $loopDrawTimes['draw_end'];
  173. } else {
  174. $block_end = $drawTimes['draw_end'];
  175. }
  176. $events = array(array('time' => $loopDrawTimes['draw_start'], 'key' => $loop_event_key));
  177. $overlap_ranges = array(array('count' => 0, 'start' => $loopDrawTimes['draw_start'], 'end' => $loopDrawTimes['draw_end']));
  178. // Only add $event if we haven't already put it in a block
  179. if (!$already_merged_once) {
  180. $events[] = array('time' => $drawTimes['draw_start'], 'key' => $uid);
  181. $overlap_ranges = merge_range($overlap_ranges, $drawTimes['draw_start'], $drawTimes['draw_end']);
  182. $already_merged_once = true;
  183. }
  184. $ol_day_array[] = array('blockStart' => $block_start, 'blockEnd' => $block_end, 'maxOverlaps' => 1, 'events' => $events, 'overlapRanges' => $overlap_ranges);
  185. foreach ($events as $max_overlap_event) {
  186. $master_array[$event_date][$max_overlap_event['time']][$max_overlap_event['key']]['event_overlap'] = 1;
  187. }
  188. // Make sure we pass in the key of the newly added item above.
  189. end($ol_day_array);
  190. $last_day_key = key($ol_day_array);
  191. $ol_day_array = flatten_ol_blocks($event_date, $ol_day_array, $last_day_key);
  192. }
  193. }
  194. }
  195. }
  196. }
  197. $overlap_array[$event_date] = $ol_day_array;
  198. //for debugging the checkOverlap function
  199. //if ($event_date == '20050506') {
  200. //print 'Date: ' . $event_date . ' / Time: ' . $event_time . ' / Key: ' . $uid . "<br />\n";
  201. //print '<pre>';
  202. //print_r($master_array[$event_date]);
  203. //print_r($overlap_array[$event_date]);
  204. //print '</pre>';
  205. //}
  206. }
  207. // Remove an event from the overlap data.
  208. // This could be completely bogus, since overlap array is empty when this gets called in my tests, but I'm leaving it in anyways.
  209. function removeOverlap($ol_start_date, $ol_start_time, $ol_key) {
  210. global $master_array, $overlap_array;
  211. if (isset($overlap_array[$ol_start_date])) {
  212. if (sizeof($overlap_array[$ol_start_date]) > 0) {
  213. $ol_end_time = $master_array[$ol_start_date][$ol_start_time][$ol_key]['event_end'];
  214. foreach ($overlap_array[$ol_start_date] as $block_key => $block) {
  215. if (in_array(array('time' => $ol_start_time, 'key' => $ol_key), $block['events'])) {
  216. // Check if this is a 2-event block (i.e., there's no block left when we remove $ol_key
  217. // and if so, just unset it and move on.
  218. if (count($block['events']) == 2) {
  219. foreach ($block['events'] as $event) {
  220. $master_array[$ol_start_date][$event['time']][$event['key']]['event_overlap'] = 0;
  221. }
  222. unset($overlap_array[$ol_start_date][$block_key]);
  223. } else {
  224. // remove $ol_key from 'events'
  225. $event_key = array_search(array('time' => $ol_start_time, 'key' => $ol_key), $block['events']);
  226. unset($overlap_array[$ol_start_date][$block_key]['events'][$event_key]);
  227. // These may be bogus, since we're not using drawEventTimes.
  228. // "clean up" 'overlapRanges' and calc the new maxOverlaps.
  229. // use the special "-2" count to tell merge_range we're deleting.
  230. $overlap_array[$ol_start_date][$block_key]['overlapRanges'] = merge_range($block['overlapRanges'], $ol_start_time, $ol_end_time, -2);
  231. $overlap_array[$ol_start_date][$block_key]['maxOverlaps'] = find_max_overlap($block['overlapRanges']);
  232. // recreate blockStart and blockEnd from the other events, and fix maxOverlap while we're at it.
  233. $blockStart = $ol_end_time;
  234. $blockEnd = $ol_start_time;
  235. foreach ($overlap_array[$ol_start_date][$block_key]['events'] as $event) {
  236. $blockStart = min($blockStart, $event['time']);
  237. $blockEnd = max($blockEnd, $master_array[$ol_start_date][$event['time']][$event['key']]['event_end']);
  238. $master_array[$ol_start_date][$event['time']][$event['key']]['event_overlap'] = $overlap_array[$ol_start_date][$block_key]['maxOverlaps'];
  239. }
  240. $overlap_array[$ol_start_date][$block_key]['blockStart'] = $blockStart;
  241. $overlap_array[$ol_start_date][$block_key]['blockEnd'] = $blockEnd;
  242. }
  243. }
  244. }
  245. }
  246. }
  247. }
  248. ?>