meisnate12 / FantraxAPI

Python Wrapper for the Frantrax API
MIT License
7 stars 6 forks source link

[Bug]: The standings API is not being used in a way that is stable across leagues #2

Closed geoffbeier closed 1 month ago

geoffbeier commented 4 months ago

Version Number

0.2.7

Describe the Bug

The API being used (at least for standings, but probably not only for standings) appears to be an internal/private API. Whether or not that's exactly the case, it's not stable across leagues.

For example, if I attempt to read my league's standings:

from fantraxapi import FantraxAPI
league_id = "e4z5zw9klprkle4z"
api = FantraxAPI(league_id)
print(api.standings())

I get:

Python 3.11.9 (main, Jul 15 2024, 10:44:16) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from fantraxapi import FantraxAPI
>>>
>>> league_id = "e4z5zw9klprkle4z"
>>>
>>> api = FantraxAPI(league_id)
>>>
>>> print(api.standings())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/geoff/junk/fantrax-explorer/venv/lib/python3.11/site-packages/fantraxapi/fantrax.py", line 112, in standings
    return Standings(self, response["tableList"][0]["rows"], week=week)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/geoff/junk/fantrax-explorer/venv/lib/python3.11/site-packages/fantraxapi/objs.py", line 250, in __init__
    self.ranks[int(rank)] = Record(self._api, team_id, rank, obj["cells"])
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/geoff/junk/fantrax-explorer/venv/lib/python3.11/site-packages/fantraxapi/objs.py", line 163, in __init__
    self.points = int(data[3]["content"])
                  ^^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: '.629'

whereas it works for the league in test.py

Python 3.11.9 (main, Jul 15 2024, 10:44:16) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from fantraxapi import FantraxAPI
>>> league_id = "yae6qgmoljsmydnu"
>>> api = FantraxAPI(league_id)
>>> print(api.standings())
Standings
1: Kashyyyk Wookies 🏴‍☠️ (20-2-0)
2: DraiH ymen (15-7-0)
3: Tage Against The Machine (14-8-0)
4: Rantanen With The Devil (13-9-0)
5: Bunch of Yahoos (12-10-0)
6: Maple leaving in the first (10-12-0)
7: MacKstreet Boys (10-12-0)
8: McBulge tease (9-13-0)
9: Dude Where’s Makar? (8-14-0)
10: Team Fantrax (8-14-0)
11: Former Ice Dancers (7-15-0)
12: Momma Ain't Raise No Bitch (6-16-0)

As the exception suggests, the problem is that the Record class expects the third item in the data array to have a content value that is an integral number of points, but with the league config in the one I was testing, the third item in that array is a float representing the team's winning percentage.

This could be reasonably solved in one of two ways:

  1. Use the fantrax public API instead. FantraxAPI_v1.pdf It provides less information but seems quite stable.

  2. Use the headers sent down in the other API to determine field presence/order. So, for example, in my error case, I get:

            "header": {
              "cells": [
                {
                  "sortDirection": 1,
                  "name": "Win",
                  "shortName": "W",
                  "key": "win"
                },
                {
                  "sortDirection": 1,
                  "name": "Loss",
                  "shortName": "L",
                  "key": "loss"
                },
                {
                  "sortDirection": 1,
                  "name": "Tie",
                  "shortName": "T",
                  "key": "tie"
                },
                {
                  "sortDirection": 1,
                  "name": "Win Percentage",
                  "shortName": "Win%",
                  "key": "winpc"
                },
                {
                  "sortDirection": 1,
                  "name": "Games Back",
                  "shortName": "GB",
                  "key": "gamesback"
                },
                {
                  "sortDirection": 1,
                  "name": "Total season fantasy points for",
                  "shortName": "FPtsF",
                  "key": "pointsFor"
                },
                {
                  "sortDirection": 1,
                  "name": "Total season fantasy points against",
                  "shortName": "FPtsA",
                  "key": "pointsAgainst"
                },
                {
                  "sortDirection": 1,
                  "name": "Streak",
                  "shortName": "Streak",
                  "key": "streak"
                }

before the rows.

Those keys could be used when assigning to fields as the response is being processed to avoid the problem I saw.

geoffbeier commented 4 months ago

Is this something you'd be interested in fixing?

I'm on the fence about what I'm going to do. I could:

  1. Take inspiration from your library and fork a version that does something similar to what you did, but for the public API described in the PDF I attached. I'd send a PR for this if you're interested.
  2. Take inspiration from your library and fork one that just hardcodes the field order in a way that matches my league's settings. I don't think that works as a PR.
  3. Write logic that uses the header row to figure out which column goes in which field. I'd send a PR for this if you're interested.

Obviously, (2) is easiest, but if you're interested in accepting a contribution along the lines of (1) or (3) I might be inclined to put something together.

meisnate12 commented 1 month ago

so i used the headers to have the right fields be populated for the object let me know if you have any other issues