selexprep 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. selexprep-0.1.0/.Rhistory +0 -0
  2. selexprep-0.1.0/.claudeignore +33 -0
  3. selexprep-0.1.0/.dockerignore +23 -0
  4. selexprep-0.1.0/.github/workflows/release.yml +62 -0
  5. selexprep-0.1.0/.github/workflows/tests.yml +47 -0
  6. selexprep-0.1.0/.gitignore +72 -0
  7. selexprep-0.1.0/.pre-commit-config.yaml +29 -0
  8. selexprep-0.1.0/CHANGELOG.md +81 -0
  9. selexprep-0.1.0/CLAUDE.md +94 -0
  10. selexprep-0.1.0/Dockerfile +45 -0
  11. selexprep-0.1.0/LICENSE +21 -0
  12. selexprep-0.1.0/PKG-INFO +408 -0
  13. selexprep-0.1.0/README.md +351 -0
  14. selexprep-0.1.0/benchmarks/README.md +245 -0
  15. selexprep-0.1.0/benchmarks/Snakefile +172 -0
  16. selexprep-0.1.0/benchmarks/audit.smk +156 -0
  17. selexprep-0.1.0/benchmarks/audit_results/audit_accessions.manifest.json +6 -0
  18. selexprep-0.1.0/benchmarks/audit_results/audit_accessions.tsv +24 -0
  19. selexprep-0.1.0/benchmarks/audit_results/audit_metrics.json +263 -0
  20. selexprep-0.1.0/benchmarks/audit_results/eligibility.tsv +126 -0
  21. selexprep-0.1.0/benchmarks/audit_results/table_audit.md +37 -0
  22. selexprep-0.1.0/benchmarks/build_project_metadata.py +482 -0
  23. selexprep-0.1.0/benchmarks/candidates.tsv +10 -0
  24. selexprep-0.1.0/benchmarks/catalog_annotations.tsv +228 -0
  25. selexprep-0.1.0/benchmarks/catalog_completeness_audit.json +12 -0
  26. selexprep-0.1.0/benchmarks/catalog_completeness_audit.py +253 -0
  27. selexprep-0.1.0/benchmarks/catalog_completeness_audit.tsv +104 -0
  28. selexprep-0.1.0/benchmarks/excluded_datasets.tsv +6 -0
  29. selexprep-0.1.0/benchmarks/ground_truth.tsv +12 -0
  30. selexprep-0.1.0/benchmarks/project_annotations.tsv +12 -0
  31. selexprep-0.1.0/benchmarks/project_metadata.csv +294 -0
  32. selexprep-0.1.0/benchmarks/project_metadata.json +5238 -0
  33. selexprep-0.1.0/benchmarks/project_metadata.trajectories.json +5946 -0
  34. selexprep-0.1.0/benchmarks/read_level_flanks.py +393 -0
  35. selexprep-0.1.0/benchmarks/read_level_flanks_tables.txt +301 -0
  36. selexprep-0.1.0/benchmarks/read_level_truth_provenance.tsv +4 -0
  37. selexprep-0.1.0/benchmarks/read_state_evidence.tsv +12 -0
  38. selexprep-0.1.0/benchmarks/round_maps/PRJEB28411.rounds.tsv +13 -0
  39. selexprep-0.1.0/benchmarks/round_maps/PRJNA883192.rounds.tsv +5 -0
  40. selexprep-0.1.0/benchmarks/round_maps/PRJNA935703.rounds.tsv +2 -0
  41. selexprep-0.1.0/benchmarks/round_maps/PRJNA975735.rounds.tsv +6 -0
  42. selexprep-0.1.0/benchmarks/screening_log.tsv +17 -0
  43. selexprep-0.1.0/conda-recipe/meta.yaml +70 -0
  44. selexprep-0.1.0/docs/api.md +24 -0
  45. selexprep-0.1.0/docs/batch.md +37 -0
  46. selexprep-0.1.0/docs/cli.md +92 -0
  47. selexprep-0.1.0/docs/examples.md +23 -0
  48. selexprep-0.1.0/docs/index.md +19 -0
  49. selexprep-0.1.0/docs/library-report.md +67 -0
  50. selexprep-0.1.0/docs/limitations.md +20 -0
  51. selexprep-0.1.0/docs/quickstart.md +49 -0
  52. selexprep-0.1.0/docs/roadmap.md +11 -0
  53. selexprep-0.1.0/examples/01_offline_toy_pipeline.ipynb +441 -0
  54. selexprep-0.1.0/examples/02_library_report_interpretation.ipynb +268 -0
  55. selexprep-0.1.0/examples/README.md +39 -0
  56. selexprep-0.1.0/examples/data/PRJEB62495.library_report.json +55 -0
  57. selexprep-0.1.0/examples/data/PRJEB70964.library_report.json +33 -0
  58. selexprep-0.1.0/examples/data/PRJNA615076.library_report.json +105 -0
  59. selexprep-0.1.0/examples/run_public_accession.sh +18 -0
  60. selexprep-0.1.0/mkdocs.yml +62 -0
  61. selexprep-0.1.0/pyproject.toml +152 -0
  62. selexprep-0.1.0/selexprep.def +54 -0
  63. selexprep-0.1.0/src/selexprep/__init__.py +3 -0
  64. selexprep-0.1.0/src/selexprep/_common.py +78 -0
  65. selexprep-0.1.0/src/selexprep/_io.py +80 -0
  66. selexprep-0.1.0/src/selexprep/benchmark/__init__.py +20 -0
  67. selexprep-0.1.0/src/selexprep/benchmark/corpus_audit.py +741 -0
  68. selexprep-0.1.0/src/selexprep/benchmark/eligibility.py +472 -0
  69. selexprep-0.1.0/src/selexprep/benchmark/equivalence.py +228 -0
  70. selexprep-0.1.0/src/selexprep/benchmark/figure_a.py +155 -0
  71. selexprep-0.1.0/src/selexprep/benchmark/figure_b.py +164 -0
  72. selexprep-0.1.0/src/selexprep/benchmark/metrics.py +1214 -0
  73. selexprep-0.1.0/src/selexprep/catalog/__init__.py +24 -0
  74. selexprep-0.1.0/src/selexprep/catalog/cli.py +149 -0
  75. selexprep-0.1.0/src/selexprep/catalog/data/__init__.py +1 -0
  76. selexprep-0.1.0/src/selexprep/catalog/data/bioprojects.csv +294 -0
  77. selexprep-0.1.0/src/selexprep/catalog/data/bioprojects_excluded.csv +62 -0
  78. selexprep-0.1.0/src/selexprep/catalog/filter.py +97 -0
  79. selexprep-0.1.0/src/selexprep/catalog/reader.py +62 -0
  80. selexprep-0.1.0/src/selexprep/catalog/rebuild.py +627 -0
  81. selexprep-0.1.0/src/selexprep/cli.py +695 -0
  82. selexprep-0.1.0/src/selexprep/count/__init__.py +1 -0
  83. selexprep-0.1.0/src/selexprep/count/counter.py +639 -0
  84. selexprep-0.1.0/src/selexprep/extract/__init__.py +6 -0
  85. selexprep-0.1.0/src/selexprep/extract/demux.py +355 -0
  86. selexprep-0.1.0/src/selexprep/extract/runner.py +752 -0
  87. selexprep-0.1.0/src/selexprep/extract/strand.py +181 -0
  88. selexprep-0.1.0/src/selexprep/extract/trim.py +309 -0
  89. selexprep-0.1.0/src/selexprep/fetch/__init__.py +27 -0
  90. selexprep-0.1.0/src/selexprep/fetch/discover.py +1145 -0
  91. selexprep-0.1.0/src/selexprep/fetch/download.py +844 -0
  92. selexprep-0.1.0/src/selexprep/fetch/inspect.py +195 -0
  93. selexprep-0.1.0/src/selexprep/fetch/library_strategy.py +203 -0
  94. selexprep-0.1.0/src/selexprep/fetch/metadata.py +459 -0
  95. selexprep-0.1.0/src/selexprep/fetch/plan.py +243 -0
  96. selexprep-0.1.0/src/selexprep/fetch/runner.py +403 -0
  97. selexprep-0.1.0/src/selexprep/library/__init__.py +25 -0
  98. selexprep-0.1.0/src/selexprep/library/adapters.py +127 -0
  99. selexprep-0.1.0/src/selexprep/library/audit.py +258 -0
  100. selexprep-0.1.0/src/selexprep/library/detect.py +1038 -0
  101. selexprep-0.1.0/src/selexprep/library/report.py +393 -0
  102. selexprep-0.1.0/src/selexprep/manifest.py +286 -0
  103. selexprep-0.1.0/src/selexprep/qc/__init__.py +1 -0
  104. selexprep-0.1.0/src/selexprep/qc/consistency.py +320 -0
  105. selexprep-0.1.0/src/selexprep/qc/coverage.py +188 -0
  106. selexprep-0.1.0/src/selexprep/qc/diversity.py +99 -0
  107. selexprep-0.1.0/src/selexprep/qc/flags.py +464 -0
  108. selexprep-0.1.0/src/selexprep/qc/plots.py +227 -0
  109. selexprep-0.1.0/src/selexprep/qc/readiness.py +887 -0
  110. selexprep-0.1.0/src/selexprep/qc/runner.py +156 -0
  111. selexprep-0.1.0/src/selexprep/run/__init__.py +5 -0
  112. selexprep-0.1.0/src/selexprep/run/runner.py +638 -0
  113. selexprep-0.1.0/tests/__init__.py +0 -0
  114. selexprep-0.1.0/tests/conftest.py +13 -0
  115. selexprep-0.1.0/tests/fixtures/.gitkeep +0 -0
  116. selexprep-0.1.0/tests/test_adapters.py +124 -0
  117. selexprep-0.1.0/tests/test_audit.py +145 -0
  118. selexprep-0.1.0/tests/test_benchmark_corpus_audit.py +636 -0
  119. selexprep-0.1.0/tests/test_benchmark_eligibility.py +412 -0
  120. selexprep-0.1.0/tests/test_benchmark_equivalence.py +124 -0
  121. selexprep-0.1.0/tests/test_benchmark_figure_a.py +123 -0
  122. selexprep-0.1.0/tests/test_benchmark_figure_b.py +113 -0
  123. selexprep-0.1.0/tests/test_benchmark_metrics.py +932 -0
  124. selexprep-0.1.0/tests/test_benchmark_tier1_curation.py +310 -0
  125. selexprep-0.1.0/tests/test_build_project_metadata.py +277 -0
  126. selexprep-0.1.0/tests/test_catalog.py +444 -0
  127. selexprep-0.1.0/tests/test_cli.py +779 -0
  128. selexprep-0.1.0/tests/test_common.py +69 -0
  129. selexprep-0.1.0/tests/test_consistency.py +140 -0
  130. selexprep-0.1.0/tests/test_counter.py +180 -0
  131. selexprep-0.1.0/tests/test_coverage.py +125 -0
  132. selexprep-0.1.0/tests/test_demux.py +186 -0
  133. selexprep-0.1.0/tests/test_detect.py +260 -0
  134. selexprep-0.1.0/tests/test_discover.py +304 -0
  135. selexprep-0.1.0/tests/test_diversity.py +151 -0
  136. selexprep-0.1.0/tests/test_download.py +301 -0
  137. selexprep-0.1.0/tests/test_extract_override.py +426 -0
  138. selexprep-0.1.0/tests/test_extract_runner.py +478 -0
  139. selexprep-0.1.0/tests/test_fetch_cli.py +447 -0
  140. selexprep-0.1.0/tests/test_fetch_plan.py +379 -0
  141. selexprep-0.1.0/tests/test_flags.py +429 -0
  142. selexprep-0.1.0/tests/test_inspect.py +214 -0
  143. selexprep-0.1.0/tests/test_io.py +92 -0
  144. selexprep-0.1.0/tests/test_library_strategy.py +193 -0
  145. selexprep-0.1.0/tests/test_manifest.py +288 -0
  146. selexprep-0.1.0/tests/test_metadata.py +388 -0
  147. selexprep-0.1.0/tests/test_plots.py +104 -0
  148. selexprep-0.1.0/tests/test_qc_runner.py +200 -0
  149. selexprep-0.1.0/tests/test_read_level_flanks.py +95 -0
  150. selexprep-0.1.0/tests/test_readiness.py +285 -0
  151. selexprep-0.1.0/tests/test_report.py +569 -0
  152. selexprep-0.1.0/tests/test_run_runner.py +671 -0
  153. selexprep-0.1.0/tests/test_strand.py +212 -0
  154. selexprep-0.1.0/tests/test_trim.py +206 -0
  155. selexprep-0.1.0/uv.lock +4311 -0
