Utilities

YFPY module for managing complex JSON data structures.

Attributes:
  • logger (Logger) –

    Module level logger for usage and debugging.

retrieve_game_code_from_user

retrieve_game_code_from_user()

Recursive function to retrieve required Yahoo Fantasy Sports game code from user input.

Returns:
  • str( str ) –

    Yahoo Fantasy Sports game code ("nfl", "nhl", "mlb", or "nba").

Source code in yfpy/utils.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def retrieve_game_code_from_user() -> str:
    """Recursive function to retrieve required Yahoo Fantasy Sports game code from user input.

    Returns:
        str: Yahoo Fantasy Sports game code ("nfl", "nhl", "mlb", or "nba").

    """
    game_code = input(
        "YFPY requires the use of a Yahoo Fantasy Sports game code in order to select the correct sport. Please enter "
        "NFL, NHL, MLB, or NBA (case does not matter) here -> "
    )
    game_code = game_code.strip().lower()
    if game_code in yahoo_fantasy_sports_game_codes:
        return game_code
    else:
        logger.warning(f"Selection \"{game_code}\" is not a valid Yahoo Fantasy Sports game code. Please try again.")
        sleep(0.25)
        return retrieve_game_code_from_user()

complex_json_handler

complex_json_handler(obj)

Custom handler to allow custom YFPY objects to be serialized into JSON.

Parameters:
  • obj (Any) –

    Unserializable Python object to be serialized into JSON.

Returns:
  • Any( Any ) –

    Serializable version of the Python object.

