Giter Club home page Giter Club logo

coh2stats's Introduction

COH2 CHARTS, PLAYER STATS, LEADERBOARDS

GitHub release (latest by date) Uptime Robot ratio (30 days) DeepScan grade Discord

With all the data, player cards, leaderboards, maps, stats, commanders, inte-bulletins and much more!

Desktop App here

For current match details, player information and other features.

This project is set to create new usage charts for the game COH2. And also create web access to leaderboards, player cards and recent matches for players. The author of the game doesn't provide any global statistics. And online leaderboards has been shutdown recently.

This project is inspired by coh2chart.com which has data only for years 2016-2017 after which it was shut down.

Open source data

All the matches downloaded by the system are exposed with matches-${unixTimestamp}.json file for every day. You can find them in this bucket https://storage.googleapis.com/storage/v1/b/coh2-stats-matches/o use the medialink to download. Or this url https://storage.googleapis.com/coh2-stats-matches/matches-${timeStamp}.json

The data are in JSON format:

{
    "matches": [{
        matchhistoryreportresults: Matchhistoryreportresult[];
        matchtypeid:               number;
        matchhistoryitems:         Matchhistoryitem[];
        description:               string;
        profileids:                number[];
        creatorProfileid:          number;
        mapname:                   string;
        startgametime:             number;
        id:                        number;
        completiontime:            number;
        steamids:                  string[];
        maxplayers:                number;}]
    "timeStamp": "1656460800"
}

The new file is generated every day at ~4 AM UTC time for the previous day. So 30 June 4 AM we generate a file for 29 June. The timestamp is always that day 00:00:00 aka 1656460800 which is Wed Jun 29 2022 00:00:00 GMT+0000 you can get previous day timestamp with the code below.

const date = new Date(); // Example to get yesterday Unix timestamp
const yesterDayTimeStamp =
  Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() - 1, 0, 0, 0) / 1000;

Files are stored for 30 days. If you are interested in long term analysis, everyday download is recommended.

In case you will use the data, please mention the source and give a shoutout to the website coh2stats.com, thank you!

Technical description

The project is created in Google Cloud with usage of Firebase. The main language of the project is set to be JavaScript. Which will be used on both FE and BE.

  • FE - JavaScript, React
  • BE - Serverless JS
  • DB - FireStore (NoSQL DB)

GCP services to be used:

  • Firebase Hosting - For hosting the website
  • Firebase Firestore - NoSQL Database
  • Firebase Cloud functions - Is the main backend for crawling and data processing
  • GCP Pub/Sub - For messaging between the functions
  • GCP Storage - For storing extra data

Thing to consider:

  • There is a large amount of matches, we store them in the FireStore, however once you store the match. You don't do any changes to it, therefore it would be better to store them in the BigQuery where we could run our analysis more easily and it would be Faster and Cheaper.

CI/CD

Only web package is automatically deploy. Cloud functions need to be done manually for now.

Prod

Tagged versions are automatically deployed to https://coh2stats.com/

Dev

master branch is automatically deployed to https://coh2-ladders-dev.web.app/

Development

The repository is yarn workspace. Use yarn to manage this. Do yarn install from the project root to install dependencies.

To run beautifier and linting: yarn fix

Use Node version 16.x or as described in /packages/functions/package.json

Web

  • To start local web dev: yarn web start
  • Test: yarn web test (located in packages/web/src/tests)
  • Build: yarn web build

Functions

BE has been moved to repo coh2-stats-be

Patch update steps for text bulletin / commander data

  1. Run script bulletinsAndCommanders.py with correct path to your COH2 folder
  2. Run script fixCommanderImages.py to fix the generated commanderData.json file
  3. Copy the generated files *ServerData.json into packages/functions/src/libs/data/
    • bulletinServerData.json
    • commanderServerData.json
  4. Copy the generates files *Data.json into packages/web/src/coh/data/
    • bulletinData.json
    • commanderData.json
  5. Run formatter by using commander yarn fix
  6. Observe the changes
  7. Update the packages/web/src/config.tsx with the right date / patch name

Images for maps

Provided by Janne252 https://github.com/Janne252/coh2-replay-discord-bot/tree/master/data/scenario-preview-images
For mass reformats from .png to .webp - use Infraview https://www.irfanview.com/ (best image utility)

Crawler process

Diagram: https://lucid.app/documents/embeddedchart/ec7ffc19-50c4-4104-bcf9-287e2af3ac62

Crawler process is set to run every day. There is a huge amount of data so we need to do it everyday to avoid big stress on the Relic API.

The crawling is design in a way which should not stress the Relic API (aka slowly).

~The crawler process should run everyday. Most likely 5 AM UTC. As that should be the time with least players (EU, US asleep). But we will treat this date as a date -1 day data.

