API: Show needed/added users by angeltype in shifts
This commit is contained in:
parent
ea93e27a9d
commit
ef3bd7c319
|
@ -167,10 +167,6 @@ components:
|
||||||
format: date-time
|
format: date-time
|
||||||
description: DateTime in ISO-8601 format
|
description: DateTime in ISO-8601 format
|
||||||
example: 2023-05-13T16:00:00.000000Z23
|
example: 2023-05-13T16:00:00.000000Z23
|
||||||
entries:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ShiftEntry'
|
|
||||||
room:
|
room:
|
||||||
$ref: '#/components/schemas/Room'
|
$ref: '#/components/schemas/Room'
|
||||||
shift_type:
|
shift_type:
|
||||||
|
@ -187,6 +183,11 @@ components:
|
||||||
format: date-time
|
format: date-time
|
||||||
description: DateTime in ISO-8601 format
|
description: DateTime in ISO-8601 format
|
||||||
example: 2023-05-13T23:00:00.000000Z
|
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:
|
url:
|
||||||
type: string
|
type: string
|
||||||
example: https://example.com/shifts/42
|
example: https://example.com/shifts/42
|
||||||
|
@ -196,22 +197,31 @@ components:
|
||||||
- description
|
- description
|
||||||
- start
|
- start
|
||||||
- end
|
- end
|
||||||
- entries
|
|
||||||
- room
|
- room
|
||||||
- shift_type
|
- shift_type
|
||||||
- created_at
|
- created_at
|
||||||
- updated_at
|
- updated_at
|
||||||
|
- entries
|
||||||
- url
|
- url
|
||||||
ShiftEntry:
|
ShiftEntry:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
user:
|
users:
|
||||||
$ref: '#/components/schemas/User'
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
type:
|
type:
|
||||||
$ref: '#/components/schemas/AngelType'
|
$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:
|
required:
|
||||||
- user
|
- users
|
||||||
- type
|
- type
|
||||||
|
- needs
|
||||||
ShiftType:
|
ShiftType:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -7,6 +7,9 @@ namespace Engelsystem\Controllers\Api;
|
||||||
use Engelsystem\Http\Request;
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Models\Room;
|
use Engelsystem\Models\Room;
|
||||||
|
use Engelsystem\Models\Shifts\NeededAngelType;
|
||||||
|
use Engelsystem\Models\Shifts\Shift;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
class ShiftsController extends ApiController
|
class ShiftsController extends ApiController
|
||||||
{
|
{
|
||||||
|
@ -15,40 +18,55 @@ class ShiftsController extends ApiController
|
||||||
$roomId = (int) $request->getAttribute('room_id');
|
$roomId = (int) $request->getAttribute('room_id');
|
||||||
/** @var Room $room */
|
/** @var Room $room */
|
||||||
$room = Room::findOrFail($roomId);
|
$room = Room::findOrFail($roomId);
|
||||||
|
/** @var Shift[]|Collection $shifts */
|
||||||
$shifts = $room->shifts()
|
$shifts = $room->shifts()
|
||||||
->with([
|
->with([
|
||||||
|
'neededAngelTypes.angelType',
|
||||||
|
'room',
|
||||||
'shiftEntries.angelType',
|
'shiftEntries.angelType',
|
||||||
'shiftEntries.user.contact',
|
'shiftEntries.user.contact',
|
||||||
'shiftEntries.user.personalData',
|
'shiftEntries.user.personalData',
|
||||||
'shiftType',
|
'shiftType',
|
||||||
])
|
])
|
||||||
|
->orderBy('start')
|
||||||
->get();
|
->get();
|
||||||
$shiftEntries = [];
|
|
||||||
|
|
||||||
|
$shiftEntries = [];
|
||||||
// Blob of not-optimized mediocre pseudo-serialization
|
// Blob of not-optimized mediocre pseudo-serialization
|
||||||
foreach ($shifts as $shift) {
|
foreach ($shifts as $shift) {
|
||||||
$entries = [];
|
// Get all needed/used angel types
|
||||||
foreach ($shift->shiftEntries as $entry) {
|
$neededAngelTypes = $this->getNeededAngelTypes($shift);
|
||||||
$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]),
|
|
||||||
];
|
|
||||||
|
|
||||||
$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(
|
$angelTypeData['url'] = $this->url->to(
|
||||||
'/angeltypes',
|
'/angeltypes',
|
||||||
['action' => 'view', 'angeltype_id' => $entry->angelType->id]
|
['action' => 'view', 'angeltype_id' => $neededAngelType->angelType->id]
|
||||||
);
|
);
|
||||||
|
|
||||||
$entries[] = [
|
$entries[] = [
|
||||||
'user' => $userData,
|
'users' => $users,
|
||||||
'type' => $angelTypeData,
|
'type' => $angelTypeData,
|
||||||
|
'needs' => $neededAngelType->count,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,11 +79,11 @@ class ShiftsController extends ApiController
|
||||||
'description' => $shift->description,
|
'description' => $shift->description,
|
||||||
'start' => $shift->start,
|
'start' => $shift->start,
|
||||||
'end' => $shift->end,
|
'end' => $shift->end,
|
||||||
'entries' => $entries,
|
|
||||||
'room' => $roomData,
|
'room' => $roomData,
|
||||||
'shift_type' => $shift->shiftType->only(['id', 'name', 'description']),
|
'shift_type' => $shift->shiftType->only(['id', 'name', 'description']),
|
||||||
'created_at' => $shift->created_at,
|
'created_at' => $shift->created_at,
|
||||||
'updated_at' => $shift->updated_at,
|
'updated_at' => $shift->updated_at,
|
||||||
|
'entries' => $entries,
|
||||||
'url' => $this->url->to('/shifts', ['action' => 'view', 'shift_id' => $shift->id]),
|
'url' => $this->url->to('/shifts', ['action' => 'view', 'shift_id' => $shift->id]),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -74,4 +92,49 @@ class ShiftsController extends ApiController
|
||||||
return $this->response
|
return $this->response
|
||||||
->withContent(json_encode($data));
|
->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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,19 @@ use Engelsystem\Controllers\Api\ShiftsController;
|
||||||
use Engelsystem\Http\Request;
|
use Engelsystem\Http\Request;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Models\Room;
|
use Engelsystem\Models\Room;
|
||||||
|
use Engelsystem\Models\Shifts\NeededAngelType;
|
||||||
use Engelsystem\Models\Shifts\Shift;
|
use Engelsystem\Models\Shifts\Shift;
|
||||||
use Engelsystem\Models\Shifts\ShiftEntry;
|
use Engelsystem\Models\Shifts\ShiftEntry;
|
||||||
use Engelsystem\Models\User\Contact;
|
use Engelsystem\Models\User\Contact;
|
||||||
use Engelsystem\Models\User\PersonalData;
|
use Engelsystem\Models\User\PersonalData;
|
||||||
use Engelsystem\Models\User\User;
|
use Engelsystem\Models\User\User;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
class ShiftsControllerTest extends ApiBaseControllerTest
|
class ShiftsControllerTest extends ApiBaseControllerTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Controllers\Api\ShiftsController::entriesByRoom
|
* @covers \Engelsystem\Controllers\Api\ShiftsController::entriesByRoom
|
||||||
|
* @covers \Engelsystem\Controllers\Api\ShiftsController::getNeededAngelTypes
|
||||||
*/
|
*/
|
||||||
public function testEntriesByRoom(): void
|
public function testEntriesByRoom(): void
|
||||||
{
|
{
|
||||||
|
@ -26,9 +29,42 @@ class ShiftsControllerTest extends ApiBaseControllerTest
|
||||||
/** @var Room $room */
|
/** @var Room $room */
|
||||||
$room = Room::factory()->create();
|
$room = Room::factory()->create();
|
||||||
|
|
||||||
Shift::factory(3)
|
// Shifts
|
||||||
->has(ShiftEntry::factory(2), 'shiftEntries')
|
/** @var Collection|Shift[] $shifts */
|
||||||
|
$shifts = Shift::factory(2)
|
||||||
->create(['room_id' => $room->id]);
|
->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) {
|
foreach (User::all() as $user) {
|
||||||
// Generate user data
|
// Generate user data
|
||||||
|
@ -50,6 +86,6 @@ class ShiftsControllerTest extends ApiBaseControllerTest
|
||||||
|
|
||||||
$data = json_decode($response->getContent(), true);
|
$data = json_decode($response->getContent(), true);
|
||||||
$this->assertArrayHasKey('data', $data);
|
$this->assertArrayHasKey('data', $data);
|
||||||
$this->assertCount(3, $data['data']);
|
$this->assertCount(2, $data['data']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue