From d483119a20f1915167440f986dd1f980f0cce4ea Mon Sep 17 00:00:00 2001 From: Sebastian Marcet Date: Fri, 9 Sep 2016 13:42:15 -0300 Subject: [PATCH] Smart Conference Added event room metrics Now you could query the "metrics" relations on get event by id endpoint to get max, min, avg and current values of the room metrics. Change-Id: Ic2f695c48a854ab6d4806ef8c471893b7b95d275 --- app/ModelSerializers/SerializerRegistry.php | 52 ++--- .../SummitEventMetricsSnapshotSerializer.php | 32 +++ .../SummitEventSerializer.php | 16 +- .../Foundation/Summit/Events/SummitEvent.php | 60 +++++- .../Metrics/RoomMetricSampleData.php | 95 +++++++++ .../Locations/Metrics/RoomMetricType.php | 200 ++++++++++++++++++ .../Metrics/SummitEventMetricsSnapshot.php | 123 +++++++++++ .../Summit/Locations/SummitVenueRoom.php | 41 ++++ tests/OAuth2SummitApiTest.php | 10 +- 9 files changed, 596 insertions(+), 33 deletions(-) create mode 100644 app/ModelSerializers/SummitEventMetricsSnapshotSerializer.php create mode 100644 app/Models/Foundation/Summit/Locations/Metrics/RoomMetricSampleData.php create mode 100644 app/Models/Foundation/Summit/Locations/Metrics/RoomMetricType.php create mode 100644 app/Models/Foundation/Summit/Locations/Metrics/SummitEventMetricsSnapshot.php 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",