Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
AKPlanning
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
KIF
AKPlanning
Merge requests
!100
Scheduling Constraints WIP WIP WIP
Code
Review changes
Check out branch
Download
Patches
Plain diff
Merged
Scheduling Constraints WIP WIP WIP
scheduling-constraints-wip
into
scheduling-constraints
Overview
0
Commits
18
Pipelines
6
Changes
10
Merged
Benjamin Hättasch
requested to merge
scheduling-constraints-wip
into
scheduling-constraints
3 years ago
Overview
0
Commits
18
Pipelines
6
Changes
1
Expand
0
0
Merge request reports
Compare
version 4
version 5
9664edc4
3 years ago
version 4
e34b6280
3 years ago
version 3
3b2164e8
3 years ago
version 2
9869be22
3 years ago
version 1
e64492e4
3 years ago
scheduling-constraints (base)
and
version 5
latest version
35aa2e04
18 commits,
3 years ago
version 5
9664edc4
11 commits,
3 years ago
version 4
e34b6280
10 commits,
3 years ago
version 3
3b2164e8
7 commits,
3 years ago
version 2
9869be22
7 commits,
3 years ago
version 1
e64492e4
7 commits,
3 years ago
Show latest version
1 file
+
27
−
1
Side-by-side
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
AKScheduling/models.py
+
238
−
1
Options
# Create your models here.
from
django.db.models.signals
import
post_save
,
m2m_changed
from
django.dispatch
import
receiver
from
AKModel.availability.models
import
Availability
from
AKModel.models
import
AK
,
AKSlot
,
Room
,
Event
,
AKOwner
,
ConstraintViolation
def
update_constraint_violations
(
new_violations
,
existing_violations_to_check
):
"""
Update existing constraint violations (subset for which new violations were computed) based on these new violations.
This will add all new violations without a match, preserve the matching ones
and delete the obsolete ones (those without a match from the newly calculated violations).
:param new_violations: list of new (not yet saved) violations that exist after the last change
:type new_violations: list[ConstraintViolation]
:param existing_violations_to_check: list of related violations currently in the db
:type existing_violations_to_check: list[ConstraintViolation]
"""
for
new_violation
in
new_violations
:
found_match
=
False
for
existing_violation
in
existing_violations_to_check
:
if
existing_violation
.
matches
(
new_violation
):
# Remove from existing violations set since it should stay in db
existing_violations_to_check
.
remove
(
existing_violation
)
found_match
=
True
break
# Only save new violation if no match was found
if
not
found_match
:
new_violation
.
save
()
# Cleanup obsolete violations (ones without matches computed under current conditions)
for
outdated_violation
in
existing_violations_to_check
:
outdated_violation
.
delete
()
def
update_cv_reso_deadline_for_slot
(
slot
):
"""
Update constraint violation AK_AFTER_RESODEADLINE for given slot
:param slot: slot to check/update
:type slot: AKSlot
"""
event
=
slot
.
event
if
slot
.
ak
.
reso
and
slot
.
event
.
reso_deadline
:
violation_type
=
ConstraintViolation
.
ViolationType
.
AK_AFTER_RESODEADLINE
new_violations
=
[]
if
slot
.
end
>
event
.
reso_deadline
:
c
=
ConstraintViolation
(
type
=
violation_type
,
level
=
ConstraintViolation
.
ViolationLevel
.
VIOLATION
,
event
=
event
,
)
c
.
aks_tmp
.
add
(
slot
.
ak
)
c
.
ak_slots_tmp
.
add
(
slot
)
new_violations
.
append
(
c
)
update_constraint_violations
(
new_violations
,
list
(
slot
.
constraintviolation_set
.
filter
(
type
=
violation_type
)))
@receiver
(
post_save
,
sender
=
AK
)
def
ak_changed_handler
(
sender
,
instance
:
AK
,
**
kwargs
):
# Changes might affect: Owner(s), Requirements, Conflicts, Prerequisites, Category, Interest
pass
@receiver
(
m2m_changed
,
sender
=
AK
.
owners
.
through
)
def
ak_changed_handler
(
sender
,
instance
:
AK
,
action
:
str
,
**
kwargs
):
"""
Owners of AK changed
"""
# Only signal after change (post_add, post_delete, post_clear) are relevant
if
not
action
.
startswith
(
"
post
"
):
return
# print(f"{instance} changed")
event
=
instance
.
event
# Owner(s) changed: Might affect multiple AKs by the same owner(s) at the same time
violation_type
=
ConstraintViolation
.
ViolationType
.
OWNER_TWO_SLOTS
new_violations
=
[]
slots_of_this_ak
:
[
AKSlot
]
=
instance
.
akslot_set
.
filter
(
start__isnull
=
False
)
# For all owners (after recent change)...
for
owner
in
instance
.
owners
.
all
():
# ...find other slots that might be overlapping...
for
ak
in
owner
.
ak_set
.
all
():
# ...find overlapping slots...
if
ak
!=
instance
:
for
slot
in
slots_of_this_ak
:
for
other_slot
in
ak
.
akslot_set
.
filter
(
start__isnull
=
False
):
if
slot
.
overlaps
(
other_slot
):
# ...and create a temporary violation if necessary...
c
=
ConstraintViolation
(
type
=
violation_type
,
level
=
ConstraintViolation
.
ViolationLevel
.
VIOLATION
,
event
=
event
,
ak_owner
=
owner
)
c
.
aks_tmp
.
add
(
instance
)
c
.
aks_tmp
.
add
(
other_slot
.
ak
)
c
.
ak_slots_tmp
.
add
(
slot
)
c
.
ak_slots_tmp
.
add
(
other_slot
)
new_violations
.
append
(
c
)
# print(f"{owner} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
existing_violations_to_check
=
list
(
instance
.
constraintviolation_set
.
filter
(
type
=
violation_type
))
# print(existing_violations_to_check)
update_constraint_violations
(
new_violations
,
existing_violations_to_check
)
@receiver
(
post_save
,
sender
=
AKSlot
)
def
akslot_changed_handler
(
sender
,
instance
:
AKSlot
,
**
kwargs
):
# Changes might affect: Duplicate parallel, Two in room, Resodeadline
print
(
f
"
{
sender
}
changed
"
)
event
=
instance
.
event
# == Check for two parallel slots by one of the owners ==
violation_type
=
ConstraintViolation
.
ViolationType
.
OWNER_TWO_SLOTS
new_violations
=
[]
# For all owners (after recent change)...
for
owner
in
instance
.
ak
.
owners
.
all
():
# ...find other slots that might be overlapping...
for
ak
in
owner
.
ak_set
.
all
():
# ...find overlapping slots...
if
ak
!=
instance
.
ak
:
for
other_slot
in
ak
.
akslot_set
.
filter
(
start__isnull
=
False
):
if
instance
.
overlaps
(
other_slot
):
# ...and create a temporary violation if necessary...
c
=
ConstraintViolation
(
type
=
violation_type
,
level
=
ConstraintViolation
.
ViolationLevel
.
VIOLATION
,
event
=
event
,
ak_owner
=
owner
)
c
.
aks_tmp
.
add
(
instance
.
ak
)
c
.
aks_tmp
.
add
(
other_slot
.
ak
)
c
.
ak_slots_tmp
.
add
(
instance
)
c
.
ak_slots_tmp
.
add
(
other_slot
)
new_violations
.
append
(
c
)
print
(
f
"
{
owner
}
has the following conflicts:
{
new_violations
}
"
)
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
existing_violations_to_check
=
list
(
instance
.
constraintviolation_set
.
filter
(
type
=
violation_type
))
print
(
existing_violations_to_check
)
update_constraint_violations
(
new_violations
,
existing_violations_to_check
)
# == Check for two aks in the same room at the same time ==
violation_type
=
ConstraintViolation
.
ViolationType
.
ROOM_TWO_SLOTS
new_violations
=
[]
# For all slots in this room...
for
other_slot
in
instance
.
room
.
akslot_set
.
all
():
if
other_slot
!=
instance
:
# ... find overlapping slots...
if
instance
.
overlaps
(
other_slot
):
# ...and create a temporary violation if necessary...
c
=
ConstraintViolation
(
type
=
violation_type
,
level
=
ConstraintViolation
.
ViolationLevel
.
WARNING
,
event
=
event
,
room
=
instance
.
room
)
c
.
aks_tmp
.
add
(
instance
.
ak
)
c
.
aks_tmp
.
add
(
other_slot
.
ak
)
c
.
ak_slots_tmp
.
add
(
instance
)
c
.
ak_slots_tmp
.
add
(
other_slot
)
new_violations
.
append
(
c
)
print
(
f
"
Multiple slots in room
{
instance
.
room
}
:
{
new_violations
}
"
)
# ... and compare to/update list of existing violations of this type
# belonging to the slot that was recently changed (important!)
existing_violations_to_check
=
list
(
instance
.
room
.
constraintviolation_set
.
filter
(
type
=
violation_type
))
print
(
existing_violations_to_check
)
update_constraint_violations
(
new_violations
,
existing_violations_to_check
)
# == Check for reso ak after reso deadline ==
update_cv_reso_deadline_for_slot
(
instance
)
# == Check for two slots of the same AK at the same time (warning) ==
violation_type
=
ConstraintViolation
.
ViolationType
.
AK_SLOT_COLLISION
new_violations
=
[]
# For all other slots of this ak...
for
other_slot
in
instance
.
ak
.
akslot_set
.
filter
(
start__isnull
=
False
):
if
other_slot
!=
instance
:
# ... find overlapping slots...
if
instance
.
overlaps
(
other_slot
):
# ...and create a temporary violation if necessary...
c
=
ConstraintViolation
(
type
=
violation_type
,
level
=
ConstraintViolation
.
ViolationLevel
.
WARNING
,
event
=
event
,
)
c
.
aks_tmp
.
add
(
instance
.
ak
)
c
.
ak_slots_tmp
.
add
(
instance
)
c
.
ak_slots_tmp
.
add
(
other_slot
)
new_violations
.
append
(
c
)
# ... and compare to/update list of existing violations of this type
# belonging to the slot that was recently changed (important!)
existing_violations_to_check
=
list
(
instance
.
constraintviolation_set
.
filter
(
type
=
violation_type
))
update_constraint_violations
(
new_violations
,
existing_violations_to_check
)
@receiver
(
post_save
,
sender
=
Room
)
def
room_changed_handler
(
sender
,
**
kwargs
):
# Changes might affect: Room size, Requirement
print
(
f
"
{
sender
}
changed
"
)
# TODO Replace with real handling
@receiver
(
post_save
,
sender
=
Availability
)
def
availability_changed_handler
(
sender
,
**
kwargs
):
# Changes might affect: category availability, AK availability, Room availability
print
(
f
"
{
sender
}
changed
"
)
# TODO Replace with real handling
@receiver
(
post_save
,
sender
=
Event
)
def
room_changed_handler
(
sender
,
instance
,
**
kwargs
):
# == Check for reso ak after reso deadline (which might have changed) ==
if
instance
.
reso_deadline
:
for
slot
in
instance
.
akslot_set
.
filter
(
start__isnull
=
False
,
ak__reso
=
True
):
update_cv_reso_deadline_for_slot
(
slot
)
Loading