uk_bin_collection 0.135.4__py3-none-any.whl → 0.137.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uk_bin_collection/tests/input.json +29 -4
 - uk_bin_collection/uk_bin_collection/councils/CambridgeCityCouncil.py +61 -0
 - uk_bin_collection/uk_bin_collection/councils/CanterburyCityCouncil.py +6 -1
 - uk_bin_collection/uk_bin_collection/councils/CastlepointDistrictCouncil.py +25 -32
 - uk_bin_collection/uk_bin_collection/councils/GooglePublicCalendarCouncil.py +38 -0
 - uk_bin_collection/uk_bin_collection/councils/HinckleyandBosworthBoroughCouncil.py +1 -0
 - uk_bin_collection/uk_bin_collection/councils/SouthamptonCityCouncil.py +52 -0
 - uk_bin_collection/uk_bin_collection/councils/SpelthorneBoroughCouncil.py +161 -0
 - uk_bin_collection/uk_bin_collection/councils/WakefieldCityCouncil.py +3 -1
 - uk_bin_collection/uk_bin_collection/councils/WalthamForest.py +8 -9
 - {uk_bin_collection-0.135.4.dist-info → uk_bin_collection-0.137.0.dist-info}/METADATA +35 -36
 - {uk_bin_collection-0.135.4.dist-info → uk_bin_collection-0.137.0.dist-info}/RECORD +15 -11
 - {uk_bin_collection-0.135.4.dist-info → uk_bin_collection-0.137.0.dist-info}/LICENSE +0 -0
 - {uk_bin_collection-0.135.4.dist-info → uk_bin_collection-0.137.0.dist-info}/WHEEL +0 -0
 - {uk_bin_collection-0.135.4.dist-info → uk_bin_collection-0.137.0.dist-info}/entry_points.txt +0 -0
 
| 
         @@ -162,11 +162,10 @@ 
     | 
|
| 
       162 
162 
     | 
    
         
             
                    "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
         
     | 
| 
       163 
163 
     | 
    
         
             
                },
         
     | 
| 
       164 
164 
     | 
    
         
             
                "BexleyCouncil": {
         
     | 
| 
       165 
     | 
    
         
            -
                    "house_number": "1 
     | 
| 
      
 165 
     | 
    
         
            +
                    "house_number": "1",
         
     | 
| 
       166 
166 
     | 
    
         
             
                    "postcode": "DA5 3AH",
         
     | 
| 
       167 
167 
     | 
    
         
             
                    "skip_get_url": true,
         
     | 
| 
       168 
     | 
    
         
            -
                    " 
     | 
| 
       169 
     | 
    
         
            -
                    "url": "https://mybexley.bexley.gov.uk/service/When_is_my_collection_day",
         
     | 
| 
      
 168 
     | 
    
         
            +
                    "url": "https://waste.bexley.gov.uk/waste",
         
     | 
| 
       170 
169 
     | 
    
         
             
                    "web_driver": "http://selenium:4444",
         
     | 
| 
       171 
170 
     | 
    
         
             
                    "wiki_name": "Bexley Council",
         
     | 
| 
       172 
171 
     | 
    
         
             
                    "wiki_note": "In order to use this parser, you will need to sign up to [Bexley's @Home app](https://www.bexley.gov.uk/services/rubbish-and-recycling/bexley-home-recycling-app/about-app). Complete the setup by entering your email and setting your address with postcode and address line. Once you can see the calendar, you should be good to run the parser. Just pass the email you used in quotes in the UPRN parameter."
         
     | 
| 
         @@ -345,6 +344,12 @@ 
     | 
|
| 
       345 
344 
     | 
    
         
             
                    "wiki_name": "Calderdale Council",
         
     | 
| 
       346 
345 
     | 
    
         
             
                    "wiki_note": "Pass the UPRN and postcode. To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
         
     | 
| 
       347 
346 
     | 
    
         
             
                },
         
     | 
| 
      
 347 
     | 
    
         
            +
                "CambridgeCityCouncil": {
         
     | 
| 
      
 348 
     | 
    
         
            +
                    "uprn": "200004159750",
         
     | 
| 
      
 349 
     | 
    
         
            +
                    "url": "https://www.cambridge.gov.uk/",
         
     | 
| 
      
 350 
     | 
    
         
            +
                    "wiki_name": "Cambridge City Council",
         
     | 
| 
      
 351 
     | 
    
         
            +
                    "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
         
     | 
| 
      
 352 
     | 
    
         
            +
                },
         
     | 
| 
       348 
353 
     | 
    
         
             
                "CannockChaseDistrictCouncil": {
         
     | 
| 
       349 
354 
     | 
    
         
             
                    "postcode": "WS15 1JA",
         
     | 
| 
       350 
355 
     | 
    
         
             
                    "skip_get_url": true,
         
     | 
| 
         @@ -827,7 +832,13 @@ 
     | 
|
| 
       827 
832 
     | 
    
         
             
                    "wiki_name": "Gloucester City Council",
         
     | 
| 
       828 
833 
     | 
    
         
             
                    "wiki_note": "Pass the house number, postcode, and UPRN in their respective parameters. This parser requires a Selenium webdriver."
         
     | 
| 
       829 
834 
     | 
    
         
             
                },
         
     | 
| 
       830 
     | 
    
         
            -
                " 
     | 
| 
      
 835 
     | 
    
         
            +
                "GooglePublicCalendarCouncil": {
         
     | 
| 
      
 836 
     | 
    
         
            +
                    "wiki_name": "Google Calendar (Public)",
         
     | 
| 
      
 837 
     | 
    
         
            +
                    "url": "https://calendar.google.com/calendar/ical/c_bca6c975545213622b3958bddab567e1e7398709c63e377b6fe9a9140bd1caf8%40group.calendar.google.com/public/basic.ics",
         
     | 
| 
      
 838 
     | 
    
         
            +
                    "skip_get_url": true,
         
     | 
| 
      
 839 
     | 
    
         
            +
                    "wiki_note": "The URL should be the public ics file URL for the public Google calendar. See https://support.google.com/calendar/answer/37083?sjid=7202815583021446882-EU. Councils that currently need this are Trafford."
         
     | 
| 
      
 840 
     | 
    
         
            +
                },
         
     | 
| 
      
 841 
     | 
    
         
            +
                    "GraveshamBoroughCouncil": {
         
     | 
| 
       831 
842 
     | 
    
         
             
                    "skip_get_url": true,
         
     | 
| 
       832 
843 
     | 
    
         
             
                    "uprn": "100060927046",
         
     | 
| 
       833 
844 
     | 
    
         
             
                    "url": "https://www.gravesham.gov.uk",
         
     | 
| 
         @@ -1647,6 +1658,13 @@ 
     | 
|
| 
       1647 
1658 
     | 
    
         
             
                    "wiki_name": "Somerset Council",
         
     | 
| 
       1648 
1659 
     | 
    
         
             
                    "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
         
     | 
| 
       1649 
1660 
     | 
    
         
             
                },
         
     | 
| 
      
 1661 
     | 
    
         
            +
                "SouthamptonCityCouncil": {
         
     | 
| 
      
 1662 
     | 
    
         
            +
                    "skip_get_url": true,
         
     | 
| 
      
 1663 
     | 
    
         
            +
                    "uprn": "100060731893",
         
     | 
| 
      
 1664 
     | 
    
         
            +
                    "url": "https://www.southampton.gov.uk",
         
     | 
| 
      
 1665 
     | 
    
         
            +
                    "wiki_name": "Southampton City Council",
         
     | 
| 
      
 1666 
     | 
    
         
            +
                    "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
         
     | 
| 
      
 1667 
     | 
    
         
            +
                },
         
     | 
| 
       1650 
1668 
     | 
    
         
             
                "SouthAyrshireCouncil": {
         
     | 
| 
       1651 
1669 
     | 
    
         
             
                    "postcode": "KA19 7BN",
         
     | 
| 
       1652 
1670 
     | 
    
         
             
                    "skip_get_url": true,
         
     | 
| 
         @@ -1740,6 +1758,13 @@ 
     | 
|
| 
       1740 
1758 
     | 
    
         
             
                    "wiki_name": "Southwark Council",
         
     | 
| 
       1741 
1759 
     | 
    
         
             
                    "wiki_note": "Replace `XXXXXXXX` with your UPRN. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
         
     | 
| 
       1742 
1760 
     | 
    
         
             
                },
         
     | 
| 
      
 1761 
     | 
    
         
            +
                "SpelthorneBoroughCouncil": {
         
     | 
| 
      
 1762 
     | 
    
         
            +
                    "house_number": "1",
         
     | 
| 
      
 1763 
     | 
    
         
            +
                    "postcode": "TW18 2PR",
         
     | 
| 
      
 1764 
     | 
    
         
            +
                    "skip_get_url": true,
         
     | 
| 
      
 1765 
     | 
    
         
            +
                    "url": "https://www.spelthorne.gov.uk",
         
     | 
| 
      
 1766 
     | 
    
         
            +
                    "wiki_name": "Spelthorne Borough Council"
         
     | 
| 
      
 1767 
     | 
    
         
            +
                },
         
     | 
| 
       1743 
