Skip to content

Commit 2bc30e5

Browse files
committed
Merge branch 'dev' into fork/DorielaGrabocka/collate_loci_action
2 parents 7dbba4f + 43773d9 commit 2bc30e5

File tree

145 files changed

+1501
-2761
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+1501
-2761
lines changed

.copier-answers.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
2+
_commit: dfb0404
3+
_src_path: https://github.com/qiime2/q2-setup-template.git
4+
module_name: types
5+
plugin_name: q2_types
6+
plugin_scripts: null
7+
project_author_email: [email protected]
8+
project_author_name: Greg Caporaso
9+
project_description: Common QIIME 2 semantic types.
10+
project_name: q2-types
11+
project_urls_homepage: https://qiime2.org
12+
project_urls_repository: https://github.com/qiime2/q2-types

.gitattributes

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
q2_types/_version.py export-subst
1+
pyproject.toml export-subst

.github/workflows/ci-dev.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ jobs:
1010
uses: qiime2/distributions/.github/workflows/lib-ci-dev.yaml@dev
1111
with:
1212
distro: amplicon
13+
recipe-path: 'conda-recipe'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Update Copyright Headers
2+
3+
on:
4+
# Runs at 00:00 UTC on Jan 4th or via manual trigger
5+
schedule:
6+
- cron: '0 0 4 1 *'
7+
workflow_dispatch:
8+
inputs:
9+
newYear:
10+
description: "Desired year to update to (e.g., 2025). If not provided, will auto-detect."
11+
required: false
12+
13+
jobs:
14+
update-headers:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Check out repository
18+
uses: actions/checkout@v3
19+
20+
- name: Determine years for update
21+
id: determine-years
22+
run: |
23+
INPUT_YEAR="${{ github.event.inputs.newYear }}"
24+
25+
if [ -z "$INPUT_YEAR" ]; then
26+
CURRENT_YEAR="$(date +'%Y')"
27+
echo "No 'newYear' input. Using current year: ${CURRENT_YEAR}"
28+
else
29+
CURRENT_YEAR="$INPUT_YEAR"
30+
echo "Received user input. Updating to: ${CURRENT_YEAR}"
31+
fi
32+
33+
echo "CURRENT_YEAR=$CURRENT_YEAR" >> $GITHUB_ENV
34+
35+
- name: Bump ending year in QIIME 2 headers
36+
run: |
37+
source $GITHUB_ENV
38+
39+
echo "Will update any QIIME 2 header years in [2015..$((CURRENT_YEAR-1))] to $CURRENT_YEAR"
40+
41+
for OLD_YEAR in $(seq 2015 $((CURRENT_YEAR - 1))); do
42+
find . -type f -exec \
43+
sed -i -E "s/(Copyright \(c\) [0-9]{4})-${OLD_YEAR}, QIIME 2/\1-${CURRENT_YEAR}, QIIME 2/g" {} +
44+
done
45+
46+
- name: Commit and push changes
47+
run: |
48+
git config --global user.name "q2d2"
49+
git config --global user.email "[email protected]"
50+
51+
if [ -n "$(git status --porcelain)" ]; then
52+
git add .
53+
git commit -m "Auto-update copyright year to $CURRENT_YEAR"
54+
git push
55+
else
56+
echo "No changes to commit."
57+
exit 0
58+
fi

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,7 @@ target/
6666

6767
.DS_Store
6868
.vscode
69+
70+
# Version file from versioningit
71+
_version.py
72+

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2016-2023, QIIME 2 development team.
3+
Copyright (c) 2016-2025, QIIME 2 development team.
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without

MANIFEST.in

-2
This file was deleted.

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ test-cov: all
1515
py.test --cov=q2_types
1616

1717
install: all
18-
$(PYTHON) setup.py install
18+
$(PYTHON) -m pip install -v .
1919

2020
dev: all
2121
pip install -e .

ci/recipe/meta.yaml

-46
This file was deleted.

conda-recipe/meta.yaml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package:
2+
name: q2-types
3+
version: {{ PLUGIN_VERSION }}
4+
source:
5+
path: ..
6+
build:
7+
script: make install
8+
requirements:
9+
host:
10+
- python {{ python }}
11+
- setuptools
12+
- versioningit
13+
- wheel
14+
run:
15+
- python {{ python }}
16+
- scikit-bio {{ scikit_bio }}
17+
- scipy {{ scipy }}
18+
- numpy
19+
- pandas {{ pandas }}
20+
- biom-format {{ biom_format }}
21+
- ijson
22+
- h5py
23+
- qiime2 {{ qiime2_epoch }}.*
24+
- samtools
25+
- pyhmmer
26+
- frictionless<=5.5.0
27+
build:
28+
- python {{ python }}
29+
- setuptools
30+
- versioningit
31+
test:
32+
commands:
33+
- py.test --pyargs q2_types
34+
requires:
35+
- pytest
36+
- qiime2 >={{ qiime2 }}
37+
imports:
38+
- q2_types
39+
- qiime2.plugins.types
40+
about:
41+
home: https://qiime2.org
42+
license: BSD-3-Clause
43+
license_family: BSD

