Import Session data through CSV file

Minimal data required

title ( text )
abstract (text )
type_id (int) or type (string type name)
track_id (int) or track ( string track name)

Optional fields

social_summary ( text )
attendees_expected_learnt ( text ) * provided only if event is presentation type
location (id or name)
level (text possible values [N/A, Beginner, Intermediate, Advanced]) * provided only if event is presentation type
allow_feedback (int possible values [0,1])
to_record (int possible values [0,1])
tags ( repetitive group: values | delimited)
sponsors ( repetitive group : names | delimited )
speakers ( repetitive group:  emails | delimited )
start_date (mandatory if is_published is 1) Y-m-d H:i:s format ( summit time zone )
end_date (mandatory if is_published is 1)  Y-m-d H:i:s format ( summit time zone )
is_published (int possible values [0,1]) , if this field is set 1 start_date and end_date are mandatories
selection_plan (string  selection plan name)  * provided only if event is presentation type
attendees_expected_learnt ( text , only presentations)
problem_addressed ( text , only presentations)

added endpoint

POST /api/v1/summits/{summit_id}/events/csv

multipart/form-data

* Expected params

file
send_speaker_email

Change-Id: I245d4e1a22e2c5e3c4120c559d0ecb4a5a021c78
Signed-off-by: smarcet <smarcet@gmail.com>
This commit is contained in:
smarcet 2020-09-24 15:24:05 -03:00
parent c14491545c
commit 3cd4efefba
24 changed files with 1028 additions and 100 deletions

View File

@ -13,6 +13,7 @@
**/
use App\Http\Utils\BooleanCellFormatter;
use App\Http\Utils\EpochCellFormatter;
use App\Http\Utils\MultipartFormDataCleaner;
use Exception;
use Illuminate\Http\Request as LaravelRequest;
use Illuminate\Support\Facades\Input;
@ -1358,4 +1359,56 @@ final class OAuth2SummitEventsApiController extends OAuth2ProtectedController
}
}
/**
* @param LaravelRequest $request
* @param $summit_id
*/
public function importEventData(LaravelRequest $request, $summit_id){
try {
$summit = SummitFinderStrategyFactory::build($this->repository, $this->getResourceServerContext())->find($summit_id);
if (is_null($summit)) return $this->error404();
$current_member = $this->resource_server_context->getCurrentUser();
if (is_null($current_member)) return $this->error403();
$payload = $request->all();
$rules = [
'file' => 'required',
'send_speaker_email' => 'required|boolean',
];
$payload = MultipartFormDataCleaner::cleanBool('send_speaker_email', $payload);
// Creates a Validator instance and validates the data.
$validation = Validator::make($payload, $rules);
if ($validation->fails()) {
$ex = new ValidationException;
$ex->setMessages($validation->messages()->toArray());
throw $ex;
}
$file = $request->file('file');
$this->service->importEventData($summit, $file, $payload);
return $this->ok();
} catch (EntityNotFoundException $ex1) {
Log::warning($ex1);
return $this->error404();
} catch (ValidationException $ex2) {
Log::warning($ex2);
return $this->error412(array($ex2->getMessage()));
} catch (\HTTP401UnauthorizedException $ex3) {
Log::warning($ex3);
return $this->error401();
} catch (\Exception $ex) {
Log::error($ex);
return $this->error500($ex);
}
}
}

View File

@ -256,7 +256,12 @@ Route::group([
Route::group(array('prefix' => 'events'), function () {
Route::get('',[ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@getEvents']);
Route::get('csv',[ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@getEventsCSV']);
Route::group(['prefix' => 'csv'], function () {
Route::get('',[ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@getEventsCSV']);
Route::post('', [ 'middleware' => 'auth.user', 'uses' =>'OAuth2SummitEventsApiController@importEventData']);
});
// bulk actions
Route::delete('/publish', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@unPublishEvents']);
Route::put('/publish', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@updateAndPublishEvents']);

View File

@ -0,0 +1,94 @@
<?php namespace App\Jobs\Emails\PresentationSubmissions;
/**
* Copyright 2020 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Jobs\Emails\AbstractEmailJob;
use Illuminate\Support\Facades\Config;
use models\summit\Presentation;
use models\summit\PresentationSpeaker;
/**
* Class ImportEventSpeakerEmail
* @package App\Jobs\Emails\PresentationSubmissions
*/
class ImportEventSpeakerEmail extends AbstractEmailJob
{
protected function getEmailEventSlug(): string
{
return self::EVENT_SLUG;
}
// metadata
const EVENT_SLUG = 'SUMMIT_SUBMISSIONS_IMPORT_EVENT_SPEAKER';
const EVENT_NAME = 'SUMMIT_SUBMISSIONS_IMPORT_EVENT_SPEAKER';
const DEFAULT_TEMPLATE = 'SUMMIT_SUBMISSIONS_IMPORT_EVENT_SPEAKER';
/**
* ImportEventSpeakerEmail constructor.
* @param Presentation $presentation
* @param PresentationSpeaker $speaker
* @param string|null $setPasswordLink
*/
public function __construct(Presentation $presentation, PresentationSpeaker $speaker, ?string $setPasswordLink)
{
$summit = $presentation->getSummit();
$creator = $presentation->getCreator();
$selection_plan = $presentation->getSelectionPlan();
$speaker_management_base_url = Config::get('cfp.base_url');
$idp_base_url = Config::get('idp.base_url');
$support_email = $summit->getSupportEmail();
$support_email = !empty($support_email) ? $support_email: Config::get("cfp.support_email", null);
if(empty($speaker_management_base_url))
throw new \InvalidArgumentException('cfp.base_url is null.');
if(empty($idp_base_url))
throw new \InvalidArgumentException('idp.base_url is null.');
if(empty($support_email))
throw new \InvalidArgumentException('cfp.support_email is null.');
$payload = [];
$payload['summit_name'] = $summit->getName();
$payload['summit_logo'] = $summit->getLogoUrl();
$payload['creator_full_name'] = is_null($creator) ? '' : $creator->getFullName();
$payload['creator_email'] = is_null($creator) ? '': $creator->getEmail();
$payload['presentation_name'] = $presentation->getTitle();
$payload['presentation_start_date'] = $presentation->getStartDateNice();
$payload['presentation_end_date'] = $presentation->getEndDateNice();
$payload['presentation_location'] = $presentation->getLocationName();
$payload['selection_plan_name'] = is_null($selection_plan) ? '': $selection_plan->getName();
$payload['presentation_edit_link'] = $presentation->getEditLink();
$payload['summit_date'] = $summit->getMonthYear();
$payload['until_date'] =is_null($selection_plan) ? '' : $selection_plan->getSubmissionEndDate()->format('d F, Y');
$payload['selection_process_link'] = sprintf("%s/app/%s", $speaker_management_base_url, $summit->getRawSlug());
$payload['speaker_management_link'] = sprintf("%s/app/%s", $speaker_management_base_url, $summit->getRawSlug());
$payload['bio_edit_link'] = sprintf("%s/app/%s/profile", $speaker_management_base_url, $summit->getRawSlug());
if(!empty($setPasswordLink)){
$payload['bio_edit_link'] = $setPasswordLink;
}
$payload['reset_password_link'] = sprintf("%s/auth/password/reset", $idp_base_url);
$payload['support_email'] = $support_email;
$payload['speaker_full_name'] = $speaker->getFullName(' ');
if(empty($payload['speaker_full_name'])){
$payload['speaker_full_name'] = $speaker->getEmail();
}
$payload['speaker_email'] = $speaker->getEmail();
$template_identifier = $this->getEmailTemplateIdentifierFromEmailEvent($summit);
parent::__construct($payload, $template_identifier, $payload['speaker_email']);
}
}

View File

@ -0,0 +1,80 @@
<?php namespace App\Jobs;
/**
* Copyright 2020 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use models\exceptions\ValidationException;
use services\model\ISummitService;
/**
* Class ProcessEventDataImport
* @package App\Jobs
*/
class ProcessEventDataImport implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 2;
public $timeout = PHP_INT_MAX;
/**
* @var int
*/
private $summit_id;
/**
* @var string
*/
private $filename;
/**
* @var boolean
*/
private $send_speaker_email;
/**
* ProcessEventDataImport constructor.
* @param int $summit_id
* @param string $filename
* @param array $payload
*/
public function __construct(int $summit_id, string $filename, array $payload)
{
Log::debug(sprintf("ProcessEventDataImport::__construct"));
$this->summit_id = $summit_id;
$this->filename = $filename;
$this->send_speaker_email = boolval($payload['send_speaker_email']);
}
/**
* @param ISummitService $service
*/
public function handle
(
ISummitService $service
)
{
try {
Log::debug(sprintf("ProcessEventDataImport::handle summit %s filename %s send_speaker_email %s", $this->summit_id, $this->filename, $this->send_speaker_email));
$service->processEventData($this->summit_id, $this->filename, $this->send_speaker_email);
} catch (ValidationException $ex) {
Log::warning($ex);
} catch (\Exception $ex) {
Log::error($ex);
}
}
}

View File

@ -648,17 +648,6 @@ class SummitEvent extends SilverstripeBaseModel
if ($end_date < $start_date)
throw new ValidationException('start datetime must be greather or equal than end datetime!');
if (!$summit->isEventInsideSummitDuration($this))
throw new ValidationException
(
sprintf
(
'start/end datetime must be between summit start/end datetime! (%s - %s)',
$summit->getLocalBeginDate()->format('Y-m-d H:i:s'),
$summit->getLocalEndDate()->format('Y-m-d H:i:s')
)
);
$this->published = true;
$this->published_date = new DateTime();
}

View File

@ -52,4 +52,10 @@ interface ISpeakerRepository extends IBaseRepository
* @return PresentationSpeaker|null
*/
public function getByFullName(string $fullname):?PresentationSpeaker;
/**
* @param string $email
* @return PresentationSpeaker|null
*/
public function getByEmail(string $email):?PresentationSpeaker;
}

View File

@ -147,7 +147,7 @@ class PresentationSpeaker extends SilverstripeBaseModel
private $promo_codes;
/**
* @ORM\ManyToMany(targetEntity="models\summit\Presentation", mappedBy="speakers")
* @ORM\ManyToMany(targetEntity="models\summit\Presentation", mappedBy="speakers", cascade={"persist"})
* @var Presentation[]
*/
private $presentations;
@ -1092,12 +1092,13 @@ class PresentationSpeaker extends SilverstripeBaseModel
}
/**
* @param string $separator
* @return string
*/
public function getFullName(){
public function getFullName(string $separator=', '){
$fullname = $this->first_name;
if(!empty($this->last_name)){
if(!empty($fullname)) $fullname .= ', ';
if(!empty($fullname)) $fullname .= $separator;
$fullname .= $this->last_name;
}
return $fullname;

View File

@ -1125,6 +1125,18 @@ class Summit extends SilverstripeBaseModel
return $event === false ? null : $event;
}
/**
* @param int $id
* @return SummitEvent|null
*/
public function getEventById(int $id):?SummitEvent{
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('id', $id));
$event = $this->events->matching($criteria)->first();
return $event === false ? null : $event;
}
/**
* @param SummitEvent $event
*/

View File

@ -759,6 +759,27 @@ SQL;
->getOneOrNullResult();
}
/**
* @param string $email
* @return PresentationSpeaker|null
*/
public function getByEmail(string $email):?PresentationSpeaker
{
return $this->getEntityManager()
->createQueryBuilder()
->select("s")
->from(PresentationSpeaker::class, "s")
->leftJoin("s.member", "m")
->leftJoin("s.registration_request", "r")
->where("m.email = :email1 or r.email = :email2")
->setParameter("email1", trim($email))
->setParameter("email2", trim($email))
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
/**
* @param string $fullname
* @return PresentationSpeaker|null

View File

@ -38,10 +38,11 @@ interface ISpeakerService
/**
* @param array $data
* @param null|Member $creator
* @param bool $send_email
* @return PresentationSpeaker
* @throws ValidationException
*/
public function addSpeaker(array $data, ?Member $creator = null);
public function addSpeaker(array $data, ?Member $creator = null, $send_email = true);
/**
* @param Summit $summit

View File

@ -498,4 +498,20 @@ interface ISummitService
* @throws EntityNotFoundException
*/
public function advanceSummit(int $summit_id, int $days, bool $negative = false, $check_summit_ends = true):void;
/**
* @param Summit $summit
* @param UploadedFile $csv_file
* @param array $payload
*/
public function importEventData(Summit $summit, UploadedFile $csv_file, array $payload):void;
/**
* @param int $summit_id
* @param string $filename
* @param bool $send_speaker_email
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function processEventData(int $summit_id, string $filename, bool $send_speaker_email):void;
}

View File

@ -29,6 +29,7 @@ use models\main\IOrganizationRepository;
use models\main\Member;
use DateTime;
use models\main\Organization;
use models\summit\ISpeakerRegistrationRequestRepository;
/**
* Class MemberService
@ -71,6 +72,11 @@ final class MemberService
*/
private $external_user_api;
/**
* @var ISpeakerRegistrationRequestRepository
*/
private $speaker_registration_request_repository;
/**
* MemberService constructor.
* @param IMemberRepository $member_repository
@ -79,6 +85,7 @@ final class MemberService
* @param IGroupRepository $group_repository
* @param ICacheService $cache_service
* @param IExternalUserApi $external_user_api
* @param ISpeakerRegistrationRequestRepository $speaker_registration_request_repository
* @param ITransactionService $tx_service
*/
public function __construct
@ -89,6 +96,7 @@ final class MemberService
IGroupRepository $group_repository,
ICacheService $cache_service,
IExternalUserApi $external_user_api,
ISpeakerRegistrationRequestRepository $speaker_registration_request_repository,
ITransactionService $tx_service
)
{
@ -99,6 +107,7 @@ final class MemberService
$this->group_repository = $group_repository;
$this->cache_service = $cache_service;
$this->external_user_api = $external_user_api;
$this->speaker_registration_request_repository = $speaker_registration_request_repository;
}
/**
@ -319,6 +328,18 @@ final class MemberService
}
$this->synchronizeGroups($member, $user_data['groups']);
// check speaker registration request by email and no member set
Log::debug(sprintf("MemberService::registerExternalUserById trying to get former registration request by email %s", $email));
$request = $this->speaker_registration_request_repository->getByEmail($email);
if(!is_null($request)){
Log::debug(sprintf("MemberService::registerExternalUserById got former registration request by email %s", $email));
$speaker = $request->getSpeaker();
if(!is_null($speaker))
if(!$speaker->hasMember()) {
Log::debug(sprintf("MemberService::registerExternalUserById setting current member to speaker %s", $speaker->getId()));
$speaker->setMember($member);
}
}
if($is_new)
Event::fire(new NewMember($member->getId()));

View File

@ -163,13 +163,14 @@ final class SpeakerService
/**
* @param array $data
* @param null|Member $creator
* @param bool $send_email
* @return PresentationSpeaker
* @throws ValidationException
*/
public function addSpeaker(array $data, ?Member $creator = null)
public function addSpeaker(array $data, ?Member $creator = null, $send_email = true)
{
return $this->tx_service->transaction(function () use ($data, $creator) {
return $this->tx_service->transaction(function () use ($data, $creator, $send_email) {
$member_id = intval($data['member_id'] ?? 0);
@ -251,7 +252,7 @@ final class SpeakerService
$this->speaker_repository->add($this->updateSpeakerRelations($speaker, $data));
// only send the email if we dont have a former registration request
if(is_null($formerRegistrationRequest))
if(is_null($formerRegistrationRequest) && $send_email)
SpeakerCreationEmail::dispatch($speaker);
if(!is_null($formerRegistrationRequest)){

View File

@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use League\Csv\Reader;
use App\Events\MyFavoritesAdd;
use App\Events\MyFavoritesRemove;
use App\Events\MyScheduleAdd;
@ -21,7 +21,9 @@ use App\Events\RSVPUpdated;
use App\Events\SummitDeleted;
use App\Events\SummitUpdated;
use App\Http\Utils\IFileUploader;
use App\Jobs\Emails\PresentationSubmissions\ImportEventSpeakerEmail;
use App\Jobs\Emails\Schedule\ShareEventEmail;
use App\Jobs\ProcessEventDataImport;
use App\Models\Foundation\Summit\Factories\SummitEventFeedbackFactory;
use App\Models\Foundation\Summit\Factories\SummitFactory;
use App\Models\Foundation\Summit\Factories\SummitRSVPFactory;
@ -30,11 +32,15 @@ use App\Models\Utils\IntervalParser;
use App\Permissions\IPermissionsManager;
use App\Services\Model\AbstractService;
use App\Services\Model\IFolderService;
use App\Services\Model\IMemberService;
use App\Services\Utils\CSVReader;
use CalDAVClient\Facade\Utils\ICalTimeZoneBuilder;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\main\File;
@ -60,6 +66,7 @@ use models\summit\ISummitEntityEventRepository;
use models\summit\ISummitEventRepository;
use models\summit\ISummitRepository;
use models\summit\Presentation;
use models\summit\PresentationSpeaker;
use models\summit\PresentationType;
use models\summit\RSVP;
use models\summit\Summit;
@ -190,6 +197,16 @@ final class SummitService extends AbstractService implements ISummitService
*/
private $resource_server_context;
/**
* @var ISpeakerService ISpeakerService
*/
private $speaker_service;
/**
* @var IMemberService
*/
private $member_service;
/**
* SummitService constructor.
* @param ISummitRepository $summit_repository
@ -210,6 +227,8 @@ final class SummitService extends AbstractService implements ISummitService
* @param IPermissionsManager $permissions_manager
* @param IFileUploader $file_uploader
* @param IResourceServerContext $resource_server_context
* @param ISpeakerService $speaker_service
* @param IMemberService $member_service
* @param ITransactionService $tx_service
*/
public function __construct
@ -232,6 +251,8 @@ final class SummitService extends AbstractService implements ISummitService
IPermissionsManager $permissions_manager,
IFileUploader $file_uploader,
IResourceServerContext $resource_server_context,
ISpeakerService $speaker_service,
IMemberService $member_service,
ITransactionService $tx_service
)
{
@ -254,6 +275,8 @@ final class SummitService extends AbstractService implements ISummitService
$this->permissions_manager = $permissions_manager;
$this->file_uploader = $file_uploader;
$this->resource_server_context = $resource_server_context;
$this->speaker_service = $speaker_service;
$this->member_service = $member_service;
}
/**
@ -632,19 +655,6 @@ final class SummitService extends AbstractService implements ISummitService
// set local time from UTC
$event->setStartDate($start_datetime);
$event->setEndDate($end_datetime);
if (!$summit->isEventInsideSummitDuration($event))
throw new ValidationException
(
sprintf
(
"event start/end (%s - %s) does not match with summit start/end (%s - %s)",
$event->getLocalStartDate()->format('Y-m-d H:i:s'),
$event->getLocalEndDate()->format('Y-m-d H:i:s'),
$summit->getLocalBeginDate()->format('Y-m-d H:i:s'),
$summit->getLocalEndDate()->format('Y-m-d H:i:s')
)
);
}
return $event;
@ -2121,7 +2131,7 @@ final class SummitService extends AbstractService implements ISummitService
{
return $this->tx_service->transaction(function () use ($summit_id, $file, $max_file_size) {
$allowed_extensions = ['png', 'jpg', 'jpeg', 'gif', 'svg','jfif'];
$allowed_extensions = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'jfif'];
$summit = $this->summit_repository->getById($summit_id);
@ -2591,19 +2601,19 @@ final class SummitService extends AbstractService implements ISummitService
* @param bool $check_summit_ends
* @throws Exception
*/
public function advanceSummit(int $summit_id, int $days, bool $negative = false, $check_summit_ends = true):void
public function advanceSummit(int $summit_id, int $days, bool $negative = false, $check_summit_ends = true): void
{
$interval = new DateInterval(sprintf("P%sD", $days));
Log::debug(sprintf("SummitService::advanceSummit summit_id %s days %s negative %s check_summit_ends %s", $summit_id, $days, $negative, $check_summit_ends));
$events_ids = $this->tx_service->transaction(function () use ($summit_id, $interval, $negative, $check_summit_ends) {
$events_ids = $this->tx_service->transaction(function () use ($summit_id, $interval, $negative, $check_summit_ends) {
$summit = $this->summit_repository->getByIdExclusiveLock($summit_id);
if(is_null($summit) || !$summit instanceof Summit)
if (is_null($summit) || !$summit instanceof Summit)
throw new EntityNotFoundException("Summit not found");
if($check_summit_ends && !$summit->isEnded()) {
if ($check_summit_ends && !$summit->isEnded()) {
Log::debug(sprintf("SummitService::advanceSummit summit %s has not ended !.", $summit_id));
return [];
}
@ -2614,10 +2624,9 @@ final class SummitService extends AbstractService implements ISummitService
if (!is_null($summitBeginDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit begin date for summit %s is %s", $summit_id, $summitBeginDate->format("Ymd His")));
if($negative){
if ($negative) {
$summit->setRawBeginDate(clone $summitBeginDate->sub($interval));
}
else {
} else {
$summit->setRawBeginDate(clone $summitBeginDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit New Summit begin date for summit %s is %s", $summit_id, $summit->getBeginDate()->format("Ymd His")));
@ -2625,10 +2634,9 @@ final class SummitService extends AbstractService implements ISummitService
if (!is_null($summitEndDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit end date for summit %s is %s", $summit_id, $summitEndDate->format("Ymd His")));
if($negative) {
if ($negative) {
$summit->setRawEndDate(clone $summitEndDate->sub($interval));
}
else{
} else {
$summit->setRawEndDate(clone $summitEndDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit New Summit end date for summit %s is %s", $summit_id, $summit->getEndDate()->format("Ymd His")));
@ -2639,18 +2647,17 @@ final class SummitService extends AbstractService implements ISummitService
$summitRegistrationBeginDate = $summit->getRegistrationBeginDate();
$summitRegistrationEndDate = $summit->getRegistrationEndDate();
if(!is_null($summitRegistrationBeginDate)){
if (!is_null($summitRegistrationBeginDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit registration begin date for summit %s is %s", $summit_id, $summitRegistrationBeginDate->format("Ymd His")));
if($negative) {
if ($negative) {
$summit->setRawRegistrationBeginDate(clone $summitRegistrationBeginDate->add($interval));
}
else {
} else {
$summit->setRawRegistrationBeginDate(clone $summitRegistrationBeginDate->sub($interval));
}
Log::debug(sprintf("SummitService::advanceSummit New Summit registration begin date for summit %s is %s", $summit_id, $summit->getRegistrationBeginDate()->format("Ymd His")));
}
if(!is_null($summitRegistrationEndDate)){
if (!is_null($summitRegistrationEndDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit registration end date for summit %s is %s", $summit_id, $summitRegistrationEndDate->format("Ymd His")));
$summit->setRawRegistrationEndDate(clone $summitRegistrationEndDate->add($interval));
Log::debug(sprintf("SummitService::advanceSummit New Summit registration end date for summit %s is %s", $summit_id, $summit->getRegistrationEndDate()->format("Ymd His")));
@ -2659,51 +2666,46 @@ final class SummitService extends AbstractService implements ISummitService
// random dates
$summitReassignTicketTillDate = $summit->getReassignTicketTillDate();
if(!is_null($summitReassignTicketTillDate)){
if($negative){
if (!is_null($summitReassignTicketTillDate)) {
if ($negative) {
$summit->setRawReassignTicketTillDate(clone $summitReassignTicketTillDate->sub($interval));
}
else {
} else {
$summit->setRawReassignTicketTillDate(clone $summitReassignTicketTillDate->add($interval));
}
}
$summitStartShowingVenuesDate = $summit->getStartShowingVenuesDate();
if(!is_null($summitStartShowingVenuesDate)){
if($negative){
if (!is_null($summitStartShowingVenuesDate)) {
if ($negative) {
$summit->setRawStartShowingVenuesDate(clone $summitStartShowingVenuesDate->sub($interval));
}
else {
} else {
$summit->setRawStartShowingVenuesDate(clone $summitStartShowingVenuesDate->add($interval));
}
}
$summitScheduleDefaultStartDate = $summit->getScheduleDefaultStartDate();
if(!is_null($summitScheduleDefaultStartDate)){
if($negative){
if (!is_null($summitScheduleDefaultStartDate)) {
if ($negative) {
$summit->setRawScheduleDefaultStartDate(clone $summitScheduleDefaultStartDate->sub($interval));
}
else {
} else {
$summit->setRawScheduleDefaultStartDate(clone $summitScheduleDefaultStartDate->add($interval));
}
}
$summitBeginAllowBookingDate = $summit->getBeginAllowBookingDate();
if(!is_null($summitBeginAllowBookingDate)){
if($negative) {
if (!is_null($summitBeginAllowBookingDate)) {
if ($negative) {
$summit->setRawBeginAllowBookingDate(clone $summitBeginAllowBookingDate->sub($interval));
}
else{
} else {
$summit->setRawBeginAllowBookingDate(clone $summitBeginAllowBookingDate->add($interval));
}
}
$summitEndAllowBookingDate = $summit->getEndAllowBookingDate();
if(!is_null($summitEndAllowBookingDate)){
if($negative) {
if (!is_null($summitEndAllowBookingDate)) {
if ($negative) {
$summit->setRawEndAllowBookingDate(clone $summitEndAllowBookingDate->sub($interval));
}
else{
} else {
$summit->setRawEndAllowBookingDate(clone $summitEndAllowBookingDate->add($interval));
}
@ -2711,37 +2713,35 @@ final class SummitService extends AbstractService implements ISummitService
// schedule
$event_ids = [];
foreach($summit->getPublishedEvents() as $event){
if(!$event instanceof SummitEvent) continue;
foreach ($summit->getPublishedEvents() as $event) {
if (!$event instanceof SummitEvent) continue;
$event_ids[] = $event->getId();
}
return $event_ids;
});
foreach ($events_ids as $event_id){
$this->tx_service->transaction(function() use($summit_id, $event_id, $interval, $negative){
foreach ($events_ids as $event_id) {
$this->tx_service->transaction(function () use ($summit_id, $event_id, $interval, $negative) {
$event = $this->event_repository->getByIdExclusiveLock($event_id);
$eventBeginDate = $event->getStartDate();
$eventEndDate = $event->getEndDate();
if(is_null($eventBeginDate) || is_null($eventEndDate)){
if (is_null($eventBeginDate) || is_null($eventEndDate)) {
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s ( has not set dates but is published!), skipping it", $summit_id, $event->getId()));
return;
}
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s current start date %s", $summit_id, $event->getId(), $eventBeginDate->format("Ymd His")));
if($negative){
if ($negative) {
$event->setRawStartDate(clone $eventBeginDate->sub($interval));
}
else {
} else {
$event->setRawStartDate(clone $eventBeginDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s new start date %s", $summit_id, $event->getId(), $event->getStartDate()->format("Ymd His")));
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s current end date %s", $summit_id, $event->getId(), $eventEndDate->format("Ymd His")));
if($negative) {
if ($negative) {
$event->setRawEndDate(clone $eventEndDate->sub($interval));
}
else{
} else {
$event->setRawEndDate(clone $eventEndDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s new end date %s", $summit_id, $event->getId(), $event->getEndDate()->format("Ymd His")));
@ -2749,4 +2749,362 @@ final class SummitService extends AbstractService implements ISummitService
});
}
}
/**
* @param Summit $summit
* @param UploadedFile $csv_file
* @param array $payload
* @throws ValidationException
*/
public function importEventData(Summit $summit, UploadedFile $csv_file, array $payload): void
{
Log::debug(sprintf("SummitService::importEventData - summit %s", $summit->getId()));
$allowed_extensions = ['txt'];
if (!in_array($csv_file->extension(), $allowed_extensions)) {
throw new ValidationException("file does not has a valid extension ('csv').");
}
$real_path = $csv_file->getRealPath();
$filename = pathinfo($real_path);
$filename = $filename['filename'] ?? sprintf("file%s", time());
$basename = sprintf("%s_%s.csv", $filename, time());
$filename = $csv_file->storeAs(sprintf("%s/events_imports", sys_get_temp_dir()), $basename);
$csv_data = \Illuminate\Support\Facades\File::get($real_path);
if (empty($csv_data))
throw new ValidationException("file content is empty!");
$csv = Reader::createFromString($csv_data);
$csv->setHeaderOffset(0);
$header = $csv->getHeader(); //returns the CSV header record
// check needed columns (headers names)
/*
columns ( min)
* title
* abstract
* type_id (int) or type (string type name)
* track_id (int) or track ( string track name)
*/
// validate format with col names
if (!in_array("title", $header))
throw new ValidationException('title column missing');
if (!in_array("abstract", $header))
throw new ValidationException('abstract column missing');
$type_data_present = in_array("type_id", $header)||
in_array("type", $header);
if (!$type_data_present) {
throw new ValidationException('type_id / type column missing');
}
$track_present = in_array("track_id", $header)||
in_array("track", $header);
if (!$track_present) {
throw new ValidationException('track_id / track column missing');
}
ProcessEventDataImport::dispatch($summit->getId(), $filename, $payload);
}
/**
* @param int $summit_id
* @param string $filename
* @param bool $send_speaker_email
* @throws ValidationException
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function processEventData(int $summit_id, string $filename, bool $send_speaker_email): void
{
Log::debug(sprintf("SummitService::processEventData summit %s filename %s", $summit_id, $filename));
if (!Storage::disk('local')->exists($filename)) {
throw new ValidationException(sprintf("file %s does not exists.", $filename));
}
$csv_data = Storage::disk('local')->get($filename);
$summit = $this->tx_service->transaction(function () use ($summit_id) {
$summit = $this->summit_repository->getById($summit_id);
if (is_null($summit) || !$summit instanceof Summit)
throw new EntityNotFoundException(sprintf("summit %s does not exists.", $summit_id));
return $summit;
});
$csv = Reader::createFromString($csv_data);
$csv->setHeaderOffset(0);
$header = $csv->getHeader(); //returns the CSV header record
$records = $csv->getRecords();
foreach ($records as $idx => $row) {
$event = $this->tx_service->transaction(function () use ($summit, $row) {
Log::debug(sprintf("SummitService::processEventData processing row %s", json_encode($row)));
$title = trim($row['title']);
$abstract = trim($row['abstract']);
// event type
$event_type = null;
if (isset($row['type_id']))
$event_type = $summit->getEventType(intval($row['type_id']));
if (isset($row['type']))
$event_type = $summit->getEventTypeByType($row['type']);
if (is_null($event_type))
throw new EntityNotFoundException("event type not found.");
// track
$track = null;
if (isset($row['track_id']))
$track = $summit->getPresentationCategory(intval($row['track_id']));
if (isset($row['track']))
$track = $summit->getPresentationCategoryByTitle($row['track']);
if (is_null($track))
throw new EntityNotFoundException("track not found.");
$event = null;
if(isset($row['id']) && !empty($row['id'])){
Log::debug(sprintf("SummitService::processEventData trying to get event %s", $row['id']));
$event = $summit->getEventById(intval($row['id']));
}
if(is_null($event))
$event = SummitEventFactory::build($event_type, $summit);
// main data
$event->setTitle(html_entity_decode($title));
$event->setAbstract(html_entity_decode($abstract));
if (isset($row['social_summary']))
$event->setSocialSummary($row['social_summary']);
if (isset($row['allow_feedback']))
$event->setAllowFeedBack(boolval($row['allow_feedback']));
$event->setType($event_type);
$event->setCategory($track);
if(isset($row['location']) && !empty($row['location'])){
$location = $summit->getLocation(intval($row['location']));
if(is_null($location))
$location = $summit->getLocationByName(trim($row['location']));
if(is_null($location))
throw new EntityNotFoundException("location not found.");
$event->setLocation($location);
}
if (isset($row['start_date']) && !empty($row['start_date']) && isset($row['end_date']) && !empty($row['end_date'])){
Log::debug
(
sprintf
(
"SummitService::processEventData publishing event start_date %s end_date %s",
$row['start_date'],
$row['end_date']
)
);
$start_date = DateTime::createFromFormat('Y-m-d H:i:s', $row['start_date'], $summit->getTimeZone());
$end_date = DateTime::createFromFormat('Y-m-d H:i:s', $row['end_date'], $summit->getTimeZone());
// set local time from UTC
$event->setStartDate($start_date);
$event->setEndDate($end_date);
}
// tags
if (isset($row['tags'])) {
$tags = explode('|', $row['tags']);
$event->clearTags();
foreach ($tags as $val) {
$tag = $this->tag_repository->getByTag($val);
if ($tag == null) {
Log::debug(sprintf("SummitService::processEventData creating tag %s", $val));
$tag = new Tag($val);
}
$event->addTag($tag);
}
}
// sponsors
$sponsors = ($event_type->isUseSponsors() && isset($row['sponsors'])) ?
$row['sponsors'] : '';
$sponsors = explode('|', $sponsors);
if ($event_type->isAreSponsorsMandatory() && count($sponsors) == 0) {
throw new ValidationException('sponsors are mandatory!');
}
if (isset($row['sponsors'])) {
$event->clearSponsors();
foreach ($sponsors as $sponsor_name) {
$sponsor = $this->company_repository->getByName(trim($sponsor_name));
if (is_null($sponsor)) throw new EntityNotFoundException(sprintf('sponsor %s', $sponsor_name));
$event->addSponsor($sponsor);
}
}
if ($event instanceof Presentation) {
if (isset($row['to_record']))
$event->setToRecord(boolval($row['to_record']));
if (isset($row['attendees_expected_learnt']))
$event->setAttendeesExpectedLearnt($row['attendees_expected_learnt']);
if (isset($row['level']))
$event->setLevel($row['level']);
if (isset($row['problem_addressed']))
$event->setProblemAddressed($row['problem_addressed']);
// speakers
if ($event_type instanceof PresentationType && $event_type->isUseSpeakers()) {
$speakers = isset($row['speakers']) ?
$row['speakers'] : '';
$speakers = explode('|', $speakers);
$speakers_names = [];
if(isset($row["speakers_names"])){
$speakers_names = isset($row['speakers_names']) ?
$row['speakers_names'] : '';
$speakers_names = explode('|', $speakers_names);
}
if(count($speakers_names) == 0 ){
$speakers_names = $speakers;
}
if(count($speakers_names) != count($speakers))
throw new ValidationException("count of speakers and speakers_name should match.");
if ($event_type->isAreSpeakersMandatory() && count($speakers) == 0) {
throw new ValidationException('speakers are mandatory!');
}
if (count($speakers) > 0) {
$event->clearSpeakers();
foreach ($speakers as $idx => $speaker_email) {
$speaker_full_name = $speakers_names[$idx];
$speaker_full_name_comps = explode(" ", $speaker_full_name, 2);
$speaker_first_name = trim($speaker_full_name_comps[0]);
$speaker_last_name = null;
if(count($speaker_full_name_comps) > 1){
$speaker_last_name = trim($speaker_full_name_comps[1]);
}
if(empty($speaker_last_name))
$speaker_last_name = $speaker_first_name;
Log::debug(sprintf("SummitService::processEventData processing speaker email %s speaker fullname %s", $speaker_email, $speaker_full_name));
$speaker = $this->speaker_repository->getByEmail(trim($speaker_email));
if (is_null($speaker)) {
Log::debug(sprintf("SummitService::processEventData speaker %s fname %s lname %s does not exists", $speaker_email, $speaker_first_name, $speaker_last_name));
$speaker = $this->speaker_service->addSpeaker([
'first_name' => $speaker_first_name,
'last_name' => $speaker_last_name,
'email' => $speaker_email
], null, false);
}
$event->addSpeaker($speaker);
}
}
}
// moderator
if ($event_type instanceof PresentationType && $event_type->isUseModerator()) {
$moderator_email = isset($row['moderator']) ? trim($row['moderator']) : null;
if ($event_type->isModeratorMandatory() && empty($moderator_email)) {
throw new ValidationException('moderator is mandatory!');
}
Log::debug(sprintf("SummitService::processEventData processing moderator %s", $moderator_email));
$moderator = $this->speaker_repository->getByEmail($moderator_email);
if (is_null($moderator)) {
Log::debug(sprintf("SummitService::processEventData moderator %s does not exists", $moderator_email));
$moderator = $this->speaker_service->addSpeaker(['email' => $moderator_email], null, false);
}
$event->setModerator($moderator);
}
// selection plan
if (isset($row['selection_plan'])) {
$selection_plan = $summit->getSelectionPlanByName($row['selection_plan']);
if (!is_null($selection_plan)) {
Log::debug(sprintf("SummitService::processEventData processing selection plan %s", $row['selection_plan']));
$track = $event->getCategory();
if (!$selection_plan->hasTrack($track)) {
throw new ValidationException(sprintf("Track %s (%s) does not belongs to Selection Plan %s (%s)", $track->getTitle(), $track->getId(), $selection_plan->getName(), $selection_plan->getId()));
}
$event->setSelectionPlan($selection_plan);
}
}
}
if (isset($row['is_published'])) {
$is_published = boolval($row['is_published']);
if ($is_published) {
if (!isset($row['start_date'])) throw new ValidationException("start_date is required.");
if (!isset($row['end_date'])) throw new ValidationException("end_date is required.");
if(!$event->isPublished())
$event->publish();
}
else{
$event->unPublish();
}
}
$summit->addEvent($event);
return $event;
});
if($send_speaker_email && $event instanceof Presentation){
foreach($event->getSpeakers() as $speaker)
$this->tx_service->transaction(function () use ($speaker, $event) {
$setPasswordLink = null;
if($speaker instanceof PresentationSpeaker) {
if(!$speaker->hasMember()) {
Log::debug(sprintf("SummitService::processEventData speaker %s has not member set, checking at idp", $speaker->getEmail()));
$user = $this->member_service->checkExternalUser($speaker->getEmail());
if(is_null($user)){
// user does not exist at idp so we need to generate a registration request
// and create the magic links to complete the registration request
Log::debug(sprintf("SummitService::processEventData speaker %s user not found at idp, creating registration request", $speaker->getEmail()));
$userRegistrationRequest = $this->member_service->emitRegistrationRequest
(
$speaker->getEmail(),
$speaker->getFirstName(),
$speaker->getLastName()
);
$setPasswordLink = $userRegistrationRequest['set_password_link'];
$speaker_management_base_url = Config::get('cfp.base_url');
$setPasswordLink = sprintf(
"%s?client_id=%s&redirect_uri=%s",
$setPasswordLink,
Config::get("cfp.client_id"),
sprintf("%s/app/profile", $speaker_management_base_url)
);
}
}
}
ImportEventSpeakerEmail::dispatch($event, $speaker, $setPasswordLink);
});
}
}
}
}

View File

@ -11,6 +11,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Illuminate\Support\Facades\Log;
use Iterator;
/**
* Class CSVReader
@ -45,12 +47,16 @@ final class CSVReader implements Iterator {
*/
public static function buildFrom(string $content):CSVReader
{
Log::debug(sprintf("CSVReader::buildFrom content %s", $content));
$data = str_getcsv($content,"\n" );
Log::debug(sprintf("CSVReader::buildFrom data %s", json_encode($data)));
$idx = 0;
$header = [];
$header = [];
$lines = [];
foreach($data as $row)
{
$row = str_getcsv($row, ",");
Log::debug(sprintf("CSVReader::buildFrom row %s", json_encode($row)));
++$idx;
if($idx === 1) {

View File

@ -33,6 +33,7 @@
"laravel-doctrine/orm": "1.4.*",
"laravel/framework": "5.6.*",
"laravel/tinker": "^1.0",
"league/csv": "^9.6",
"league/oauth2-client": "^2.4",
"php-amqplib/php-amqplib": "^2.11",
"php-opencloud/openstack": "dev-master",

78
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "dd60518aeb8ca6000e03c1ac89053208",
"content-hash": "a441f3fcf95ec732067dc7b61c451a5b",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -2576,6 +2576,78 @@
],
"time": "2019-08-07T15:10:45+00:00"
},
{
"name": "league/csv",
"version": "9.6.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "634322df4aed210fdfbb7c94e434dc860da733d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/634322df4aed210fdfbb7c94e434dc860da733d9",
"reference": "634322df4aed210fdfbb7c94e434dc860da733d9",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-mbstring": "*",
"php": "^7.2.5"
},
"require-dev": {
"ext-curl": "*",
"ext-dom": "*",
"friendsofphp/php-cs-fixer": "^2.16",
"phpstan/phpstan": "^0.12.0",
"phpstan/phpstan-phpunit": "^0.12.0",
"phpstan/phpstan-strict-rules": "^0.12.0",
"phpunit/phpunit": "^8.5"
},
"suggest": {
"ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes",
"ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "9.x-dev"
}
},
"autoload": {
"psr-4": {
"League\\Csv\\": "src"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ignace Nyamagana Butera",
"email": "nyamsprod@gmail.com",
"homepage": "https://github.com/nyamsprod/",
"role": "Developer"
}
],
"description": "CSV data manipulation made easy in PHP",
"homepage": "http://csv.thephpleague.com",
"keywords": [
"convert",
"csv",
"export",
"filter",
"import",
"read",
"transform",
"write"
],
"time": "2020-09-05T08:40:12+00:00"
},
{
"name": "league/flysystem",
"version": "1.0.69",
@ -3322,12 +3394,12 @@
"version": "v1.0.4",
"source": {
"type": "git",
"url": "https://github.com/nrk/predis.git",
"url": "https://github.com/predis/predis.git",
"reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nrk/predis/zipball/9ead747663bb1b1ae017dfa0d152aca87792b42f",
"url": "https://api.github.com/repos/predis/predis/zipball/9ead747663bb1b1ae017dfa0d152aca87792b42f",
"reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f",
"shasum": ""
},

View File

@ -15,4 +15,5 @@
return [
'base_url' => env('CFP_APP_BASE_URL', null),
'support_email' => env('CFP_SUPPORT_EMAIL', null),
'client_id' => env('CFP_OAUTH2_CLIENT_ID', null),
];

View File

@ -0,0 +1,66 @@
<?php namespace Database\Migrations\Model;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use App\Models\Foundation\Summit\EmailFlows\SummitEmailFlowType;
use App\Jobs\Emails\InviteAttendeeTicketEditionMail;
use App\Models\Foundation\Summit\EmailFlows\SummitEmailEventFlowType;
use LaravelDoctrine\ORM\Facades\Registry;
use models\utils\SilverstripeBaseModel;
use SummitEmailFlowTypeSeeder;
use LaravelDoctrine\ORM\Facades\EntityManager;
use App\Jobs\Emails\PresentationSubmissions\ImportEventSpeakerEmail;
/**
* Class Version20200924203451
* @package Database\Migrations\Model
*/
class Version20200924203451 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
DB::setDefaultConnection("model");
$em = Registry::getManager(SilverstripeBaseModel::EntityManager);
$repository = $em->getRepository(SummitEmailFlowType::class);
$flow = $repository->findOneBy([
"name" => "Presentation Submissions"
]);
SummitEmailFlowTypeSeeder::createEventsTypes(
[
[
'name' => ImportEventSpeakerEmail::EVENT_NAME,
'slug' => ImportEventSpeakerEmail::EVENT_SLUG,
'default_email_template' => ImportEventSpeakerEmail::DEFAULT_TEMPLATE
]
],
$flow
);
$em->persist($flow);
$em->flush();
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
}
}

View File

@ -0,0 +1,47 @@
<?php namespace Database\Migrations\Model;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use LaravelDoctrine\ORM\Facades\Registry;
use models\summit\Summit;
use models\utils\SilverstripeBaseModel;
/**
* Class Version20200924210244
* @package Database\Migrations\Model
*/
class Version20200924210244 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$em = Registry::getManager(SilverstripeBaseModel::EntityManager);
$repository = $em->getRepository(Summit::class);
$summits = $repository->findAll();
foreach($summits as $summit){
$summit->seedDefaultEmailFlowEvents();
$em->persist($summit);
}
$em->flush();
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
}
}

View File

@ -2256,7 +2256,7 @@ class ApiEndpointsSeeder extends Seeder
IGroup::SummitAdministrators,
]
),
array(
[
'name' => 'get-events-csv',
'route' => '/api/v1/summits/{id}/events/csv',
'http_method' => 'GET',
@ -2269,8 +2269,22 @@ class ApiEndpointsSeeder extends Seeder
IGroup::Administrators,
IGroup::SummitAdministrators,
]
),
array(
],
[
'name' => 'import-events-csv',
'route' => '/api/v1/summits/{id}/events/csv',
'http_method' => 'POST',
'scopes' => [
sprintf(SummitScopes::WriteSummitData, $current_realm),
sprintf(SummitScopes::WriteEventData, $current_realm)
],
'authz_groups' => [
IGroup::SuperAdmins,
IGroup::Administrators,
IGroup::SummitAdministrators,
]
],
[
'name' => 'get-published-events',
'route' => '/api/v1/summits/{id}/events/published',
'http_method' => 'GET',
@ -2278,8 +2292,8 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
),
array(
],
[
'name' => 'get-published-events-tags',
'route' => '/api/v1/summits/{id}/events/all/published/tags',
'http_method' => 'GET',
@ -2287,8 +2301,8 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
),
array(
],
[
'name' => 'get-schedule-empty-spots',
'route' => '/api/v1/summits/{id}/events/published/empty-spots',
'http_method' => 'GET',
@ -2296,8 +2310,8 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
),
array(
],
[
'name' => 'get-unpublished-events',
'route' => '/api/v1/summits/{id}/events/unpublished',
'http_method' => 'GET',
@ -2305,8 +2319,8 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
),
array(
],
[
'name' => 'get-all-events',
'route' => '/api/v1/summits/events',
'http_method' => 'GET',
@ -2314,8 +2328,8 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
),
array(
],
[
'name' => 'get-all-published-events',
'route' => '/api/v1/summits/events/published',
'http_method' => 'GET',
@ -2323,7 +2337,7 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
),
],
[
'name' => 'get-event',
'route' => '/api/v1/summits/{id}/events/{event_id}',

View File

@ -56,6 +56,7 @@ use App\Jobs\Emails\PresentationSubmissions\SelectionProcess\PresentationSpeaker
use App\Jobs\Emails\PresentationSubmissions\SelectionProcess\PresentationSpeakerSelectionProcessAlternateOnlyEmail;
use App\Jobs\Emails\PresentationSubmissions\SelectionProcess\PresentationSpeakerSelectionProcessAlternateRejectedEmail;
use App\Jobs\Emails\PresentationSubmissions\SelectionProcess\PresentationSpeakerSelectionProcessRejectedEmail;
use App\Jobs\Emails\PresentationSubmissions\ImportEventSpeakerEmail;
/**
* Class SummitEmailFlowTypeSeeder
*/
@ -277,6 +278,11 @@ final class SummitEmailFlowTypeSeeder extends Seeder
'slug' => PresentationSpeakerSelectionProcessRejectedEmail::EVENT_SLUG,
'default_email_template' => PresentationSpeakerSelectionProcessRejectedEmail::DEFAULT_TEMPLATE
],
[
'name' => ImportEventSpeakerEmail::EVENT_NAME,
'slug' => ImportEventSpeakerEmail::EVENT_SLUG,
'default_email_template' => ImportEventSpeakerEmail::DEFAULT_TEMPLATE
],
], $flow);
@ -288,7 +294,7 @@ final class SummitEmailFlowTypeSeeder extends Seeder
* @param array $payload
* @param SummitEmailFlowType $flow
*/
static private function createEventsTypes(array $payload , SummitEmailFlowType $flow){
static public function createEventsTypes(array $payload , SummitEmailFlowType $flow){
foreach($payload as $definition){
$event_type = new SummitEmailEventFlowType();
$event_type->setName($definition['name']);

View File

@ -86,6 +86,16 @@ trait InsertSummitTestData
$presentation_type = new PresentationType();
$presentation_type->setType('TEST PRESENTATION TYPE');
$presentation_type->setMinSpeakers(1);
$presentation_type->setMaxSpeakers(3);
$presentation_type->setMinModerators(0);
$presentation_type->setMaxModerators(0);
$presentation_type->setUseSpeakers(true);
$presentation_type->setShouldBeAvailableOnCfp(true);
$presentation_type->setAreSpeakersMandatory(false);
$presentation_type->setUseModerator(false);
$presentation_type->setIsModeratorMandatory(false);
self::$summit->addEventType($presentation_type);
self::$summit2 = new Summit();
@ -103,6 +113,7 @@ trait InsertSummitTestData
self::$summit2->setName("TEST SUMMIT2");
self::$mainVenue = new SummitVenue();
self::$mainVenue->setName("TEST VENUE");
self::$mainVenue->setIsMain(true);
self::$summit->addLocation(self::$mainVenue);

View File

@ -31,7 +31,7 @@ final class OAuth2SummitApiTest extends ProtectedApiTest
public function tearDown()
{
self::clearTestData();
// self::clearTestData();
Mockery::close();
}
@ -1619,4 +1619,50 @@ final class OAuth2SummitApiTest extends ProtectedApiTest
return intval($video_id);
}
public function testImportEventData(){
/* $csv_content = <<<CSV
title,abstract,type,track,social_summary,allow_feedback,to_record,tags,speakers_names,speakers,start_date,end_date,is_published,selection_plan,attendees_expected_learnt,problem_addressed,location
test1,test abstract1,TEST PRESENTATION TYPE,DEFAULT TRACK,social test1,1,1,tag1|tag2|tag3,Sebas Marcet|Sebas 1 Marcet|Sebas 2 Marcet,smarcet@gmail.com|smarcet+1@gmail.com,smarcet+2@gmail.com,2020-01-01 13:00:00,2020-01-01 13:45:00,1,TEST_SELECTION_PLAN,DEFAULT TRACK,big things,world issues,TEST VENUE
test2,test abstract2,TEST PRESENTATION TYPE,DEFAULT TRACK,social test2,1,1,tag1|tag2,Sebas Marcet,smarcet@gmail.com,2020-01-01 13:45:00,2020-01-01 14:45:00,1,TEST_SELECTION_PLAN,big things,world issues,TEST VENUE
test3,test abstract3,TEST PRESENTATION TYPE,DEFAULT TRACK,social test3,1,1,tag4,Sebas 2 Marcet,smarcet+2@gmail.com,2020-01-01 14:45:00,2020-01-01 15:45:00,1,TEST_SELECTION_PLAN,big things,world issues,
CSV;*/
$csv_content = <<<CSV
track,start_date,end_date,type,title,abstract,attendees_expected_learnt,social_summary ,speakers_names,speakers,selection_plan
Security,2020-11-12 8:00:00,2020-11-12 9:00:00,Presentation,Security Projects Alignment,"OCP-Security scope / threat model
Compare Resiliency approaches
General role of RoT
Alignment on security requirements across OCP Server sub-groups.",Cross-orgs alignment/sync on scope and approaches ,,JP Mon,jp@tipit.net,Draft Presentations Submissions
CSV;
$path = "/tmp/events.csv";
file_put_contents($path, $csv_content);
$file = new UploadedFile($path, "events.csv", 'text/csv', null, true);
$params = [
'summit_id' => self::$summit->getId(),
];
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
];
$response = $this->action(
"POST",
"OAuth2SummitEventsApiController@importEventData",
$params,
[
'send_speaker_email' => true,
],
[],
[
'file' => $file,
],
$headers
);
$content = $response->getContent();
$this->assertResponseStatus(200);
}
}