Source code in yfpy/utils.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def complex_json_handler(obj: Any) -> Any:
    """Custom handler to allow custom YFPY objects to be serialized into JSON.

    Args:
        obj (Any): Unserializable Python object to be serialized into JSON.

    Returns:
        Any: Serializable version of the Python object.

    """
    if hasattr(obj, "serialized"):
        return obj.serialized()
    else:
        try:
            return str(obj, "utf-8")
        except TypeError:
            raise TypeError('Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj)))

jsonify_data

jsonify_data(data)

Function to serialize a YahooFantasyObject to a JSON string.

Parameters:
  • data (object) –

    YahooFantasyObject to be serialized to a JSON string.

Returns:
  • str( str ) –

    JSON string serialized from YahooFantasyObject.

Source code in yfpy/utils.py
65
66
67
68
69
70
71
72
73
74
75
def jsonify_data(data: object) -> str:
    """Function to serialize a YahooFantasyObject to a JSON string.

    Args:
        data (object): YahooFantasyObject to be serialized to a JSON string.

    Returns:
        str: JSON string serialized from YahooFantasyObject.

    """
    return json.dumps(data, indent=2, ensure_ascii=False, default=complex_json_handler)

jsonify_data_to_file

jsonify_data_to_file(data, data_file)

Function to serialize a YahooFantasyObject to JSON and output it to a file.

Parameters:
  • data (object) –

    YahooFantasyObject to be serialized to JSON and output to a file.

  • data_file (IO[str]) –

    Target filestream for writing the data.

Source code in yfpy/utils.py
78
79
80
81
82
83
84
85
86
def jsonify_data_to_file(data: object, data_file: IO[str]) -> None:
    """Function to serialize a YahooFantasyObject to JSON and output it to a file.

    Args:
        data (object): YahooFantasyObject to be serialized to JSON and output to a file.
        data_file (IO[str]): Target filestream for writing the data.

    """
    json.dump(data, data_file, indent=2, ensure_ascii=False, default=complex_json_handler)

prettify_data

prettify_data(data)

Function to return pretty formatted JSON strings for easily readable output from objects.

Parameters:
  • data (object) –

    Data object to be printed as an easily readable JSON string.

Returns:
  • str( str ) –

    JSON string that has been formatted with indents (two spaces).

Source code in yfpy/utils.py
89
90
91
92
93
94
95
96
97
98
99
def prettify_data(data: object) -> str:
    """Function to return pretty formatted JSON strings for easily readable output from objects.

    Args:
        data (object): Data object to be printed as an easily readable JSON string.

    Returns:
        str: JSON string that has been formatted with indents (two spaces).

    """
    return f"\n{jsonify_data(data)}\n"

unpack_data

unpack_data(json_obj, parent_class=None)

Recursive function to parse, clean, and assign custom data types to retrieved Yahoo Fantasy Sports data.

Parameters:
  • json_obj (Any) –

    JSON object for parsing (can be a dictionary, list, or primitive).

  • parent_class (Type, default: None ) –

    Parent class type used to extract custom subclass type options for casting.

Returns:
  • Any( Any ) –

    Recursively returns JSON objects until data is completely parsed, cleaned, and typed (where applicable).

Source code in yfpy/utils.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def unpack_data(json_obj: Any, parent_class: Type = None) -> Any:
    """Recursive function to parse, clean, and assign custom data types to retrieved Yahoo Fantasy Sports data.

    Args:
        json_obj (Any): JSON object for parsing (can be a dictionary, list, or primitive).
        parent_class (Type): Parent class type used to extract custom subclass type options for casting.

    Returns:
        Any: Recursively returns JSON objects until data is completely parsed, cleaned, and typed (where applicable).

    """
    # extract subclasses from parent class for typing
    subclasses = {}
    if parent_class:
        subclasses = {stringcase.snakecase(cls.__name__): cls for cls in parent_class.__subclasses__()}

    # discard empty lists and dictionaries and include when json value = 0
    if json_obj == 0 or json_obj:
        # handle lists
        if isinstance(json_obj, list):
            json_obj = [obj for obj in json_obj if (obj == 0 or obj)]

            if len(json_obj) == 1:
                return unpack_data(json_obj[0], parent_class)
            else:
                # flatten list of dicts if any objects in the list are dicts
                if any(isinstance(obj, dict) for obj in json_obj):
                    return flatten_json_dict_list(json_obj, parent_class)

                return [unpack_data(obj, parent_class) for obj in json_obj if (obj == 0 or obj)]

        # handle dictionaries
        elif isinstance(json_obj, dict):

            # eliminate odd single-key Yahoo dicts with key = "0" and value = <next layer of desired data>
            if "0" in json_obj.keys() and "1" not in json_obj.keys():
                if len(json_obj.keys()) == 1:
                    return unpack_data(json_obj.get("0"), parent_class)
                else:
                    if isinstance(json_obj.get("0"), dict):
                        json_obj.update(json_obj.pop("0"))

            # eliminate data obj counts (except in player_position dicts, which have position counts in league settings)
            if "count" in json_obj.keys() and "position" in json_obj.keys():
                # assign/cast data type where applicable
                # TODO: figure out how to do this without explicit object type keys
                return get_type(
                    {k: unpack_data(v, parent_class) for k, v in json_obj.items()},
                    parent_class,
                    subclasses
                )
            else:
                # assign/cast data type where applicable
                # TODO: figure out how to do this without explicit object type keys
                json_obj = get_type(
                    {k: unpack_data(v, parent_class) for k, v in json_obj.items() if k != "count"},
                    parent_class,
                    subclasses
                )

                # flatten dicts with keys "0", "1",..., "n" to a list of objects
                if "0" in json_obj.keys() and "1" in json_obj.keys():
                    json_obj = flatten_to_list(json_obj)
                # TODO: figure out how to do this without breaking the above unpacking using explicit type keys
                # else:
                #     # flatten dicts with redundant keys to a list of objects
                #     if len(json_obj.keys()) == 1 and len(json_obj.values()) == 1:
                #         key = list(json_obj.keys())[0]
                #         value = list(json_obj.values())[0]
                #         json_obj = value

                return json_obj
        else:
            return convert_strings_to_numeric_equivalents(json_obj)

convert_strings_to_numeric_equivalents

convert_strings_to_numeric_equivalents(json_obj)

Convert JSON strings with integer or float numeric representations to their respective integer or float values.

Parameters:
  • json_obj (Any) –

    JSON object (typically a dictionary or list, but can also be a primitive).

Returns:
  • Union[int, float, Any]

    int | float | Any: The numeric representation of any JSON strings that can be represented as integers or floats,

  • Union[int, float, Any]

    else the original JSON object.

Source code in yfpy/utils.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def convert_strings_to_numeric_equivalents(json_obj: Any) -> Union[int, float, Any]:
    """Convert JSON strings with integer or float numeric representations to their respective integer or float values.

    Args:
        json_obj (Any): JSON object (typically a dictionary or list, but can also be a primitive).

    Returns:
        int | float | Any: The numeric representation of any JSON strings that can be represented as integers or floats,
        else the original JSON object.

    """
    if isinstance(json_obj, str):

        if len(json_obj) > 1 and str.startswith(json_obj, "0"):
            return json_obj
        else:
            if str.isdigit(json_obj):
                return int(json_obj)
            elif str.isdigit(re.sub("[-]", "", re.sub("[.]", "", json_obj, count=1), count=1)):
                return float(json_obj)
            else:
                return json_obj
    else:
        return json_obj

get_type

get_type(json_obj_dict, parent_class, subclasses)

Cast JSON object to custom subclass type extracted from parent class.

Parameters:
  • json_obj_dict (dict of str) –

    Any): JSON dictionary with strings of data type as keys and JSON objects as values.

  • parent_class (Type) –

    Parent class from which to derive subclasses for casting.

  • subclasses (dict of str) –

    Type): Dictionary of subclasses with strings that match the json dict keys as keys and classes for casting as values.

