EpiVerse
  • Organization
  • Repositories
  • Maintainers
  • Community Health
    • Source Code
    • Request a Feature

On this page

  • pkgcheck Summary
  • R-universe summary
  • CRAN Summary
  • Statistical properties
  • Function usage in other packages
  • CHAOSS metrics and models

Repositories

function sparkbar(max) {
  const colourScale = d3.scaleSequential(d3.interpolateCool)
    .domain([0, max]);

  return (x) => htl.html`<div style="
    background: ${colourScale(x)};
    color: black;
    width: ${100 * x / max}%;
    float: right;
    padding-right: 3px;
    box-sizing: border-box;
    overflow: visible;
    display: flex;
    justify-content: end;">${x.toFixed(2).toLocaleString("en-US")}`
}
metricsTranspose = {
    return transpose(metrics_in).map(row => ({
        ...row,
    }));
}
repo_src = {
    return transpose(repo_src_in).map(row => ({
        ...row,
    }));
}
ctb_absTranspose = {
    return transpose(ctb_abs_in).map(row => ({
        ...row,
    }));
}
issue_respTranspose = {
    return transpose(issue_resp_in).map(row => ({
        ...row,
    }));
}
issue_bugsTranspose = {
    return transpose(issue_bugs_in).map(row => ({
        ...row,
    }));
}
depsTranspose = {
    return transpose(deps_in).map(row => ({
        ...row,
    }));
}
fnCallsTranspose = {
    return transpose(fn_calls_in).map(row => ({
        ...row,
    }));
}
json_data = FileAttachment("results-json-data.json").json();
gitlogDataTranspose = json_data['gitlog'].map(row => ({
    ...row,
    first_commit: new Date(row.first_commit)
}));

cranDataAll = json_data['cran'];
not_cran_in = json_data['not_cran'];
repos_on_r_univ = json_data['r_universe']['data_is_on_r_univ'];
data_r_univ_jobs = json_data['r_universe']['r_univ_jobs'];
data_r_univ_builds = json_data['r_universe']['r_univ_builds'];
reposAll = metricsTranspose.map(function(item) {
    return item.package;
});
repos = Array.from(new Set(reposAll));
repoSet = localStorage.getItem("orgmetricsRepo") || repos [0]
viewof repo = Inputs.select(
    repos,
    {
        multiple: false,
        value: repoSet,
        label: htl.html`<b>Repository:</b>`
    }
)
s = localStorage.setItem("orgmetricsRepo", repo.toString());
repoURL = repo_src.filter(function(r) {
    return r.package === repo
})[0].url;
htl.html`<div>
Click here for the source code of the
<a href=${repoURL} target="_blank"><i>${repo}</i> repository</a>.
</div>`
ctb_abs = ctb_absTranspose.filter(function(r) {
    return r.repo === repo
})
issue_resp = issue_respTranspose.filter(function(r) {
    return r.repo === repo
})
issue_bugs = issue_bugsTranspose.filter(function(r) {
    return r.repo === repo
})
deps = depsTranspose.filter(function(r) {
    return r.package === repo
})
fn_calls = fnCallsTranspose.filter(function(r) {
    return r.package === repo
})
has_fn_calls = fn_calls.length > 0;
cran_data = cranDataAll.filter(function(r) {
    return r.package === repo
})
has_cran_data = cran_data.length > 0;
not_on_cran = not_cran_in.includes(repo);
gitlog = gitlogDataTranspose.filter(function(r) {
    return r.package === repo
})
gitlog_total_commits = gitlog[0]['num_commits'].toLocaleString();
gitlog_first_commit = gitlog[0]['first_commit'].toLocaleDateString("en-GB", {
    month: 'short',
    year: 'numeric'
});

r_univ_jobs = data_r_univ_jobs[repo];
r_univ_builds = data_r_univ_builds[repo];

r_univ_page = repos_on_r_univ.filter(function(r) {
    return r.package === repo
})
repoMetrics = {
    return transpose(repo_metrics_in[repo]).map(row => ({
        ...row,
    }))
}
ctbs = {
    return transpose(ctbs_in[repo]).map(row => ({
        ...row,
    }))
}
ctbs_gh = ctbs.map((ctb) => ctb['gh_handle']).filter(item => item != null);
rank_index = rank_names_in.indexOf(repo);
rank_vector = ranks_in[rank_index];
order_vector = Array
    .from(rank_vector.keys())
    .sort((a, b) => rank_vector[b] - rank_vector[a])
    .filter(item => item !== rank_index);
