Source code for waleg
from typing import Any, Dict, List, Tuple
import requests
from bs4 import BeautifulSoup, NavigableString, Tag
from wa_leg_api.exceptions import WaLegApiException
from wa_leg_api.make_stubs import snake_case
WSLSITE = "http://wslwebservices.leg.wa.gov"
def detect_array(maybe_array: Tag) -> bool:
"""An array is a tag where all the child tags have the same name"""
first_subtag = None
for count, item in enumerate(maybe_array):
if not isinstance(item, Tag):
continue
if first_subtag:
if item.name != first_subtag:
return False
else:
first_subtag = item.name
return True
def unpack_array(array: Tag, keydict: Dict[str, Any]) -> List[Any]:
"""Parse section of return where tag == arrayofsomething
into a list
Parameters
----------
array: bs4.Tag
Section of returned tree to be parsed as an array
Returns:
list of items
"""
answer = []
for item in array:
if not isinstance(item, Tag):
continue
answer.append(unpack_thing(item, keydict)[1])
return answer
def unpack_struct(struct: Tag, keydict: Dict[str, Any]) -> Dict[str, Any]:
"""Parse a tag with children, if tag is not arrayof....
Parameters
----------
struct: bs4.Tag
Section of returned tree to be parsed as a complex type
Returns
-------
Dict[str,Any]
The parsed section
"""
answer = {}
for item in struct:
if not isinstance(item, Tag):
continue
name, content = unpack_thing(item, keydict)
answer[name] = content
return answer
def bs4_string_decode(thing: Any) -> Any:
"""Parse a bs4 item to a str or bool type"""
if type(thing) is NavigableString:
string = thing.strip()
else:
string = str(thing)
# Active flag returned by get_legislation isn't
# in the schema and doesn't get parsed correctly
if string == "true":
return True
elif string == "false":
return False
else:
return string
def unpack_thing(thing: Tag, keydict: Dict[str, Any]) -> Tuple[str, Any]:
"""Parse a chunk of the returned data
Parameters
----------
thing: bs4.Tag
root node to unpack from
Returns
-------
name: str
Name of field unpacked
contents: Any
- str if it's a single item
- list if it's an array item
- dict if it's a more complex return type
"""
name = snake_case(thing.name)
if len(thing.contents) > 1:
# "votes" returned by legislation.get_roll_calls is also an array
if detect_array(thing):
return name, unpack_array(thing, keydict)
else:
return name, unpack_struct(thing, keydict)
else:
contents = thing.string
if contents is None:
return name, contents
typecaster = keydict.get(name, bs4_string_decode)
return name, typecaster(contents)
[docs]def call(service: str, function: str, argdict: Dict[str, Any], keydict: Dict[str, Any]) -> Dict[str, Any]:
"""This is the backend to all the stubs
service: str
Which service
function: str
Function request inside service
argdict: Dict
Arguments to the request
keydict: Dict
Dictionary of returned keys and functions to cast the key
the correct type (if the correct type for key is something
other than a string).
"""
url = f"{WSLSITE}/{service}Service.asmx/{function}"
response = requests.get(url, params=argdict)
if not response.ok:
raise WaLegApiException(response.status_code, response.reason, response.text, argdict)
body = BeautifulSoup(response.text, "xml")
answer = unpack_struct(body, keydict)
return answer
if __name__ == "__main__":
from wa_leg_api.sponsor import get_sponsors
result = get_sponsors("2019-20")