File without changes
@@ -0,0 +1,33 @@
1
+ # Build artifacts
2
+ dist/
3
+ build/
4
+ *.egg-info/
5
+ htmlcov/
6
+ coverage.xml
7
+
8
+ # Caches (regenerated on demand)
9
+ .venv/
10
+ __pycache__/
11
+ .pytest_cache/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .coverage
15
+ .coverage.*
16
+
17
+ # Lockfile — large; regenerated by `uv sync`. Excluding keeps a fresh
18
+ # Claude session from indexing thousands of resolved-package entries.
19
+ uv.lock
20
+
21
+ # Editor / OS
22
+ .DS_Store
23
+ .idea/
24
+ .vscode/
25
+
26
+ # Built docs (the source is in docs/, not docs/site/)
27
+ docs/site/
28
+ site/
29
+
30
+ # Local scratch
31
+ scratch/
32
+ tmp/
33
+ *.log
@@ -0,0 +1,23 @@
1
+ # Keep the Docker build context small and reproducible: only pyproject,
2
+ # README, LICENSE, and src/ are needed to build the wheel (see Dockerfile).
3
+ .git
4
+ .github
5
+ .venv
6
+ venv
7
+ __pycache__
8
+ *.pyc
9
+ .pytest_cache
10
+ .mypy_cache
11
+ .ruff_cache
12
+ htmlcov
13
+ .coverage
14
+ coverage.xml
15
+ dist
16
+ build
17
+ *.egg-info
18
+ docs
19
+ site
20
+ examples
21
+ tests
22
+ benchmarks
23
+ *.sif
@@ -0,0 +1,62 @@
1
+ name: release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Set up Python
15
+ uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+
19
+ - name: Build sdist + wheel
20
+ run: |
21
+ python -m pip install --upgrade pip build
22
+ python -m build
23
+
24
+ - name: Upload build artifacts
25
+ uses: actions/upload-artifact@v4
26
+ with:
27
+ name: dist
28
+ path: dist/
29
+
30
+ publish-testpypi:
31
+ needs: build
32
+ runs-on: ubuntu-latest
33
+ environment: testpypi
34
+ permissions:
35
+ id-token: write
36
+ if: github.event_name == 'workflow_dispatch'
37
+ steps:
38
+ - uses: actions/download-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+
43
+ - name: Publish to TestPyPI
44
+ uses: pypa/gh-action-pypi-publish@release/v1
45
+ with:
46
+ repository-url: https://test.pypi.org/legacy/
47
+
48
+ publish-pypi:
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ environment: pypi
52
+ permissions:
53
+ id-token: write
54
+ if: github.event_name == 'release'
55
+ steps:
56
+ - uses: actions/download-artifact@v4
57
+ with:
58
+ name: dist
59
+ path: dist/
60
+
61
+ - name: Publish to PyPI
62
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,47 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ cache: pip
25
+
26
+ - name: Install package + dev deps
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install -e ".[dev]"
30
+
31
+ - name: Lint with ruff
32
+ run: |
33
+ ruff check src/ tests/
34
+ ruff format --check src/ tests/
35
+
36
+ - name: Type-check with mypy
37
+ run: mypy src/
38
+
39
+ - name: Run pytest with coverage
40
+ run: pytest --cov=selexprep --cov-report=term-missing --cov-report=xml
41
+
42
+ - name: Upload coverage
43
+ if: matrix.python-version == '3.12'
44
+ uses: actions/upload-artifact@v4
45
+ with:
46
+ name: coverage-report
47
+ path: coverage.xml
@@ -0,0 +1,72 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg-info/
7
+ *.egg
8
+ build/
9
+ dist/
10
+ wheels/
11
+
12
+ # Testing & coverage
13
+ .pytest_cache/
14
+ .tox/
15
+ .coverage
16
+ .coverage.*
17
+ htmlcov/
18
+ coverage.xml
19
+ *.cover
20
+
21
+ # Type-checking & linting
22
+ .mypy_cache/
23
+ .ruff_cache/
24
+
25
+ # Snakemake (created by --dry-run too — both repo-root and benchmarks/)
26
+ .snakemake/
27
+
28
+ # Virtual environments
29
+ .venv/
30
+ venv/
31
+ env/
32
+ .python-version
33
+
34
+ # Environment files
35
+ .env
36
+ .env.local
37
+
38
+ # Documentation
39
+ docs/site/
40
+ site/
41
+
42
+ # OS
43
+ .DS_Store
44
+ Thumbs.db
45
+
46
+ # IDE
47
+ .idea/
48
+ .vscode/
49
+ *.swp
50
+ *.swo
51
+
52
+ # Local data / scratch
53
+ scratch/
54
+ tmp/
55
+ *.log
56
+
57
+ # Benchmark candidate-curation: local-only tools + scratch (not package content;
58
+ # the curation decisions they produced are captured in the committed evidence
59
+ # files: ground_truth.tsv / read_state_evidence.tsv / screening_log.tsv)
60
+ benchmarks/screen_read_state.py
61
+ benchmarks/*_screen.tsv
62
+
63
+ # Project-metadata builder API caches (regenerable from OpenAlex / figshare / Zenodo)
64
+ benchmarks/.oa_cache.json
65
+ benchmarks/.discovery_doi_cache.json
66
+
67
+ # Example run artifacts — notebooks self-generate demo/, run_public_accession.sh writes demo_public/
68
+ examples/**/demo*/
69
+ .ipynb_checkpoints/
70
+
71
+ # Transient HPC run-result tarballs (scp'd back for analysis, not repo content)
72
+ full_list_*.tar.gz
@@ -0,0 +1,29 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: end-of-file-fixer
6
+ - id: trailing-whitespace
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - id: check-added-large-files
10
+ args: [--maxkb=500]
11
+ - id: check-merge-conflict
12
+
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.6.0
15
+ hooks:
16
+ - id: ruff
17
+ args: [--fix]
18
+ - id: ruff-format
19
+
20
+ - repo: https://github.com/pre-commit/mirrors-mypy
21
+ rev: v1.10.0
22
+ hooks:
23
+ - id: mypy
24
+ additional_dependencies:
25
+ - pydantic>=2.0
26
+ - types-pyyaml
27
+ - types-requests
28
+ args: [--config-file=pyproject.toml]
29
+ files: ^src/
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ All notable changes to `selexprep` are documented here.
4
+
5
+ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ _No unreleased changes yet._
10
+
11
+ ## [0.1.0] - 2026-06-13
12
+
13
+ First public release: accession-first preprocessing for high-throughput SELEX
14
+ (HT-SELEX) sequencing deposits, with automatic primer / constant-region
15
+ inference. Give it an INSDC accession (ENA / SRA / DDBJ) and it fetches the
16
+ runs, infers the library's flanking constants from the reads, and extracts the
17
+ random regions — no manual primer entry required.
18
+
19
+ ### Added
20
+
21
+ - **Command-line interface** (`selexprep <verb>`):
22
+ - `inspect` — summarize an accession's runs and metadata.
23
+ - `fetch` — download FASTQs for an accession (ENA-direct by default).
24
+ - `detect` — infer the 5′/3′ constant regions (primers) from the reads.
25
+ - `extract` — strip the inferred constants and emit the random-region reads.
26
+ - `count` — collapse extracted reads to unique-sequence counts per round.
27
+ - `qc` — quality-control flags and plots.
28
+ - `run` — end-to-end fetch → detect → extract → count, with `--resume`.
29
+ - `catalog` — browse the bundled discovery catalog of SELEX deposits.
30
+ - **Primer / constant-region inference** (`detect`): position-anchored
31
+ consensus over the read pool, with a typed `LibraryReport` describing the
32
+ inferred 5′/3′ constants, random-region length, match rates, read state
33
+ (raw vs. pre-trimmed), and a confidence-graded status. Cross-round inference
34
+ reconciles evidence across selection rounds.
35
+ - **Adapter awareness**: known Illumina sequencing adapters (e.g. TruSeq) are
36
+ recorded where present and excluded from primer candidates, so adapter
37
+ read-through is reported as diagnostic information rather than mistaken for a
38
+ library constant.
39
+ - **`extract`**: paired-end handling, strand-orientation detection, and
40
+ per-mode adapter handling. cutadapt is invoked as a subprocess (its CLI is
41
+ the stable contract). Supports `--override-primer-{5p,3p}` to bypass
42
+ inference, and a rebuild path for manually corrected primers.
43
+ - **Discovery catalog** (`catalog`): a bundled, refreshable index of public
44
+ SELEX deposits built from INSDC `library_strategy="SELEX"` queries, with
45
+ per-run and per-BioProject strategy filtering and manual exclusions for
46
+ mislabelled deposits.
47
+ - **Quality control** (`qc`): diversity / rarefaction helpers and depth-aware
48
+ flags (e.g. unexpected rarefied-diversity increase, modal-length spread,
49
+ orientation skew, low read depth, adapter contamination).
50
+ - **Deterministic outputs**: all gzip writes are byte-identical across reruns
51
+ (gzip header `mtime=0`) and JSON is written with sorted keys, so a
52
+ `SelexprepManifestV1` run manifest carries reproducible `sha256` hashes.
53
+ - **Benchmark suite** (under `benchmarks/`, not shipped on PyPI): a Tier 1
54
+ primer-recovery benchmark against paper-grounded ground truth and a Tier 2
55
+ corpus-audit pipeline over the discovery catalog.
56
+
57
+ ### Known limitations (v0.2 carry-forward)
58
+
59
+ - **Multiplexed (inline-barcoded) deposits** need a user-supplied sample sheet;
60
+ automatic demultiplex detection is deferred.
61
+ - **Read merging** of overlapping mates is not implemented.
62
+ - **`qc.readiness`** (clustering / enrichment review) is a faithful library
63
+ API but expects clustering artifacts that v0.1 does not produce; it is not
64
+ wired into the `qc` verb.
65
+ - **`count.counter`** can still trim raw FASTQs inline; the clean split
66
+ (`extract` strips, `count` only counts) is partial.
67
+ - **`--from-pretrimmed-fastq`** validates record completeness but not per-line
68
+ FASTQ conformance — adequate for the power-user opt-in.
69
+ - **Network coverage**: the non-ENA fetch backends still lack offline mocked
70
+ tests (carried into v0.2).
71
+
72
+ ### Packaging
73
+
74
+ - MIT-licensed. A default `pip install selexprep` pulls only MIT-compatible
75
+ dependencies (pydantic v2, Typer, pandas, numpy); cutadapt is invoked as a
76
+ subprocess.
77
+ - `kingfisher` (GPL-3.0) is an optional, runtime-detected subprocess backend —
78
+ not a declared dependency — so the default install stays MIT-only.
79
+
80
+ [Unreleased]: https://github.com/marcorotanegroni/selexprep/compare/v0.1.0...HEAD
81
+ [0.1.0]: https://github.com/marcorotanegroni/selexprep/releases/tag/v0.1.0
@@ -0,0 +1,94 @@
1
+ # selexprep — Claude Code project context
2
+
3
+ Accession-first preprocessing for public HT-SELEX datasets with empirical
4
+ primer / constant-region inference. v0.1 ships a CLI + bundled discovery
5
+ catalog. Target venues: Bioinformatics Advances Application Note (tool) +
6
+ NAR Database Issue (catalog, v0.2).
7
+
8
+ The locked Codex-peer-reviewed implementation plan lives at
9
+ `~/.claude/plans/unified-seeking-treehouse.md` (four review passes; treat
10
+ as a contract — don't rewrite for additive extensions, just commit +
11
+ CHANGELOG entry).
12
+
13
+ ## Commands
14
+
15
+ | Task | Command |
16
+ | ------------------- | ---------------------------------------- |
17
+ | Tests | `uv run pytest` |
18
+ | Lint | `uv run ruff check src/ tests/` |
19
+ | Format | `uv run ruff format src/ tests/` |
20
+ | Type-check | `uv run mypy src/` |
21
+ | Catalog browse | `uv run selexprep catalog list --target X --insdc-only` |
22
+ | Catalog refresh | `uv run selexprep catalog refresh` |
23
+
24
+ CI runs lint + format + mypy + pytest on Python 3.10 / 3.11 / 3.12.
25
+ Always run all four locally before committing.
26
+
27
+ ## Layout
28
+
29
+ ```
30
+ src/selexprep/
31
+ ├── _common.py shared utilities (iter_srr_files, load_csv, …)
32
+ ├── _io.py deterministic gzip + sha256 helpers
33
+ ├── cli.py Typer dispatcher (root)
34
+ ├── catalog/ bundled public-SELEX catalog + filters + CLI subapp
35
+ ├── count/ per-round sequence counting
36
+ ├── extract/ demux + trim (sample-sheet driven; deterministic gzip)
37
+ ├── fetch/ accession discovery + download (ENA-first)
38
+ ├── library/ primer detection + audit (Phase 2: LibraryReport)
39
+ └── qc/ round coverage, consistency, readiness, plots
40
+ ```
41
+
42
+ ## Strict-mypy boundary
43
+
44
+ Only `selexprep.library.report` is strict-mypy (Phase 2 LibraryReport
45
+ schema — type precision matters for confidence calibration). Other
46
+ modules use the permissive profile declared in `pyproject.toml`'s
47
+ `[tool.mypy] disable_error_code`.
48
+
49
+ ## Critical gotchas (do not relearn)
50
+
51
+ - **macOS TCC** blocks `~/Documents` on this Mac → repo lives at
52
+ `/Users/marcorotanegroni/selexprep`, NOT under `~/Documents/...`.
53
+ - **`detect.detect_from_parquet`** defaults to `top_n=None` (no
54
+ subsampling). Don't reintroduce the 10_000 cap — the long tail of
55
+ rare uniques confirms primer consensus.
56
+ - **`download_srr`** defaults to `backend="auto"` → ENA-direct first
57
+ (MIT-licensed path). kingfisher is GPL-3.0 and only invoked as
58
+ opt-in fallback (with a loud GPL notice). Never promote it to default.
59
+ - **All `.gz` writes** must go through
60
+ `selexprep._io.open_gzip_text_deterministic` (mtime=0 + suppressed
61
+ FNAME). Plain `gzip.open` breaks SHA256 reproducibility.
62
+ - **Curation flags** (`include`, `manual_curation_notes`,
63
+ `library_type_verification`, `library_type_evidence`,
64
+ `has_processed_counts`) are NEVER in the package catalog or in any
65
+ user-facing output. Curation is the user's downstream job; the
66
+ package reflects the public archives.
67
+ - **Phase 2 centerpiece** is `LibraryReport` with explicit
68
+ `extraction_mode` × `read_source` × `required_action` matrix
69
+ (see the plan file for the full pydantic schema and the
70
+ cross-round-persistence inference algorithm).
71
+
72
+ ## Workflow
73
+
74
+ - **Big design decisions** → user cross-checks with Codex
75
+ (independent peer-review). Don't self-approve scope changes. Trigger
76
+ phrase: *"let me run this through Codex"*. Wait for the response
77
+ before applying changes.
78
+ - **Per-feature implementations** → use `EnterPlanMode` to get plan
79
+ approval before writing code on anything > a one-file change.
80
+ - **Subagents** → `Explore` agent for read-only mapping when the
81
+ surface area is unclear; do edits in the main context.
82
+ - **Background polling** (CI, long-running jobs) → use `Bash` with
83
+ `run_in_background: true` + a sha-aware Python poll script at
84
+ `/tmp/wait_for_sha_ci.py`. Don't write nested `python -c` with
85
+ escaped quotes — they break and run forever silently.
86
+
87
+ ## Where context lives
88
+
89
+ - **Plan (locked contract):** `~/.claude/plans/unified-seeking-treehouse.md`
90
+ - **Change log + v0.1 follow-ups:** `CHANGELOG.md` in this repo
91
+ - **Project memory** (durable user preferences, project state):
92
+ `~/.claude/projects/-Users-marcorotanegroni-Documents-subtractive-proteomics-pipeline-aptameri-selex-corpus/memory/MEMORY.md`
93
+ (the path is keyed on the original thesis directory, not on this
94
+ repo — index file lists every memory note).
@@ -0,0 +1,45 @@
1
+ # syntax=docker/dockerfile:1
2
+ #
3
+ # selexprep container image.
4
+ #
5
+ # Build: docker build -t selexprep:0.1.0 .
6
+ # Run: docker run --rm selexprep:0.1.0 inspect SRR1234567
7
+ # docker run --rm -v "$PWD":/data selexprep:0.1.0 run /data/accessions.tsv --outdir /data/out
8
+ #
9
+ # The image installs selexprep from the local source tree (this checkout), so it
10
+ # is buildable before the PyPI release. cutadapt arrives as a declared pip
11
+ # dependency and is invoked as a subprocess at runtime — no apt package needed
12
+ # (its manylinux wheel runs on the debian-slim base).
13
+
14
+ FROM python:3.12-slim
15
+
16
+ LABEL org.opencontainers.image.title="selexprep" \
17
+ org.opencontainers.image.description="Accession-first preprocessing for public HT-SELEX with primer auto-inference" \
18
+ org.opencontainers.image.source="https://github.com/marcorotanegroni/selexprep" \
19
+ org.opencontainers.image.licenses="MIT" \
20
+ org.opencontainers.image.version="0.1.0"
21
+
22
+ ENV PYTHONDONTWRITEBYTECODE=1 \
23
+ PYTHONUNBUFFERED=1 \
24
+ PIP_NO_CACHE_DIR=1 \
25
+ PIP_DISABLE_PIP_VERSION_CHECK=1
26
+
27
+ WORKDIR /build
28
+
29
+ # Copy only the files the build backend (hatchling) needs to produce the wheel.
30
+ # pyproject references README.md (readme) and LICENSE (license-files); the
31
+ # package code and bundled catalog CSV live under src/.
32
+ COPY pyproject.toml README.md LICENSE ./
33
+ COPY src ./src
34
+
35
+ RUN python -m pip install --upgrade pip \
36
+ && python -m pip install . \
37
+ && rm -rf /build
38
+
39
+ # Run as an unprivileged user; default working dir is a writable home.
40
+ RUN useradd --create-home --uid 1000 selex
41
+ USER selex
42
+ WORKDIR /home/selex
43
+
44
+ ENTRYPOINT ["selexprep"]
45
+ CMD ["--help"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marco Rota Negroni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.