diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSelectionPlansApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSelectionPlansApiController.php index 15c199c5..9bbd820e 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSelectionPlansApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSelectionPlansApiController.php @@ -270,4 +270,32 @@ final class OAuth2SummitSelectionPlansApiController extends OAuth2ProtectedContr return $this->error500($ex); } } + + /** + * @param string $status + * @return mixed + */ + public function getCurrentSelectionPlanByStatus($status){ + try { + + $selection_plan = $this->selection_plan_service->getCurrentSelectionPlanByStatus($status); + + if (is_null($selection_plan)) return $this->error404(); + + return $this->ok(SerializerRegistry::getInstance()->getSerializer($selection_plan)->serialize(Request::input('expand', ''))); + } + catch (ValidationException $ex1) { + Log::warning($ex1); + return $this->error412([$ex1->getMessage()]); + } + catch(EntityNotFoundException $ex2) + { + Log::warning($ex2); + return $this->error404(['message'=> $ex2->getMessage()]); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } } \ No newline at end of file diff --git a/app/Http/routes.php b/app/Http/routes.php index aa6ab56e..28b983ac 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -103,7 +103,12 @@ Route::group([ Route::group(array('prefix' => 'summits'), function () { Route::get('', [ 'middleware' => 'cache:'.Config::get('cache_api_response.get_summits_response_lifetime', 600), 'uses' => 'OAuth2SummitApiController@getSummits']); - Route::get('all', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitApiController@getAllSummits']); + Route::group(['prefix' => 'all'], function () { + Route::get('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitApiController@getAllSummits']); + Route::group(['prefix' => 'selection-plans'], function () { + Route::get('current/{status}', ['uses' => 'OAuth2SummitSelectionPlansApiController@getCurrentSelectionPlanByStatus'])->where('status', 'submission|selection|voting'); + }); + }); Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitApiController@addSummit']); Route::group(['prefix' => '{id}'], function () { Route::put('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitApiController@updateSummit']); diff --git a/app/ModelSerializers/Summit/SelectionPlanSerializer.php b/app/ModelSerializers/Summit/SelectionPlanSerializer.php index 1f17b8d2..f2fa9154 100644 --- a/app/ModelSerializers/Summit/SelectionPlanSerializer.php +++ b/app/ModelSerializers/Summit/SelectionPlanSerializer.php @@ -65,6 +65,11 @@ final class SelectionPlanSerializer extends SilverStripeSerializer $values['track_groups'] = $category_groups; } break; + case 'summit':{ + unset($values['summit_id']); + $values['summit'] = SerializerRegistry::getInstance()->getSerializer($selection_plan->getSummit())->serialize(); + } + break; } } } diff --git a/app/Models/Foundation/Summit/Events/Presentations/Presentation.php b/app/Models/Foundation/Summit/Events/Presentations/Presentation.php index 63db5fa5..dfff1147 100644 --- a/app/Models/Foundation/Summit/Events/Presentations/Presentation.php +++ b/app/Models/Foundation/Summit/Events/Presentations/Presentation.php @@ -11,9 +11,9 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - -use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Mapping AS ORM; +use App\Models\Foundation\Summit\SelectionPlan; +use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\ArrayCollection; use models\exceptions\ValidationException; @@ -116,6 +116,13 @@ class Presentation extends SummitEvent */ private $moderator; + /** + * @ORM\ManyToOne(targetEntity="App\Models\Foundation\Summit\SelectionPlan") + * @ORM\JoinColumn(name="SelectionPlanID", referencedColumnName="ID") + * @var SelectionPlan + */ + private $selection_plan; + /** * @ORM\OneToMany(targetEntity="models\summit\PresentationMaterial", mappedBy="presentation", cascade={"persist", "remove"}, orphanRemoval=true) * @var PresentationMaterial[] @@ -529,4 +536,24 @@ class Presentation extends SummitEvent return Presentation::SelectionStatus_Alternate; } + + /** + * @return SelectionPlan + */ + public function getSelectionPlan() + { + return $this->selection_plan; + } + + /** + * @param SelectionPlan $selection_plan + */ + public function setSelectionPlan($selection_plan) + { + $this->selection_plan = $selection_plan; + } + + public function clearSelectionPlan(){ + $this->selection_plan = null; + } } diff --git a/app/Models/Foundation/Summit/Repositories/ISummitRepository.php b/app/Models/Foundation/Summit/Repositories/ISummitRepository.php index fd9a185d..630ec6f7 100644 --- a/app/Models/Foundation/Summit/Repositories/ISummitRepository.php +++ b/app/Models/Foundation/Summit/Repositories/ISummitRepository.php @@ -43,4 +43,9 @@ interface ISummitRepository extends IBaseRepository * @return Summit */ public function getByName($name); + + /** + * @return Summit[] + */ + public function getCurrentAndFutureSummits(); } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/SelectionPlan.php b/app/Models/Foundation/Summit/SelectionPlan.php index 992436d5..6c67da6d 100644 --- a/app/Models/Foundation/Summit/SelectionPlan.php +++ b/app/Models/Foundation/Summit/SelectionPlan.php @@ -11,13 +11,14 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +use Doctrine\ORM\Mapping AS ORM; use App\Models\Utils\TimeZoneEntity; use Doctrine\Common\Collections\ArrayCollection; +use models\summit\Presentation; use models\summit\PresentationCategoryGroup; use models\summit\Summit; use models\summit\SummitOwned; use models\utils\SilverstripeBaseModel; -use Doctrine\ORM\Mapping AS ORM; use DateTime; /** * @ORM\Entity @@ -31,13 +32,9 @@ class SelectionPlan extends SilverstripeBaseModel use TimeZoneEntity; - /** - * @return string - */ - public function getTimeZoneId() - { - return $this->summit->getTimeZoneId(); - } + const STATUS_SUBMISSION = 'SUBMISSION'; + const STATUS_SELECTION = 'SELECTION'; + const STATUS_VOTING = 'VOTING'; /** * @ORM\Column(name="Name", type="string") @@ -103,6 +100,20 @@ class SelectionPlan extends SilverstripeBaseModel */ private $category_groups; + /** + * @ORM\OneToMany(targetEntity="models\summit\Presentation", mappedBy="selection_plan", cascade={"persist"}) + * @var Presentation[] + */ + private $presentations; + + /** + * @return string + */ + public function getTimeZoneId() + { + return $this->summit->getTimeZoneId(); + } + /** * @return DateTime */ @@ -249,11 +260,15 @@ class SelectionPlan extends SilverstripeBaseModel $this->is_enabled = $is_enabled; } + /** + * SelectionPlan constructor. + */ public function __construct() { parent::__construct(); $this->is_enabled = false; $this->category_groups = new ArrayCollection; + $this->presentations = new ArrayCollection; $this->max_submission_allowed_per_user = Summit::DefaultMaxSubmissionAllowedPerUser; } @@ -297,4 +312,20 @@ class SelectionPlan extends SilverstripeBaseModel $this->max_submission_allowed_per_user = $max_submission_allowed_per_user; } + /** + * @return Presentation[] + */ + public function getPresentations() + { + return $this->presentations; + } + + /** + * @param Presentation $presentation + */ + public function addPresentation(Presentation $presentation){ + if($this->presentations->contains($presentation)) return; + $this->presentations->add($presentation); + $presentation->setSelectedPresentations($this); + } } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Summit.php b/app/Models/Foundation/Summit/Summit.php index ff5c1133..4cb6fcc3 100644 --- a/app/Models/Foundation/Summit/Summit.php +++ b/app/Models/Foundation/Summit/Summit.php @@ -2060,6 +2060,31 @@ SQL; return $selection_plan === false ? null : $selection_plan; } + /** + * @param string $status + * @return null|SelectionPlan + */ + public function getCurrentSelectionPlanByStatus($status){ + $now_utc = new \DateTime('now', new \DateTimeZone('UTC')); + $criteria = Criteria::create(); + switch (strtoupper($status)){ + case SelectionPlan::STATUS_SUBMISSION:{ + $criteria->where(Criteria::expr()->lte('submission_begin_date', $now_utc))->andWhere(Criteria::expr()->gte('submission_end_date', $now_utc)); + } + break; + case SelectionPlan::STATUS_VOTING:{ + $criteria->where(Criteria::expr()->lte('voting_begin_date', $now_utc))->andWhere(Criteria::expr()->gte('voting_end_date', $now_utc)); + } + break; + case SelectionPlan::STATUS_SELECTION:{ + $criteria->where(Criteria::expr()->lte('selection_begin_date', $now_utc))->andWhere(Criteria::expr()->gte('selection_end_date', $now_utc)); + } + break; + } + $selection_plan = $this->selection_plans->matching($criteria)->first(); + return $selection_plan === false ? null : $selection_plan; + } + /** * @param int $id * @return null|SelectionPlan diff --git a/app/Repositories/Summit/DoctrineSummitRepository.php b/app/Repositories/Summit/DoctrineSummitRepository.php index 33128899..ae35bdce 100644 --- a/app/Repositories/Summit/DoctrineSummitRepository.php +++ b/app/Repositories/Summit/DoctrineSummitRepository.php @@ -40,6 +40,22 @@ final class DoctrineSummitRepository return $res[0]; } + /** + * @return Summit[] + */ + public function getCurrentAndFutureSummits(){ + $now_utc = new \DateTime('now', new \DateTimeZone('UTC')); + return $this->getEntityManager()->createQueryBuilder() + ->select("s") + ->from(\models\summit\Summit::class, "s") + ->where('s.begin_date <= :now and s.end_date >= :now') + ->orWhere('s.begin_date >= :now') + ->orderBy('s.begin_date', 'DESC') + ->setParameter('now', $now_utc) + ->getQuery() + ->getResult(); + } + /** * @return Summit[] */ diff --git a/app/Services/Model/ISummitSelectionPlanService.php b/app/Services/Model/ISummitSelectionPlanService.php index 53c8d5af..7cce1f81 100644 --- a/app/Services/Model/ISummitSelectionPlanService.php +++ b/app/Services/Model/ISummitSelectionPlanService.php @@ -66,4 +66,11 @@ interface ISummitSelectionPlanService * @return void */ public function deleteTrackGroupToSelectionPlan(Summit $summit, $selection_plan_id, $track_group_id); + + + /** + * @param string $status + * @return SelectionPlan|null + */ + public function getCurrentSelectionPlanByStatus($status); } \ No newline at end of file diff --git a/app/Services/Model/SummitSelectionPlanService.php b/app/Services/Model/SummitSelectionPlanService.php index 7a76334a..6f973667 100644 --- a/app/Services/Model/SummitSelectionPlanService.php +++ b/app/Services/Model/SummitSelectionPlanService.php @@ -13,8 +13,10 @@ **/ use App\Models\Foundation\Summit\Factories\SummitSelectionPlanFactory; use App\Models\Foundation\Summit\SelectionPlan; +use libs\utils\ITransactionService; use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; +use models\summit\ISummitRepository; use models\summit\Summit; /** * Class SummitSelectionPlanService @@ -24,6 +26,16 @@ final class SummitSelectionPlanService extends AbstractService implements ISummitSelectionPlanService { + /** + * @var ISummitRepository + */ + private $summit_repository; + + public function __construct(ISummitRepository $summit_repository, ITransactionService $tx_service) + { + $this->summit_repository = $summit_repository; + parent::__construct($tx_service); + } /** * @param Summit $summit @@ -50,6 +62,9 @@ final class SummitSelectionPlanService // validate selection plan $summit->checkSelectionPlanConflicts($selection_plan); + foreach($this->summit_repository->getCurrentAndFutureSummits() as $cur_summit){ + $cur_summit->checkSelectionPlanConflicts($selection_plan); + } $summit->addSelectionPlan($selection_plan); @@ -95,7 +110,9 @@ final class SummitSelectionPlanService // validate selection plan $summit->checkSelectionPlanConflicts($selection_plan); - + foreach($this->summit_repository->getCurrentAndFutureSummits() as $cur_summit){ + $cur_summit->checkSelectionPlanConflicts($selection_plan); + } return $selection_plan; }); } @@ -193,4 +210,24 @@ final class SummitSelectionPlanService $selection_plan->removeTrackGroup($track_group); }); } + + /** + * @param string $status + * @return SelectionPlan|null + * @throws \Exception + */ + public function getCurrentSelectionPlanByStatus($status) + { + return $this->tx_service->transaction(function() use($status) { + // first get current summit plus future summits + $summits = $this->summit_repository->getCurrentAndFutureSummits(); + + foreach($summits as $summit){ + $selection_plan = $summit->getCurrentSelectionPlanByStatus($status); + if(!is_null($selection_plan)) return $selection_plan; + } + + return null; + }); + } } \ No newline at end of file diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index 63d99cc9..44d840c4 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -1612,6 +1612,15 @@ class ApiEndpointsSeeder extends Seeder ], ], // selection plans + [ + 'name' => 'get-current-selection-plan-by-status', + 'route' => '/api/v1/summits/all/selection-plans/current/{status}', + 'http_method' => 'GET', + 'scopes' => [ + sprintf(SummitScopes::ReadAllSummitData, $current_realm), + sprintf(SummitScopes::ReadSummitData, $current_realm) + ], + ], [ 'name' => 'get-selection-plan-by-id', 'route' => '/api/v1/summits/{id}/selection-plans/{selection_plan_id}', diff --git a/tests/OAuth2SelectionPlansApiTest.php b/tests/OAuth2SelectionPlansApiTest.php index a4c811f6..009e31a4 100644 --- a/tests/OAuth2SelectionPlansApiTest.php +++ b/tests/OAuth2SelectionPlansApiTest.php @@ -129,4 +129,35 @@ final class OAuth2SelectionPlansApiTest extends ProtectedApiTest $content = $response->getContent(); $this->assertResponseStatus(404); } + + /** + * @param string $status + */ + public function testGetCurrentSelectionPlanByStatus($status = 'submission'){ + + $params = [ + 'status' => $status, + 'expand' => 'summit,track_groups' + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "GET", + "OAuth2SummitSelectionPlansApiController@getCurrentSelectionPlanByStatus", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + $selection_plan = json_decode($content); + $this->assertTrue(!is_null($selection_plan)); + } } \ No newline at end of file