diff --git a/openstats/__init__.py b/openstats/__init__.py index ae1ecc2..125b451 100644 --- a/openstats/__init__.py +++ b/openstats/__init__.py @@ -1,3 +1,3 @@ """Discover and share your OpenSource KPIs""" -__version__ = "0.1.9.2" +__version__ = "0.1.9.3" diff --git a/openstats/app.py b/openstats/app.py index 1f4f1da..dce5d15 100644 --- a/openstats/app.py +++ b/openstats/app.py @@ -20,6 +20,8 @@ def stats(cfg: Config): st.markdown("---") builder.good_first_issues_component() st.markdown("---") + builder.support_issues_component() + st.markdown("---") builder.contributors_component() st.markdown("---") builder.traffic_component() diff --git a/openstats/components.py b/openstats/components.py index b81c08c..05d71e4 100644 --- a/openstats/components.py +++ b/openstats/components.py @@ -84,6 +84,19 @@ def good_first_issues_component(self): open_issues.metric("Open good first issues", len(open_gfi)) closed_issues.metric("Closed good first issues", len(closed_gfi)) + def support_issues_component(self): + """ + Present the good first issues + """ + open_supp, closed_supp = self.data.support_issues_data() + + with st.container(): + st.subheader("Support issues") + + open_issues, closed_issues = st.columns(2) + open_issues.metric("Open support issues", len(open_supp)) + closed_issues.metric("Closed support issues", len(closed_supp)) + @staticmethod def clear_cache_button(): """ diff --git a/openstats/data.py b/openstats/data.py index 81612f0..90a2d3d 100644 --- a/openstats/data.py +++ b/openstats/data.py @@ -4,7 +4,7 @@ """ from collections import Counter from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Callable, Dict, List, Optional, Tuple import pandas as pd from dateutil import parser @@ -108,19 +108,37 @@ def is_good_first_issue(issue: List[Dict[str, Any]]) -> Optional[Dict[str, Any]] return None - def good_first_issues_data(self) -> Tuple[List[dict], List[dict]]: + @staticmethod + def is_support_issue(issue: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]: """ - Analyze issues data for open and closed good - first issues. + Check if an issue is a support issue + """ + if isinstance(issue, dict): + is_support = next( + iter( + label + for label in issue.get("labels") + if isinstance(label, dict) and label.get("name") == "support" + ), + None, + ) + + return is_support + + return None + + def _issues_data(self, filter_fn: Callable) -> Tuple[List[dict], List[dict]]: + """ + Return issue data with callable filtering. + + filter_fn should return True / False from a list of issues """ open_issues = self.client.get_all( self.client.root / "repos" / self.client.owner / self.client.repo / "issues" ) - open_first_issues = [ - issue for issue in open_issues if self.is_good_first_issue(issue) - ] + open_filtered_issues = [issue for issue in open_issues if filter_fn(issue)] closed_issues = self.client.get_all( self.client.root @@ -131,11 +149,24 @@ def good_first_issues_data(self) -> Tuple[List[dict], List[dict]]: "&state=closed", ) - closed_first_issues = [ - issue for issue in closed_issues if self.is_good_first_issue(issue) - ] + closed_filtered_issues = [issue for issue in closed_issues if filter_fn(issue)] + + return open_filtered_issues, closed_filtered_issues + + def good_first_issues_data(self) -> Tuple[List[dict], List[dict]]: + """ + Analyze issues data for open and closed good + first issues. + """ + + return self._issues_data(filter_fn=self.is_good_first_issue) + + def support_issues_data(self) -> Tuple[List[dict], List[dict]]: + """ + Analyzes issues data for open and closed support issues + """ - return open_first_issues, closed_first_issues + return self._issues_data(filter_fn=self.is_support_issue) def contributors_data(self): """