##### 1. We request top 200 players from all leaderboards

This gives us 5200 positions. It's done by cloud function getCOHLadders. We can request by 200 chunks from the API => 26 Relic API calls.
This operation takes around 30 seconds.

##### 2. Filter only unique players

We extract the Steam IDs, only unique players are kept. ~~This gives us ~2900 players for the top 5200 positions.~~

3. Send player ids to Pub/Sub que

We send the player Steam IDs as a messages to the que called "download-matches" Each message has array of IDs. The current chunk is set to X. TODO: Experiment with the best chunk size. We want the chunk size to be pretty big because we can filter the duplicates only in one chunk. (We filter the rest when we hit the DB but we want to avoid unnecessary writes to the DB)

4. Pub/Sub que "download-matches" is consumed

The que is consumed by cloud functions getPlayerMatches. The main benefit of the que is that any service of our application can send a message into it. Making sure the match is saved. Only 2-3 instances of the function are allowed. To slow down the processing. We had to limit it to 1 function. We were getting errors "Too many requests" from the API.

The function takes the array of the IDs and downloads the matches of each player in sequence (again not to stress the API).

This takes around 45-85 seconds. Usually ~350-500ms per player. However there are anomalies which can go up to 5-6 seconds per player.

This makes the amount of players API calls. (~2900) The process takes around 25 minutes.

5. Matches are filtered and modified

We filter matches only from the previous day (The API returns all player matches). We try to filter any duplicated matches (1 match is shown that many times as it has players). This is designed not to do extra writes to the DB with the data we already have. This saves us DB reads and writes.

We remove any extra fields we don't care about.

6. Matches are saved to the DB

All matches are saved in the FireStore under collection /matches The ID of the document is the ID of the match which ensures nothing can be duplicated.

7. Analysis is run

We can get matches for particular day and run analysis on them.

coh2stats's People

Contributors

3nder990 avatar aliazizi avatar atrievel avatar binkpitch avatar bricakeld avatar charlygg avatar eduardo-rebase avatar imgbot[bot] avatar indremak avatar johannesmerkt avatar lpegoraro avatar malay-dev avatar manthan-kuber avatar matejdurech avatar mikulasmascautanu avatar milan218 avatar najeebkp avatar petrvecera avatar r4md4c avatar rempaut avatar renovate[bot] avatar saimaheshtaduri avatar sepi4 avatar some85 avatar thenicekat avatar tichopad avatar y4qub avatar yashdhanore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

coh2stats's Issues

Add map winrate analysis to the main page

The idea is that we know the map name and we can get the winrate for the specific team composition from the database.
We could display something like this (attached image) under the main table - btw this is just suggestion, if you have better idea feel free to change it to your liking!

image

How you can get the data is showed in the app.ts file in the render folder. The database will return object like this
image

You will need to build a faction matrix key, for example OOxBB:
It always starts with german players and than allies.
The keys are like this:

const raceIdsShortCuts: Record<number, string> = {
  0: "W",
  1: "S",
  2: "O", // west-german or oberkommando
  3: "U",
  4: "B",
};

There are functions for building these strings packages/functions/src/libs/analysis/composition.ts:21 however I am not sure if they are compatible with your data - maybe yes. They take PlayerReport array data from the API - I would recommend copying that function.

To calculate the winrate - the data saved there are for AXIS.
So if we have for example OWWxBSS: {losses: 50, wins: 67} it means:
Axis: losses 50, wins 67
Allies: losses 67, wins 50

Winrate Axis (67 / (50 + 67)
Winrate Allies (50 / (50 + 67)


And I just got an idea that we could display how common is the composition they are plying:
We could could total games for each compositon and maybe display something like:
Your composition OOWxBBB is 24 most played out of 36 possible team compositions.

^^ but that would be just cherry on top.

image

Please hit me up if you have any questions / or if you won't have time to work on this , I could take a look on it next weekend maybe.

I think this feature will completely shoot our app to the moon - because it's something no other app like this have!

Add patch dates into a datepicker dates

The date picker could have marked dates which are the patch dates.
image
This could be added into the stats date pickers but also leaderboards datepickers. Should be visible only in day, week pickers. I am not sure how it would behave in the month one?

The patches info can be found here packages/web/src/coh/patches.ts
We only care about the release date of the patch

And to achieve this we could use custom date rendering as described here
https://ant.design/components/date-picker/#components-date-picker-demo-date-render

image

Add links to the player names in the full match overview

When you open the match over-view, for example on this player https://coh2-ladders-dev.web.app/players/76561198366572706?view=matches and click on some match. The player names are not links. They should be links as in the table.

Task: Make the names links too.
image

The function which renders the player name is here:
packages/web/src/pages/matches/match-details-table.tsx line ~63

The code to make it a link should be:

<Link to={routes.playerCardWithId(convertSteamNameToID(playerInfo.profile["name"]))}> </Link>

^^ could probably work like this...

But to explain: The path can be taken from function routes.playerCardWithId(steamIDjustNumbers) , the steam id is often saved as "/steam/1231231" so we can use function convertSteamNameToID() to get just the number.

Improve the search to include local data too - Part 1

We have search on the page https://coh2stats.com/search/isildur
image

Right now the search calls BE function which finds the players in the database.
However the page except for players has also Commanders and intel bulletins

We would like to improve the search to also search trough commanders and intell bulletins - which is the static data saved on the website. The inspiration could be taken from search on Dotabuff https://www.dotabuff.com/search?utf8=%E2%9C%93&q=riki&button=


Task 1 - Prepare the functions for search

In the file packages/web/src/coh/commanders.ts we need to prepare function searchCommanders which should accept one string, the search param. On search we should go trough the commander data located in allCommanders variable.

Each commander (object) has lot of fields. We should go trough field commanderName and each name on abilities array. Don't forget to do to lowercase and search on all words in the name.

The function should return array of commanders ( objects) which fulfill the search params.

The same should be done for Intel bulletins you find in packages/web/src/coh/bulletins.ts with them we should search on bulletinName and descriptionShort too. Again return array of bulletins which fulfil the search.

To prove the functions work. Write very simple test in packages/web/src/tests/coh/bulletins.spec.ts and packages/web/src/tests/coh/commanders.spec.ts


See ticket TODO - Part 2 where we can implement the visuals. Doesn't need to be done as part of this ticket.

Sync the search on the intel bulletins with query

On the page https://coh2stats.com/bulletins we can do search - however it's not put into the path.

We should modify this component to add search data into the url as a query, probably https://coh2stats.com/bulletins?search=text

This component is written at packages/web/src/pages/bulletins/bulletinList.tsx

We just need to add code into this function:

  const handleNameSearch = (e: ChangeEvent<HTMLInputElement>) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      handleSearch(e.target.value, "bulletinName");
    }, 300);
  };

  const handleSearch = (value: string, column: string) =>
    setFilteredInfo({ ...filteredInfo, [column]: value });

To read the query you can use useQuery hook
import { useQuery } from "../../utils/helpers";
https://github.com/petrvecera/coh2ladders/blob/master/packages/web/src/pages/stats/custom-stats-details.tsx#L28

Write simple unit tests (~8) for commanders and intel bulletins

We need to write a simple unit tests for functions written in:

packages/web/src/coh/bulletins.ts
packages/web/src/coh/commanders.ts

Both files have very similar functions. Each has ~4 simple functions.
The unit tests can be written with just few assertions.

The unit tests should be created in files:

packages/web/src/test/coh/bulletins.spec.ts
packages/web/src/test/coh/commanders.spec.ts

You can take insperation in packages/web/src/tests/coh/patches.spec.ts
The describe should be always the the name of the function. Example:

