og-statistics 2.0.4 → 3.0.1

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