feat(i18n): add scripts for locale translation and patching
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 45s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 45s
- Implemented `fill-de-extended-gaps.js` to fill missing billing/orders keys in de-extended from de. - Created `fill-i18n-deep.py` for deep translation of locale JSONs using deep-translator with fallback options. - Added `fill-i18n-locales.js` to translate locale JSONs and write overrides for untranslated keys. - Introduced `fix-en-leaks.py` to translate keys that still match the en-US merge, addressing English leaks. - Developed `patch-de-ch-swiss.js` to replace 'ß' with 'ss' in de-CH.json without deleting existing entries. - Created `patch-en-gb-au.js` to apply UK/AU spelling corrections in en-GB and en-AU locales. - Added shell scripts `run-fix-en-leaks.sh` and `run-i18n-deep-fill.sh` for sequential execution of translation tasks. - Implemented `update-i18n-todo-stats.js` to update statistics in the I18N_TODO.md file based on translation completeness.
This commit is contained in:
@@ -7,22 +7,6 @@ import PredefinedActivity from '../models/PredefinedActivity.js';
|
||||
import GroupActivity from '../models/GroupActivity.js';
|
||||
import { Op } from 'sequelize';
|
||||
|
||||
const STANDARD_ACTIVITY_NAMES = new Set([
|
||||
'Begrüßung',
|
||||
'Aktivierung',
|
||||
'Aufbauen',
|
||||
'Turnier',
|
||||
'Abbauen',
|
||||
'Abschlussgespräch',
|
||||
]);
|
||||
|
||||
const isTrackablePredefinedActivity = (predefinedActivity) => {
|
||||
if (!predefinedActivity) {
|
||||
return false;
|
||||
}
|
||||
return !predefinedActivity.excludeFromStats && !STANDARD_ACTIVITY_NAMES.has(predefinedActivity.name);
|
||||
};
|
||||
|
||||
export const getMemberActivities = async (req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
@@ -229,18 +213,12 @@ export const getMemberActivities = async (req, res) => {
|
||||
if (!ga.activity || !ga.activity.id || !ga.participant || !ga.participant.id) {
|
||||
return false;
|
||||
}
|
||||
if (!isTrackablePredefinedActivity(ga.activity.predefinedActivity)) {
|
||||
return false;
|
||||
}
|
||||
const key = `${ga.activity.id}-${ga.participant.id}`;
|
||||
return !explicitActivityKeys.has(key);
|
||||
});
|
||||
|
||||
// Kombiniere beide Listen
|
||||
const allActivities = [...filteredMemberActivities, ...uniqueGroupActivities].filter((entry) => {
|
||||
const predefinedActivity = entry?.activity?.predefinedActivity;
|
||||
return isTrackablePredefinedActivity(predefinedActivity);
|
||||
});
|
||||
const allActivities = [...filteredMemberActivities, ...uniqueGroupActivities];
|
||||
|
||||
// Group activities by name and count occurrences
|
||||
// Verwende einen Set pro Aktivität, um eindeutige Datum-Aktivität-Kombinationen zu tracken
|
||||
@@ -250,10 +228,6 @@ export const getMemberActivities = async (req, res) => {
|
||||
if (!ma.activity || !ma.activity.predefinedActivity || !ma.participant) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isTrackablePredefinedActivity(ma.activity.predefinedActivity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const activity = ma.activity.predefinedActivity;
|
||||
const activityName = activity.name;
|
||||
@@ -480,18 +454,12 @@ export const getMemberLastParticipations = async (req, res) => {
|
||||
if (!ga.activity || !ga.activity.id || !ga.participant || !ga.participant.id) {
|
||||
return false;
|
||||
}
|
||||
if (!isTrackablePredefinedActivity(ga.activity.predefinedActivity)) {
|
||||
return false;
|
||||
}
|
||||
const key = `${ga.activity.id}-${ga.participant.id}`;
|
||||
return !explicitActivityKeys.has(key);
|
||||
});
|
||||
|
||||
// Kombiniere beide Listen
|
||||
const allActivities = [...filteredMemberActivities, ...uniqueGroupActivities].filter((entry) => {
|
||||
const predefinedActivity = entry?.activity?.predefinedActivity;
|
||||
return isTrackablePredefinedActivity(predefinedActivity);
|
||||
});
|
||||
const allActivities = [...filteredMemberActivities, ...uniqueGroupActivities];
|
||||
|
||||
// Gruppiere nach Datum
|
||||
const participationsByDate = new Map();
|
||||
@@ -501,7 +469,7 @@ export const getMemberLastParticipations = async (req, res) => {
|
||||
if (!ma.activity || !ma.activity.predefinedActivity || !ma.activity.diaryDate || !ma.participant) {
|
||||
return false;
|
||||
}
|
||||
return isTrackablePredefinedActivity(ma.activity.predefinedActivity);
|
||||
return true;
|
||||
})
|
||||
.forEach(ma => {
|
||||
const date = ma.activity.diaryDate.date;
|
||||
@@ -560,3 +528,4 @@ export const getMemberLastParticipations = async (req, res) => {
|
||||
return res.status(500).json({ error: 'Failed to fetch member last participations' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,49 +4,12 @@ import { emitTournamentChanged } from '../services/socketService.js';
|
||||
import TournamentClass from '../models/TournamentClass.js';
|
||||
import HttpError from '../exceptions/HttpError.js';
|
||||
|
||||
// Pools (zusammengelegte Gruppenphasen)
|
||||
export const mergeClassesIntoPool = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, sourceClassId, targetClassId, strategy, outOfCompetitionForSource } = req.body;
|
||||
try {
|
||||
await tournamentService.mergeClassesIntoPool(
|
||||
token,
|
||||
clubId,
|
||||
tournamentId,
|
||||
sourceClassId,
|
||||
targetClassId,
|
||||
strategy, // 'singleGroup' | 'distribute'
|
||||
!!outOfCompetitionForSource
|
||||
);
|
||||
// Broadcast
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('[mergeClassesIntoPool] Error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
export const resetPool = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, poolId } = req.body;
|
||||
try {
|
||||
await tournamentService.resetPool(token, clubId, tournamentId, poolId);
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('[resetPool] Error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 1. Alle Turniere eines Vereins (query: type = 'internal' | 'external' | 'mini')
|
||||
// 1. Alle Turniere eines Vereins
|
||||
export const getTournaments = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId } = req.params;
|
||||
const type = req.query.type || null;
|
||||
try {
|
||||
const tournaments = await tournamentService.getTournaments(token, clubId, type);
|
||||
const tournaments = await tournamentService.getTournaments(token, clubId);
|
||||
res.status(200).json(tournaments);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -59,27 +22,13 @@ export const getTournaments = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** Ranglisten interne Einzel-Turniere (Gruppen-% + K.-o.-Bonus wie im Service) */
|
||||
export const getInternalTournamentStats = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId } = req.params;
|
||||
const months = req.query.months;
|
||||
const ageClassKeys = req.query.ageClassKeys;
|
||||
try {
|
||||
const data = await tournamentService.getInternalTournamentPlayerStats(token, clubId, months, ageClassKeys);
|
||||
res.status(200).json(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Neues Turnier anlegen
|
||||
export const addTournament = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentName, date, winningSets, allowsExternal, isDoublesTournament } = req.body;
|
||||
const { clubId, tournamentName, date, winningSets, allowsExternal } = req.body;
|
||||
try {
|
||||
const tournament = await tournamentService.addTournament(token, clubId, tournamentName, date, winningSets, allowsExternal, isDoublesTournament);
|
||||
const tournament = await tournamentService.addTournament(token, clubId, tournamentName, date, winningSets, allowsExternal);
|
||||
// Emit Socket-Event
|
||||
if (clubId && tournament && tournament.id) {
|
||||
emitTournamentChanged(clubId, tournament.id);
|
||||
}
|
||||
@@ -90,22 +39,6 @@ export const addTournament = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Minimeisterschaft anlegen (Turnier + 6 Klassen); Name: "Minimeisterschaften <Jahr> Ortsentscheid <ort>"
|
||||
export const addMiniChampionship = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, ort, date, year, winningSets } = req.body;
|
||||
try {
|
||||
const tournament = await tournamentService.addMiniChampionship(token, clubId, ort, date, year, winningSets);
|
||||
if (clubId && tournament && tournament.id) {
|
||||
emitTournamentChanged(clubId, tournament.id);
|
||||
}
|
||||
res.status(201).json(tournament);
|
||||
} catch (error) {
|
||||
console.error('[addMiniChampionship] Error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. Teilnehmer hinzufügen - klassengebunden
|
||||
export const addParticipant = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
@@ -286,9 +219,9 @@ export const getTournament = async (req, res) => {
|
||||
export const updateTournament = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId } = req.params;
|
||||
const { name, date, winningSets, numberOfTables, isDoublesTournament } = req.body;
|
||||
const { name, date, winningSets } = req.body;
|
||||
try {
|
||||
const tournament = await tournamentService.updateTournament(token, clubId, tournamentId, name, date, winningSets, numberOfTables, isDoublesTournament);
|
||||
const tournament = await tournamentService.updateTournament(token, clubId, tournamentId, name, date, winningSets);
|
||||
// Emit Socket-Event
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json(tournament);
|
||||
@@ -435,19 +368,6 @@ export const resetMatches = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const cleanupOrphanedMatches = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId } = req.body;
|
||||
try {
|
||||
const result = await tournamentService.cleanupOrphanedMatches(token, clubId, tournamentId);
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json(result);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
export const removeParticipant = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, participantId } = req.body;
|
||||
@@ -478,20 +398,6 @@ export const updateParticipantSeeded = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const setParticipantGaveUp = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, participantId } = req.params;
|
||||
const { gaveUp } = req.body;
|
||||
try {
|
||||
await tournamentService.setParticipantGaveUp(token, clubId, tournamentId, participantId, gaveUp);
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json({ message: 'Aufgabe-Status aktualisiert' });
|
||||
} catch (err) {
|
||||
console.error('[setParticipantGaveUp] Error:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteMatchResult = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, matchId, set } = req.body;
|
||||
@@ -557,27 +463,12 @@ export const setMatchActive = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const setMatchTableNumber = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, matchId } = req.params;
|
||||
const { tableNumber } = req.body;
|
||||
try {
|
||||
await tournamentService.setMatchTableNumber(token, clubId, tournamentId, matchId, tableNumber);
|
||||
// Emit Socket-Event
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json({ message: 'Tischnummer aktualisiert' });
|
||||
} catch (err) {
|
||||
console.error('[setMatchTableNumber] Error:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Externe Teilnehmer hinzufügen
|
||||
export const addExternalParticipant = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, classId, firstName, lastName, club, birthDate, gender, email, address } = req.body;
|
||||
const { clubId, tournamentId, classId, firstName, lastName, club, birthDate, gender } = req.body;
|
||||
try {
|
||||
await tournamentService.addExternalParticipant(token, clubId, classId, firstName, lastName, club, birthDate, gender, email, address);
|
||||
await tournamentService.addExternalParticipant(token, clubId, classId, firstName, lastName, club, birthDate, gender);
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json({ message: 'Externer Teilnehmer hinzugefügt' });
|
||||
} catch (error) {
|
||||
@@ -628,20 +519,6 @@ export const updateExternalParticipantSeeded = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const setExternalParticipantGaveUp = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, participantId } = req.params;
|
||||
const { gaveUp } = req.body;
|
||||
try {
|
||||
await tournamentService.setExternalParticipantGaveUp(token, clubId, tournamentId, participantId, gaveUp);
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json({ message: 'Aufgabe-Status aktualisiert' });
|
||||
} catch (error) {
|
||||
console.error('[setExternalParticipantGaveUp] Error:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Tournament Classes
|
||||
export const getTournamentClasses = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
@@ -658,9 +535,9 @@ export const getTournamentClasses = async (req, res) => {
|
||||
export const addTournamentClass = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId } = req.params;
|
||||
const { name, isDoubles, gender, minBirthYear, maxBirthYear } = req.body;
|
||||
const { name, isDoubles, gender, minBirthYear } = req.body;
|
||||
try {
|
||||
const tournamentClass = await tournamentService.addTournamentClass(token, clubId, tournamentId, name, isDoubles, gender, minBirthYear, maxBirthYear);
|
||||
const tournamentClass = await tournamentService.addTournamentClass(token, clubId, tournamentId, name, isDoubles, gender, minBirthYear);
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json(tournamentClass);
|
||||
} catch (error) {
|
||||
@@ -672,9 +549,11 @@ export const addTournamentClass = async (req, res) => {
|
||||
export const updateTournamentClass = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId, tournamentId, classId } = req.params;
|
||||
const { name, sortOrder, isDoubles, gender, minBirthYear, maxBirthYear } = req.body;
|
||||
const { name, sortOrder, isDoubles, gender, minBirthYear } = req.body;
|
||||
try {
|
||||
const tournamentClass = await tournamentService.updateTournamentClass(token, clubId, tournamentId, classId, name, sortOrder, isDoubles, gender, minBirthYear, maxBirthYear);
|
||||
console.log('[updateTournamentClass] Request body:', { name, sortOrder, isDoubles, gender, minBirthYear });
|
||||
const tournamentClass = await tournamentService.updateTournamentClass(token, clubId, tournamentId, classId, name, sortOrder, isDoubles, gender, minBirthYear);
|
||||
console.log('[updateTournamentClass] Updated class:', JSON.stringify(tournamentClass.toJSON(), null, 2));
|
||||
emitTournamentChanged(clubId, tournamentId);
|
||||
res.status(200).json(tournamentClass);
|
||||
} catch (error) {
|
||||
@@ -763,4 +642,4 @@ export const deletePairing = async (req, res) => {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,10 +20,6 @@ const TournamentGroup = sequelize.define('TournamentGroup', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
poolId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
}, {
|
||||
underscored: true,
|
||||
tableName: 'tournament_group',
|
||||
|
||||
@@ -63,12 +63,6 @@ const TournamentMatch = sequelize.define('TournamentMatch', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
tableNumber: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
comment: 'Tischnummer, an der das Match stattfindet'
|
||||
},
|
||||
}, {
|
||||
underscored: true,
|
||||
tableName: 'tournament_match',
|
||||
|
||||
@@ -20,7 +20,6 @@ import Match from './Match.js';
|
||||
import League from './League.js';
|
||||
import Team from './Team.js';
|
||||
import ClubTeam from './ClubTeam.js';
|
||||
import ClubTeamMember from './ClubTeamMember.js';
|
||||
import TeamDocument from './TeamDocument.js';
|
||||
import Season from './Season.js';
|
||||
import Location from './Location.js';
|
||||
@@ -42,30 +41,16 @@ import OfficialTournament from './OfficialTournament.js';
|
||||
import OfficialCompetition from './OfficialCompetition.js';
|
||||
import OfficialCompetitionMember from './OfficialCompetitionMember.js';
|
||||
import MyTischtennis from './MyTischtennis.js';
|
||||
import ClickTtAccount from './ClickTtAccount.js';
|
||||
import MyTischtennisUpdateHistory from './MyTischtennisUpdateHistory.js';
|
||||
import MyTischtennisFetchLog from './MyTischtennisFetchLog.js';
|
||||
import ApiLog from './ApiLog.js';
|
||||
import MemberTransferConfig from './MemberTransferConfig.js';
|
||||
import MemberContact from './MemberContact.js';
|
||||
import MemberImage from './MemberImage.js';
|
||||
import MemberGroupPhoto from './MemberGroupPhoto.js';
|
||||
import MemberTtrHistory from './MemberTtrHistory.js';
|
||||
import MemberPlayInterest from './MemberPlayInterest.js';
|
||||
import MemberOrder from './MemberOrder.js';
|
||||
import MemberOrderHistory from './MemberOrderHistory.js';
|
||||
import TrainingGroup from './TrainingGroup.js';
|
||||
import MemberTrainingGroup from './MemberTrainingGroup.js';
|
||||
import ClubDisabledPresetGroup from './ClubDisabledPresetGroup.js';
|
||||
import TrainingTime from './TrainingTime.js';
|
||||
import TrainingCancellation from './TrainingCancellation.js';
|
||||
import CalendarEvent from './CalendarEvent.js';
|
||||
import BillingTemplate from './BillingTemplate.js';
|
||||
import BillingTemplateField from './BillingTemplateField.js';
|
||||
import BillingRun from './BillingRun.js';
|
||||
import BillingDocument from './BillingDocument.js';
|
||||
import BillingDocumentValue from './BillingDocumentValue.js';
|
||||
import BillingUserSetting from './BillingUserSetting.js';
|
||||
// Official tournaments relations
|
||||
OfficialTournament.hasMany(OfficialCompetition, { foreignKey: 'tournamentId', as: 'competitions' });
|
||||
OfficialCompetition.belongsTo(OfficialTournament, { foreignKey: 'tournamentId', as: 'tournament' });
|
||||
@@ -104,25 +89,6 @@ DiaryNote.belongsTo(Member, { foreignKey: 'memberId' });
|
||||
Member.hasMany(MemberNote, { as: 'memberNotes', foreignKey: 'memberId' });
|
||||
MemberNote.belongsTo(Member, { foreignKey: 'memberId' });
|
||||
|
||||
Member.hasMany(MemberTtrHistory, { as: 'ttrHistoryEntries', foreignKey: 'memberId' });
|
||||
MemberTtrHistory.belongsTo(Member, { as: 'member', foreignKey: 'memberId' });
|
||||
Member.hasMany(MemberPlayInterest, { as: 'playInterests', foreignKey: 'memberId' });
|
||||
MemberPlayInterest.belongsTo(Member, { as: 'member', foreignKey: 'memberId' });
|
||||
Club.hasMany(MemberPlayInterest, { as: 'memberPlayInterests', foreignKey: 'clubId' });
|
||||
MemberPlayInterest.belongsTo(Club, { as: 'club', foreignKey: 'clubId' });
|
||||
|
||||
Club.hasMany(MemberGroupPhoto, { as: 'memberGroupPhotos', foreignKey: 'clubId' });
|
||||
MemberGroupPhoto.belongsTo(Club, { as: 'club', foreignKey: 'clubId' });
|
||||
User.hasMany(MemberGroupPhoto, { as: 'createdMemberGroupPhotos', foreignKey: 'createdByUserId' });
|
||||
MemberGroupPhoto.belongsTo(User, { as: 'createdByUser', foreignKey: 'createdByUserId' });
|
||||
|
||||
Member.hasMany(MemberOrder, { as: 'orders', foreignKey: 'memberId' });
|
||||
MemberOrder.belongsTo(Member, { as: 'member', foreignKey: 'memberId' });
|
||||
Club.hasMany(MemberOrder, { as: 'memberOrders', foreignKey: 'clubId' });
|
||||
MemberOrder.belongsTo(Club, { as: 'club', foreignKey: 'clubId' });
|
||||
MemberOrder.hasMany(MemberOrderHistory, { as: 'historyEntries', foreignKey: 'memberOrderId' });
|
||||
MemberOrderHistory.belongsTo(MemberOrder, { as: 'order', foreignKey: 'memberOrderId' });
|
||||
|
||||
DiaryDate.hasMany(DiaryNote, { as: 'diaryNotes', foreignKey: 'diaryDateId' });
|
||||
DiaryNote.belongsTo(DiaryDate, { foreignKey: 'diaryDateId' });
|
||||
|
||||
@@ -188,11 +154,6 @@ ClubTeam.belongsTo(League, { foreignKey: 'leagueId', as: 'league' });
|
||||
Season.hasMany(ClubTeam, { foreignKey: 'seasonId', as: 'clubTeams' });
|
||||
ClubTeam.belongsTo(Season, { foreignKey: 'seasonId', as: 'season' });
|
||||
|
||||
ClubTeam.hasMany(ClubTeamMember, { foreignKey: 'clubTeamId', as: 'lineupEntries' });
|
||||
ClubTeamMember.belongsTo(ClubTeam, { foreignKey: 'clubTeamId', as: 'clubTeam' });
|
||||
Member.hasMany(ClubTeamMember, { foreignKey: 'memberId', as: 'clubTeamAssignments' });
|
||||
ClubTeamMember.belongsTo(Member, { foreignKey: 'memberId', as: 'member' });
|
||||
|
||||
// TeamDocument relationships
|
||||
ClubTeam.hasMany(TeamDocument, { foreignKey: 'clubTeamId', as: 'documents' });
|
||||
TeamDocument.belongsTo(ClubTeam, { foreignKey: 'clubTeamId', as: 'clubTeam' });
|
||||
@@ -221,9 +182,6 @@ GroupActivity.belongsTo(Group, { foreignKey: 'groupId', as: 'groupsGroupActivity
|
||||
GroupActivity.belongsTo(PredefinedActivity, { foreignKey: 'customActivity', as: 'groupPredefinedActivity' });
|
||||
PredefinedActivity.hasMany(GroupActivity, { foreignKey: 'predefinedActivityId', as: 'groupPredefinedActivities' });
|
||||
|
||||
DiaryDateActivity.belongsTo(Group, { foreignKey: 'groupId', as: 'planGroup' });
|
||||
Group.hasMany(DiaryDateActivity, { foreignKey: 'groupId', as: 'groupPlanItems' });
|
||||
|
||||
DiaryTag.hasMany(DiaryDateTag, { foreignKey: 'tagId', as: 'diaryDateTags' });
|
||||
DiaryDateTag.belongsTo(DiaryTag, { foreignKey: 'tagId', as: 'tag' });
|
||||
|
||||
@@ -364,8 +322,6 @@ DiaryDate.hasMany(Accident, { foreignKey: 'diaryDateId', as: 'accidents' });
|
||||
|
||||
User.hasOne(MyTischtennis, { foreignKey: 'userId', as: 'myTischtennis' });
|
||||
MyTischtennis.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
User.hasOne(ClickTtAccount, { foreignKey: 'userId', as: 'clickTtAccount' });
|
||||
ClickTtAccount.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
|
||||
User.hasMany(MyTischtennisUpdateHistory, { foreignKey: 'userId', as: 'updateHistory' });
|
||||
MyTischtennisUpdateHistory.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
@@ -409,28 +365,6 @@ ClubDisabledPresetGroup.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
// Training Times
|
||||
TrainingGroup.hasMany(TrainingTime, { foreignKey: 'trainingGroupId', as: 'trainingTimes' });
|
||||
TrainingTime.belongsTo(TrainingGroup, { foreignKey: 'trainingGroupId', as: 'trainingGroup' });
|
||||
Club.hasMany(TrainingCancellation, { foreignKey: 'clubId', as: 'trainingCancellations' });
|
||||
TrainingCancellation.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
Club.hasMany(CalendarEvent, { foreignKey: 'clubId', as: 'calendarEvents' });
|
||||
CalendarEvent.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
|
||||
// Billing
|
||||
Club.hasMany(BillingTemplate, { foreignKey: 'clubId', as: 'billingTemplates' });
|
||||
BillingTemplate.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
BillingTemplate.hasMany(BillingTemplateField, { foreignKey: 'templateId', as: 'fields' });
|
||||
BillingTemplateField.belongsTo(BillingTemplate, { foreignKey: 'templateId', as: 'template' });
|
||||
Club.hasMany(BillingRun, { foreignKey: 'clubId', as: 'billingRuns' });
|
||||
BillingRun.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
BillingTemplate.hasMany(BillingRun, { foreignKey: 'templateId', as: 'runs' });
|
||||
BillingRun.belongsTo(BillingTemplate, { foreignKey: 'templateId', as: 'template' });
|
||||
BillingRun.hasMany(BillingDocument, { foreignKey: 'runId', as: 'documents' });
|
||||
BillingDocument.belongsTo(BillingRun, { foreignKey: 'runId', as: 'run' });
|
||||
BillingDocument.hasMany(BillingDocumentValue, { foreignKey: 'billingDocumentId', as: 'values' });
|
||||
BillingDocumentValue.belongsTo(BillingDocument, { foreignKey: 'billingDocumentId', as: 'document' });
|
||||
Club.hasMany(BillingUserSetting, { foreignKey: 'clubId', as: 'billingUserSettings' });
|
||||
BillingUserSetting.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
User.hasMany(BillingUserSetting, { foreignKey: 'userId', as: 'billingUserSettings' });
|
||||
BillingUserSetting.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
|
||||
export {
|
||||
User,
|
||||
@@ -456,7 +390,6 @@ export {
|
||||
League,
|
||||
Team,
|
||||
ClubTeam,
|
||||
ClubTeamMember,
|
||||
TeamDocument,
|
||||
Group,
|
||||
GroupActivity,
|
||||
@@ -474,28 +407,14 @@ export {
|
||||
OfficialCompetition,
|
||||
OfficialCompetitionMember,
|
||||
MyTischtennis,
|
||||
ClickTtAccount,
|
||||
MyTischtennisUpdateHistory,
|
||||
MyTischtennisFetchLog,
|
||||
ApiLog,
|
||||
MemberTransferConfig,
|
||||
MemberContact,
|
||||
MemberImage,
|
||||
MemberGroupPhoto,
|
||||
MemberTtrHistory,
|
||||
MemberPlayInterest,
|
||||
MemberOrder,
|
||||
MemberOrderHistory,
|
||||
TrainingGroup,
|
||||
MemberTrainingGroup,
|
||||
ClubDisabledPresetGroup,
|
||||
TrainingTime,
|
||||
TrainingCancellation,
|
||||
CalendarEvent,
|
||||
BillingTemplate,
|
||||
BillingTemplateField,
|
||||
BillingRun,
|
||||
BillingDocument,
|
||||
BillingDocumentValue,
|
||||
BillingUserSetting,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ import express from 'express';
|
||||
import {
|
||||
getTournaments,
|
||||
addTournament,
|
||||
addMiniChampionship,
|
||||
updateTournament,
|
||||
addParticipant,
|
||||
getParticipants,
|
||||
@@ -19,22 +18,17 @@ import {
|
||||
manualAssignGroups,
|
||||
resetGroups,
|
||||
resetMatches,
|
||||
cleanupOrphanedMatches,
|
||||
removeParticipant,
|
||||
updateParticipantSeeded,
|
||||
setParticipantGaveUp,
|
||||
deleteMatchResult,
|
||||
reopenMatch,
|
||||
deleteKnockoutMatches,
|
||||
setMatchActive,
|
||||
setMatchTableNumber,
|
||||
addExternalParticipant,
|
||||
getExternalParticipants,
|
||||
removeExternalParticipant,
|
||||
updateExternalParticipantSeeded,
|
||||
setExternalParticipantGaveUp,
|
||||
getTournamentClasses,
|
||||
getInternalTournamentStats,
|
||||
addTournamentClass,
|
||||
updateTournamentClass,
|
||||
deleteTournamentClass,
|
||||
@@ -46,7 +40,6 @@ import {
|
||||
updatePairing,
|
||||
deletePairing,
|
||||
} from '../controllers/tournamentController.js';
|
||||
import { mergeClassesIntoPool, resetPool } from '../controllers/tournamentController.js';
|
||||
import {
|
||||
getStages,
|
||||
upsertStages,
|
||||
@@ -56,31 +49,23 @@ import { authenticate } from '../middleware/authMiddleware.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/internal-stats/:clubId', authenticate, getInternalTournamentStats);
|
||||
|
||||
router.post('/participant', authenticate, addParticipant);
|
||||
router.post('/participants', authenticate, getParticipants);
|
||||
router.delete('/participant', authenticate, removeParticipant);
|
||||
router.put('/participant/:clubId/:tournamentId/:participantId/seeded', authenticate, updateParticipantSeeded);
|
||||
router.put('/participant/:clubId/:tournamentId/:participantId/gave-up', authenticate, setParticipantGaveUp);
|
||||
router.post('/modus', authenticate, setModus);
|
||||
router.post('/groups/reset', authenticate, resetGroups);
|
||||
router.post('/matches/reset', authenticate, resetMatches);
|
||||
router.post('/matches/cleanup-orphaned', authenticate, cleanupOrphanedMatches);
|
||||
router.put('/groups', authenticate, createGroups);
|
||||
router.post('/groups/create', authenticate, createGroupsPerClass);
|
||||
router.post('/groups', authenticate, fillGroups);
|
||||
router.post('/matches/create', authenticate, createGroupMatches);
|
||||
// Pools
|
||||
router.post('/pools/merge', authenticate, mergeClassesIntoPool);
|
||||
router.post('/pools/reset', authenticate, resetPool);
|
||||
router.get('/groups', authenticate, getGroups);
|
||||
router.post('/match/result', authenticate, addMatchResult);
|
||||
router.delete('/match/result', authenticate, deleteMatchResult);
|
||||
router.post("/match/reopen", authenticate, reopenMatch);
|
||||
router.post('/match/finish', authenticate, finishMatch);
|
||||
router.put('/match/:clubId/:tournamentId/:matchId/active', authenticate, setMatchActive);
|
||||
router.put('/match/:clubId/:tournamentId/:matchId/table', authenticate, setMatchTableNumber);
|
||||
router.get('/matches/:clubId/:tournamentId', authenticate, getTournamentMatches);
|
||||
router.post('/knockout', authenticate, startKnockout);
|
||||
router.delete("/matches/knockout", authenticate, deleteKnockoutMatches);
|
||||
@@ -93,7 +78,6 @@ router.post('/external-participant', authenticate, addExternalParticipant);
|
||||
router.post('/external-participants', authenticate, getExternalParticipants);
|
||||
router.delete('/external-participant', authenticate, removeExternalParticipant);
|
||||
router.put('/external-participant/:clubId/:tournamentId/:participantId/seeded', authenticate, updateExternalParticipantSeeded);
|
||||
router.put('/external-participant/:clubId/:tournamentId/:participantId/gave-up', authenticate, setExternalParticipantGaveUp);
|
||||
|
||||
// Tournament Classes
|
||||
router.get('/classes/:clubId/:tournamentId', authenticate, getTournamentClasses);
|
||||
@@ -113,9 +97,6 @@ router.get('/stages', authenticate, getStages);
|
||||
router.put('/stages', authenticate, upsertStages);
|
||||
router.post('/stages/advance', authenticate, advanceStage);
|
||||
|
||||
// Minimeisterschaft anlegen (vor :clubId, damit 'mini' nicht als clubId matcht)
|
||||
router.post('/mini', authenticate, addMiniChampionship);
|
||||
|
||||
// Muss NACH allen festen Pfaden stehen, sonst matcht z.B. '/stages' als clubId='stages'
|
||||
router.get('/:clubId', authenticate, getTournaments);
|
||||
router.post('/', authenticate, addTournament);
|
||||
|
||||
@@ -2,7 +2,6 @@ import cron from 'node-cron';
|
||||
import autoUpdateRatingsService from './autoUpdateRatingsService.js';
|
||||
import autoFetchMatchResultsService from './autoFetchMatchResultsService.js';
|
||||
import apiLogService from './apiLogService.js';
|
||||
import memberService from './memberService.js';
|
||||
import { devLog } from '../utils/logger.js';
|
||||
|
||||
class SchedulerService {
|
||||
@@ -25,10 +24,10 @@ class SchedulerService {
|
||||
}
|
||||
}
|
||||
|
||||
async runMatchResultsFetchJob(isAutomatic = true, options = {}) {
|
||||
async runMatchResultsFetchJob(isAutomatic = true) {
|
||||
const startTime = Date.now();
|
||||
try {
|
||||
const result = await autoFetchMatchResultsService.executeAutomaticFetch(options);
|
||||
const result = await autoFetchMatchResultsService.executeAutomaticFetch();
|
||||
const executionTime = Date.now() - startTime;
|
||||
await apiLogService.logSchedulerExecution('match_results', true, result || { success: true }, executionTime, null);
|
||||
return { success: true, result, executionTime, isAutomatic };
|
||||
@@ -39,20 +38,6 @@ class SchedulerService {
|
||||
}
|
||||
}
|
||||
|
||||
async runInactiveMemberCleanupJob(isAutomatic = true) {
|
||||
const startTime = Date.now();
|
||||
try {
|
||||
const result = await memberService.cleanupInactiveMembersWithoutRecentTraining({ inactiveDays: 365 });
|
||||
const executionTime = Date.now() - startTime;
|
||||
await apiLogService.logSchedulerExecution('member_cleanup', true, result || { success: true }, executionTime, null);
|
||||
return { success: true, result, executionTime, isAutomatic };
|
||||
} catch (error) {
|
||||
const executionTime = Date.now() - startTime;
|
||||
await apiLogService.logSchedulerExecution('member_cleanup', false, { success: false }, executionTime, error?.message || String(error));
|
||||
return { success: false, error: error?.message || String(error), executionTime, isAutomatic };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the scheduler
|
||||
*/
|
||||
@@ -80,16 +65,8 @@ class SchedulerService {
|
||||
timezone: 'Europe/Berlin'
|
||||
});
|
||||
|
||||
const inactiveMemberCleanupJob = cron.schedule('45 6 * * *', async () => {
|
||||
devLog('[Scheduler] Running inactive member cleanup job...');
|
||||
await this.runInactiveMemberCleanupJob(true);
|
||||
}, {
|
||||
timezone: 'Europe/Berlin'
|
||||
});
|
||||
|
||||
this.jobs.set('ratingUpdates', ratingUpdateJob);
|
||||
this.jobs.set('matchResults', matchResultsJob);
|
||||
this.jobs.set('inactiveMemberCleanup', inactiveMemberCleanupJob);
|
||||
|
||||
this.isRunning = true;
|
||||
const now = new Date();
|
||||
@@ -138,6 +115,7 @@ class SchedulerService {
|
||||
|
||||
/**
|
||||
* Manually trigger rating updates (for testing)
|
||||
* HINWEIS: Deaktiviert - automatische MyTischtennis-Abrufe sind nicht mehr verfügbar
|
||||
*/
|
||||
async triggerRatingUpdates() {
|
||||
devLog('[Scheduler] Manual rating updates trigger called');
|
||||
@@ -146,10 +124,11 @@ class SchedulerService {
|
||||
|
||||
/**
|
||||
* Manually trigger match results fetch (for testing)
|
||||
* HINWEIS: Deaktiviert - automatische MyTischtennis-Abrufe sind nicht mehr verfügbar
|
||||
*/
|
||||
async triggerMatchResultsFetch(options = {}) {
|
||||
async triggerMatchResultsFetch() {
|
||||
devLog('[Scheduler] Manual match results fetch trigger called');
|
||||
return await this.runMatchResultsFetchJob(false, options);
|
||||
return await this.runMatchResultsFetchJob(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user