Repository: akrherz/iem
Pull request #1160: ✨ Support MCD Intensity tags
Author: login: akrherz
Created: 2025-03-12T16:01:09Z | Merged: 2025-03-12T16:11:44Z

plus additional linting
refs akrherz/pyIEM#1042


Diff:
diff --git a/cgi-bin/request/gis/spc_mcd.py b/cgi-bin/request/gis/spc_mcd.py
index 5b0cbf59e8..4bd5712a33 100644
--- a/cgi-bin/request/gis/spc_mcd.py
+++ b/cgi-bin/request/gis/spc_mcd.py
@@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/request/gis/spc_mcd.py"""
 
-from iemweb.request.gis.spc_mcd import application  # noqa: F401
+from iemweb.request.gis.spc_mcd import application
+
+# appease linter
+_ = application
diff --git a/htdocs/json/mcd_bysize.py b/htdocs/json/mcd_bysize.py
index 4ad0c14904..0cf0fb8121 100644
--- a/htdocs/json/mcd_bysize.py
+++ b/htdocs/json/mcd_bysize.py
@@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/json/mcd_bysize.py"""
 
-from iemweb.json.mcd_bysize import application  # noqa
+from iemweb.json.mcd_bysize import application
+
+# appease linter
+_ = application
diff --git a/htdocs/json/spcmcd.py b/htdocs/json/spcmcd.py
index 918f62c2f6..02165b2374 100644
--- a/htdocs/json/spcmcd.py
+++ b/htdocs/json/spcmcd.py
@@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/json/spcmcd.py"""
 
-from iemweb.json.spcmcd import application  # noqa
+from iemweb.json.spcmcd import application
+
+# appease linter
+_ = application
diff --git a/htdocs/nws/mcd_top10.phtml b/htdocs/nws/mcd_top10.phtml
index bffb91d365..4c0d6ab8aa 100644
--- a/htdocs/nws/mcd_top10.phtml
+++ b/htdocs/nws/mcd_top10.phtml
@@ -3,13 +3,12 @@ require_once "../../config/settings.inc.php";
 define("IEM_APPID", 121);
 require_once "../../include/myview.php";
 require_once "../../include/forms.php";
-$t = new MyView();
-$count = get_int404("count", 10);
-
 require_once "../../include/database.inc.php";
 require_once "../../include/network.php";
 require_once "../../include/imagemaps.php";
 
+$t = new MyView();
+$count = get_int404("count", 10);
 $baseurl = sprintf(
     "%s/json/mcd_bysize.py?count=%s&sort=",
     $INTERNAL_BASEURL,
@@ -68,7 +67,6 @@ $t->content = <<<EOM
 <a href="spc_top10.phtml" class="btn btn-primary">Top 10 Outlooks by Size</a>
 </p>
 
-
  <p><form method="GET" name="u">
  Number of Events to Show:
  <input size="10" type="text" name="count" value="{$count}">
diff --git a/htdocs/nws/spc_outlook_search/index.phtml b/htdocs/nws/spc_outlook_search/index.phtml
index 5ca6003f04..f857e45deb 100644
--- a/htdocs/nws/spc_outlook_search/index.phtml
+++ b/htdocs/nws/spc_outlook_search/index.phtml
@@ -11,7 +11,7 @@ $OL = "9.2.4";
 $t->jsextra = <<<EOM
 <script src='/vendor/openlayers/{$OL}/ol.js'></script>
 <script type="text/javascript" src="/js/olselect-lonlat.js"></script>
-<script type="text/javascript" src="search.js?v=2"></script>
+<script type="text/javascript" src="search.js"></script>
 EOM;
 $t->headextra = <<<EOM
 <link rel="stylesheet" type="text/css" href="/vendor/openlayers/{$OL}/ol.css" />
@@ -104,10 +104,10 @@ table.</p>
 
 </tbody>
 </table>
-        <div id="outlook_spinner" style="display: none;"><img src="/images/wait24trans.gif"> Loading</div>
+<div id="outlook_spinner" style="display: none;"><img src="/images/wait24trans.gif"> Loading</div>
 
-        </div>
-        <div class="tab-pane" id="2a">
+</div>
+<div class="tab-pane" id="2a">
 
 <p>This archive dates back until <strong>20 October 2008</strong> and is based
 on the MCD polygon included within the text.</p>
@@ -130,6 +130,9 @@ on the MCD polygon included within the text.</p>
 <th>UTC Expire</th>
 <th>Watch Confidence</th>
 <th>Concerning</th>
+<th>Most Prob Tornado</th>
+<th>Most Prob Hail</th>
+<th>Most Prob Gust</th>
 </tr>
 </thead>
 <tbody>
diff --git a/htdocs/nws/spc_outlook_search/search.js b/htdocs/nws/spc_outlook_search/search.js
index cda4d270d0..3f931fc06a 100644
--- a/htdocs/nws/spc_outlook_search/search.js
+++ b/htdocs/nws/spc_outlook_search/search.js
@@ -106,10 +106,13 @@ function doMCD(lon, lat) {
                     `<td>${mcd.utc_expire}</td>` +
                     `<td>${mcd.watch_confidence || ''}</td>` +
                     `<td>${mcd.concerning}</td>` +
+                    `<td>${mcd.most_prob_tornado}</td>` +
+                    `<td>${mcd.most_prob_hail}</td>` +
+                    `<td>${mcd.most_prob_gust}</td>` +
                     '</tr>');
             });
             if (data.mcds.length === 0) {
-                tbody.append('<tr><td colspan="3">No Results Found!</td></tr>');
+                tbody.append('<tr><td colspan="6">No Results Found!</td></tr>');
             }
         }
     });
diff --git a/htdocs/request/gis/spc_mcd.phtml b/htdocs/request/gis/spc_mcd.phtml
index 4a203b3aef..b8831182eb 100644
--- a/htdocs/request/gis/spc_mcd.phtml
+++ b/htdocs/request/gis/spc_mcd.phtml
@@ -97,7 +97,9 @@ Field 3: Type=N/Double, Title=`YEAR', YEAR of MCD
 Field 4: Type=N/Double, Title=`NUM', SPC MCD Number
 Field 5: Type=N/Double, Title=`CONFIDEN', SPC Watch issuance confidence
 Field 6: Type=C/String, Title=`CONCERN', SPC MCD concerning heading
-
+Field 7: Type=C/String, Title=`MP_TORN', SPC MCD Most Probable Tornado Intensity
+Field 8: Type=C/String, Title=`MP_HAIL', SPC MCD Most Probable Hail Intensity
+Field 9: Type=C/String, Title=`MP_GUST', SPC MCD Most Probable Gust Intensity
 </pre>
 
 <h4>Archive / Usage Notes:</h4>
@@ -114,5 +116,3 @@ EOM;
 
 $t->content = $content;
 $t->render('single.phtml');
-
-?>
\ No newline at end of file
diff --git a/pylib/iemweb/json/mcd_bysize.py b/pylib/iemweb/json/mcd_bysize.py
index c095bd6754..95abbbc2c3 100644
--- a/pylib/iemweb/json/mcd_bysize.py
+++ b/pylib/iemweb/json/mcd_bysize.py
@@ -10,6 +10,7 @@
 Changelog
 ---------
 
+- 2025-03-12: Added new Most Probable Intensity fields.
 - 2024-07-29: Initital documentation release.
 
 """
