Spaces:
Sleeping
Sleeping
fix: filter noise tags (tautological + language duplicates) from all views
Browse files
app.py
CHANGED
|
@@ -7,6 +7,36 @@ import os
|
|
| 7 |
import logging
|
| 8 |
from datetime import datetime, timedelta, timezone
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
# Configure logging
|
| 11 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S")
|
| 12 |
logger = logging.getLogger("govtech-dashboard")
|
|
@@ -79,8 +109,11 @@ def load_filter_options():
|
|
| 79 |
languages = pd.read_sql_query(
|
| 80 |
"SELECT DISTINCT language FROM repositories WHERE language IS NOT NULL AND language != '' ORDER BY language", conn
|
| 81 |
)["language"].tolist()
|
|
|
|
|
|
|
| 82 |
tags = pd.read_sql_query(
|
| 83 |
-
"SELECT tag, COUNT(
|
|
|
|
| 84 |
)["tag"].tolist()
|
| 85 |
orgs = pd.read_sql_query(
|
| 86 |
"SELECT owner, COUNT(*) as c FROM repositories GROUP BY owner ORDER BY c DESC LIMIT 300", conn
|
|
@@ -380,15 +413,17 @@ with tab_tags:
|
|
| 380 |
|
| 381 |
with col_tl:
|
| 382 |
st.subheader("Top Tags")
|
|
|
|
|
|
|
| 383 |
df_top_tags = query_df(
|
| 384 |
f"""SELECT rt2.tag, COUNT(DISTINCT r.html_url) as count
|
| 385 |
FROM repositories r
|
| 386 |
JOIN repository_tags rt2 ON r.html_url = rt2.html_url
|
| 387 |
{tag_join_t.replace("rt", "rt_f") if sel_tags else ""}
|
| 388 |
{where_t.replace("rt.", "rt2.") if where_t else ""}
|
| 389 |
-
{"AND" if where_t else "WHERE"}
|
| 390 |
-
GROUP BY rt2.tag ORDER BY count DESC LIMIT 30""",
|
| 391 |
-
params_t,
|
| 392 |
)
|
| 393 |
if not df_top_tags.empty:
|
| 394 |
fig = px.bar(
|
|
@@ -690,6 +725,8 @@ with tab_trends:
|
|
| 690 |
w_base = (" AND ".join(conds_base)) if conds_base else ""
|
| 691 |
and_base = ("AND " + w_base) if w_base else ""
|
| 692 |
|
|
|
|
|
|
|
| 693 |
df_momentum = query_df(
|
| 694 |
f"""
|
| 695 |
SELECT
|
|
@@ -698,13 +735,13 @@ with tab_trends:
|
|
| 698 |
COUNT(DISTINCT CASE WHEN r.created_at >= ? AND r.created_at < ? THEN r.html_url END) as prior
|
| 699 |
FROM repository_tags rt
|
| 700 |
JOIN repositories r ON rt.html_url = r.html_url
|
| 701 |
-
WHERE r.created_at IS NOT NULL {and_base}
|
| 702 |
GROUP BY rt.tag
|
| 703 |
HAVING recent >= 5 AND prior >= 5
|
| 704 |
ORDER BY (CAST(recent AS FLOAT) / prior) DESC
|
| 705 |
LIMIT 30
|
| 706 |
""",
|
| 707 |
-
[cutoff_12m, cutoff_24m_str, cutoff_12m] + params_base,
|
| 708 |
)
|
| 709 |
|
| 710 |
if not df_momentum.empty:
|
|
@@ -734,10 +771,10 @@ with tab_trends:
|
|
| 734 |
f"""
|
| 735 |
SELECT rt.tag, COUNT(DISTINCT r.html_url) as recent_count
|
| 736 |
FROM repository_tags rt JOIN repositories r ON rt.html_url = r.html_url
|
| 737 |
-
WHERE r.created_at >= ? {and_base}
|
| 738 |
GROUP BY rt.tag ORDER BY recent_count DESC LIMIT 20
|
| 739 |
""",
|
| 740 |
-
[cutoff_12m] + params_base,
|
| 741 |
)
|
| 742 |
if not df_recent_top.empty:
|
| 743 |
fig2 = px.bar(
|
|
|
|
| 7 |
import logging
|
| 8 |
from datetime import datetime, timedelta, timezone
|
| 9 |
|
| 10 |
+
# Tags that describe the dataset itself rather than individual repos — excluded from all charts/filters
|
| 11 |
+
BLOCKLIST_TAGS = frozenset([
|
| 12 |
+
"government", "open-source", "public-sector", "open-government",
|
| 13 |
+
"government-software", "government-tool", "government-project",
|
| 14 |
+
"government-repository", "government-platform", "government-code",
|
| 15 |
+
])
|
| 16 |
+
|
| 17 |
+
# Tags that duplicate the language field already in the schema
|
| 18 |
+
LANGUAGE_TAGS = frozenset([
|
| 19 |
+
"javascript", "python", "java", "typescript", "html", "css", "php",
|
| 20 |
+
"ruby", "shell", "r", "scala", "c#", "kotlin", "go", "rust", "c",
|
| 21 |
+
"c++", "perl", "swift", "matlab", "bash", "json", "xml", "yaml",
|
| 22 |
+
"sql", "makefile",
|
| 23 |
+
])
|
| 24 |
+
|
| 25 |
+
# Combined filter — tags to hide from dashboard display
|
| 26 |
+
EXCLUDED_TAGS = BLOCKLIST_TAGS | LANGUAGE_TAGS
|
| 27 |
+
|
| 28 |
+
# Minimum repos a tag must appear in to show in charts/filters
|
| 29 |
+
MIN_TAG_REPOS = 2
|
| 30 |
+
|
| 31 |
+
def _tag_filter_sql(tag_col: str = "tag") -> str:
|
| 32 |
+
"""Return a SQL fragment excluding noise tags. Use with AND."""
|
| 33 |
+
excluded = EXCLUDED_TAGS
|
| 34 |
+
ph = ",".join(["?"] * len(excluded))
|
| 35 |
+
return f"{tag_col} NOT IN ({ph}) AND {tag_col} IS NOT NULL"
|
| 36 |
+
|
| 37 |
+
def _tag_filter_params() -> list:
|
| 38 |
+
return list(EXCLUDED_TAGS)
|
| 39 |
+
|
| 40 |
# Configure logging
|
| 41 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S")
|
| 42 |
logger = logging.getLogger("govtech-dashboard")
|
|
|
|
| 109 |
languages = pd.read_sql_query(
|
| 110 |
"SELECT DISTINCT language FROM repositories WHERE language IS NOT NULL AND language != '' ORDER BY language", conn
|
| 111 |
)["language"].tolist()
|
| 112 |
+
tf_sql = _tag_filter_sql()
|
| 113 |
+
tf_params = _tag_filter_params()
|
| 114 |
tags = pd.read_sql_query(
|
| 115 |
+
f"SELECT tag, COUNT(DISTINCT html_url) as c FROM repository_tags WHERE {tf_sql} GROUP BY tag HAVING c >= {MIN_TAG_REPOS} ORDER BY c DESC",
|
| 116 |
+
conn, params=tf_params
|
| 117 |
)["tag"].tolist()
|
| 118 |
orgs = pd.read_sql_query(
|
| 119 |
"SELECT owner, COUNT(*) as c FROM repositories GROUP BY owner ORDER BY c DESC LIMIT 300", conn
|
|
|
|
| 413 |
|
| 414 |
with col_tl:
|
| 415 |
st.subheader("Top Tags")
|
| 416 |
+
tf_sql_t = _tag_filter_sql("rt2.tag")
|
| 417 |
+
tf_params_t = _tag_filter_params()
|
| 418 |
df_top_tags = query_df(
|
| 419 |
f"""SELECT rt2.tag, COUNT(DISTINCT r.html_url) as count
|
| 420 |
FROM repositories r
|
| 421 |
JOIN repository_tags rt2 ON r.html_url = rt2.html_url
|
| 422 |
{tag_join_t.replace("rt", "rt_f") if sel_tags else ""}
|
| 423 |
{where_t.replace("rt.", "rt2.") if where_t else ""}
|
| 424 |
+
{"AND" if where_t else "WHERE"} {tf_sql_t}
|
| 425 |
+
GROUP BY rt2.tag HAVING count >= {MIN_TAG_REPOS} ORDER BY count DESC LIMIT 30""",
|
| 426 |
+
params_t + tf_params_t,
|
| 427 |
)
|
| 428 |
if not df_top_tags.empty:
|
| 429 |
fig = px.bar(
|
|
|
|
| 725 |
w_base = (" AND ".join(conds_base)) if conds_base else ""
|
| 726 |
and_base = ("AND " + w_base) if w_base else ""
|
| 727 |
|
| 728 |
+
tf_sql_mom = _tag_filter_sql("rt.tag")
|
| 729 |
+
tf_params_mom = _tag_filter_params()
|
| 730 |
df_momentum = query_df(
|
| 731 |
f"""
|
| 732 |
SELECT
|
|
|
|
| 735 |
COUNT(DISTINCT CASE WHEN r.created_at >= ? AND r.created_at < ? THEN r.html_url END) as prior
|
| 736 |
FROM repository_tags rt
|
| 737 |
JOIN repositories r ON rt.html_url = r.html_url
|
| 738 |
+
WHERE r.created_at IS NOT NULL AND {tf_sql_mom} {and_base}
|
| 739 |
GROUP BY rt.tag
|
| 740 |
HAVING recent >= 5 AND prior >= 5
|
| 741 |
ORDER BY (CAST(recent AS FLOAT) / prior) DESC
|
| 742 |
LIMIT 30
|
| 743 |
""",
|
| 744 |
+
[cutoff_12m, cutoff_24m_str, cutoff_12m] + tf_params_mom + params_base,
|
| 745 |
)
|
| 746 |
|
| 747 |
if not df_momentum.empty:
|
|
|
|
| 771 |
f"""
|
| 772 |
SELECT rt.tag, COUNT(DISTINCT r.html_url) as recent_count
|
| 773 |
FROM repository_tags rt JOIN repositories r ON rt.html_url = r.html_url
|
| 774 |
+
WHERE r.created_at >= ? AND {tf_sql_mom} {and_base}
|
| 775 |
GROUP BY rt.tag ORDER BY recent_count DESC LIMIT 20
|
| 776 |
""",
|
| 777 |
+
[cutoff_12m] + tf_params_mom + params_base,
|
| 778 |
)
|
| 779 |
if not df_recent_top.empty:
|
| 780 |
fig2 = px.bar(
|