Fixed bugs on Submit presentation flow

* added tags per allowed track
* updated presentation progress
* added presentation progress on presentation serializer
* added new endpoint to mark presentation as completed
PUT /api/v1/summits/{id}/presentations/{presentation_id}/completed

Change-Id: I23ec05341f74498312f074236b691dd6473700b1
This commit is contained in:
Sebastian Marcet 2018-09-26 15:01:33 -03:00
parent 76b206edbe
commit aebbcd1bd4
10 changed files with 214 additions and 3 deletions

View File

@ -284,6 +284,7 @@ final class OAuth2PresentationApiController extends OAuth2ProtectedController
'attending_media' => 'required|boolean', 'attending_media' => 'required|boolean',
'links' => 'sometimes|url_array', 'links' => 'sometimes|url_array',
'extra_questions' => 'sometimes|entity_value_array', 'extra_questions' => 'sometimes|entity_value_array',
'tags' => 'sometimes|string_array',
]; ];
$data = $data->all(); $data = $data->all();
@ -360,6 +361,7 @@ final class OAuth2PresentationApiController extends OAuth2ProtectedController
'attending_media' => 'sometimes|boolean', 'attending_media' => 'sometimes|boolean',
'links' => 'sometimes|url_array', 'links' => 'sometimes|url_array',
'extra_questions' => 'sometimes|entity_value_array', 'extra_questions' => 'sometimes|entity_value_array',
'tags' => 'sometimes|string_array',
]; ];
$data = $data->all(); $data = $data->all();
@ -405,6 +407,51 @@ final class OAuth2PresentationApiController extends OAuth2ProtectedController
} }
} }
/**
* @param $summit_id
* @param $presentation_id
* @return mixed
*/
public function completePresentationSubmission($summit_id, $presentation_id){
try {
$summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id);
if (is_null($summit)) return $this->error404();
$member_id = $this->resource_server_context->getCurrentUserExternalId();
if(is_null($member_id))
return $this->error403();
$member = $this->member_repository->getById($member_id);
if(is_null($member))
return $this->error403();
$presentation = $this->presentation_service->completePresentationSubmission
(
$summit,
$presentation_id,
$member
);
return $this->updated(SerializerRegistry::getInstance()->getSerializer($presentation)->serialize());
}
catch (EntityNotFoundException $ex1)
{
Log::warning($ex1);
return $this->error404();
}
catch (ValidationException $ex2)
{
Log::warning($ex2);
return $this->error412($ex2->getMessages());
}
catch (Exception $ex)
{
Log::error($ex);
return $this->error500($ex);
}
}
/** /**
* @param $presentation_id * @param $presentation_id
* @return mixed * @return mixed

View File

@ -288,6 +288,8 @@ Route::group([
// opened without role CFP - valid selection plan on CFP status // opened without role CFP - valid selection plan on CFP status
Route::put('', 'OAuth2PresentationApiController@updatePresentationSubmission'); Route::put('', 'OAuth2PresentationApiController@updatePresentationSubmission');
Route::put('completed', 'OAuth2PresentationApiController@completePresentationSubmission');
Route::delete('', 'OAuth2PresentationApiController@deletePresentation'); Route::delete('', 'OAuth2PresentationApiController@deletePresentation');
Route::group(['prefix' => 'videos'], function () { Route::group(['prefix' => 'videos'], function () {

View File

@ -27,6 +27,7 @@ class PresentationSerializer extends SummitEventSerializer
'ToRecord' => 'to_record:json_boolean', 'ToRecord' => 'to_record:json_boolean',
'AttendingMedia' => 'attending_media:json_boolean', 'AttendingMedia' => 'attending_media:json_boolean',
'StatusNice' => 'status:json_string', 'StatusNice' => 'status:json_string',
'ProgressNice' => 'progress:json_string',
]; ];
protected static $allowed_fields = [ protected static $allowed_fields = [
@ -38,6 +39,7 @@ class PresentationSerializer extends SummitEventSerializer
'to_record', 'to_record',
'attending_media', 'attending_media',
'status', 'status',
'progress',
]; ];
protected static $allowed_relations = [ protected static $allowed_relations = [

View File

@ -463,6 +463,32 @@ class Presentation extends SummitEvent
return $this->status; return $this->status;
} }
/**
* @return string
*/
public function getProgressNice(){
switch($this->progress){
case self::PHASE_NEW:
return 'NEW';
break;
case self::PHASE_SUMMARY:
return 'SUMMARY';
break;
case self::PHASE_TAGS:
return 'TAGS';
break;
case self::PHASE_SPEAKERS:
return 'SPEAKERS';
break;
case self::PHASE_COMPLETE:
return 'COMPLETE';
break;
default:
return 'NEW';
break;
}
}
/** /**
* @return mixed * @return mixed
*/ */
@ -472,7 +498,7 @@ class Presentation extends SummitEvent
} }
/** /**
* @param mixed $progress * @param int $progress
*/ */
public function setProgress($progress) public function setProgress($progress)
{ {

View File

@ -285,6 +285,17 @@ class PresentationCategory extends SilverstripeBaseModel
$this->allowed_tags->clear(); $this->allowed_tags->clear();
} }
/**
* @param string $tag_value
* @return Tag|null
*/
public function getAllowedTagByVal($tag_value){
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('tag', trim($tag_value)));
$res = $this->allowed_tags->matching($criteria)->first();
return $res === false ? null : $res;
}
/** /**
* @param Tag $tag * @param Tag $tag
* @return $this * @return $this

View File

@ -68,6 +68,17 @@ interface IPresentationService
*/ */
public function updatePresentationSubmission(Summit $summit, $presentation_id, Member $member, array $data); public function updatePresentationSubmission(Summit $summit, $presentation_id, Member $member, array $data);
/**
* @param Summit $summit
* @param int $presentation_id
* @param Member $member
* @return Presentation
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function completePresentationSubmission(Summit $summit, $presentation_id, Member $member);
/** /**
* @param Member $member * @param Member $member
* @param int $presentation_id * @param int $presentation_id

View File

@ -20,6 +20,7 @@ use App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackAnswer
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use models\exceptions\EntityNotFoundException; use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException; use models\exceptions\ValidationException;
use models\main\ITagRepository;
use models\main\Member; use models\main\Member;
use models\summit\factories\IPresentationVideoFactory; use models\summit\factories\IPresentationVideoFactory;
use models\summit\ISpeakerRepository; use models\summit\ISpeakerRepository;
@ -49,23 +50,38 @@ final class PresentationService
*/ */
private $video_factory; private $video_factory;
/** /**
* @var ISpeakerRepository * @var ISpeakerRepository
*/ */
private $speaker_repository; private $speaker_repository;
/**
* @var ITagRepository
*/
private $tag_repository;
/**
* PresentationService constructor.
* @param IPresentationVideoFactory $video_factory
* @param ISummitEventRepository $presentation_repository
* @param ISpeakerRepository $speaker_repository
* @param ITagRepository $tag_repository
* @param ITransactionService $tx_service
*/
public function __construct public function __construct
( (
IPresentationVideoFactory $video_factory, IPresentationVideoFactory $video_factory,
ISummitEventRepository $presentation_repository, ISummitEventRepository $presentation_repository,
ISpeakerRepository $speaker_repository, ISpeakerRepository $speaker_repository,
ITagRepository $tag_repository,
ITransactionService $tx_service ITransactionService $tx_service
) )
{ {
parent::__construct($tx_service); parent::__construct($tx_service);
$this->presentation_repository = $presentation_repository; $this->presentation_repository = $presentation_repository;
$this->speaker_repository = $speaker_repository; $this->speaker_repository = $speaker_repository;
$this->tag_repository = $tag_repository;
$this->video_factory = $video_factory; $this->video_factory = $video_factory;
} }
@ -282,19 +298,25 @@ final class PresentationService
$presentation = $summit->getEvent($presentation_id); $presentation = $summit->getEvent($presentation_id);
if (is_null($presentation)) if (is_null($presentation))
throw new EntityNotFoundException(trans( throw new EntityNotFoundException(trans(
'not_found_errors.PresentationService.updatePresentationSubmission.PresentationNotFound', 'not_found_errors.PresentationService.updatePresentationSubmission.PresentationNotFound',
['presentation_id' => $presentation_id] ['presentation_id' => $presentation_id]
)); ));
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException(trans(
'not_found_errors.PresentationService.updatePresentationSubmission.PresentationNotFound',
['presentation_id' => $presentation_id]
));
if(!$presentation->canEdit($current_speaker)) if(!$presentation->canEdit($current_speaker))
throw new ValidationException(trans( throw new ValidationException(trans(
'validation_errors.PresentationService.updatePresentationSubmission.CurrentSpeakerCanNotEditPresentation', 'validation_errors.PresentationService.updatePresentationSubmission.CurrentSpeakerCanNotEditPresentation',
['presentation_id' => $presentation_id] ['presentation_id' => $presentation_id]
)); ));
return $this->saveOrUpdatePresentation return $this->saveOrUpdatePresentation
( (
$summit, $summit,
@ -387,6 +409,31 @@ final class PresentationService
// add me as speaker // add me as speaker
$presentation->addSpeaker($current_speaker); $presentation->addSpeaker($current_speaker);
if (isset($data['tags'])) {
$presentation->clearTags();
if(count($data['tags']) > 0){
if($presentation->getProgress() == Presentation::PHASE_SUMMARY)
$presentation->setProgress(Presentation::PHASE_TAGS);
}
foreach ($data['tags'] as $tag_value) {
$tag = $track->getAllowedTagByVal($tag_value);
if(is_null($tag)){
throw new ValidationException(
trans(
'validation_errors.PresentationService.saveOrUpdatePresentation.TagNotAllowed',
[
'tag' => $tag_value,
'track_id' => $track->getId()
]
)
);
}
$presentation->addTag($tag);
}
}
if (isset($data['links'])) { if (isset($data['links'])) {
$presentation->clearLinks(); $presentation->clearLinks();
@ -476,4 +523,46 @@ final class PresentationService
$this->event_repository->delete($presentation); $this->event_repository->delete($presentation);
}); });
} }
/**
* @param Summit $summit
* @param int $presentation_id
* @param Member $member
* @return Presentation
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function completePresentationSubmission(Summit $summit, $presentation_id, Member $member)
{
return $this->tx_service->transaction(function () use ($summit, $member, $presentation_id) {
$current_speaker = $this->speaker_repository->getByMember($member);
if(is_null($current_speaker))
throw new EntityNotFoundException(sprintf("member %s does not has a speaker profile", $member->getId()));
$presentation = $summit->getEvent($presentation_id);
if(is_null($presentation))
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if(!$presentation instanceof Presentation)
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if(!$presentation->canEdit($current_speaker))
throw new ValidationException(sprintf("member %s can not edit presentation %s",
$member->getId(),
$presentation_id
));
if($presentation->getProgress() != Presentation::PHASE_SPEAKERS){
throw new ValidationException
(
sprintf("presentation %s is not allowed to mark as completed", $presentation_id)
);
}
$presentation->setProgress(Presentation::PHASE_COMPLETE);
return $presentation;
});
}
} }

View File

@ -1691,6 +1691,9 @@ final class SummitService extends AbstractService implements ISummitService
if (is_null($speaker)) if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id)); throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if($presentation->getProgress() == Presentation::PHASE_TAGS)
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->addSpeaker($speaker); $presentation->addSpeaker($speaker);
}); });
} }
@ -1732,6 +1735,9 @@ final class SummitService extends AbstractService implements ISummitService
if (is_null($speaker)) if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id)); throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if($presentation->getProgress() == Presentation::PHASE_TAGS)
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->removeSpeaker($speaker); $presentation->removeSpeaker($speaker);
}); });
} }
@ -1772,6 +1778,9 @@ final class SummitService extends AbstractService implements ISummitService
if (is_null($speaker)) if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id)); throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if($presentation->getProgress() == Presentation::PHASE_TAGS)
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->setModerator($speaker); $presentation->setModerator($speaker);
}); });
} }
@ -1813,6 +1822,9 @@ final class SummitService extends AbstractService implements ISummitService
if (is_null($speaker)) if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id)); throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if($presentation->getProgress() == Presentation::PHASE_TAGS)
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->unsetModerator(); $presentation->unsetModerator();
}); });
} }

View File

@ -1471,6 +1471,16 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::WritePresentationData, $current_realm) sprintf(SummitScopes::WritePresentationData, $current_realm)
], ],
], ],
[
'name' => 'complete-submit-presentation',
'route' => '/api/v1/summits/{id}/presentations/{presentation_id}/completed',
'http_method' => 'PUT',
'scopes' => [
sprintf(SummitScopes::WriteSummitData, $current_realm),
sprintf(SummitScopes::WriteEventData, $current_realm),
sprintf(SummitScopes::WritePresentationData, $current_realm)
],
],
[ [
'name' => 'delete-submit-presentation', 'name' => 'delete-submit-presentation',
'route' => '/api/v1/summits/{id}/presentations/{presentation_id}', 'route' => '/api/v1/summits/{id}/presentations/{presentation_id}',

View File

@ -89,6 +89,7 @@ return [
'PresentationService.updatePresentationSubmission.NotValidSpeaker' => 'Current Member not has a valid speaker profile', 'PresentationService.updatePresentationSubmission.NotValidSpeaker' => 'Current Member not has a valid speaker profile',
'PresentationService.updatePresentationSubmission.NotValidSelectionPlan' => 'Current Summit not has a valid selection plan', 'PresentationService.updatePresentationSubmission.NotValidSelectionPlan' => 'Current Summit not has a valid selection plan',
'PresentationService.updatePresentationSubmission.CurrentSpeakerCanNotEditPresentation' => 'Current Speaker can not edit :presentation_id presentation', 'PresentationService.updatePresentationSubmission.CurrentSpeakerCanNotEditPresentation' => 'Current Speaker can not edit :presentation_id presentation',
'PresentationService.saveOrUpdatePresentation.TagNotAllowed' => 'tag :tag is not allowed on track :track_id',
// organizations // organizations
'OrganizationService.addOrganization.alreadyExistName' => 'Organization name :name already exists!', 'OrganizationService.addOrganization.alreadyExistName' => 'Organization name :name already exists!',
// track tag groups // track tag groups