num_similar = 5
order_vector_sub = order_vector.slice(0, num_similar);
matched_names = order_vector_sub.map(i => rank_names_in[i]);

similar_pkgs = matched_names.map(i => {
    const url = (repo_src.filter(function(r) {
        return r.package === i
    }, i)[0] || {}).url || null;
    return htl.html`<li><a href="/repometrics-demo/repo.html" onclick="localStorage.setItem('orgmetricsRepo', '${i}')">${i}</a></li>`
});
function pluraliseObjects(x, what) {
    if (x === 0) {
        return "No " + what + "s";
    } else if (x === 1) {
        return "1 " + what;
    } else {
        return x + " " + what + "s";
    }
}

gitlog_txt = htl.html`<li>${gitlog_total_commits} commits since ${gitlog_first_commit}</li>`;

maintainer_count = repoMetrics.filter(function(m) {
    return m.name == "maintainer_count"
})[0].value;
maintainer_count_txt = pluraliseObjects(maintainer_count, "primary maintainer");

maintainer_gh = [].concat(ctbs_gh.slice(0, maintainer_count) || []);
maintainer_gh_list = maintainer_gh.length == 0 ? undefined:
    (maintainer_gh.length == 1 ? htl.html`
<div onclick=${() => localStorage.setItem('orgmetricsMaintainer', maintainer_gh)}>
<li><a href='/repometrics-demo/maintainer.html'>${maintainer_gh}</a></li>
</div>
` : maintainer_gh.map(m => htl.html`
<div onclick=${() => localStorage.setItem('orgmetricsMaintainer', m)}>
<li><a href='/repometrics-demo/maintainer.html'>${m}</a></li>
</div>
`));
maintainer_gh_list_txt = maintainer_gh.length > 0 ? htl.html`<ul>${maintainer_gh_list}</ul>` : undefined;

ctb_gh = [].concat(ctbs_gh.slice(maintainer_count) || []);
ctb_count = ctb_gh.length;
ctb_count_txt = pluraliseObjects(ctb_count, "additional contributor");
ctb_count_list = ctb_gh.map((item, i) => htl.html`
<span onclick=${() => localStorage.setItem('orgmetricsMaintainer', item)}>
<a href='/repometrics-demo/maintainer.html'>${item}</a>${i < ctb_gh.length - 1 ? ", " : ""}</span>`);
ctb_count_html = ctb_count == 0 ? htl.html`<div>No additional contributors</div>` :
    htl.html`<div>${ctb_count_txt}: ${ctb_count_list}</div>`;

num_commits = repoMetrics.filter(function(m) {
    return m.name == "num_commits"
})[0].value;
test_coverage = repoMetrics.filter(function(m) {
    return m.name == "test_coverage"
})[0].value;
test_coverage_txt = "Test coverage: " + (test_coverage ? (test_coverage * 100) + "%" : "none");

num_stars = repoMetrics.filter(function(m) {
    return m.name == "num_stars"
})[0].value * 100;
num_forks = repoMetrics.filter(function(m) {
    return m.name == "num_forks"
})[0].value * 100;
stars_forks = (num_stars + num_forks).toLocaleString();

num_dl = repoMetrics.filter(function(m) {
    return m.name == "cran_downloads"
})[0].value.toLocaleString();

issues_active = repoMetrics.filter(function(m) {
    return m.name == "issues_active"
})[0].value;
change_req_n_opened = repoMetrics.filter(function(m) {
    return m.name == "change_req_n_opened"
})[0].value;
issue_count = pluraliseObjects(issues_active + change_req_n_opened, "Github Issue")

// 'issue_cmt_count' from repometrics is *mean* value; convert back to
// full count here:
issue_cmt_count = Math.ceil(repoMetrics.filter(function(m) {
    return m.name == "issue_cmt_count"
})[0].value * issues_active);
issue_cmt_txt = pluraliseObjects(issue_cmt_count, "Issue comment")

imp_txt = 'Imports: ' + deps[0]['imports'];
sug_txt = 'Suggests: ' + deps[0]['suggests'];

deps_in_org = [].concat(deps[0]['deps_in_org'] || []);
deps_in_org_txt = deps_in_org.length == 0 ?
    'Depends on no other pkgs in org' :
    'Depends on org pkgs:';
deps_in_org_list = deps_in_org.length == 0 ? undefined :
    (deps_in_org.length == 1 ? htl.html`<li>${deps_in_org}</li>` :
    deps_in_org.map(d => htl.html`<li>${d}</li>`));
n_deps_in_org_txt = deps_in_org.length > 0 ? htl.html`<ul>${deps_in_org_list}</ul>` : undefined;

revdeps = [].concat(deps[0]['revdeps'] || []);
revdeps_list = revdeps.length == 0 ? undefined :
    (revdeps.length == 1 ? htl.html`<li>${revdeps}</li>` :
    revdeps.map(d => htl.html`<li>${d}</li>`));
revdeps_in_org = revdeps.length > 0 ?
    'Dependend on by org pkgs:' :
    'Not depended on by any other pkgs in org';
revdeps_list_txt = revdeps.length > 0 ? htl.html`<ul>${revdeps_list}</ul>` : undefined;
Bootstrap

Maintainance:

htl.html`<ul>
    <li>${gitlog_txt}</li>
    <li>${maintainer_count_txt}${maintainer_gh_list_txt}</li>
    <li>${ctb_count_html}</li>
    <li>${test_coverage_txt}</li>
</ul>`
Bootstrap

Dependencies

htl.html`<ul>
    <li>${imp_txt}</li>
    <li>${sug_txt}</li>
    <li>${deps_in_org_txt}
        ${n_deps_in_org_txt}
    </li>
    <li>${revdeps_in_org}
        ${revdeps_list_txt}
    </li>
</ul>`
Bootstrap

Popularity &
GitHub Activity

htl.html`<ul>
    <li>${stars_forks} GitHub stars and forks</li>
    <li>${num_dl} CRAN downloads</li>
    <li>${issue_count}</li>
    <li>${issue_cmt_txt}</li>
</ul>`
Bootstrap

Similar Packages

The five most similar packages are:
htl.html`<ul>${similar_pkgs}</ul>`

(Assessed from similarities between language model embedding vectors.)

pkgcheck Summary

This shows the summary output from rOpenSci’s pkgcheck package. Passing checks are marked by ✅; failing checks by ❌; and optional checks that may be worth examining with 👀.

this_pkg_summary = pkgcheck_in[repo];

function unescapeHtml(unsafe) {
  const textarea = document.createElement('textarea');
  textarea.innerHTML = unsafe;
  return textarea.value;
}

htl.html`
  <div>${this_pkg_summary.map((item) => htl.html.fragment`<ul>
    <li>${unescapeHtml(item)}</li>
  </ul>`)}</div>`

R-universe summary

is_on_r_univ = r_univ_page.length > 0;
r_univ_universe = is_on_r_univ ? r_univ_page[0]['universe'] : undefined;
r_univ_package = is_on_r_univ ? r_univ_page[0]['package'] : undefined;
build_url = is_on_r_univ ? r_univ_builds[0]['buildurl'] : undefined;

build_checks = is_on_r_univ ? r_univ_builds.filter(d => (d['status'] !== "success")) : [];
build_checks_okay = build_checks.length == 0;

is_on_r_univ ?
htl.html`<div>Links to
<a href="https://${r_univ_universe}.r-universe.dev/${r_univ_package}" target="_blank" rel="noopener noreferrer"><i>${r_univ_package}</i> on R-universe</a>
and to
<a href="${build_url}" target="_blank" rel="noopener noreferrer">recent R-universe builds</a>.
</div>` :
htl.html`<div>This package is not on R-universe</div>`;
htl.html`<div style="margin-top: 10px; margin-bottom: 10px;"></div>`
is_on_r_univ ?
    (build_checks_okay ?
        htl.html`<div>&#9989; All recent R-universe builds were successful</div>` :
        htl.html`<div>&#10060; Some recent R-universe builds were unsuccessful</div>`) :
    htl.html`<div></div>`;
r_univ_jobs_table = is_on_r_univ ?
    Inputs.table(r_univ_jobs, {
        columns: ["job", "config", "r", "check"],
        format: {
            job: d => htl.html`<a href="${build_url}/job/${d}" target="_blank" rel="noopener noreferrer">${d.toFixed(0)}</a>`,
        },
    }) : htl.html`<div></div>`;

is_on_r_univ ?
htl.html`<div style="margin-top: 10px;"><details><summary>Recent R-Universe Jobs</summary>${r_univ_jobs_table}</details></div>` : htl.html`<div></div>`;
is_on_r_univ && !build_checks_okay ? htl.html`<h3>Recent Builds</h3>` : htl.html`<div></div>`;
is_on_r_univ && !build_checks_okay ?
    htl.html`<div><a href=${build_url}>The following recent builds were unsuccessful</a></div>` :
    htl.html`<div></div>`;
is_on_r_univ && !build_checks_okay?
    Inputs.table(build_checks, {
        columns: ["r", "os", "status", "check"],
    }) : htl.html`<div></div>`;

CRAN Summary

cran_txt = has_cran_data ? "currently list these issues:" : "are all good";
cran_url = "https://cran.r-project.org/web/checks/check_results_" + repo + ".html";
not_on_cran ?
    htl.html`<div>This package is not on CRAN</div>` :
    htl.html`<div><a href="${cran_url}" target="_blank" rel="noopener noreferrer">CRAN checks</a> on this package ${cran_txt}</div>`;
htl.html`<div style="margin-top: 20px; margin-bottom: 20px;"></div>`
has_cran_data ?
    Inputs.table(cran_data, {
        columns: ["package", "version", "result", "check", "message"],
        format: {
            check: d => htl.html`<span style="white-space:normal">${d}`,
            message: d => htl.html`<span style="white-space:normal">${d}`,
        }
    }) : htl.html`<div></div>`;

Statistical properties

This section shows some of the statistical properties of the code base, as generated by the pkgstats package. By default, only outlier values are shown, for which the “Limits” slider below can be used to reduce statistical properties to only those lying in the lower or upper percentiles of the specified value. The table uses the following abbreviations:

  • “LOC” for Lines-of-Code
  • “Nr.” for Number
  • “fn” for function
  • “Doc” for Documentation
viewof statsLimit = Inputs.range(
    [0,50],
    {value: 10, step: 5, label: htl.html`<b>Limits:</b>`}
);
pkgStatsData = {
    return transpose(pkgcheck_stats_in[repo]).map(row => ({
        ...row,
    })).filter(function(m) {
        return m.percentile <= statsLimit || m.percentile >= (100 - statsLimit)
    });
}
Inputs.table(pkgStatsData, {
    format: {
        percentile: sparkbar(100),
        noteworth: null,
    },
    header: {
        measure: "Measure",
        value: "Value",
        percentile: "Percentile"
    }
})

Function usage in other packages

has_fn_calls ? htl.html`<div>
The following table shows the number of times different functions are used in
other packages. Numbers may be less than total numbers of packages listed above
in <q><i>Dependencies: Used by org pkgs</i></q> because some of those usages may be only
in tests, whereas the following usage counts are within actual R code only.
</div>` :
htl.html`<div>This package uses no other packages from the organizations,
and so its functions are not used anywhere else.</div>`;
has_fn_calls ? Inputs.table(fn_calls, {
    columns: ["fn_name", "n"],
    header: {
        fn_name: "Function name",
        n: "Usage"
    }
}) : htl.html`<div></div>`;

CHAOSS metrics and models

url_chaoss = "https://chaoss.community/kb-metrics-and-metrics-models/";
url_chaoss_models = "https://chaoss.community/kbtopic/all-metrics-models/";
url_chaoss_metrics = "https://chaoss.community/kbtopic/all-metrics/";

htl.html`<div>
This section highlights important <a href="${url_chaoss}" target="_blank">
CHAOSS (<i> Community Health Analytics in Open Source Software</i>)</a> metrics
and models for the
<a href=${repoURL} target="_blank"><i>${repo}</i> repository</a>.
The first graph shows
<q><a href=${url_chaoss_models} target="_blank">models</a></q>,
which are aggregations of
<a href=${url_chaoss_metrics} target="_blank">metrics</a>
into conceptual or thematic groups, and the second provides more detail into
individual metrics. All values are standardised between 0 and 1 such that
higher values are always better than lower values.
</div>`

By default, the following charts of both models and metrics only show categories for which the repository is an outlier, defined as lying in the lower or upper 10% of all repositories. Note that these outlier proportions depend on distributions of values measured across all repositories, and will generally not correspond values beyond the limits of [10, 90]% on the scale shown. For example, values for metrics of models may be very concentrated around 0.5 with only a very few extreme values. The lower 10% of values may, for example, be all those below a value of 0.4. Clicking on the following button will toggle to display all values.