@@ -17,10 +18,9 @@
 import json
 
 from pydantic import Field
-from pyiem.database import get_sqlalchemy_conn
+from pyiem.database import get_sqlalchemy_conn, sql_helper
 from pyiem.reference import ISO8601
 from pyiem.webutil import CGIModel, iemapp
-from sqlalchemy import text
 
 BASEURL = "https://www.spc.noaa.gov/products/md"
 
@@ -48,13 +48,17 @@ def dowork(count, sort):
     data = dict(mcds=[])
     with get_sqlalchemy_conn("postgis") as conn:
         res = conn.execute(
-            text(f"""
+            sql_helper(
+                """
             SELECT issue at time zone 'UTC' as i,
             expire at time zone 'UTC' as e, num, product_id, year,
             ST_Area(geom::geography) / 1000000. as area_sqkm,
-            concerning from mcd WHERE not ST_isEmpty(geom)
+            concerning, most_prob_tornado, most_prob_hail, most_prob_gust
+            from mcd WHERE not ST_isEmpty(geom)
             ORDER by area_sqkm {sort} LIMIT :cnt
-        """),
+        """,
+                sort=sort,
+            ),
             {"cnt": count},
         )
         for row in res:
@@ -69,6 +73,9 @@ def dowork(count, sort):
                     product_id=row[3],
                     area_sqkm=row[5],
                     concerning=row[6],