pyproject.toml

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[project]
2+
name = "q2-types"
3+
authors = [
4+
{ name = "Greg Caporaso", email = "[email protected]" }
5+
]
6+
description = "Common QIIME 2 semantic types."
7+
readme = {file = "README.md", content-type = "text/markdown"}
8+
license = {file = "LICENSE"}
9+
dynamic = ["version"]
10+
11+
[project.urls]
12+
Homepage = "https://qiime2.org"
13+
Repository = "https://github.com/qiime2/q2-types"
14+
15+
[project.entry-points.'qiime2.plugins']
16+
"q2-types" = "q2_types.plugin_setup:plugin"
17+
18+
[build-system]
19+
requires = [
20+
"setuptools",
21+
"versioningit",
22+
"wheel"
23+
]
24+
build-backend = "setuptools.build_meta"
25+
26+
[tool.versioningit.vcs]
27+
method = "git-archive"
28+
describe-subst = "$Format:%(describe)$"
29+
default-tag = "0.0.1"
30+
31+
[tool.versioningit.next-version]
32+
method = "minor"
33+
34+
[tool.versioningit.format]
35+
distance = "{base_version}+{distance}.{vcs}{rev}"
36+
dirty = "{base_version}+{distance}.{vcs}{rev}.dirty"
37+
distance-dirty = "{base_version}+{distance}.{vcs}{rev}.dirty"
38+
39+
[tool.versioningit.write]
40+
file = "q2_types/_version.py"
41+
42+
[tool.setuptools]
43+
include-package-data = true
44+
45+
[tool.setuptools.packages.find]
46+
where = ["."]
47+
include = ["q2_types*"]
48+
49+
[tool.setuptools.package-data]
50+
q2_types = ["**/*"]

q2_types/__init__.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# ----------------------------------------------------------------------------
2-
# Copyright (c) 2016-2023, QIIME 2 development team.
2+
# Copyright (c) 2016-2025, QIIME 2 development team.
33
#
44
# Distributed under the terms of the Modified BSD License.
55
#
66
# The full license is in the file LICENSE, distributed with this software.
77
# ----------------------------------------------------------------------------
88

9-
from ._version import get_versions
109

11-
__version__ = get_versions()['version']
12-
del get_versions
10+
try:
11+
from ._version import __version__
12+
except ModuleNotFoundError:
13+
__version__ = '0.0.0+notfound'

q2_types/_util.py

+96-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# ----------------------------------------------------------------------------
2-
# Copyright (c) 2016-2023, QIIME 2 development team.
2+
# Copyright (c) 2016-2025, QIIME 2 development team.
33
#
44
# Distributed under the terms of the Modified BSD License.
55
#
66
# The full license is in the file LICENSE, distributed with this software.
77
# ----------------------------------------------------------------------------
88
import gzip
99
import itertools
10+
import re
1011
import warnings
12+
from collections import defaultdict
1113
from typing import List
1214

1315
import skbio
@@ -138,3 +140,96 @@ def _validate_mag_ids(
138140
"correctly. Printing duplicate MAG IDs: "
139141
f"{set(duplicates)}"
140142
)
143+
144+
145+
class FileDictMixin:
146+
def file_dict(self, relative=False):
147+
"""
148+
For per sample directories it returns a mapping of sample id to
149+
another dictionary where keys represent the file name and values
150+
correspond to the filepath for each file matching the pathspec.
151+
For files, it returns a mapping of file name to filepath for each
152+
file matching the pathspec. If the dir format has the attribute
153+
'suffixes', then these are removed from filenames.
154+
155+
Parameters
156+
---------
157+
relative : bool
158+
Whether to return filepaths relative to the directory's location.
159+
Returns absolute filepaths by default.
160+
161+
Returns
162+
-------
163+
dict
164+
Mapping of sample id -> filepath as described above.
165+
Or mapping of sample id -> dict {filename: filepath} as
166+
described above.
167+
Both levels of the dictionary are sorted alphabetically by key.
168+
"""
169+
file_pattern = re.compile(self.pathspec)
170+
ids = defaultdict(dict)
171+
172+
for entry in self.path.iterdir():
173+
if entry.is_dir():
174+
outer_id = entry.name
175+
for path in entry.iterdir():
176+
if file_pattern.match(path.name):
177+
178+
file_path, inner_id = self._process_path(
179+
path=path,
180+
relative=relative,
181+
)
182+
183+
ids[outer_id][inner_id] = file_path
184+
ids[outer_id] = dict(sorted(ids[outer_id].items()))
185+
else:
186+
if file_pattern.match(entry.name):
187+
188+
file_path, inner_id = self._process_path(
189+
path=entry,
190+
relative=relative,
191+
)
192+
193+
ids[inner_id] = file_path
194+
195+
return dict(sorted(ids.items()))
196+
197+
def _process_path(self, path, relative=False):
198+
"""
199+
This function processes the input file path to generate an absolute or
200+
relative path string and the ID derived from the file name. The ID is
201+
extracted by removing one of the suffixes from the file name. If the
202+
class does not have a suffixes attribute, then the ID is defined to
203+
be the filename.
204+
205+
Parameters:
206+
---------
207+
path : Path
208+
A Path object representing the file path to process.
209+
relative : bool
210+
A flag indicating whether the returned path should be relative
211+
to the directory formats path or absolute.
212+
213+
Returns:
214+
-------
215+
processed_path : str
216+
The relative or absolute path to the file.
217+
_id : str
218+
The ID derived from the file name.
219+
"""
220+
file_name = path.stem
221+
_id = file_name
222+
suffixes = getattr(self, "suffixes", [])
223+
224+
if suffixes:
225+
for suffix in suffixes:
226+
if file_name.endswith(suffix):
227+
_id = file_name[:-len(suffix)]
228+
break
229+
230+
processed_path = (
231+
path.absolute().relative_to(self.path.absolute())
232+
if relative
233+
else path.absolute()
234+
)
235+
return str(processed_path), _id

0 commit comments

Comments
 (0)