Last active
September 1, 2024 09:02
-
-
Save Polkas/2b656d4a4497054a1b931cd3ae12ba1e to your computer and use it in GitHub Desktop.
pyshiny - compare R package DESCRIPTION files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import pyodide.http | |
| from shiny import App, reactive, render, ui, req | |
| from htmltools import TagList, div | |
| from htmltools.tags import th, td, tr, thead, tbody, table, style, head, span, a | |
| from re import compile, MULTILINE, split | |
| fields = ["Title", "Version", "Author", "Description", "License", "URL", | |
| "BugReports", "Encoding", "RoxygenNote", "Depends", "Imports", "LinkingTo", | |
| "Suggests", "Enhance", "Config/testthat/edition", "VignetteBuilder", "NeedsCompilation", | |
| "Packaged", "Repository", "Date/Publication"] | |
| fields_suggested = ["Title", "License", "Maintainer", "Maintaner", | |
| "Depends", "Imports", "LinkingTo", "Suggests"] | |
| base_github_raw = "https://raw.githubusercontent.com/cran" | |
| base_github_api = "https://api.github.com/repos/cran" | |
| av_cran = "https://cran.r-project.org/web/packages/available_packages_by_name.html" | |
| def parse_dcf(string: str) -> dict: | |
| """ | |
| Parse a string like Debian Control file | |
| Parameters: | |
| string (str): a string to parse, dcf format | |
| Returns: | |
| dict: dcf formatted to key-value structure | |
| """ | |
| split_regex = compile(r"^[A-Za-z-@/]+:\s", flags=MULTILINE) | |
| keys = [key[:-2].strip() for key in split_regex.findall(string)] | |
| values = [value.strip() for value in split(split_regex, string)[1:]] | |
| return dict(zip(keys, values)) | |
| def table_comparison(str1: str, str2: str, fields: list) -> TagList: | |
| """ | |
| Built a table from two dcf like formated strings | |
| Parameters: | |
| str1 (str): a string to parse, dcf format | |
| str2 (str): a string to parse, dcf format | |
| fields (list): a list with fields, please check R cran docs | |
| Returns: | |
| TagList: a html table with 3 columns | |
| """ | |
| str1_pre = str1.replace("\t", " ") | |
| str2_pre = str2.replace("\t", " ") | |
| dic1 = parse_dcf(str1_pre) | |
| dic2 = parse_dcf(str2_pre) | |
| res = list() | |
| for k in fields: | |
| res.append( | |
| tr( | |
| th(k), | |
| td(str(dic1.get(k, None))), | |
| td(str(dic2.get(k, None))) | |
| ) | |
| ) | |
| return TagList(table( | |
| thead( | |
| tr( | |
| th(""), | |
| th("Version: " + dic1.get("Version", "1")), | |
| th("Version: " + dic2.get("Version", "2")) | |
| ) | |
| ), | |
| tbody( | |
| res | |
| ) | |
| ).add_class("table table-striped")) | |
| app_ui = ui.page_fluid( | |
| ui.panel_title("Compare R DESCRIPTION files"), | |
| head(style(".control-label {font-weight: bold;}")), | |
| ui.page_sidebar( | |
| ui.sidebar( | |
| ui.input_text( | |
| "pac", | |
| span(a("CRAN", href = av_cran, class_="mx-2"), "Package:"), | |
| "shiny" | |
| ), | |
| ui.input_selectize( | |
| "fields", | |
| "Fields", | |
| choices = fields, | |
| selected = fields_suggested, | |
| multiple = True | |
| ), | |
| ui.input_select( | |
| "version_old", | |
| "Version 1:", | |
| choices = [""] | |
| ), | |
| ui.input_select( | |
| "version_new", | |
| "Version 2:", | |
| choices = [""] | |
| ) | |
| ), | |
| ui.card( | |
| ui.output_ui("info") | |
| ) | |
| ) | |
| ) | |
| def server(input, output, session): | |
| @reactive.Calc | |
| @reactive.event(input.version_old) | |
| async def pac_github_old(): | |
| response = await pyodide.http.pyfetch( | |
| base_github_raw + | |
| f"/{input.pac()}/{input.version_old()}/DESCRIPTION" | |
| ) | |
| if (response.status != 200): | |
| raise Exception(f"Error fetching") | |
| try: | |
| data = await response.string() | |
| except: | |
| data = None | |
| return data | |
| @reactive.Calc | |
| @reactive.event(input.version_new) | |
| async def pac_github_new(): | |
| response = await pyodide.http.pyfetch( | |
| base_github_raw + | |
| f"/{input.pac()}/{input.version_new()}/DESCRIPTION" | |
| ) | |
| if (response.status != 200): | |
| raise Exception(f"Error fetching") | |
| try: | |
| data = await response.string() | |
| except: | |
| data = "" | |
| return data | |
| @reactive.Calc | |
| @reactive.event(input.pac) | |
| async def tags_data(): | |
| response = await pyodide.http.pyfetch( | |
| base_github_api + f"/{input.pac()}/tags?per_page=500" | |
| ) | |
| if (response.status == 200): | |
| try: | |
| data = await response.json() | |
| except: | |
| data = "" | |
| else: | |
| data = "" | |
| return data | |
| @reactive.Effect() | |
| @reactive.event(input.pac) | |
| async def versions(): | |
| tags_pac_raw = await tags_data() | |
| tags_pac = [k["name"] for k in tags_pac_raw] | |
| tags_pac = list(filter(lambda x: not x.startswith("R-"), tags_pac)) | |
| if len(tags_pac) >= 2: | |
| ui.update_select( | |
| "version_old", | |
| choices = tags_pac, | |
| selected = tags_pac[1] | |
| ), | |
| ui.update_select( | |
| "version_new", | |
| choices = tags_pac, | |
| selected = tags_pac[0] | |
| ) | |
| else: | |
| ui.update_select( | |
| "version_old", | |
| choices = [""] | |
| ), | |
| ui.update_select( | |
| "version_new", | |
| choices = [""] | |
| ) | |
| @output | |
| @render.ui | |
| async def info(): | |
| req(input.version_old()) | |
| req(input.version_new()) | |
| data_old = await pac_github_old() | |
| data_new = await pac_github_new() | |
| if (data_old != "") and (data_new != ""): | |
| return div( | |
| table_comparison(data_old, data_new, input.fields()) | |
| ).add_class("row") | |
| else: | |
| return div("Failed") | |
| app = App(app_ui, server) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment