[med-svn] [Git][med-team/q2-feature-table][master] 7 commits: routine-update: New upstream version
Michael R. Crusoe (@crusoe)
gitlab at salsa.debian.org
Tue Jun 25 02:34:29 BST 2024
Michael R. Crusoe pushed to branch master at Debian Med / q2-feature-table
Commits:
72a07d41 by Michael R. Crusoe at 2024-06-25T02:52:52+02:00
routine-update: New upstream version
- - - - -
43fd6906 by Michael R. Crusoe at 2024-06-25T02:52:53+02:00
New upstream version 2024.5.0+dfsg
- - - - -
13f0770f by Michael R. Crusoe at 2024-06-25T02:52:53+02:00
Update upstream source from tag 'upstream/2024.5.0+dfsg'
Update to upstream version '2024.5.0+dfsg'
with Debian dir 0aab4aa647dfc60af640803cafeabc7eb19ead40
- - - - -
a80fb06e by Michael R. Crusoe at 2024-06-25T02:53:12+02:00
routine-update: Regenerate debian/control from debian/control.in
- - - - -
4e36c75b by Michael R. Crusoe at 2024-06-25T03:21:40+02:00
d/patches/configparser.patch: copy from qiime
- - - - -
6a0aaf56 by Michael R. Crusoe at 2024-06-25T03:28:56+02:00
Only build for Python 3.11 until upstream catches up with Python 3.12.
- - - - -
1a56d11e by Michael R. Crusoe at 2024-06-25T03:28:56+02:00
routine-update: Ready to upload to unstable
- - - - -
15 changed files:
- ci/recipe/meta.yaml
- debian/changelog
- debian/control
- + debian/patches/configparser.patch
- + debian/patches/series
- debian/rules
- debian/tests/run-unit-test
- q2_feature_table/_summarize/_visualizer.py
- q2_feature_table/_summarize/summarize_assets/feature-frequency-detail.html
- q2_feature_table/_summarize/summarize_assets/index.html
- q2_feature_table/_summarize/summarize_assets/sample-frequency-detail.html
- + q2_feature_table/_summarize/summarize_assets/utils/util.js
- q2_feature_table/_version.py
- q2_feature_table/tests/test_summarize.py
- setup.py
Changes:
=====================================
ci/recipe/meta.yaml
=====================================
@@ -20,10 +20,8 @@ requirements:
- python {{ python }}
- scikit-bio {{ scikit_bio }}
- biom-format {{ biom_format }}
- - seaborn
- # pinned here on 10.19.22 due to pkg int build failure on community
- # due to matplotlib bug. will be resolved once 3.7.0 is released
- - matplotlib =3.6.0
+ - seaborn {{ seaborn }}
+ - matplotlib {{ matplotlib }}
- pandas {{ pandas }}
- numpy
# `ipywidgets` included to avoid ShimWarning from
=====================================
debian/changelog
=====================================
@@ -1,3 +1,14 @@
+q2-feature-table (2024.5.0+dfsg-1) unstable; urgency=medium
+
+ * Team upload.
+ * New upstream version
+ * Regenerate debian/control from debian/control.in (routine-update)
+ * d/patches/configparser.patch: copy from qiime
+ * Only build for Python 3.11 until upstream catches up with Python
+ 3.12.
+
+ -- Michael R. Crusoe <crusoe at debian.org> Tue, 25 Jun 2024 02:53:13 +0200
+
q2-feature-table (2024.2.0+dfsg-1) unstable; urgency=medium
* Team upload.
=====================================
debian/control
=====================================
@@ -6,14 +6,14 @@ Section: science
Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-sequence-python3,
- python3,
+ python3-all,
python3-setuptools,
python3-pytest-cov,
python3-biom-format,
python3-seaborn,
- qiime (>= 2024.2),
- q2templates (>= 2024.2),
- q2-types (>= 2024.2)
+ qiime (>= 2024.5),
+ q2templates (>= 2024.5),
+ q2-types (>= 2024.5)
Standards-Version: 4.6.2
Vcs-Browser: https://salsa.debian.org/med-team/q2-feature-table
Vcs-Git: https://salsa.debian.org/med-team/q2-feature-table.git
@@ -31,9 +31,9 @@ Depends: ${shlibs:Depends},
python3-seaborn,
python3-numpy,
python3-ipywidgets,
- qiime (>= 2024.2),
- q2templates (>= 2024.2),
- q2-types (>= 2024.2)
+ qiime (>= 2024.5),
+ q2templates (>= 2024.5),
+ q2-types (>= 2024.5)
Description: QIIME 2 plugin supporting operations on feature tables
QIIME 2 is a powerful, extensible, and decentralized microbiome analysis
package with a focus on data and analysis transparency. QIIME 2 enables
=====================================
debian/patches/configparser.patch
=====================================
@@ -0,0 +1,27 @@
+From: Athos Ribeiro <athos.ribeiro at canonical.com>
+Date: Mon, 3 Jun 2024 11:31:54 -0300
+Subject: Use ConfigParser instead of SafeConfigParser
+
+The configparser's SafeConfigParser has been renamed to ConfigParser in
+Python 3.2 [1]. It was finally removed in Python 3.12 [2].
+
+[1] https://docs.python.org/dev/whatsnew/3.2.html#configparser
+[2] https://docs.python.org/3/whatsnew/3.12.html#configparser
+
+Last-Update: 2024-06-03
+Forwarded: not-needed, see https://github.com/qiime2/q2-sample-classifier/pull/229
+
+--- q2-types.orig/versioneer.py
++++ q2-types/versioneer.py
+@@ -340,9 +340,9 @@
+ # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
+ # the top of versioneer.py for instructions on writing your setup.cfg .
+ setup_cfg = os.path.join(root, "setup.cfg")
+- parser = configparser.SafeConfigParser()
++ parser = configparser.ConfigParser()
+ with open(setup_cfg, "r") as f:
+- parser.readfp(f)
++ parser.read_file(f)
+ VCS = parser.get("versioneer", "VCS") # mandatory
+
+ def get(parser, name):
=====================================
debian/patches/series
=====================================
@@ -0,0 +1 @@
+configparser.patch
=====================================
debian/rules
=====================================
@@ -19,6 +19,8 @@ export PYBUILD_BEFORE_TEST=python{version} setup.py develop --install-dir {build
export PYBUILD_BEFORE_INSTALL=rm -rvf {build_dir}/q2-feature-table.egg-* {build_dir}/site.py \
{build_dir}/.coverage* {build_dir}/easy-install.pth
+export PYBUILD_DISABLE=python3.12
+
%:
dh $@ --buildsystem=pybuild
=====================================
debian/tests/run-unit-test
=====================================
@@ -18,7 +18,9 @@ if [ ! -f /usr/lib/python3/dist-packages/pytest_cov/__init__.py ] ; then
fi
# Run build-time tests
-for py in $(py3versions -s 2> /dev/null)
-do
- ${py} -m pytest -v --cov=${pkg}
-done
+# for py in $(py3versions -s 2> /dev/null)
+# do
+# ${py} -m pytest -v --cov=${pkg}
+# done
+
+python3.11 -m pytest -v --cov=${pkg}
=====================================
q2_feature_table/_summarize/_visualizer.py
=====================================
@@ -151,40 +151,42 @@ def summarize(output_dir: str, table: biom.Table,
feature_frequencies_ax.get_figure().savefig(
os.path.join(output_dir, 'feature-frequencies.png'))
- sample_summary_table = q2templates.df_to_html(
- sample_summary.apply('{:,}'.format).to_frame('Frequency'))
- feature_summary_table = q2templates.df_to_html(
- feature_summary.apply('{:,}'.format).to_frame('Frequency'))
+ sample_summary_json = pd.DataFrame(
+ sample_summary, columns=['Frequency']).to_json()
+ feature_summary_json = pd.DataFrame(
+ feature_summary, columns=['Frequency']).to_json()
index = os.path.join(TEMPLATES, 'summarize_assets', 'index.html')
context = {
'number_of_samples': number_of_samples,
'number_of_features': number_of_features,
'total_frequencies': int(np.sum(sample_frequencies)),
- 'sample_summary_table': sample_summary_table,
- 'feature_summary_table': feature_summary_table,
+ 'sample_summary_table': sample_summary_json,
+ 'feature_summary_table': feature_summary_json,
}
- feature_qualitative_data = _compute_qualitative_summary(table)
- sample_frequencies.sort_values(inplace=True, ascending=False)
-
- sample_frequencies_json = pd.Series(["{:,}".format(int(x)) for x in
- sample_frequencies])
-
- feature_frequencies.sort_values(inplace=True, ascending=False)
+ # Create a JSON object containing the Sample Frequencies to build the
+ # table in sample-frequency-detail.html
+ #
+ # Cast to DataFrame to standardize with other tables
+ sample_frequencies_json = pd.DataFrame(
+ sample_frequencies, columns=['Frequency']).to_json()
- feature_frequencies = feature_frequencies.astype(int) \
- .apply('{:,}'.format).to_frame('Frequency')
+ # Create a JSON object containing the Feature Frequencies to build the
+ # table in feature-frequency-detail.html
+ feature_qualitative_data = _compute_qualitative_summary(table)
+ feature_frequencies = feature_frequencies.astype(int).to_frame('Frequency')
feature_frequencies['# of Samples Observed In'] = \
- pd.Series(feature_qualitative_data).astype(int).apply('{:,}'.format)
- feature_frequencies_table = q2templates.df_to_html(feature_frequencies)
+ pd.Series(feature_qualitative_data).astype(int)
+ feature_frequencies_json = feature_frequencies.to_json()
+
sample_frequency_template = os.path.join(
TEMPLATES, 'summarize_assets', 'sample-frequency-detail.html')
feature_frequency_template = os.path.join(
TEMPLATES, 'summarize_assets', 'feature-frequency-detail.html')
context.update({'max_count': sample_frequencies.max(),
- 'feature_frequencies_table': feature_frequencies_table,
+ 'feature_frequencies_json': feature_frequencies_json,
'feature_qualitative_data': feature_qualitative_data,
'tabs': [{'url': 'index.html',
'title': 'Overview'},
@@ -193,10 +195,6 @@ def summarize(output_dir: str, table: biom.Table,
{'url': 'feature-frequency-detail.html',
'title': 'Feature Detail'}]})
- # Create a JSON object containing the Sample Frequencies to build the
- # table in sample-frequency-detail.html
- sample_frequencies_json = sample_frequencies_json.to_json()
-
templates = [index, sample_frequency_template, feature_frequency_template]
context.update({'frequencies_list':
json.dumps(sorted(sample_frequencies.values.tolist()))})
@@ -211,6 +209,10 @@ def summarize(output_dir: str, table: biom.Table,
'summarize_assets',
'vega'),
output_dir)
+ q2templates.util.copy_assets(os.path.join(TEMPLATES,
+ 'summarize_assets',
+ 'utils'),
+ output_dir)
q2templates.render(templates, output_dir, context=context)
plt.close('all')
=====================================
q2_feature_table/_summarize/summarize_assets/feature-frequency-detail.html
=====================================
@@ -1,9 +1,37 @@
-{% extends "tabbed.html" %}
-
+{% extends "tabbed.html" %} {% block head %}
+<script src="util.js"></script> {% endblock %}
{% block tabcontent %}
- <div class="row">
+<div class="row">
+ <div class="col-lg-12">
<div class="col-lg-12">
- {{ feature_frequencies_table }}
+ <table class="table table-striped table-hover" border="0">
+ <thead>
+ <tr style="text-align: right;">
+ <th></th>
+ <th>Frequency</th>
+ <th># of Samples Observed In</th>
+ </tr>
+ </thead>
+ <tbody id="table-body"></tbody>
+ </table>
</div>
</div>
+</div>
+
+<script id="table-data" type="application/json">
+{{ feature_frequencies_json }}
+</script>
+
+<script type="text/javascript">
+ const tableBody = document.getElementById("table-body");
+ const tableData = JSON.parse(document.getElementById("table-data").innerHTML);
+ const featureFrequencies = tableData["Frequency"];
+
+ const sortedFeatureIDs = Object.keys(featureFrequencies).sort(function(a, b) {
+ return sortIDs(a, b, featureFrequencies)
+ });
+
+ formatTable(tableBody, tableData, sortedFeatureIDs);
+</script>
+
{% endblock %}
=====================================
q2_feature_table/_summarize/summarize_assets/index.html
=====================================
@@ -1,5 +1,5 @@
-{% extends "tabbed.html" %}
-
+{% extends "tabbed.html" %} {% block head %}
+<script src="util.js"></script> {% endblock %}
{% block tabcontent %}
<div class="row">
<div class="col-lg-12">
@@ -10,22 +10,22 @@
<table class="table table-striped table-hover">
<thead>
<tr>
- <th>Metric</th>
- <th>Sample</th>
+ <th>Summary Statistic</th>
+ <th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Number of samples</th>
- <td>{{ "{:,}".format(number_of_samples) }}</td>
+ <td id="number-of-samples"></td>
</tr>
<tr>
<th scope="row">Number of features</th>
- <td>{{ "{:,}".format(number_of_features) }}</td>
+ <td id="number-of-features"></td>
</tr>
<tr>
<th scope="row">Total frequency</th>
- <td>{{ "{:,}".format(total_frequencies) }}</td>
+ <td id="total-frequency"></td>
</tr>
</tbody>
</table>
@@ -36,7 +36,15 @@
<div class="row">
<div class="col-lg-6">
- {{ sample_summary_table }}
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Frequency</th>
+ </tr>
+ </thead>
+ <tbody id="sample-table-body"></tbody>
+ </table>
</div>
{% if number_of_samples > 1 %}
<div class="col-lg-6">
@@ -51,10 +59,19 @@
{% endif %}
</div>
+ <h1>Frequency per feature</h1>
+
<div class="row">
<div class="col-lg-6">
- <h1>Frequency per feature</h1>
- {{ feature_summary_table }}
+ <table class="table table-striped table-hover">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Frequency</th>
+ </tr>
+ </thead>
+ <tbody id="feature-table-body"></tbody>
+ </table>
</div>
{% if number_of_features > 1 %}
<div class="col-lg-6">
@@ -70,4 +87,34 @@
</div>
</div>
</div>
+
+<script id="sample-table-data" type="application/json">
+{{ sample_summary_table }}
+</script>
+
+<script id="feature-table-data" type="application/json">
+{{ feature_summary_table }}
+</script>
+
+<script type="text/javascript">
+ const numberOfSamples = document.getElementById("number-of-samples");
+ numberOfSamples.innerText = formatter.format({{number_of_samples}});
+
+ const numberOfFeatures = document.getElementById("number-of-features");
+ numberOfFeatures.innerText = formatter.format({{number_of_features}});
+
+ const totalFrequency = document.getElementById("total-frequency");
+ totalFrequency.innerText = formatter.format({{total_frequencies}});
+
+ const sampleTableBody = document.getElementById("sample-table-body");
+ const sampleTableData = JSON.parse(document.getElementById("sample-table-data").innerHTML);
+
+ formatTable(sampleTableBody, sampleTableData, Object.keys(sampleTableData["Frequency"]).reverse());
+
+ const featureTableBody = document.getElementById("feature-table-body");
+ const featureTableData = JSON.parse(document.getElementById("feature-table-data").innerHTML);
+
+ formatTable(featureTableBody, featureTableData, Object.keys(featureTableData["Frequency"]).reverse());
+</script>
+
{% endblock %}
=====================================
q2_feature_table/_summarize/summarize_assets/sample-frequency-detail.html
=====================================
@@ -1,6 +1,7 @@
{% extends "tabbed.html" %} {% block head %}
<script src="js/vega.min.js"></script>
<script src="js/vega-embed.min.js"></script>
+<script src="util.js"></script>
<link rel="stylesheet" type="text/css" href="css/spinkit.css" /> {% endblock %}
{% block tabcontent %}
@@ -148,7 +149,7 @@
<thead>
<tr>
<th scope="col">Sample ID</th>
- <th scope="col"> Frequency </th>
+ <th scope="col">Frequency</th>
</tr>
</thead>
<tbody id="table-body"></tbody>
@@ -175,56 +176,45 @@
// when the viz loads the default description is displayed
textField.innerHTML = defaultDescription;
- var sampleFrequency = JSON.parse(document.getElementById("table-data").innerHTML);
+ let sampleTable = JSON.parse(document.getElementById("table-data").innerHTML);
+ let sampleFrequencies = sampleTable["Frequency"];
// get object keys and store them in an ascending order based on the key value
// this order is used to create the table rows
- sortedSampleIDs = Object.keys(sampleFrequency).sort(function(a, b) {
- var temp = sampleFrequency[a] - sampleFrequency[b];
- // if two samples have the same number of features then we
- // determine the order using the sample ID alphabetical order
- if (temp == 0){
- return b.localeCompare(a);
- }
-
- return temp;
- });
-
- sortedSampleIDs.forEach(function(element) {
- var row = tableBody.insertRow(0);
- var cell1 = row.insertCell(0);
- var cell2 = row.insertCell(1);
- cell1.innerHTML = element;
- cell2.innerHTML = sampleFrequency[element];
-
+ sortedSampleIDs = Object.keys(sampleFrequencies).sort(function(a, b) {
+ return sortIDs(a, b, sampleFrequencies)
});
+ formatTable(tableBody, sampleTable, sortedSampleIDs);
- function updateTableandText(val) {
- var retainedSampleCount = 0;
+ function updateTableandText(samplingDepth) {
+ let retainedSampleCount = 0;
// start the counter at 1 to ignore the header row
for (var i = 1; row = table.rows[i]; i++) {
-
- if (Number(row.cells[1].innerHTML) < val) {
+ // The value in table is formatted as a string for display to the user,
+ // but the value in sampleFrequencies is formatted as an int for data
+ // manipulation, so we use the ID we are on in table to get the frequency
+ // from sampleFrequencies.
+ const sampleID = row.cells[0].innerText;
+ const sampleFrequency = sampleFrequencies[sampleID];
+
+ if (sampleFrequency < samplingDepth) {
row.className = "danger";
} else {
row.className = "";
retainedSampleCount += 1;
}
}
- if (val == 0){
-
- textField.innerHTML = defaultDescription;
-
+ if (samplingDepth == 0){
+ textField.innerHTML = defaultDescription;
}
else{
- var retainedFeatureCount = retainedSampleCount * val;
- textField.innerHTML = "Retained " + retainedFeatureCount.toLocaleString('en')
+ var retainedFeatureCount = retainedSampleCount * samplingDepth;
+ textField.innerHTML = "Retained " + formatter.format(retainedFeatureCount)
+ " (" + (retainedFeatureCount/totalFrequencies*100).toFixed(2) + "%) features in "
+ retainedSampleCount + " (" + (retainedSampleCount/sampleCount*100).toFixed(2)
+ "%) samples at the specifed sampling depth.";
}
-
}
@@ -245,7 +235,6 @@
function sliderHelperFunction(val){
updateBoxVal(val);
updateTableandText(val);
-
}
=====================================
q2_feature_table/_summarize/summarize_assets/utils/util.js
=====================================
@@ -0,0 +1,33 @@
+// Formats the number in a manner based on the locale but using latin numerals
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
+const formatter = new Intl.NumberFormat({numberingSystem: "latn"})
+
+
+function formatTable(tableBody, data, sortedKeys) {
+ for (const key of sortedKeys) {
+ let colIdx = 1;
+ let row = tableBody.insertRow(0);
+
+ let keyCell = row.insertCell(0)
+ keyCell.innerText = key;
+ keyCell.style.setProperty("text-align", "left");
+ keyCell.style.setProperty("font-weight", "bold");
+
+ for (const innerKey of Object.keys(data)) {
+ let dataCell = row.insertCell(colIdx++);
+ dataCell.innerText = formatter.format(data[innerKey][key]);
+ }
+ }
+}
+
+
+function sortIDs(a, b, data) {
+ const diff = data[a] - data[b];
+ // if two samples have the same number of features then we
+ // determine the order using the sample ID alphabetical order
+ if (diff == 0){
+ return b.localeCompare(a);
+ }
+
+ return diff;
+}
=====================================
q2_feature_table/_version.py
=====================================
@@ -23,9 +23,9 @@ def get_keywords():
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
- git_refnames = " (tag: 2024.2.0, Release-2024.2)"
- git_full = "8635d787a83854309dc6cb26afdc5dc5476db208"
- git_date = "2024-02-16 21:57:35 +0000"
+ git_refnames = " (tag: 2024.5.0, Release-2024.5)"
+ git_full = "6b106f1aafe333144ee7f30918e5bb852639fc69"
+ git_date = "2024-05-29 04:15:53 +0000"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
=====================================
q2_feature_table/tests/test_summarize.py
=====================================
@@ -9,6 +9,8 @@
import os
from unittest import TestCase, main
import tempfile
+import re
+import json
import skbio
import biom
@@ -383,6 +385,23 @@ class SummarizeTests(TestCase):
index_fp = os.path.join(output_dir, 'index.html')
self.assertTrue(os.path.exists(index_fp))
+ sample_frequency_fp = os.path.join(output_dir,
+ 'sample-frequency-detail.html')
+ self.assertTrue(os.path.exists(sample_frequency_fp))
+ rx = (r'<script id="table-data" type="application/json">' +
+ r'\n.*[^}]*.*\n</script>')
+
+ with open(sample_frequency_fp) as fi:
+ text = fi.read()
+ tbl_rx = re.compile(rx)
+ tbl = tbl_rx.findall(text)[0].split('\n')[1].strip()
+
+ sample_ids = json.loads(tbl)['Frequency'].keys()
+
+ self.assertTrue('S1' in sample_ids)
+ self.assertTrue('S2' in sample_ids)
+ self.assertTrue('S3' in sample_ids)
+
def test_frequency_ranges_are_zero(self):
table = biom.Table(np.array([[25, 25, 25], [25, 25, 25]]),
['O1', 'O2'],
=====================================
setup.py
=====================================
@@ -22,6 +22,7 @@ setup(
'summarize_assets/vega/licenses/*',
'summarize_assets/vega/js/*',
'summarize_assets/vega/css/*',
+ 'summarize_assets/utils/*',
'tabulate_seqs_assets/js/*',
'tabulate_seqs_assets/index.html'],
'q2_feature_table._core_features': [
View it on GitLab: https://salsa.debian.org/med-team/q2-feature-table/-/compare/7aba2938f7a5537e9918ee35eb87908438919081...1a56d11ee162bb1412fc1d38f9a3e1e264df833b
--
This project does not include diff previews in email notifications.
View it on GitLab: https://salsa.debian.org/med-team/q2-feature-table/-/compare/7aba2938f7a5537e9918ee35eb87908438919081...1a56d11ee162bb1412fc1d38f9a3e1e264df833b
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20240625/d6b5101e/attachment-0001.htm>
More information about the debian-med-commit
mailing list