1768 
     | 
    
         
             
                "StAlbansCityAndDistrictCouncil": {
         
     | 
| 
       1744 
1769 
     | 
    
         
             
                    "skip_get_url": true,
         
     | 
| 
       1745 
1770 
     | 
    
         
             
                    "uprn": "100081153583",
         
     | 
| 
         @@ -0,0 +1,61 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import time
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            import requests
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.common import *
         
     | 
| 
      
 6 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            # import the wonderful Beautiful Soup and the URL grabber
         
     | 
| 
      
 10 
     | 
    
         
            +
            class CouncilClass(AbstractGetBinDataClass):
         
     | 
| 
      
 11 
     | 
    
         
            +
                """
         
     | 
| 
      
 12 
     | 
    
         
            +
                Concrete classes have to implement all abstract operations of the
         
     | 
| 
      
 13 
     | 
    
         
            +
                base class. They can also override some operations with a default
         
     | 
| 
      
 14 
     | 
    
         
            +
                implementation.
         
     | 
| 
      
 15 
     | 
    
         
            +
                """
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def parse_data(self, page: str, **kwargs) -> dict:
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    user_uprn = kwargs.get("uprn")
         
     | 
| 
      
 20 
     | 
    
         
            +
                    check_uprn(user_uprn)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    bindata = {"bins": []}
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    URI = f"https://servicelayer3c.azure-api.net/wastecalendar/collection/search/{user_uprn}/?authority=CCC/?numberOfCollections=255"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    # Make the GET request
         
     | 
| 
      
 26 
     | 
    
         
            +
                    response = requests.get(URI)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    # Parse the JSON response
         
     | 
| 
      
 29 
     | 
    
         
            +
                    bin_collection = response.json()
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    # Loop through each collection in bin_collection
         
     | 
| 
      
 32 
     | 
    
         
            +
                    for collection in bin_collection["collections"]:
         
     | 
| 
      
 33 
     | 
    
         
            +
                        bin_types = collection["roundTypes"]
         
     | 
| 
      
 34 
     | 
    
         
            +
                        collection_date = collection["date"]
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                        # Loop through the dates for each collection type
         
     | 
| 
      
 37 
     | 
    
         
            +
                        for bin_type in bin_types:
         
     | 
| 
      
 38 
     | 
    
         
            +
                            # print(f"Bin Type: {bin_type}")
         
     | 
| 
      
 39 
     | 
    
         
            +
                            # print(f"Collection Date: {collection_date}")
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                            if bin_type == "ORGANIC":
         
     | 
| 
      
 42 
     | 
    
         
            +
                                bin_type = "Green Bin"
         
     | 
| 
      
 43 
     | 
    
         
            +
                            if bin_type == "RECYCLE":
         
     | 
| 
      
 44 
     | 
    
         
            +
                                bin_type = "Blue Bin"
         
     | 
| 
      
 45 
     | 
    
         
            +
                            if bin_type == "DOMESTIC":
         
     | 
| 
      
 46 
     | 
    
         
            +
                                bin_type = "Black Bin"
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                            dict_data = {
         
     | 
| 
      
 49 
     | 
    
         
            +
                                "type": bin_type,
         
     | 
| 
      
 50 
     | 
    
         
            +
                                "collectionDate": datetime.strptime(
         
     | 
| 
      
 51 
     | 
    
         
            +
                                    collection_date,
         
     | 
| 
      
 52 
     | 
    
         
            +
                                    "%Y-%m-%dT%H:%M:%SZ",
         
     | 
| 
      
 53 
     | 
    
         
            +
                                ).strftime("%d/%m/%Y"),
         
     | 
| 
      
 54 
     | 
    
         
            +
                            }
         
     | 
| 
      
 55 
     | 
    
         
            +
                            bindata["bins"].append(dict_data)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    bindata["bins"].sort(
         
     | 
| 
      
 58 
     | 
    
         
            +
                        key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
         
     | 
| 
      
 59 
     | 
    
         
            +
                    )
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    return bindata
         
     | 
| 
         @@ -22,12 +22,17 @@ class CouncilClass(AbstractGetBinDataClass): 
     | 
|
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                    data = {"uprn": user_uprn, "usrn": "1"}
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
      
 25 
     | 
    
         
            +
                    headers = {
         
     | 
| 
      
 26 
     | 
    
         
            +
                        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
         
     | 
| 
      
 27 
     | 
    
         
            +
                        "Accept": "application/json",
         
     | 
| 
      
 28 
     | 
    
         
            +
                    }
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       25 
30 
     | 
    
         
             
                    URI = (
         
     | 
| 
       26 
31 
     | 
    
         
             
                        "https://zbr7r13ke2.execute-api.eu-west-2.amazonaws.com/Beta/get-bin-dates"
         
     | 
| 
       27 
32 
     | 
    
         
             
                    )
         
     | 
| 
       28 
33 
     | 
    
         | 
| 
       29 
34 
     | 
    
         
             
                    # Make the GET request
         
     | 
| 
       30 
     | 
    
         
            -
                    response = requests.post(URI, json=data)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    response = requests.post(URI, json=data, headers=headers)
         
     | 
| 
       31 
36 
     | 
    
         
             
                    response.raise_for_status()
         
     | 
| 
       32 
37 
     | 
    
         | 
| 
       33 
38 
     | 
    
         
             
                    # Parse the JSON response
         
     | 
| 
         @@ -1,4 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            from bs4 import BeautifulSoup
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       2 
3 
     | 
    
         
             
            from uk_bin_collection.uk_bin_collection.common import *
         
     | 
| 
       3 
4 
     | 
    
         
             
            from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
         @@ -25,7 +26,7 @@ class CouncilClass(AbstractGetBinDataClass): 
     | 
|
| 
       25 
26 
     | 
    
         
             
                    uprn = kwargs.get("uprn")
         
     | 
| 
       26 
27 
     | 
    
         
             
                    check_uprn(uprn)
         
     | 
| 
       27 
28 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                    post_url = "https://apps.castlepoint.gov.uk/cpapps/index.cfm?fa= 
     | 
| 
      
 29 
     | 
    
         
            +
                    post_url = "https://apps.castlepoint.gov.uk/cpapps/index.cfm?fa=myStreet.displayDetails"
         
     | 
| 
       29 
30 
     | 
    
         
             
                    post_header_str = (
         
     | 
| 
       30 
31 
     | 
    
         
             
                        "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,"
         
     | 
| 
       31 
32 
     | 
    
         
             
                        "image/apng,"
         
     | 
| 
         @@ -41,7 +42,7 @@ class CouncilClass(AbstractGetBinDataClass): 
     | 
|
| 
       41 
42 
     | 
    
         
             
                    )
         
     | 
| 
       42 
43 
     | 
    
         | 
| 
       43 
44 
     | 
    
         
             
                    post_headers = parse_header(post_header_str)
         
     | 
| 
       44 
     | 
    
         
            -
                    form_data = {" 
     | 
| 
      
 45 
     | 
    
         
            +
                    form_data = {"USRN": uprn}
         
     | 
| 
       45 
46 
     | 
    
         
             
                    post_response = requests.post(
         
     | 
| 
       46 
47 
     | 
    
         
             
                        post_url, headers=post_headers, data=form_data, verify=False
         
     | 
| 
       47 
48 
     | 
    
         
             
                    )
         
     | 
| 
         @@ -53,36 +54,28 @@ class CouncilClass(AbstractGetBinDataClass): 
     | 
|
| 
       53 
54 
     | 
    
         
             
                    data = {"bins": []}
         
     | 
| 
       54 
55 
     | 
    
         
             
                    collection_tuple = []
         
     | 
| 
       55 
56 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
                     
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                        ). 
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                            )
         
     | 
| 
       79 
     | 
    
         
            -
                            collection_tuple.append(("Pink collection", collection_date))
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
                        for day in black_days:
         
     | 
| 
       82 
     | 
    
         
            -
                            collection_date = datetime(
         
     | 
| 
       83 
     | 
    
         
            -
                                year=int(year), month=int(month), day=int(day)
         
     | 
| 
       84 
     | 
    
         
            -
                            )
         
     | 
| 
       85 
     | 
    
         
            -
                            collection_tuple.append(("Normal collection", collection_date))
         
     | 
| 
      
 57 
     | 
    
         
            +
                    calendar = soup.find("table", class_="calendar")
         
     | 
| 
      
 58 
     | 
    
         
            +
                    month = datetime.strptime(
         
     | 
| 
      
 59 
     | 
    
         
            +
                        soup.find("div", class_="calMonthCurrent").get_text(), "[%b]"
         
     | 
| 
      
 60 
     | 
    
         
            +
                    ).strftime("%m")
         
     | 
