[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