diff --git a/public/wcif-extensions/CompetitionConfig.json b/public/wcif-extensions/CompetitionConfig.json
index 96019e6..118dc16 100644
--- a/public/wcif-extensions/CompetitionConfig.json
+++ b/public/wcif-extensions/CompetitionConfig.json
@@ -62,6 +62,10 @@
"printScrambleCheckerForBlankScorecards": {
"description": "A flag indicating whether the box for scrambler checker signature should be printed for blank scorecards.",
"type": "boolean"
+ },
+ "printDedicatedMultiBlindScorecards": {
+ "description": "A flag indicating whether special Multi-Blind scorecards should be printed",
+ "type": "boolean"
}
},
"required": ["localNamesFirst", "printOneName", "scorecardsBackgroundUrl", "competitorsSortingRule", "noTasksForNewcomers", "tasksForOwnEventsOnly"]
diff --git a/src/components/Competition/ConfigManager/GeneralConfig/GeneralConfig.js b/src/components/Competition/ConfigManager/GeneralConfig/GeneralConfig.js
index fd033ec..f3e3bca 100644
--- a/src/components/Competition/ConfigManager/GeneralConfig/GeneralConfig.js
+++ b/src/components/Competition/ConfigManager/GeneralConfig/GeneralConfig.js
@@ -107,6 +107,7 @@ const GeneralConfig = ({ wcif, onWcifChange }) => {
printScrambleCheckerForTopRankedCompetitors,
printScrambleCheckerForFinalRounds,
printScrambleCheckerForBlankScorecards,
+ printDedicatedMultiBlindScorecards,
} = getExtensionData('CompetitionConfig', wcif);
return (
@@ -299,6 +300,18 @@ const GeneralConfig = ({ wcif, onWcifChange }) => {
label="Print out scrambler checker sign box for blank scorecards"
/>
+
+
+ }
+ label="Print out dedicated Multi-Blind scorecards"
+ />
+
{
const [tabValue, setTabValue] = useState(0);
+ const { printDedicatedMultiBlindScorecards } = getExtensionData(
+ 'CompetitionConfig',
+ wcif
+ );
+
const roundsMissingAssignmentsNames = roundsMissingAssignments(
wcif
).map(round => activityCodeToName(round.id));
@@ -49,11 +55,30 @@ const PrintingManager = ({ wcif }) => {
setTabValue(value)}>
+ {printDedicatedMultiBlindScorecards && (
+
+ )}
- {tabValue === 0 && }
+ {tabValue === 0 && (
+
+ )}
{tabValue === 1 && }
+ {printDedicatedMultiBlindScorecards && tabValue === 2 && (
+
+ )}
);
diff --git a/src/logic/documents/scorecards.js b/src/logic/documents/scorecards.js
index d40373a..71d30a6 100644
--- a/src/logic/documents/scorecards.js
+++ b/src/logic/documents/scorecards.js
@@ -48,7 +48,13 @@ const scorecardPaperSizeInfos = {
const maxAttemptCountByFormat = { '1': 1, '2': 2, '3': 3, m: 3, a: 5 };
-export const downloadScorecards = (wcif, rounds, rooms, language) => {
+export const downloadScorecards = (
+ wcif,
+ rounds,
+ rooms,
+ language,
+ orientation
+) => {
const { scorecardsBackgroundUrl, scorecardPaperSize } = getExtensionData(
'CompetitionConfig',
wcif
@@ -57,13 +63,14 @@ export const downloadScorecards = (wcif, rounds, rooms, language) => {
const pdfDefinition = scorecardsPdfDefinition(
scorecards(wcif, rounds, rooms, language),
imageData,
- scorecardPaperSize
+ scorecardPaperSize,
+ orientation
);
pdfMake.createPdf(pdfDefinition).download(`${wcif.id}-scorecards.pdf`);
});
};
-export const downloadBlankScorecards = (wcif, language) => {
+export const downloadBlankScorecards = (wcif, language, orientation) => {
const { scorecardsBackgroundUrl, scorecardPaperSize } = getExtensionData(
'CompetitionConfig',
wcif
@@ -72,7 +79,8 @@ export const downloadBlankScorecards = (wcif, language) => {
const pdfDefinition = scorecardsPdfDefinition(
blankScorecards(wcif, language),
imageData,
- scorecardPaperSize
+ scorecardPaperSize,
+ orientation
);
pdfMake
.createPdf(pdfDefinition)
@@ -83,7 +91,8 @@ export const downloadBlankScorecards = (wcif, language) => {
const scorecardsPdfDefinition = (
scorecardList,
imageData,
- scorecardPaperSize
+ scorecardPaperSize,
+ pageOrientation = 'horizontal'
) => {
const {
pageWidth,
@@ -100,7 +109,16 @@ const scorecardsPdfDefinition = (
{ x: 60, y: 590 },
{ x: 360, y: 590 },
].slice(0, scorecardsPerPage);
- const cutLines =
+
+ let actualPageWidth = pageWidth;
+ let actualPageHeight = pageHeight;
+
+ if (pageOrientation === 'landscape') {
+ actualPageWidth = pageHeight;
+ actualPageHeight = pageWidth;
+ }
+
+ const horizontalCutlines =
scorecardsPerPage === 4
? {
canvas: [
@@ -119,7 +137,28 @@ const scorecardsPdfDefinition = (
],
}
: {};
+ const verticalCutlines =
+ scorecardsPerPage === 4
+ ? {
+ canvas: [
+ cutLine({
+ y1: horizontalMargin,
+ x1: pageHeight / 2,
+ y2: pageWidth - horizontalMargin,
+ x2: pageHeight / 2,
+ }),
+ cutLine({
+ y1: pageWidth / 2,
+ x1: verticalMargin,
+ y2: pageWidth / 2,
+ x2: pageHeight - verticalMargin,
+ }),
+ ],
+ }
+ : {};
+ const cutLines =
+ pageOrientation === 'horizontal' ? horizontalCutlines : verticalCutlines;
return {
background: [
...(imageData === null
@@ -132,8 +171,9 @@ const scorecardsPdfDefinition = (
}))),
cutLines,
],
- pageSize: { width: pageWidth, height: pageHeight },
+ pageSize: { width: actualPageWidth, height: actualPageHeight },
pageMargins: [horizontalMargin, verticalMargin],
+ pageOrientation: pageOrientation,
content: {
layout: {
/* Outer margin is done using pageMargins, we use padding for the remaining inner margins. */
@@ -149,7 +189,7 @@ const scorecardsPdfDefinition = (
},
table: {
widths: Array(scorecardsPerRow).fill('*'),
- heights: pageHeight / scorecardsPerRow - 2 * verticalMargin,
+ heights: actualPageHeight / scorecardsPerRow - 2 * verticalMargin,
dontBreakRows: true,
body: chunk(scorecardList, scorecardsPerRow),
},
@@ -173,6 +213,7 @@ const scorecards = (wcif, rounds, rooms, language) => {
scorecardPaperSize,
scorecardOrder,
printScorecardsCoverSheets,
+ printDedicatedMultiBlindScorecards,
} = getExtensionData('CompetitionConfig', wcif);
const { scorecardsPerPage } = scorecardPaperSizeInfos[scorecardPaperSize];
let cards = flatMap(rounds, round => {
@@ -225,6 +266,7 @@ const scorecards = (wcif, rounds, rooms, language) => {
round,
wcif
),
+ printDedicatedMultiBlindScorecards,
})
);
if (groupCoverSheet) {
@@ -351,6 +393,7 @@ const blankScorecards = (wcif, language) => {
printStations,
scorecardPaperSize,
printScrambleCheckerForBlankScorecards,
+ printDedicatedMultiBlindScorecards,
} = getExtensionData('CompetitionConfig', wcif);
const { scorecardsPerPage } = scorecardPaperSizeInfos[scorecardPaperSize];
return flatMap(uniq(attemptCounts), attemptCount =>
@@ -362,6 +405,7 @@ const blankScorecards = (wcif, language) => {
scorecardPaperSize,
language: language,
printScrambleCheckerBox: printScrambleCheckerForBlankScorecards,
+ printDedicatedMultiBlindScorecards,
})
)
);
@@ -382,6 +426,7 @@ const scorecard = ({
featured = false,
language = 'en',
printScrambleCheckerBox,
+ printDedicatedMultiBlindScorecards = false,
}) => {
const defaultTranslationData = translation('en');
const translationData = translation(language);
@@ -391,7 +436,6 @@ const scorecard = ({
([data1, data2], key) => [data1[key], data2[key]],
[translationData, defaultTranslationData]
);
-
return phrase || defaultPhrase;
};
@@ -406,7 +450,12 @@ const scorecard = ({
} = scorecardPaperSizeInfos[scorecardPaperSize];
const scorecardWidth = pageWidth / scorecardsPerRow - 2 * horizontalMargin;
- return [
+ const isDedicatedMultiBlindScorecard =
+ printDedicatedMultiBlindScorecards &&
+ eventId &&
+ eventId.startsWith('333mbf');
+
+ const baseHeader = [
{
fontSize: 10,
columns: [
@@ -417,7 +466,7 @@ const scorecard = ({
featured
? {
text: '★',
- font: 'WenQuanYiZenHei', // Roboto (default) does not support unicode icons like ★
+ font: 'WenQuanYiZenHei',
alignment: 'right',
}
: {},
@@ -430,133 +479,194 @@ const scorecard = ({
margin: [0, 0, 0, 10],
alignment: 'center',
},
- {
- margin: [25, 0, 0, 0],
- table: {
- widths: ['*', 30, 30, ...(printStations ? [30] : [])],
- body: [
- columnLabels([
- t('eventLabel'),
- { text: t('round'), alignment: 'center' },
- { text: t('group'), alignment: 'center' },
- ...(printStations
- ? [{ text: t('station'), alignment: 'center' }]
- : []),
- ]),
- [
- eventId ? t('eventName', eventId) : ' ',
- { text: roundNumber, alignment: 'center' },
- { text: groupNumber, alignment: 'center' },
- ...(printStations
- ? [{ text: stationNumber, alignment: 'center' }]
- : []),
- ],
+ ];
+
+ const eventRoundTable = {
+ margin: [25, 0, 0, 0],
+ table: {
+ widths: ['*', 30, 30, ...(printStations ? [30] : [])],
+ body: [
+ columnLabels([
+ t('eventLabel'),
+ { text: t('round'), alignment: 'center' },
+ { text: t('group'), alignment: 'center' },
+ ...(printStations
+ ? [{ text: t('station'), alignment: 'center' }]
+ : []),
+ ]),
+ [
+ eventId ? t('eventName', eventId) : ' ',
+ { text: roundNumber, alignment: 'center' },
+ { text: groupNumber, alignment: 'center' },
+ ...(printStations
+ ? [{ text: stationNumber, alignment: 'center' }]
+ : []),
],
- },
+ ],
},
- {
- margin: [25, 0, 0, 0],
- table: {
- widths: [30, '*'],
- body: [
- columnLabels([
- 'ID',
- [
- { text: t('name'), alignment: 'left', width: 'auto' },
- {
- text:
- competitor.wcaId ||
- // If the competitor has a name, then this is a new competitor
- // Else this is a blank scorecard
- (competitor.name ? t('newCompetitor') : ' '),
- alignment: 'right',
- },
- ],
- ]),
+ };
+
+ const competitorTable = {
+ margin: [25, 0, 0, 0],
+ table: {
+ widths: [30, '*'],
+ body: [
+ columnLabels([
+ 'ID',
[
- { text: competitor.registrantId || ' ', alignment: 'center' },
+ { text: t('name'), alignment: 'left', width: 'auto' },
{
- text: pdfName(competitor.name || ' ', {
- swapLatinWithLocalNames: localNamesFirst,
- short: printOneName,
- }),
- maxHeight: 20 /* See: https://github.com/bpampuch/pdfmake/issues/264#issuecomment-108347567 */,
+ text:
+ competitor.wcaId ||
+ (competitor.name ? t('newCompetitor') : ' '),
+ alignment: 'right',
},
],
+ ]),
+ [
+ { text: competitor.registrantId || ' ', alignment: 'center' },
+ {
+ text: pdfName(competitor.name || ' ', {
+ swapLatinWithLocalNames: localNamesFirst,
+ short: printOneName,
+ }),
+ maxHeight: 20,
+ },
],
- },
+ ],
},
- {
- margin: [0, 10, 0, 0],
- table: {
- widths: [
- 16,
- 25,
- ...(printScrambleCheckerBox ? [25] : []),
- '*',
- 25,
- 25,
- ] /* Note: 16 (width) + 4 + 4 (defult left and right padding) + 1 (left border) = 25 */,
- body: [
- columnLabels(
- [
- '',
- t('scr'),
- ...(printScrambleCheckerBox ? [t('check')] : []),
- t('result'),
- t('judge'),
- t('comp'),
- ],
- {
- alignment: 'center',
- }
- ),
- ...attemptRows(
- cutoff,
- attemptCount,
- scorecardWidth,
- printScrambleCheckerBox
- ),
+ };
+
+ const multiBlindTable = {
+ margin: [0, 10, 0, 0],
+ table: {
+ widths: [
+ 16,
+ 25,
+ 25,
+ ...(printScrambleCheckerBox ? [25] : []),
+ 25,
+ 25,
+ '*',
+ 25,
+ 25,
+ ],
+ body: [
+ columnLabels(
[
- {
- text: t('extra') + ' (' + t('delegateInitials') + ' _______)',
- ...noBorder,
- colSpan: 5 + printScrambleCheckerBox,
- margin: [0, 1],
- fontSize: 10,
- },
+ '',
+ t('del'),
+ t('scr'),
+ ...(printScrambleCheckerBox ? [t('check')] : []),
+ t('solved'),
+ t('declared'),
+ t('time'),
+ t('judge'),
+ t('comp'),
],
- attemptRow('_', printScrambleCheckerBox),
+ { alignment: 'center' }
+ ),
+ ...Array.from({ length: attemptCount }).map((_, i) =>
+ multiBlindAttemptRow(i + 1, printScrambleCheckerBox)
+ ),
+ [
+ {
+ text: t('extra') + ' (' + t('delegateInitials') + ' _______)',
+ ...noBorder,
+ colSpan: 8 + (printScrambleCheckerBox ? 1 : 0),
+ margin: [0, 1],
+ fontSize: 10,
+ },
+ ...Array(printScrambleCheckerBox ? 1 : 0).fill(''),
+ ],
+ multiBlindAttemptRow('_', printScrambleCheckerBox),
+ [
+ {
+ text: '',
+ ...noBorder,
+ colSpan: 8 + (printScrambleCheckerBox ? 1 : 0),
+ margin: [0, 1],
+ },
+ ...Array(printScrambleCheckerBox ? 1 : 0).fill(''),
+ ],
+ ],
+ },
+ };
+
+ const standardAttemptTable = {
+ margin: [0, 10, 0, 0],
+ table: {
+ widths: [16, 25, ...(printScrambleCheckerBox ? [25] : []), '*', 25, 25],
+ body: [
+ columnLabels(
[
- {
- text: '',
- ...noBorder,
- colSpan: 5 + printScrambleCheckerBox,
- margin: [0, 1],
- },
+ '',
+ t('scr'),
+ ...(printScrambleCheckerBox ? [t('check')] : []),
+ t('result'),
+ t('judge'),
+ t('comp'),
],
+ {
+ alignment: 'center',
+ }
+ ),
+ ...attemptRows(
+ cutoff,
+ attemptCount,
+ scorecardWidth,
+ printScrambleCheckerBox
+ ),
+ [
+ {
+ text: t('extra') + ' (' + t('delegateInitials') + ' _______)',
+ ...noBorder,
+ colSpan: 5 + (printScrambleCheckerBox ? 1 : 0),
+ margin: [0, 1],
+ fontSize: 10,
+ },
+ ...Array(printScrambleCheckerBox ? 1 : 0).fill(''),
+ ],
+ attemptRow('_', printScrambleCheckerBox),
+ [
+ {
+ text: '',
+ ...noBorder,
+ colSpan: 5 + (printScrambleCheckerBox ? 1 : 0),
+ margin: [0, 1],
+ },
+ ...Array(printScrambleCheckerBox ? 1 : 0).fill(''),
],
- },
- },
- {
- fontSize: 10,
- columns: [
- cutoff
- ? {
- text: `${t('cutoff')}: ${cutoffToString(cutoff, eventId)}`,
- alignment: 'center',
- }
- : {},
- timeLimit
- ? {
- text: `${t('timeLimit')}: ${timeLimitToString(timeLimit, {
- totalText: t('total'),
- })}`,
- alignment: 'center',
- }
- : {},
],
},
+ };
+
+ const limitsRow = {
+ fontSize: 10,
+ columns: [
+ cutoff
+ ? {
+ text: `${t('cutoff')}: ${cutoffToString(cutoff, eventId)}`,
+ alignment: 'center',
+ }
+ : {},
+ timeLimit
+ ? {
+ text: `${t('timeLimit')}: ${timeLimitToString(timeLimit, {
+ totalText: t('total'),
+ })}`,
+ alignment: 'center',
+ }
+ : {},
+ ],
+ };
+
+ return [
+ ...baseHeader,
+ eventRoundTable,
+ competitorTable,
+ isDedicatedMultiBlindScorecard ? multiBlindTable : standardAttemptTable,
+ limitsRow,
];
};
@@ -729,4 +839,22 @@ const attemptRow = (attemptNumber, needsScrambleChecker) => [
{},
];
+const multiBlindAttemptRow = (attemptNumber, needsScrambleChecker) => [
+ {
+ text: attemptNumber,
+ ...noBorder,
+ fontSize: 20,
+ bold: true,
+ alignment: 'center',
+ },
+ {},
+ {},
+ ...(needsScrambleChecker ? [{}] : []),
+ {},
+ {},
+ {},
+ {},
+ {},
+];
+
const noBorder = { border: [false, false, false, false] };
diff --git a/src/logic/translations.js b/src/logic/translations.js
index b3e19fc..a54c2af 100644
--- a/src/logic/translations.js
+++ b/src/logic/translations.js
@@ -24,6 +24,10 @@ const texts = {
scr: 'Scr',
check: 'Check',
result: 'Result',
+ del: 'Del',
+ solved: 'Solv',
+ declared: 'Decl',
+ time: 'Time',
judge: 'Judge',
comp: 'Comp',
extra: 'Extra attempt',
@@ -314,6 +318,10 @@ const texts = {
scr: 'Miesz',
check: 'Spr',
result: 'Wynik',
+ del: 'Del',
+ solved: 'Ułożo',
+ declared: 'Dekla',
+ time: 'Czas',
judge: 'Sędz',
comp: 'Zaw',
extra: 'Dodatkowe ułożenie',
diff --git a/src/logic/wcif-extensions.js b/src/logic/wcif-extensions.js
index a973b29..9855584 100644
--- a/src/logic/wcif-extensions.js
+++ b/src/logic/wcif-extensions.js
@@ -40,6 +40,7 @@ const defaultExtensionData = {
printScrambleCheckerForTopRankedCompetitors: false,
printScrambleCheckerForFinalRounds: false,
printScrambleCheckerForBlankScorecards: false,
+ printDedicatedMultiBlindScorecards: false,
},
};