| 
      
 61 
     | 
    
         
            +
                    year = datetime.strptime(
         
     | 
| 
      
 62 
     | 
    
         
            +
                        soup.find("h1").get_text(), "About my Street - %B %Y"
         
     | 
| 
      
 63 
     | 
    
         
            +
                    ).strftime("%Y")
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    pink_days = [
         
     | 
| 
      
 66 
     | 
    
         
            +
                        day.get_text().strip() for day in calendar.find_all("td", class_="pink")
         
     | 
| 
      
 67 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 68 
     | 
    
         
            +
                    black_days = [
         
     | 
| 
      
 69 
     | 
    
         
            +
                        day.get_text().strip() for day in calendar.find_all("td", class_="normal")
         
     | 
| 
      
 70 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    for day in pink_days:
         
     | 
| 
      
 73 
     | 
    
         
            +
                        collection_date = datetime(year=int(year), month=int(month), day=int(day))
         
     | 
| 
      
 74 
     | 
    
         
            +
                        collection_tuple.append(("Pink collection", collection_date))
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                    for day in black_days:
         
     | 
| 
      
 77 
     | 
    
         
            +
                        collection_date = datetime(year=int(year), month=int(month), day=int(day))
         
     | 
| 
      
 78 
     | 
    
         
            +
                        collection_tuple.append(("Normal collection", collection_date))
         
     | 
| 
       86 
79 
     | 
    
         | 
| 
       87 
80 
     | 
    
         
             
                    ordered_data = sorted(collection_tuple, key=lambda x: x[1])
         
     | 
| 
       88 
81 
     | 
    
         | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from datetime import datetime
         
     | 
| 
      
 2 
     | 
    
         
            +
            from typing import Any
         
     | 
| 
      
 3 
     | 
    
         
            +
            import requests
         
     | 
| 
      
 4 
     | 
    
         
            +
            from ics import Calendar
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
         
     | 
| 
      
 7 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.common import date_format
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            class CouncilClass(AbstractGetBinDataClass):
         
     | 
| 
      
 11 
     | 
    
         
            +
                def parse_data(self, page: str, **kwargs: Any) -> dict:
         
     | 
| 
      
 12 
     | 
    
         
            +
                    ics_url: str = kwargs.get("url")
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    if not ics_url:
         
     | 
| 
      
 15 
     | 
    
         
            +
                        raise ValueError("Missing required argument: google_calendar_ics_url")
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    response = requests.get(ics_url)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    response.raise_for_status()
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    calendar = Calendar(response.text)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    bindata = {"bins": []}
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    for event in calendar.events:
         
     | 
| 
      
 24 
     | 
    
         
            +
                        if not event.name or not event.begin:
         
     | 
| 
      
 25 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 28 
     | 
    
         
            +
                            # .begin is a datetime-like object (Arrow)
         
     | 
| 
      
 29 
     | 
    
         
            +
                            collection_date = event.begin.date().strftime(date_format)
         
     | 
| 
      
 30 
     | 
    
         
            +
                        except Exception:
         
     | 
| 
      
 31 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                        bindata["bins"].append({
         
     | 
| 
      
 34 
     | 
    
         
            +
                            "type": event.name,
         
     | 
| 
      
 35 
     | 
    
         
            +
                            "collectionDate": collection_date
         
     | 
| 
      
 36 
     | 
    
         
            +
                        })
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    return bindata
         
     | 
| 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import time
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            import requests
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.common import *
         
     | 
| 
      
 6 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            # import the wonderful Beautiful Soup and the URL grabber
         
     | 
| 
      
 10 
     | 
    
         
            +
            class CouncilClass(AbstractGetBinDataClass):
         
     | 
| 
      
 11 
     | 
    
         
            +
                """
         
     | 
| 
      
 12 
     | 
    
         
            +
                Concrete classes have to implement all abstract operations of the
         
     | 
| 
      
 13 
     | 
    
         
            +
                base class. They can also override some operations with a default
         
     | 
| 
      
 14 
     | 
    
         
            +
                implementation.
         
     | 
| 
      
 15 
     | 
    
         
            +
                """
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def parse_data(self, page: str, **kwargs) -> dict:
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    user_uprn = kwargs.get("uprn")
         
     | 
| 
      
 20 
     | 
    
         
            +
                    check_uprn(user_uprn)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    bindata = {"bins": []}
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    REGEX = r"(Glass|Recycling|General Waste|Garden Waste).*?([0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4})"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    s = requests.Session()
         
     | 
| 
      
 26 
     | 
    
         
            +
                    r = s.get(
         
     | 
| 
      
 27 
     | 
    
         
            +
                        f"https://www.southampton.gov.uk/whereilive/waste-calendar?UPRN={user_uprn}"
         
     | 
| 
      
 28 
     | 
    
         
            +
                    )
         
     | 
| 
      
 29 
     | 
    
         
            +
                    r.raise_for_status()
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    # Limit search scope to avoid duplicates
         
     | 
| 
      
 32 
     | 
    
         
            +
                    calendar_view_only = re.search(
         
     | 
| 
      
 33 
     | 
    
         
            +
                        r"#calendar1.*?listView", r.text, flags=re.DOTALL
         
     | 
| 
      
 34 
     | 
    
         
            +
                    )[0]
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    results = re.findall(REGEX, calendar_view_only)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    for item in results:
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                        dict_data = {
         
     | 
| 
      
 41 
     | 
    
         
            +
                            "type": item[0],
         
     | 
| 
      
 42 
     | 
    
         
            +
                            "collectionDate": datetime.strptime(item[1], "%m/%d/%Y").strftime(
         
     | 
| 
      
 43 
     | 
    
         
            +
                                "%d/%m/%Y"
         
     | 
| 
      
 44 
     | 
    
         
            +
                            ),
         
     | 
| 
      
 45 
     | 
    
         
            +
                        }
         
     | 
| 
      
 46 
     | 
    
         
            +
                        bindata["bins"].append(dict_data)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    bindata["bins"].sort(
         
     | 
| 
      
 49 
     | 
    
         
            +
                        key=lambda x: datetime.strptime(x.get("collectionDate"), "%d/%m/%Y")
         
     | 
| 
      
 50 
     | 
    
         
            +
                    )
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    return bindata
         
     | 
| 
         @@ -0,0 +1,161 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import time
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            import requests
         
     | 
| 
      
 4 
     | 
    
         
            +
            from dateutil.relativedelta import relativedelta
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.common import *
         
     | 
| 
      
 7 
     | 
    
         
            +
            from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # import the wonderful Beautiful Soup and the URL grabber
         
     | 
| 
      
 11 
     | 
    
         
            +
            class CouncilClass(AbstractGetBinDataClass):
         
     | 
| 
      
 12 
     | 
    
         
            +
                """
         
     | 
| 
      
 13 
     | 
    
         
            +
                Concrete classes have to implement all abstract operations of the
         
     | 
| 
      
 14 
     | 
    
         
            +
                base class. They can also override some operations with a default
         
     | 
| 
      
 15 
     | 
    
         
            +
                implementation.
         
     | 
| 
      
 16 
     | 
    
         
            +
                """
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def parse_data(self, page: str, **kwargs) -> dict:
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    user_postcode = kwargs.get("postcode")
         
     | 
| 
      
 21 
     | 
    
         
            +
                    check_postcode(user_postcode)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    user_paon = kwargs.get("paon")
         
     | 
| 
      
 23 
     | 
    
         
            +
                    check_paon(user_paon)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    bindata = {"bins": []}
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    SESSION_URL = "https://spelthorne-self.achieveservice.com/authapi/isauthenticated?uri=https%253A%252F%252Fspelthorne-self.achieveservice.com%252Fen%252FAchieveForms%252F%253Fmode%253Dfill%2526consentMessage%253Dyes%2526form_uri%253Dsandbox-publish%253A%252F%252FAF-Process-8d0df9d6-6bd0-487e-bbfe-38815dcc780d%252FAF-Stage-bce7fc80-bcd7-45f1-bf55-a76d38dbebba%252Fdefinition.json%2526process%253D1%2526process_uri%253Dsandbox-processes%253A%252F%252FAF-Process-8d0df9d6-6bd0-487e-bbfe-38815dcc780d%2526process_id%253DAF-Process-8d0df9d6-6bd0-487e-bbfe-38815dcc780d%2526noLoginPrompt%253D1&hostname=spelthorne-self.achieveservice.com&withCredentials=true"
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    API_URL = "https://spelthorne-self.achieveservice.com/apibroker/runLookup"
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    headers = {
         
     | 
| 
      
 31 
     | 
    
         
            +
                        "Content-Type": "application/json",
         
     | 
| 
      
 32 
     | 
    
         
            +
                        "Accept": "application/json",
         
     | 
| 
      
 33 
     | 
    
         
            +
                        "User-Agent": "Mozilla/5.0",
         
     | 
| 
      
 34 
     | 
    
         
            +
                        "X-Requested-With": "XMLHttpRequest",
         
     | 
| 
      
 35 
     | 
    
         
            +
                        "Referer": "https://spelthorne-self.achieveservice.com/fillform/?iframe_id=fillform-frame-1&db_id=",
         
     | 
| 
      
 36 
     | 
    
         
            +
                    }
         
     | 
| 
      
 37 
     | 
    
         
            +
                    s = requests.session()
         
     | 
| 
      
 38 
     | 
    
         
            +
                    r = s.get(SESSION_URL)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    r.raise_for_status()
         
     | 
| 
      
 40 
     | 
    
         
            +
                    session_data = r.json()
         
     | 
| 
      
 41 
     | 
    
         
            +
                    sid = session_data["auth-session"]
         
     | 
| 
      
 42 
     | 
    
         
            +
                    data = {
         
     | 
| 
      
 43 
     | 
    
         
            +
                        "formValues": {
         
     | 
| 
      
 44 
     | 
    
         
            +
                            "Property details": {
         
     | 
| 
      
 45 
     | 
    
         
            +
                                "postalcode": {"value": user_postcode},
         
     | 
| 
      
 46 
     | 
    
         
            +
                            }
         
     | 
| 
      
 47 
     | 
    
         
            +
                        }
         
     | 
| 
      
 48 
     | 
    
         
            +
                    }
         
     | 
| 
      
 49 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 50 
     | 
    
         
            +
                        "id": "59b4477d6d84e",
         
     | 
| 
      
 51 
     | 
    
         
            +
                        "repeat_against": "",
         
     | 
| 
      
 52 
     | 
    
         
            +
                        "noRetry": "false",
         
     | 
| 
      
 53 
     | 
    
         
            +
                        "getOnlyTokens": "undefined",
         
     | 
| 
      
 54 
     | 
    
         
            +
                        "log_id": "",
         
     | 
| 
      
 55 
     | 
    
         
            +
                        "app_name": "AF-Renderer::Self",
         
     | 
| 
      
 56 
     | 
    
         
            +
                        # unix_timestamp
         
     | 
| 
      
 57 
     | 
    
         
            +
                        "_": str(int(time.time() * 1000)),
         
     | 
| 
      
 58 
     | 
    
         
            +
                        "sid": sid,
         
     | 
| 
      
 59 
     | 
    
         
            +
                    }
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    r = s.post(API_URL, json=data, headers=headers, params=params)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    r.raise_for_status()
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    data = r.json()
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    rows_data = data["integration"]["transformed"]["rows_data"]
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    for key, value in rows_data.items():
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                        address = value.get("display")
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                        if address.startswith(user_paon):
         
     | 
| 
      
 73 
     | 
    
         
            +
                            user_uprn = value.get("value")
         
     | 
| 
      
 74 
     | 
    
         
            +
                            break
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 77 
     | 
    
         
            +
                        "id": "5f97e6e09fedd",
         
     | 
| 
      
 78 
     | 
    
         
            +
                        "repeat_against": "",
         
     | 
| 
      
 79 
     | 
    
         
            +
                        "noRetry": "true",
         
     | 
| 
      
 80 
     | 
    
         
            +
                        "getOnlyTokens": "undefined",
         
     | 
| 
      
 81 
     | 
    
         
            +
                        "log_id": "",
         
     | 
| 
      
 82 
     | 
    
         
            +
                        "app_name": "AF-Renderer::Self",
         
     | 
| 
      
 83 
     | 
    
         
            +
                        # unix_timestamp
         
     | 
| 
      
 84 
     | 
    
         
            +
                        "_": str(int(time.time() * 1000)),
         
     | 
| 
      
 85 
     | 
    
         
            +
                        "sid": sid,
         
     | 
| 
      
 86 
     | 
    
         
            +
                    }
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    r = s.post(API_URL, headers=headers, params=params)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    r.raise_for_status()
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    data = r.json()
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                    rows_data = data["integration"]["transformed"]["rows_data"]["0"]
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    token = rows_data.get("tokenString")
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    start_date = datetime.today() + relativedelta(weeks=2)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    last2weeks = datetime.today() - relativedelta(weeks=2)
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    # Format as YYYY-MM-DD
         
     | 
| 
      
 101 
     | 
    
         
            +
                    formatted_date = start_date.strftime("%Y-%m-%d")
         
     | 
| 
      
 102 
     | 
    
         
            +
                    last2weeks = last2weeks.strftime("%Y-%m-%d")
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    data = {
         
     | 
| 
      
 105 
     | 
    
         
            +
                        "formValues": {
         
     | 
| 
      
 106 
     | 
    
         
            +
                            "Property details": {
         
     | 
| 
      
 107 
     | 
    
         
            +
                                "uprn1": {"value": user_uprn},
         
     | 
| 
      
 108 
     | 
    
         
            +
                                "endDate": {"value": formatted_date},
         
     | 
| 
      
 109 
     | 
    
         
            +
                                "last2Weeks": {"value": last2weeks},
         
     | 
| 
      
 110 
     | 
    
         
            +
                                "token": {"value": token},
         
     | 
| 
      
 111 
     | 
    
         
            +
                            }
         
     | 
| 
      
 112 
     | 
    
         
            +
                        }
         
     | 
| 
      
 113 
     | 
    
         
            +
                    }
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 116 
     | 
    
         
            +
                        "id": "66042a164c9a5",
         
     | 
| 
      
 117 
     | 
    
         
            +
                        "repeat_against": "",
         
     | 
| 
      
 118 
     | 
    
         
            +
                        "noRetry": "true",
         
     | 
| 
      
 119 
     | 
    
         
            +
                        "getOnlyTokens": "undefined",
         
     | 
| 
      
 120 
     | 
    
         
            +
                        "log_id": "",
         
     | 
| 
      
 121 
     | 
    
         
            +
                        "app_name": "AF-Renderer::Self",
         
     | 
| 
      
 122 
     | 
    
         
            +
                        # unix_timestamp
         
     | 
| 
      
 123 
     | 
    
         
            +
                        "_": str(int(time.time() * 1000)),
         
     | 
| 
      
 124 
     | 
    
         
            +
                        "sid": sid,
         
     | 
| 
      
 125 
     | 
    
         
            +
                    }
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                    r = s.post(API_URL, json=data, headers=headers, params=params)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    r.raise_for_status()
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                    data = r.json()
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                    rows_data = data["integration"]["transformed"]["rows_data"]["0"]
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                    if not isinstance(rows_data, dict):
         
     | 
| 
      
 135 
     | 
    
         
            +
                        raise ValueError("Invalid data returned from API")
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                    use_new = any(k.endswith("New") and v for k, v in rows_data.items())
         
     | 
| 
      
 138 
     | 
    
         
            +
                    next_date_key = "NextDateNew" if use_new else "NextDateOld"
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                    for key, value in rows_data.items():
         
     | 
| 
      
 141 
     | 
    
         
            +
                        if not (key.endswith("NextCollection") or key.endswith(next_date_key)):
         
     | 
| 
      
 142 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                        bin_type = key.split("NextCollection")[0]
         
     | 
| 
      
 145 
     | 
    
         
            +
                        if bin_type == "Gw":
         
     | 
| 
      
 146 
     | 
    
         
            +
                            bin_type = ["Garden Waste"]
         
     | 
| 
      
 147 
     | 
    
         
            +
                        if bin_type == "Rec":
         
     | 
| 
      
 148 
     | 
    
         
            +
                            bin_type = ["Recycling Bin", "Food Waste Bin"]
         
     | 
| 
      
 149 
     | 
    
         
            +
                        if bin_type == "Ref":
         
     | 
| 
      
 150 
     | 
    
         
            +
                            bin_type = ["Rubbish Bin", "Food Waste Bin"]
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 153 
     | 
    
         
            +
                            date = datetime.strptime(value, "%Y-%m-%d").strftime("%d/%m/%Y")
         
     | 
| 
      
 154 
     | 
    
         
            +
                        except ValueError:
         
     | 
| 
      
 155 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                        for bin in bin_type:
         
     | 
| 
      
 158 
     | 
    
         
            +
                            dict_data = {"type": bin, "collectionDate": date}
         
     | 
| 
      
 159 
     | 
    
         
            +
                            bindata["bins"].append(dict_data)
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                    return bindata
         
     | 
| 
         @@ -1,7 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from datetime import datetime
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            from bs4 import BeautifulSoup
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       2 
5 
     | 
    
         
             
            from uk_bin_collection.uk_bin_collection.common import *
         
     | 
| 
       3 
6 
     | 
    
         
             
            from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
         
     | 
| 
       4 
     | 
    
         
            -
            from datetime import datetime
         
     | 
| 
       5 
7 
     | 
    
         | 
| 
       6 
8 
     | 
    
         | 
| 
       7 
9 
     | 
    
         
             
            class CouncilClass(AbstractGetBinDataClass):
         
     | 
| 
         @@ -73,7 +73,7 @@ class CouncilClass(AbstractGetBinDataClass): 
     | 
|
| 
       73 
73 
     | 
    
         
             
                        find_ac_button.send_keys(Keys.RETURN)
         
     | 
| 
       74 
74 
     | 
    
         
             
                        h4_element = wait.until(
         
     | 
| 
       75 
75 
     | 
    
         
             
                            EC.presence_of_element_located(
         
     | 
| 
       76 
     | 
    
         
            -
                                (By.XPATH, "//h4[contains(text(), ' 
     | 
| 
      
 76 
     | 
    
         
            +
                                (By.XPATH, "//h4[contains(text(), 'Your upcoming collections')]")
         
     | 
| 
       77 
77 
     | 
    
         
             
                            )
         
     | 
| 
       78 
78 
     | 
    
         
             
                        )
         
     | 
| 
       79 
79 
     | 
    
         | 
| 
         @@ -81,7 +81,7 @@ class CouncilClass(AbstractGetBinDataClass): 
     | 
|
| 
       81 
81 
     | 
    
         
             
                            EC.presence_of_element_located(
         
     | 
| 
       82 
82 
     | 
    
         
             
                                (
         
     | 
| 
       83 
83 
     | 
    
         
             
                                    By.XPATH,
         
     | 
| 
       84 
     | 
    
         
            -
                                    '//div[contains(@class, " 
     | 
| 
      
 84 
     | 
    
         
            +
                                    '//div[contains(@class, "repeatable-table-wrapper")]',
         
     | 
| 
       85 
85 
     | 
    
         
             
                                )
         
     | 
| 
       86 
86 
     | 
    
         
             
                            )
         
     | 
| 
       87 
87 
     | 
    
         
             
                        )
         
     | 
| 
         @@ -90,16 +90,15 @@ class CouncilClass(AbstractGetBinDataClass): 
     | 
|
| 
       90 
90 
     | 
    
         | 
| 
       91 
91 
     | 
    
         
             
                        data = {"bins": []}
         
     | 
| 
       92 
92 
     | 
    
         | 
