"""GitHub Model""" __docformat__ = "numpy" # pylint: disable=C0201,W1401 import logging from typing import Any, Dict import math from datetime import datetime import requests import pandas as pd from openbb_terminal import config_terminal as cfg from openbb_terminal.decorators import log_start_end from openbb_terminal.rich_config import console from openbb_terminal.helper_funcs import get_user_agent logger = logging.getLogger(__name__) def get_github_data(url: str, **kwargs): """Get repository stats Parameters ---------- url: str github api endpoint params: dict params to pass to api endpoint Returns ------- dict with data """ res = requests.get( url, headers={ "Authorization": f"token {cfg.API_GITHUB_KEY}", "User-Agent": get_user_agent(), "Accept": "application/vnd.github.v3.star+json", }, **kwargs, ) if res.status_code == 200: return res.json() if res.status_code in (401, 403): console.print("[red]Rate limit reached, please provide a GitHub API key.[/red]") elif res.status_code == 404: console.print("[red]Repo not found.[/red]") else: console.print(f"[red]Error occurred {res.json()}[/red]") return None def search_repos( sortby: str = "stars", page: int = 1, categories: str = "" ) -> pd.DataFrame: """Get repos sorted by stars or forks. Can be filtered by categories Parameters ---------- sortby : str Sort repos by {stars, forks} categories : str Check for repo categories. If more than one separate with a comma: e.g., finance,investment. Default: None page : int Page number to get repos Returns ------- pd.DataFrame with list of repos """ params: Dict[str, Any] = {"page": page} if categories: params["sort"] = sortby params["q"] = categories.replace(",", "+") else: params["q"] = f"{sortby}:>1" data = get_github_data("https://api.github.com/search/repositories", params=params) if data and "items" in data: return pd.DataFrame(data["items"]) return pd.DataFrame() @log_start_end(log=logger) def get_stars_history(repo: str): """Get repository star history Parameters ---------- repo : str Repo to search for Format: org/repo, e.g., openbb-finance/openbbterminal Returns ------- pd.DataFrame - Columns: Date, Stars """ data = get_github_data(f"https://api.github.com/repos/{repo}") if data and "stargazers_count" in data: stars_number = data["stargazers_count"] stars: Dict[str, int] = {} pages = math.ceil(stars_number / 100) for page in range(0, pages): data = get_github_data( f"https://api.github.com/repos/{repo}/stargazers", params={"per_page": 100, "page": page}, ) if data: for star in data: day = star["starred_at"].split("T")[0] if day in stars: stars[day] += 1 else: stars[day] = 1 sorted_keys = sorted(stars.keys()) for i in range(1, len(sorted_keys)): stars[sorted_keys[i]] += stars[sorted_keys[i - 1]] df = pd.DataFrame( { "Date": [ datetime.strptime(date, "%Y-%m-%d").date() for date in stars.keys() ], "Stars": stars.values(), } ) df.set_index("Date") return df return pd.DataFrame() @log_start_end(log=logger) def get_top_repos(sortby: str, top: int, categories: str) -> pd.DataFrame: """Get repos sorted by stars or forks. Can be filtered by categories Parameters ---------- sortby : str Sort repos by {stars, forks} categories : str Check for repo categories. If more than one separate with a comma: e.g., finance,investment. Default: None top : int Number of repos to search for Returns ------- pd.DataFrame with list of repos """ initial_top = top df = pd.DataFrame( columns=[ "full_name", "open_issues", "stargazers_count", "forks_count", "language", "created_at", "updated_at", "html_url", ] ) if top <= 100: df2 = search_repos(sortby=sortby, page=1, categories=categories) df = pd.concat([df, df2], ignore_index=True) else: p = 2 while top > 0: df2 = search_repos(sortby=sortby, page=p, categories=categories) df = pd.concat([df, df2], ignore_index=True) top -= 100 p += 1 return df.head(initial_top) @log_start_end(log=logger) def get_repo_summary(repo: str): """Get repository summary Parameters ---------- repo : str Repo to search for Format: org/repo, e.g., openbb-finance/openbbterminal Returns ------- pd.DataFrame - Columns: Metric, Value """ data = get_github_data(f"https://api.github.com/repos/{repo}") if not data: return pd.DataFrame() release_data = get_github_data(f"https://api.github.com/repos/{repo}/releases") if not release_data: return pd.DataFrame() total_release_downloads: Any = "N/A" if len(release_data) > 0: total_release_downloads = 0 for asset in release_data[0]["assets"]: total_release_downloads += asset["download_count"] obj: Dict[str, Any] = { "Metric": [ "Name", "Owner", "Creation Date", "Last Update", "Topics", "Stars", "Forks", "Open Issues", "Language", "License", "Releases", "Last Release Downloads", ], "Value": [ data["name"], data["owner"]["login"], data["created_at"].split("T")[0], data["updated_at"].split("T")[0], ", ".join(data["topics"]), data["stargazers_count"], data["forks"], data["open_issues"], data["language"], data["license"]["name"], len(release_data), total_release_downloads, ], } return pd.DataFrame(obj)