blob: 92bbdacfb2b241d03481adb83ad150b348e153bb [file] [log] [blame]
/**
* @license
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
AccountInfo,
ApprovalInfo,
DetailedLabelInfo,
isDetailedLabelInfo,
LabelInfo,
LabelNameToInfoMap,
VotingRangeInfo,
} from '../types/common';
// Name of the standard Code-Review label.
export const CODE_REVIEW = 'Code-Review';
export enum LabelStatus {
APPROVED = 'APPROVED',
REJECTED = 'REJECTED',
RECOMMENDED = 'RECOMMENDED',
DISLIKED = 'DISLIKED',
NEUTRAL = 'NEUTRAL',
}
export function getVotingRange(label?: LabelInfo): VotingRangeInfo | undefined {
if (!label || !isDetailedLabelInfo(label) || !label.values) return undefined;
const values = Object.keys(label.values).map(v => Number(v));
values.sort((a, b) => a - b);
if (!values.length) return undefined;
return {min: values[0], max: values[values.length - 1]};
}
export function getVotingRangeOrDefault(label?: LabelInfo): VotingRangeInfo {
const range = getVotingRange(label);
return range ? range : {min: 0, max: 0};
}
/**
* If we don't know the label config, then we still need some way to decide
* which vote value is the most important one, so we apply the standard rule
* of a Code-Review label, where -2 blocks. So the most negative vote is
* regarded as representative, if its absolute value is greater than or equal
* to the most positive vote.
*/
export function getRepresentativeValue(label?: DetailedLabelInfo): number {
if (!label?.all) return 0;
const allValues = label.all.map(approvalInfo => approvalInfo.value ?? 0);
if (allValues.length === 0) return 0;
const max = Math.max(...allValues);
const min = Math.min(...allValues);
return max > -min ? max : min;
}
export function getLabelStatus(label?: DetailedLabelInfo): LabelStatus {
const value = getRepresentativeValue(label);
const range = getVotingRangeOrDefault(label);
if (value < 0) {
return value === range.min ? LabelStatus.REJECTED : LabelStatus.DISLIKED;
}
if (value > 0) {
return value === range.max ? LabelStatus.APPROVED : LabelStatus.RECOMMENDED;
}
return LabelStatus.NEUTRAL;
}
export function valueString(value?: number) {
if (!value) return ' 0';
let s = `${value}`;
if (value > 0) s = `+${s}`;
return s;
}
export function getMaxAccounts(label?: LabelInfo): ApprovalInfo[] {
if (!label || !isDetailedLabelInfo(label) || !label.all) return [];
const votingRange = getVotingRangeOrDefault(label);
return label.all.filter(account => account.value === votingRange.max);
}
export function getApprovalInfo(
label: DetailedLabelInfo,
account: AccountInfo
): ApprovalInfo | undefined {
return label.all?.filter(x => x._account_id === account._account_id)[0];
}
export function labelCompare(labelName1: string, labelName2: string) {
if (labelName1 === CODE_REVIEW && labelName2 === CODE_REVIEW) return 0;
if (labelName1 === CODE_REVIEW) return -1;
if (labelName2 === CODE_REVIEW) return 1;
return labelName1.localeCompare(labelName2);
}
export function getCodeReviewLabel(
labels: LabelNameToInfoMap
): LabelInfo | undefined {
for (const label of Object.keys(labels)) {
if (label === CODE_REVIEW) {
return labels[label];
}
}
return;
}