diff --git a/app/ModelSerializers/SerializerRegistry.php b/app/ModelSerializers/SerializerRegistry.php index 67a795c1..66a667ff 100644 --- a/app/ModelSerializers/SerializerRegistry.php +++ b/app/ModelSerializers/SerializerRegistry.php @@ -48,33 +48,35 @@ final class SerializerRegistry private function __construct() { - $this->registry['Summit'] = SummitSerializer::class; - $this->registry['SummitType'] = SummitTypeSerializer::class; - $this->registry['SummitEventType'] = SummitEventTypeSerializer::class; - $this->registry['SummitTicketType'] = SummitTicketTypeSerializer::class; - $this->registry['PresentationCategory'] = PresentationCategorySerializer::class; - $this->registry['PresentationCategoryGroup'] = PresentationCategoryGroupSerializer::class; - $this->registry['Tag'] = TagSerializer::class; - $this->registry['SummitEvent'] = SummitEventSerializer::class; - $this->registry['Presentation'] = PresentationSerializer::class; - $this->registry['PresentationVideo'] = PresentationVideoSerializer::class; - $this->registry['PresentationSlide'] = PresentationSlideSerializer::class; - $this->registry['PresentationLink'] = PresentationLinkSerializer::class; - $this->registry['Company'] = CompanySerializer::class; - $this->registry['PresentationSpeaker'] = PresentationSpeakerSerializer::class; - $this->registry['SummitEventFeedback'] = SummitEventFeedbackSerializer::class; - $this->registry['SummitAttendee'] = SummitAttendeeSerializer::class; - $this->registry['SummitAttendeeSchedule'] = SummitAttendeeScheduleSerializer::class; - $this->registry['SummitEntityEvent'] = SummitEntityEventSerializer::class; + $this->registry['Summit'] = SummitSerializer::class; + $this->registry['SummitType'] = SummitTypeSerializer::class; + $this->registry['SummitEventType'] = SummitEventTypeSerializer::class; + $this->registry['SummitTicketType'] = SummitTicketTypeSerializer::class; + $this->registry['PresentationCategory'] = PresentationCategorySerializer::class; + $this->registry['PresentationCategoryGroup'] = PresentationCategoryGroupSerializer::class; + $this->registry['Tag'] = TagSerializer::class; + $this->registry['SummitEvent'] = SummitEventSerializer::class; + $this->registry['SummitEventMetricsSnapshot'] = SummitEventMetricsSnapshotSerializer::class; + $this->registry['Presentation'] = PresentationSerializer::class; + $this->registry['PresentationVideo'] = PresentationVideoSerializer::class; + $this->registry['PresentationSlide'] = PresentationSlideSerializer::class; + $this->registry['PresentationLink'] = PresentationLinkSerializer::class; + $this->registry['Company'] = CompanySerializer::class; + $this->registry['PresentationSpeaker'] = PresentationSpeakerSerializer::class; + $this->registry['SummitEventFeedback'] = SummitEventFeedbackSerializer::class; + $this->registry['SummitAttendee'] = SummitAttendeeSerializer::class; + $this->registry['SummitAttendeeSchedule'] = SummitAttendeeScheduleSerializer::class; + $this->registry['SummitEntityEvent'] = SummitEntityEventSerializer::class; // locations - $this->registry['SummitVenue'] = SummitVenueSerializer::class; - $this->registry['SummitVenueRoom'] = SummitVenueRoomSerializer::class; - $this->registry['SummitVenueFloor'] = SummitVenueFloorSerializer::class; - $this->registry['SummitExternalLocation'] = SummitExternalLocationSerializer::class; - $this->registry['SummitHotel'] = SummitHotelSerializer::class; - $this->registry['SummitAirport'] = SummitAirportSerializer::class; - $this->registry['SummitLocationImage'] = SummitLocationImageSerializer::class; + $this->registry['SummitVenue'] = SummitVenueSerializer::class; + $this->registry['SummitVenueRoom'] = SummitVenueRoomSerializer::class; + $this->registry['SummitVenueFloor'] = SummitVenueFloorSerializer::class; + $this->registry['SummitExternalLocation'] = SummitExternalLocationSerializer::class; + $this->registry['SummitHotel'] = SummitHotelSerializer::class; + $this->registry['SummitAirport'] = SummitAirportSerializer::class; + $this->registry['SummitLocationImage'] = SummitLocationImageSerializer::class; + // member $this->registry['Member'] = MemberSerializer::class; diff --git a/app/ModelSerializers/SummitEventMetricsSnapshotSerializer.php b/app/ModelSerializers/SummitEventMetricsSnapshotSerializer.php new file mode 100644 index 00000000..b5d53030 --- /dev/null +++ b/app/ModelSerializers/SummitEventMetricsSnapshotSerializer.php @@ -0,0 +1,32 @@ + 'avg:json_float', + 'Max' => 'max:json_float', + 'Min' => 'min:json_float', + 'Current' => 'current:json_float', + 'TypeName' => 'type:json_string', + 'EventId' => 'event_id:json_int', + ); + +} \ No newline at end of file diff --git a/app/ModelSerializers/SummitEventSerializer.php b/app/ModelSerializers/SummitEventSerializer.php index be2d1596..47ffe078 100644 --- a/app/ModelSerializers/SummitEventSerializer.php +++ b/app/ModelSerializers/SummitEventSerializer.php @@ -12,6 +12,7 @@ * limitations under the License. **/ use libs\utils\JsonUtils; +use models\summit\SummitEvent; /** * Class SummitEventSerializer @@ -70,8 +71,12 @@ class SummitEventSerializer extends SilverStripeSerializer */ public function serialize($expand = null, array $fields = array(), array $relations = array(), array $params = array() ) { - if(!count($relations)) $relations = $this->getAllowedRelations(); $event = $this->object; + if(!$event instanceof SummitEvent) return []; + + if(!count($relations)) $relations = $this->getAllowedRelations(); + + $values = parent::serialize($expand, $fields, $relations, $params); //check if description is empty, if so, set short description @@ -125,6 +130,15 @@ class SummitEventSerializer extends SilverStripeSerializer } } + if(in_array('metrics', $relations)){ + // show metrics snapshot + $metrics = []; + foreach($event->getMetricsSnapShots() as $snapshot){ + $metrics[] = SerializerRegistry::getInstance()->getSerializer($snapshot)->serialize(); + } + $values['metrics'] = $metrics; + } + return $values; } } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Events/SummitEvent.php b/app/Models/Foundation/Summit/Events/SummitEvent.php index d5b7e421..7d7c01f4 100644 --- a/app/Models/Foundation/Summit/Events/SummitEvent.php +++ b/app/Models/Foundation/Summit/Events/SummitEvent.php @@ -18,6 +18,7 @@ use App\Events\SummitEventUpdated; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Event\PreUpdateEventArgs; use models\exceptions\ValidationException; +use models\main\Company; use models\main\Tag; use models\utils\PreRemoveEventArgs; use models\utils\SilverstripeBaseModel; @@ -705,7 +706,6 @@ class SummitEvent extends SilverstripeBaseModel $this->pre_update_args = $args; } - /** * @ORM\PostUpdate: */ @@ -722,4 +722,62 @@ class SummitEvent extends SilverstripeBaseModel Event::fire(new SummitEventCreated($this, $args)); } + public function hasMetricsAvailable(){ + if(is_null($this->location)) return false; + if(!$this->location instanceof SummitVenueRoom) return false; + return $this->location->getMetrics()->count() > 0; + } + + /** + * @return SummitEventMetricsSnapshot[] + */ + public function getMetricsSnapShots(){ + $snapshots = []; + if(is_null($this->location)) return $snapshots; + if(!$this->location instanceof SummitVenueRoom) return $snapshots; + foreach($this->location->getMetrics() as $metric){ + $snapshot = $this->getMetricsValuesByType($metric); + if(is_null($snapshot)) continue; + $snapshots[] = $snapshot; + } + return $snapshots; + } + + /** + * @param int $type_id + * @return SummitEventMetricsSnapshot + */ + public function getMetricValuesByTypeId($type_id){ + + $metrics = []; + if(is_null($this->location)) return $metrics; + if(!$this->location instanceof SummitVenueRoom) return $metrics; + + $metric_type = $this->location->getMetricsByType($type_id); + if(is_null($metric_type)) return $metrics; + if(!$metric_type instanceof RoomMetricType) return $metrics; + + return $this->getMetricsValuesByType($metric_type); + } + + /** + * @param RoomMetricType $type + * @return SummitEventMetricsSnapshot + */ + private function getMetricsValuesByType(RoomMetricType $type){ + + $epoch_start_date = $this->getStartDate()->getTimestamp(); + $epoch_end_date = $this->getEndDate()->getTimestamp(); + + return new SummitEventMetricsSnapshot + ( + $this, + $type, + $type->getMeanValueByTimeWindow($epoch_start_date, $epoch_end_date), + $type->getMaxValueByTimeWindow($epoch_start_date, $epoch_end_date), + $type->getMinValueByTimeWindow($epoch_start_date, $epoch_end_date), + $type->getCurrentValueByTimeWindow($epoch_start_date, $epoch_end_date) + ); + } + } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Locations/Metrics/RoomMetricSampleData.php b/app/Models/Foundation/Summit/Locations/Metrics/RoomMetricSampleData.php new file mode 100644 index 00000000..ff2e69fa --- /dev/null +++ b/app/Models/Foundation/Summit/Locations/Metrics/RoomMetricSampleData.php @@ -0,0 +1,95 @@ +timestamp; + } + + /** + * @param int $timestamp + */ + public function setTimestamp($timestamp) + { + $this->timestamp = $timestamp; + } + + /** + * @return float + */ + public function getValue() + { + return $this->value; + } + + /** + * @param float $value + */ + public function setValue($value) + { + $this->value = $value; + } + + /** + * @return RoomMetricType + */ + public function getType() + { + return $this->type; + } + + /** + * @param RoomMetricType $type + */ + public function setType($type) + { + $this->type = $type; + } +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Locations/Metrics/RoomMetricType.php b/app/Models/Foundation/Summit/Locations/Metrics/RoomMetricType.php new file mode 100644 index 00000000..f8321002 --- /dev/null +++ b/app/Models/Foundation/Summit/Locations/Metrics/RoomMetricType.php @@ -0,0 +1,200 @@ +samples = new ArrayCollection(); + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @param string $type + */ + public function setType($type) + { + $this->type = $type; + } + + /** + * @return string + */ + public function getEndpoint() + { + return $this->endpoint; + } + + /** + * @param string $endpoint + */ + public function setEndpoint($endpoint) + { + $this->endpoint = $endpoint; + } + + /** + * @return SummitVenueRoom + */ + public function getRoom() + { + return $this->room; + } + + /** + * @param SummitVenueRoom $room + */ + public function setRoom($room) + { + $this->room = $room; + } + + /** + * @return ArrayCollection + */ + public function getSamples() + { + return $this->samples; + } + + /** + * @param ArrayCollection $samples + */ + public function setSamples($samples) + { + $this->samples = $samples; + } + + /** + * @param int $epoch_start_date + * @param int $epoch_end_date + * @return float + */ + public function getMaxValueByTimeWindow($epoch_start_date, $epoch_end_date){ + $samples = $this->getValuesByTimeWindow($epoch_start_date, $epoch_end_date, 'value', Criteria::DESC); + $max = count($samples) > 0 ? $samples[0] : null; + return !is_null($max) ? $max->getValue() : 0.0; + } + + /** + * @param int $epoch_start_date + * @param int $epoch_end_date + * @return float + */ + public function getMinValueByTimeWindow($epoch_start_date, $epoch_end_date){ + $samples = $this->getValuesByTimeWindow($epoch_start_date, $epoch_end_date, 'value', Criteria::ASC); + $min = count($samples) > 0 ? $samples[0] : null; + return !is_null($min) ? $min->getValue() : 0.0; + } + + /** + * @param int $epoch_start_date + * @param int $epoch_end_date + * @return float + */ + public function getCurrentValueByTimeWindow($epoch_start_date, $epoch_end_date){ + $utc_epoch = time(); + $rounded_epoch = $utc_epoch- $utc_epoch % 60; + $res = 0.0; + if($epoch_start_date <= $rounded_epoch && $rounded_epoch <= $epoch_end_date){ + $samples = $this->getValuesByTimeWindow($epoch_start_date, $epoch_end_date, 'timestamp', Criteria::ASC); + foreach($samples as $sample){ + if($sample->getTimestamp() == $rounded_epoch) + $res = $sample->getValue(); + } + } + return $res; + } + + /** + * @param int $epoch_start_date + * @param int $epoch_end_date + * @return float + */ + public function getMeanValueByTimeWindow($epoch_start_date, $epoch_end_date){ + $samples = $this->getValuesByTimeWindow($epoch_start_date, $epoch_end_date, 'value', Criteria::ASC); + $sum = 0.0; + foreach($samples as $sample){ + $sum += $sample->getValue(); + } + return count($samples) > 0 ? $sum / count($samples) : 0.0; + } + + /** + * @param int $epoch_start_date + * @param int $epoch_end_date + * @param string|null $order_by + * @param string|null $order_by_direction + * @return array + */ + public function getValuesByTimeWindow($epoch_start_date, $epoch_end_date, $order_by = null, $order_by_direction = null){ + $criteria = Criteria::create() + ->where(Criteria::expr()->gte("timestamp", $epoch_start_date)) + ->andWhere(Criteria::expr()->lte("timestamp", $epoch_end_date)); + + if(!is_null($order_by) && !is_null($order_by_direction)){ + $criteria = $criteria->orderBy(array($order_by => $order_by_direction)); + } + + $samples = $this->samples->matching($criteria); + return $samples->toArray(); + } +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Locations/Metrics/SummitEventMetricsSnapshot.php b/app/Models/Foundation/Summit/Locations/Metrics/SummitEventMetricsSnapshot.php new file mode 100644 index 00000000..54896372 --- /dev/null +++ b/app/Models/Foundation/Summit/Locations/Metrics/SummitEventMetricsSnapshot.php @@ -0,0 +1,123 @@ +average = $average; + $this->max = $max; + $this->min = $min; + $this->current = $current; + $this->event = $event; + $this->type = $type; + } + + /** + * @return RoomMetricType + */ + public function getType() + { + return $this->type; + } + + /** + * @return string + */ + public function getTypeName() + { + return $this->type->getType(); + } + + /** + * @return int + */ + public function getEventId(){ + return $this->event->getId(); + } + + /** + * @return float + */ + public function getAverage() + { + return $this->average; + } + + /** + * @return float + */ + public function getMax() + { + return $this->max; + } + + /** + * @return float + */ + public function getMin() + { + return $this->min; + } + + /** + * @return float + */ + public function getCurrent() + { + return $this->current; + } + + +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Locations/SummitVenueRoom.php b/app/Models/Foundation/Summit/Locations/SummitVenueRoom.php index 6a1471fa..817abb8b 100644 --- a/app/Models/Foundation/Summit/Locations/SummitVenueRoom.php +++ b/app/Models/Foundation/Summit/Locations/SummitVenueRoom.php @@ -13,6 +13,8 @@ * limitations under the License. **/ +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Mapping AS ORM; /** @@ -140,4 +142,43 @@ class SummitVenueRoom extends SummitAbstractLocation * @var bool */ private $override_blackouts; + + /** + * @ORM\OneToMany(targetEntity="RoomMetricType", mappedBy="room", cascade={"persist"}) + */ + private $metrics; + + /** + * @return ArrayCollection + */ + public function getMetrics() + { + return $this->metrics; + } + + /** + * @param ArrayCollection $metrics + */ + public function setMetrics($metrics) + { + $this->metrics = $metrics; + } + + /** + * SummitVenueRoom constructor. + */ + public function __construct() + { + $this->metrics = new ArrayCollection(); + } + + /** + * @param int $type_id + * @return RoomMetricType + */ + public function getMetricByType($type_id){ + + $criteria = Criteria::create()->where(Criteria::expr()->eq("id", $type_id)); + return $this->metrics->matching($criteria)->first(); + } } \ No newline at end of file diff --git a/tests/OAuth2SummitApiTest.php b/tests/OAuth2SummitApiTest.php index 9ca759ac..04545fb7 100644 --- a/tests/OAuth2SummitApiTest.php +++ b/tests/OAuth2SummitApiTest.php @@ -717,13 +717,12 @@ final class OAuth2SummitApiTest extends ProtectedApiTest public function testGetPublishedEventFields(){ - $params = array ( - 'id' => 6, - 'event_id' => 8900, - 'fields' => 'id,avg_feedback_rate,head_count', - 'relations' => 'none' + 'id' => 7, + 'event_id' => 15987, + 'fields' => 'id, avg_feedback_rate, head_count', + 'relations' => 'metrics' ); $headers = array @@ -732,7 +731,6 @@ final class OAuth2SummitApiTest extends ProtectedApiTest "CONTENT_TYPE" => "application/json" ); - $response = $this->action ( "GET",