Skip to content

Commit b228565

Browse files
committed
fix assigning designated participants to positions they dont qualify for
1 parent 511597d commit b228565

File tree

2 files changed

+25
-6
lines changed

2 files changed

+25
-6
lines changed

ephios/core/services/matching.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class Position:
1919
preferred_by: Collection[AbstractParticipant] # preferred by participant (less important)
2020
label: Optional[str] = None
2121
aux_score: float = 0.0 # additional score control in range [0,1]
22+
designation_only: bool = (
23+
False # if this Position was created purely out of overdesignation, mark it here
24+
)
2225

2326
def __post_init__(self):
2427
self.required_qualifications = frozenset(self.required_qualifications)
@@ -128,27 +131,41 @@ def score_pairing(
128131
required_value = padded_participant_count * sum(
129132
(base_score, preferred_value, max_skill_value, confirmed_value, max_aux_value)
130133
)
131-
designated_value = required_value**2
132-
unqualified_penalty = designated_value**2
134+
designated_unqualified_value = required_value**2
135+
designated_and_qualified_value = 2 * designated_unqualified_value
136+
undesignated_unqualified_value = designated_unqualified_value**2
133137

134138
is_designated = participant in position.designated_for
135139
# optimally, we should reject a pairing for a participant designated for another position,
136140
# but we don't have that info here.
141+
is_qualified = position.required_skill <= participant.skill
137142

138-
if not is_designated and not position.required_skill <= participant.skill:
143+
if not is_designated and not is_qualified:
139144
# the participant does not have some required skill
140-
return -unqualified_penalty # avoid matching unqualified participants
145+
return -undesignated_unqualified_value # avoid matching unqualified participants
141146

142147
score = base_score
143148
if is_designated:
144-
score += designated_value
149+
if is_qualified:
150+
score += designated_and_qualified_value
151+
else:
152+
# designated participants get assigned even if they don't qualify, and at a lower score
153+
score += designated_unqualified_value
154+
145155
if participant in position.preferred_by:
146156
score += preferred_value
147157
if position.required:
148158
score += required_value
149159
if participant in confirmed_participants:
150160
score += confirmed_value
151-
score += position.skill_level * max_skill_value
161+
162+
if is_qualified:
163+
# if qualified, lets prefer high skill positions
164+
score += position.skill_level * max_skill_value
165+
else:
166+
# if not qualified (but designated), let's prefer low skill positions
167+
score -= position.skill_level * max_skill_value
168+
152169
score += position.aux_score * max_aux_value
153170
return score
154171

ephios/plugins/complexsignup/structure.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def atomic_block_participant_qualifies_for(structure, participant: AbstractParti
3434
if block["qualification_ids"] <= available_qualification_ids
3535
and any(
3636
{q.id for q in position.required_qualifications} <= available_qualification_ids
37+
and not position.designation_only
3738
for position in block["positions"]
3839
)
3940
]
@@ -472,6 +473,7 @@ def _build_atomic_block_structure(
472473
aux_score=0,
473474
required=False, # designated -> always optional
474475
label=block.name,
476+
designation_only=True,
475477
)
476478
participation = matching.participation_for_position(opt_match_id) if matching else None
477479
structure["signup_stats"] += SignupStats.ZERO.replace(

0 commit comments

Comments
 (0)