Returns:
  • object( Dict[str, Any] ) –

    A Python object (representing the original JSON object) that has been cast to the specified type.

Source code in yfpy/utils.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
def get_type(json_obj_dict: Dict[str, Any], parent_class: Type, subclasses: Dict[str, Type]) -> Dict[str, Any]:
    """Cast JSON object to custom subclass type extracted from parent class.

    Args:
        json_obj_dict (dict of str: Any): JSON dictionary with strings of data type as keys and JSON objects as values.
        parent_class (Type): Parent class from which to derive subclasses for casting.
        subclasses (dict of str: Type): Dictionary of subclasses with strings that match the json dict keys as keys
            and classes for casting as values.

    Returns:
        object: A Python object (representing the original JSON object) that has been cast to the specified type.

    """
    for k, v in json_obj_dict.items():
        # check if key is in the provided subclasses' dict, that the object isn't already cast
        if k in subclasses.keys() and isinstance(v, dict) and not isinstance(v, subclasses.get(k)):
            json_obj_dict[k] = subclasses[k](unpack_data(v, parent_class))
    return json_obj_dict

flatten_json_dict_list

flatten_json_dict_list(json_obj_dict_list, parent_class)

Recursive function to flatten JSON lists containing all disparate JSON dictionaries with no overlapping keys.

Parameters:
  • json_obj_dict_list (list[dict[str, Any]]) –

    List of JSON dictionaries.

  • parent_class (Type) –

    Parent class type used to extract custom subclass type options.

Returns:
  • Any

    dict | list: Returns a dictionary if the list was flattened, else a cleaned list if no flattening was needed.

Source code in yfpy/utils.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
def flatten_json_dict_list(json_obj_dict_list: List[Dict[str, Any]], parent_class: Type) -> Any:
    """Recursive function to flatten JSON lists containing all disparate JSON dictionaries with no overlapping keys.

    Args:
        json_obj_dict_list (list[dict[str, Any]]): List of JSON dictionaries.
        parent_class (Type): Parent class type used to extract custom subclass type options.

    Returns:
        dict | list: Returns a dictionary if the list was flattened, else a cleaned list if no flattening was needed.

    """
    # filter out empty lists and dicts but include when value = 0
    json_obj_dict_list = [obj for obj in json_obj_dict_list if (obj == 0 or obj)]
    item_keys = []
    ndx = 0
    for item in json_obj_dict_list:
        if isinstance(item, list):
            flattened_item = flatten_json_dict_list(item, parent_class)
            json_obj_dict_list[ndx] = flattened_item
            item_keys.extend(list(flattened_item.keys()))
        else:
            item_keys.extend(list(item.keys()))
        ndx += 1

    if len(item_keys) == len(set(item_keys)):
        agg_dict = {}
        for dict_item in json_obj_dict_list:
            agg_dict.update(dict_item)

        return unpack_data(agg_dict, parent_class)
    else:
        return [unpack_data(obj, parent_class) for obj in json_obj_dict_list if (obj == 0 or obj)]

flatten_to_list

flatten_to_list(json_obj)

Function to flatten JSON dictionaries with unnecessary keys to a list of objects.

Parameters:
  • json_obj (Any) –

    JSON object (typically a dictionary or list, but can also be a primitive).

Returns:
  • list( Any ) –

    A list made from a flattened dictionary if json_obj was a dictionary, the original list if json_obj was a

  • Any

    list, or the original value if json_obj was a primitive.

Source code in yfpy/utils.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def flatten_to_list(json_obj: Any) -> Any:
    """Function to flatten JSON dictionaries with unnecessary keys to a list of objects.

    Args:
        json_obj (Any): JSON object (typically a dictionary or list, but can also be a primitive).

    Returns:
        list: A list made from a flattened dictionary if json_obj was a dictionary, the original list if json_obj was a
        list, or the original value if json_obj was a primitive.

    """
    if isinstance(json_obj, dict):
        out = []
        for k, v in json_obj.items():
            out.append(v)
        return out
    else:
        return json_obj

flatten_to_objects

flatten_to_objects(json_obj)

Function to flatten a JSON dictionary (or a JSON dictionary in a list) to a dictionary of cast objects.

Parameters:
  • json_obj (Any) –

    JSON object (typically a dictionary or list, but can also be a primitive).

