Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
D
Doorcode
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
5
Issues
5
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Package Registry
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Guardians of the Kretschmar Elock System
Doorcode
Commits
47f9024c
Commit
47f9024c
authored
Dec 13, 2020
by
Jacob Priddy
👌
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
current progress on this
parent
fabf7698
Pipeline
#13192
failed with stages
in 1 minute and 40 seconds
Changes
9
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
245 additions
and
86 deletions
+245
-86
src/backend/src/Entities/Override.php
src/backend/src/Entities/Override.php
+41
-1
src/backend/src/Gateways/Overrides/DatabaseOverridesRepository.php
...nd/src/Gateways/Overrides/DatabaseOverridesRepository.php
+6
-9
src/backend/src/Gateways/Overrides/InMemoryOverridesRepository.php
...nd/src/Gateways/Overrides/InMemoryOverridesRepository.php
+6
-5
src/backend/src/Gateways/Overrides/OverridesRepository.php
src/backend/src/Gateways/Overrides/OverridesRepository.php
+4
-3
src/backend/src/UseCases/Door/Access/Authorizers/OverrideAuthorizer.php
...c/UseCases/Door/Access/Authorizers/OverrideAuthorizer.php
+19
-12
src/backend/src/UseCases/Door/Commands/OverrideCommand.php
src/backend/src/UseCases/Door/Commands/OverrideCommand.php
+3
-3
src/backend/src/UseCases/Door/ScheduleEvents/ScheduleEventOverrideMerge.php
...eCases/Door/ScheduleEvents/ScheduleEventOverrideMerge.php
+153
-42
src/backend/src/UseCases/Door/ScheduleEvents/ScheduleEvents.php
...ckend/src/UseCases/Door/ScheduleEvents/ScheduleEvents.php
+11
-9
src/backend/tests/Database/OverrideDatabaseTest.php
src/backend/tests/Database/OverrideDatabaseTest.php
+2
-2
No files found.
src/backend/src/Entities/Override.php
View file @
47f9024c
...
...
@@ -158,7 +158,7 @@ class Override
}
/**
* @param \Carbon\Carbon $date
* @param \Carbon\Carbon
|null
$date
* @return bool
*/
public
function
isActiveForDate
(
?Carbon
$date
):
bool
...
...
@@ -191,4 +191,44 @@ class Override
{
return
$this
->
getId
()
===
(
int
)
$id
;
}
/**
* Determines if the current override engulfs another
*
* @param \Source\Entities\Override $o
* @return bool
*/
public
function
engulfs
(
Override
$o
):
bool
{
return
$this
->
engulfsDates
(
$o
->
getStart
(),
$o
->
getEnd
());
}
/**
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
* @return bool
*/
public
function
engulfsDates
(
Carbon
$start
,
Carbon
$end
):
bool
{
return
$this
->
getStart
()
<
$start
&&
$this
->
getEnd
()
>
$end
;
}
/**
* @param \Source\Entities\Override $o
* @return bool
*/
public
function
hasNoOverlapWith
(
Override
$o
):
bool
{
return
$this
->
hasNoOverlapWithDates
(
$o
->
getStart
(),
$o
->
getEnd
());
}
/**
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
* @return bool
*/
public
function
hasNoOverlapWithDates
(
Carbon
$start
,
Carbon
$end
):
bool
{
return
$this
->
getStart
()
>
$end
||
$this
->
getEnd
()
<
$start
;
}
}
src/backend/src/Gateways/Overrides/DatabaseOverridesRepository.php
View file @
47f9024c
...
...
@@ -95,28 +95,25 @@ class DatabaseOverridesRepository implements OverridesRepository
/**
* @inheritDoc
*/
public
function
activeOverride
ForDoorBetween
(
string
$doorId
,
Carbon
$begin
,
Carbon
$end
):
?Override
public
function
activeOverride
sForDoorBetween
(
string
$doorId
,
Carbon
$begin
,
Carbon
$end
):
array
{
$query
=
<<<QUERY
select A.id, A.reason, A.user_id, A.door_id, A.type, A.start, A.end, A.created_at, A.updated_at
from overrides as A
WHERE A.door_id = :DOOR_ID
AND ((:BEGIN, :END) OVERLAPS (A.start, A.end))
ORDER BY A.created_at DESC
LIMIT 1
ORDER BY A.start
QUERY;
$override
=
$this
->
db
->
selectOne
(
$query
,
[
$override
s
=
$this
->
db
->
select
(
$query
,
[
':DOOR_ID'
=>
self
::
castToInt
(
$doorId
),
':BEGIN'
=>
$begin
,
':END'
=>
$end
,
]);
if
(
!
$override
)
{
return
null
;
}
return
$this
->
toOverrideFromRaw
(
$override
);
return
array_map
(
static
function
(
\
App\Override
$override
)
{
return
self
::
toOverrideFromRaw
(
$override
);
},
$overrides
);
}
/**
...
...
src/backend/src/Gateways/Overrides/InMemoryOverridesRepository.php
View file @
47f9024c
...
...
@@ -32,9 +32,9 @@ class InMemoryOverridesRepository implements OverridesRepository
if
(
$begin
&&
$end
)
{
$include
=
$include
&&
(
$begin
->
lessThanOrEqualTo
(
$override
->
getEnd
())
&&
$end
->
greaterThanOrEqualTo
(
$override
->
getStart
()));
}
elseif
(
$begin
)
{
}
else
if
(
$begin
)
{
$include
=
$include
&&
$begin
->
lessThanOrEqualTo
(
$override
->
getEnd
());
}
elseif
(
$end
)
{
}
else
if
(
$end
)
{
$include
=
$include
&&
$end
->
greaterThanOrEqualTo
(
$override
->
getStart
());
}
...
...
@@ -45,7 +45,7 @@ class InMemoryOverridesRepository implements OverridesRepository
/**
* @inheritDoc
*/
public
function
activeOverride
ForDoorBetween
(
string
$doorId
,
Carbon
$begin
,
Carbon
$end
):
?Override
public
function
activeOverride
sForDoorBetween
(
string
$doorId
,
Carbon
$begin
,
Carbon
$end
):
array
{
$overrides
=
collect
(
array_filter
(
$this
->
overrides
,
static
function
(
Override
$override
)
use
(
$doorId
,
$begin
,
$end
)
{
return
$override
->
hasDoorIdOf
(
$doorId
)
&&
...
...
@@ -53,8 +53,9 @@ class InMemoryOverridesRepository implements OverridesRepository
}));
return
$overrides
->
sortBy
(
fn
(
Override
$item
)
=>
$item
->
getCreatedAt
(),
SORT_DESC
,
true
)
->
first
();
->
sortBy
(
fn
(
Override
$item
)
=>
$item
->
getStart
())
->
values
()
->
all
();
}
/**
...
...
src/backend/src/Gateways/Overrides/OverridesRepository.php
View file @
47f9024c
...
...
@@ -20,14 +20,15 @@ interface OverridesRepository
public
function
filterHistory
(
?string
$doorId
,
?string
$userId
,
?Carbon
$begin
,
?Carbon
$end
):
array
;
/**
* Gets active override during date (if any). Returns null otherwise.
* Gets active override during date (if any). Closed modes take precedence over open modes.
* They are sorted by their start date
*
* @param string $doorId
* @param \Carbon\Carbon $begin
* @param \Carbon\Carbon $end
* @return
\Source\Entities\Override|null
* @return
Override[]
*/
public
function
activeOverride
ForDoorBetween
(
string
$doorId
,
Carbon
$begin
,
Carbon
$end
):
?Override
;
public
function
activeOverride
sForDoorBetween
(
string
$doorId
,
Carbon
$begin
,
Carbon
$end
):
array
;
/**
* Persists an override to the application. Only the latest override is ever looked at for scheduling
...
...
src/backend/src/UseCases/Door/Access/Authorizers/OverrideAuthorizer.php
View file @
47f9024c
...
...
@@ -30,22 +30,29 @@ class OverrideAuthorizer implements AccessAuthorizer
public
function
check
(
?User
$user
,
Carbon
$date
,
Door
$door
,
string
$doorcode
,
?string
$commandString
):
int
{
// Get currently active override if there is any
$
active
=
$this
->
overrides
->
activeOverride
ForDoorBetween
(
$door
->
getId
(),
$date
,
$date
);
$
overrides
=
$this
->
overrides
->
activeOverrides
ForDoorBetween
(
$door
->
getId
(),
$date
,
$date
);
if
(
!
$active
)
{
return
self
::
CONTINUE
;
$open
=
false
;
foreach
(
$overrides
as
$active
)
{
switch
(
$active
->
getType
())
{
case
Override
::
TYPE_LOCKED
:
$this
->
reason
=
'Door is in override locked mode.'
;
return
self
::
DENY
;
case
Override
::
TYPE_OPEN
:
$open
=
true
;
break
;
default
:
break
;
}
}
switch
(
$active
->
getType
())
{
case
Override
::
TYPE_LOCKED
:
$this
->
reason
=
'Door is in override locked mode.'
;
return
self
::
DENY
;
case
Override
::
TYPE_OPEN
:
$this
->
reason
=
'Door is in override open mode.'
;
return
self
::
ALLOW
;
default
:
return
self
::
CONTINUE
;
if
(
$open
)
{
$this
->
reason
=
'Door is in override open mode.'
;
return
self
::
ALLOW
;
}
return
self
::
CONTINUE
;
}
/**
...
...
src/backend/src/UseCases/Door/Commands/OverrideCommand.php
View file @
47f9024c
...
...
@@ -95,9 +95,9 @@ class OverrideCommand extends Command
return
false
;
case
self
::
CLEAR_MODE
:
$override
=
$this
->
overrides
->
activeOverride
ForDoorBetween
(
$door
->
getId
(),
Carbon
::
now
(),
Carbon
::
now
());
$override
s
=
$this
->
overrides
->
activeOverrides
ForDoorBetween
(
$door
->
getId
(),
Carbon
::
now
(),
Carbon
::
now
());
if
(
$override
)
{
foreach
(
$overrides
as
$override
)
{
try
{
$this
->
overrides
->
updateOverride
(
$override
->
getId
(),
new
Override
(
$override
->
getId
(),
...
...
@@ -109,7 +109,7 @@ class OverrideCommand extends Command
Carbon
::
now
()
->
subSecond
()
));
}
catch
(
EntityNotFoundException
$e
)
{
return
false
;
// Do nothing and continue on
}
}
...
...
src/backend/src/UseCases/Door/ScheduleEvents/ScheduleEventOverrideMerge.php
View file @
47f9024c
...
...
@@ -10,62 +10,173 @@ use Source\Entities\ScheduleEvent;
trait
ScheduleEventOverrideMerge
{
/**
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
* @param \Source\Entities\Override|null $override
* @return \Source\Entities\ScheduleEvent[]
* @param \Source\Entities\Override[] $closed
* @param \Source\Entities\Override[] $open
* @return \Source\Entities\Override[]
*/
protected
function
mergeOverrideAndScheduleEvent
(
Carbon
$start
,
Carbon
$end
,
?Override
$override
):
array
protected
function
splitOpenOverrides
(
array
$closed
,
array
$open
):
array
{
/*
* Parsing of schedules
*
* schedule no overlapping override
* - schedule encloses override
* - break schedule into two events
* - start of schedule to start of override
* - end of override to end of schedule
* - Don't intersect
* - add schedule
* schedule overlaps with override
* - schedule begin and end overlap with override
* - ignore
* - schedule begin overlaps with override
* - end of override to end of schedule
* - schedule end overlaps with override
* - beginning of schedule to beginning of override
* Preprocessing:
* Clip all overrides around closed mode overrides, then process all overrides. once we have resolved all
* overlap.
* Closed modes have precedence over open overrides.
*/
if
(
$override
&&
$override
->
hasTypeOf
(
Override
::
TYPE_LOCKED
))
{
// If the beginning of the schedule overlaps with the override
$overlapBegin
=
$override
->
isActiveForDate
(
$start
);
// If the end of the schedule overlaps with the override
$overlapEnd
=
$override
->
isActiveForDate
(
$end
);
if
(
!
$overlapBegin
&&
!
$overlapEnd
)
{
if
(
$start
->
isBefore
(
$override
->
getStart
())
&&
$end
->
isAfter
(
$override
->
getEnd
()))
{
// Override is enclosed by the event, break up the open event
return
[
new
ScheduleEvent
(
$start
,
$override
->
getStart
()),
new
ScheduleEvent
(
$override
->
getEnd
(),
$end
),
];
$nonOverlappingOverrides
=
[];
// TODO: Rework this there is bug when what happens if your open needs to be split by multiple closeds
// So you end up with a segmented thing where you will have a conclomeration of stuff, we need to merge
// with previous processed segments
foreach
(
$open
as
$oo
)
{
foreach
(
$closed
as
$co
)
{
// No action needed if no overlap
if
(
$co
->
hasNoOverlapWith
(
$oo
))
{
continue
;
}
/*
* Closed engulfs an open
* |----open----|
* |-----closed--------|
* In this case ignore the open override.
*/
if
(
$co
->
engulfs
(
$oo
))
{
continue
;
}
/*
* Open is at the left side of a closed
* |---open---|
* |-----closed----|
* In this case cut the open override.
*
* Node: This case and the other edge case take care of when an open engulfs a closed
*/
if
(
$oo
->
getStart
()
<
$co
->
getStart
())
{
$nonOverlappingOverrides
[]
=
new
Override
(
$oo
->
getId
(),
$oo
->
getReason
(),
$oo
->
getUserId
(),
$oo
->
getDoorId
(),
$oo
->
getType
(),
$oo
->
getStart
(),
$co
->
getStart
()
);
}
/*
* Open is at the right side of a closed
* |---open---|
* |-----closed----|
* In this case cut the open override.
*
* Node: This case and the other edge case take care of when an open engulfs a closed
*/
if
(
$oo
->
getEnd
()
>
$co
->
getEnd
())
{
$nonOverlappingOverrides
[]
=
new
Override
(
$oo
->
getId
(),
$oo
->
getReason
(),
$oo
->
getUserId
(),
$oo
->
getDoorId
(),
$oo
->
getType
(),
$co
->
getEnd
(),
$oo
->
getEnd
()
);
}
}
}
return
$nonOverlappingOverrides
;
}
/**
* @param \Source\Entities\Override[] $closedOverrides
* @return \Source\Entities\Override[]
*/
protected
function
mergeClosedOverrides
(
array
$closedOverrides
):
array
{
if
(
count
(
$closedOverrides
)
<
1
)
{
return
[];
}
$merged
=
[];
$prev
=
array_shift
(
$closedOverrides
);
// override does not overlap with event add whole event range
return
[
new
ScheduleEvent
(
$start
,
$end
)];
foreach
(
$closedOverrides
as
$override
)
{
if
(
$override
->
getStart
()
<
$prev
->
getEnd
())
{
$prev
=
new
Override
(
$prev
->
getId
(),
$prev
->
getReason
(),
$prev
->
getUserId
(),
$prev
->
getDoorId
(),
$prev
->
getType
(),
$prev
->
getStart
(),
$override
->
getEnd
()
);
}
else
{
$merged
[]
=
$prev
;
$prev
=
$override
;
}
}
return
$merged
;
}
if
(
$overlapBegin
&&
$overlapEnd
)
{
// Override envelops the event, ignore the event
/**
* This method breaks up a time range defined from a non overlapping list of closed overrides.
* This relies on there being NO OVERLAP between closed overrides.
*
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
* @param \Source\Entities\Override[] $closedOverrides
* @return \Source\Entities\ScheduleEvent[]
*/
protected
function
mergeOverridesAndScheduleEvent
(
Carbon
$start
,
Carbon
$end
,
array
$closedOverrides
):
array
{
$splits
=
[];
foreach
(
$closedOverrides
as
$override
)
{
/*
* Closed engulfs the open segment
* |----open----|
* |-----closed--------|
* In this case ignore the schedule segment
*/
if
(
$override
->
engulfsDates
(
$start
,
$end
))
{
return
[];
}
if
(
$overlapBegin
)
{
return
[
new
ScheduleEvent
(
$override
->
getEnd
(),
$end
)];
// Next override as this one has no overlap.
if
(
$override
->
hasNoOverlapWithDates
(
$start
,
$end
))
{
continue
;
}
return
[
new
ScheduleEvent
(
$start
,
$override
->
getStart
())];
/*
* Open is at the left side of a closed
* |---open---|
* |-----closed----|
* In this case cut the open schedule segment.
*
* Node: This case and the other edge case take care of when an open engulfs a closed
*/
if
(
$start
<
$override
->
getStart
())
{
$splits
[]
=
new
ScheduleEvent
(
$start
,
$override
->
getStart
());
}
/*
* Open is at the right side of a closed
* |---open---|
* |-----closed----|
* In this case cut the open override.
*
* Node: This case and the other edge case take care of when an open engulfs a closed
*/
if
(
$end
>
$override
->
getEnd
())
{
$splits
[]
=
new
ScheduleEvent
(
$override
->
getEnd
(),
$end
);
}
}
// No override to worry about, add the event
return
[
new
ScheduleEvent
(
$start
,
$end
)];
return
$splits
;
}
}
src/backend/src/UseCases/Door/ScheduleEvents/ScheduleEvents.php
View file @
47f9024c
...
...
@@ -59,13 +59,15 @@ class ScheduleEvents implements ScheduleEventsUseCase
Presenter
$presenter
):
void
{
/*
* Ignore override overlaps, allow override edits
* only take latest override
* Override is immediate for not planned things
*
* Only one override can be active at a time
* Need to merge overrides :(
* I tried imposing a only one override at a time, but... they're used like schedules are supposed to be
* anyway so I'm gonna just have to make them work like schedules...
*/
$override
=
$this
->
overrides
->
activeOverrideForDoorBetween
(
$doorId
,
$begin
,
$end
);
$overrides
=
$this
->
overrides
->
activeOverridesForDoorBetween
(
$doorId
,
$begin
,
$end
);
$closed
=
$this
->
mergeClosedOverrides
(
array_filter
(
$overrides
,
static
fn
(
Override
$o
)
=>
$o
->
hasTypeOf
(
Override
::
TYPE_LOCKED
))
);
$open
=
array_filter
(
$overrides
,
static
fn
(
Override
$o
)
=>
$o
->
hasTypeOf
(
Override
::
TYPE_OPEN
));
$openSchedules
=
$this
->
doorSchedules
->
getSchedulesForDoorBetween
(
$doorId
,
$begin
,
$end
,
Schedule
::
TYPE_OPEN_MODE
);
$userSchedules
=
[];
...
...
@@ -77,8 +79,8 @@ class ScheduleEvents implements ScheduleEventsUseCase
$response
=
new
ResponseModel
();
if
(
$override
&&
$override
->
hasTypeOf
(
Override
::
TYPE_OPEN
)
)
{
$response
->
addOpenEvent
(
new
ScheduleEvent
(
$o
verride
->
getStart
(),
$o
verride
->
getEnd
()));
foreach
(
$this
->
splitOpenOverrides
(
$closed
,
$open
)
as
$openOverride
)
{
$response
->
addOpenEvent
(
new
ScheduleEvent
(
$o
penOverride
->
getStart
(),
$openO
verride
->
getEnd
()));
}
// Get all open schedule times in the specified range.
...
...
@@ -94,7 +96,7 @@ class ScheduleEvents implements ScheduleEventsUseCase
foreach
(
$this
->
rset
->
occurrencesBetween
(
$b
,
$end
)
as
$eventStart
)
{
$eventEnd
=
$schedule
->
addDuration
(
$eventStart
);
foreach
(
$this
->
mergeOverride
AndScheduleEvent
(
$eventStart
,
$eventEnd
,
$override
)
as
$scheduleEvent
)
{
foreach
(
$this
->
mergeOverride
sAndScheduleEvent
(
$eventStart
,
$eventEnd
,
$closed
)
as
$scheduleEvent
)
{
// Lock to be beginning and end date times requested.
if
(
$scheduleEvent
->
getBegin
()
<
$begin
)
{
$scheduleEvent
->
setBegin
(
$begin
);
...
...
src/backend/tests/Database/OverrideDatabaseTest.php
View file @
47f9024c
...
...
@@ -130,9 +130,9 @@ class OverrideDatabaseTest extends DatabaseTestCase
Carbon
::
now
()
->
addHour
(),
Carbon
::
now
()
->
subMinute
()
));
self
::
assertNull
(
$this
->
repository
->
activeOverrideForDoorBetween
(
$d1
->
getId
(),
Carbon
::
now
(),
Carbon
::
now
()
->
addSecond
()));
self
::
assertNull
(
$this
->
repository
->
activeOverride
s
ForDoorBetween
(
$d1
->
getId
(),
Carbon
::
now
(),
Carbon
::
now
()
->
addSecond
()));
// It picks the latest override
self
::
assertEquals
(
Override
::
TYPE_OPEN
,
$this
->
repository
->
activeOverrideForDoorBetween
(
$d1
->
getId
(),
Carbon
::
now
(),
Carbon
::
now
()
->
addHours
(
3
))
->
getType
());
self
::
assertEquals
(
Override
::
TYPE_OPEN
,
$this
->
repository
->
activeOverride
s
ForDoorBetween
(
$d1
->
getId
(),
Carbon
::
now
(),
Carbon
::
now
()
->
addHours
(
3
))
->
getType
());
}
/**
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment