Repository: CS-SI/eodag (python)

Issue:
prevent mixed results between earth_search and earth_search_cog
See #362 .

@GriffinBabe pointed out that if we omit to specify the product type when searching on `earth_search_cog`, we get also results from `earth_search` that cannot be downloaded with the wrong download plugin:
```py
from eodag import EODataAccessGateway

dag = EODataAccessGateway()
dag.set_preferred_provider("earth_search_cog")

# Here I forget to specify which productType I am looking for
cog_products, _ = dag.search()
path = dag.download(cog_products[0])
```
Raises
```
requests.exceptions.InvalidSchema: No connection adapters were found for 's3://sentinel-s2-l2a/tiles/42/F/UK/2021/11/23/0/qi/L2A_PVI.jp2'
```
To prevent this, we'd have to:

- [ ] only accept `S2_MSI_L2A_COG` product type with `earth_search_cog`
- [ ] filter out `S2_MSI_L2A_COG` product type with `earth_search`


Pull request description:
feat: guess eoproduct.product_type from properties
Guess `eoproduct.product_type` if missing using `eoproduct.properties`.

Needed by #364

Patch:
diff --git a/eodag/api/core.py b/eodag/api/core.py
index 0ce2f7fe..7d5d039b 100644
--- a/eodag/api/core.py
+++ b/eodag/api/core.py
@@ -17,6 +17,7 @@
 # limitations under the License.
 import logging
 import os
+import re
 import shutil
 from operator import itemgetter
 
@@ -1055,6 +1056,31 @@ class EODataAccessGateway(object):
             # be returned as a search result if there was no search extent (because we
             # will not try to do an intersection)
             for eo_product in res:
+                # if product_type is not defined, try to guess using properties
+                if eo_product.product_type is None:
+                    pattern = re.compile(r"[^\w,]+")
+                    try:
+                        guesses = self.guess_product_type(
+                            **{
+                                # k:str(v) for k,v in eo_product.properties.items()
+                                k: pattern.sub("", str(v).upper())
+                                for k, v in eo_product.properties.items()
+                                if k
+                                in [
+                                    "instrument",
+                                    "platform",
+                                    "platformSerialIdentifier",
+                                    "processingLevel",
+                                    "sensorType",
+                                    "keywords",
+                                ]
+                                and v is not None
+                            }
+                        )
+                    except NoMatchingProductType:
+                        pass
+                    else:
+                        eo_product.product_type = guesses[0]
                 if eo_product.search_intersection is not None:
                     download_plugin = self._plugins_manager.get_download_plugin(
                         eo_product


Test patch:
diff --git a/tests/integration/test_search_stac_static.py b/tests/integration/test_search_stac_static.py
index ee3483b0..71a31b4c 100644
--- a/tests/integration/test_search_stac_static.py
+++ b/tests/integration/test_search_stac_static.py
@@ -83,8 +83,8 @@ class TestSearchStacStatic(unittest.TestCase):
         self.assertIsInstance(items, SearchResult)
         self.assertEqual(len(items), self.child_cat_len)
         self.assertEqual(items[0].provider, self.stac_provider)
-        # if no product_type is provided, product_type is None
-        self.assertIsNone(items[0].product_type)
+        # if no product_type is provided, product_type should be guessed from properties
+        self.assertEqual(items[0].product_type, "S2_MSI_L1C")
 
     def test_search_stac_static_load_root_not_recursive(self):
         """load_stac_items from root must provide an empty list when no recursive"""
@@ -120,8 +120,8 @@ class TestSearchStacStatic(unittest.TestCase):
         self.assertIsInstance(item, SearchResult)
         self.assertEqual(len(item), 1)
         self.assertEqual(item[0].provider, self.stac_provider)
-        # if no product_type is provided, product_type is None
-        self.assertIsNone(item[0].product_type)
+        # if no product_type is provided, product_type should be guessed from properties
+        self.assertEqual(item[0].product_type, "S2_MSI_L1C")
 
     def test_search_stac_static_load_item_updated_provider(self):
         """load_stac_items from a single item using updated provider"""