API: Show needed/added users by angeltype in shifts

This commit is contained in:
Igor Scheller 2023-07-29 21:37:46 +02:00 committed by Michael Weimann
parent ea93e27a9d
commit ef3bd7c319
3 changed files with 137 additions and 28 deletions

View File

@ -167,10 +167,6 @@ components:
format: date-time
description: DateTime in ISO-8601 format
example: 2023-05-13T16:00:00.000000Z23
entries:
type: array
items:
$ref: '#/components/schemas/ShiftEntry'
room:
$ref: '#/components/schemas/Room'
shift_type:
@ -187,6 +183,11 @@ components:
format: date-time
description: DateTime in ISO-8601 format
example: 2023-05-13T23:00:00.000000Z
entries:
type: array
description: Can be empty (for example on Schedule import of unused room)
items:
$ref: '#/components/schemas/ShiftEntry'
url:
type: string
example: https://example.com/shifts/42
@ -196,22 +197,31 @@ components:
- description
- start
- end
- entries
- room
- shift_type
- created_at
- updated_at
- entries
- url
ShiftEntry:
type: object
properties:
user:
$ref: '#/components/schemas/User'
users:
type: array
items:
$ref: '#/components/schemas/User'
type:
$ref: '#/components/schemas/AngelType'
needs:
type: integer
description: |
Number of users needed for the shift.
Can be more than users count when not full, less when overbooked or 0 when additional users have been added.
example: 3
required:
- user
- users
- type
- needs
ShiftType:
type: object
properties:

View File

@ -7,6 +7,9 @@ namespace Engelsystem\Controllers\Api;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\NeededAngelType;
use Engelsystem\Models\Shifts\Shift;
use Illuminate\Database\Eloquent\Collection;
class ShiftsController extends ApiController
{
@ -15,40 +18,55 @@ class ShiftsController extends ApiController
$roomId = (int) $request->getAttribute('room_id');
/** @var Room $room */
$room = Room::findOrFail($roomId);
/** @var Shift[]|Collection $shifts */
$shifts = $room->shifts()
->with([
'neededAngelTypes.angelType',
'room',
'shiftEntries.angelType',
'shiftEntries.user.contact',
'shiftEntries.user.personalData',
'shiftType',
])
->orderBy('start')
->get();
$shiftEntries = [];
$shiftEntries = [];
// Blob of not-optimized mediocre pseudo-serialization
foreach ($shifts as $shift) {
$entries = [];
foreach ($shift->shiftEntries as $entry) {
$user = $entry->user;
$userData = [
'id' => $user->id,
'name' => $user->name,
'first_name' => $user->personalData->first_name,
'last_name' => $user->personalData->last_name,
'pronoun' => $user->personalData->pronoun,
'contact' => $user->contact->only(['dect', 'mobile']),
'url' => $this->url->to('/users', ['action' => 'view', 'user_id' => $user->id]),
];
// Get all needed/used angel types
$neededAngelTypes = $this->getNeededAngelTypes($shift);
$angelTypeData = $entry->angelType->only(['id', 'name']);
$entries = new Collection();
foreach ($neededAngelTypes as $neededAngelType) {
$users = [];
foreach ($neededAngelType->users ?? [] as $user) {
$users[] = [
'id' => $user->id,
'name' => $user->name,
'first_name' => $user->personalData->first_name,
'last_name' => $user->personalData->last_name,
'pronoun' => $user->personalData->pronoun,
'contact' => $user->contact->only(['dect', 'mobile']),
'url' => $this->url->to('/users', ['action' => 'view', 'user_id' => $user->id]),
];
}
// Skip empty entries
if ($neededAngelType->count <= 0 && empty($users)) {
continue;
}
$angelTypeData = $neededAngelType->angelType->only(['id', 'name', 'description']);
$angelTypeData['url'] = $this->url->to(
'/angeltypes',
['action' => 'view', 'angeltype_id' => $entry->angelType->id]
['action' => 'view', 'angeltype_id' => $neededAngelType->angelType->id]
);
$entries[] = [
'user' => $userData,
'users' => $users,
'type' => $angelTypeData,
'needs' => $neededAngelType->count,
];
}
@ -61,11 +79,11 @@ class ShiftsController extends ApiController
'description' => $shift->description,
'start' => $shift->start,
'end' => $shift->end,
'entries' => $entries,
'room' => $roomData,
'shift_type' => $shift->shiftType->only(['id', 'name', 'description']),
'created_at' => $shift->created_at,
'updated_at' => $shift->updated_at,
'entries' => $entries,
'url' => $this->url->to('/shifts', ['action' => 'view', 'shift_id' => $shift->id]),
];
}
@ -74,4 +92,49 @@ class ShiftsController extends ApiController
return $this->response
->withContent(json_encode($data));
}
/**
* Collect all needed angeltypes
*/
protected function getNeededAngelTypes(Shift $shift): Collection
{
// From shift
$neededAngelTypes = $shift->neededAngelTypes;
// Add from room
foreach ($shift->room->neededAngelTypes as $neededAngelType) {
/** @var NeededAngelType $existingNeededAngelType */
$existingNeededAngelType = $neededAngelTypes
->where('angel_type_id', $neededAngelType->angel_type_id)
->first();
if (!$existingNeededAngelType) {
$neededAngelTypes[] = clone $neededAngelType;
continue;
}
$existingNeededAngelType->room_id = $shift->room->id;
$existingNeededAngelType->count += $neededAngelType->count;
}
// Add needed angeltypes from additionally added users
foreach ($shift->shiftEntries as $entry) {
$neededAngelType = $neededAngelTypes->where('angel_type_id', $entry->angelType->id)->first();
if (!$neededAngelType) {
$neededAngelType = new NeededAngelType([
'shift_id' => $shift->id,
'angel_type_id' => $entry->angelType->id,
'count' => 0,
]);
$neededAngelTypes[] = $neededAngelType;
}
// Add users to entries
$neededAngelType->users = isset($neededAngelType->users)
? $neededAngelType->users
: new Collection();
$neededAngelType->users[] = $entry->user;
}
return $neededAngelTypes;
}
}

View File

@ -8,16 +8,19 @@ use Engelsystem\Controllers\Api\ShiftsController;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\NeededAngelType;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\PersonalData;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Collection;
class ShiftsControllerTest extends ApiBaseControllerTest
{
/**
* @covers \Engelsystem\Controllers\Api\ShiftsController::entriesByRoom
* @covers \Engelsystem\Controllers\Api\ShiftsController::getNeededAngelTypes
*/
public function testEntriesByRoom(): void
{
@ -26,9 +29,42 @@ class ShiftsControllerTest extends ApiBaseControllerTest
/** @var Room $room */
$room = Room::factory()->create();
Shift::factory(3)
->has(ShiftEntry::factory(2), 'shiftEntries')
// Shifts
/** @var Collection|Shift[] $shifts */
$shifts = Shift::factory(2)
->create(['room_id' => $room->id]);
$shiftA = $shifts[0];
// "Empty" entry to be skipped
NeededAngelType::factory(1)->create(['room_id' => null, 'shift_id' => $shiftA->id, 'count' => 0]);
// Needed entry by shift
/** @var NeededAngelType $byShift */
$byShift = NeededAngelType::factory(2)
->create(['room_id' => null, 'shift_id' => $shiftA->id, 'count' => 2])
->first();
// Needed entry by room
/** @var NeededAngelType $byRoom */
$byRoom = NeededAngelType::factory(1)
->create(['room_id' => $room->id, 'shift_id' => null, 'count' => 3])
->first();
// Added by both
NeededAngelType::factory(1)
->create([
'room_id' => $room->id, 'shift_id' => null, 'angel_type_id' => $byShift->angel_type_id, 'count' => 3,
])
->first();
// By shift
ShiftEntry::factory(2)->create(['shift_id' => $shiftA->id, 'angel_type_id' => $byShift->angel_type_id]);
// By room
ShiftEntry::factory(1)->create(['shift_id' => $shiftA->id, 'angel_type_id' => $byRoom->angel_type_id]);
// Additional (not required by shift nor room)
ShiftEntry::factory(1)->create(['shift_id' => $shiftA->id]);
foreach (User::all() as $user) {
// Generate user data
@ -50,6 +86,6 @@ class ShiftsControllerTest extends ApiBaseControllerTest
$data = json_decode($response->getContent(), true);
$this->assertArrayHasKey('data', $data);
$this->assertCount(3, $data['data']);
$this->assertCount(2, $data['data']);
}
}