From 8b1d207a4ecd70a5245f6a4b8dac5ed41746d78a Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 31 Jan 2026 23:58:19 +0000 Subject: [PATCH] Auto commit: 2026-01-31T23:58:19.892Z --- backend/src/routes/tickets.js | 8 ++- backend/src/services/tickets.js | 73 ++++++++++++++++++++- frontend/src/stores/tickets/ticketsSlice.ts | 31 ++++++++- 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/backend/src/routes/tickets.js b/backend/src/routes/tickets.js index c244f20..2882320 100644 --- a/backend/src/routes/tickets.js +++ b/backend/src/routes/tickets.js @@ -1,4 +1,3 @@ - const express = require('express'); const TicketsService = require('../services/tickets'); @@ -54,6 +53,11 @@ router.use(checkCrudPermissions('tickets')); * description: The Tickets managing API */ +router.post('/generate', wrapAsync(async (req, res) => { + const payload = await TicketsService.generateAutoTicket(req.body.data, req.currentUser); + res.status(200).send(payload); +})); + /** * @swagger * /api/tickets: @@ -439,4 +443,4 @@ router.get('/:id', wrapAsync(async (req, res) => { router.use('/', require('../helpers').commonErrorHandler); -module.exports = router; +module.exports = router; \ No newline at end of file diff --git a/backend/src/services/tickets.js b/backend/src/services/tickets.js index 4c73c2f..6cf322f 100644 --- a/backend/src/services/tickets.js +++ b/backend/src/services/tickets.js @@ -132,7 +132,76 @@ module.exports = class TicketsService { } } - -}; + static async generateAutoTicket(data, currentUser) { + const { desired_odds, game_count } = data; + const targetOdds = parseFloat(desired_odds) || 2.0; + const count = parseInt(game_count) || 3; + // 1. Get all active selections with their game info + const selections = await db.selections.findAll({ + include: [{ + model: db.markets, + as: 'market', + include: [{ + model: db.games, + as: 'game', + }] + }] + }); + if (selections.length === 0) { + throw new Error('No selections available in the database'); + } + + // Group selections by game to ensure we pick from different games + const gameGroups = {}; + selections.forEach(s => { + if (s.market && s.market.game) { + const gameId = s.market.game.id; + if (!gameGroups[gameId]) gameGroups[gameId] = []; + gameGroups[gameId].push(s); + } + }); + + const gameIds = Object.keys(gameGroups); + const actualCount = Math.min(count, gameIds.length); + + if (actualCount === 0) { + throw new Error('Not enough games available'); + } + + // Try multiple times to find a good combination + let bestTicket = null; + let minDiff = Infinity; + + for (let i = 0; i < 200; i++) { + // Randomly pick actualCount games + const shuffledGames = gameIds.sort(() => 0.5 - Math.random()); + const selectedGameIds = shuffledGames.slice(0, actualCount); + + const currentSelections = []; + let currentTotalOdds = 1; + + selectedGameIds.forEach(gid => { + const gameSelections = gameGroups[gid]; + const randomSelection = gameSelections[Math.floor(Math.random() * gameSelections.length)]; + currentSelections.push(randomSelection); + currentTotalOdds *= parseFloat(randomSelection.odds); + }); + + const diff = Math.abs(currentTotalOdds - targetOdds); + if (diff < minDiff) { + minDiff = diff; + bestTicket = { + selections: currentSelections, + totalOdds: currentTotalOdds, + gameCount: actualCount + }; + } + + if (minDiff < 0.05) break; // Close enough + } + + return bestTicket; + } +}; \ No newline at end of file diff --git a/frontend/src/stores/tickets/ticketsSlice.ts b/frontend/src/stores/tickets/ticketsSlice.ts index 907e51b..2145eb0 100644 --- a/frontend/src/stores/tickets/ticketsSlice.ts +++ b/frontend/src/stores/tickets/ticketsSlice.ts @@ -122,6 +122,22 @@ export const update = createAsyncThunk('tickets/updateTickets', async (payload: } }) +export const generateAutoTicket = createAsyncThunk('tickets/generateAutoTicket', async (data: any, { rejectWithValue }) => { + try { + const result = await axios.post( + 'tickets/generate', + { data } + ) + return result.data + } catch (error) { + if (!error.response) { + throw error; + } + + return rejectWithValue(error.response.data); + } +}) + export const ticketsSlice = createSlice({ name: 'tickets', @@ -221,6 +237,19 @@ export const ticketsSlice = createSlice({ rejectNotify(state, action); }) + builder.addCase(generateAutoTicket.pending, (state) => { + state.loading = true; + resetNotify(state); + }) + builder.addCase(generateAutoTicket.fulfilled, (state) => { + state.loading = false; + fulfilledNotify(state, 'Ticket generated successfully'); + }) + builder.addCase(generateAutoTicket.rejected, (state, action) => { + state.loading = false; + rejectNotify(state, action); + }) + }, }) @@ -228,4 +257,4 @@ export const ticketsSlice = createSlice({ // Action creators are generated for each case reducer function export const { setRefetch } = ticketsSlice.actions -export default ticketsSlice.reducer +export default ticketsSlice.reducer \ No newline at end of file