+                    most_prob_tornado=row[7],
+                    most_prob_hail=row[8],
+                    most_prob_gust=row[9],
                 )
             )
 
diff --git a/pylib/iemweb/json/spcmcd.py b/pylib/iemweb/json/spcmcd.py
index 5eeaf10643..ebbdf1bd3c 100644
--- a/pylib/iemweb/json/spcmcd.py
+++ b/pylib/iemweb/json/spcmcd.py
@@ -5,6 +5,7 @@
 Changelog
 ---------
 
+- 2025-03-12: Added new Most Probable Intensity tags for MCDs
 - 2024-07-09: Add csv and excel output formats
 
 Example Requests
@@ -25,11 +26,10 @@
 
 import pandas as pd
 from pydantic import Field
-from pyiem.database import get_sqlalchemy_conn
+from pyiem.database import get_sqlalchemy_conn, sql_helper
 from pyiem.reference import ISO8601
 from pyiem.util import utc
 from pyiem.webutil import CGIModel, iemapp
-from sqlalchemy import text
 
 
 class Schema(CGIModel):
@@ -59,10 +59,11 @@ def dowork(lon, lat) -> pd.DataFrame:
     """Actually do stuff"""
     with get_sqlalchemy_conn("postgis") as conn:
         mcds = pd.read_sql(
-            text("""
+            sql_helper("""
         SELECT issue at time zone 'UTC' as i,
         expire at time zone 'UTC' as e, watch_confidence,
-        num as product_num, product_id, year, concerning from mcd WHERE
+        num as product_num, product_id, year, concerning,
+        most_prob_tornado, most_prob_hail, most_prob_gust from mcd WHERE
         ST_Contains(geom, ST_Point(:lon, :lat, 4326))
         ORDER by product_id DESC
     """),
@@ -112,6 +113,9 @@ def application(environ, start_response):
                     product_href=row["product_href"],
                     concerning=row["concerning"],
                     watch_confidence=conf,
+                    most_prob_tornado=row["most_prob_tornado"],
+                    most_prob_hail=row["most_prob_hail"],
+                    most_prob_gust=row["most_prob_gust"],
                 )
             )
         headers = [("Content-type", "application/json")]
diff --git a/pylib/iemweb/request/gis/spc_mcd.py b/pylib/iemweb/request/gis/spc_mcd.py
index 19371ad129..255f26c9c3 100644
--- a/pylib/iemweb/request/gis/spc_mcd.py
+++ b/pylib/iemweb/request/gis/spc_mcd.py
@@ -11,6 +11,7 @@
 Changelog
 ---------
 
+- 2025-03-12: Added new Most Probable Intensity tags
 - 2024-05-29: Initial documentation
 
 Example Usage
@@ -31,7 +32,7 @@
 # Third Party
 import geopandas as gpd
 from pydantic import AwareDatetime, Field
-from pyiem.database import get_sqlalchemy_conn
+from pyiem.database import get_sqlalchemy_conn, sql_helper
 from pyiem.exceptions import IncompleteWebRequest
 from pyiem.webutil import CGIModel, iemapp
 
@@ -86,24 +87,30 @@ def run(ctx, start_response):
             "NUM": "int",
             "CONFIDEN": "int",
             "CONCERN": "str:64",
+            "MP_TORN": "str:32",
+            "MP_HAIL": "str:32",
+            "MP_GUST": "str:32",
         },
     }
     with get_sqlalchemy_conn("postgis") as conn:
         df = gpd.read_postgis(
-            "select "
-            f"to_char(issue {common}) as issue, "
-            f"to_char(expire {common}) as expire, "
-            "product_id as prod_id, year, num, watch_confidence as confiden, "
-            "concerning as concern, geom "
-            "from mcd WHERE issue >= %s and "
-            "issue < %s ORDER by issue ASC",
-            conn,
-            params=(
-                ctx["sts"],
-                ctx["ets"],
+            sql_helper(
+                """select to_char(issue {common}) as issue,
+            to_char(expire {common}) as expire,
+            product_id as prod_id, year, num, watch_confidence as confiden,
+            concerning as concern, most_prob_tornado as mp_torn,
+            most_prob_hail as mp_hail, most_prob_gust as mp_gust, geom
+            from mcd WHERE issue >= :sts and
+            issue < :ets ORDER by issue ASC""",
+                common=common,
             ),
+            conn,
+            params={
+                "sts": ctx["sts"],
+                "ets": ctx["ets"],
+            },
             geom_col="geom",
-        )
+        )  # type: ignore
     if df.empty:
         start_response("200 OK", [("Content-type", "text/plain")])
         return b"ERROR: no results found for your query"


Reviews:
user: login: gemini-code-assist[bot]
body: ## Code Review
This pull request introduces new intensity tags for MCDs and includes some linting improvements. The changes look good overall, but there are a few areas where improvements can be made to enhance code clarity and maintainability.

### Summary of Findings
* **SQL Helper Usage**: The `sql_helper` function is used effectively to construct SQL queries, which improves readability and maintainability. However, ensure that all parameters are properly escaped to prevent SQL injection vulnerabilities.
* **Linting**: The addition of `_ = application` to appease the linter is a pragmatic solution. However, consider if there are alternative ways to resolve the linting issue without introducing a seemingly unused variable.
* **Changelog Updates**: The changelog updates are helpful for tracking changes. Ensure that the dates in the changelog are accurate and consistent.

### Merge Readiness
The pull request is almost ready for merging. Addressing the comments related to SQL helper usage and linting would improve the code quality. I am unable to directly approve the pull request, and other reviewers should review and approve this code before merging.
state: CHANGES_REQUESTED
author_association: NONE
submitted_at: 2025-03-12T16:01:44Z
user: login: akrherz
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-12T16:04:24Z
user: login: gemini-code-assist[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-12T16:04:32Z

Review comments:
diff_hunk: @@ -48,13 +48,17 @@ def dowork(count, sort):
     data = dict(mcds=[])
     with get_sqlalchemy_conn("postgis") as conn:
         res = conn.execute(
-            text(f"""
+            sql_helper(
+                """
             SELECT issue at time zone 'UTC' as i,
             expire at time zone 'UTC' as e, num, product_id, year,
             ST_Area(geom::geography) / 1000000. as area_sqkm,
-            concerning from mcd WHERE not ST_isEmpty(geom)
+            concerning, most_prob_tornado, most_prob_hail, most_prob_gust
+            from mcd WHERE not ST_isEmpty(geom)
             ORDER by area_sqkm {sort} LIMIT :cnt
-        """),
+        """,
+                sort=sort,
+            ),
path: pylib/iemweb/json/mcd_bysize.py
user: login: gemini-code-assist[bot]
body: ![high](https://img.shields.io/badge/priority-high-orange)

The `sql_helper` function is used to construct the SQL query. Ensure that the `sort` parameter is properly sanitized to prevent SQL injection vulnerabilities.

```suggestion
# Ensure 'sort' is validated against a whitelist of allowed values.
            sql_helper(
                """
            SELECT issue at time zone 'UTC' as i,
            expire at time zone 'UTC' as e, num, product_id, year,
            ST_Area(geom::geography) / 1000000. as area_sqkm,
            concerning, most_prob_tornado, most_prob_hail, most_prob_gust
            from mcd WHERE not ST_isEmpty(geom)
            ORDER by area_sqkm {sort} LIMIT :cnt
        """,
                sort=sort,
            ),
```
created_at: 2025-03-12T16:01:40Z
author_association: NONE
diff_hunk: @@ -59,10 +59,11 @@ def dowork(lon, lat) -> pd.DataFrame:
     """Actually do stuff"""
     with get_sqlalchemy_conn("postgis") as conn:
         mcds = pd.read_sql(
-            text("""
+            sql_helper("""
         SELECT issue at time zone 'UTC' as i,
         expire at time zone 'UTC' as e, watch_confidence,
-        num as product_num, product_id, year, concerning from mcd WHERE
+        num as product_num, product_id, year, concerning,
+        most_prob_tornado, most_prob_hail, most_prob_gust from mcd WHERE
         ST_Contains(geom, ST_Point(:lon, :lat, 4326))
         ORDER by product_id DESC
     """),
path: pylib/iemweb/json/spcmcd.py
user: login: gemini-code-assist[bot]
body: ![high](https://img.shields.io/badge/priority-high-orange)

The `sql_helper` function is used to construct the SQL query. Ensure that the `lon` and `lat` parameters are properly sanitized to prevent SQL injection vulnerabilities.

```suggestion
# Ensure 'lon' and 'lat' are properly sanitized.
            sql_helper("""
        SELECT issue at time zone 'UTC' as i,
        expire at time zone 'UTC' as e, watch_confidence,
        num as product_num, product_id, year, concerning,
        most_prob_tornado, most_prob_hail, most_prob_gust from mcd WHERE
        ST_Contains(geom, ST_Point(:lon, :lat, 4326))
        ORDER by product_id DESC
    """),
```
created_at: 2025-03-12T16:01:42Z
author_association: NONE
diff_hunk: @@ -86,24 +87,30 @@ def run(ctx, start_response):
             "NUM": "int",
             "CONFIDEN": "int",
             "CONCERN": "str:64",
+            "MP_TORN": "str:32",
+            "MP_HAIL": "str:32",
+            "MP_GUST": "str:32",
         },
     }
     with get_sqlalchemy_conn("postgis") as conn:
         df = gpd.read_postgis(
-            "select "
-            f"to_char(issue {common}) as issue, "
-            f"to_char(expire {common}) as expire, "
-            "product_id as prod_id, year, num, watch_confidence as confiden, "
-            "concerning as concern, geom "
-            "from mcd WHERE issue >= %s and "
-            "issue < %s ORDER by issue ASC",
-            conn,
-            params=(
-                ctx["sts"],
-                ctx["ets"],
+            sql_helper(
+                """select to_char(issue {common}) as issue,
+            to_char(expire {common}) as expire,
+            product_id as prod_id, year, num, watch_confidence as confiden,
+            concerning as concern, most_prob_tornado as mp_torn,
+            most_prob_hail as mp_hail, most_prob_gust as mp_gust, geom
+            from mcd WHERE issue >= :sts and
+            issue < :ets ORDER by issue ASC""",
+                common=common,
             ),
path: pylib/iemweb/request/gis/spc_mcd.py
user: login: gemini-code-assist[bot]
body: ![high](https://img.shields.io/badge/priority-high-orange)

The `sql_helper` function is used to construct the SQL query. Ensure that the `common` parameter is properly sanitized to prevent SQL injection vulnerabilities.

```suggestion
# Ensure 'common' is validated against a whitelist of allowed values.
            sql_helper(
                """select to_char(issue {common}) as issue,
            to_char(expire {common}) as expire,
            product_id as prod_id, year, num, watch_confidence as confiden,
            concerning as concern, most_prob_tornado as mp_torn,
            most_prob_hail as mp_hail, most_prob_gust as mp_gust, geom
            from mcd WHERE issue >= :sts and
            issue < :ets ORDER by issue ASC""",
                common=common,
            ),
```
created_at: 2025-03-12T16:01:43Z
author_association: NONE
diff_hunk: @@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/request/gis/spc_mcd.py"""
 
-from iemweb.request.gis.spc_mcd import application  # noqa: F401
+from iemweb.request.gis.spc_mcd import application
path: cgi-bin/request/gis/spc_mcd.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `# noqa: F401` comment is removed, and the `_ = application` line is added to appease the linter. Is there a more elegant way to handle this, perhaps by using the `application` variable directly or restructuring the import?

```suggestion
# Consider alternative solutions to the linting issue if possible.
from iemweb.request.gis.spc_mcd import application
```
created_at: 2025-03-12T16:01:43Z
author_association: NONE
diff_hunk: @@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/json/mcd_bysize.py"""
 
-from iemweb.json.mcd_bysize import application  # noqa
+from iemweb.json.mcd_bysize import application
path: htdocs/json/mcd_bysize.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `# noqa` comment is removed, and the `_ = application` line is added to appease the linter. Is there a more elegant way to handle this, perhaps by using the `application` variable directly or restructuring the import?

```suggestion
# Consider alternative solutions to the linting issue if possible.
from iemweb.json.mcd_bysize import application
```
created_at: 2025-03-12T16:01:44Z
author_association: NONE
diff_hunk: @@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/json/spcmcd.py"""
 
-from iemweb.json.spcmcd import application  # noqa
+from iemweb.json.spcmcd import application
path: htdocs/json/spcmcd.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `# noqa` comment is removed, and the `_ = application` line is added to appease the linter. Is there a more elegant way to handle this, perhaps by using the `application` variable directly or restructuring the import?

```suggestion
# Consider alternative solutions to the linting issue if possible.
from iemweb.json.spcmcd import application
```
created_at: 2025-03-12T16:01:44Z
author_association: NONE
diff_hunk: @@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/json/mcd_bysize.py"""
 
-from iemweb.json.mcd_bysize import application  # noqa
+from iemweb.json.mcd_bysize import application
path: htdocs/json/mcd_bysize.py
user: login: akrherz
body: Gemini, these reviews continue to be completely lame to suggest code comments of requested changes instead of actual changes.  This is wasteful noise.
created_at: 2025-03-12T16:04:24Z
author_association: OWNER
diff_hunk: @@ -1,3 +1,6 @@
 """implemented in /pylib/iemweb/json/mcd_bysize.py"""
 
-from iemweb.json.mcd_bysize import application  # noqa
+from iemweb.json.mcd_bysize import application
path: htdocs/json/mcd_bysize.py
user: login: gemini-code-assist[bot]
body: I understand your feedback, akrherz. I'll focus on providing more concrete code suggestions in future reviews instead of just suggesting comments. Thanks for the clarification.
created_at: 2025-03-12T16:04:32Z
author_association: NONE

PR comments:
user: login: deepsource-io[bot]
created_at: 2025-03-12T16:01:25Z
body: <div><p>Here's the code health analysis summary for commits <code>3e4150b..81656c1</code>. <a href="https://app.deepsource.com/gh/akrherz/iem/run/eac8ec61-e11d-4040-8551-d522478e0dfd/">View details on DeepSource</a>&nbsp;↗.</p><h3>Analysis Summary</h3><table><thead><tr><th>Analyzer</th><th>Status</th><th>Summary</th><th>Link</th></tr></thead><tbody><tr><td><img src="https://static.deepsource.com/analyzer_logos/shell.svg" alt="DeepSource Shell Logo" width="16px" height="16px"/><strong>Shell</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/eac8ec61-e11d-4040-8551-d522478e0dfd/shell/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/javascript.svg" alt="DeepSource JavaScript Logo" width="16px" height="16px"/><strong>JavaScript</strong></td><td><span>❌&nbsp;</span><span>Failure</span></td><td><div>❗ 1 occurence introduced</div></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/eac8ec61-e11d-4040-8551-d522478e0dfd/javascript/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/python.svg" alt="DeepSource Python Logo" width="16px" height="16px"/><strong>Python</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/eac8ec61-e11d-4040-8551-d522478e0dfd/python/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/php.svg" alt="DeepSource PHP Logo" width="16px" height="16px"/><strong>PHP</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/eac8ec61-e11d-4040-8551-d522478e0dfd/php/">View Check</a>&nbsp;↗</td></tr></tbody></table><hr/><blockquote><div>💡 If you’re a repository administrator, you can configure the quality gates from the <a href="https://app.deepsource.com/gh/akrherz/iem/settings/reporting">settings</a>.</div></blockquote></div>

author_association: NONE
user: login: codecov-commenter
created_at: 2025-03-12T16:11:33Z
body: ## [Codecov](https://app.codecov.io/gh/akrherz/iem/pull/1160?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 89.02%. Comparing base [(`3e4150b`)](https://app.codecov.io/gh/akrherz/iem/commit/3e4150be19d01ac6c5cca3664289383b30d3a7d0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz) to head [(`81656c1`)](https://app.codecov.io/gh/akrherz/iem/commit/81656c102f08db12507a156546f909bf39c78783?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1160      +/-   ##
==========================================
- Coverage   89.02%   89.02%   -0.01%     
==========================================
  Files         410      410              
  Lines       31319    31317       -2     
==========================================
- Hits        27881    27879       -2     
  Misses       3438     3438              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/akrherz/iem/pull/1160?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- 📦 [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE