Reviewer-Proposal Matching System
The Waldur proposal module includes an automated reviewer-proposal matching system that computes expertise affinity scores and uses them to suggest reviewers and to generate reviewer assignments. This ensures qualified reviewers are matched with proposals in their area of expertise.
Architecture Overview
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
Affinity Scoring Methods
The system computes affinity scores between reviewers and proposals using configurable methods.
Keyword-Based Scoring
Matches reviewer expertise keywords against proposal text content.
How it works:
- Extracts reviewer's expertise keywords with proficiency weights
- Searches proposal text (title, summary, description) for keyword matches
- Calculates weighted match score
Proficiency weights:
| Proficiency Level | Weight |
|---|---|
| Expert | 1.0 |
| Familiar | 0.7 |
| Basic | 0.3 |
1 2 3 4 5 6 7 8 9 10 | |
TF-IDF Text Similarity
Computes semantic similarity between reviewer expertise and proposal content using Term Frequency-Inverse Document Frequency (TF-IDF) vectors.
Reviewer text sources:
- Expertise keywords (weighted by proficiency)
- Recent publication titles and abstracts (last 5 years)
- Biography text
Proposal text sources:
- Proposal name/title
- Project summary
- Project description
Algorithm:
- Tokenize text (lowercase, remove stopwords)
- Compute TF-IDF vectors for reviewer and proposal
- Calculate cosine similarity between vectors
1 2 3 4 5 6 | |
Combined Method
Default method that combines keyword and text-based scoring with configurable weights.
1 2 3 4 5 | |
Matching Configuration
Each call can have its own matching configuration via MatchingConfiguration:
| Field | Type | Default | Description |
|---|---|---|---|
affinity_method |
choice | combined |
Scoring method: keyword, tfidf, or combined |
keyword_weight |
float | 0.4 | Weight for keyword scoring (0-1) |
text_weight |
float | 0.6 | Weight for TF-IDF scoring (0-1) |
min_reviewers_per_proposal |
int | 3 | Minimum reviewers per proposal |
max_reviewers_per_proposal |
int | 5 | Maximum reviewers per proposal |
min_proposals_per_reviewer |
int | 3 | Minimum proposals per reviewer |
max_proposals_per_reviewer |
int | 10 | Maximum proposals per reviewer |
algorithm |
choice | minmax |
Reserved; not used by assignment today |
min_affinity_threshold |
float | 0.1 | Minimum affinity for suggestions |
use_reviewer_bids |
bool | true | Reserved; bids are not used today |
bid_weight |
float | 0.3 | Reserved; bids are not used today |
Validation: keyword_weight + text_weight must equal 1.0
Assignment
POST /api/proposal-protected-calls/{uuid}/generate-assignments/ creates draft
assignment batches (AssignmentBatch, one per reviewer, with an AssignmentItem
per reviewer–proposal pair) that the call manager reviews and sends. For each
submitted or in-review proposal it picks reviewers in one greedy pass:
- Accepted pool members ordered by their stored affinity for the proposal, highest first.
- Skipped: reviewers with a
pendingorrecusedconflict of interest on the proposal (dismissedandwaivedconflicts do not block), and reviewers already assigned to it. - If affinity yields too few reviewers, remaining accepted pool members are added in pool order.
The number of reviewers per proposal comes from the request or, if absent, the
call's min_reviewers_per_proposal (2 when the call has no matching
configuration). There is no global optimisation across proposals: the
algorithm setting (minmax, fairflow, hungarian) is stored but not read.
Reviewer Bids
Reviewers can record a preference for each proposal (eager, willing,
not_willing, conflict) through /api/reviewer-bids/. Bids are stored and
listed only: neither affinity scoring nor assignment reads them, and a
conflict bid does not create a conflict-of-interest record. Declare conflicts
through COI disclosure instead.
Reviewer Discovery Workflow
The system supports two paths for finding reviewers:
Path A: Algorithm-Based Discovery
For reviewers with published profiles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
Path B: Direct Email Invitation
For reviewers without existing profiles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Reviewer Profile Visibility
Reviewer profiles have visibility controls:
| Field | Default | Description |
|---|---|---|
is_published |
false | Profile discoverable by algorithm |
available_for_reviews |
true | Currently accepting review requests |
published_at |
null | When profile was published |
Visibility rules:
- Own profile: Always visible to the profile owner
- Pool members: Managers see ACCEPTED pool members only
- Suggestions: Managers see full profile in suggestion list
- Discovery: Algorithm only considers published + available profiles
Suggestion Status Workflow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
API Endpoints
Affinity Computation
1 | |
Computes affinity scores for all reviewer-proposal pairs in the call.
Response:
1 2 3 4 5 | |
Get Affinity Matrix
1 | |
Returns the complete affinity matrix for visualization.
Response:
1 2 3 4 5 6 7 8 9 10 11 | |
Generate Suggestions
1 | |
Runs affinity algorithm on all published profiles to generate reviewer suggestions.
Response:
1 2 3 4 5 | |
View Suggestions
1 | |
Lists all suggestions for a call with affinity scores.
Filter parameters:
status: Filter by status (pending,confirmed,rejected,invited)min_affinity_score: Minimum affinity score (0-1)reviewer_uuid: Filter by specific reviewer
Manage Suggestions
1 2 | |
Manager confirms or rejects suggestions. Rejection requires a reason.
Send Invitations
1 | |
Sends invitations to all confirmed suggestions.
Invite by Email
1 | |
Invites a reviewer by email address (profile not required initially).
Request:
1 2 3 4 5 | |
View Invitation Details
1 | |
Returns invitation details for reviewers to review before accepting.
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Accept Invitation
1 | |
Accepts an invitation with optional COI self-declaration.
Request:
1 2 3 4 5 6 7 8 9 10 | |
Notes:
declared_conflictsis optional- Creates
ConflictOfInterestrecords withdetection_method='self_disclosed' - Acceptance NOT blocked by declared conflicts (manager handles via COI workflow)
- For email invitations, requires published profile first
Decline Invitation
1 | |
Declines a reviewer invitation.
Request:
1 2 3 | |
Manage Reviewer Bids
1 2 3 | |
Manage reviewer preferences for proposals.
Data Models
ReviewerProposalAffinity
Cached affinity scores between reviewers and proposals.
| Field | Type | Description |
|---|---|---|
call |
FK | Call for this affinity |
reviewer |
FK | Reviewer profile |
proposal |
FK | Proposal |
affinity_score |
float | Combined score (0-1) |
keyword_score |
float | Keyword-based score |
text_score |
float | TF-IDF score |
ReviewerSuggestion
Algorithm-generated reviewer suggestions.
| Field | Type | Description |
|---|---|---|
call |
FK | Call for suggestion |
reviewer |
FK | Suggested reviewer profile |
affinity_score |
float | Combined score (0-1) |
keyword_score |
float | Keyword match score |
text_score |
float | Text similarity score |
status |
choice | pending/confirmed/rejected/invited |
reviewed_by |
FK | Manager who reviewed |
reviewed_at |
datetime | When reviewed |
rejection_reason |
text | Reason for rejection |
ProposedAssignment
Reserved for algorithm-proposed assignments. No code path writes it today; the
proposed-assignments endpoint only lists it. Assignments are created as
AssignmentBatch / AssignmentItem.
| Field | Type | Description |
|---|---|---|
call |
FK | Call for assignment |
reviewer |
FK | Assigned reviewer |
proposal |
FK | Assigned proposal |
affinity_score |
float | Score at assignment time |
algorithm_used |
choice | minmax/fairflow/hungarian |
rank |
int | Assignment priority (1=best) |
is_deployed |
bool | Assignment finalized |
deployed_at |
datetime | When deployed |
deployed_by |
FK | Who deployed |
ReviewerBid
Reviewer preferences for proposals.
| Field | Type | Description |
|---|---|---|
call |
FK | Call |
reviewer |
FK | Reviewer profile |
proposal |
FK | Proposal |
bid |
choice | eager/willing/not_willing/conflict |
comment |
text | Optional explanation |
Integration with COI Detection
The matching system integrates with Conflict of Interest Detection:
generate-assignmentsskips reviewers with apendingorrecusedconflict on the proposaldismissedandwaivedconflicts do not block assignment- Reviewer suggestions do not check conflicts; the affinity matrix reports each pair's conflict status
- A
conflictbid is not a conflict record and is not checked
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Performance Considerations
Affinity Computation
- Computation is O(reviewers × proposals)
- Results cached in
ReviewerProposalAffinitytable - Recompute when profiles or proposals change significantly
Corpus IDF
- TF-IDF uses corpus-wide IDF for better term weighting
- Corpus includes all reviewer texts and proposal texts
- Computed once per call, reused for all pairs
Large Calls
- For calls with 100+ proposals and 50+ reviewers:
- Use batch processing
- Consider incremental updates
- Monitor computation time
Related Documentation
- Proposals Overview - Complete proposal module documentation
- Conflict of Interest Detection - COI detection and management
- Review System - Review assignment and scoring