Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ QUEUE_DATABASE=
MAIL_DRIVER=sendgrid
SENDGRID_API_KEY='YOUR_SENDGRID_API_KEY'

CORS_ALLOWED_HEADERS=origin, content-type, accept, authorization, x-requested-with
CORS_ALLOWED_METHODS=GET, POST, OPTIONS, PUT, DELETE
CORS_ALLOWED_HEADERS="origin, content-type, accept, authorization, x-requested-with"
CORS_ALLOWED_METHODS="GET, POST, OPTIONS, PUT, DELETE"
CORS_USE_PRE_FLIGHT_CACHING=true
CORS_MAX_AGE=3200
CORS_EXPOSED_HEADERS=
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ public/apc.php
.nvmrc
.codegraph
docs/
docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,9 @@ function () {
'full_name' => 'sometimes|string',
'member_id' => 'sometimes|integer',
'member_user_external_id' => 'sometimes|integer',
'has_accepted_presentations' => 'sometimes|required|string|in:true,false',
'has_alternate_presentations' => 'sometimes|required|string|in:true,false',
'has_rejected_presentations' => 'sometimes|required|string|in:true,false',
'has_accepted_presentations' => 'sometimes|string|in:true,false',
'has_alternate_presentations' => 'sometimes|string|in:true,false',
'has_rejected_presentations' => 'sometimes|string|in:true,false',
'presentations_track_id' => 'sometimes|integer',
'presentations_track_group_id' => 'sometimes|integer',
'presentations_selection_plan_id' => 'sometimes|integer',
Expand Down Expand Up @@ -392,6 +392,113 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($summit) {
);
}

#[OA\Get(
path: '/api/v1/summits/{id}/speakers/all/events/count',
operationId: 'getSpeakersActivitiesCount',
description: 'Get the count of unique activities associated with speakers matching the filter criteria',
tags: ['Summit Speakers'],
security: [['summit_speakers_oauth2' => [
SummitScopes::ReadSummitData,
SummitScopes::ReadAllSummitData
]]],
parameters: [
new OA\Parameter(
name: 'id',
description: 'Summit ID',
in: 'path',
required: true,
schema: new OA\Schema(type: 'integer')
),
new OA\Parameter(
name: 'filter',
description: 'Filter query (supports multiple operators). Filterable fields: id, not_id, first_name, last_name, email, full_name, member_id, member_user_external_id, has_accepted_presentations, has_alternate_presentations, has_rejected_presentations, presentations_track_id, presentations_track_group_id, presentations_selection_plan_id, presentations_type_id, presentations_title, presentations_abstract, presentations_submitter_full_name, presentations_submitter_email, has_media_upload_with_type, has_not_media_upload_with_type.',
in: 'query',
required: false,
schema: new OA\Schema(type: 'string')
),
],
responses: [
new OA\Response(
response: Response::HTTP_OK,
description: 'Unique activities count',
content: new OA\JsonContent(
properties: [new OA\Property(property: 'count', type: 'integer')]
)
),
new OA\Response(response: Response::HTTP_BAD_REQUEST, description: 'Bad Request'),
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'),
new OA\Response(response: Response::HTTP_FORBIDDEN, description: 'Forbidden'),
new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit not found'),
new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: 'Server Error'),
]
)]
public function getSpeakersActivitiesCount($summit_id)
{
return $this->processRequest(function () use ($summit_id) {

$summit = SummitFinderStrategyFactory::build($this->getRepository(), $this->getResourceServerContext())->find(intval($summit_id));
if (is_null($summit)) return $this->error404();

$filter = null;

if (Request::has('filter')) {
$filter = FilterParser::parse(Request::input('filter'), [
'id' => ['=='],
'not_id' => ['=='],
'first_name' => ['=@', '@@', '=='],
'last_name' => ['=@', '@@', '=='],
'email' => ['=@', '@@', '=='],
'full_name' => ['=@', '@@', '=='],
'member_id' => ['=='],
'member_user_external_id' => ['=='],
'has_accepted_presentations' => ['=='],
'has_alternate_presentations' => ['=='],
'has_rejected_presentations' => ['=='],
'presentations_track_id' => ['=='],
'presentations_track_group_id' => ['=='],
'presentations_selection_plan_id' => ['=='],
'presentations_type_id' => ['=='],
'presentations_title' => ['=@', '@@', '=='],
'presentations_abstract' => ['=@', '@@', '=='],
'presentations_submitter_full_name' => ['=@', '@@', '=='],
'presentations_submitter_email' => ['=@', '@@', '=='],
'has_media_upload_with_type' => ['=='],
'has_not_media_upload_with_type' => ['=='],
]);
}

if (!is_null($filter)) {
$filter->validate([
'id' => 'sometimes|integer',
'not_id' => 'sometimes|integer',
'first_name' => 'sometimes|string',
'last_name' => 'sometimes|string',
'email' => 'sometimes|string',
'full_name' => 'sometimes|string',
'member_id' => 'sometimes|integer',
'member_user_external_id' => 'sometimes|integer',
'has_accepted_presentations' => 'sometimes|string|in:true,false',
'has_alternate_presentations' => 'sometimes|string|in:true,false',
'has_rejected_presentations' => 'sometimes|string|in:true,false',
'presentations_track_id' => 'sometimes|integer',
'presentations_track_group_id' => 'sometimes|integer',
'presentations_selection_plan_id' => 'sometimes|integer',
'presentations_type_id' => 'sometimes|integer',
'presentations_title' => 'sometimes|string',
'presentations_abstract' => 'sometimes|string',
'presentations_submitter_full_name' => 'sometimes|string',
'presentations_submitter_email' => 'sometimes|string',
'has_media_upload_with_type' => 'sometimes|integer',
'has_not_media_upload_with_type' => 'sometimes|integer',
]);
}

$count = $this->speaker_repository->getUniqueActivitiesCountBySummit($summit, $filter);

return $this->ok(['count' => $count]);
});
}

