3 votes

Python Loops for JSON

How the heck do these work? I've cobbled together the script below for my bot (using Limnoria) to return the F1 standings in a line. Right now it returns the first value perfectly, but I can't figure out the loop to get the other drivers at all.

The script
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import supybot.ircmsgs as ircmsgs
import requests
import os
import collections
import json

try:
    from supybot.i18n import PluginInternationalization

    _ = PluginInternationalization("F1")
except ImportError:
    _ = lambda x: x


class F1(callbacks.Plugin):
    """Uses API to retrieve information"""

    threaded = True

    def f1(self, irc, msg, args):
        """
        F1 Standings
        """

        channel = msg.args[0]
        data = requests.get("https://ergast.com/api/f1/2022/driverStandings.json")
        data = json.loads(data.content)["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"][0]

        name = data["Driver"]["code"]
        position = data["positionText"].zfill(2)
        points = data["points"]

        output = ", ".join(['\x0306\x02' + name + '\x0303' + " [" + position + ", "+ points + "]"])
        irc.reply(output)

    result = wrap(f1)

Class = F1

The output should be

VER [1, 125], LEC [2, 116], PER [3, 110], RUS [4, 84], SAI [5, 83], HAM [6, 50], NOR [7, 48], BOT [8, 40], OCO [9, 30], MAG [10, 15], RIC [11, 11], TSU [12, 11], ALO [13, 10], GAS [14, 6], VET [15, 5], ALB [16, 3], STR [17, 2], ZHO [18, 1], MSC [19, 0], HUL [20, 0], LAT [21, 0]

...but it only returns VER [1, 125]

I'm in that state where I can read the stuff, but when I put it together, it doesn't always work.

Final script
# this accepts @champ or @constructor with an optional year
# and also @gp with an optional race number for the current season


import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import supybot.ircmsgs as ircmsgs
import requests
import os
import collections
import json

try:
    from supybot.i18n import PluginInternationalization

    _ = PluginInternationalization("F1")
except ImportError:
    _ = lambda x: x


class F1(callbacks.Plugin):
    """Uses API to retrieve information"""

    threaded = True

    def champ(self, irc, msg, args, year):
        """<year>
        Call standings by year
        F1 Standings
        """

        data = requests.get("https://ergast.com/api/f1/current/driverStandings.json")
        if year:
            data = requests.get(
                "https://ergast.com/api/f1/%s/driverStandings.json" % (year)
            )
        driver_standings = json.loads(data.content)["MRData"]["StandingsTable"][
            "StandingsLists"
        ][0]["DriverStandings"]
        string_segments = []

        for driver in driver_standings:
            name = driver["Driver"]["code"]
            position = driver["positionText"]
            points = driver["points"]
            string_segments.append(f"\x035{name}\x0F {points}")

        irc.reply(", ".join(string_segments))

    champ = wrap(champ, [optional("int")])

    def gp(self, irc, msg, args, race):
        """<year>
        Call standings by year
        F1 Standings
        """

        data = requests.get("https://ergast.com/api/f1/current/last/results.json")
        if race:
            data = requests.get(
                "https://ergast.com/api/f1/current/%s/results.json" % (race)
            )
        driver_result = json.loads(data.content)["MRData"]["RaceTable"]["Races"][0][
            "Results"
        ]
        string_segments = []

        for driver in driver_result:
            name = driver["Driver"]["code"]
            position = driver["positionText"]
            points = driver["points"]
            string_segments.append(f"{position} \x035{name}\x0F {points}")

        irc.reply(", ".join(string_segments))

    gp = wrap(gp, [optional("int")])

    def constructor(self, irc, msg, args, year):
        """<year>
        Call standings by year
        F1 Standings
        """

        data = requests.get(
            "https://ergast.com/api/f1/current/constructorStandings.json"
        )
        if year:
            data = requests.get(
                "https://ergast.com/api/f1/current/constructorStandings.json" % (year)
            )
        driver_result = json.loads(data.content)["MRData"]["StandingsTable"][
            "StandingsLists"
        ][0]["ConstructorStandings"]
        string_segments = []

        for driver in driver_result:
            name = driver["Constructor"]["name"]
            position = driver["positionText"]
            points = driver["points"]
            string_segments.append(f"{position} \x035{name}\x0F {points}")

        irc.reply(", ".join(string_segments))

    constructor = wrap(constructor, [optional("int")])


Class = F1

6 comments

  1. [4]
    TonyLozano
    Link
    So I think the problem is on the line data = json.loads(data.content)["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"][0] You only grab a single driver at the end when you wrote...

    So I think the problem is on the line

    data = json.loads(data.content)["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"][0]
    

    You only grab a single driver at the end when you wrote [0].

    Here is a version that loops over the standings.

    import json
    
    import requests as requests
    
    data = requests.get("https://ergast.com/api/f1/2022/driverStandings.json")
    driver_standings = json.loads(data.content)["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"]
    string_segments = []
    for driver in driver_standings:
        name = driver["Driver"]["code"]
        position = driver["positionText"].zfill(2)
        points = driver["points"]
        string_segments.append(f'\x0306\x02{name}\x0303[{position}, {points}]')
    print(", ".join(string_segments))
    

    Output:

    ?06?VER?03[01, 125], ?06?LEC?03[02, 116], ?06?PER?03[03, 110], ?06?RUS?03[04, 84], ?06?SAI?03[05, 83], ?06?HAM?03[06, 50], ?06?NOR?03[07, 48], ?06?BOT?03[08, 40], ?06?OCO?03[09, 30], ?06?MAG?03[10, 15], ?06?RIC?03[11, 11], ?06?TSU?03[12, 11], ?06?ALO?03[13, 10], ?06?GAS?03[14, 6], ?06?VET?03[15, 5], ?06?ALB?03[16, 3], ?06?STR?03[17, 2], ?06?ZHO?03[18, 1], ?06?MSC?03[19, 0], ?06?HUL?03[20, 0], ?06?LAT?03[21, 0]
    

    Not sure what you are doing with the \x0306 stuff, but I left it in. Output without that:

    VER [01, 125], LEC [02, 116], PER [03, 110], RUS [04, 84], SAI [05, 83], HAM [06, 50], NOR [07, 48], BOT [08, 40], OCO [09, 30], MAG [10, 15], RIC [11, 11], TSU [12, 11], ALO [13, 10], GAS [14, 6], VET [15, 5], ALB [16, 3], STR [17, 2], ZHO [18, 1], MSC [19, 0], HUL [20, 0], LAT [21, 0]
    
    5 votes
    1. [2]
      TonyLozano
      Link Parent
      also fun tip, the requests library will handle the json parsing for you when the HTTP response's content type is application/json, like this: import requests response =...

      also fun tip, the requests library will handle the json parsing for you when the HTTP response's content type is application/json, like this:

      import requests
      
      response = requests.get("https://ergast.com/api/f1/2022/driverStandings.json")
      driver_standings = response.json()["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"]
      
      6 votes
      1. bhrgunatha
        Link Parent
        lol import antigravity indeed

        lol

        import antigravity 
        

        indeed

        2 votes
    2. tomf
      Link Parent
      holy shit! that works like a charm! The \x0306 etc are mirc color codes. Here's the output, which needs some tweaking. Thanks so much for this! The loop makes more sense now. Thanks so much for this!

      holy shit! that works like a charm! The \x0306 etc are mirc color codes. Here's the output, which needs some tweaking.

      Thanks so much for this! The loop makes more sense now. Thanks so much for this!

      1 vote
  2. [2]
    DataWraith
    Link
    I think the last [0]in the data = json.loads(data.content)["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"][0] line is too much. You're selecting the first element of that array...

    I think the last [0]in the data = json.loads(data.content)["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"][0] line is too much.

    You're selecting the first element of that array instead of looping over the entire array.

    The following worked for me:

    import requests
    import json
    
    data = requests.get("https://ergast.com/api/f1/2022/driverStandings.json")
    data = json.loads(data.content)
    
    for driver_standing in data["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"]:
        name = driver_standing["Driver"]["code"]
        position = driver_standing["positionText"].zfill(2)
        points = driver_standing["points"]
    
        print(name, position, points)
    

    The JSON is a bit hairy -- it gets a lot easier to analyze if you pretty-print it using the jq utility: jq . example.json (the period before the filename tells it to pretty-print).

    3 votes
    1. tomf
      Link Parent
      thanks so much for this. I'm definitely going to look into jq.

      thanks so much for this. I'm definitely going to look into jq.

      1 vote