From 8ae36e88c72e1584735884bbf9a711aa8d9903a6 Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Fri, 17 Jul 2026 13:02:44 +0200 Subject: [PATCH 01/11] version -> 2.18.6, update grouped db handling to latest db format --- gffquant/__init__.py | 2 +- gffquant/annotation/count_annotator.py | 40 +++++++++++++++----------- gffquant/db/annotation_db.py | 18 ++++++++---- gffquant/db/models/db.py | 10 +++++++ 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/gffquant/__init__.py b/gffquant/__init__.py index f3a023d6..0f42e1ec 100644 --- a/gffquant/__init__.py +++ b/gffquant/__init__.py @@ -5,7 +5,7 @@ from enum import Enum, auto, unique -__version__ = "2.18.5" +__version__ = "2.18.6" __tool__ = "gffquant" diff --git a/gffquant/annotation/count_annotator.py b/gffquant/annotation/count_annotator.py index feb96b92..98260ed4 100644 --- a/gffquant/annotation/count_annotator.py +++ b/gffquant/annotation/count_annotator.py @@ -286,6 +286,8 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): if self.strand_specific else None ) + grouped_counts = {} + for rid in set(count_manager.uniq_seqcounts).union( count_manager.ambig_seqcounts ): @@ -302,27 +304,33 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): strand_specific_counts=strand_specific_counts, ) - if gene_group_db: - ref_tokens = ref.split(".") - gene_id, ggroup_id = ".".join(ref_tokens[:-1]), ref_tokens[-1] - else: - ggroup_id, gene_id = ref, ref - gcounts = self.gene_counts.setdefault(gene_id, np.zeros(self.bins)) gcounts += counts self.total_gene_counts += counts[:4] - region_annotation = db.query_sequence(ggroup_id) - if region_annotation is not None: - _, _, region_annotation = region_annotation - # logger.info( - # "GCAnnotator: Distributing counts of Gene %s (group=%s) %s %s", - # gene_id, ggroup_id, counts[0], counts[2], - # ) - self.distribute_feature_counts(counts, region_annotation) - + if gene_group_db: + ref_tokens = ref.split(".") + gene_id, ggroup_id = ".".join(ref_tokens[:-1]), ref_tokens[-1] + grouped_counts.setdefault(ggroup_id, np.zeroes(self.bins)) + grouped_counts += counts else: - # logger.info("GCAnnotator: Gene %s (group=%s) has no information in database.", gene_id, ggroup_id) + region_annotation = db.query_sequence(ref) + if region_annotation is not None: + _, _, region_annotation = region_annotation + # logger.info( + # "GCAnnotator: Distributing counts of Gene %s (group=%s) %s %s", + # gene_id, ggroup_id, counts[0], counts[2], + # ) + self.distribute_feature_counts(counts, region_annotation) + else: + # logger.info("GCAnnotator: Gene %s (group=%s) has no information in database.", gene_id, ggroup_id) + self.unannotated_counts += counts[:4] + + for group_id, counts in grouped_counts.items(): + if group_id == "0": self.unannotated_counts += counts[:4] + else: + _, _, region_annotation = db.query_sequence(int(group_id, 16), grouped_db=True,) + self.distribute_feature_counts(counts, region_annotation) self.calculate_scaling_factors() diff --git a/gffquant/db/annotation_db.py b/gffquant/db/annotation_db.py index 6623a444..1f7b54ea 100644 --- a/gffquant/db/annotation_db.py +++ b/gffquant/db/annotation_db.py @@ -59,14 +59,18 @@ def get_features(self, category_id): def get_categories(self): ... - def query_sequence(self, seqid, start=None, end=None): + def query_sequence(self, seqid, start=None, end=None, grouped_db=False,): """ Returns strand, seq-feature id (e.g. contig id), functional categories """ - db_sequence = self.query_sequence_internal(seqid, start=start, end=end) + db_sequence = self.query_sequence_internal(seqid, start=start, end=end, grouped_db=grouped_db,) if db_sequence is None: return None categories = tuple() - for cat_features in db_sequence.annotation_str.strip().split(";"): + + annotation_str = db_sequence.annotation_str if hasattr(db_sequence, "annotation_str") else db_sequence.annotation + + # for cat_features in db_sequence.annotation_str.strip().split(";"): + for cat_features in annotation_str.strip().split(";"): category, features = cat_features.split("=") categories += ((category, tuple(feature.strip() for feature in features.split(",") if feature.strip())),) return db_sequence.strand, db_sequence.featureid, categories @@ -192,8 +196,12 @@ def __init__(self, db_path): super().__init__() self.db_session = db_path - def query_sequence_internal(self, seqid, start=None, end=None): - if start is not None and end is not None: + def query_sequence_internal(self, seqid, start=None, end=None, grouped_db=False,): + if grouped_db: + db_sequence = self.db_session.query(db.AnnotationGroup) \ + .filter(db.AnnotationGroup.id == seqid) \ + .one_or_none() + elif start is not None and end is not None: db_sequence = self.db_session.query(db.AnnotatedSequence) \ .filter(db.AnnotatedSequence.seqid == seqid) \ .filter((db.AnnotatedSequence.start == start) & (db.AnnotatedSequence.end == end)).one_or_none() diff --git a/gffquant/db/models/db.py b/gffquant/db/models/db.py index bcff37d6..c81aa2ea 100644 --- a/gffquant/db/models/db.py +++ b/gffquant/db/models/db.py @@ -8,6 +8,16 @@ from .meta import Base +class AnnotationGroup(Base): + __tablename__ = "annotation_group" + + id = Column(Integer, primary_key=True) + id16 = Column(String, index=True) + annotation = Column(String) + n_members = Column(Integer) + sha256 = Column(String) + + class AnnotationString(Base): __tablename__ = "annotationstring" From cb0260a3ab228dc08419633154c8ee93ba7f7c18 Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Fri, 17 Jul 2026 15:25:13 +0200 Subject: [PATCH 02/11] fix --- gffquant/annotation/count_annotator.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/gffquant/annotation/count_annotator.py b/gffquant/annotation/count_annotator.py index 98260ed4..1ffeccaf 100644 --- a/gffquant/annotation/count_annotator.py +++ b/gffquant/annotation/count_annotator.py @@ -304,17 +304,14 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): strand_specific_counts=strand_specific_counts, ) - gcounts = self.gene_counts.setdefault(gene_id, np.zeros(self.bins)) - gcounts += counts - self.total_gene_counts += counts[:4] - if gene_group_db: ref_tokens = ref.split(".") gene_id, ggroup_id = ".".join(ref_tokens[:-1]), ref_tokens[-1] grouped_counts.setdefault(ggroup_id, np.zeroes(self.bins)) grouped_counts += counts else: - region_annotation = db.query_sequence(ref) + gene_id = ref + region_annotation = db.query_sequence(gene_id) if region_annotation is not None: _, _, region_annotation = region_annotation # logger.info( @@ -326,6 +323,10 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): # logger.info("GCAnnotator: Gene %s (group=%s) has no information in database.", gene_id, ggroup_id) self.unannotated_counts += counts[:4] + gcounts = self.gene_counts.setdefault(gene_id, np.zeros(self.bins)) + gcounts += counts + self.total_gene_counts += counts[:4] + for group_id, counts in grouped_counts.items(): if group_id == "0": self.unannotated_counts += counts[:4] From 1cac6c1841cc4c2f8295001cd9675c060c11100f Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Fri, 17 Jul 2026 16:26:41 +0200 Subject: [PATCH 03/11] fix --- gffquant/annotation/count_annotator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gffquant/annotation/count_annotator.py b/gffquant/annotation/count_annotator.py index 1ffeccaf..44b55787 100644 --- a/gffquant/annotation/count_annotator.py +++ b/gffquant/annotation/count_annotator.py @@ -307,7 +307,7 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): if gene_group_db: ref_tokens = ref.split(".") gene_id, ggroup_id = ".".join(ref_tokens[:-1]), ref_tokens[-1] - grouped_counts.setdefault(ggroup_id, np.zeroes(self.bins)) + grouped_counts.setdefault(ggroup_id, np.zeros(self.bins)) grouped_counts += counts else: gene_id = ref From d3dddc569209489f332e75321242f2a80086f670 Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Fri, 17 Jul 2026 18:22:48 +0200 Subject: [PATCH 04/11] fix --- gffquant/annotation/count_annotator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gffquant/annotation/count_annotator.py b/gffquant/annotation/count_annotator.py index 44b55787..c29cb9b2 100644 --- a/gffquant/annotation/count_annotator.py +++ b/gffquant/annotation/count_annotator.py @@ -308,7 +308,7 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): ref_tokens = ref.split(".") gene_id, ggroup_id = ".".join(ref_tokens[:-1]), ref_tokens[-1] grouped_counts.setdefault(ggroup_id, np.zeros(self.bins)) - grouped_counts += counts + grouped_counts[ggroup_id] += counts else: gene_id = ref region_annotation = db.query_sequence(gene_id) From d9bfd96a49311f766d1fc47ba3b726f3e94004d5 Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Fri, 17 Jul 2026 19:18:43 +0200 Subject: [PATCH 05/11] fix --- gffquant/db/models/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gffquant/db/models/db.py b/gffquant/db/models/db.py index c81aa2ea..d103acc6 100644 --- a/gffquant/db/models/db.py +++ b/gffquant/db/models/db.py @@ -14,7 +14,7 @@ class AnnotationGroup(Base): id = Column(Integer, primary_key=True) id16 = Column(String, index=True) annotation = Column(String) - n_members = Column(Integer) + nmembers = Column(Integer) sha256 = Column(String) From 9f92379a7e850745fe66bf481dfc43c5f7173f0c Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Fri, 17 Jul 2026 20:12:01 +0200 Subject: [PATCH 06/11] fix --- gffquant/db/annotation_db.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gffquant/db/annotation_db.py b/gffquant/db/annotation_db.py index 1f7b54ea..71dbc4e4 100644 --- a/gffquant/db/annotation_db.py +++ b/gffquant/db/annotation_db.py @@ -68,12 +68,14 @@ def query_sequence(self, seqid, start=None, end=None, grouped_db=False,): categories = tuple() annotation_str = db_sequence.annotation_str if hasattr(db_sequence, "annotation_str") else db_sequence.annotation + strand = db_sequence.strand if hasattr(db_sequence, "strand") else None + featureid = db_sequence.feature_id if hasattr(db_sequence, "feature_id") else None # for cat_features in db_sequence.annotation_str.strip().split(";"): for cat_features in annotation_str.strip().split(";"): category, features = cat_features.split("=") categories += ((category, tuple(feature.strip() for feature in features.split(",") if feature.strip())),) - return db_sequence.strand, db_sequence.featureid, categories + return strand, featureid, categories @abstractmethod def query_feature(self, feature_id): From 074b4845b9b84daefcdffba4b6d5115244b78a94 Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Sat, 18 Jul 2026 11:01:00 +0200 Subject: [PATCH 07/11] fix --- gffquant/db/annotation_db.py | 11 ++++++++--- gffquant/db/models/db.py | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gffquant/db/annotation_db.py b/gffquant/db/annotation_db.py index 71dbc4e4..83402dd6 100644 --- a/gffquant/db/annotation_db.py +++ b/gffquant/db/annotation_db.py @@ -65,16 +65,21 @@ def query_sequence(self, seqid, start=None, end=None, grouped_db=False,): if db_sequence is None: return None + categories = tuple() - annotation_str = db_sequence.annotation_str if hasattr(db_sequence, "annotation_str") else db_sequence.annotation - strand = db_sequence.strand if hasattr(db_sequence, "strand") else None - featureid = db_sequence.feature_id if hasattr(db_sequence, "feature_id") else None + annotation_str = db_sequence.annotation_str + strand = getattr(db_sequence, "feature_id", None) + featureid = getattr(db_sequence, "feature_id", None) + + # db_sequence.strand if hasattr(db_sequence, "strand") else None + # featureid = db_sequence.feature_id if hasattr(db_sequence, "feature_id") else None # for cat_features in db_sequence.annotation_str.strip().split(";"): for cat_features in annotation_str.strip().split(";"): category, features = cat_features.split("=") categories += ((category, tuple(feature.strip() for feature in features.split(",") if feature.strip())),) + return strand, featureid, categories @abstractmethod diff --git a/gffquant/db/models/db.py b/gffquant/db/models/db.py index d103acc6..04187cb2 100644 --- a/gffquant/db/models/db.py +++ b/gffquant/db/models/db.py @@ -13,8 +13,8 @@ class AnnotationGroup(Base): id = Column(Integer, primary_key=True) id16 = Column(String, index=True) - annotation = Column(String) - nmembers = Column(Integer) + annotation_str = Column(String) + n_members = Column(Integer) sha256 = Column(String) From 9a1f9622dcc461375483c9b930b7fbede9d8e6aa Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Sat, 18 Jul 2026 11:10:55 +0200 Subject: [PATCH 08/11] clean --- gffquant/db/annotation_db.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/gffquant/db/annotation_db.py b/gffquant/db/annotation_db.py index 83402dd6..8d3d875a 100644 --- a/gffquant/db/annotation_db.py +++ b/gffquant/db/annotation_db.py @@ -65,18 +65,12 @@ def query_sequence(self, seqid, start=None, end=None, grouped_db=False,): if db_sequence is None: return None - - categories = tuple() - annotation_str = db_sequence.annotation_str strand = getattr(db_sequence, "feature_id", None) featureid = getattr(db_sequence, "feature_id", None) - - # db_sequence.strand if hasattr(db_sequence, "strand") else None - # featureid = db_sequence.feature_id if hasattr(db_sequence, "feature_id") else None - # for cat_features in db_sequence.annotation_str.strip().split(";"): - for cat_features in annotation_str.strip().split(";"): + categories = tuple() + for cat_features in db_sequence.annotation_str.strip().split(";"): category, features = cat_features.split("=") categories += ((category, tuple(feature.strip() for feature in features.split(",") if feature.strip())),) From cec63b358195b500b31e752ebe7d5da716904ad8 Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Sat, 18 Jul 2026 11:19:50 +0200 Subject: [PATCH 09/11] fix --- gffquant/annotation/count_annotator.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/gffquant/annotation/count_annotator.py b/gffquant/annotation/count_annotator.py index c29cb9b2..d47b2e40 100644 --- a/gffquant/annotation/count_annotator.py +++ b/gffquant/annotation/count_annotator.py @@ -314,13 +314,8 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): region_annotation = db.query_sequence(gene_id) if region_annotation is not None: _, _, region_annotation = region_annotation - # logger.info( - # "GCAnnotator: Distributing counts of Gene %s (group=%s) %s %s", - # gene_id, ggroup_id, counts[0], counts[2], - # ) self.distribute_feature_counts(counts, region_annotation) else: - # logger.info("GCAnnotator: Gene %s (group=%s) has no information in database.", gene_id, ggroup_id) self.unannotated_counts += counts[:4] gcounts = self.gene_counts.setdefault(gene_id, np.zeros(self.bins)) @@ -331,7 +326,9 @@ def annotate(self, refmgr, db, count_manager, gene_group_db=False): if group_id == "0": self.unannotated_counts += counts[:4] else: - _, _, region_annotation = db.query_sequence(int(group_id, 16), grouped_db=True,) - self.distribute_feature_counts(counts, region_annotation) + region_annotation = db.query_sequence(int(group_id, 16), grouped_db=True,) + if region_annotation is not None: + _, _, region_annotation = region_annotation + self.distribute_feature_counts(counts, region_annotation) self.calculate_scaling_factors() From 1bb605d1d64f91d13ed014362c28b59d07ac736f Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Sat, 18 Jul 2026 11:24:07 +0200 Subject: [PATCH 10/11] fix --- gffquant/db/annotation_db.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gffquant/db/annotation_db.py b/gffquant/db/annotation_db.py index 8d3d875a..0537bea6 100644 --- a/gffquant/db/annotation_db.py +++ b/gffquant/db/annotation_db.py @@ -48,7 +48,7 @@ def from_db(cls, db_path, in_memory=True): return SQL_ADM(db_path) @abstractmethod - def query_sequence_internal(self, seqid, start=None, end=None): + def query_sequence_internal(self, seqid, start=None, end=None, grouped_db=False,): ... @abstractmethod @@ -238,7 +238,7 @@ def __init__(self, db_path): super().__init__() self.db = db_path - def query_sequence_internal(self, seqid, start=None, end=None): + def query_sequence_internal(self, seqid, start=None, end=None, grouped_db=False,): if start is not None and end is not None: seqs = [ seq From fbed08eff31ca138b5d500af4f9d16efd4ee758b Mon Sep 17 00:00:00 2001 From: Christian Schudoma Date: Sat, 18 Jul 2026 11:59:55 +0200 Subject: [PATCH 11/11] fix --- gffquant/db/annotation_db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gffquant/db/annotation_db.py b/gffquant/db/annotation_db.py index 0537bea6..c9615b5c 100644 --- a/gffquant/db/annotation_db.py +++ b/gffquant/db/annotation_db.py @@ -66,7 +66,7 @@ def query_sequence(self, seqid, start=None, end=None, grouped_db=False,): if db_sequence is None: return None - strand = getattr(db_sequence, "feature_id", None) + strand = getattr(db_sequence, "strand", None) featureid = getattr(db_sequence, "feature_id", None) categories = tuple()