describe("getBulletinData", () => {
 test("Retrurns correct bulletin on ID", () => {
   const bulletin = getBulletinData(bulletinId);
   ...
 }
}

You can runt the tests using command yarn web test

Add amount of online players

Let's add amount of online players - I imagine that we could have some component in the top right corner ? Maybe it could be a floating one ?

Can look exactly as on on coh2stats.com
image

  // The date is * 1000 because we store them as unix timestamp, javascript uses different timestamp
    // We could have some element like on https://coh2stats.com/ , check packages/web/src/components/main-header.tsx:99
    // I think we could take exactly what is in that file 

image

Feature Request - Add team balance stats based on ranks

It would be a very interesting statistic to see if matchmaking is allies or axis favored. Basically you would calculate the average rank for the axis and allies team to see if allies or axis got an rank "advantage" by the matchmaker. This statistic could be part of the stats page where it would be calculated for General, 1v1, 2v2, 3v3 and 4v4 and displayed as a Pie chart for example.

Discussion - Merging a game stats display into this project

Hi there again,
three months ago I wrote a little electron app that shows the rankings of the players in your current match.
https://github.com/John-Wickedsick/coh2-player-stats

Back then I wanted to continue to extend this application to also have a detail view available and stumbled upon this project. I have looked at this project and its issues (that have been posted on the repo) in detail and it made me think that it might be very interesting to merge these projects together.

Let me elaborate:
I was thinking, since the electron app uses react too, we could use the UI and data of this project to extend the amount of info (with detail player infos) in the app and provide this app as a download on the site. Then inside this app we can ask the user if he is ok with sharing his match data with us. This is fully optional and the user can still use the app even if he declines. If the user agrees we could use the match replays that are generated after each data to collect additional information to show on the website. Such as match balance info: #118
or arranged team info:
#21
in case we have knowledge about how the data of replays are structured, there are even more possibilities, like unit timings:
#102
This data will be processed on the users pc and posted on the server. The replay won't be uploaded of course only the data we want to collect and show on the website.

Now I would love to hear your opinion about this @petrvecera. Is there any interest in such a thing? Does it make sense when coh3 is on the horizon? Is it ok to have incomplete data? Since we cant collect this kind of data for every match of course.

I for sure would find it very exciting to see such stats on this projects website ;)

Error in fetching the player matches - Too many requests - again

We hit too many requests error again - not really sure why. It's not obviously super fast in that particular moment in the logs.
image

Mitigation - use rate limiter on our side https://www.npmjs.com/package/limiter
at
https://github.com/petrvecera/coh2ladders/blob/76021bdd6d253c873ac90c34598dfcbdd0e8db0e/packages/functions/src/libs/matches/matches.ts#L11
^^ also re-write the code in this part to better handle the errors, we should not throw, but return empty array of something

The max rate:

  • functions has max timeout of 540
  • we should count with 450 as max time for us , so we have 90s window for degraded performance
  • we are sending 1k players at the time for the function
  • this gives us nice max 450ms/request rate limiting
  • However we need to count with the fact that requests takes from 50-1500ms

-- consider rate limiting for 200ms for the start testing?

We are already doing everything in sync mode ( only 1 request to the API ) so we don't need to worry about:

Both the token bucket and rate limiter should be used with a message queue or some way of preventing multiple simultaneous calls to removeTokens()

Generate filenames for new icons - commanders [Python]

In commander patch 2021, we got new commander icons. The problem is that Relic did not update the coh2 tools where data about icons for each commander are saved --> we can't use our scripts to get the right filenames.

And the new icons have new names - we can't just replace them.

  • We were able to get the new icons, we have all the exported image files for the game.
  • We were able thanks to the 3rd party tools to partially re-create and extract the game data files. And we do have .xml files for each commander. These file are partially "corrupted" however they DO have the filename icons. These files are saved in https://github.com/petrvecera/coh2ladders/tree/master/scripts/data/cu2021/commander

Task:

Write a python script (or modify the current python script) which generates the commanderData.json to have icons from the .xml files located in https://github.com/petrvecera/coh2ladders/tree/master/scripts/data/cu2021/commander

Bonus:

If you would be able to get (filter) those specific images from all the icons (https://github.com/petrvecera/coh2ladders/blob/master/scripts/data/all-img.7z) and put them into packages/web/public/resources/exportedIcons it would be awesome.

Refactor the commanders list

https://coh2stats.com/commanders/wermacht

Right now we have a list like this:
image

The goal would be to have a list similar to the list as shown here https://coh2index.com/army/west_german.html (bottom of page)
but still maintain the description ...
image

Example:
image

Tasks:

  • Swap the icon for the big one (it's in the assets, _large ... )
  • Create a new component for the mini icon of bulletins (small icon which is suppose to be shown), icon should have tooltip with the name of bulletin
  • Refactor the element in the list

Unit Timing stats

  • when does a flak HT usually first hit the field?
  • when do you need to expect the first Comet in a 2v2?

If you can extract this data, this could make an interesting chart.
something like this:
image

For the medium level players this is pretty valuable
(I guess the new ones do not care and the top level know anyhow)

There is more result types

packages/functions/src/libs/analysis/match-analysis.ts

There is more results types than

const resultType = {
  lose: 0,
  win: 1,
};

There is also 2,3,4 ? We have in DB 4 - see match ID 251620740

The code which counts this looks like this

  for (const playerReport of match.matchhistoryreportresults) {
    if (playerReport.resulttype == resultType.win) {
      const faction = raceIds[playerReport.race_id];
      stats[faction]["wins"] = stats[faction]["wins"] + 1 || 1;
    } else if(playerReport.resulttype == resultType.lose) {
      const faction = raceIds[playerReport.race_id];
      stats[faction]["losses"] = stats[faction]["losses"] + 1 || 1;
    }

We might need to consider what do to with those matches. Do we want to count them too?

Enable dark mode / light mode switch

We could try to add dark mode, there could be a switch in top right corner

https://ant.design/docs/react/customize-theme#Official-Themes-%F0%9F%8C%88

If anyone wants to help with this: I am looking mostly for POC how to nicely swap the themes for both antd and our components.

My POC requires taking out .css files for antd. Which is not very efficient or elegant. Would require to manually do this on every antd update. Could be probably automated with the webpack ?

After adding this we need to verify that all components are displayed correctly.
We could have localstorage where persons preference would be stored.

[Feature Request] Match stats vs. faction

First of all, thank you for reviving coh2stats. I'm really happy to see the site again.

My request is that since we already have detail match info. present (such as commando / intel bulletins), add one more section to show what faction they played against & win ratio. (at least for 1vs1).

It would give such a good insights.

Thank you!

Add commanders and bulletins to the search results

Difficulty: Medium
Getting started steps: https://github.com/cohstats/coh2stats#development

We have a search on the site which does API call and shows player cards.
For Example:
https://coh2stats.com/search/killer

We want to improve this search so it also shows results from:

(this is static data on the site, the search functions are ready)

It could look like this:
where we would see additional cards for commandres (the link would go to the commander details ) or intel bulletins - the link would just go to the intel bulletins
image


Implementation

The search is written in coh2ladders\packages\web\src\pages\search\search.tsx
We do the search at the line 109 , where we call to the API.

At this line we can call function searchBulletins - this is static data, sync function packages/web/src/coh/bulletins.ts
At this line we can call function searchCommanders, - again static data, sync function packages/web/src/coh/commanders.ts

We will need to to build new small card for commanders and bulletins, something as userCard component in packages/web/src/pages/search/search.tsx

We will need to change the data to object with 3 fields - found users, found commanders, found bulletins
We can than easily build the output html.

For the divider we can use divider with text https://ant.design/components/divider/
When no data are used we can use https://ant.design/components/empty/#header

Feel free to ask any additional questions

Make component for error message & use it with errors.

Right now when something fails. The error message is awful.
For example: https://coh2stats.com/players/76561198018614asfdaf
image

Task:
Create a new component in packages/web/src/components called AlertBox which will be build using Antd Alert component https://ant.design/components/alert/
image
We will use icon by default. We don't need closable.

Our component should accept props:

  • Type
  • Message
  • Description

Which will be passed into antd component.


Next: Use new component on the players page: packages/web/src/pages/players/player-card.tsx
line ~82

Type error, message: Error loading the player card, description - what is returned from BE ( current thing)

Make sure the element is centered on the page - probably add to some div?

Add table summary row to player card

On player cards we have tables https://coh2stats.com/players/76561198018614046

image

TASK: Add summary row

See antd documentation how to do that https://ant.design/components/table/#components-table-demo-summary
The table is in packages/web/src/pages/players/player-single-matches-table.tsx

Columns to skip: mode, rank, level, streak --> we don't need to add any summary to those columns
Columns to have summary:

  • Wins --> sum of all wins
  • Losses --> sum of all losses
  • Ratio --> (sum of all wins) / (sum of total) ... because we don't want to do sum of ratios, it will create non-precise result due to rounding
  • Total --> sum of total
  • Drops --> sum of drops
  • Disputes --> sum of disputes
  • Last Game --> The date which is latest

Add the same summary to packages/web/src/pages/players/player-team-matches-table.tsx which is the table for AXIS and Allies
image

Questions about getting data from api and interpreting it

So far I only know these api methods from a forum https://pastebin.com/raw/8CNjUJA8. However, I am more curious about the other methods available to users. Where can I find documentation, list of methods etc to request data by myself?

In addition, if available, I want to know if the following data are available for each match:

Also, by inspecting a match info data, I notice the

options: "eNozYPvGAAGMDEyMIHpj9qa5gt18FlBhBjlGORCJB4OAEBCbFMQXJWbmFMcn5qXE56aWJOYwDAYAAHUmDME=",
slotinfo:
    "eNrjYWBg4GB4tVOGAQQYGTABM4zxHwpA7BdAtYwgDiMDJ2Om60QOkGBxMCtYIYQESlRAJXRvMKFKLL4iLgliZTxnQ5UohOrQQ9eRAZVg+8KMKhEOlZiLLlEJldiCKsGIAhEA5jN0MGWfOkowMCIFExO2oBFjRAqaV+15/FiD5gVUwhjdoy+hEkboEn+vHRUEsTjeo3mUoQOiAyNoWKASGEHDCJXAGjScjMI+k4XBPvFjRZfmZOSD6n38ACXqCAekYJgeaqAhAUa8aYyT0SfaDuz3DEgyRbiIkzHs2GawVOl1tPDygOo5LsqAKqEHlTiDLmEHlZAUR5OwgErIoEvk37IXAbHuiTNQksbEH1uCaRYsaYwBW9DoIqcx9znRIljTWBpU4gm6s3OgEs/QJTyhEt6f0VKMF1RCGl2HN1QCI2gCoRKSmEHDyWgckQCW7ENNRCApa0+IVJ44RlRnQ40kOen9RwNYxZAkGTGlGUnXQhtbAJrRNIc=",

are encoded in base64. But a regular base64 decoder doesnt interpret it correctly. It just doesnt make sense.

So in general, it would be greatly appreciated if you can provide some helps/documentation on such where I can get these data and how to interpret. Thanks!

Add download button into match details - generate .json file

Task: Add download button to the modal which will export the game data into .json file

image
The button should be named "Download game data." - or if you have better idea

You can do this by customizing the footer buttons on the modal https://ant.design/components/modal/#components-modal-demo-footer

The modal itself is defined in file packages/web/src/pages/matches/last-matches-table.tsx in component ExpandedMatch line ~147

And the data we need to download are the props record which you will get in the component ExpandedMatch .


Another thing is - how to generate the json file from basically nothing?

The filename should be probably coh2stats_match_${record.id}.json

I can't remember how do it. But if I am not mistaken we should be using blobs
Something like: https://stackoverflow.com/a/44661948/1768843

Split stats into arranged teams / random teams

It should be as with http://coh2chart.com/

Problem: We don't know how to tell the teams apart.

Explore the API response on available ladders https://coh2-api.reliclink.com/community/leaderboard/GetAvailableLeaderboards?title=coh2

It looks like statgroup_type is either 1(random) or 2 in (arranged teams)
But than on the single match object it's not mentioned anywhere.

The single match object from the API looks like this:

  id: 235119300,
  creator_profile_id: 1882602,
  mapname: "4p_rails_and_metal",
  maxplayers: 8,
  matchtype_id: 0,
  options: "eNozYPvGAAGMDEyMIHpj9qa5gt18FlBhBjlGORCJB4OAEBCbFMQXJWbmFMcn5qXE56aWJOYwDAYAAHUmDME=",
  slotinfo:
    "eNrjYWBg4GB4tVOGAQQYGTABM4zxHwpA7BdAtYwgDiMDJ2Om60QOkGBxMCtYIYQESlRAJXRvMKFKLL4iLgliZTxnQ5UohOrQQ9eRAZVg+8KMKhEOlZiLLlEJldiCKsGIAhEA5jN0MGWfOkowMCIFExO2oBFjRAqaV+15/FiD5gVUwhjdoy+hEkboEn+vHRUEsTjeo3mUoQOiAyNoWKASGEHDCJXAGjScjMI+k4XBPvFjRZfmZOSD6n38ACXqCAekYJgeaqAhAUa8aYyT0SfaDuz3DEgyRbiIkzHs2GawVOl1tPDygOo5LsqAKqEHlTiDLmEHlZAUR5OwgErIoEvk37IXAbHuiTNQksbEH1uCaRYsaYwBW9DoIqcx9znRIljTWBpU4gm6s3OgEs/QJTyhEt6f0VKMF1RCGl2HN1QCI2gCoRKSmEHDyWgckQCW7ENNRCApa0+IVJ44RlRnQ40kOen9RwNYxZAkGTGlGUnXQhtbAJrRNIc=",
  description: "AUTOMATCH",
  startgametime: 1605637963,
  completiontime: 1605639516,
  observertotal: 0,
  matchhistoryreportresults: [
    {
      matchhistory_id: 235119300,
      profile_id: 1882602,
      resulttype: 1,
      teamid: 0,
      race_id: 3,
      xpgained: 12552,
      counters:
        '{"manmax":650,"vvetrank":13,"cabil":5,"dmgdone":14227,"plost":3,"svetrank":2,"fuelmax":234,"cpearn":15,"blost":0,"edeaths":79,"pcap":9,"munspnt":855,"precap":4,"sqkill":10,"popmax":0,"sqprod":14,"bprod":16,"vabnd":0,"svetxp":2890,"fuelearn":696,"ismod":0,"vkill":4,"utypes":17,"abil":55,"sqlost":8,"fuelspnt":595,"vcap":0,"vlost":3,"manspnt":5562,"gt":1468,"upg":8,"version":3,"vvetxp":8816,"munearn":1099,"vp1":0,"vp0":440,"manearn":5523,"erein":54,"munmax":444,"cflags":0,"wpnpu":0,"ekills":81,"vprod":6}',
      matchstartdate: 1605637963,
    },
    {
      matchhistory_id: 235119300,
      profile_id: 2604692,
      resulttype: 0,
      teamid: 1,
      race_id: 2,
      xpgained: 10249,
      counters:
        '{"manmax":645,"vvetrank":13,"cabil":0,"dmgdone":11539,"plost":3,"svetrank":2,"fuelmax":136,"cpearn":13,"blost":0,"edeaths":78,"pcap":4,"munspnt":540,"precap":2,"sqkill":4,"popmax":0,"sqprod":16,"bprod":13,"vabnd":0,"svetxp":2060,"fuelearn":416,"ismod":0,"vkill":2,"utypes":8,"abil":39,"sqlost":5,"fuelspnt":290,"vcap":0,"vlost":2,"manspnt":5880,"gt":1468,"upg":7,"version":3,"vvetxp":6600,"munearn":701,"vp1":0,"vp0":440,"manearn":5804,"erein":54,"munmax":194,"cflags":128,"wpnpu":0,"ekills":62,"vprod":7}',
      matchstartdate: 1605637963,
    },
    {
      matchhistory_id: 235119300,
      profile_id: 3036689,
      resulttype: 1,
      teamid: 0,
      race_id: 1,
      xpgained: 11574,
      counters:
        '{"manmax":676,"vvetrank":9,"cabil":6,"dmgdone":12778,"plost":7,"svetrank":5,"fuelmax":318,"cpearn":13,"blost":0,"edeaths":66,"pcap":13,"munspnt":430,"precap":9,"sqkill":5,"popmax":0,"sqprod":14,"bprod":16,"vabnd":1,"svetxp":3570,"fuelearn":696,"ismod":0,"vkill":3,"utypes":11,"abil":70,"sqlost":6,"fuelspnt":500,"vcap":1,"vlost":2,"manspnt":5737,"gt":1468,"upg":3,"version":3,"vvetxp":5200,"munearn":1099,"vp1":0,"vp0":440,"manearn":5745,"erein":42,"munmax":772,"cflags":128,"wpnpu":0,"ekills":58,"vprod":5}',
      matchstartdate: 1605637963,
    },
    {
      matchhistory_id: 235119300,
      profile_id: 3793687,
      resulttype: 0,
      teamid: 1,
      race_id: 0,
      xpgained: 12607,
      counters:
        '{"manmax":1725,"vvetrank":12,"cabil":0,"dmgdone":16187,"plost":10,"svetrank":5,"fuelmax":150,"cpearn":15,"blost":0,"edeaths":70,"pcap":13,"munspnt":650,"precap":8,"sqkill":8,"popmax":0,"sqprod":17,"bprod":18,"vabnd":0,"svetxp":5950,"fuelearn":416,"ismod":0,"vkill":3,"utypes":7,"abil":23,"sqlost":11,"fuelspnt":325,"vcap":0,"vlost":6,"manspnt":5765,"gt":1468,"upg":10,"version":3,"vvetxp":6360,"munearn":701,"vp1":0,"vp0":440,"manearn":6062,"erein":25,"munmax":267,"cflags":128,"wpnpu":0,"ekills":67,"vprod":4}',
      matchstartdate: 1605637963,
    },
  ],
  matchhistoryitems: [
    {
      profile_id: 1882602,
      matchhistory_id: 235119300,
      iteminstance_id: 143738225,
      itemdefinition_id: 186414,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 1882602,
      matchhistory_id: 235119300,
      iteminstance_id: 143738232,
      itemdefinition_id: 186413,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 1882602,
      matchhistory_id: 235119300,
      iteminstance_id: 420992163,
      itemdefinition_id: 452456,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 1882602,
      matchhistory_id: 235119300,
      iteminstance_id: 143738199,
      itemdefinition_id: 259229,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 1882602,
      matchhistory_id: 235119300,
      iteminstance_id: 143738216,
      itemdefinition_id: 259078,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 1882602,
      matchhistory_id: 235119300,
      iteminstance_id: 143738233,
      itemdefinition_id: 259252,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 1882602,
      matchhistory_id: 235119300,
      iteminstance_id: 143738217,
      itemdefinition_id: 349043,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 6,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 328420371,
      itemdefinition_id: 347670,
      durabilitytype: 0,
      durability: 1,
      metadata: "",
      itemlocation_id: 2,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 258901992,
      itemdefinition_id: 186419,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 258901993,
      itemdefinition_id: 186418,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 298178301,
      itemdefinition_id: 257800,
      durabilitytype: 0,
      durability: 1,
      metadata: "{}",
      itemlocation_id: 3,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 258902016,
      itemdefinition_id: 259078,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 258902017,
      itemdefinition_id: 259252,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 258902020,
      itemdefinition_id: 259229,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 258901994,
      itemdefinition_id: 349043,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 6,
    },
    {
      profile_id: 2604692,
      matchhistory_id: 235119300,
      iteminstance_id: 258902030,
      itemdefinition_id: 450787,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 8,
    },
    {
      profile_id: 3036689,
      matchhistory_id: 235119300,
      iteminstance_id: 289299246,
      itemdefinition_id: 5580,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 3036689,
      matchhistory_id: 235119300,
      iteminstance_id: 289299272,
      itemdefinition_id: 5575,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 3036689,
      matchhistory_id: 235119300,
      iteminstance_id: 296994390,
      itemdefinition_id: 186229,
      durabilitytype: 0,
      durability: 1,
      metadata: "{}",
      itemlocation_id: 3,
    },
    {
      profile_id: 3036689,
      matchhistory_id: 235119300,
      iteminstance_id: 289299256,
      itemdefinition_id: 5916,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 3036689,
      matchhistory_id: 235119300,
      iteminstance_id: 289299262,
      itemdefinition_id: 5913,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 3036689,
      matchhistory_id: 235119300,
      iteminstance_id: 339729007,
      itemdefinition_id: 6110,
      durabilitytype: 0,
      durability: 1,
      metadata: "",
      itemlocation_id: 4,
    },
    {
      profile_id: 3036689,
      matchhistory_id: 235119300,
      iteminstance_id: 289299276,
      itemdefinition_id: 7272,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 5,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341854523,
      itemdefinition_id: 5998,
      durabilitytype: 0,
      durability: 1,
      metadata: "",
      itemlocation_id: 2,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341858355,
      itemdefinition_id: 450702,
      durabilitytype: 0,
      durability: 1,
      metadata: "",
      itemlocation_id: 2,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548105,
      itemdefinition_id: 258891,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548134,
      itemdefinition_id: 6116,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548140,
      itemdefinition_id: 6118,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 3,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548106,
      itemdefinition_id: 5915,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548107,
      itemdefinition_id: 5916,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548113,
      itemdefinition_id: 5913,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 4,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548103,
      itemdefinition_id: 349043,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 6,
    },
    {
      profile_id: 3793687,
      matchhistory_id: 235119300,
      iteminstance_id: 341548139,
      itemdefinition_id: 450787,
      durabilitytype: 0,
      durability: 1,
      metadata: '{"dlc":1}',
      itemlocation_id: 8,
    },
  ],
  matchurls: [],

Error: Value for argument "FieldValue.increment()" is not a valid number.

Error: Value for argument "FieldValue.increment()" is not a valid number.
at Object.validateNumber (/workspace/node_modules/@google-cloud/firestore/build/src/validate.js:166)
at NumericIncrementTransform.validate (/workspace/node_modules/@google-cloud/firestore/build/src/field-value.js:313)
at transforms.forEach.transform (document.js:809)
at Map.forEach (<anonymous>)
at DocumentTransform.validate (/workspace/node_modules/@google-cloud/firestore/build/src/document.js:809)
at WriteBatch.set (/workspace/node_modules/@google-cloud/firestore/build/src/write-batch.js:252)
at DocumentReference.set (/workspace/node_modules/@google-cloud/firestore/build/src/reference.js:349)
at Object.globallyAnalyzedTopMatches (/workspace/lib/libs/global-stats.js:15)
at Object.analyzeAndSaveTopMatchStats (/workspace/lib/libs/analysis/analysis.js:63)

[Feature Request] Implement "month stat so far" feature

Hello,

Currently, week or month view shows below message if the full period has not been ended.

We are currently not tracking any records for the month stats on timestamp 1625097600.
The statistics are run after the end of the day/week/month ~2AM UTC time.

But (I believe) many users want to see recent statistics before the month ends. Especially if half of the month has been passed.

So I suggest to enable Month view that shows (1st day of the month ~ day before today).

It will benefit user experience, since at the first click of week or month, it is very likely to give user not tracking any record error.

Thank you.

Add desktop app page to coh2stats.com

We will have new page "Desktop App" probably on the site, where it's gonna be just simple page describing that there is a desktop app
few features and link to the github releases for download

image

Feral accounts are not supported

There exists multiple types of accounts. For example when you search this one https://coh2-api.reliclink.com/community/leaderboard/GetPersonalStat?title=coh2&search=rak

 {
      "id": 6556450,
      "name": "",
      "type": 1,
      "members": [
        {
          "profile_id": 4412017,
          "name": "/feral/97200",
          "alias": "RAK",
          "personal_statgroup_id": 6556450,
          "xp": 149145,
          "level": 13,
          "leaderboardregion_id": 2,
          "country": "sg"
        }
      ]
    },

Which are not supported by the system. Only steam accounts like this:

 {
      "id": 6556450,
      "name": "",
      "type": 1,
      "members": [
        {
          "profile_id": 4412017,
          "name": "/feral/97200",
          "alias": "RAK",
          "personal_statgroup_id": 6556450,
          "xp": 149145,
          "level": 13,
          "leaderboardregion_id": 2,
          "country": "sg"
        }
      ]
    },

IDK where everywhere we count with just steam but 100% search does not support feral accounts and filters them out.

Desktop App - add (google) analytics

We should add google analytics (or any other analytics) to the app.

I've read that there are some problems with Electron and google analytics but there seems to be ways to do it
https://www.npmjs.com/package/electron-google-analytics

We can create any new project in google analytics for this

We basically just need how many people are using it and you can also track how many people are using the basic features.
You might be surprised what people are actually doing most and sometimes you can find out that they don't use any feature because they might not know it's there.

If google analytics can't be added - we can use something different.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.