og-statistics 2.0.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +255 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "og-statistics",
3
- "version": "2.0.3",
3
+ "version": "3.0.0",
4
4
  "description": "",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -105,21 +105,25 @@ card
105
105
  function filterIncidents(incidents) {
106
106
  try {
107
107
 
108
+ const home = incidents.incidents.filter(s => s.isHome === true);
108
109
  const home_cards = filterCards(incidents.incidents, true);
109
110
  const home_goals = filterGoals(incidents.incidents, true);
110
111
  const home_subs = filterSubs(incidents.incidents, true);
111
112
 
113
+ const away = incidents.incidents.filter(s => s.isHome === false);
112
114
  const away_cards = filterCards(incidents.incidents, false);
113
115
  const away_goals = filterGoals(incidents.incidents, false);
114
116
  const away_subs = filterSubs(incidents.incidents, false);
115
117
 
116
118
  return {
117
119
  home: {
120
+ all: home,
118
121
  cards: home_cards,
119
122
  goals: home_goals,
120
123
  subs: home_subs
121
124
  },
122
125
  away: {
126
+ all: away,
123
127
  cards: away_cards,
124
128
  goals: away_goals,
125
129
  subs: away_subs
@@ -184,4 +188,254 @@ function filterSubs(incidents, isHome) {
184
188
  }
185
189
 
186
190
 
187
- module.exports = { filterStats, filterIncidents };
191
+ function goalSummary(incidentsData, windowMinutes = 15) {
192
+ try {
193
+ const { local = [], away = [] } = incidentsData;
194
+
195
+ // Normaliza goles de cada lado
196
+ function extractGoals(events, teamLabel) {
197
+ return events
198
+ .filter(e => e.incidentType === 'goal')
199
+ .map(e => ({
200
+ team: teamLabel, // 'home' | 'away'
201
+ time: e.time, // minuto
202
+ playerId: e.player?.id ?? null, // id jugador
203
+ playerName: e.player?.name ?? null // nombre jugador
204
+ }));
205
+ }
206
+
207
+ const homeGoals = extractGoals(local, 'home');
208
+ const awayGoals = extractGoals(away, 'away');
209
+ const goals = [...homeGoals, ...awayGoals].sort((a, b) => a.time - b.time);
210
+
211
+ const noGoals = goals.length === 0;
212
+
213
+ // Caso sin goles
214
+ if (noGoals) {
215
+ return {
216
+ noGoals: true,
217
+ goalsCount: { home: 0, away: 0, total: 0 },
218
+
219
+ firstScorerTeam: null,
220
+ firstScorerPlayer: null,
221
+ lastScorerTeam: null,
222
+ lastScorerPlayer: null,
223
+
224
+ earliestGoalByTeam: { home: null, away: null },
225
+ scoredFirst15: { home: false, away: false },
226
+
227
+ twoGoalsInWindow: { home: false, away: false },
228
+
229
+ goalsBySegment: {},
230
+
231
+ playersGoals: { home: [], away: [] },
232
+ multiScorersList: { home: [], away: [] },
233
+ multiScorers: { home: false, away: false, any: false },
234
+ hatTricks: { home: [], away: [] },
235
+
236
+ comeback: false,
237
+ leadChanges: 0
238
+ };
239
+ }
240
+
241
+ // 1. Conteo de goles
242
+ const goalsCount = {
243
+ home: homeGoals.length,
244
+ away: awayGoals.length,
245
+ total: goals.length
246
+ };
247
+
248
+ // 2. Primero y último goleador
249
+ const firstGoal = goals[0];
250
+ const lastGoal = goals[goals.length - 1];
251
+
252
+ const firstScorerTeam = firstGoal.team;
253
+ const lastScorerTeam = lastGoal.team;
254
+
255
+ const firstScorerPlayer = {
256
+ team: firstGoal.team,
257
+ playerId: firstGoal.playerId,
258
+ playerName: firstGoal.playerName,
259
+ minute: firstGoal.time
260
+ };
261
+
262
+ const lastScorerPlayer = {
263
+ team: lastGoal.team,
264
+ playerId: lastGoal.playerId,
265
+ playerName: lastGoal.playerName,
266
+ minute: lastGoal.time
267
+ };
268
+
269
+ // 3. Primer gol por equipo y si marcaron en los primeros 15
270
+ function earliestGoalMinute(goalArray) {
271
+ if (!goalArray.length) return null;
272
+ return goalArray.reduce((min, g) => Math.min(min, g.time), goalArray[0].time);
273
+ }
274
+
275
+ const earliestGoalByTeam = {
276
+ home: earliestGoalMinute(homeGoals),
277
+ away: earliestGoalMinute(awayGoals)
278
+ };
279
+
280
+ const scoredFirst15 = {
281
+ home: earliestGoalByTeam.home !== null && earliestGoalByTeam.home <= 15,
282
+ away: earliestGoalByTeam.away !== null && earliestGoalByTeam.away <= 15
283
+ };
284
+
285
+ // --- 4. 2+ goles de un equipo en un lapso de windowMinutes ---
286
+ function hasTwoGoalsInWindow(goalArray) {
287
+ if (goalArray.length < 2) return false;
288
+ const times = goalArray.map(g => g.time).sort((a, b) => a - b);
289
+
290
+ for (let i = 0; i < times.length; i++) {
291
+ for (let j = i + 1; j < times.length; j++) {
292
+ if (times[j] - times[i] <= windowMinutes) {
293
+ return true;
294
+ }
295
+ }
296
+ }
297
+ return false;
298
+ }
299
+
300
+ const twoGoalsInWindow = {
301
+ home: hasTwoGoalsInWindow(homeGoals),
302
+ away: hasTwoGoalsInWindow(awayGoals)
303
+ };
304
+
305
+ // --- 5. Goles por segmentos de tiempo ---
306
+ const segments = [
307
+ { label: '0-15', start: 0, end: 15 },
308
+ { label: '16-30', start: 16, end: 30 },
309
+ { label: '31-45', start: 31, end: 45 },
310
+ { label: '46-60', start: 46, end: 60 },
311
+ { label: '61-75', start: 61, end: 75 },
312
+ { label: '76-90', start: 76, end: 90 },
313
+ { label: '91+', start: 91, end: Infinity }
314
+ ];
315
+
316
+ const goalsBySegment = {};
317
+ for (const seg of segments) {
318
+ goalsBySegment[seg.label] = { home: 0, away: 0 };
319
+ }
320
+
321
+ for (const g of goals) {
322
+ const seg = segments.find(s => g.time >= s.start && g.time <= s.end);
323
+ if (seg) {
324
+ goalsBySegment[seg.label][g.team] += 1;
325
+ }
326
+ }
327
+
328
+ // --- 6. Goles por jugador (id + nombre) ---
329
+ function buildPlayersGoals(goalArray) {
330
+ const map = new Map(); // key: playerId, value: { playerId, playerName, count }
331
+
332
+ for (const g of goalArray) {
333
+ if (!g.playerId) continue;
334
+ const existing = map.get(g.playerId) || {
335
+ playerId: g.playerId,
336
+ playerName: g.playerName,
337
+ count: 0
338
+ };
339
+ existing.count += 1;
340
+ map.set(g.playerId, existing);
341
+ }
342
+
343
+ return Array.from(map.values());
344
+ }
345
+
346
+ const playersGoalsHome = buildPlayersGoals(homeGoals);
347
+ const playersGoalsAway = buildPlayersGoals(awayGoals);
348
+
349
+ const playersGoals = {
350
+ home: playersGoalsHome,
351
+ away: playersGoalsAway
352
+ };
353
+
354
+ // --- 7. Multiscorers (2+ goles) ---
355
+ const multiScorersList = {
356
+ home: playersGoalsHome.filter(p => p.count >= 2),
357
+ away: playersGoalsAway.filter(p => p.count >= 2)
358
+ };
359
+
360
+ const multiScorers = {
361
+ home: multiScorersList.home.length > 0,
362
+ away: multiScorersList.away.length > 0,
363
+ any:
364
+ multiScorersList.home.length > 0 ||
365
+ multiScorersList.away.length > 0
366
+ };
367
+
368
+ // --- 8. Hat-tricks (3+ goles) ---
369
+ const hatTricks = {
370
+ home: playersGoalsHome.filter(p => p.count >= 3),
371
+ away: playersGoalsAway.filter(p => p.count >= 3)
372
+ };
373
+
374
+ // --- 9. Timeline, remontada y cambios de líder ---
375
+ let homeScore = 0;
376
+ let awayScore = 0;
377
+ let everHomeBehind = false;
378
+ let everAwayBehind = false;
379
+ let leadChanges = 0;
380
+ let previousLeader = 'draw'; // 'home' | 'away' | 'draw'
381
+
382
+ for (const g of goals) {
383
+ if (g.team === 'home') homeScore += 1;
384
+ else awayScore += 1;
385
+
386
+ if (homeScore < awayScore) everHomeBehind = true;
387
+ if (awayScore < homeScore) everAwayBehind = true;
388
+
389
+ let currentLeader = 'draw';
390
+ if (homeScore > awayScore) currentLeader = 'home';
391
+ else if (awayScore > homeScore) currentLeader = 'away';
392
+
393
+ if (
394
+ currentLeader !== previousLeader &&
395
+ previousLeader !== 'draw' &&
396
+ currentLeader !== 'draw'
397
+ ) {
398
+ leadChanges += 1;
399
+ }
400
+
401
+ previousLeader = currentLeader;
402
+ }
403
+
404
+ const finalHome = homeScore;
405
+ const finalAway = awayScore;
406
+
407
+ let comeback = false;
408
+ if (finalHome > finalAway && everHomeBehind) comeback = true;
409
+ if (finalAway > finalHome && everAwayBehind) comeback = true;
410
+
411
+ // --- Resultado final ---
412
+ return {
413
+ noGoals,
414
+ goalsCount,
415
+
416
+ firstScorerTeam,
417
+ firstScorerPlayer,
418
+ lastScorerTeam,
419
+ lastScorerPlayer,
420
+
421
+ earliestGoalByTeam,
422
+ scoredFirst15,
423
+
424
+ twoGoalsInWindow,
425
+ goalsBySegment,
426
+
427
+ playersGoals,
428
+ multiScorersList,
429
+ multiScorers,
430
+ hatTricks,
431
+
432
+ comeback,
433
+ leadChanges
434
+ };
435
+ } catch (error) {
436
+ return { ok: false, error: { code: error.message, message: error.stack } }
437
+ }
438
+ }
439
+
440
+
441
+ module.exports = { filterStats, filterIncidents, goalSummary };