# Python script that get a web API

I've written a program that gets the web API endpoint for some functions on a website. I'm just wondering if you guys see anything I could do to improve the code at all.

import sys
import requests
import dateutil

class KenoBase:
'''
Methods that are to be inherited to 'KenoAPI' class.
Do not use this class, instead use 'KenoAPI''.
'''

def __init__(self, state="NT"):
self.__state__ = state.upper()
self.__states__ = ["ACT", "NSW", "QLD", "VIC", "WA", "NT", "SA", "TAS"]
self.__base_url__ = "https://api-info-{}.keno.com.au".format(
self.__state_redirect__.lower())

'''
Private Method:
concatenates the base URL and the endpoint together a
'''
end_point = str(end_point)
params = "?jurisdiction={}".format(
complete_url = self.__base_url__ + end_point + params

return str(complete_url)

@property
def __state_redirect__(self):
'''
Private Method:
redirects user input
'''
if any(x == self.__state__ for x in self.__states__) is False:
return sys.exit(str("Check state input: '{}' - is invalid").format(
self.__state__))

if self.__state__ == self.__states__[4]:
print("Keno is not available in WA-Automatically changed to NSW")
self.__state__ = self.__states__[2]
return self.__state__

redirect = [self.__states__[5], self.__states__[6], self.__states__[7]]

if any(x == self.__state__ for x in redirect) is True:
print(str("Keno is not available in '{}', this state uses ACT ").format(
self.__state__))
self.__state__ = self.__states__[0]
return self.__state__

return self.__state__

# noinspection PyDefaultArgument
@staticmethod
'''
Private Method:
this function speeds up the lookup times for nested dictionaries
'''

def __transform_time__(self, _datetime):
pass
'''
Private Method:
Transforms a date in a datetime object with the correct
time information, it also factors in daylight savings

currently working on adding tz and dst to this function
'''
return dateutil.parser.isoparse(_datetime).strftime("%Y-%m-%d %H:%M:%S.%f")

def __results_selection__(self, initial_draw=1,
total_draws=1, start_date="2021-02-08", page_size=1, page_number=1):
'''
Private Method:

initial_draw: the first game number
total_draws: how many games you want to select
start_date: which date you would like to get the games from
page_size: how many games are on each page
page_number: if your page size is less than the total draws the games will be split among multiple pages

Min adn Max values for a each parameter
game_number: Min: 0, Max: 999

Number of Games: Min:1, Max:200

page_size: Min:1, Max:200

page_number: Min:1, Max:100
'''

initial_draw, total_draws, start_date, page_size, page_number)
return dict(requests.get(url).json())

class KenoAPI(KenoBase):
'''
Has all possible URL endpoints.
'''

def __init__(self, state):
super().__init__(state)

def game_status(self):
'''
Public Method:
Desc: Retrieves information about the current and next game
'''
retrieved = dict(requests.get(url).json())

status_current = {
}

status_selling = {
}

status = {
"state": self.__state__,
"current_game": status_current,
"next_game": status_selling
}

return status

def live_draw(self):
'''
Public Method:
Desc: Retrieves data from the current draw
'''
retrieved = dict(requests.get(url).json().get("current"))
status = str(retrieved.get("_type")).split(".")
status_type = status[-1]

live_draw = {
"state": self.__state__,
"game_number": retrieved.get("game-number"),
"status": status_type,
"started_at": self.__transform_time__(_datetime=retrieved.get("closed")),
"is_finished": None,
"draw_numbers": retrieved.get("draw"),
}

if retrieved.get("_type") == "application/vnd.tabcorp.keno.game.complete":
live_draw.update({"is_finished": bool(True)})

else:
live_draw.update({"is_finished": bool(False)})

return live_draw

def jackpot(self):
'''
Public Method:
Desc: Retrieves MegaMillions(leveraged) and Regular jackpots
'''
url = self.__get_url__(
retrieved = dict(requests.get(url).json())["jackpots"]

jackpot_regular = {
}

jackpot_leveraged = {
}

jackpot_combined = {
"state": self.__state__,
"regular": jackpot_regular,
"leveraged": jackpot_leveraged
}

return jackpot_combined

def hot_cold(self):
'''
Public Method:
Desc: Retrieves trending numbers which are defined the official keno website
'''
url = self.__get_url__(
retrieved = dict(requests.get(url).json())

hot_cold = {
"cold": retrieved.get("coldNumbers"),
"hot": retrieved.get("hotNumbers"),
"state": self.__state__
}
return hot_cold

• Can you show how this is invoked? Jul 9, 2021 at 15:59
• Calling KenoAPI('NT').game_status() fails - you're not checking for the success of your get, but it's returning a 403. Jul 9, 2021 at 16:06
• In fact the site seems entirely down, as I get a 403 no matter what URL I visit Jul 9, 2021 at 16:24
• Hey, I'm not too sure what happened to the website, When I posted this everything was working fine. Seems like bad luck that it happened when it did. Jul 10, 2021 at 1:17

Aside the fact that I can't test anything due to the site being down, the code has some issues that could be cleaned up:

• It's not clear why KenoBase exists - there's only one child, and I don't see this buying you any useful abstraction
• You need to get out of the habit of __decorating__ your members. Double underscores are reserved for name mangling. Private variables in Python are very much "by convention" instead of being enforced, and use a single leading underscore rather than leading and trailing dunders.
• The params = "?jurisdiction={}" setup you do is unneeded, and should be rewritten as a params kwarg dict passed into get() rather than being baked into the URL
• str(complete_url) is redundant; that's already a string
• if any(...) is False should be if not any(...); and if any(...) is True should be if any(...)
• Consider replacing your format() calls with interpolated f-strings
• return sys.exit makes no sense. exit will raise an exception meant to cut through the stack and terminate the program, and so certainly the return will not happen.
• key=dict is a mystery. You're setting the default of the key argument to be a type. If you allow the default to be set, then key.get() is going to explode.
• The pass in transform_time can be deleted
• There is no need to re-cast the result of json() into a dict; it's already a dict

Lines like this:

dict(requests.get(url).json())


should be

with requests.get(url) as response:
response.raise_for_status()
retrieved = response.json()


otherwise, when the site fails (as it is now with a 403) your application will not fail as early as it should, and the error will be much less clear.

• Hey, thanks for all the useful pointers, regarding the private methods, is there any way I can make them so they can only be used inside the class, for example when KenoAPI("NT") is called I don't want the user to have access to state_redirect Jul 10, 2021 at 1:23
• Not really, no. Convention is that privates are marked with one underscore, but they can still be accessed. Jul 10, 2021 at 3:14
• I found something regarding this, using '__decorating' hides the function or variable when calling it outside the class. Jul 10, 2021 at 15:08
• Don't do it. Read stackoverflow.com/questions/7456807/python-name-mangling Jul 10, 2021 at 15:21