| 
       93 
     | 
    
         
            -
                        collection_divs = soup.find_all(" 
     | 
| 
      
 93 
     | 
    
         
            +
                        collection_divs = soup.find_all("tr", {"class": "repeatable-value"})
         
     | 
| 
       94 
94 
     | 
    
         | 
| 
       95 
95 
     | 
    
         
             
                        for collection_div in collection_divs:
         
     | 
| 
       96 
     | 
    
         
            -
                             
     | 
| 
       97 
     | 
    
         
            -
                             
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
                            if h5_tag and p_tag:
         
     | 
| 
       100 
     | 
    
         
            -
                                bin_type = h5_tag.get_text(strip=True)
         
     | 
| 
       101 
     | 
    
         
            -
                                collection_date_text = p_tag.find("b").get_text(strip=True)
         
     | 
| 
      
 96 
     | 
    
         
            +
                            td_list = collection_div.find_all("td")
         
     | 
| 
      
 97 
     | 
    
         
            +
                            bin_type = td_list[1].get_text(strip=True)
         
     | 
| 
      
 98 
     | 
    
         
            +
                            collection_date_text = td_list[2].get_text(strip=True)
         
     | 
| 
       102 
99 
     | 
    
         | 
| 
      
 100 
     | 
    
         
            +
                            # if collection_date_text is not 'NaN'
         
     | 
| 
      
 101 
     | 
    
         
            +
                            if collection_date_text != "NaN":
         
     | 
| 
       103 
102 
     | 
    
         
             
                                # Extract and format the date
         
     | 
| 
       104 
103 
     | 
    
         
             
                                date_match = re.search(r"(\d+ \w+)", collection_date_text)
         
     | 
| 
       105 
104 
     | 
    
         
             
                                if date_match:
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.1
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: uk_bin_collection
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 0. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 0.137.0
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: Python Lib to collect UK Bin Data
         
     | 
| 
       5 
5 
     | 
    
         
             
            Author: Robert Bradley
         
     | 
| 
       6 
6 
     | 
    
         
             
            Author-email: robbrad182@gmail.com
         
     | 
| 
         @@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.12 
     | 
|
| 
       10 
10 
     | 
    
         
             
            Classifier: Programming Language :: Python :: 3.13
         
     | 
| 
       11 
11 
     | 
    
         
             
            Requires-Dist: bs4
         
     | 
| 
       12 
12 
     | 
    
         
             
            Requires-Dist: holidays
         
     | 
| 
      
 13 
     | 
    
         
            +
            Requires-Dist: ics (>=0.7.2,<0.8.0)
         
     | 
| 
       13 
14 
     | 
    
         
             
            Requires-Dist: lxml
         
     | 
| 
       14 
15 
     | 
    
         
             
            Requires-Dist: pandas
         
     | 
| 
       15 
16 
     | 
    
         
             
            Requires-Dist: python-dateutil
         
     | 
| 
         @@ -41,10 +42,10 @@ Description-Content-Type: text/markdown 
     | 
|
| 
       41 
42 
     | 
    
         
             
            # UK Bin Collection Data (UKBCD)
         
     | 
| 
       42 
43 
     | 
    
         
             
            This project aims to provide a neat and standard way of providing bin collection data in JSON format from UK councils that have no API to do so.
         
     | 
| 
       43 
44 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
            Why do this?
         
     | 
| 
       45 
     | 
    
         
            -
            You might want to use this in  
     | 
| 
      
 45 
     | 
    
         
            +
            Why would you want to do this?
         
     | 
| 
      
 46 
     | 
    
         
            +
            You might want to use this in Home Automation—for example, say you had an LED bar that lit up on the day of bin collection to the colour of the bin you want to take out; then this repo provides the data for that. 
         
     | 
| 
       46 
47 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
            **PLEASE respect a councils infrastructure / usage policy and only collect data for your own personal use on a suitable frequency to your collection schedule.**
         
     | 
| 
      
 48 
     | 
    
         
            +
            **PLEASE respect a councils' infrastructure / usage policy and only collect data for your own personal use on a suitable frequency to your collection schedule.**
         
     | 
| 
       48 
49 
     | 
    
         | 
| 
       49 
50 
     | 
    
         
             
            Most scripts make use of [Beautiful Soup 4](https://pypi.org/project/beautifulsoup4/) to scrape data, although others use different approaches, such as emulating web browser behaviour, or reading data from CSV files.
         
     | 
| 
       50 
51 
     | 
    
         | 
| 
         @@ -59,12 +60,12 @@ Most scripts make use of [Beautiful Soup 4](https://pypi.org/project/beautifulso 
     | 
|
| 
       59 
60 
     | 
    
         
             
            If an issue already exists, please comment on that issue to express your interest. Please do not open a new issue, as it will be closed as a duplicate.
         
     | 
| 
       60 
61 
     | 
    
         | 
| 
       61 
62 
     | 
    
         
             
            If an issue does not already exist, please fill in a new [Council Request](https://github.com/robbrad/UKBinCollectionData/issues/new/choose) form, including as much information as possible, including:
         
     | 
| 
       62 
     | 
    
         
            -
            - Name of the council
         
     | 
| 
       63 
     | 
    
         
            -
            - URL to bin collections
         
     | 
| 
       64 
     | 
    
         
            -
            - An example postcode and/or UPRN (whichever is relevant)
         
     | 
| 
       65 
     | 
    
         
            -
            - Any further information
         
     | 
| 
      
 63 
     | 
    
         
            +
            - Name of the council.
         
     | 
| 
      
 64 
     | 
    
         
            +
            - URL to bin collections.
         
     | 
| 
      
 65 
     | 
    
         
            +
            - An example postcode and/or [UPRN](https://uprn.uk/) (whichever is relevant).
         
     | 
| 
      
 66 
     | 
    
         
            +
            - Any further information.
         
     | 
| 
       66 
67 
     | 
    
         | 
| 
       67 
     | 
    
         
            -
            Please be aware that this project is run by volunteer contributors and completion depends on numerous factors - even with a request, we cannot guarantee if/when your council  
     | 
| 
      
 68 
     | 
    
         
            +
            Please be aware that this project is run by volunteer contributors and completion depends on numerous factors - even with a request, we cannot guarantee if/when your council is added to this integration.
         
     | 
| 
       68 
69 
     | 
    
         | 
| 
       69 
70 
     | 
    
         
             
            ---
         
     | 
| 
       70 
71 
     | 
    
         | 
| 
         @@ -80,7 +81,7 @@ This integration can be installed directly via HACS. To install: 
     | 
|
| 
       80 
81 
     | 
    
         
             
            * [Add the repository](https://my.home-assistant.io/redirect/hacs_repository/?owner=robbrad&repository=UKBinCollectionData&category=integration) to your HACS installation
         
     | 
| 
       81 
82 
     | 
    
         
             
            * Click `Download`
         
     | 
| 
       82 
83 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
            For details how setup the custom component integration see the [ 
     | 
| 
      
 84 
     | 
    
         
            +
            For details on how to setup the custom component integration, see the [documentation](https://github.com/robbrad/UKBinCollectionData/tree/master/custom_components/uk_bin_collection).
         
     | 
| 
       84 
85 
     | 
    
         | 
| 
       85 
86 
     | 
    
         
             
            #### Manual
         
     | 
| 
       86 
87 
     | 
    
         
             
            1. Ensure you have [HACS](https://hacs.xyz/) installed
         
     | 
| 
         @@ -90,7 +91,7 @@ For details how setup the custom component integration see the [this documentati 
     | 
|
| 
       90 
91 
     | 
    
         
             
            1. Click `+ Add Integration` and search for and select `UK Bin Collection Data` then click `Download`.
         
     | 
| 
       91 
92 
     | 
    
         
             
            1. Restart your Home Assistant.
         
     | 
| 
       92 
93 
     | 
    
         
             
            1. In the Home Assistant UI go to `Settings` > `Devices & Services` click `+ Add Integration` and search for `UK Bin Collection Data`.
         
     | 
| 
       93 
     | 
    
         
            -
            1. If  
     | 
| 
      
 94 
     | 
    
         
            +
            1. If you see a "URL of the remote Selenium web driver to use" field when setting up your council, you'll need to provide the URL to a web driver you've set up separately such as [standalone-chrome](https://hub.docker.com/r/selenium/standalone-chrome).
         
     | 
| 
       94 
95 
     | 
    
         | 
| 
       95 
96 
     | 
    
         
             
            ### Install manually
         
     | 
| 
       96 
97 
     | 
    
         | 
| 
         @@ -101,15 +102,15 @@ For details how setup the custom component integration see the [this documentati 
     | 
|
| 
       101 
102 
     | 
    
         
             
            1. In the Home Assistant UI go to `Settings` > `Devices & Services` click `+ Add Integration` and search for `UK Bin Collection Data`.
         
     | 
| 
       102 
103 
     | 
    
         | 
| 
       103 
104 
     | 
    
         
             
            ### Overriding the Bin Icon and Bin Colour
         
     | 
| 
       104 
     | 
    
         
            -
            We realise it is difficult to set a colour from the councils text for the Bin Type and to keep the integration generic we  
     | 
| 
      
 105 
     | 
    
         
            +
            We realise it is difficult to set a colour from the councils text for the Bin Type and to keep the integration generic, we don't capture colour from a council (not all councils supply this as a field), only bin type and next collection date.
         
     | 
| 
       105 
106 
     | 
    
         | 
| 
       106 
     | 
    
         
            -
            When you configure the  
     | 
| 
      
 107 
     | 
    
         
            +
            When you configure the component on the first screen, you can set a JSON string to map the bin type to the colour and icon
         
     | 
| 
       107 
108 
     | 
    
         | 
| 
       108 
109 
     | 
    
         
             
            Here is an example to set the colour and icon for the type `Empty Standard General Waste`. This type is the type returned from the council for the bin. You can do this for multiple bins.
         
     | 
| 
       109 
110 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
            If you miss this on the first setup you can reconfigure it.
         
     | 
| 
      
 111 
     | 
    
         
            +
            If you miss this on the first setup, you can reconfigure it.
         
     | 
| 
       111 
112 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
            ```
         
     | 
| 
      
 113 
     | 
    
         
            +
            ```json
         
     | 
| 
       113 
114 
     | 
    
         
             
            {     
         
     | 
| 
       114 
115 
     | 
    
         
             
              "Empty Standard General Waste": 
         
     | 
| 
       115 
116 
     | 
    
         
             
              {         
         
     | 
| 
         @@ -147,7 +148,7 @@ where ```council_name``` is the name of the council's .py script (without the .p 
     | 
|
| 
       147 
148 
     | 
    
         
             
            The help documentation refers to these as "module" and "URL", respectively. Supported council scripts can be found in the `uk_bin_collection/uk_bin_collection/councils` folder.
         
     | 
| 
       148 
149 
     | 
    
         | 
| 
       149 
150 
     | 
    
         
             
            Some scripts require additional parameters, for example, when a UPRN is not passed in a URL, or when the script is not scraping a web page.
         
     | 
| 
       150 
     | 
    
         
            -
            For example, the Leeds City Council script needs two additional parameters 
     | 
| 
      
 151 
     | 
    
         
            +
            For example, the Leeds City Council script needs two additional parameters—a postcode, and a house number. This is done like so:
         
     | 
| 
       151 
152 
     | 
    
         | 
| 
       152 
153 
     | 
    
         
             
            ```commandline
         
     | 
| 
       153 
154 
     | 
    
         
             
            python collect_data.py LeedsCityCouncil https://www.leeds.gov.uk/residents/bins-and-recycling/check-your-bin-day -p "LS1 2JG" -n 41
         
     | 
| 
         @@ -161,9 +162,8 @@ To check the parameters needed for your council's script, please check the [proj 
     | 
|
| 
       161 
162 
     | 
    
         | 
| 
       162 
163 
     | 
    
         | 
| 
       163 
164 
     | 
    
         
             
            ### Project dependencies
         
     | 
| 
       164 
     | 
    
         
            -
            Some scripts rely on external packages to function. A list of required scripts for both development and execution can be found in the project's [PROJECT_TOML](https://github.com/robbrad/UKBinCollectionData/blob/feature/%2353_integration_tests/pyproject.toml) 
     | 
| 
       165 
     | 
    
         
            -
            Install can be done via 
         
     | 
| 
       166 
     | 
    
         
            -
            `poetry install` from within the root of the repo.
         
     | 
| 
      
 165 
     | 
    
         
            +
            Some scripts rely on external packages to function. A list of required scripts for both development and execution can be found in the project's [PROJECT_TOML](https://github.com/robbrad/UKBinCollectionData/blob/feature/%2353_integration_tests/pyproject.toml).
         
     | 
| 
      
 166 
     | 
    
         
            +
            Install can be done via  `poetry install` from within the root of the repo.
         
     | 
| 
       167 
167 
     | 
    
         | 
| 
       168 
168 
     | 
    
         
             
            ---
         
     | 
| 
       169 
169 
     | 
    
         | 
| 
         @@ -172,7 +172,7 @@ Some councils make use of the UPRN (Unique property reference number) to identif 
     | 
|
| 
       172 
172 
     | 
    
         | 
| 
       173 
173 
     | 
    
         
             
            ---
         
     | 
| 
       174 
174 
     | 
    
         
             
            ## Selenium
         
     | 
| 
       175 
     | 
    
         
            -
            Some councils need Selenium to run the scrape on behalf of Home Assistant. The easiest way to do this is run Selenium as in a Docker container. However you do this the Home Assistant server must be able to reach the Selenium server
         
     | 
| 
      
 175 
     | 
    
         
            +
            Some councils need Selenium to run the scrape on behalf of Home Assistant. The easiest way to do this is run Selenium as in a Docker container. However that you do this, the Home Assistant server must be able to reach the Selenium server.
         
     | 
| 
       176 
176 
     | 
    
         | 
| 
       177 
177 
     | 
    
         
             
            ### Instructions for Windows, Linux, and Mac
         
     | 
| 
       178 
178 
     | 
    
         | 
| 
         @@ -182,7 +182,7 @@ Some councils need Selenium to run the scrape on behalf of Home Assistant. The e 
     | 
|
| 
       182 
182 
     | 
    
         | 
| 
       183 
183 
     | 
    
         
             
            1.  **Download Docker Desktop for Windows:**
         
     | 
| 
       184 
184 
     | 
    
         | 
| 
       185 
     | 
    
         
            -
                *   Go to the Docker website: Docker Desktop for Windows
         
     | 
| 
      
 185 
     | 
    
         
            +
                *   Go to the Docker website: Docker Desktop for Windows.
         
     | 
| 
       186 
186 
     | 
    
         
             
                *   Download and install Docker Desktop.
         
     | 
| 
       187 
187 
     | 
    
         
             
            2.  **Run Docker Desktop:**
         
     | 
| 
       188 
188 
     | 
    
         | 
| 
         @@ -235,7 +235,7 @@ Some councils need Selenium to run the scrape on behalf of Home Assistant. The e 
     | 
|
| 
       235 
235 
     | 
    
         | 
| 
       236 
236 
     | 
    
         
             
            1.  **Download Docker Desktop for Mac:**
         
     | 
| 
       237 
237 
     | 
    
         | 
| 
       238 
     | 
    
         
            -
                *   Go to the Docker website: Docker Desktop for Mac
         
     | 
| 
      
 238 
     | 
    
         
            +
                *   Go to the Docker website: Docker Desktop for Mac.
         
     | 
| 
       239 
239 
     | 
    
         
             
                *   Download and install Docker Desktop.
         
     | 
| 
       240 
240 
     | 
    
         
             
            2.  **Run Docker Desktop:**
         
     | 
| 
       241 
241 
     | 
    
         | 
| 
         @@ -253,7 +253,7 @@ Some councils need Selenium to run the scrape on behalf of Home Assistant. The e 
     | 
|
| 
       253 
253 
     | 
    
         
             
                docker pull selenium/standalone-chrome
         
     | 
| 
       254 
254 
     | 
    
         
             
                ```
         
     | 
| 
       255 
255 
     | 
    
         | 
| 
       256 
     | 
    
         
            -
             
     | 
| 
      
 256 
     | 
    
         
            +
            3.  **Run the Selenium Standalone Chrome container:**
         
     | 
| 
       257 
257 
     | 
    
         | 
| 
       258 
258 
     | 
    
         
             
                ```bash
         
     | 
| 
       259 
259 
     | 
    
         
             
                docker run -d -p 4444:4444 --name selenium-chrome selenium/standalone-chrome
         
     | 
| 
         @@ -262,17 +262,17 @@ Some councils need Selenium to run the scrape on behalf of Home Assistant. The e 
     | 
|
| 
       262 
262 
     | 
    
         
             
            #### Step 3: Test the Selenium Server
         
     | 
| 
       263 
263 
     | 
    
         | 
| 
       264 
264 
     | 
    
         
             
            1.  **Navigate to the Selenium server URL in your web browser:**
         
     | 
| 
       265 
     | 
    
         
            -
                *   Open a web browser and go to `http://localhost:4444 
     | 
| 
      
 265 
     | 
    
         
            +
                *   Open a web browser and go to `http://localhost:4444`.
         
     | 
| 
       266 
266 
     | 
    
         
             
                *   You should see the Selenium Grid console.
         
     | 
| 
       267 
267 
     | 
    
         | 
| 
       268 
268 
     | 
    
         
             
            #### Step 4: Supply the Selenium Server URL to UKBinCollectionData
         
     | 
| 
       269 
269 
     | 
    
         | 
| 
       270 
270 
     | 
    
         
             
            1.  **Find the `UKBinCollectionData` project:**
         
     | 
| 
       271 
271 
     | 
    
         | 
| 
       272 
     | 
    
         
            -
                *   Go to the GitHub repository: [UKBinCollectionData](https://github.com/robbrad/UKBinCollectionData)
         
     | 
| 
      
 272 
     | 
    
         
            +
                *   Go to the GitHub repository: [UKBinCollectionData](https://github.com/robbrad/UKBinCollectionData).
         
     | 
| 
       273 
273 
     | 
    
         
             
            2.  **Supply the Selenium Server URL:**
         
     | 
| 
       274 
274 
     | 
    
         | 
| 
       275 
     | 
    
         
            -
                *   Typically, the URL will be `http://localhost:4444/wd/hub 
     | 
| 
      
 275 
     | 
    
         
            +
                *   Typically, the URL will be `http://localhost:4444/wd/hub`.
         
     | 
| 
       276 
276 
     | 
    
         
             
                *   You might need to update a configuration file or environment variable in the project to use this URL. Check the project's documentation for specific instructions.
         
     | 
| 
       277 
277 
     | 
    
         | 
| 
       278 
278 
     | 
    
         
             
            ### Summary of Commands
         
     | 
| 
         @@ -297,7 +297,7 @@ This guide is based on a Raspberry Pi 4. Instructions for other systems may vary 
     | 
|
| 
       297 
297 
     | 
    
         | 
| 
       298 
298 
     | 
    
         
             
            #### Prerequisites
         
     | 
| 
       299 
299 
     | 
    
         
             
            1. Install **Portainer** from Alex Belgium's add-on repository:
         
     | 
| 
       300 
     | 
    
         
            -
               [ 
     | 
| 
      
 300 
     | 
    
         
            +
               [alexbelgium/hassio-addons](https://github.com/alexbelgium/hassio-addons)
         
     | 
| 
       301 
301 
     | 
    
         | 
| 
       302 
302 
     | 
    
         
             
            ---
         
     | 
| 
       303 
303 
     | 
    
         | 
| 
         @@ -367,13 +367,13 @@ We have created an API for this located under [uk_bin_collection_api_server](htt 
     | 
|
| 
       367 
367 
     | 
    
         | 
| 
       368 
368 
     | 
    
         
             
            ### Prerequisites
         
     | 
| 
       369 
369 
     | 
    
         | 
| 
       370 
     | 
    
         
            -
            - Docker installed on your machine
         
     | 
| 
       371 
     | 
    
         
            -
            - Python (if you plan to run the API locally without Docker)
         
     | 
| 
      
 370 
     | 
    
         
            +
            - Docker installed on your machine.
         
     | 
| 
      
 371 
     | 
    
         
            +
            - Python (if you plan to run the API locally without Docker).
         
     | 
| 
       372 
372 
     | 
    
         | 
| 
       373 
373 
     | 
    
         
             
            ### Running the API with Docker
         
     | 
| 
       374 
374 
     | 
    
         | 
| 
       375 
375 
     | 
    
         
             
            1. Clone this repository.
         
     | 
| 
       376 
     | 
    
         
            -
            2. Navigate to the uk_bin_collection_api_server directory of the project.
         
     | 
| 
      
 376 
     | 
    
         
            +
            2. Navigate to the `uk_bin_collection_api_server` directory of the project.
         
     | 
| 
       377 
377 
     | 
    
         | 
| 
       378 
378 
     | 
    
         
             
            #### Build the Docker Container
         
     | 
| 
       379 
379 
     | 
    
         | 
| 
         @@ -401,7 +401,7 @@ The API documentation can be accessed via the Swagger UI. Use the Swagger UI to 
     | 
|
| 
       401 
401 
     | 
    
         
             
            #### API Endpoints
         
     | 
| 
       402 
402 
     | 
    
         
             
            `GET /bin_collection/{council}`
         
     | 
| 
       403 
403 
     | 
    
         | 
| 
       404 
     | 
    
         
            -
            Description: Retrieves information about bin  
     | 
| 
      
 404 
     | 
    
         
            +
            Description: Retrieves information about bin collections for the specified council.
         
     | 
| 
       405 
405 
     | 
    
         | 
| 
       406 
406 
     | 
    
         
             
            Parameters:
         
     | 
| 
       407 
407 
     | 
    
         | 
| 
         @@ -415,9 +415,9 @@ curl -X GET "http://localhost:8080/api/bin_collection/{council}" -H "accept: app 
     | 
|
| 
       415 
415 
     | 
    
         
             
            ```
         
     | 
| 
       416 
416 
     | 
    
         | 
| 
       417 
417 
     | 
    
         
             
            ## Docker Compose
         
     | 
| 
       418 
     | 
    
         
            -
            This includes the Selenium standalone-chrome for Selenium 
     | 
| 
      
 418 
     | 
    
         
            +
            This includes the Selenium standalone-chrome for Selenium-based councils.
         
     | 
| 
       419 
419 
     | 
    
         | 
| 
       420 
     | 
    
         
            -
            ```
         
     | 
| 
      
 420 
     | 
    
         
            +
            ```yaml
         
     | 
| 
       421 
421 
     | 
    
         
             
            version: '3'
         
     | 
| 
       422 
422 
     | 
    
         | 
| 
       423 
423 
     | 
    
         
             
            services:
         
     | 
| 
         @@ -447,8 +447,8 @@ docker-compose up 
     | 
|
| 
       447 
447 
     | 
    
         
             
            ---
         
     | 
| 
       448 
448 
     | 
    
         | 
| 
       449 
449 
     | 
    
         
             
            ## FAQ
         
     | 
| 
       450 
     | 
    
         
            -
            #### I've got an issue/support question 
     | 
| 
       451 
     | 
    
         
            -
            Please post in the [HomeAssistant thread](https://community.home-assistant.io/t/bin-waste-collection/55451) or raise a new (non 
     | 
| 
      
 450 
     | 
    
         
            +
            #### I've got an issue/support question—what do I do?
         
     | 
| 
      
 451 
     | 
    
         
            +
            Please post in the [HomeAssistant thread](https://community.home-assistant.io/t/bin-waste-collection/55451) or raise a new (non-council request) [issue](https://github.com/robbrad/UKBinCollectionData/issues/new).
         
     | 
| 
       452 
452 
     | 
    
         | 
| 
       453 
453 
     | 
    
         
             
            #### I'd like to contribute, where do I start?
         
     | 
| 
       454 
454 
     | 
    
         
             
            Contributions are always welcome! See ```CONTRIBUTING.md``` to get started. Please adhere to the project's [code of conduct](https://github.com/robbrad/UKBinCollectionData/blob/master/CODE_OF_CONDUCT.md).
         
     | 
| 
         @@ -461,4 +461,3 @@ Contributions are always welcome! See ```CONTRIBUTING.md``` to get started. Plea 
     | 
|
| 
       461 
461 
     | 
    
         
             
              <img src="https://contrib.rocks/image?repo=robbrad/UKBinCollectionData"  alt="Image of contributors"/>
         
     | 
| 
       462 
462 
     | 
    
         
             
            </a>
         
     | 
| 
       463 
463 
     | 
    
         | 
| 
       464 
     | 
    
         
            -
             
     | 
| 
         @@ -3,7 +3,7 @@ uk_bin_collection/tests/check_selenium_url_in_input.json.py,sha256=Iecdja0I3XIiY 
     | 
|
| 
       3 
3 
     | 
    
         
             
            uk_bin_collection/tests/council_feature_input_parity.py,sha256=DO6Mk4ImYgM5ZCZ-cutwz5RoYYWZRLYx2tr6zIs_9Rc,3843
         
     | 
| 
       4 
4 
     | 
    
         
             
            uk_bin_collection/tests/features/environment.py,sha256=VQZjJdJI_kZn08M0j5cUgvKT4k3iTw8icJge1DGOkoA,127
         
     | 
| 
       5 
5 
     | 
    
         
             
            uk_bin_collection/tests/features/validate_council_outputs.feature,sha256=SJK-Vc737hrf03tssxxbeg_JIvAH-ddB8f6gU1LTbuQ,251
         
     | 
| 
       6 
     | 
    
         
            -
            uk_bin_collection/tests/input.json,sha256= 
     | 
| 
      
 6 
     | 
    
         
            +
            uk_bin_collection/tests/input.json,sha256=z7WZPptXZTQ49n322hbxD5wa6lpCz4y0WC0cCCkGsHQ,117511
         
     | 
| 
       7 
7 
     | 
    
         
             
            uk_bin_collection/tests/output.schema,sha256=ZwKQBwYyTDEM4G2hJwfLUVM-5v1vKRvRK9W9SS1sd18,1086
         
     | 
| 
       8 
8 
     | 
    
         
             
            uk_bin_collection/tests/step_defs/step_helpers/file_handler.py,sha256=Ygzi4V0S1MIHqbdstUlIqtRIwnynvhu4UtpweJ6-5N8,1474
         
     | 
| 
       9 
9 
     | 
    
         
             
            uk_bin_collection/tests/step_defs/test_validate_council.py,sha256=VZ0a81sioJULD7syAYHjvK_-nT_Rd36tUyzPetSA0gk,3475
         
     | 
| 
         @@ -58,11 +58,12 @@ uk_bin_collection/uk_bin_collection/councils/BuckinghamshireCouncil.py,sha256=_E 
     | 
|
| 
       58 
58 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/BurnleyBoroughCouncil.py,sha256=GJf1OPvUVj3vqsR3KjG0DFHZrSBu4ogIz_MJeVV8tNA,3192
         
     | 
| 
       59 
59 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/BuryCouncil.py,sha256=H7wAxO1nfxkewVoRRolumq8bBJG04siE3jieFH3RGpQ,2632
         
     | 
| 
       60 
60 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/CalderdaleCouncil.py,sha256=OJZcHYlvZDzmBpjjPPm3J8CRK9Twc49vRj7O9c5fyQ4,4971
         
     | 
| 
      
 61 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/CambridgeCityCouncil.py,sha256=CxmFCgaWN_kzQAo7uiE2tzMX8QGzbJl5FCUx9cvb2Jk,2048
         
     | 
| 
       61 
62 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/CannockChaseDistrictCouncil.py,sha256=ZamevXN8_Q8mRZOTESWtkb8jVyDXkTczcmhXMAVVSkM,2276
         
     | 
| 
       62 
     | 
    
         
            -
            uk_bin_collection/uk_bin_collection/councils/CanterburyCityCouncil.py,sha256= 
     | 
| 
      
 63 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/CanterburyCityCouncil.py,sha256=Uz75GqCTFXv489-lZHN3a2r0G7A-PEKq-hWmcoXGuw4,1932
         
     | 
| 
       63 
64 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/CardiffCouncil.py,sha256=_k3sT_WR-gnjjt3tHQ-L2-keP9sdBV0ZeW7gHDzFPYo,7208
         
     | 
| 
       64 
65 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/CarmarthenshireCountyCouncil.py,sha256=viiZprMos_qcD44dki5_RRVZgGE4cH4zSHAzYRKrjHw,1864
         
     | 
| 
       65 
     | 
    
         
            -
            uk_bin_collection/uk_bin_collection/councils/CastlepointDistrictCouncil.py,sha256= 
     | 
| 
      
 66 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/CastlepointDistrictCouncil.py,sha256=AjxuIqKxx0tYHMaTBITXTcnN2L-Zrx78uwUkqTjI0xU,3599
         
     | 
| 
       66 
67 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/CharnwoodBoroughCouncil.py,sha256=tXfzMetN6wxahuGGRp2mIyCCDSL4F2aG61HhUxw6COQ,2172
         
     | 
| 
       67 
68 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/ChelmsfordCityCouncil.py,sha256=EB88D0MNJwuDZ2GX1ENc5maGYx17mnHTCtNl6s-v11E,5090
         
     | 
| 
       68 
69 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/CheltenhamBoroughCouncil.py,sha256=fo9n2AuD8a174h3YMa9awa3S4YO2XU1KaInQ4ULz6s4,13686
         
     | 
| 
         @@ -124,6 +125,7 @@ uk_bin_collection/uk_bin_collection/councils/GatesheadCouncil.py,sha256=SRCgYhYs 
     | 
|
| 
       124 
125 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/GedlingBoroughCouncil.py,sha256=XzfFMCwclh9zAJgsbaj4jywjdiH0wPaFicaVsLrN3ms,2297
         
     | 
| 
       125 
126 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/GlasgowCityCouncil.py,sha256=9Rk9KfcglJvRhh4373OfRX-fwywE2xgwx5KejqzV5fE,3399
         
     | 
| 
       126 
127 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/GloucesterCityCouncil.py,sha256=iMIlZMoBE4TG-ygw89zynvu-TlC4bWJ7g0vNH92JzHs,4890
         
     | 
| 
      
 128 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/GooglePublicCalendarCouncil.py,sha256=DsheXk3sooWWKctIY3PbHI88HI7GfdgZ7qS-iSVCKYA,1140
         
     | 
| 
       127 
129 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/GraveshamBoroughCouncil.py,sha256=ueQ9xFiTxMUBTGV9VjtySHA1EFWliTM0AeNePBIG9ho,4568
         
     | 
| 
       128 
130 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/GuildfordCouncil.py,sha256=9pVrmQhZcK2AD8gX8mNvP--L4L9KaY6L3B822VX6fec,5695
         
     | 
| 
       129 
131 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/GwyneddCouncil.py,sha256=eK2KkY1NbIxVtBruQYSNPA0J7fuzMik5it02dFbKYV0,1855
         
     | 
| 
         @@ -138,7 +140,7 @@ uk_bin_collection/uk_bin_collection/councils/HerefordshireCouncil.py,sha256=JpQh 
     | 
|
| 
       138 
140 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/HertsmereBoroughCouncil.py,sha256=-ThSG6NIJP_wf2GmGL7SAvxbOujdhanZ8ECP4VSQCBs,5415
         
     | 
| 
       139 
141 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/HighPeakCouncil.py,sha256=x7dfy8mdt2iGl8qJxHb-uBh4u0knmi9MJ6irOJw9WYA,4805
         
     | 
| 
       140 
142 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/HighlandCouncil.py,sha256=GNxDU65QuZHV5va2IrKtcJ6TQoDdwmV03JvkVqOauP4,3291
         
     | 
| 
       141 
     | 
    
         
            -
            uk_bin_collection/uk_bin_collection/councils/HinckleyandBosworthBoroughCouncil.py,sha256= 
     | 
| 
      
 143 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/HinckleyandBosworthBoroughCouncil.py,sha256=51vXTKrstfJhb7cLCcrsvA9qKCsptyNMZvy7ML9DasM,2344
         
     | 
| 
       142 
144 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/HounslowCouncil.py,sha256=LXhJ47rujx7k3naz0tFiTT1l5k6gAYcVdekJN1t_HLY,4564
         
     | 
| 
       143 
145 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/HullCityCouncil.py,sha256=UHcesBoctFVcXDYuwfag43KbcJcopkEDzJ-54NxtK0Q,1851
         
     | 
| 
       144 
146 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/HuntingdonDistrictCouncil.py,sha256=dGyhhG6HRjQ2SPeiRwUPTGlk9dPIslagV2k0GjEOn1s,1587
         
     | 
| 
         @@ -248,7 +250,9 @@ uk_bin_collection/uk_bin_collection/councils/SouthOxfordshireCouncil.py,sha256=u 
     | 
|
| 
       248 
250 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/SouthRibbleCouncil.py,sha256=OdexbeiI5WsCfjlsnHjAce8oGF5fW-n7q2XOuxcpHzw,3604
         
     | 
| 
       249 
251 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/SouthStaffordshireDistrictCouncil.py,sha256=ACQMHWyamnj1ag3gNF-8Jhp-DKUok1GhFdnzH4nCzwU,3201
         
     | 
| 
       250 
252 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/SouthTynesideCouncil.py,sha256=dxXGrJfg_fn2IPTBgq6Duwy0WY8GYLafMuisaCjOnbs,3426
         
     | 
| 
      
 253 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/SouthamptonCityCouncil.py,sha256=exNoI-Vun_C5FowCYhZ_600MBUe_OPR7MdGZEMNLL0I,1542
         
     | 
| 
       251 
254 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/SouthwarkCouncil.py,sha256=Z6JIbUt3yr4oG60n1At4AjPIGrs7Qzn_sDNY-TsS62E,4882
         
     | 
| 
      
 255 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/SpelthorneBoroughCouncil.py,sha256=iamG0wxIyYprttJPVc0jdbOoHavc1AmRpMhyc02MUrE,5703
         
     | 
| 
       252 
256 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/StAlbansCityAndDistrictCouncil.py,sha256=mPZz6Za6kTSkrfHnj0OfwtnpRYR1dKvxbuFEKnWsiL8,1451
         
     | 
| 
       253 
257 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/StHelensBC.py,sha256=X9dvnQTNn7QUO8gv1A587e1aDI92TWN4iNLATTn3H3w,4777
         
     | 
| 
       254 
258 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/StaffordBoroughCouncil.py,sha256=9Qj4HJI7Dbiqb2mVSG2UtkBe27Y7wvQ5SYFTwGzJ5g0,2292
         
     | 
| 
         @@ -280,9 +284,9 @@ uk_bin_collection/uk_bin_collection/councils/TunbridgeWellsCouncil.py,sha256=s8N 
     | 
|
| 
       280 
284 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/UttlesfordDistrictCouncil.py,sha256=GSELWbSn5jtznv6FSLIMxK6CyQ27MW9FoY_m5jhTEBA,4175
         
     | 
| 
       281 
285 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/ValeofGlamorganCouncil.py,sha256=dz3zDJsJKYYqUB-Gxf6twvP6kG_wyENaszxXxK4iJn0,5089
         
     | 
| 
       282 
286 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/ValeofWhiteHorseCouncil.py,sha256=fyskrQ4-osGOeCZuB_8m2TpW8iwHr7lpl52nrR06Xpo,4441
         
     | 
| 
       283 
     | 
    
         
            -
            uk_bin_collection/uk_bin_collection/councils/WakefieldCityCouncil.py,sha256= 
     | 
| 
      
 287 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/WakefieldCityCouncil.py,sha256=mdAXKcoSSF-xnXS8boesFftS8RiKyuR76ITHQ9Q5DgM,4916
         
     | 
| 
       284 
288 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/WalsallCouncil.py,sha256=wv-M3zZj0E6EIPoVB9AF_NCg_8XGM9uhqGa-F5yzoc4,2277
         
     | 
| 
       285 
     | 
    
         
            -
            uk_bin_collection/uk_bin_collection/councils/WalthamForest.py,sha256= 
     | 
| 
      
 289 
     | 
    
         
            +
            uk_bin_collection/uk_bin_collection/councils/WalthamForest.py,sha256=mN-USLAG5bKQcEDAhuVjA-kDLkFWlE8OAwDOUjHdc3g,5028
         
     | 
| 
       286 
290 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/WandsworthCouncil.py,sha256=aQoJpC7YkhKewQyZlsv5m4__2IPoKRsOrPxjNH5xRNo,2594
         
     | 
| 
       287 
291 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/WarringtonBoroughCouncil.py,sha256=AB9mrV1v4pKKhfsBS8MpjO8XXBifqojSk53J9Q74Guk,1583
         
     | 
| 
       288 
292 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/WarwickDistrictCouncil.py,sha256=DAL_f0BcIxFj7Ngkms5Q80kgSGp9y5zB35wRPudayz8,1644
         
     | 
| 
         @@ -315,8 +319,8 @@ uk_bin_collection/uk_bin_collection/councils/YorkCouncil.py,sha256=I2kBYMlsD4bId 
     | 
|
| 
       315 
319 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/councils/council_class_template/councilclasstemplate.py,sha256=EQWRhZ2pEejlvm0fPyOTsOHKvUZmPnxEYO_OWRGKTjs,1158
         
     | 
| 
       316 
320 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/create_new_council.py,sha256=m-IhmWmeWQlFsTZC4OxuFvtw5ZtB8EAJHxJTH4O59lQ,1536
         
     | 
| 
       317 
321 
     | 
    
         
             
            uk_bin_collection/uk_bin_collection/get_bin_data.py,sha256=YvmHfZqanwrJ8ToGch34x-L-7yPe31nB_x77_Mgl_vo,4545
         
     | 
| 
       318 
     | 
    
         
            -
            uk_bin_collection-0. 
     | 
| 
       319 
     | 
    
         
            -
            uk_bin_collection-0. 
     | 
| 
       320 
     | 
    
         
            -
            uk_bin_collection-0. 
     | 
| 
       321 
     | 
    
         
            -
            uk_bin_collection-0. 
     | 
| 
       322 
     | 
    
         
            -
            uk_bin_collection-0. 
     | 
| 
      
 322 
     | 
    
         
            +
            uk_bin_collection-0.137.0.dist-info/LICENSE,sha256=vABBUOzcrgfaTKpzeo-si9YVEun6juDkndqA8RKdKGs,1071
         
     | 
| 
      
 323 
     | 
    
         
            +
            uk_bin_collection-0.137.0.dist-info/METADATA,sha256=_mc1mfVauQD8RLPBsVM1F9aYNhddWsEzYC-KRiQhZkE,19851
         
     | 
| 
      
 324 
     | 
    
         
            +
            uk_bin_collection-0.137.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
         
     | 
| 
      
 325 
     | 
    
         
            +
            uk_bin_collection-0.137.0.dist-info/entry_points.txt,sha256=36WCSGMWSc916S3Hi1ZkazzDKHaJ6CD-4fCEFm5MIao,90
         
     | 
| 
      
 326 
     | 
    
         
            +
            uk_bin_collection-0.137.0.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
    
        {uk_bin_collection-0.135.4.dist-info → uk_bin_collection-0.137.0.dist-info}/entry_points.txt
    RENAMED
    
    | 
         
            File without changes
         
     |