Participants Overview
Overview
Participants are the core entities in TODS that represent players, teams, and other competing units in tournament structures. The Competition Factory provides a flexible, type-agnostic participant system that supports individuals, pairs (doubles), teams, and groups.
Key Concepts
Participant Types: INDIVIDUAL, PAIR, TEAM, GROUP
Participant Agnostic: Draw logic works uniformly across all participant types
Individual Participants: The atomic units that compose pairs, teams, and groups
Position Assignments: How participants are placed in draw structures
Hydration: Adding contextual information to participant objects
Participant Types
TODS defines four core participant types:
INDIVIDUAL
The most basic participant type representing a single player or competitor.
type IndividualParticipant = {
participantId: string;
participantType: 'INDIVIDUAL';
participantRole: 'COMPETITOR' | 'ALTERNATE' | 'OFFICIAL';
person: {
personId: string;
standardFamilyName: string;
standardGivenName: string;
nationalityCode?: string;
sex?: 'MALE' | 'FEMALE';
birthDate?: string;
};
// Optional attributes
signInStatus?: 'SIGNED_IN' | 'SIGNED_OUT';
onlineResources?: OnlineResource[];
timeItems?: TimeItem[]; // Rankings, ratings, seedings
extensions?: Extension[];
};
Example:
const player = {
participantId: 'player-123',
participantType: 'INDIVIDUAL',
participantRole: 'COMPETITOR',
person: {
personId: 'person-456',
standardFamilyName: 'Federer',
standardGivenName: 'Roger',
nationalityCode: 'SUI',
sex: 'MALE',
},
};
PAIR
Represents a doubles partnership, composed of two individual participants.
type PairParticipant = {
participantId: string;
participantType: 'PAIR';
participantRole: 'COMPETITOR';
individualParticipantIds: [string, string]; // References to INDIVIDUAL participants
// Optional attributes
participantName?: string; // Custom pair name (e.g., "Smith/Jones")
timeItems?: TimeItem[]; // Pair-specific rankings/ratings
extensions?: Extension[];
};
Example:
const doublesPair = {
participantId: 'pair-789',
participantType: 'PAIR',
participantRole: 'COMPETITOR',
individualParticipantIds: ['player-123', 'player-456'],
participantName: 'Bryan/Bryan',
};
TEAM
Represents a team of individual participants, used in team competitions.
type TeamParticipant = {
participantId: string;
participantType: 'TEAM';
participantRole: 'COMPETITOR';
participantName: string;
individualParticipantIds: string[]; // Array of team members
// Optional attributes
teamName?: string;
timeItems?: TimeItem[];
extensions?: Extension[];
};
Example:
const team = {
participantId: 'team-abc',
participantType: 'TEAM',
participantRole: 'COMPETITOR',
participantName: 'United States',
individualParticipantIds: [
'player-1',
'player-2',
'player-3',
'player-4',
'player-5',
'player-6', // Team roster
],
};
GROUP
Represents a collection of participants, typically used for organizing or categorizing participants.
type GroupParticipant = {
participantId: string;
participantType: 'GROUP';
participantRole?: string;
participantName: string;
individualParticipantIds: string[];
};
Participant Roles
Participants can have different roles within a tournament:
- COMPETITOR - Active participant in tournament events (default)
- ALTERNATE - Standby participant who can replace withdrawn competitors
- OFFICIAL - Referee, umpire, or other tournament official
// Adding an official
tournamentEngine.addParticipant({
participant: {
participantType: 'INDIVIDUAL',
participantRole: 'OFFICIAL',
person: {
standardFamilyName: 'Johnson',
standardGivenName: 'Mark',
},
},
});
Participant-Agnostic Logic
A fundamental design principle of TODS: draw logic is participant-agnostic. The system doesn't differentiate between INDIVIDUAL, PAIR, or TEAM participants when managing draw structures and participant progression.
How It Works
Position Assignments are the universal mechanism:
// Same structure works for any participant type
positionAssignment = {
drawPosition: 1,
participantId: 'any-participant-id', // Could be INDIVIDUAL, PAIR, or TEAM
bye: false,
};
Match progression logic is identical:
Winner of Position 1 → Advances to Position 1 of next round
Loser of Position 2 → Feeds to Position 3 of consolation
This works whether participants are:
- Individual singles players
- Doubles pairs
- Davis Cup teams
Benefits
- Unified codebase: One set of algorithms for all tournament types
- Flexibility: Easy to create hybrid tournaments mixing different participant types
- Simplicity: Developers learn one system that works everywhere
- Maintainability: Changes to draw logic automatically apply to all participant types
Example: Multi-Type Event
const event = {
eventId: 'mixed-event',
eventType: 'MIXED', // Can include different participant types
drawDefinitions: [
{
drawId: 'singles-draw',
entries: individualParticipantEntries, // INDIVIDUAL participants
},
{
drawId: 'doubles-draw',
entries: pairParticipantEntries, // PAIR participants
},
],
};
Participant Creation
Adding Individual Participants
const { participant } = tournamentEngine.addParticipant({
participant: {
participantType: 'INDIVIDUAL',
participantRole: 'COMPETITOR',
person: {
standardFamilyName: 'Williams',
standardGivenName: 'Serena',
nationalityCode: 'USA',
sex: 'FEMALE',
birthDate: '1981-09-26',
},
},
});
Creating Pairs Automatically
The Competition Factory can automatically create PAIR participants from individuals:
// When assigning individuals to a DOUBLES matchUp, pairs are created automatically
tournamentEngine.assignTieMatchUpParticipantId({
participantId: 'player-1', // Individual
tieMatchUpId: 'doubles-matchup-id',
drawId: 'team-draw',
});
// System automatically creates a PAIR participant if needed
Adding Teams
const { participant } = tournamentEngine.addParticipant({
participant: {
participantType: 'TEAM',
participantName: 'Spain',
individualParticipantIds: ['nadal-id', 'alcaraz-id', 'bautista-id', 'lopez-id'],
},
});
Retrieving Participants
Basic Retrieval
const { participants } = tournamentEngine.getParticipants({
participantFilters: {
participantTypes: ['INDIVIDUAL'],
participantRoles: ['COMPETITOR'],
},
});
With Context (Hydration)
Add contextual information like events, matchUps, and statistics:
const { participants } = tournamentEngine.getParticipants({
withMatchUps: true, // Include matchUps for each participant
withStatistics: true, // Add win/loss statistics
withOpponents: true, // Include opponent information
withIndividualParticipants: true, // For PAIR/TEAM, include individual details
withScaleValues: true, // Include ratings/rankings
convertExtensions: true // Convert extensions to _extensionName attributes
});
// Result for a PAIR participant:
{
participantId: 'pair-123',
participantType: 'PAIR',
participantName: 'Smith/Jones',
individualParticipantIds: ['player-1', 'player-2'],
individualParticipants: [ // Added by withIndividualParticipants
{ participantId: 'player-1', person: { ... } },
{ participantId: 'player-2', person: { ... } }
],
matchUps: [...], // Added by withMatchUps
statistics: { // Added by withStatistics
matchUpsWon: 5,
matchUpsLost: 2
}
}
Filtering Participants
const participantFilters = {
// Filter by type
participantTypes: ['INDIVIDUAL', 'PAIR'],
// Filter by role
participantRoles: ['COMPETITOR'],
// Filter by events
eventIds: ['event-1', 'event-2'],
// Filter by entry status
eventEntryStatuses: ['ACCEPTED', 'ALTERNATE'],
// Filter by sign-in status
signInStatus: 'SIGNED_IN',
// Custom accessor filters
accessorValues: [{ accessor: 'person.nationalityCode', value: 'USA' }],
};
const { participants } = tournamentEngine.getParticipants({
participantFilters,
});
Individual Participants Within Groups
When retrieving PAIR, TEAM, or GROUP participants, use withIndividualParticipants to expand their composition:
const { participants } = tournamentEngine.getParticipants({
participantFilters: { participantTypes: ['PAIR'] },
withIndividualParticipants: true,
});
// Each PAIR now includes full individual details:
participants.forEach((pair) => {
console.log(`Pair: ${pair.participantName}`);
pair.individualParticipants.forEach((individual) => {
console.log(` - ${individual.person.standardGivenName} ${individual.person.standardFamilyName}`);
});
});
Participant Membership
Find all grouping participants (PAIR, TEAM, GROUP) that include a specific individual:
const {
PAIR: doublesParticipantIds,
GROUP: groupParticipantIds,
TEAM: teamParticipantIds,
} = tournamentEngine.getParticipantMembership({
participantId: 'player-123',
});
console.log(`Player appears in ${doublesParticipantIds.length} pairs`);
console.log(`Player appears in ${teamParticipantIds.length} teams`);
Sign-In Management
Track participant availability for matches:
// Check in a participant
tournamentEngine.checkInParticipant({
participantId: 'player-123',
matchUpId: 'matchup-456',
});
// Check out a participant
tournamentEngine.checkOutParticipant({
participantId: 'player-123',
matchUpId: 'matchup-456',
});
// Toggle sign-in state
tournamentEngine.toggleParticipantCheckInState({
participantId: 'player-123',
matchUpId: 'matchup-456',
});
Privacy and Data Protection
Use Participant Policies to control which participant data is exposed:
const participantPolicy = {
participant: {
// Hide birth dates
excludeBirthDates: true,
// Hide specific attributes
excludeAttributes: ['person.nationalityCode'],
// Only show initials
initialsOnly: true,
},
};
const { participants } = tournamentEngine.getParticipants({
policyDefinitions: { participant: participantPolicy },
});
// Results respect privacy settings
// { person: { standardFamilyName: 'F.', standardGivenName: 'R.' } }
Related Documentation
- Participant Context - Understanding hydration and contextual data
- Draw Generation - How participants are assigned to draws
- Participant Policy - Configuring privacy and data filters
- Query Governor - Complete API reference
- Participant Governor - Participant management methods