/**
* @param $summit_id
* @return mixed
Expand Down Expand Up @@ -510,9 +617,9 @@ function () {
'full_name' => 'sometimes|string',
'member_id' => 'sometimes|integer',
'member_user_external_id' => 'sometimes|integer',
'has_accepted_presentations' => 'sometimes|required|string|in:true,false',
'has_alternate_presentations' => 'sometimes|required|string|in:true,false',
'has_rejected_presentations' => 'sometimes|required|string|in:true,false',
'has_accepted_presentations' => 'sometimes|string|in:true,false',
'has_alternate_presentations' => 'sometimes|string|in:true,false',
'has_rejected_presentations' => 'sometimes|string|in:true,false',
'presentations_track_id' => 'sometimes|integer',
'presentations_track_group_id' => 'sometimes|integer',
'presentations_selection_plan_id' => 'sometimes|integer',
Expand Down Expand Up @@ -3171,9 +3278,9 @@ public function send($summit_id)
'last_name' => 'sometimes|string',
'email' => 'sometimes|string',
'full_name' => 'sometimes|string',
'has_accepted_presentations' => 'sometimes|required|string|in:true,false',
'has_alternate_presentations' => 'sometimes|required|string|in:true,false',
'has_rejected_presentations' => 'sometimes|required|string|in:true,false',
'has_accepted_presentations' => 'sometimes|string|in:true,false',
'has_alternate_presentations' => 'sometimes|string|in:true,false',
'has_rejected_presentations' => 'sometimes|string|in:true,false',
'presentations_track_id' => 'sometimes|integer',
'presentations_track_group_id' => 'sometimes|integer',
'presentations_selection_plan_id' => 'sometimes|integer',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,4 +495,111 @@ public function send($summit_id)
return $this->ok();
});
}

#[OA\Get(
path: "/api/v1/summits/{id}/submitters/all/events/count",
summary: "Get unique activities count for submitters",
operationId: "getSubmittersActivitiesCount",
tags: ["Summit Submitters"],
security: [['summit_submitters_oauth2' => [
SummitScopes::ReadSummitData,
SummitScopes::ReadAllSummitData,
]]],
parameters: [
new OA\Parameter(
name: "id",
in: "path",
required: true,
description: "Summit ID",
schema: new OA\Schema(type: "integer")
),
new OA\Parameter(
name: "filter",
in: "query",
required: false,
description: "Filter query (supports multiple operators). Filterable fields: id, not_id, first_name, last_name, email, full_name, member_id, member_user_external_id, has_accepted_presentations, has_alternate_presentations, has_rejected_presentations, presentations_track_id, presentations_selection_plan_id, presentations_type_id, presentations_title, presentations_abstract, presentations_submitter_full_name, presentations_submitter_email, is_speaker, has_media_upload_with_type, has_not_media_upload_with_type.",
schema: new OA\Schema(type: "string", example: "has_accepted_presentations==true")
),
],
responses: [
new OA\Response(
response: Response::HTTP_OK,
description: "Unique activities count",
content: new OA\JsonContent(
properties: [new OA\Property(property: "count", type: "integer")]
)
),
new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"),
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"),
new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"),
new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"),
new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"),
]
)]
public function getSubmittersActivitiesCount($summit_id)
{
return $this->processRequest(function () use ($summit_id) {

$summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->getResourceServerContext())->find(intval($summit_id));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (is_null($summit)) return $this->error404();

$filter = null;

if (Request::has('filter')) {
$filter = FilterParser::parse(Request::input('filter'), [
'id' => ['=='],
'not_id' => ['=='],
'first_name' => ['=@', '@@', '=='],
'last_name' => ['=@', '@@', '=='],
'email' => ['=@', '@@', '=='],
'full_name' => ['=@', '@@', '=='],
'member_id' => ['=='],
'member_user_external_id' => ['=='],
'has_accepted_presentations' => ['=='],
'has_alternate_presentations' => ['=='],
'has_rejected_presentations' => ['=='],
'presentations_track_id' => ['=='],
'presentations_selection_plan_id' => ['=='],
'presentations_type_id' => ['=='],
'presentations_title' => ['=@', '@@', '=='],
'presentations_abstract' => ['=@', '@@', '=='],
'presentations_submitter_full_name' => ['=@', '@@', '=='],
'presentations_submitter_email' => ['=@', '@@', '=='],
'is_speaker' => ['=='],
'has_media_upload_with_type' => ['=='],
'has_not_media_upload_with_type' => ['=='],
]);
}

if (!is_null($filter)) {
$filter->validate([
'id' => 'sometimes|integer',
'not_id' => 'sometimes|integer',
'first_name' => 'sometimes|string',
'last_name' => 'sometimes|string',
'email' => 'sometimes|string',
'full_name' => 'sometimes|string',
'member_id' => 'sometimes|integer',
'member_user_external_id' => 'sometimes|integer',
'has_accepted_presentations' => 'sometimes|string|in:true,false',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mulldug The getSpeakersActivitiesCount validation rules for has_accepted_presentations, has_alternate_presentations, and has_rejected_presentations use sometimes|required|string|in:true,false,
while the sibling getSubmittersActivitiesCount uses sometimes|string|in:true,false (no required). Since both endpoints accept the same filter semantics, the validation should be consistent
— pick one form and apply it to both.

'has_alternate_presentations' => 'sometimes|string|in:true,false',
'has_rejected_presentations' => 'sometimes|string|in:true,false',
'presentations_track_id' => 'sometimes|integer',
'presentations_selection_plan_id' => 'sometimes|integer',
'presentations_type_id' => 'sometimes|integer',
'presentations_title' => 'sometimes|string',
'presentations_abstract' => 'sometimes|string',
'presentations_submitter_full_name' => 'sometimes|string',
'presentations_submitter_email' => 'sometimes|string',
'is_speaker' => 'sometimes|string|in:true,false',
'has_media_upload_with_type' => 'sometimes|integer',
'has_not_media_upload_with_type' => 'sometimes|integer',
]);
}

$count = $this->repository->getUniqueActivitiesCountBySummit($summit, $filter);

return $this->ok(['count' => $count]);
});
}
}
8 changes: 8 additions & 0 deletions app/Models/Foundation/Main/Repositories/IMemberRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,12 @@ public function getSubmittersBySummit(Summit $summit, PagingInfo $paging_info, F
* @throws \Doctrine\DBAL\Exception
*/
public function getSubmittersIdsBySummit(Summit $summit, PagingInfo $paging_info, Filter $filter = null, Order $order = null);

/**
* @param Summit $summit
* @param Filter|null $filter
* @return int
* @throws \Doctrine\DBAL\Exception
*/
public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int;
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,12 @@ public function getSpeakersIdsBySummit(Summit $summit, PagingInfo $paging_info,
* @return PagingResponse
*/
public function getAllCompaniesByPage(PagingInfo $paging_info, Filter $filter = null, Order $order = null);

/**
* @param Summit $summit
* @param Filter|null $filter
* @return int
* @throws \Doctrine\DBAL\Exception
*/
public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int;
}
28 changes: 27 additions & 1 deletion app/Repositories/Summit/DoctrineMemberRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ protected function getBaseEntity()
*/
protected function applyExtraJoins(QueryBuilder $query, ?Filter $filter = null, ?Order $order = null): QueryBuilder
{
if($filter->hasFilter("summit_id") || $filter->hasFilter("schedule_event_id")){
if(!is_null($filter) && ($filter->hasFilter("summit_id") || $filter->hasFilter("schedule_event_id"))){
$query
->leftJoin("e.schedule","sch")
->leftJoin("sch.event", "evt")
Expand Down Expand Up @@ -638,6 +638,32 @@ function ($query) {
});
}

/**
* @param Summit $summit
* @param Filter|null $filter
* @return int
* @throws \Doctrine\DBAL\Exception
*/
public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int
{
// Start from Presentation and JOIN to the submitter (created_by).
// Bounded by summit — no subquery needed, filter mappings apply to e unchanged.
$countQb = $this->getEntityManager()->createQueryBuilder()
->select("COUNT(DISTINCT p.id)")
->from('models\summit\Presentation', 'p')
->join('p.created_by', 'e')
->where('p.summit = :summit')
->setParameter('summit', $summit);

$countQb = $this->applyExtraJoins($countQb, $filter);

if (!is_null($filter)) {
$filter->apply2Query($countQb, $this->getFilterMappings($filter));
}

return intval($countQb->getQuery()->getSingleScalarResult());
}

/**
* @param PagingInfo $paging_info
* @param Filter|null $filter
Expand Down
Loading
Loading