From cf8812a9ef877ab5b72ae3c6b8783c6ddfb90fa3 Mon Sep 17 00:00:00 2001 From: Nicholas Melhado Date: Tue, 10 Jan 2023 11:14:11 -0500 Subject: [PATCH] Converting to tracking rankings by manager (#185) This PR holds several key changes - Rankings are now tracked by manager (for all time records as well as in the manager page) - This increases the overall accuracy of the Records and Rankings page (as well as the manager pages) - It now uses the league specific avatar (if one exists) - The chart style has been updated to better accommodate more entries (horizontal bars instead of vertical) - Waivers have received a face lift and now look much cleaner and better - Commissioner badge added on the managers pages Preview of changes below: | Old | New | |---|---| | ![old waivers](https://user-images.githubusercontent.com/39418000/211601742-568d71ff-e3e1-4419-8c23-2aed436b46d7.png) | ![new waivers](https://user-images.githubusercontent.com/39418000/211601802-6ec40035-569b-4d58-902e-4fa4d936f6aa.png) | | ![old waivers tr](https://user-images.githubusercontent.com/39418000/211602064-c2617271-12ba-40e9-acc5-ddfc53efb5ff.png) | ![new waivers tr](https://user-images.githubusercontent.com/39418000/211602119-cb56b85f-8f52-481e-86d2-9ee9722b6a2e.png) | | ![old records single](https://user-images.githubusercontent.com/39418000/211602226-72b5d79c-a8c8-40ab-920b-62ff879846e9.png) | ![new records - single](https://user-images.githubusercontent.com/39418000/211602267-cbb9ac26-c7c1-4eb7-8306-2fcc0c4e4776.png) | | ![old records vs](https://user-images.githubusercontent.com/39418000/211602352-814d3daa-a013-4548-bd00-b2fa99f7be89.png) | ![new records vs](https://user-images.githubusercontent.com/39418000/211602397-92b7275e-7ff9-4b05-b1fd-355f67c7bbb6.png) | | ![old chart](https://user-images.githubusercontent.com/39418000/211602484-f59b219a-bda8-4901-8d4c-f3d320453b0e.png) | ![new chart](https://user-images.githubusercontent.com/39418000/211602542-17908108-f74a-4e32-a7f0-b1d9883a6056.png) | __**Due to the enormity of this change, there may be bugs. I have done my best to check the edge conditions, but the scope of this is large enough that I may have missed some**__ --- CHANGELOG.md | 15 + README.md | 2 +- TRAINING_WHEELS.md | 15 +- package-lock.json | 16 +- package.json | 2 +- src/lib/Awards/Awards.svelte | 32 +- src/lib/Bar.svelte | 156 ++++++++++ src/lib/BarChart.svelte | 227 +++----------- src/lib/BlogPosts/Comments.svelte | 23 +- src/lib/BlogPosts/HomePost.svelte | 11 +- src/lib/BlogPosts/Post.svelte | 8 +- src/lib/BlogPosts/Posts.svelte | 8 +- src/lib/Drafts/Draft.svelte | 13 +- src/lib/Drafts/DraftRow.svelte | 23 +- src/lib/Drafts/index.svelte | 17 +- src/lib/Footer.svelte | 79 +++-- src/lib/Managers/AllManagers.svelte | 4 +- src/lib/Managers/Manager.svelte | 71 +++-- src/lib/Managers/ManagerAwards.svelte | 101 ++++--- src/lib/Managers/ManagerRow.svelte | 54 +++- src/lib/Matchups/Brackets.svelte | 8 +- src/lib/Matchups/BracketsColumn.svelte | 27 +- src/lib/Matchups/Matchup.svelte | 5 +- src/lib/Matchups/MatchupWeeks.svelte | 4 +- src/lib/Matchups/MatchupsAndBrackets.svelte | 10 +- .../PowerRankings/PowerRankingsDisplay.svelte | 47 +-- src/lib/PowerRankings/index.svelte | 9 +- src/lib/Records/AllTimeRecords.svelte | 67 ++--- src/lib/Records/PerSeasonRecords.svelte | 14 +- src/lib/Records/RecordTeam.svelte | 85 ++++++ src/lib/Records/RecordsAndRankings.svelte | 278 ++++++++++-------- src/lib/Records/index.svelte | 10 +- src/lib/Rosters/Roster.svelte | 10 +- src/lib/Rosters/RosterSorter.svelte | 4 +- src/lib/Rosters/Rosters.svelte | 4 +- src/lib/Standings/Standing.svelte | 8 +- src/lib/Standings/index.svelte | 9 +- src/lib/Transactions/Transaction.svelte | 19 +- src/lib/Transactions/TransactionMove.svelte | 9 +- src/lib/Transactions/Transactions.svelte | 15 +- src/lib/Transactions/TransactionsPage.svelte | 10 +- src/lib/Transactions/WaiverTransaction.svelte | 187 ++++++++++++ src/lib/barChartResize.js | 63 ---- src/lib/stores.js | 2 +- src/lib/utils/Classes/records.js | 80 ++--- src/lib/utils/helper.js | 7 +- src/lib/utils/helperFunctions/leagueAwards.js | 73 +---- .../utils/helperFunctions/leagueBrackets.js | 42 +-- src/lib/utils/helperFunctions/leagueDrafts.js | 107 ++----- .../utils/helperFunctions/leagueMatchups.js | 14 +- .../utils/helperFunctions/leagueRecords.js | 231 ++++++--------- .../helperFunctions/leagueTeamManagers.js | 69 +++++ .../helperFunctions/leagueTransactions.js | 132 +++------ src/lib/utils/helperFunctions/leagueUsers.js | 27 -- .../helperFunctions/universalFunctions.js | 262 ++++++++++++++--- src/lib/utils/leagueInfo.js | 8 +- src/lib/version.js | 2 +- src/routes/+page.svelte | 57 ++-- .../api/addBlogComments/[id]/+server.js | 26 +- src/routes/awards/+page.js | 4 +- src/routes/awards/+page.svelte | 9 +- src/routes/blog/+page.js | 6 +- src/routes/blog/+page.svelte | 4 +- src/routes/drafts/+page.js | 10 +- src/routes/drafts/+page.svelte | 4 +- src/routes/manager/+page.js | 9 +- src/routes/manager/+page.svelte | 33 ++- src/routes/managers/+page.js | 24 +- src/routes/managers/+page.svelte | 18 +- src/routes/matchups/+page.js | 3 +- src/routes/matchups/+page.svelte | 4 +- src/routes/records/+page.js | 3 +- src/routes/records/+page.svelte | 4 +- src/routes/rosters/+page.js | 4 +- src/routes/rosters/+page.svelte | 4 +- src/routes/standings/+page.js | 6 +- src/routes/standings/+page.svelte | 4 +- src/routes/transactions/+page.js | 4 +- src/routes/transactions/+page.svelte | 8 +- src/theme/_smui-theme.scss | 14 +- src/theme/dark/_smui-theme.scss | 12 + static/retired.png | Bin 0 -> 9332 bytes 82 files changed, 1735 insertions(+), 1374 deletions(-) create mode 100644 src/lib/Bar.svelte create mode 100644 src/lib/Records/RecordTeam.svelte create mode 100644 src/lib/Transactions/WaiverTransaction.svelte delete mode 100644 src/lib/barChartResize.js create mode 100644 src/lib/utils/helperFunctions/leagueTeamManagers.js delete mode 100644 src/lib/utils/helperFunctions/leagueUsers.js create mode 100644 static/retired.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a22b3b7..0e8706d5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. +## [2.1.0] - 2023-01-10 + +### Changed + +- Rankings are now organize by manager instead of team! [(issue #164)](https://github.com/nmelhado/league-page/issues/164) + - This means that if managers have changed throughout the years, you will now see accurate rankings for a given manager + - **To take full advantage of this you need to add managerIDs to the managers object in leagueInfo`src/lib/utils/leagueInfo.js`** + - More instructions in the updated [Training Wheels managers instructions](https://github.com/nmelhado/league-page/blob/master/TRAINING_WHEELS.md#2-add-managers) +- Use league specific avatar if avilable [(issue #156)](https://github.com/nmelhado/league-page/issues/156) + +### Fixed + +- Standings order issue [(issue #179)](https://github.com/nmelhado/league-page/issues/179) + - The re-implememntation of how team and user data is rendered should have fixed this + ## [2.0.2] - 2022-09-29 ### Fixed diff --git a/README.md b/README.md index 9afd0f73a..bf13bd4f1 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Generate a custom league page for your Sleeper fantasy football league in just a ![league ID instructions](https://storage.googleapis.com/nfl-player-data/league_id_instructions.png) - Write your homepage text (league intro/bio) `/src/lib/utils/leagueInfo.js` (lines 9-14) ![homepage text](https://storage.googleapis.com/nfl-player-data/homepage_text.png) -- Next, fill out and uncomment (delete the `// ` at the beginning of each line) the managers' object (lines 27 - 92), also located in `/src/lib/utils/leagueInfo.js`, there should be one object for each manager (for assistance, consult the [Training Wheels guide](https://github.com/nmelhado/league-page/blob/master/TRAINING_WHEELS.md#ii-adding-managers-and-changing-the-homepage-text)) +- Next, fill out and uncomment (delete the `// ` at the beginning of each line) the managers' object (lines 27 - 92), also located in `/src/lib/utils/leagueInfo.js`, there should be one object for each manager. The structure may change in the future (it has already 😅). The source of truth is down at the bottom, lines 104-126 (for assistance, consult the [Training Wheels guide](https://github.com/nmelhado/league-page/blob/master/TRAINING_WHEELS.md#ii-adding-managers-and-changing-the-homepage-text)). ![manager object](https://storage.googleapis.com/nfl-player-data/managersObj.png) ![manager rendering](https://storage.googleapis.com/nfl-player-data/managerRendering.png) - Add corresponding images for managers to the `/static/managers/` directory and make the sure the name matches with what was provided above diff --git a/TRAINING_WHEELS.md b/TRAINING_WHEELS.md index 30e923491..5c30add80 100644 --- a/TRAINING_WHEELS.md +++ b/TRAINING_WHEELS.md @@ -117,15 +117,14 @@ Generate a custom league page for your Sleeper fantasy football league in just a
- Fill each one out as follows: - - `"roster" :` give the roster ID for this manager - - To find the roster ID for the manager, go back to your website and scroll down to the `Power Rankings` graph (or to any of the graphs in your records page) - > ![pRankings](https://storage.googleapis.com/nfl-player-data/pRankings.png) - -
- - - The roster ID is the order of the bar chart, the first bar is roster ID 1, the second is roster ID 2, etc. + - `"roster" :` **This has been deprecated! You can ignore this field as long as you provide a managerID** + - ~~To find the roster ID for the manager, go back to your website and scroll down to the `Power Rankings` graph (or to any of the graphs in your records page)~~ (Power Rankings are no longer in order) + - `"managerID" :` the user ID of the manager + - To find a manager's user ID, go to `https://api.sleeper.app/v1/league/ - import {cleanName, gotoManager} from '$lib/utils/helper'; - export let podium, currentManagers; + import { gotoManager } from '$lib/utils/helper'; + import { getAvatarFromTeamManagers, getNestedTeamNamesFromTeamManagers } from '$lib/utils/helperFunctions/universalFunctions'; + export let podium, leagueTeamManagers; const { year, champion, second, third, divisions, toilet } = podium; - - const getNames = (name, rosterID) => { - if(cleanName(name) != cleanName(currentManagers[rosterID].name)) { - return `${name}
(${currentManagers[rosterID].name})
`; - } - return name; - } + +
+ team avatar gotoManager({leagueTeamManagers, managerID, rosterID})} style="border-color: var({color});" class="teamAvatar clickable" src="{user ? `https://sleepercdn.com/avatars/thumbs/${user.avatar}` : getAvatarFromTeamManagers(leagueTeamManagers, rosterID, year)}" /> + gotoManager({leagueTeamManagers, managerID, rosterID})}> + {#if user} + {user.display_name} + {:else if rosterID} + {getTeamNameFromTeamManagers(leagueTeamManagers, rosterID, year)} + {/if} + +
+
+
+
+
+ {#if !secondStat} + {stat}{label} + {/if} +
+ {#if secondStat} +
+ {secondStat}  of  {stat}  ({round(secondStat/stat*100)}%) +
+ {/if} +
+
+
+
+
\ No newline at end of file diff --git a/src/lib/BarChart.svelte b/src/lib/BarChart.svelte index e235f7eda..f006f9857 100644 --- a/src/lib/BarChart.svelte +++ b/src/lib/BarChart.svelte @@ -1,104 +1,39 @@ - -
{header}
-
+
-
- - {#each stats as stat, ix} -
- {stat}{labels.stat} -
- {/each} - {#each secondStats as stat, ix} -
- {/each} -
- - -
-
{labels.y}
-
- {#each yScales as yScale, ix} -
{yScale}
- {/each} -
-
- - -
-
{labels.x}
-
- {#each managers as manager, ix} -
{manager.name}
- {/each} -
-
+ {#each managerIDs as managerID, ix} + + {/each}
diff --git a/src/lib/BlogPosts/Comments.svelte b/src/lib/BlogPosts/Comments.svelte index 753baa209..deb18dc2d 100644 --- a/src/lib/BlogPosts/Comments.svelte +++ b/src/lib/BlogPosts/Comments.svelte @@ -7,7 +7,7 @@ const lang = "en-US"; - export let comments, total, rosters, users, postID; + export let comments, total, leagueTeamManagers, postID; let open = false; let errorMessage = ''; @@ -53,17 +53,16 @@ } const validateID = (author) => { - let user = null; - for(const userKey of Object.keys(users)) { - if(users[userKey].display_name.toLowerCase() == author.toLowerCase()) { - user = users[userKey]; - break; + for(const yearKey in leagueTeamManagers.teamManagersMap) { + for(const rosterKey in leagueTeamManagers.teamManagersMap[yearKey]) { + for(const manager of leagueTeamManagers.teamManagersMap[yearKey][rosterKey].managers) { + if(leagueTeamManagers.users[manager].display_name.toLowerCase() == author.toLowerCase()) { + return manager; + } + } } } - if(!user) { - return false; - } - return user.user_id; + return false; } let showWrite = false; @@ -134,8 +133,8 @@
{#each comments as comment}
- author avatar - {@html getAuthor(rosters, users, comment.fields.author)} + author avatar + {@html getAuthor(leagueTeamManagers, comment.fields.author)} - {@html comment.fields.comment}
{parseDate(comment.sys.createdAt)}
diff --git a/src/lib/BlogPosts/HomePost.svelte b/src/lib/BlogPosts/HomePost.svelte index 7984476d6..d013db79e 100644 --- a/src/lib/BlogPosts/HomePost.svelte +++ b/src/lib/BlogPosts/HomePost.svelte @@ -2,19 +2,20 @@ import { onMount } from "svelte"; import LinearProgress from '@smui/linear-progress'; import Post from "./Post.svelte"; - import { getBlogPosts, getLeagueRosters, getLeagueUsers, waitForAll } from "$lib/utils/helper"; + import { getBlogPosts, getLeagueRosters, getLeagueTeamManagers, waitForAll } from "$lib/utils/helper"; const lang = "en-US"; let post; let createdAt; let loading = true; - let users, rosters = []; + let rosters = []; + let leagueTeamManagers = {}; onMount(async() => { - const [{posts, fresh}, usersData, rostersData] = await waitForAll(getBlogPosts(null), getLeagueUsers(), getLeagueRosters()); - users = usersData; + const [{posts, fresh}, leagueTeamManagersData, rostersData] = await waitForAll(getBlogPosts(null), getLeagueTeamManagers(), getLeagueRosters()); rosters = rostersData.rosters; + leagueTeamManagers = leagueTeamManagersData; for(const singlePost of posts) { if(singlePost.fields.featured) { createdAt = singlePost.sys.createdAt; @@ -78,7 +79,7 @@
{:else}

League Blog

- + diff --git a/src/lib/BlogPosts/Post.svelte b/src/lib/BlogPosts/Post.svelte index 430ce9c2e..f8a56f838 100644 --- a/src/lib/BlogPosts/Post.svelte +++ b/src/lib/BlogPosts/Post.svelte @@ -4,7 +4,7 @@ import { fly } from "svelte/transition"; import Comments from "./Comments.svelte"; - export let rosters, users, post, createdAt, id = null, direction = 1, home = false; + export let rosters, leagueTeamManagers, post, createdAt, id = null, direction = 1, home = false; const lang = "en-US"; @@ -220,15 +220,15 @@
{type} - author avatar - {@html getAuthor(rosters, users, author)} - + author avatar + {@html getAuthor(leagueTeamManagers, author)} - {parseDate(createdAt)}
{#if !loadingComments && !home}
- + {/if}
diff --git a/src/lib/BlogPosts/Posts.svelte b/src/lib/BlogPosts/Posts.svelte index 2bf9af8d0..a982bd262 100644 --- a/src/lib/BlogPosts/Posts.svelte +++ b/src/lib/BlogPosts/Posts.svelte @@ -7,7 +7,7 @@ import Post from "./Post.svelte"; import { browser } from '$app/environment'; - export let postsData, usersData, rostersData, queryPage = 1, filterKey = ''; + export let postsData, leagueTeamManagersData, rostersData, queryPage = 1, filterKey = ''; let page = queryPage - 1; @@ -16,7 +16,7 @@ let loading = true; let allPosts = []; let posts = []; - let users = {}; + let leagueTeamManagers = {}; let rosters = []; let categories; @@ -38,7 +38,7 @@ onMount(async ()=> { const startPostData = await postsData; - users = await usersData; + leagueTeamManagers = await leagueTeamManagersData; const rostersInfo = await rostersData; rosters = rostersInfo.rosters; allPosts = startPostData.posts; @@ -166,7 +166,7 @@ {#each displayPosts as post} {#key post.sys.id} - + {/key} {/each} diff --git a/src/lib/Drafts/Draft.svelte b/src/lib/Drafts/Draft.svelte index a339c22f9..9922b979a 100644 --- a/src/lib/Drafts/Draft.svelte +++ b/src/lib/Drafts/Draft.svelte @@ -3,11 +3,12 @@ import LinearProgress from '@smui/linear-progress'; import { onMount } from 'svelte'; import DraftRow from './DraftRow.svelte'; - import { cleanName, gotoManager } from '$lib/utils/helper' + import { gotoManager } from '$lib/utils/helper' + import { getAvatarFromTeamManagers, getTeamNameFromTeamManagers } from '$lib/utils/helperFunctions/universalFunctions'; - export let draftData, previous = false; + export let draftData, leagueTeamManagers, previous = false, year, players; - const {draftOrder, draft, currentManagers, originalManagers, accuracy, reversalRound, draftType} = draftData; + const {draftOrder, draft, accuracy, reversalRound, draftType} = draftData; let progress = 0; let closed = false; @@ -122,9 +123,9 @@ {#each draftOrder as draftPosition} {#if draftPosition} - gotoManager(draftPosition)} src="{originalManagers[draftPosition].avatar}" alt="{originalManagers[draftPosition].name} avatar"/> + gotoManager({leagueTeamManagers, rosterID: draftPosition})} src="{getAvatarFromTeamManagers(leagueTeamManagers, draftPosition, year)}" alt="{getTeamNameFromTeamManagers(leagueTeamManagers, draftPosition, year)} avatar"/>
- gotoManager(draftPosition)}>{originalManagers[draftPosition].name}{@html currentManagers && cleanName(currentManagers[draftPosition].name) != cleanName(originalManagers[draftPosition].name) ? `
(${currentManagers[draftPosition].name})` : ''}
+ gotoManager({leagueTeamManagers, rosterID: draftPosition})}>{getTeamNameFromTeamManagers(leagueTeamManagers, draftPosition, year)}{@html getTeamNameFromTeamManagers(leagueTeamManagers, draftPosition, year) != getTeamNameFromTeamManagers(leagueTeamManagers, draftPosition) ? `
(${getTeamNameFromTeamManagers(leagueTeamManagers, draftPosition)})` : ''}
{/if} {/each} @@ -132,7 +133,7 @@ {#each draft as draftRow, row} - + {/each} diff --git a/src/lib/Drafts/DraftRow.svelte b/src/lib/Drafts/DraftRow.svelte index d60fc638e..af83e7e76 100644 --- a/src/lib/Drafts/DraftRow.svelte +++ b/src/lib/Drafts/DraftRow.svelte @@ -1,6 +1,7 @@ -{#await upcomingDraftData } +{#await waitForAll(upcomingDraftData, leagueTeamManagersData, playersData) }

Retrieving upcoming draft...


-{:then upcomingDraft } +{:then [upcomingDraft, leagueTeamManagers, {players}] }

Upcoming {upcomingDraft.year} Draft

- + {:catch error}

Something went wrong: {error.message}

{/await} -{#await previousDraftsData } +{#await waitForAll(previousDraftsData, leagueTeamManagersData, playersData) }

Previous Drafts

@@ -46,14 +47,14 @@
-{:then previousDrafts } +{:then [previousDrafts, leagueTeamManagers, {players}] } {#if previousDrafts.length}

Previous Drafts

{#each previousDrafts as previousDraft}
{previousDraft.year} Draft
- + {/each} {/if} {:catch error} diff --git a/src/lib/Footer.svelte b/src/lib/Footer.svelte index e39c8c732..84a14015e 100644 --- a/src/lib/Footer.svelte +++ b/src/lib/Footer.svelte @@ -1,5 +1,6 @@ @@ -78,42 +95,18 @@ font-size: 0.8em; margin-top: 0; } - - .invisible { - visibility: hidden; - pointer-events: none; - } -
+
-
-

There is an update available for your League Page. Follow the Update Instructions to get all of the newest features!

- - - © 2021 - {year} League Page -
- - Built by Nicholas Melhado
- - Love League Page? Please consider donating to support enhancements or just to say thank you! -
- -
+
+ {#if outOfDate} +

There is an update available for your League Page. Follow the Update Instructions to get all of the newest features!

+ {/if} + {#if managersOutOfDate} +

Your managers page needs an update, please follow the instructions to get the most up-to-date experience.

+ {/if} {:else} - + {/if}
diff --git a/src/lib/Managers/ManagerAwards.svelte b/src/lib/Managers/ManagerAwards.svelte index 4b4523ab8..5e0dfa2db 100644 --- a/src/lib/Managers/ManagerAwards.svelte +++ b/src/lib/Managers/ManagerAwards.svelte @@ -1,7 +1,8 @@ -
goto(`/manager?manager=${key}`)}> - {manager.name} +
goto(`/manager?manager=${key}`)}> +
+ {manager.name} + {#if commissioner} +
+ C +
+ {/if} +
{manager.name}
-
{user.metadata.team_name ? user.metadata.team_name : user.display_name}
+
{getTeamNameFromTeamManagers(leagueTeamManagers, rosterID, year)}
diff --git a/src/lib/Matchups/Brackets.svelte b/src/lib/Matchups/Brackets.svelte index 3779a097b..54fe135da 100644 --- a/src/lib/Matchups/Brackets.svelte +++ b/src/lib/Matchups/Brackets.svelte @@ -5,7 +5,7 @@ import BracketsColumn from "./BracketsColumn.svelte"; import Matchup from "./Matchup.svelte"; - export let players, brackets, selection, queryWeek; + export let leagueTeamManagers, players, brackets, selection, queryWeek; const {playoffsStart, champs, losers, numRosters} = brackets; @@ -103,14 +103,14 @@
{#each bracket as matchCol, ix} - + {/each}
{#each consolations as consolation, consolationNum}
{#each consolation as matchCol, ix} - + {/each}
{/each} @@ -132,6 +132,6 @@
{/if} - + {/if}
\ No newline at end of file diff --git a/src/lib/Matchups/BracketsColumn.svelte b/src/lib/Matchups/BracketsColumn.svelte index ffb1753ab..4f483d63b 100644 --- a/src/lib/Matchups/BracketsColumn.svelte +++ b/src/lib/Matchups/BracketsColumn.svelte @@ -1,7 +1,8 @@ -{#if validGraph} -
- +{#if validGraph && !seasonOver} +
+
{/if} \ No newline at end of file diff --git a/src/lib/PowerRankings/index.svelte b/src/lib/PowerRankings/index.svelte index 43b2c8a21..72aa96639 100644 --- a/src/lib/PowerRankings/index.svelte +++ b/src/lib/PowerRankings/index.svelte @@ -1,13 +1,12 @@ diff --git a/src/lib/Records/PerSeasonRecords.svelte b/src/lib/Records/PerSeasonRecords.svelte index 2a8968b91..9afde3071 100644 --- a/src/lib/Records/PerSeasonRecords.svelte +++ b/src/lib/Records/PerSeasonRecords.svelte @@ -3,7 +3,7 @@ import {round} from '$lib/utils/helper' import RecordsAndRankings from './RecordsAndRankings.svelte'; - export let leagueRosterRecords, seasonWeekRecords, currentManagers, currentYear, lastYear, transactionTotals, key; + export let leagueRosterRecords, seasonWeekRecords, leagueTeamManagers, currentYear, lastYear, transactionTotals, key; let yearsObj = {}; let years = []; @@ -41,20 +41,18 @@ for(const rosterID in transactionTotals.seasons[season]) { yearsObj[season].tradesData.push({ rosterID, - manager: transactionTotals.seasons[season][rosterID].manager, trades: transactionTotals.seasons[season][rosterID].trade, }) yearsObj[season].waiversData.push({ rosterID, - manager: transactionTotals.seasons[season][rosterID].manager, waivers: transactionTotals.seasons[season][rosterID].waiver, }) } } for(const rosterID in lRR) { - const leagueRosterRecord = lRR[rosterID]; - for(const season of leagueRosterRecord.years) { + const leagueManagerRecord = lRR[rosterID]; + for(const season of leagueManagerRecord.years) { // check for ties if(season.ties > 0) { yearsObj[season.year].showTies = true; @@ -65,7 +63,6 @@ // add season-long scoring record yearsObj[season.year].seasonLongRecords.push({ - manager: season.manager, rosterID, fpts, fptsPerGame, @@ -75,7 +72,6 @@ // add win percentage rankings yearsObj[season.year].winPercentages.push({ rosterID, - manager: season.manager, percentage: round((season.wins + season.ties / 2) / (season.wins + season.ties + season.losses) * 100), wins: season.wins, ties: season.ties, @@ -85,7 +81,6 @@ // add lineup IQ rankings let lineupIQ = { rosterID, - manager: season.manager, fpts: round(season.fpts), } if(season.potentialPoints) { @@ -98,7 +93,6 @@ // add fantasy points histories yearsObj[season.year].fptsHistories.push({ rosterID, - manager: season.manager, fptsFor: round(season.fpts), fptsAgainst: round(season.fptsAgainst), fptsPerGame: round(season.fptsPerGame), @@ -186,6 +180,6 @@ blowouts={years[display].blowouts} closestMatchups={years[display].closestMatchups} prefix={years[display].year} - {currentManagers} + {leagueTeamManagers} {key} /> diff --git a/src/lib/Records/RecordTeam.svelte b/src/lib/Records/RecordTeam.svelte new file mode 100644 index 000000000..6fa2dbd83 --- /dev/null +++ b/src/lib/Records/RecordTeam.svelte @@ -0,0 +1,85 @@ + + + + +
+ {#if user} + team avatar + {:else if rosterID} + team avatar + {/if} + +
+ {#if user} + {user.display_name} + {:else if rosterID} + {getTeamNameFromTeamManagers(leagueTeamManagers, rosterID, year)} + {points ? ` (${points})` : ""} + {/if} +
+ {#if !user} +
+ {renderManagerNames(leagueTeamManagers, rosterID, year)} +
+ {/if} +
+
\ No newline at end of file diff --git a/src/lib/Records/RecordsAndRankings.svelte b/src/lib/Records/RecordsAndRankings.svelte index 6c546fea0..f6aa19a0a 100644 --- a/src/lib/Records/RecordsAndRankings.svelte +++ b/src/lib/Records/RecordsAndRankings.svelte @@ -1,11 +1,12 @@ + +
gotoManager({leagueTeamManagers, rosterID: owner})}> +
+ + {getTeamFromTeamManagers(leagueTeamManagers, owner, transaction.season).name} + {#if getTeamFromTeamManagers(leagueTeamManagers, owner, transaction.season).name != getTeamFromTeamManagers(leagueTeamManagers, owner).name} + ({getTeamFromTeamManagers(leagueTeamManagers, owner).name}) + {/if} + {#if transaction.moves[0][0].bid} + + - {transaction.moves[0][0].bid}$ + + {/if} + + {getTeamFromTeamManagers(leagueTeamManagers, owner, transaction.season).name} avatar +
+
+
+
+ {#each transaction.moves as move} +
+
+ {#if move[0].type == "Added"} + + {:else if move[0].type == "Dropped"} + + {/if} +
+ + {`${players[move[0].player].fn} ${players[move[0].player].ln}`} + + {players[move[0].player].pos} + {#if players[move[0].player].t} + - + {players[move[0].player].t} + {/if} + + +
+ {/each} +
+
+ + {transaction.date} + +
+
diff --git a/src/lib/barChartResize.js b/src/lib/barChartResize.js deleted file mode 100644 index 90821d463..000000000 --- a/src/lib/barChartResize.js +++ /dev/null @@ -1,63 +0,0 @@ -export const xIntervalFont = (width) => { - if(width > 780) { - return 1; - } - if(width > 650) { - return 0.9; - } - if(width > 570) { - return 0.7; - } - if(width > 460) { - return 0.6; - } - if(width > 420) { - return 0.5; - } - return 0.4; -} - -export const xIntervalHeight = (width) => { - if(width > 900) { - return 160; - } - if(width > 780) { - return 140; - } - if(width > 650) { - return 120; - } - if(width > 570) { - return 100; - } - if(width > 460) { - return 80; - } - if(width > 420) { - return 70; - } - if(width > 350) { - return 60; - } - return 56; -} - -export const barLabelFont = (width) => { - if(width > 650) { - return 0.7; - } - if(width > 570) { - return 0.5; - } - if(width > 460) { - return 0.4; - } - return 0.3; -} - -export const labelFont = (width) => { - if(width > 460) { - return 1; - } - return 0.7; -} \ No newline at end of file diff --git a/src/lib/stores.js b/src/lib/stores.js index 4c2d29a5f..f177d3a14 100644 --- a/src/lib/stores.js +++ b/src/lib/stores.js @@ -8,7 +8,7 @@ export const matchupsStore = writable({}); export const records = writable({}); export const rostersStore = writable({}); export const transactionsStore = writable({}); -export const users = writable({}); +export const teamManagersStore = writable({}); export const nflState = writable({}); export const players = writable({}); export const news = writable([]); diff --git a/src/lib/utils/Classes/records.js b/src/lib/utils/Classes/records.js index 83f20706f..7aed4b665 100644 --- a/src/lib/utils/Classes/records.js +++ b/src/lib/utils/Classes/records.js @@ -8,7 +8,8 @@ export class Records { * Can be used for both regular season, as well as playoff records. */ constructor() { // constructor returns empty records block - this.leagueRosterRecords = {}; // every full season stat point (for each year and all years combined) + this.leagueManagerRecords = {}; // holds the all-time stats for each manager in the league + this.leagueRosterRecords = {}; // holds the per season stats for each roster (for each year) this.seasonWeekRecords = []; // highest weekly points within a single season this.leagueWeekRecords = []; // keeps track of weekly points in all seasons combined this.seasonLongPoints = []; // keeps track of season long points @@ -21,7 +22,6 @@ export class Records { this.leagueWeekLows = []; this.leagueWeekHighs = []; - this.currentManagers = []; this.currentYear = null; this.lastYear = null; } @@ -30,13 +30,13 @@ export class Records { // Class functions to help build league records below /** - * Check if a roster record exists for a given roster ID and, if not, create one. - * @param {int} rosterID + * Check if a manger record exists for a given manager ID and, if not, create one. + * @param {int} managerID */ -Records.prototype.confirmRosterRecord = function(rosterID) { - // if no leagueRosterRecord exists for a roster, create one - if(!this.leagueRosterRecords[rosterID]) { - this.leagueRosterRecords[rosterID] = { +Records.prototype.confirmManagerRecord = function(managerID) { + // if no leagueManagerRecord exists for a manager, create one + if(!this.leagueManagerRecords[managerID]) { + this.leagueManagerRecords[managerID] = { wins: 0, losses: 0, ties: 0, @@ -46,6 +46,18 @@ Records.prototype.confirmRosterRecord = function(rosterID) { pOGames: 0, byes: 0, playoffAppearances: 0, + } + } +} + +/** + * Check if a roster record exists for a given roster ID and, if not, create one. + * @param {int} rosterID + */ +Records.prototype.confirmRosterRecord = function(rosterID) { + // if no leagueRosterRecords exists for a roster, create one + if(!this.leagueRosterRecords[rosterID]) { + this.leagueRosterRecords[rosterID] = { years: [] } } @@ -53,10 +65,10 @@ Records.prototype.confirmRosterRecord = function(rosterID) { /** - * Update the internal leagueRosterRecords for a given roster ID - * @param {number} rosterID + * Update the internal leagueManagerRecords for a given roster ID + * @param {Object[]} managers array of managers that need this record attached * @param {Object} recordsData - * @param {any} recordsData.manager + * @param {any} recordsData.team * @param {number} recordsData.year * @param {number} recordsData.wins * @param {number} recordsData.losses @@ -68,21 +80,24 @@ Records.prototype.confirmRosterRecord = function(rosterID) { * @param {number} recordsData.pOGames * @param {number} recordsData.byes */ -Records.prototype.updateRosterRecord = function(rosterID, {manager, year, wins, losses, ties, fptsPerGame, fptsFor, fptsAgainst, potentialPoints, pOGames, byes}) { +Records.prototype.updateManagerRecord = function(managers, {rosterID, year, wins, losses, ties, fptsPerGame, fptsFor, fptsAgainst, potentialPoints, pOGames, byes}) { // check that a roster record has already been started for a given roster ID + for(const managerID of managers) { + this.confirmManagerRecord(managerID); + + // add all-time data + this.leagueManagerRecords[managerID].wins += wins; + this.leagueManagerRecords[managerID].losses += losses; + this.leagueManagerRecords[managerID].ties += ties; + this.leagueManagerRecords[managerID].fptsFor += fptsFor; + this.leagueManagerRecords[managerID].fptsAgainst += fptsAgainst; + this.leagueManagerRecords[managerID].potentialPoints += potentialPoints; + this.leagueManagerRecords[managerID].pOGames += pOGames; + this.leagueManagerRecords[managerID].byes += byes; + this.leagueManagerRecords[managerID].playoffAppearances ++; + } + this.confirmRosterRecord(rosterID); - - // add all-time data - this.leagueRosterRecords[rosterID].wins += wins; - this.leagueRosterRecords[rosterID].losses += losses; - this.leagueRosterRecords[rosterID].ties += ties; - this.leagueRosterRecords[rosterID].fptsFor += fptsFor; - this.leagueRosterRecords[rosterID].fptsAgainst += fptsAgainst; - this.leagueRosterRecords[rosterID].potentialPoints += potentialPoints; - this.leagueRosterRecords[rosterID].pOGames += pOGames; - this.leagueRosterRecords[rosterID].byes += byes; - this.leagueRosterRecords[rosterID].playoffAppearances ++; - // add the single season data this.leagueRosterRecords[rosterID].years.push({ wins, @@ -94,7 +109,7 @@ Records.prototype.updateRosterRecord = function(rosterID, {manager, year, wins, potentialPoints, pOGames, byes, - manager, + rosterID, year, }); } @@ -103,20 +118,19 @@ Records.prototype.updateRosterRecord = function(rosterID, {manager, year, wins, /** * add an entry to the seasonLongPoints array * @param {Object} recordData - * @param {int} recordData.rosterID + * @param {Object[]} recordData.rosterID * @param {float} recordData.fpts * @param {float} recordData.fptsPerGame * @param {int} recordData.year - * @param {Object} recordData.manager + * @param {Object} recordData.team */ -Records.prototype.addSeasonLongPoints = function({rosterID, fpts, fptsPerGame, year, manager}) { +Records.prototype.addSeasonLongPoints = function({rosterID, fpts, fptsPerGame, year}) { this.seasonLongPoints.push({ rosterID, fpts, fptsPerGame, year, - manager - }) + }); } @@ -157,11 +171,10 @@ Records.prototype.addSeasonWeekRecord = function(entry) { * Once all data has been gathered, finalizeAllTimeRecords will compute the allTimeBiggestBlowouts, allTimeClosestMatchups, * leagueWeekHighs, leagueWeekLows, mostSeasonLongPoints, and leastSeasonLongPoints as well as adding the current season managers, * currentYear, and lastYear to the records Class - * @param {Object[]} currentManagers * @param {int} currentYear * @param {int} lastYear */ - Records.prototype.finalizeAllTimeRecords = function({currentManagers, currentYear, lastYear}) { + Records.prototype.finalizeAllTimeRecords = function({currentYear, lastYear}) { // sort allTimeMatchupDifferentials and return the biggest blowouts and narrowest victories const [allTimeBiggestBlowouts, allTimeClosestMatchups] = sortHighAndLow(this.allTimeMatchupDifferentials, 'differential') this.allTimeBiggestBlowouts = allTimeBiggestBlowouts; @@ -177,7 +190,6 @@ Records.prototype.addSeasonWeekRecord = function(entry) { this.mostSeasonLongPoints = mostSeasonLongPoints; this.leastSeasonLongPoints = leastSeasonLongPoints; - this.currentManagers = currentManagers; this.currentYear = currentYear; this.lastYear = lastYear; } @@ -196,8 +208,8 @@ Records.prototype.returnRecords = function() { leagueWeekLows: this.leagueWeekLows, leagueWeekHighs: this.leagueWeekHighs, seasonWeekRecords: this.seasonWeekRecords, + leagueManagerRecords: this.leagueManagerRecords, leagueRosterRecords: this.leagueRosterRecords, - currentManagers: this.currentManagers, currentYear: this.currentYear, lastYear: this.lastYear, } diff --git a/src/lib/utils/helper.js b/src/lib/utils/helper.js index 31cfe6444..28ac75be4 100644 --- a/src/lib/utils/helper.js +++ b/src/lib/utils/helper.js @@ -3,7 +3,7 @@ import {dues, leagueID, leagueName, dynasty, managers, homepageText, enableBlog} import {getLeagueTransactions} from './helperFunctions/leagueTransactions'; import {getNflState} from './helperFunctions/nflState'; import {getLeagueRosters} from './helperFunctions/leagueRosters'; -import {getLeagueUsers} from './helperFunctions/leagueUsers'; +import {getLeagueTeamManagers} from './helperFunctions/leagueTeamManagers'; import {getLeagueMatchups} from './helperFunctions/leagueMatchups' import {getNews, stringDate} from './helperFunctions/news'; import {loadPlayers} from './helperFunctions/players'; @@ -11,7 +11,7 @@ import { waitForAll } from './helperFunctions/multiPromise'; import { getUpcomingDraft, getPreviousDrafts } from './helperFunctions/leagueDrafts' import { getLeagueRecords } from './helperFunctions/leagueRecords' import { getAwards } from './helperFunctions/leagueAwards' -import { cleanName, round, generateGraph, gotoManager, getAuthor, parseDate, getAvatar } from './helperFunctions/universalFunctions'; +import { cleanName, round, generateGraph, getTeamFromTeamManagers, gotoManager, getAuthor, parseDate, getAvatar } from './helperFunctions/universalFunctions'; import { predictScores } from './helperFunctions/predictOptimalScore'; import { getBrackets } from './helperFunctions/leagueBrackets'; import { getBlogPosts, generateParagraph } from './helperFunctions/getBlogPosts'; @@ -26,7 +26,7 @@ export { getLeagueTransactions, getNflState, getLeagueRosters, - getLeagueUsers, + getLeagueTeamManagers, getLeagueMatchups, getNews, loadPlayers, @@ -51,4 +51,5 @@ export { getAuthor, parseDate, getAvatar, + getTeamFromTeamManagers, } diff --git a/src/lib/utils/helperFunctions/leagueAwards.js b/src/lib/utils/helperFunctions/leagueAwards.js index e8092a384..3274102b0 100644 --- a/src/lib/utils/helperFunctions/leagueAwards.js +++ b/src/lib/utils/helperFunctions/leagueAwards.js @@ -1,47 +1,20 @@ import { getLeagueData } from './leagueData'; import { getLeagueRosters } from './leagueRosters'; -import { getLeagueUsers } from './leagueUsers'; -import {waitForAll} from './multiPromise'; +import { waitForAll } from './multiPromise'; import { get } from 'svelte/store'; -import {awards} from '$lib/stores'; +import { awards } from '$lib/stores'; export const getAwards = async () => { if(get(awards).podiums) { return get(awards); } - const [rosterRes, users, leagueData] = await waitForAll( - getLeagueRosters(), - getLeagueUsers(), - getLeagueData() - ).catch((err) => { console.error(err); }); - - const rosters = rosterRes.rosters; - - const currentManagers = {}; - - for(const roster of rosters) { - const user = users[roster.owner_id]; - if(user) { - currentManagers[roster.roster_id] = { - avatar: `https://sleepercdn.com/avatars/thumbs/${user.avatar}`, - name: user.metadata.team_name ? user.metadata.team_name : user.display_name, - } - } else { - currentManagers[roster.roster_id] = { - avatar: `https://sleepercdn.com/images/v2/icons/player_default.webp`, - name: 'Unknown Manager', - } - } - } + const leagueData = await getLeagueData().catch((err) => { console.error(err); }); let previousSeasonID = leagueData.status == "complete" ? leagueData.league_id : leagueData.previous_league_id; const podiums = await getPodiums(previousSeasonID) - const gatheredAwards = { - podiums, - currentManagers - }; + const gatheredAwards = podiums; awards.update(() => gatheredAwards); @@ -61,7 +34,6 @@ const getPodiums = async (previousSeasonID) => { year, previousRosters, numDivisions, - usersData, playoffRounds, toiletRounds, leagueMetadata @@ -69,24 +41,23 @@ const getPodiums = async (previousSeasonID) => { previousSeasonID = previousSeasonData.previousSeasonID; - const {divisions, prevManagers} = buildDivisionsAndManagers({usersData, previousRosters, leagueMetadata, numDivisions}); + const divisions = buildDivisionsAndManagers({previousRosters, leagueMetadata, numDivisions}); // add manager to division obj and convert to array const divisionArr = [] for(const key in divisions) { - divisions[key].manager = prevManagers[divisions[key].roster]; divisionArr.push(divisions[key]); } const finalsMatch = winnersData.filter(m => m.r == playoffRounds && m.t1_from.w)[0]; - const champion = prevManagers[finalsMatch.w]; - const second = prevManagers[finalsMatch.l]; + const champion = finalsMatch.w; + const second = finalsMatch.l; const runnersUpMatch = winnersData.filter(m => m.r == playoffRounds && m.t1_from.l)[0]; - const third = prevManagers[runnersUpMatch.w]; + const third = runnersUpMatch.w; const toiletBowlMatch = losersData.filter(m => m.r == toiletRounds && (!m.t1_from || m.t1_from.w))[0]; - const toilet = prevManagers[toiletBowlMatch.w] + const toilet = toiletBowlMatch.w if(!champion) { continue; @@ -110,12 +81,11 @@ const getPreviousLeagueData = async (previousSeasonID) => { const resPromises = [ fetch(`https://api.sleeper.app/v1/league/${previousSeasonID}`, {compress: true}), getLeagueRosters(previousSeasonID), - getLeagueUsers(previousSeasonID), fetch(`https://api.sleeper.app/v1/league/${previousSeasonID}/losers_bracket`, {compress: true}), fetch(`https://api.sleeper.app/v1/league/${previousSeasonID}/winners_bracket`, {compress: true}), ] - const [leagueRes, rostersData, usersData, losersRes, winnersRes] = await waitForAll(...resPromises).catch((err) => { console.error(err); }); + const [leagueRes, rostersData, losersRes, winnersRes] = await waitForAll(...resPromises).catch((err) => { console.error(err); }); if(!leagueRes.ok || !losersRes.ok || !winnersRes.ok) { throw new Error(data); @@ -146,7 +116,6 @@ const getPreviousLeagueData = async (previousSeasonID) => { year, previousRosters, numDivisions, - usersData, previousSeasonID, playoffRounds, toiletRounds, @@ -155,9 +124,7 @@ const getPreviousLeagueData = async (previousSeasonID) => { } // determine division champions and construct previousManagers object -const buildDivisionsAndManagers = ({usersData, previousRosters, leagueMetadata, numDivisions}) => { - const prevManagers = {}; - +const buildDivisionsAndManagers = ({previousRosters, leagueMetadata, numDivisions}) => { const divisions = {}; for(let i = 0; i < numDivisions; i++) { @@ -175,23 +142,9 @@ const buildDivisionsAndManagers = ({usersData, previousRosters, leagueMetadata, if(rSettings.wins > divisions[div].wins || (rSettings.wins == divisions[div].wins && (rSettings.fpts + rSettings.fpts_decimal / 100) == divisions[div].points)) { divisions[div].points = rSettings.fpts + rSettings.fpts_decimal / 100; divisions[div].wins = rSettings.wins; - divisions[div].roster = roster.roster_id; - } - const user = usersData[roster.owner_id]; - prevManagers[roster.roster_id] = { - rosterID: roster.roster_id, - avatar: 'https://sleepercdn.com/images/v2/icons/player_default.webp', - name: 'Unknown Manager', - } - if(user) { - if(user.avatar) { - prevManagers[roster.roster_id].avatar = `https://sleepercdn.com/avatars/thumbs/${user.avatar}`; - } else { - prevManagers[roster.roster_id].avatar = 'https://sleepercdn.com/images/v2/icons/player_default.webp'; - } - prevManagers[roster.roster_id].name = user.metadata.team_name ? user.metadata.team_name : user.display_name; + divisions[div].rosterID = roster.roster_id; } } - return {divisions, prevManagers} + return divisions; } \ No newline at end of file diff --git a/src/lib/utils/helperFunctions/leagueBrackets.js b/src/lib/utils/helperFunctions/leagueBrackets.js index 99f5b528a..14982f277 100644 --- a/src/lib/utils/helperFunctions/leagueBrackets.js +++ b/src/lib/utils/helperFunctions/leagueBrackets.js @@ -1,7 +1,6 @@ import { getLeagueData } from './leagueData'; import { leagueID } from '$lib/utils/leagueInfo'; import { getLeagueRosters } from './leagueRosters'; -import { getLeagueUsers } from './leagueUsers'; import {waitForAll} from './multiPromise'; import { get } from 'svelte/store'; import {brackets} from '$lib/stores'; @@ -12,16 +11,13 @@ export const getBrackets = async (queryLeagueID = leagueID) => { } // get roster, user, and league data - const [rosterRes, users, leagueData] = await waitForAll( + const [rosterRes, leagueData] = await waitForAll( getLeagueRosters(queryLeagueID), - getLeagueUsers(queryLeagueID), getLeagueData(queryLeagueID), ).catch((err) => { console.error(err); }); - const rosters = rosterRes.rosters; - // Number of rosters (in order to determine the number of places, i.e. 1st - 12th) - const numRosters = rosters.length; + const numRosters = rosterRes.rosters.length; // get bracket data for winners and losers const bracketsAndMatchupFetches = [ @@ -82,11 +78,11 @@ export const getBrackets = async (queryLeagueID = leagueID) => { // champBracket is an object where the key will be the round number // the value at each key will be an array of matchups - const champs = evaluateBracket(winnersData, playoffRounds, playoffMatchups, rosters, users, playoffType); + const champs = evaluateBracket(winnersData, playoffRounds, playoffMatchups, playoffType); // champBracket is an object where the key will be the round number // the value at each key will be an array of matchups - let losers = evaluateBracket(losersData, loserRounds, playoffMatchups, rosters, users, playoffType); + let losers = evaluateBracket(losersData, loserRounds, playoffMatchups, playoffType); const finalBrackets = { numRosters, @@ -105,7 +101,7 @@ export const getBrackets = async (queryLeagueID = leagueID) => { return finalBrackets; } -const evaluateBracket = (contestants, rounds, playoffMatchups, rosters, users, playoffType) => { +const evaluateBracket = (contestants, rounds, playoffMatchups, playoffType) => { let bracket = []; let consolations = []; // which matches in the previous round were consolation matches @@ -124,14 +120,14 @@ const evaluateBracket = (contestants, rounds, playoffMatchups, rosters, users, p for(const playoffBracket of playoffBrackets) { if((!playoffBracket.t1_from && playoffBracket.t2_from) || (!teamsSeen[playoffBracket.t1] && teamsSeen[playoffBracket.t2])) { // this was from a team that got a bye - let byeMatchup = processPlayoffMatchup({playoffBracket, playoffMatchups, i: i - 2, rosters, users, consolationMs, fromWs, playoffType, teamsSeen}); + let byeMatchup = processPlayoffMatchup({playoffBracket, playoffMatchups, i: i - 2, consolationMs, fromWs, playoffType, teamsSeen}); byeMatchup.bye = true; byeMatchup[0].m = null; byeMatchup[1].m = null; byeMatchup[0].r--; byeMatchup[1].r--; // set the opponent to null - byeMatchup[1].manager = null; + byeMatchup[1].roster_id = null; if(first) { bracket[i - 2].unshift(byeMatchup); first = false; @@ -141,7 +137,7 @@ const evaluateBracket = (contestants, rounds, playoffMatchups, rosters, users, p } teamsSeen[playoffBracket.t1] = playoffBracket.m; teamsSeen[playoffBracket.t2] = playoffBracket.m; - const roundMatchup = processPlayoffMatchup({playoffBracket, playoffMatchups, i: i - 1, rosters, users, consolationMs, fromWs, playoffType, teamsSeen}); + const roundMatchup = processPlayoffMatchup({playoffBracket, playoffMatchups, i: i - 1, consolationMs, fromWs, playoffType, teamsSeen}); if(roundMatchup[0].winners) { // This matchup came from winners localFromWs.push(roundMatchup[0].m) @@ -184,7 +180,7 @@ const newConsolation = (consolationMatchups, rounds, i) => { return newCons; } -const processPlayoffMatchup = ({playoffBracket, playoffMatchups, i, rosters, users, consolationMs, fromWs, playoffType, teamsSeen}) => { +const processPlayoffMatchup = ({playoffBracket, playoffMatchups, i, consolationMs, fromWs, playoffType, teamsSeen}) => { const matchup = []; const m = playoffBracket.m; const r = playoffBracket.r; @@ -202,18 +198,17 @@ const processPlayoffMatchup = ({playoffBracket, playoffMatchups, i, rosters, use // first team in matchup const t1 = playoffBracket.t1; - matchup.push(generateMatchupData(t1, t1From, {m, r, playoffMatchups, i, rosters, users, playoffType, winners, fromWinners, consolation, p})); + matchup.push(generateMatchupData(t1, t1From, {m, r, playoffMatchups, i, playoffType, winners, fromWinners, consolation, p})); // second team in matchup const t2 = playoffBracket.t2; - matchup.push(generateMatchupData(t2, t2From, {m, r, playoffMatchups, i, rosters, users, playoffType, winners, fromWinners, consolation, p})); + matchup.push(generateMatchupData(t2, t2From, {m, r, playoffMatchups, i, playoffType, winners, fromWinners, consolation, p})); return matchup; } -const generateMatchupData = (t, tFrom, {m, r, playoffMatchups, i, rosters, users, playoffType, winners, fromWinners, consolation, p}) => { +const generateMatchupData = (t, tFrom, {m, r, playoffMatchups, i, playoffType, winners, fromWinners, consolation, p}) => { let matchup = { - manager: null, roster_id: null, points: undefined, starters: undefined, @@ -226,7 +221,6 @@ const generateMatchupData = (t, tFrom, {m, r, playoffMatchups, i, rosters, users } if(t) { - const tuser = users[rosters[t - 1].owner_id]; const tMatchup = playoffMatchups[i].filter(ma => ma.roster_id == t)[0]; let tMatchupStarters = {} tMatchupStarters[1] = tMatchup?.starters; @@ -244,18 +238,6 @@ const generateMatchupData = (t, tFrom, {m, r, playoffMatchups, i, rosters, users matchup.starters = tMatchupStarters; matchup.points = tMatchupStartersPoints; matchup.roster_id = t; - - if(tuser) { - matchup.manager = { - name: tuser.metadata.team_name ? tuser.metadata.team_name : tuser.display_name, - avatar: `https://sleepercdn.com/avatars/thumbs/${tuser.avatar}`, - }; - } else { - matchup.manager = { - name: 'Unknown Manager', - avatar: `https://sleepercdn.com/images/v2/icons/player_default.webp`, - }; - } } return matchup; diff --git a/src/lib/utils/helperFunctions/leagueDrafts.js b/src/lib/utils/helperFunctions/leagueDrafts.js index 90bb43695..53883cdcf 100644 --- a/src/lib/utils/helperFunctions/leagueDrafts.js +++ b/src/lib/utils/helperFunctions/leagueDrafts.js @@ -1,18 +1,16 @@ import { getLeagueData } from './leagueData'; import { leagueID } from '$lib/utils/leagueInfo'; -import { getLeagueRosters } from "./leagueRosters" -import { getLeagueUsers } from "./leagueUsers" import { waitForAll } from './multiPromise'; import { get } from 'svelte/store'; import {upcomingDraft, previousDrafts} from '$lib/stores'; +import { getLeagueRosters } from './leagueRosters'; export const getUpcomingDraft = async () => { if(get(upcomingDraft).draft) { return get(upcomingDraft); } - const [rosterRes, users, leagueData] = await waitForAll( + const [rosterRes, leagueData] = await waitForAll( getLeagueRosters(), - getLeagueUsers(), getLeagueData() ).catch((err) => { console.error(err); }); @@ -21,25 +19,6 @@ export const getUpcomingDraft = async () => { let year = parseInt(leagueData.season); - const rosters = rosterRes.rosters; - - const originalManagers = {}; - - for(const roster of rosters) { - const user = users[roster.owner_id]; - if(user) { - originalManagers[roster.roster_id] = { - avatar: `https://sleepercdn.com/avatars/thumbs/${user.avatar}`, - name: user.metadata.team_name ? user.metadata.team_name : user.display_name, - } - } else { - originalManagers[roster.roster_id] = { - avatar: `https://sleepercdn.com/images/v2/icons/player_default.webp`, - name: 'Unknown Manager', - } - } - } - const [officialDraftRes, picksRes] = await waitForAll( fetch(`https://api.sleeper.app/v1/draft/${draftID}`, {compress: true}), fetch(`https://api.sleeper.app/v1/league/${leagueID}/traded_picks`, {compress: true}), @@ -54,14 +33,16 @@ export const getUpcomingDraft = async () => { let draftOrder; let accuracy; + const rosters = rosterRes.rosters; + if(officialDraft.status == "complete") { year = year + 1; - const buildRes = buildFromScratch(rosters, officialDraft.slot_to_roster_id, officialDraft.settings.rounds, picks.filter(pick => parseInt(pick.season) == year), originalManagers, regularSeasonLength); + const buildRes = buildFromScratch(rosters, officialDraft.slot_to_roster_id, officialDraft.settings.rounds, picks.filter(pick => parseInt(pick.season) == year), regularSeasonLength); draft = buildRes.draft; draftOrder = buildRes.draftOrder; accuracy = buildRes.accuracy; } else { - const buildRes = buildConfirmed(officialDraft.slot_to_roster_id, officialDraft.settings.rounds, picks.filter(pick => parseInt(pick.season) == year), originalManagers); + const buildRes = buildConfirmed(officialDraft.slot_to_roster_id, officialDraft.settings.rounds, picks.filter(pick => parseInt(pick.season) == year)); draft = buildRes.draft; draftOrder = buildRes.draftOrder; } @@ -73,7 +54,6 @@ export const getUpcomingDraft = async () => { accuracy, draftType: officialDraft.type, reversalRound: officialDraft.settings.reversal_round, - originalManagers } upcomingDraft.update(() => draftData); @@ -82,7 +62,7 @@ export const getUpcomingDraft = async () => { } // Predict draft board -const buildFromScratch = (rosters, previousOrder, rounds, picks, originalManagers, regularSeasonLength) => { +const buildFromScratch = (rosters, previousOrder, rounds, picks, regularSeasonLength) => { const draftOrder = []; const testRoster = rosters[0].settings; const progression = testRoster.wins + testRoster.ties + testRoster.losses; @@ -119,7 +99,7 @@ const buildFromScratch = (rosters, previousOrder, rounds, picks, originalManager for(const pick of picks) { if(pick.owner_id == pick.roster_id || pick.round > rounds) continue; - draft[pick.round - 1][draftOrder.indexOf(pick.roster_id)] = originalManagers[pick.owner_id].name; + draft[pick.round - 1][draftOrder.indexOf(pick.roster_id)] = pick.owner_id; } let accuracy = (progression + 1) / (regularSeasonLength + 1); @@ -130,7 +110,7 @@ const buildFromScratch = (rosters, previousOrder, rounds, picks, originalManager } // Build pre-determined draft board -const buildConfirmed = (draftOrderObj, rounds, picks, originalManagers, players = null, type = null) => { +const buildConfirmed = (draftOrderObj, rounds, picks, players = null, type = null) => { const draftOrder = []; let leagueSize = 0; @@ -148,59 +128,47 @@ const buildConfirmed = (draftOrderObj, rounds, picks, originalManagers, players if(players && type != 'auction') { // non-auction leagues - draft = completedNonAuction({players, draft, picks, originalManagers, draftOrder, rounds}); + draft = completedNonAuction({players, draft, picks, draftOrder, rounds}); } else if(players) { // auction leagues - draft = completedAuction({players, draft, picks, originalManagers, draftOrder, draftOrderObj}); + draft = completedAuction({players, draft, draftOrder, draftOrderObj}); } else { for(const pick of picks) { if(pick.owner_id == pick.roster_id || pick.round > rounds) continue; - draft[pick.round - 1][draftOrder.indexOf(pick.roster_id)] = originalManagers[pick.owner_id].name; + draft[pick.round - 1][draftOrder.indexOf(pick.roster_id)] = pick.owner_id; } } return {draft, draftOrder}; } -const completedNonAuction = ({players, draft, picks, originalManagers, draftOrder, rounds}) => { +const completedNonAuction = ({players, draft, picks, draftOrder, rounds}) => { for(const playerData of players) { - const player = { - name: `${playerData.metadata.first_name} ${playerData.metadata.last_name}`, - position: playerData.metadata.position, - team: playerData.metadata.team, - avatar: playerData.metadata.position == "DEF" ? `background-image: url(https://sleepercdn.com/images/team_logos/nfl/${playerData.player_id.toLowerCase()}.png)` : `background-image: url(https://sleepercdn.com/content/nfl/players/thumb/${playerData.player_id}.jpg), url(https://sleepercdn.com/images/v2/icons/player_default.webp)`, - } + const player = playerData.player_id; draft[playerData.round - 1][playerData.draft_slot - 1] = {player}; } for(const pick of picks) { if(pick.owner_id == pick.roster_id || pick.round > rounds) continue; - draft[pick.round - 1][draftOrder.indexOf(pick.roster_id)].newOwner = originalManagers[pick.owner_id].name; + draft[pick.round - 1][draftOrder.indexOf(pick.roster_id)].newOwner = pick.owner_id; } return draft; } -const completedAuction = ({players, draft, picks, originalManagers, draftOrder, draftOrderObj}) => { +const completedAuction = ({players, draft, draftOrder, draftOrderObj}) => { const rosters = {}; for (const key in draftOrderObj) { // array to be used for players rosters[draftOrderObj[key]] = []; } for(const playerData of players) { - const player = { - name: `${playerData.metadata.first_name} ${playerData.metadata.last_name}`, - position: playerData.metadata.position, - team: playerData.metadata.team, - amount: playerData.metadata.amount, - avatar: playerData.metadata.position == "DEF" ? `background-image: url(https://sleepercdn.com/images/team_logos/nfl/${playerData.player_id.toLowerCase()}.png)` : `background-image: url(https://sleepercdn.com/content/nfl/players/thumb/${playerData.player_id}.jpg), url(https://sleepercdn.com/images/v2/icons/player_default.webp)`, - } - rosters[playerData.roster_id].push(player); + const data = {player: playerData.player_id, amount: playerData.amount}; + rosters[playerData.roster_id].push(data); } for (const roster in rosters) { const col = draftOrder.indexOf(parseInt(roster)); const sortedRoster = rosters[roster].sort((a,b) => b.amount - a.amount); for(let i = 0; i < sortedRoster.length; i++) { - const player = sortedRoster[i]; - draft[i][col] = {player}; + draft[i][col] = sortedRoster[i]; } } return draft; @@ -214,41 +182,13 @@ export const getPreviousDrafts = async () => { const drafts = []; - let currentManagers; while(curSeason && curSeason != 0) { - const [rosterRes, users, leagueData] = await waitForAll( - getLeagueRosters(curSeason), - getLeagueUsers(curSeason), - getLeagueData(curSeason) - ).catch((err) => { console.error(err); }); + const leagueData = await getLeagueData(curSeason).catch((err) => { console.error(err); }); const draftID = leagueData.draft_id; let year = parseInt(leagueData.season); curSeason = leagueData.previous_league_id; - const rosters = rosterRes.rosters; - - const originalManagers = {}; - - for(const roster of rosters) { - const user = users[roster.owner_id]; - if(user) { - originalManagers[roster.roster_id] = { - avatar: `https://sleepercdn.com/avatars/thumbs/${user.avatar}`, - name: user.metadata.team_name ? user.metadata.team_name : user.display_name, - } - } else { - originalManagers[roster.roster_id] = { - avatar: `https://sleepercdn.com/images/v2/icons/player_default.webp`, - name: 'Unknown Manager', - } - } - } - - if(!currentManagers) { - currentManagers = originalManagers; - } - const [officialDraftRes, picksRes, playersRes] = await waitForAll( fetch(`https://api.sleeper.app/v1/draft/${draftID}`, {compress: true}), fetch(`https://api.sleeper.app/v1/draft/${draftID}/traded_picks`, {compress: true}), @@ -267,7 +207,7 @@ export const getPreviousDrafts = async () => { let draftOrder; - const buildRes = buildConfirmed(officialDraft.slot_to_roster_id, officialDraft.settings.rounds, picks, originalManagers, players, officialDraft.type); + const buildRes = buildConfirmed(officialDraft.slot_to_roster_id, officialDraft.settings.rounds, picks, players, officialDraft.type); draft = buildRes.draft; draftOrder = buildRes.draftOrder; @@ -277,11 +217,6 @@ export const getPreviousDrafts = async () => { draftOrder, draftType: officialDraft.type, reversalRound: officialDraft.settings.reversal_round, - originalManagers - } - - if(originalManagers != currentManagers) { - newDraft.currentManagers = currentManagers; } drafts.push(newDraft); diff --git a/src/lib/utils/helperFunctions/leagueMatchups.js b/src/lib/utils/helperFunctions/leagueMatchups.js index 5376d31d2..2c17220af 100644 --- a/src/lib/utils/helperFunctions/leagueMatchups.js +++ b/src/lib/utils/helperFunctions/leagueMatchups.js @@ -2,7 +2,6 @@ import { getLeagueData } from "./leagueData" import { leagueID } from '$lib/utils/leagueInfo'; import { getNflState } from "./nflState" import { getLeagueRosters } from "./leagueRosters" -import { getLeagueUsers } from "./leagueUsers" import { waitForAll } from './multiPromise'; import { get } from 'svelte/store'; import {matchupsStore} from '$lib/stores'; @@ -12,11 +11,10 @@ export const getLeagueMatchups = async () => { return get(matchupsStore); } - const [nflState, leagueData, rosterRes, users] = await waitForAll( + const [nflState, leagueData, rosterRes] = await waitForAll( getNflState(), getLeagueData(), getLeagueRosters(), - getLeagueUsers() ).catch((err) => { console.error(err); }); let week = 1; @@ -51,7 +49,7 @@ export const getLeagueMatchups = async () => { const matchupWeeks = []; // process all the matchups for(let i = 1; i < matchupsData.length + 1; i++) { - const processed = processMatchups(matchupsData[i - 1], rosters, users, i); + const processed = processMatchups(matchupsData[i - 1], rosters, i); if(processed) { matchupWeeks.push({ matchups: processed.matchups, @@ -72,7 +70,7 @@ export const getLeagueMatchups = async () => { return matchupsResponse; } -const processMatchups = (inputMatchups, rosters, users, week) => { +const processMatchups = (inputMatchups, rosters, week) => { if(!inputMatchups || inputMatchups.length == 0) { return false; } @@ -81,12 +79,8 @@ const processMatchups = (inputMatchups, rosters, users, week) => { if(!matchups[match.matchup_id]) { matchups[match.matchup_id] = []; } - let user = users[rosters[match.roster_id - 1].owner_id]; matchups[match.matchup_id].push({ - manager: { - name: user.metadata.team_name ? user.metadata.team_name : user.display_name, - avatar: `https://sleepercdn.com/avatars/thumbs/${user.avatar}`, - }, + roster_id: match.roster_id, starters: match.starters, points: match.starters_points, }) diff --git a/src/lib/utils/helperFunctions/leagueRecords.js b/src/lib/utils/helperFunctions/leagueRecords.js index 7d8aa01e9..433bed13c 100644 --- a/src/lib/utils/helperFunctions/leagueRecords.js +++ b/src/lib/utils/helperFunctions/leagueRecords.js @@ -1,12 +1,11 @@ import { getLeagueData } from './leagueData'; import { leagueID } from '$lib/utils/leagueInfo'; import { getNflState } from './nflState'; -import { getLeagueRosters } from "./leagueRosters" -import { getLeagueUsers } from "./leagueUsers" +import { getLeagueRosters } from "./leagueRosters"; import { waitForAll } from './multiPromise'; import { get } from 'svelte/store'; -import {records} from '$lib/stores'; -import { round, sortHighAndLow } from './universalFunctions'; +import { records } from '$lib/stores'; +import { getManagers, round, sortHighAndLow } from './universalFunctions'; import { Records } from '$lib/utils/dataClasses'; import { getBrackets } from './leagueBrackets'; import { browser } from '$app/environment'; @@ -14,7 +13,7 @@ import { browser } from '$app/environment'; /** * getLeagueRecords obtains all the record for a league since it was first created * @param {bool} refresh if set to false, getLeagueRecords returns the records stored in localStorage - * @returns {Object} { allTimeBiggestBlowouts, allTimeClosestMatchups, leastSeasonLongPoints, mostSeasonLongPoints, leagueWeekLows, leagueWeekHighs, seasonWeekRecords, leagueRosterRecords, currentManagers, currentYear, lastYear} + * @returns {Object} { allTimeBiggestBlowouts, allTimeClosestMatchups, leastSeasonLongPoints, mostSeasonLongPoints, leagueWeekLows, leagueWeekHighs, seasonWeekRecords, leagueManagerRecords, currentYear, lastYear} */ export const getLeagueRecords = async (refresh = false) => { // records temporarily cached for an individual session @@ -45,12 +44,6 @@ export const getLeagueRecords = async (refresh = false) => { // initiate current season to be your current // league page leagueID let curSeason = leagueID; - - // currentManagers is a placeholder that will eventually - // hold the most recent season's manager info. This is used - // on the front end to tie old team names to the current one, - // so that it's easier to track the continuity of records - let currentManagers; // currentYear will eventually be assigned as the most recent year // that has record information (current season if past week 1, @@ -71,9 +64,8 @@ export const getLeagueRecords = async (refresh = false) => { // loop through each season until the previous_league_id becomes null (or in some cases 0) while(curSeason && curSeason != 0) { - const [rosterRes, users, leagueData] = await waitForAll( + const [rosterRes, leagueData] = await waitForAll( getLeagueRosters(curSeason), - getLeagueUsers(curSeason), getLeagueData(curSeason), ).catch((err) => { console.error(err); }); @@ -87,25 +79,18 @@ export const getLeagueRecords = async (refresh = false) => { // regular season data const { - originalManagers, season, year, - rS - } = await processRegularSeason({leagueData, users, rosters, curSeason, week, regularSeason}) - - regularSeason = rS; // update the regular season record + } = await processRegularSeason({leagueData, rosters, curSeason, week, regularSeason}) // post season data - const pS = await processPlayoffs({year, originalManagers, curSeason, week, playoffRecords}) + const pS = await processPlayoffs({year, curSeason, week, playoffRecords, rosters}) if(pS) { playoffRecords = pS; // update the regular season records } lastYear = year; - if(!currentManagers) { - currentManagers = originalManagers; - } if(!currentYear && year) { currentYear = year; @@ -116,10 +101,9 @@ export const getLeagueRecords = async (refresh = false) => { playoffRecords.currentYear = regularSeason.currentYear; playoffRecords.lastYear = regularSeason.lastYear; - playoffRecords.currentManagers = regularSeason.currentManagers; - regularSeason.finalizeAllTimeRecords({currentManagers, currentYear, lastYear}); - playoffRecords.finalizeAllTimeRecords({currentManagers, currentYear, lastYear}); + regularSeason.finalizeAllTimeRecords({currentYear, lastYear}); + playoffRecords.finalizeAllTimeRecords({currentYear, lastYear}); const regularSeasonData = regularSeason.returnRecords() const playoffData = playoffRecords.returnRecords() @@ -142,13 +126,12 @@ export const getLeagueRecords = async (refresh = false) => { * @param {Object} regularSeasonInfo an object with the function arguments needed to process a regular season * @param {Object[]} regularSeasonInfo.rosters the rosters of the league that year * @param {Object} regularSeasonInfo.leagueData the basic info for the league that season - * @param {Object[]} regularSeasonInfo.users the users in the league that season (the managers) * @param {string} regularSeasonInfo.curSeason the league ID of the current season * @param {int} regularSeasonInfo.week the week to start analyzing (most recently completed week) * @param {Records} regularSeasonInfo.regularSeason the global regularSeason record object - * @returns {Object} { interSeasonEntry, season: (curSeason), year, originalManagers, matchupDifferentials, lRR: (leagueRosterRecords), mSLP: (seasonLongPoints), lWR: (leagueWeekRecords)} + * @returns {Object} { season: (curSeason), year} */ -const processRegularSeason = async ({rosters, leagueData, users, curSeason, week, regularSeason}) => { +const processRegularSeason = async ({rosters, leagueData, curSeason, week, regularSeason}) => { let year = parseInt(leagueData.season); // on first run, week is provided above from nflState, @@ -157,12 +140,8 @@ const processRegularSeason = async ({rosters, leagueData, users, curSeason, week week = leagueData.settings.playoff_week_start - 1; } - let originalManagers = {}; - for(const roster of rosters) { - const {oM, rS} = analyzeRosters({year, roster, users, regularSeason, originalManagers}) - regularSeason = rS; - originalManagers = oM; + analyzeRosters({year, roster, regularSeason}); } // loop through each week of the season @@ -194,14 +173,13 @@ const processRegularSeason = async ({rosters, leagueData, users, curSeason, week // process all the matchups for(const matchupWeek of matchupsData) { - const {sPR, r, mD, sW} = processMatchups({matchupWeek, originalManagers, seasonPointsRecord, record: regularSeason, startWeek, matchupDifferentials, year}) + const {sPR, mD, sW} = processMatchups({matchupWeek, seasonPointsRecord, record: regularSeason, startWeek, matchupDifferentials, year}) seasonPointsRecord = sPR; - regularSeason = r; matchupDifferentials = mD; startWeek = sW; } - // sort season point records + // sort matchup differentials const [biggestBlowouts, closestMatchups] = sortHighAndLow(matchupDifferentials, 'differential') // sort season point records @@ -226,8 +204,6 @@ const processRegularSeason = async ({rosters, leagueData, users, curSeason, week return { season: curSeason, year, - originalManagers, - rS: regularSeason } } @@ -238,33 +214,20 @@ const processRegularSeason = async ({rosters, leagueData, users, curSeason, week * @param {Object} rosterData the roster data to be analyzed * @param {int} rosterData.year the year being analyzed * @param {Object} rosterData.roster the roster being analyzed - * @param {Object[]} rosterData.users all users for that season * @param {Records} rosterData.regularSeason the global regularSeason object that will be updated and returned - * @param {Object} rosterData.originalManagers the originalManagers object for that season - * @returns {Object} {rS: RegularSeason, oM: originalManagers} */ -const analyzeRosters = ({year, roster, users, regularSeason, originalManagers}) => { - const rosterID = roster.roster_id; - const user = users[roster.owner_id]; - - if(user) { - originalManagers[rosterID] = { - avatar: `https://sleepercdn.com/avatars/thumbs/${user.avatar}`, - name: user.metadata.team_name ? user.metadata.team_name : user.display_name, - } - } else { - originalManagers[rosterID] = { - avatar: `https://sleepercdn.com/images/v2/icons/player_default.webp`, - name: 'Unknown Manager', - } - } +const analyzeRosters = ({year, roster, regularSeason}) => { + // team name and logo are tied to the ownerID + const rosterID = roster.roster_id; + + const managers = getManagers(roster); // season hasn't started, no records to obtain - if(roster.settings.wins == 0 && roster.settings.ties == 0 && roster.settings.losses == 0) return {rS: regularSeason, oM: originalManagers}; + if(roster.settings.wins == 0 && roster.settings.ties == 0 && roster.settings.losses == 0) return; // fptsFor and fptsPerGame are used for both rosterRecords and seasonLongPoints const fptsFor = roster.settings.fpts + (roster.settings.fpts_decimal / 100); - const fptsPerGame = round(fptsFor / (roster.settings.wins + roster.settings.losses + roster.settings.ties)) + const fptsPerGame = round(fptsFor / (roster.settings.wins + roster.settings.losses + roster.settings.ties)); const rosterRecords = { wins: roster.settings.wins, @@ -274,12 +237,12 @@ const analyzeRosters = ({year, roster, users, regularSeason, originalManagers}) fptsAgainst: roster.settings.fpts_against + (roster.settings.fpts_against_decimal / 100), fptsPerGame, potentialPoints: roster.settings.ppts + (roster.settings.ppts_decimal / 100), - manager: originalManagers[rosterID], + rosterID, year, } // update the roster records for this roster ID - regularSeason.updateRosterRecord(rosterID, rosterRecords) + regularSeason.updateManagerRecord(managers, rosterRecords); // add season long points entry regularSeason.addSeasonLongPoints({ @@ -287,13 +250,7 @@ const analyzeRosters = ({year, roster, users, regularSeason, originalManagers}) fpts: fptsFor, fptsPerGame, year, - manager: originalManagers[rosterID] - }) - - return { - rS: regularSeason, - oM: originalManagers - } + }); } /** @@ -301,64 +258,63 @@ const analyzeRosters = ({year, roster, users, regularSeason, originalManagers}) * differentials, and adds the points to the season-long points * @param {Object} matchupData the data needed to process a matchup * @param {Object[]} matchupData.matchupWeek the week being analyzed - * @param {Object} matchupData.originalManagers * @param {Object[]} matchupData.seasonPointsRecord - * @param {Records} matchupData.regularSeason + * @param {Records} matchupData.record * @param {int} matchupData.startWeek * @param {Object[]} matchupData.matchupDifferentials * @param {int} matchupData.year * @returns {any} */ -const processMatchups = ({matchupWeek, originalManagers, seasonPointsRecord, record, startWeek, matchupDifferentials, year}) => { +const processMatchups = ({matchupWeek, seasonPointsRecord, record, startWeek, matchupDifferentials, year}) => { let matchups = {}; // only used when building post season record let pSD = {}; for(const matchup of matchupWeek) { - const rosterID = matchup.roster_id; - if(!rosterID) continue; - let mID = matchup.matchup_id; - if(!mID) { - if(!pSD[rosterID]) { - pSD[rosterID] = { - wins: 0, - losses: 0, - ties: 0, - fptsFor: 0, - fptsAgainst: 0, - potentialPoints: 0, - fptspg: 0, - pOGames: 0, - byes: 0, - manager: originalManagers[rosterID] - } - } - pSD[rosterID].pOGames = 1; - const m = matchup.m; - if(!m) { - pSD[rosterID].byes = 1; - continue; - } - mID = `PS:${m}` - } - - const entry = { - manager: originalManagers[rosterID], - fpts: matchup.points, - week: startWeek, - year, - rosterID: rosterID - } - seasonPointsRecord.push(entry); - record.addLeagueWeekRecord(entry); - // add each entry to the matchup object - - if(!matchups[mID]) { - matchups[mID] = []; - } - matchups[mID].push(entry); - + // exit if there's no roster ID + const rosterID = matchup.roster_id; + if(!rosterID) continue; + + let mID = matchup.matchup_id; + + if(!mID) { + if(!pSD[rosterID]) { + pSD[rosterID] = { + wins: 0, + losses: 0, + ties: 0, + fptsFor: 0, + fptsAgainst: 0, + potentialPoints: 0, + fptspg: 0, + pOGames: 0, + byes: 0, + } + } + pSD[rosterID].pOGames = 1; + const m = matchup.m; + if(!m) { + pSD[rosterID].byes = 1; + continue; + } + mID = `PS:${m}` + } + + const entry = { + rosterID, + fpts: matchup.points, + week: startWeek, + year, + } + + // add each entry to the matchup object + if(!matchups[mID]) { + matchups[mID] = []; + } + matchups[mID].push(entry); + record.addLeagueWeekRecord(entry); + seasonPointsRecord.push(entry); } startWeek--; @@ -375,14 +331,12 @@ const processMatchups = ({matchupWeek, originalManagers, seasonPointsRecord, rec year: home.year, week: home.week, home: { - manager: home.manager, - fpts: home.fpts, rosterID: home.rosterID, + fpts: home.fpts, }, away: { - manager: away.manager, - fpts: away.fpts, rosterID: away.rosterID, + fpts: away.fpts, }, differential: home.fpts - away.fpts } @@ -390,25 +344,25 @@ const processMatchups = ({matchupWeek, originalManagers, seasonPointsRecord, rec // handle post-season data if(matchupKey.split(":")[0] == "PS") { - pSD[home.rosterID].wins = 1; - pSD[home.rosterID].fptsFor = home.fpts; - pSD[home.rosterID].fptsAgainst = away.fpts; - pSD[away.rosterID].losses = 1; - pSD[away.rosterID].fptsFor = away.fpts; - pSD[away.rosterID].fptsAgainst = home.fpts; - }; + pSD[home.rosterID].wins = 1; + pSD[home.rosterID].fptsFor = home.fpts; + pSD[home.rosterID].fptsAgainst = away.fpts; + + pSD[away.rosterID].losses = 1; + pSD[away.rosterID].fptsFor = away.fpts; + pSD[away.rosterID].fptsAgainst = home.fpts; + } } return { sPR: seasonPointsRecord, - r: record, mD: matchupDifferentials, sW: startWeek, pSD } } -const processPlayoffs = async ({originalManagers, curSeason, playoffRecords, year, week}) => { +const processPlayoffs = async ({curSeason, playoffRecords, year, week, rosters}) => { const { playoffsStart, playoffRounds, @@ -424,7 +378,7 @@ const processPlayoffs = async ({originalManagers, curSeason, playoffRecords, yea let postSeasonData = {}; // process all the championship matches - const champBracket = digestBracket({bracket: champs.bracket, playoffsStart, matchupDifferentials, postSeasonData, playoffRecords, playoffRounds, consolation: false, seasonPointsRecord, originalManagers, year}); + const champBracket = digestBracket({bracket: champs.bracket, playoffsStart, matchupDifferentials, postSeasonData, playoffRecords, playoffRounds, consolation: false, seasonPointsRecord, year}); postSeasonData = champBracket.postSeasonData; seasonPointsRecord = champBracket.seasonPointsRecord; @@ -432,7 +386,7 @@ const processPlayoffs = async ({originalManagers, curSeason, playoffRecords, yea matchupDifferentials = champBracket.matchupDifferentials; // process all the consolation matches - const consolationBracket = digestBracket({bracket: champs.consolations, playoffsStart, matchupDifferentials, postSeasonData, playoffRecords, playoffRounds, consolation: true, seasonPointsRecord, originalManagers, year}); + const consolationBracket = digestBracket({bracket: champs.consolations, playoffsStart, matchupDifferentials, postSeasonData, playoffRecords, playoffRounds, consolation: true, seasonPointsRecord, year}); postSeasonData = consolationBracket.postSeasonData; seasonPointsRecord = consolationBracket.seasonPointsRecord; @@ -440,31 +394,31 @@ const processPlayoffs = async ({originalManagers, curSeason, playoffRecords, yea matchupDifferentials = consolationBracket.matchupDifferentials; for(const rosterID in postSeasonData) { - // update the roster records for this roster ID const pSD = postSeasonData[rosterID]; const fptsPerGame = round(pSD.fptsFor / (pSD.wins + pSD.losses + pSD.ties)); pSD.fptsPerGame = fptsPerGame; pSD.year = year; - playoffRecords.updateRosterRecord(rosterID, pSD); - // add season long points entry playoffRecords.addSeasonLongPoints({ - rosterID, fpts: pSD.fptsFor, fptsPerGame, year, - manager: originalManagers[rosterID] + rosterID: rosterID, }) + + // update the manager records for this roster ID + const managers = getManagers(rosters[rosterID - 1]); + playoffRecords.updateManagerRecord(managers, pSD); } - // sort season point records + // sort matchup differentials const [biggestBlowouts, closestMatchups] = sortHighAndLow(matchupDifferentials, 'differential') // sort season point records const [seasonPointsHighs, seasonPointsLows] = sortHighAndLow(seasonPointsRecord, 'fpts') - // add matchupDifferentials to tha all time records + // add matchupDifferentials to the all time records playoffRecords.addAllTimeMatchupDifferentials(matchupDifferentials); @@ -481,7 +435,7 @@ const processPlayoffs = async ({originalManagers, curSeason, playoffRecords, yea return playoffRecords; } -const digestBracket = ({bracket, playoffRecords, playoffRounds, matchupDifferentials, postSeasonData, consolation, seasonPointsRecord, playoffsStart, originalManagers, year}) => { +const digestBracket = ({bracket, playoffRecords, playoffRounds, matchupDifferentials, postSeasonData, consolation, seasonPointsRecord, playoffsStart, year}) => { for(let i = 0; i < bracket.length; i++) { const startWeek = getStartWeek(i + (playoffRounds - bracket.length), playoffRounds, consolation, playoffsStart); const matchupWeek = []; @@ -489,7 +443,7 @@ const digestBracket = ({bracket, playoffRecords, playoffRounds, matchupDifferent for(let matchups of bracket[i]) { if(consolation) { // consolation matchups are nested within an additional array, we need to flatten them before proceeding - matchups = matchups.flat(); + matchups.flat(); } for(const matchup of matchups) { if(matchup.r) { @@ -503,13 +457,12 @@ const digestBracket = ({bracket, playoffRecords, playoffRounds, matchupDifferent } } } - const {sPR, r, mD, pSD} = processMatchups({matchupWeek, originalManagers, seasonPointsRecord, record: playoffRecords, startWeek, matchupDifferentials, year}) + const {sPR, mD, pSD} = processMatchups({matchupWeek, seasonPointsRecord, record: playoffRecords, startWeek, matchupDifferentials, year}) postSeasonData = meshPostSeasonData(postSeasonData, pSD); postSeasonData = meshPostSeasonData(postSeasonData, pSD); seasonPointsRecord = sPR; - playoffRecords = r; matchupDifferentials = mD; } diff --git a/src/lib/utils/helperFunctions/leagueTeamManagers.js b/src/lib/utils/helperFunctions/leagueTeamManagers.js new file mode 100644 index 000000000..d6201bd16 --- /dev/null +++ b/src/lib/utils/helperFunctions/leagueTeamManagers.js @@ -0,0 +1,69 @@ +import { leagueID } from '$lib/utils/leagueInfo'; +import { get } from 'svelte/store'; +import { teamManagersStore } from '$lib/stores'; +import { waitForAll } from './multiPromise'; +import { getManagers, getTeamData } from './universalFunctions'; +import { getLeagueData } from './leagueData'; + +export const getLeagueTeamManagers = async () => { + if(get(teamManagersStore) && get(teamManagersStore).currentSeason) { + return get(teamManagersStore); + } + let currentLeagueID = leagueID; + let teamManagersMap = {}; + let finalUsers = {}; + let currentSeason = null; + + // loop through all seasons and create a [year][roster_id]: team, managers object + while(currentLeagueID && currentLeagueID != 0) { + const [usersRaw, leagueData, rostersRaw] = await waitForAll( + fetch(`https://api.sleeper.app/v1/league/${currentLeagueID}/users`, {compress: true}), + getLeagueData(currentLeagueID), + fetch(`https://api.sleeper.app/v1/league/${currentLeagueID}/rosters`, {compress: true}), + ).catch((err) => { console.error(err); }); + + const [users, rosters] = await waitForAll( + usersRaw.json(), + rostersRaw.json(), + ).catch((err) => { console.error(err); }); + + const year = parseInt(leagueData.season); + if(!teamManagersMap.currentSeason) { + teamManagersMap.currentSeason = year; + } + + currentLeagueID = leagueData.previous_league_id; + if(!currentSeason) { + currentSeason = leagueData.season; + } + teamManagersMap[year] = {}; + const processedUsers = processUsers(users); + + // in order to not overwrite most recent data, only add new entries to finalUsers + for(const processedUserKey in processedUsers) { + if(finalUsers[processedUserKey]) continue; + finalUsers[processedUserKey] = processedUsers[processedUserKey]; + } + for(const roster of rosters) { + teamManagersMap[year][roster.roster_id] = { + team: getTeamData(processedUsers, roster.owner_id), + managers: getManagers(roster, processedUsers), + }; + } + } + const response = { + currentSeason, + teamManagersMap, + users: finalUsers, + } + teamManagersStore.update(() => response); + return response; +} + +const processUsers = (rawUsers) => { + let finalUsers = {}; + for(const user of rawUsers) { + finalUsers[user.user_id] = user; + } + return finalUsers; +} diff --git a/src/lib/utils/helperFunctions/leagueTransactions.js b/src/lib/utils/helperFunctions/leagueTransactions.js index ec3c1c1b3..244c8f0b5 100644 --- a/src/lib/utils/helperFunctions/leagueTransactions.js +++ b/src/lib/utils/helperFunctions/leagueTransactions.js @@ -1,20 +1,18 @@ import { getLeagueData } from './leagueData'; import { leagueID } from '$lib/utils/leagueInfo'; import { getNflState } from './nflState'; -import { getLeagueRosters } from './leagueRosters'; -import { getLeagueUsers } from './leagueUsers'; import { waitForAll } from './multiPromise'; import { get } from 'svelte/store'; import {transactionsStore} from '$lib/stores'; import { browser } from '$app/environment'; +import { getLeagueTeamManagers } from './leagueTeamManagers'; export const getLeagueTransactions = async (preview, refresh = false) => { const transactionsStoreVal = get(transactionsStore); - if(transactionsStoreVal.transactions) { + if(transactionsStoreVal.totals) { return { transactions: checkPreview(preview, transactionsStoreVal.transactions), - currentManagers: transactionsStoreVal.currentManagers, totals: transactionsStoreVal.totals, stale: false }; @@ -39,13 +37,12 @@ export const getLeagueTransactions = async (preview, refresh = false) => { week = nflState.week; } - const {transactionsData, prevManagers, currentManagers, currentSeason} = await combThroughTransactions(week, leagueID).catch((err) => { console.error(err); }); + const {transactionsData, currentSeason} = await combThroughTransactions(week, leagueID).catch((err) => { console.error(err); }); - const { transactions, totals } = digestTransactions(transactionsData, prevManagers, currentSeason, Object.keys(currentManagers).length); + const { transactions, totals } = await digestTransactions({transactionsData, currentSeason}); const transactionPackage = { transactions, - currentManagers, totals }; @@ -59,7 +56,6 @@ export const getLeagueTransactions = async (preview, refresh = false) => { return { transactions: checkPreview(preview, transactions), - currentManagers, totals, stale: false }; @@ -93,49 +89,18 @@ const combThroughTransactions = async (week, currentLeagueID) => { week = week > 0 ? week : 1; const leagueIDs = []; - const prevManagers = {}; - let currentManagers = null; let currentSeason = null; while(currentLeagueID && currentLeagueID != 0) { // gather supporting info simultaneously - const [leagueData, rosterRes, users] = await waitForAll( - getLeagueData(currentLeagueID), - getLeagueRosters(currentLeagueID), - getLeagueUsers(currentLeagueID), - ).catch((err) => { console.error(err); }); + const leagueData = await getLeagueData(currentLeagueID).catch((err) => { console.error(err); }); leagueIDs.push(currentLeagueID); - const rosters = rosterRes.rosters; - - const managers = {}; - - for(const roster of rosters) { - const user = users[roster.owner_id]; - if(user) { - managers[roster.roster_id] = { - avatar: `https://sleepercdn.com/avatars/thumbs/${user.avatar}`, - name: user.metadata.team_name ? user.metadata.team_name : user.display_name, - } - } else { - managers[roster.roster_id] = { - avatar: `https://sleepercdn.com/images/v2/icons/player_default.webp`, - name: 'Unknown Manager', - } - } - } - - if(!currentManagers) { - currentManagers = managers; - } - if(!currentSeason) { currentSeason = leagueData.season; } - prevManagers[leagueData.season] = managers; - currentLeagueID = leagueData.previous_league_id; } @@ -168,69 +133,72 @@ const combThroughTransactions = async (week, currentLeagueID) => { transactionsData = transactionsData.concat(transactionDataJson); } - return {transactionsData, prevManagers, currentManagers, currentSeason}; + return {transactionsData, currentSeason}; } -const digestTransactions = (transactionsData, prevManagers, currentSeason, numRosters) => { +const digestTransactions = async ({transactionsData, currentSeason}) => { const transactions = []; const totals = { allTime: {}, seasons: {} }; - for(let i = 1; i <= numRosters; i++) { - totals.allTime[i] = { - trade: 0, - waiver: 0 - }; - } + const leagueTeamManagers = await getLeagueTeamManagers(); // trades can be out of order because they are aded to sleeper when the offer is sent // this sort puts everything in the correct order const transactionOrder = transactionsData.sort((a,b) => b.status_updated - a.status_updated); for(const transaction of transactionOrder) { - const {digestedTransaction, season, success} = digestTransaction(transaction, prevManagers, currentSeason) + let {digestedTransaction, season, success} = digestTransaction({transaction, currentSeason}) if(!success) continue; transactions.push(digestedTransaction); + if(!leagueTeamManagers.teamManagersMap[season]) { + season--; + } for(const roster of digestedTransaction.rosters) { const type = digestedTransaction.type; - // add to league long totals - totals.allTime[roster][type]++; - - // add to season long totals - if(prevManagers[season]) { - if(!totals.seasons[season]) { - totals.seasons[season] = {}; - for(let i = 1; i <= Object.keys(prevManagers[season]).length; i++) { - totals.seasons[season][i] = { - trade: 0, - waiver: 0, - manager: prevManagers[season][i] - }; - } - } - totals.seasons[season][roster][type]++; - } + for(const manager of leagueTeamManagers.teamManagersMap[season][roster].managers) { + // add to league long totals for each manager involved with the transaction + if(!totals.allTime[manager]) { + totals.allTime[manager] = { + trade: 0, + waiver: 0 + }; + } + totals.allTime[manager][type]++; + } + + // add to season long totals for each manager + if(!totals.seasons[season]) { + totals.seasons[season] = {}; + } + if(!totals.seasons[season][roster]) { + totals.seasons[season][roster] = { + trade: 0, + waiver: 0, + rosterID: roster, + }; + } + totals.seasons[season][roster][type]++; } } - return {transactions, totals}; } const digestDate = (tStamp) => { - var a = new Date(tStamp); - var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; - var year = a.getFullYear(); - var month = months[a.getMonth()]; - var date = a.getDate(); - var hour = a.getHours(); - var min = a.getMinutes(); + const a = new Date(tStamp); + const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + const year = a.getFullYear(); + const month = months[a.getMonth()]; + const date = a.getDate(); + const hour = a.getHours(); + const min = a.getMinutes(); return month + ' ' + date + ' ' + year + ', ' + (hour % 12 == 0 ? 12 : hour % 12) + ':' + min + (hour / 12 >= 1 ? "PM" : "AM"); } -const digestTransaction = (transaction, prevManagers, currentSeason) => { +const digestTransaction = ({transaction, currentSeason}) => { // don't include failed waiver claims if(transaction.status == 'failed') return {success: false}; const handled = []; @@ -243,6 +211,7 @@ const digestTransaction = (transaction, prevManagers, currentSeason) => { let digestedTransaction = { id: transaction.transaction_id, date, + season, type: "waiver", rosters: transactionRosters, moves : [] @@ -252,11 +221,8 @@ const digestTransaction = (transaction, prevManagers, currentSeason) => { digestedTransaction.type = "trade"; } - if(season != currentSeason && prevManagers[season]) { - digestedTransaction.previousOwners = []; - for(const roster of transactionRosters) { - digestedTransaction.previousOwners.push(prevManagers[season][roster]); - } + if(season != currentSeason) { + digestedTransaction.previousOwners = true; } const adds = transaction.adds; @@ -302,11 +268,7 @@ const digestTransaction = (transaction, prevManagers, currentSeason) => { } if(pick.roster_id != pick.previous_owner_id) { - const original_owner = { - original: season != currentSeason ? prevManagers[season][pick.roster_id].name : null, - current: pick.roster_id - } - move[transactionRosters.indexOf(pick.previous_owner_id)].pick.original_owner = original_owner; + move[transactionRosters.indexOf(pick.previous_owner_id)].pick.original_owner = pick.roster_id; } move[transactionRosters.indexOf(pick.owner_id)] = "destination"; diff --git a/src/lib/utils/helperFunctions/leagueUsers.js b/src/lib/utils/helperFunctions/leagueUsers.js deleted file mode 100644 index 13e0ce979..000000000 --- a/src/lib/utils/helperFunctions/leagueUsers.js +++ /dev/null @@ -1,27 +0,0 @@ -import { leagueID } from '$lib/utils/leagueInfo'; -import { get } from 'svelte/store'; -import {users} from '$lib/stores'; - -export const getLeagueUsers = async (queryLeagueID = leagueID) => { - if(get(users)[queryLeagueID]) { - return get(users)[queryLeagueID]; - } - const res = await fetch(`https://api.sleeper.app/v1/league/${queryLeagueID}/users`, {compress: true}).catch((err) => { console.error(err); }); - const data = await res.json().catch((err) => { console.error(err); }); - - if (res.ok) { - const usersData = processUsers(data); - users.update(u => {u[queryLeagueID] = usersData; return u}); - return usersData; - } else { - throw new Error(data); - } -} - -const processUsers = (rawUsers) => { - let finalUsers = {}; - for(const user of rawUsers) { - finalUsers[user.user_id] = user; - } - return finalUsers; -} \ No newline at end of file diff --git a/src/lib/utils/helperFunctions/universalFunctions.js b/src/lib/utils/helperFunctions/universalFunctions.js index e91e5a50d..4c448759f 100644 --- a/src/lib/utils/helperFunctions/universalFunctions.js +++ b/src/lib/utils/helperFunctions/universalFunctions.js @@ -1,4 +1,4 @@ -import { managers } from '$lib/utils/leagueInfo'; +import { managers as managersObj } from '$lib/utils/leagueInfo'; import { goto } from "$app/navigation"; import { stringDate } from './news'; @@ -13,52 +13,90 @@ export const round = (num) => { return (Math.round((num + Number.EPSILON) * 100) / 100).toFixed(2); } -const min = (stats, roundOverride) => { - const num = Math.min(...stats) - return Math.floor(num / roundOverride) * roundOverride; +const min = (stats, roundOverride, max) => { + const num = Math.min(...stats); + let minAnswer = Math.floor(num / roundOverride) * roundOverride; + if(max && num > 0) { + let i = 0; + while(minAnswer > 0 && (num - minAnswer) / (max - minAnswer) < .15) { + minAnswer -= roundOverride; + i++; + // prevent infinite loop, emergency exit + if(i > 100) { + break; + } + } + } + return minAnswer > 0 ? minAnswer : 0; } const max = (stats, roundOverride) => { - const num = Math.max(...stats) + const num = Math.max(...stats); return Math.ceil(num / roundOverride) * roundOverride; } -export const gotoManager = (rosterID) => { - if(!managers.length) return; - const managersIndex = managers.findIndex(m => m.roster == rosterID); +export const gotoManager = ({leagueTeamManagers, managerID, rosterID}) => { + if(!managersObj.length) return; + let managersIndex = -1; + + if(managerID) { + // modern approach + managersIndex = managersObj.findIndex(m => m.managerID == managerID); + + // support for league pages still using deprecated roster field + if(managersIndex) { + for(const rID in leagueTeamManagers.teamManagersMap[leagueTeamManagers.currentSeason]) { + for(const mID of leagueTeamManagers.teamManagersMap[leagueTeamManagers.currentSeason][rID].managers) { + if(mID == managerID) { + managersIndex = managersObj.findIndex(m => m.roster == rID); + goto(`/manager?manager=${managersIndex}`); + return; + } + } + } + } + } else if(rosterID) { + // check for matching managerID first + for(const mID of leagueTeamManagers.teamManagersMap[leagueTeamManagers.currentSeason][rosterID].managers) { + managersIndex = managersObj.findIndex(m => m.managerID == mID); + if(managersIndex > -1) { + goto(`/manager?manager=${managersIndex}`); + return; + } + } + + // support for league pages still using deprecated roster field + managersIndex = managersObj.findIndex(m => m.roster == rosterID); + } + // if no manager exists for that roster, -1 will take you to the main managers page goto(`/manager?manager=${managersIndex}`); } -export const getAuthor = (rosters, users, author) => { - let user = null; - for(const userKey of Object.keys(users)) { - if(users[userKey].display_name.toLowerCase() == author.toLowerCase()) { - user = users[userKey]; - break; +export const getAuthor = (leagueTeamManagers, author) => { + for(const yearKey in leagueTeamManagers.teamManagersMap) { + for(const rosterKey in leagueTeamManagers.teamManagersMap[yearKey]) { + for(const manager of leagueTeamManagers.teamManagersMap[yearKey][rosterKey].managers) { + if(leagueTeamManagers.users[manager].display_name.toLowerCase() == author.toLowerCase()) { + return `${leagueTeamManagers.users[manager].display_name}`; + } + } } } - if(!user) { - return author; - } - const userID = user.user_id; - const roster = rosters.find(r => r.owner_id == userID || (r.co_owners && r.co_owners.indexOf(userID) > -1)); - return `${user.metadata.team_name ? user.metadata.team_name : user.display_name}`; + return author; } -export const getAvatar = (users, author) => { - let user = null; - for(const userKey of Object.keys(users)) { - if(users[userKey].display_name.toLowerCase() == author.toLowerCase()) { - user = users[userKey]; - break; +export const getAvatar = (leagueTeamManagers, author) => { + for(const yearKey in leagueTeamManagers.teamManagersMap) { + for(const rosterKey in leagueTeamManagers.teamManagersMap[yearKey]) { + for(const manager of leagueTeamManagers.teamManagersMap[yearKey][rosterKey].managers) { + if(leagueTeamManagers.users[manager].display_name.toLowerCase() == author.toLowerCase()) { + return getTeamFromTeamManagers(leagueTeamManagers, rosterKey).avatar; + } + } } } - if(!user) { - return 'managers/question.jpg'; - } - - return `https://sleepercdn.com/avatars/thumbs/${user.avatar}`; + return 'managers/question.jpg'; } export const parseDate = (rawDate) => { @@ -67,40 +105,46 @@ export const parseDate = (rawDate) => { return stringDate(d); } -export const generateGraph = ({stats, x, y, stat, header, field, short, secondField = null}, roundOverride = 10, yMinOverride = null) => { +export const generateGraph = ({stats, x, stat, header, field, short, secondField = null}, year, roundOverride = 10, xMinOverride = null) => { if(!stats) { return null; } const graph = { stats: [], secondStats: [], - managers: [], + managerIDs: [], rosterIDs: [], - labels: {x, y, stat}, + labels: {x, stat}, header, - yMin: 0, - yMax: 0, - short + xMin: 0, + xMax: 0, + short, + year } - const sortedStats = [...stats].sort((a, b) => a.rosterID - b.rosterID); + const sortedStats = [...stats].sort((a, b) => b[field] - a[field]); for(const indivStat of sortedStats) { - graph.stats.push(Math.round(indivStat[field])); + graph.stats.push(indivStat[field]); if(secondField) { - graph.secondStats.push(Math.round(indivStat[secondField])); + graph.secondStats.push(indivStat[secondField]); + } + if(indivStat.managerID) { + graph.managerIDs.push(indivStat.managerID); + graph.rosterIDs.push(null); + } else if(indivStat.rosterID) { + graph.managerIDs.push(null); + graph.rosterIDs.push(indivStat.rosterID); } - graph.managers.push(indivStat.manager); - graph.rosterIDs.push(indivStat.rosterID) } - graph.yMax = max(graph.stats, roundOverride); - graph.yMin = min(graph.stats, roundOverride); + graph.xMax = max(graph.stats, roundOverride); + graph.xMin = min(graph.stats, roundOverride, graph.xMax); if(secondField) { - graph.yMin = min(graph.secondStats, roundOverride); + graph.xMin = min(graph.secondStats, roundOverride, graph.xMax); } - if(yMinOverride) { - graph.yMin = yMinOverride; + if(xMinOverride) { + graph.xMin = xMinOverride; } return graph; @@ -119,4 +163,128 @@ export const generateGraph = ({stats, x, y, stat, header, field, short, secondFi const high = sorted.slice(0, 10); const low = sorted.slice(-10).reverse(); return [high, low] +} + +/** + * get all managers of a roster + * @param {Object} roster an object with all data for a roster + * @returns {Object[]} [managerIDs...] an array of manager IDs + */ +export const getManagers = (roster) => { + const managers = []; + if(roster.owner_id) { + managers.push(roster.owner_id); + } + if(roster.co_owners) { + for(const coOwner of roster.co_owners) { + managers.push(coOwner); + } + } + return managers; +} + +/** + * takes in a map of users and a owner ID and returns an object with a user's avatar and name + * @param {Object} users the map of users + * @param {string} ownerID the ID of the owner + * @returns {Object} {avatar, name} an object containing a user's avatar image url and their name + */ +export const getTeamData = (users, ownerID) => { + const user = users[ownerID]; + if(user) { + return { + avatar: user.metadata?.avatar ? user.metadata.avatar : `https://sleepercdn.com/avatars/thumbs/${user.avatar}`, + name: user.metadata.team_name ? user.metadata.team_name : user.display_name, + } + } + return { + avatar: `https://sleepercdn.com/images/v2/icons/player_default.webp`, + name: 'Unknown Team', + } +} + +export const getAvatarFromTeamManagers = (teamManagers, rosterID, year) => { + if(!year || year > teamManagers.currentSeason) { + year = teamManagers.currentSeason; + } + return teamManagers.teamManagersMap[year][rosterID].team.avatar; +} + +export const getTeamNameFromTeamManagers = (teamManagers, rosterID, year) => { + if(!year || year > teamManagers.currentSeason) { + year = teamManagers.currentSeason; + } + return teamManagers.teamManagersMap[year][rosterID].team.name; +} + +export const renderManagerNames = (teamManagers, rosterID, year) => { + if(!year || year > teamManagers.currentSeason) { + year = teamManagers.currentSeason; + } + let managersString = ""; + for(const managerID of teamManagers.teamManagersMap[year][rosterID].managers) { + const manager = teamManagers.users[managerID]; + if(manager) { + if(managersString != "") { + managersString += ", " + } + managersString += manager.display_name; + } + } + return managersString; +} + +export const getTeamFromTeamManagers = (teamManagers, rosterID, year) => { + if(!year || year > teamManagers.currentSeason) { + year = teamManagers.currentSeason; + } + return teamManagers.teamManagersMap[year][rosterID]['team']; +} + +export const getNestedTeamNamesFromTeamManagers = (teamManagers, year, rosterID) => { + const originalName = teamManagers.teamManagersMap[year][rosterID]['team']['name']; + const currentName = teamManagers.teamManagersMap[teamManagers.currentSeason][rosterID]['team']['name']; + if(cleanName(originalName) != cleanName(currentName)) { + return `${originalName}
(${currentName})
`; + } + return originalName; +} + +export const getDatesActive = (teamManagers, managerID) => { + if(!managerID) return; + let datesActive = {start: null, end: null}; + for(const year in teamManagers.teamManagersMap) { + for(const rosterID in teamManagers.teamManagersMap[year]) { + if(teamManagers.teamManagersMap[year][rosterID].managers.indexOf(managerID) > -1) { + if(!datesActive.start || datesActive.start > year) { + datesActive.start = year; + } + if(!datesActive.end || datesActive.end < year) { + datesActive.end = year; + } + break; + } + } + } + if(datesActive.end == teamManagers.currentSeason) { + datesActive.end = null; + } + return datesActive; +} + +export const getRosterIDFromManagerID = (teamManagers, managerID) => { + if(!managerID) return null; + for(const year in teamManagers.teamManagersMap) { + for(const rosterID in teamManagers.teamManagersMap[year]) { + if(teamManagers.teamManagersMap[year][rosterID].managers.indexOf(managerID) > -1) { + return {rosterID, year}; + } + } + } + return null; +} + +export const checkIfManagerReceivedAward = (teamManagers, awardRosterID, year, managerID) => { + if(!managerID) return false; + return teamManagers.teamManagersMap[year][awardRosterID].managers.indexOf(managerID) > -1; } \ No newline at end of file diff --git a/src/lib/utils/leagueInfo.js b/src/lib/utils/leagueInfo.js index 40cfa35d0..f47c13e06 100644 --- a/src/lib/utils/leagueInfo.js +++ b/src/lib/utils/leagueInfo.js @@ -102,9 +102,10 @@ export const managers = [ */ // { - // "roster": 3, // ID of the roster that the manager manages (look at the order of the power rankings graph) + // "roster": 3, // (DEPRECATED! Don't use this anymore) ID of the roster that the manager manages (look at the order of the power rankings graph) + // "managerID": "12345678", // the user's manager ID, go to https://api.sleeper.app/v1/league//users to find user IDs (you can use older leagueIDs to find user IDs for managers that are no longer in the league) // "name": "Your Name", - // "tookOver": 2020, // (optional) used if a manager took over a team, delete this line or change to null otherwise + // "tookOver": 2020, // (DEPRECATED! You don't need to use this anymore) (optional) used if a manager took over a team, delete this line or change to null otherwise // "location": "Brooklyn", // (optional) // "bio": "Lorem ipsum...", // "photo": "/managers/name.jpg", // square ratio recommended (no larger than 500x500) @@ -122,4 +123,5 @@ export const managers = [ // "philosophy": "Your fantasy team's philosophy", // (optional) // "tradingScale": 10, // 1 - 10 (optional) // "preferredContact": "Text", // (optional) 'Text', 'WhatsApp', 'Sleeper', 'Email', 'Phone', 'Discord', and 'Carrier Pigeon' are currently supplied in the template - // }, \ No newline at end of file + // }, + \ No newline at end of file diff --git a/src/lib/version.js b/src/lib/version.js index 9bcb91cd8..507ae2d1e 100644 --- a/src/lib/version.js +++ b/src/lib/version.js @@ -5,4 +5,4 @@ available for your copy of League Page */ // Keep in sync with package.json -export const version = "2.0.2"; +export const version = "2.1.0"; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 9d6b1103c..df66bd690 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,18 +1,12 @@
- {#await awardsData } + {#await waitForAll(awardsData, teamManagersData) }

Retrieving awards data...

- {:then {podiums, currentManagers} } + {:then [podiums, leagueTeamManagers] } {#each podiums as podium} - + {:else}

No seasons have been completed yet, so no awards have been earned...

{/each} diff --git a/src/routes/blog/+page.js b/src/routes/blog/+page.js index c53d45c75..35cc9affd 100644 --- a/src/routes/blog/+page.js +++ b/src/routes/blog/+page.js @@ -1,5 +1,5 @@ -import { enableBlog, getBlogPosts, getLeagueRosters, getLeagueUsers } from '$lib/utils/helper'; +import { enableBlog, getBlogPosts, getLeagueRosters, getLeagueTeamManagers } from '$lib/utils/helper'; export function load({ url, fetch }) { if(!enableBlog) return false; @@ -7,14 +7,14 @@ export function load({ url, fetch }) { const queryPage = url?.searchParams?.get('page') || 1; const filterKey = url?.searchParams?.get('filter') || ''; const postsData = getBlogPosts(fetch); - const usersData = getLeagueUsers(); + const leagueTeamManagersData = getLeagueTeamManagers(); const rostersData = getLeagueRosters(); return { queryPage, postsData, filterKey, - usersData, + leagueTeamManagersData, rostersData, }; } \ No newline at end of file diff --git a/src/routes/blog/+page.svelte b/src/routes/blog/+page.svelte index 5a7638ea6..72579db2a 100644 --- a/src/routes/blog/+page.svelte +++ b/src/routes/blog/+page.svelte @@ -2,7 +2,7 @@ import { Posts } from "$lib/components"; export let data; - const {postsData, queryPage, filterKey, usersData, rostersData} = data; + const {postsData, queryPage, filterKey, leagueTeamManagersData, rostersData} = data;
- +
\ No newline at end of file diff --git a/src/routes/drafts/+page.js b/src/routes/drafts/+page.js index 28d94a303..5d0c359b4 100644 --- a/src/routes/drafts/+page.js +++ b/src/routes/drafts/+page.js @@ -1,11 +1,15 @@ -import { getUpcomingDraft, getPreviousDrafts } from '$lib/utils/helper'; +import { getUpcomingDraft, getPreviousDrafts, getLeagueTeamManagers, loadPlayers } from '$lib/utils/helper'; -export async function load() { +export async function load({ fetch }) { const upcomingDraftData = getUpcomingDraft(); const previousDraftsData = getPreviousDrafts(); + const leagueTeamManagersData = getLeagueTeamManagers(); + const playersData = loadPlayers(fetch); return { upcomingDraftData, - previousDraftsData + previousDraftsData, + leagueTeamManagersData, + playersData, }; } \ No newline at end of file diff --git a/src/routes/drafts/+page.svelte b/src/routes/drafts/+page.svelte index 308f9e3d1..b715bd5c5 100644 --- a/src/routes/drafts/+page.svelte +++ b/src/routes/drafts/+page.svelte @@ -2,7 +2,7 @@ import { Drafts } from '$lib/components'; export let data; - const {upcomingDraftData, previousDraftsData} = data; + const {upcomingDraftData, previousDraftsData, leagueTeamManagersData, playersData} = data;
- +
\ No newline at end of file diff --git a/src/routes/manager/+page.js b/src/routes/manager/+page.js index 642c86767..70a71dbee 100644 --- a/src/routes/manager/+page.js +++ b/src/routes/manager/+page.js @@ -1,7 +1,7 @@ import { waitForAll, getLeagueRosters, - getLeagueUsers, + getLeagueTeamManagers, getLeagueData, getLeagueTransactions, getAwards, @@ -12,7 +12,7 @@ export async function load({ url }) { if(!managersObj.length) return false; const managersInfo = waitForAll( getLeagueRosters(), - getLeagueUsers(), + getLeagueTeamManagers(), getLeagueData(), getLeagueTransactions(), getAwards(), @@ -22,13 +22,10 @@ export async function load({ url }) { const manager = url?.searchParams?.get('manager'); const props = { - manager: null, + manager: manager && manager < managersObj.length ? manager : -1, managers: managersObj, managersInfo } - if(manager && (manager >= 0 && manager < managersObj.length)) { - props.manager = manager; - } return props; } \ No newline at end of file diff --git a/src/routes/manager/+page.svelte b/src/routes/manager/+page.svelte index 91efd7236..a71a61d70 100644 --- a/src/routes/manager/+page.svelte +++ b/src/routes/manager/+page.svelte @@ -1,9 +1,16 @@
- {#await managersInfo} - -
-

Retrieving managers...

- -
- {:then [rostersData, users, leagueData, transactionsData, awards, records]} - - {:catch error} - -

Something went wrong: {error.message}

- {/await} + {#await managersInfo} + +
+

Retrieving managers...

+ +
+ {:then [rostersData, leagueTeamManagers, leagueData, transactionsData, awards, records]} + {#if managers.length && manager > -1} + + {/if} + {:catch error} + +

Something went wrong: {error.message}

+ {/await}
\ No newline at end of file diff --git a/src/routes/managers/+page.js b/src/routes/managers/+page.js index 778fc9661..004f7b610 100644 --- a/src/routes/managers/+page.js +++ b/src/routes/managers/+page.js @@ -1,25 +1,15 @@ import { - waitForAll, - getLeagueRosters, - getLeagueUsers, - managers as managersObj + getLeagueTeamManagers, + managers, } from '$lib/utils/helper'; -export async function load({ url }) { - if(!managersObj.length) return false; - const managersInfo = waitForAll( - getLeagueRosters(), - getLeagueUsers(), - ); - const manager = url?.searchParams?.get('manager'); +export async function load() { + if(!managers.length) return {managers}; + const leagueTeamManagersData = getLeagueTeamManagers(); const props = { - manager: null, - managers: managersObj, - managersInfo - } - if(manager && (manager >= 0 && manager < managersObj.length)) { - props.manager = manager; + managers, + leagueTeamManagersData } return props; diff --git a/src/routes/managers/+page.svelte b/src/routes/managers/+page.svelte index ea611fa15..7dcf9efd6 100644 --- a/src/routes/managers/+page.svelte +++ b/src/routes/managers/+page.svelte @@ -1,9 +1,17 @@
- {#await managersInfo} + {#await leagueTeamManagersData}

Retrieving managers...

- {:then [rostersData, users,]} - + {:then leagueTeamManagers} + {#if managers.length} + + {/if} {:catch error}

Something went wrong: {error.message}

diff --git a/src/routes/matchups/+page.js b/src/routes/matchups/+page.js index c4327647b..2a05e64fe 100644 --- a/src/routes/matchups/+page.js +++ b/src/routes/matchups/+page.js @@ -1,4 +1,4 @@ -import { getBrackets, getLeagueMatchups, loadPlayers } from '$lib/utils/helper'; +import { getBrackets, getLeagueMatchups, getLeagueTeamManagers, loadPlayers } from '$lib/utils/helper'; export async function load({ url, fetch }) { const queryWeek = url?.searchParams?.get('week'); @@ -6,6 +6,7 @@ export async function load({ url, fetch }) { queryWeek: isNaN(queryWeek) ? null : queryWeek, matchupsData: getLeagueMatchups(), bracketsData: getBrackets(), + leagueTeamManagersData: getLeagueTeamManagers(), playersData: loadPlayers(fetch), }; } \ No newline at end of file diff --git a/src/routes/matchups/+page.svelte b/src/routes/matchups/+page.svelte index 187cb2b0b..91b65e9be 100644 --- a/src/routes/matchups/+page.svelte +++ b/src/routes/matchups/+page.svelte @@ -2,7 +2,7 @@ import { MatchupsAndBrackets } from '$lib/components'; export let data; - const {queryWeek, matchupsData, bracketsData, playersData} = data; + const {queryWeek, matchupsData, bracketsData, playersData, leagueTeamManagersData} = data;
- +
\ No newline at end of file diff --git a/src/routes/records/+page.js b/src/routes/records/+page.js index 5dd8b61bf..412a3119a 100644 --- a/src/routes/records/+page.js +++ b/src/routes/records/+page.js @@ -1,9 +1,10 @@ -import { getLeagueRecords, getLeagueTransactions, waitForAll } from '$lib/utils/helper'; +import { getLeagueRecords, getLeagueTeamManagers, getLeagueTransactions, waitForAll } from '$lib/utils/helper'; export async function load() { const recordsInfo = waitForAll( getLeagueRecords(false), getLeagueTransactions(false), + getLeagueTeamManagers(), ) return { diff --git a/src/routes/records/+page.svelte b/src/routes/records/+page.svelte index bd500df48..7e09b1aaa 100644 --- a/src/routes/records/+page.svelte +++ b/src/routes/records/+page.svelte @@ -26,8 +26,8 @@

Loading league records...

- {:then [leagueData, {totals, stale}]} - + {:then [leagueData, {totals, stale}, leagueTeamManagers]} + {:catch error}

Something went wrong: {error.message}

diff --git a/src/routes/rosters/+page.js b/src/routes/rosters/+page.js index 6f29e1724..1682d9815 100644 --- a/src/routes/rosters/+page.js +++ b/src/routes/rosters/+page.js @@ -1,10 +1,10 @@ -import { getLeagueData, getLeagueRosters, getLeagueUsers, loadPlayers, waitForAll } from '$lib/utils/helper'; +import { getLeagueData, getLeagueRosters, getLeagueTeamManagers, loadPlayers, waitForAll } from '$lib/utils/helper'; export async function load({fetch}) { const rostersInfo = waitForAll( getLeagueData(), getLeagueRosters(), - getLeagueUsers(), + getLeagueTeamManagers(), loadPlayers(fetch), ) diff --git a/src/routes/rosters/+page.svelte b/src/routes/rosters/+page.svelte index 6c9f933ba..071d2a3c6 100644 --- a/src/routes/rosters/+page.svelte +++ b/src/routes/rosters/+page.svelte @@ -26,9 +26,9 @@
- {:then [leagueData, rosterData, users, playersInfo]} + {:then [leagueData, rosterData, leagueTeamManagers, playersInfo]} - + {:catch error}

Something went wrong: {error.message}

diff --git a/src/routes/standings/+page.js b/src/routes/standings/+page.js index cea2041e9..17add6918 100644 --- a/src/routes/standings/+page.js +++ b/src/routes/standings/+page.js @@ -1,12 +1,12 @@ -import { getLeagueStandings, getLeagueUsers } from '$lib/utils/helper'; +import { getLeagueStandings, getLeagueTeamManagers } from '$lib/utils/helper'; export async function load() { const standingsData = getLeagueStandings(); - const usersData = getLeagueUsers(); + const leagueTeamManagersData = getLeagueTeamManagers(); return { standingsData, - usersData, + leagueTeamManagersData, }; } \ No newline at end of file diff --git a/src/routes/standings/+page.svelte b/src/routes/standings/+page.svelte index 3766f74cc..9c1b13188 100644 --- a/src/routes/standings/+page.svelte +++ b/src/routes/standings/+page.svelte @@ -2,7 +2,7 @@ import { Standings } from '$lib/components' export let data; - const {standingsData, usersData} = data; + const {standingsData, leagueTeamManagersData} = data;
- +
diff --git a/src/routes/transactions/+page.js b/src/routes/transactions/+page.js index 0a96d3ff4..ae5b468d5 100644 --- a/src/routes/transactions/+page.js +++ b/src/routes/transactions/+page.js @@ -1,4 +1,4 @@ -import { getLeagueTransactions, loadPlayers } from '$lib/utils/helper'; +import { getLeagueTransactions, loadPlayers, getLeagueTeamManagers } from '$lib/utils/helper'; export async function load({ url, fetch }) { const show = url?.searchParams?.get('show'); @@ -6,6 +6,7 @@ export async function load({ url, fetch }) { const curPage = url?.searchParams?.get('page'); const transactionsData = getLeagueTransactions(false); + const leagueTeamManagersData = getLeagueTeamManagers(); const playersData = loadPlayers(fetch); @@ -18,6 +19,7 @@ export async function load({ url, fetch }) { query: "", playersData, transactionsData, + leagueTeamManagersData, page: 0, } if(show && (show == "trade" || show == "waiver" || show == "both")) { diff --git a/src/routes/transactions/+page.svelte b/src/routes/transactions/+page.svelte index 204815013..985b321a3 100644 --- a/src/routes/transactions/+page.svelte +++ b/src/routes/transactions/+page.svelte @@ -4,7 +4,7 @@ import { waitForAll } from '$lib/utils/helper'; export let data; - const {show, query, page, playersData, transactionsData} = data; + const {show, query, page, playersData, transactionsData, leagueTeamManagersData} = data; let el, masterOffset; @@ -45,13 +45,13 @@
- {#await waitForAll(transactionsData, playersData)} + {#await waitForAll(transactionsData, playersData, leagueTeamManagersData)}

Loading league transactions...

- {:then [{transactions, currentManagers, stale}, playersInfo]} - + {:then [{transactions, currentTeams, stale}, playersInfo, leagueTeamManagers]} + {:catch error}

Something went wrong: {error.message}

{/await} diff --git a/src/theme/_smui-theme.scss b/src/theme/_smui-theme.scss index e4e623357..28286e251 100644 --- a/src/theme/_smui-theme.scss +++ b/src/theme/_smui-theme.scss @@ -65,9 +65,21 @@ --fadeThree: rgba(255, 255, 255, 0.9) 90%; --fadeFour: rgba(255, 255, 255, 1) 100%; --blueOne: #00316b; + --blueTwo: #0082c3; --r1: #f7f9fd; --r2: #fbf7f7; --r3: #f7fbf7; + --headerPrimary: #ddedfa; + --borderOverride: rgba(0,0,0,.12); + + // Chart bar colors + --barChartOne: #7fc4f5; + --barChartTwo: #5bcff7; + --barChartThree: #38daf0; + --barChartFour: #2fe2e0; + --barChartFive: #4fe9c9; + --barChartSix: #79edad; + // Positions --QBfade: #ff8fb2; --WRfade: #afd4ff; @@ -88,7 +100,7 @@ --RB: #00ceb8; --TE: #ffae58; --K: #bd66ff; - --DEF: #fff67a; + --DEF: #eee45e; --DL: #ff795a; --LB: #6d7df5; --DB: #ff7cb6; diff --git a/src/theme/dark/_smui-theme.scss b/src/theme/dark/_smui-theme.scss index b15b635e6..c15493c9a 100644 --- a/src/theme/dark/_smui-theme.scss +++ b/src/theme/dark/_smui-theme.scss @@ -65,9 +65,21 @@ --fadeThree: rgba(0, 0, 0, 0.9) 90%; --fadeFour: rgba(0, 0, 0, 1) 100%; --blueOne: #0082c3; + --blueTwo: #00316b; --r1: #1c1c1d; --r2: #1f1d1d; --r3: #181a18; + --headerPrimary: #2a2e31; + --borderOverride: rgba(255,255,255,.3); + + // Chart bar colors + --barChartOne: #446a88; + --barChartTwo: #32788f; + --barChartThree: #1d808d; + --barChartFour: #198886; + --barChartFive: #2b8673; + --barChartSix: #448864; + // Positions --QBfade: #ff8fb2; --WRfade: #afd4ff; diff --git a/static/retired.png b/static/retired.png new file mode 100644 index 0000000000000000000000000000000000000000..d00fcf6dc5777532ced0e2d30c1e548316da7fd6 GIT binary patch literal 9332 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 i$3j2%wiS`^F8E)`*B*-SOfJX(0000