Returns:
  • Any

    dict | list | int | float | str | bool: JSON dictionary/list/primitive with contents cast to Python objects.

Source code in yfpy/utils.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def flatten_to_objects(json_obj: Any) -> Any:
    """Function to flatten a JSON dictionary (or a JSON dictionary in a list) to a dictionary of cast objects.

    Args:
        json_obj (Any): JSON object (typically a dictionary or list, but can also be a primitive).

    Returns:
        dict | list | int | float | str | bool: JSON dictionary/list/primitive with contents cast to Python objects.

    """
    if isinstance(json_obj, dict):
        return dict_to_list(json_obj)
    elif isinstance(json_obj, list):
        if isinstance(json_obj[0], dict):
            return dict_to_list(json_obj[0])
    else:
        return json_obj

dict_to_list

dict_to_list(json_dict)

Function to convert a JSON dictionary to a list.

Parameters:
  • json_dict (dict[str, Any]) –

    JSON dictionary.

Returns:
  • list( Any ) –

    A list derived from a JSON dictionary, or the original dictionary if it does not contain dictionaries as

  • Any

    values.

Source code in yfpy/utils.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
def dict_to_list(json_dict: Dict[str, Any]) -> Any:
    """Function to convert a JSON dictionary to a list.

    Args:
        json_dict (dict[str, Any]): JSON dictionary.

    Returns:
        list: A list derived from a JSON dictionary, or the original dictionary if it does not contain dictionaries as
        values.

    """
    first_key = list(json_dict.keys())[0]
    if isinstance(json_dict.get(first_key), dict):
        first_val_key = list(json_dict.get(first_key).keys())[0]
        if first_key[:-1] == first_val_key:
            out = []
            for k, v in json_dict.items():
                out.append(v.get(first_val_key))
            return out
    return json_dict

reorganize_json_dict

reorganize_json_dict(json_dict, obj_key, val_to_key)

Function to reorganize a JSON dictionary of dictionaries.

The reorganized JSON dictionary is an ordered dictionary sorted by a specific attribute of the value dictionaries.

Parameters:
  • json_dict (dict of str) –

    Any): JSON dictionary.

  • obj_key (str) –

    Key to access the dictionaries contained in json_dict.

  • val_to_key (str) –

    Key used to sort the dictionaries contained in json_dict.

Returns:
  • Dict[str, Any]

    dict[str, Any]: An ordered dictionary of dictionaries sorted by val_to_key.

Source code in yfpy/utils.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
def reorganize_json_dict(json_dict: Dict[str, Any], obj_key: str, val_to_key: str) -> Dict[str, Any]:
    """Function to reorganize a JSON dictionary of dictionaries.

    The reorganized JSON dictionary is an ordered dictionary sorted by a specific attribute of the value dictionaries.

    Args:
        json_dict (dict of str: Any): JSON dictionary.
        obj_key (str): Key to access the dictionaries contained in json_dict.
        val_to_key (str): Key used to sort the dictionaries contained in json_dict.

    Returns:
        dict[str, Any]: An ordered dictionary of dictionaries sorted by val_to_key.

    """
    out = {}
    for k, v in json_dict.items():
        out[str(getattr(v.get(obj_key), val_to_key))] = v.get(obj_key)

    return OrderedDict(
        (str(k), out[str(k)]) for k in sorted(
            [int(k_v) if isinstance(k_v, int) else k_v for k_v in out.keys()]))

reformat_json_list

reformat_json_list(json_obj)

Function to clean and reformat JSON lists to eliminate empty values and unnecessarily nested lists.

Parameters:
  • json_obj (Any) –

    JSON object (typically a dictionary or list, but can also be a primitive) to be cleaned.

Returns:
  • Any( Any ) –

    Reformatted JSON list derived from original JSON object.

Source code in yfpy/utils.py
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
def reformat_json_list(json_obj: Any) -> Any:
    """Function to clean and reformat JSON lists to eliminate empty values and unnecessarily nested lists.

    Args:
        json_obj (Any): JSON object (typically a dictionary or list, but can also be a primitive) to be cleaned.

    Returns:
        Any: Reformatted JSON list derived from original JSON object.

    """
    if isinstance(json_obj[0], list):
        if len(json_obj) > 1:
            return reformat_json_list(
                [reformat_json_list(item) if isinstance(item, list) else item for item in json_obj])
        else:
            return reformat_json_list(json_obj[0])
    else:
        # create chain map that filters out empty lists/dicts but leaves objects where value = 0
        return ChainMap(*[value for value in json_obj if (value == 0 or value)])