Extract weblate types, constants, logic

This commit is contained in:
schroda
2025-02-17 02:07:21 +01:00
parent f39c902b5f
commit 5dcbf6c168
6 changed files with 184 additions and 146 deletions

View File

@@ -10,7 +10,8 @@ import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import dayjs from 'dayjs';
import { createCommitChangelog } from './release/CommitChangelog.utils.ts';
import { createTranslationChangelog, validateWeblateDates } from './release/TranslationChangelog.utils.ts';
import { createTranslationChangelog } from './release/TranslationChangelog.utils.ts';
import { validateWeblateDates } from './weblate/Weblate.utils.ts';
const { sha, date } = yargs(hideBin(process.argv))
.options({

View File

@@ -8,10 +8,8 @@
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import {
createTranslationChangelog,
TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT,
} from './release/TranslationChangelog.utils.ts';
import { createTranslationChangelog } from './release/TranslationChangelog.utils.ts';
import { TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT } from './weblate/Weblate.constants.ts';
const { afterDate, beforeDate, requiredContributionCount, keepKnownContributors } = yargs(hideBin(process.argv))
.options({

View File

@@ -11,136 +11,16 @@ import fs from 'fs';
import readline from 'readline';
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
import tokens from '../tokens.json';
enum WeblateChangeActions {
RESOURCE_UPDATED = 0,
TRANSLATION_COMPLETED = 1,
TRANSLATION_CHANGED = 2,
COMMENT_ADDED = 3,
SUGGESTION_ADDED = 4,
TRANSLATION_ADDED = 5,
SUGGESTION_ACCEPTED = 7,
TRANSLATION_REVERTED = 8,
TRANSLATION_UPLOADED = 9,
COMPONENT_LOCKED = 14,
COMPONENT_UNLOCKED = 15,
CHANGES_COMMITTED = 17,
CHANGES_PUSHED = 18,
REPOSITORY_RESET = 19,
REPOSITORY_MERGED = 20,
REPOSITORY_REBASED = 21,
REPOSITORY_MERGE_FAILED = 22,
REPOSITORY_REBASE_FAILED = 23,
PARSING_FAILED = 24,
TRANSLATION_REMOVED = 25,
SUGGESTION_REMOVED = 26,
MARKED_FOR_EDIT = 37,
COMPONENT_RENAMED = 42,
CONTRIBUTOR_JOINED = 45,
LANGUAGE_ADDED = 48,
COMPONENT_CREATED = 51,
ADD_ON_CONFIGURATION_CHANGED = 61,
}
const creditRelevantActions: WeblateChangeActions[] = [
WeblateChangeActions.TRANSLATION_CHANGED,
WeblateChangeActions.TRANSLATION_ADDED,
WeblateChangeActions.TRANSLATION_REVERTED,
WeblateChangeActions.TRANSLATION_UPLOADED,
WeblateChangeActions.TRANSLATION_REMOVED,
WeblateChangeActions.MARKED_FOR_EDIT,
];
interface WeblateChangeResult {
translation: string;
action: WeblateChangeActions;
action_name: keyof WeblateChangeActions;
user: string;
}
interface WeblateChangePayload {
next: string;
results: WeblateChangeResult[];
}
interface WeblateUserPayload {
full_name: string;
}
interface WeblateLanguagePayload {
language: {
name: string;
};
}
type Username = string;
type UserUrl = string;
type LanguageName = string;
type TranslationUrl = string;
type UsernameByUserUrl = Record<UserUrl, Username>;
type LanguageNameByTranslationUrl = Record<TranslationUrl, LanguageName>;
type UserUrlsByTranslationUrl = Record<TranslationUrl, UserUrl[]>;
type ActionByTranslationUrlByUserUrl = Record<
UserUrl,
Record<TranslationUrl, { count: number; actions: Record<WeblateChangeActions, number> }>
>;
interface Contributor {
username: Username;
contributionCount: number;
}
type ContributionsByLanguage = Record<LanguageName, Contributor[]>;
export const TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT = {
requiredContributionCount: 10,
keepKnownContributors: true,
};
const fetchData = async <T = any>(url: string, authToken: string): Promise<T> => {
const response = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Token ${authToken}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`Weblate request failed with status ${response.status} - ${response.statusText}`);
}
return response.json();
};
const fetchWeblateChanges = async (
url: string,
authToken: string,
weblateChangeResults: WeblateChangeResult[] = [],
): Promise<WeblateChangeResult[]> => {
const weblateChangePage = await fetchData<WeblateChangePayload>(url, authToken);
if (!weblateChangePage.next) {
return [...weblateChangePage.results, ...weblateChangeResults];
}
return fetchWeblateChanges(weblateChangePage.next, authToken, [
...weblateChangePage.results,
...weblateChangeResults,
]);
};
const getUsername = async (url: string, authToken: string): Promise<string> => {
const userPayload = await fetchData<WeblateUserPayload>(url, authToken);
return userPayload.full_name;
};
const getLanguageName = async (url: string, authToken: string): Promise<string> => {
const languagePayload = await fetchData<WeblateLanguagePayload>(url, authToken);
return languagePayload.language.name.replace(/\(([a-zA-Z]+) Han script\)/g, '($1)');
};
import {
ActionByTranslationUrlByUserUrl,
ContributionsByLanguage,
LanguageNameByTranslationUrl,
UsernameByUserUrl,
UserUrlsByTranslationUrl,
WeblateChangeResult,
} from '../weblate/Weblate.types.ts';
import { fetchWeblateChanges, getLanguageName, getUsername, validateWeblateDates } from '../weblate/Weblate.utils.ts';
import { creditRelevantActions, TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT } from '../weblate/Weblate.constants.ts';
const extractContributionInfoFromChanges = (
changes: WeblateChangeResult[],
@@ -305,17 +185,6 @@ const getKnownContributorsByLanguage = async (): Promise<Record<string, string[]
return contributorByLanguage;
};
const dateRegex = /[0-9]{4}-[0-9]{2}-[0-9]{2}/g;
const isValidIS08601Date = (date: string): boolean => !!date.match(dateRegex);
export const validateWeblateDates = (afterDate: string, beforeDate: string) => {
if (!isValidIS08601Date(afterDate) || !isValidIS08601Date(beforeDate)) {
throw new Error(
`The passed timestamps are not properly formatted. They have to match "${dateRegex}" (e.g. 2024-05-11)`,
);
}
};
export const createTranslationChangelog = async (
afterDate: string,
beforeDate: string,

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { WeblateChangeActions } from '@/../tools/scripts/weblate/Weblate.types';
export const creditRelevantActions: WeblateChangeActions[] = [
WeblateChangeActions.TRANSLATION_CHANGED,
WeblateChangeActions.TRANSLATION_ADDED,
WeblateChangeActions.TRANSLATION_REVERTED,
WeblateChangeActions.TRANSLATION_UPLOADED,
WeblateChangeActions.TRANSLATION_REMOVED,
WeblateChangeActions.MARKED_FOR_EDIT,
];
export const TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT = {
requiredContributionCount: 10,
keepKnownContributors: true,
};

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
export enum WeblateChangeActions {
RESOURCE_UPDATED = 0,
TRANSLATION_COMPLETED = 1,
TRANSLATION_CHANGED = 2,
COMMENT_ADDED = 3,
SUGGESTION_ADDED = 4,
TRANSLATION_ADDED = 5,
SUGGESTION_ACCEPTED = 7,
TRANSLATION_REVERTED = 8,
TRANSLATION_UPLOADED = 9,
COMPONENT_LOCKED = 14,
COMPONENT_UNLOCKED = 15,
CHANGES_COMMITTED = 17,
CHANGES_PUSHED = 18,
REPOSITORY_RESET = 19,
REPOSITORY_MERGED = 20,
REPOSITORY_REBASED = 21,
REPOSITORY_MERGE_FAILED = 22,
REPOSITORY_REBASE_FAILED = 23,
PARSING_FAILED = 24,
TRANSLATION_REMOVED = 25,
SUGGESTION_REMOVED = 26,
MARKED_FOR_EDIT = 37,
COMPONENT_RENAMED = 42,
CONTRIBUTOR_JOINED = 45,
LANGUAGE_ADDED = 48,
COMPONENT_CREATED = 51,
ADD_ON_CONFIGURATION_CHANGED = 61,
}
export interface WeblateChangeResult {
translation: string;
action: WeblateChangeActions;
action_name: keyof WeblateChangeActions;
user: string;
}
export interface WeblateChangePayload {
next: string;
results: WeblateChangeResult[];
}
export interface WeblateUserPayload {
full_name: string;
}
export interface WeblateLanguagePayload {
language: {
name: string;
};
}
export type Username = string;
export type UserUrl = string;
export type LanguageName = string;
export type TranslationUrl = string;
export type UsernameByUserUrl = Record<UserUrl, Username>;
export type LanguageNameByTranslationUrl = Record<TranslationUrl, LanguageName>;
export type UserUrlsByTranslationUrl = Record<TranslationUrl, UserUrl[]>;
export type ActionByTranslationUrlByUserUrl = Record<
UserUrl,
Record<TranslationUrl, { count: number; actions: Record<WeblateChangeActions, number> }>
>;
export interface Contributor {
username: Username;
contributionCount: number;
}
export type ContributionsByLanguage = Record<LanguageName, Contributor[]>;

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import {
WeblateChangePayload,
WeblateChangeResult,
WeblateLanguagePayload,
WeblateUserPayload,
} from '@/../tools/scripts/weblate/Weblate.types.ts';
export const fetchData = async <T = any>(url: string, authToken: string): Promise<T> => {
const response = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Token ${authToken}`,
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`Weblate request failed with status ${response.status} - ${response.statusText}`);
}
return response.json();
};
export const fetchWeblateChanges = async (
url: string,
authToken: string,
weblateChangeResults: WeblateChangeResult[] = [],
): Promise<WeblateChangeResult[]> => {
const weblateChangePage = await fetchData<WeblateChangePayload>(url, authToken);
if (!weblateChangePage.next) {
return [...weblateChangePage.results, ...weblateChangeResults];
}
return fetchWeblateChanges(weblateChangePage.next, authToken, [
...weblateChangePage.results,
...weblateChangeResults,
]);
};
export const getUsername = async (url: string, authToken: string): Promise<string> => {
const userPayload = await fetchData<WeblateUserPayload>(url, authToken);
return userPayload.full_name;
};
export const getLanguageName = async (url: string, authToken: string): Promise<string> => {
const languagePayload = await fetchData<WeblateLanguagePayload>(url, authToken);
return languagePayload.language.name.replace(/\(([a-zA-Z]+) Han script\)/g, '($1)');
};
const dateRegex = /[0-9]{4}-[0-9]{2}-[0-9]{2}/g;
const isValidIS08601Date = (date: string): boolean => !!date.match(dateRegex);
export const validateWeblateDates = (afterDate: string, beforeDate: string) => {
if (!isValidIS08601Date(afterDate) || !isValidIS08601Date(beforeDate)) {
throw new Error(
`The passed timestamps are not properly formatted. They have to match "${dateRegex}" (e.g. 2024-05-11)`,
);
}
};