Plot = import("https://esm.sh/@observablehq/plot");
viewof outliersOnly = Inputs.radio(
    ["true", "false"],
    {
        value: "true",
        label: htl.html`<b>Outliers only:</b>`,
    }
)

CHAOSS Models

rm_metrics_models = json_data['rm_metrics_models'];
rm_models = rm_metrics_models['models_text'];
rm_metrics = rm_metrics_models['metrics'];
modelsTextMap = rm_models.reduce((map, item) => {
    return map.set(String(item.name), item.text);
}, new Map());
metricsTextMap = rm_metrics.reduce((map, item) => {
    return map.set(String(item.name), item.airtable_name);
}, new Map());
modelsAll = {
    return transpose(models_in).map(row => ({
        ...row,
    }));
}
modelRepoVarnames = modelsAll.filter(function(mod) {
    return mod.package === repo && mod.name !== "final"
})

modelRepoAll = modelRepoVarnames.map(item => {
    const key = String(item.name);
    return {
        ...item,
        name: modelsTextMap.has(key) ? modelsTextMap.get(key) : item.name
    }
})

metricsAll = metricsTranspose.map(item => {
    const key = String(item.name);
    return {
        ...item,
        name: metricsTextMap.has(key) ? metricsTextMap.get(key) : item.name
    }
})
modelRepoFilt = outliersOnly === "true" ?
    modelRepoAll.filter(function(m) {
        return m.outlier === true;
    }) : modelRepoAll;
modelRepoLen = modelRepoFilt.length;
oneBarHeight = 20;
modelPlotHeight = modelRepoLen * oneBarHeight + 100;
fullScaleValues = modelRepoAll.map(d => d.value);
minVal = Math.min(...fullScaleValues);
maxVal = Math.max(...fullScaleValues);
Plot.plot({
    height: modelPlotHeight,
    marginLeft: 60,
    marginRight: 160,
    marginTop: 50,
    marginBottom: 50,
    axis: null,
    x: {
        axis: "top",
        grid: true,
        label: "Model Scores"
    },
    y: { grid: true },
    marks: [
        Plot.barX(modelRepoFilt, {
            y: "name",
            x: "value",
            sort: {y: "-x" },
            fill: "value",
        }),
        Plot.barX(modelRepoFilt,
            Plot.pointer(
            {
                y: "name",
                x: "value",
                sort: {y: "-x" },
                stroke: "gray",
                fill: "value",
                strokeWidth: 2,
            })
        ),
        Plot.text(modelRepoFilt, {
            x: (d) => d.value < 0 ? 0 : d.value,
            y: "name",
            text: "name",
            textAnchor: "start",
            fontSize: 16,
            dx: 5
        })
    ],
    color: {
        scheme: "Cool",
        type: "linear",
        domain: [minVal, maxVal]
    }
})

CHAOSS metrics

This graph provides more detailed insight into the state of the selected repository, through showing values for individual CHAOSS metrics used to inform the aggregate models.

metrics = outliersOnly === "true" ?
    metricsAll.filter(function(m) {
        return m.outlier === true;
    }) : metricsAll;

metricsRepo = metrics.filter(function(mod) {
    return mod.package === repo
})
metricsPlotHeight = metricsRepo.length * oneBarHeight + 100;
Plot.plot({
    height: metricsPlotHeight,
    marginLeft: 60,
    marginRight: 160,
    marginTop: 50,
    marginBottom: 50,
    axis: null,
    x: {
        axis: "top",
        grid: true,
        label: "Metric Scores"
    },
    y: { grid: true },
    marks: [
        Plot.barX(metricsRepo, {
            y: "name",
            x: "value",
            sort: {y: "-x" },
            fill: "value",
        }),
        Plot.barX(metricsRepo,
            Plot.pointer(
            {
                y: "name",
                x: "value",
                sort: {y: "-x" },
                stroke: "gray",
                fill: "value",
                strokeWidth: 2,
            })
        ),
        Plot.text(metricsRepo, {
            x: (d) => d.value < 0 ? 0 : d.value,
            y: "name",
            text: "name",
            textAnchor: "start",
            fontSize: 16,
            dx: 5
        })
    ],
    color: {
        scheme: "Cool",
        type: "ordinal"
    }
})
 
Cookie Preferences