Repository: mampfes/hacs_waste_collection_schedule

Issue:
[Bug]: ecoharmonogram shows invalid schedule
### I Have A Problem With:

A specific source

### What's Your Problem

I see a schedule for a different location. Currently it's for `Kiełkowice (gmina Zawiercie) ul. Łąki` instead of `Zawiercie ul. Równa`. See the reference https://www.czystagmina.zawiercie.eu/kategorie/harmonogram

I'm not very sure if this is the exact place of the issue, but checking if "Zawiercie" is in `name` is not always valid solution: [source code](https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/custom_components/waste_collection_schedule/waste_collection_schedule/service/EcoHarmonogramPL.py#L65)

There are multiple towns that contain "Zawiercie" in its name. See https://ecoharmonogram.pl/api/api.php?action=getTowns:
```
{"id":"3360","communityId":"157","name":"Kie\u0142kowice (gmina Zawiercie)","district":"Zawiercie","province":"\u015al\u0105skie","icon":"https:\/\/ecoharmonogram.pl\/ecopagenew\/logo\/zawiercie.png","isStrNmrReq":"1","schedulePeriodId":2}

{"id":"3335","communityId":"157","name":"Zawiercie","district":"Zawiercie","province":"\u015al\u0105skie","icon":"https:\/\/ecoharmonogram.pl\/ecopagenew\/logo\/zawiercie.png","isStrNmrReq":"1","schedulePeriodId":2}
```

### Source (if relevant)

ecoharmonogram_pl

### Logs

```Shell
no relevant logs
```


### Relevant Configuration

```YAML
waste_collection_schedule:
   sources:
     - name: "ecoharmonogram_pl"
       args:
         town: Zawiercie
         street: Równa
         district: Zawiercie
```


### Checklist Source Error

- [X] Use the example parameters for your source (often available in the documentation) (don't forget to restart Home Assistant after changing the configuration)
- [X] Checked that the website of your service provider is still working
- [X] Tested my attributes on the service provider website (if possible)
- [X] I have tested with the latest version of the integration (master) (for HACS in the 3 dot menu of the integration click on "Redownload" and choose master as version)

### Checklist Sensor Error

- [ ] Checked in the Home Assistant Calendar tab if the event names match the types names (if types argument is used)

### Required

- [X] I have searched past (closed AND opened) issues to see if this bug has already been reported, and it hasn't been.
- [X] I understand that people give their precious time for free, and thus I've done my very best to make this problem as easy as possible to investigate.


Discussion:

Resolving patch:
diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/ecoharmonogram_pl.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/ecoharmonogram_pl.py
--- a/custom_components/waste_collection_schedule/waste_collection_schedule/source/ecoharmonogram_pl.py
+++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/ecoharmonogram_pl.py
@@ -1,6 +1,6 @@
 import datetime
-from ..collection import Collection
 
+from ..collection import Collection
 from ..service.EcoHarmonogramPL import Ecoharmonogram
 
 TITLE = "Ecoharmonogram"
@@ -8,17 +8,48 @@
 URL = "https://ecoharmonogram.pl"
 TEST_CASES = {
     "Simple test case": {"town": "Krzeszowice", "street": "Wyki", "house_number": ""},
-    "Sides multi test case": {"town": "Częstochowa", "street": "Boczna", "additional_sides_matcher": "wie"},
-    "Sides test case": {"town": "Częstochowa", "street": "Azaliowa", "house_number": "1",
-                        "additional_sides_matcher": "jedn"},
-    "Sides multi test case with district": {"town": "Borkowo", "district": "Pruszcz Gdański", "street": "Sadowa",
-                                            "additional_sides_matcher": "Wielorodzinna - powyżej 7 lokali"},
-    "Simple test with community": {"town": "Gdańsk", "street": "Jabłoniowa", "house_number": "55", "additional_sides_matcher": "", "community": "108" },
+    "Sides multi test case": {
+        "town": "Częstochowa",
+        "street": "Boczna",
+        "additional_sides_matcher": "wie",
+    },
+    "Sides test case": {
+        "town": "Częstochowa",
+        "street": "Azaliowa",
+        "house_number": "1",
+        "additional_sides_matcher": "jedn",
+    },
+    "Sides multi test case with district": {
+        "town": "Borkowo",
+        "district": "Pruszcz Gdański",
+        "street": "Sadowa",
+        "additional_sides_matcher": "Wielorodzinna - powyżej 7 lokali",
+    },
+    "Simple test with community": {
+        "town": "Gdańsk",
+        "street": "Jabłoniowa",
+        "house_number": "55",
+        "additional_sides_matcher": "",
+        "community": "108",
+    },
+    "Zawiercie, Równa, Zawiercie": {
+        "town": "Zawiercie",
+        "street": "Równa",
+        "district": "Zawiercie",
+    },
 }
 
 
 class Source:
-    def __init__(self, town, district="", street="", house_number="", additional_sides_matcher="", community=""):    
+    def __init__(
+        self,
+        town,
+        district="",
+        street="",
+        house_number="",
+        additional_sides_matcher="",
+        community="",
+    ):
         self.town_input = town
         self.street_input = street
         self.house_number_input = house_number
@@ -27,17 +58,54 @@ def __init__(self, town, district="", street="", house_number="", additional_sid
         self.community_input = community
 
     def fetch(self):
-
         if self.community_input == "":
             town_data = Ecoharmonogram.fetch_town()
         else:
             town_data = Ecoharmonogram.fetch_town_with_community(self.community_input)
 
-        matching_towns = filter(lambda x: self.town_input.lower() in x.get('name').lower(), town_data.get('towns'))
-        matching_towns_district = filter(lambda x: self.district_input.lower() in x.get('district').lower(),
-                                         matching_towns)
+        matching_towns = filter(
+            lambda x: self.town_input.lower() in x.get("name").lower(),
+            town_data.get("towns"),
+        )
+        matching_towns_district = list(
+            filter(
+                lambda x: self.district_input.lower() in x.get("district").lower(),
+                matching_towns,
+            )
+        )
+
+        if len(matching_towns_district) == 0:
+            raise Exception(
+                f"Town {self.town_input} with district {self.district_input} not found"
+            )
 
-        town = list(matching_towns_district)[0]
+        town = matching_towns_district[0]
+
+        if len(matching_towns_district) > 1:
+            match = False
+            for town_district in matching_towns_district:
+                if (
+                    self.town_input.lower() == town_district.get("name").lower()
+                    and self.district_input.lower()
+                    == town_district.get("district").lower()
+                ):
+                    match = True
+                    town = town_district
+                    break
+            if not match:
+                matches = list(
+                    map(
+                        lambda x: "town: "
+                        + x.get("name")
+                        + ", district:"
+                        + x.get("district"),
+                        matching_towns_district,
+                    )
+                )
+
+                raise Exception(
+                    f"Found multiple matches but no exact match found {matches}"
+                )
 
         schedule_periods_data = Ecoharmonogram.fetch_scheduled_periods(town)
         schedule_periods = schedule_periods_data.get("schedulePeriods")
@@ -46,38 +114,46 @@ def fetch(self):
         for sp in schedule_periods:
             entries.extend(self._create_entries(sp, town))
         return entries
-    
-    
+
     def _create_entries(self, sp, town):
-        streets = Ecoharmonogram.fetch_streets(sp, town, self.street_input, self.house_number_input)
+        streets = Ecoharmonogram.fetch_streets(
+            sp, town, self.street_input, self.house_number_input
+        )
         entries = []
         for street in streets:
-            if self.additional_sides_matcher_input.lower() in street.get("sides").lower():
+            if (
+                self.additional_sides_matcher_input.lower()
+                in street.get("sides").lower()
+            ):
                 schedules_response = Ecoharmonogram.fetch_schedules(sp, street)
-                schedules_raw = schedules_response.get('schedules')
+                schedules_raw = schedules_response.get("schedules")
                 schedules_descriptions_dict = dict()
-                schedules_descriptions_raw = schedules_response.get('scheduleDescription')
+                schedules_descriptions_raw = schedules_response.get(
+                    "scheduleDescription"
+                )
 
                 for sd in schedules_descriptions_raw:
-                    schedules_descriptions_dict[sd.get('id')] = sd
+                    schedules_descriptions_dict[sd.get("id")] = sd
 
                 schedules = []
                 for sr in schedules_raw:
                     z = sr.copy()
-                    get = schedules_descriptions_dict.get(sr.get('scheduleDescriptionId'))
-                    z['name'] = get.get("name")
+                    get = schedules_descriptions_dict.get(
+                        sr.get("scheduleDescriptionId")
+                    )
+                    z["name"] = get.get("name")
                     schedules.append(z)
 
                 entries = []
                 for sch in schedules:
-                    days = sch.get("days").split(';')
+                    days = sch.get("days").split(";")
                     month = sch.get("month")
                     year = sch.get("year")
                     for d in days:
                         entries.append(
                             Collection(
                                 datetime.date(int(year), int(month), int(d)),
-                                sch.get('name')
+                                sch.get("name"),
                             )
                         )
                 if self.additional_sides_matcher_input != "":


Test patch: