80484198a43b2152023cadd0b6abd26785ab69ea hiram Wed Jul 29 14:53:53 2026 -0700 now archiving versions of NCBI GenBank gene track when updated refs #37958 diff --git src/hg/utils/automation/AsmHub.pm src/hg/utils/automation/AsmHub.pm index e6a28d0bfce..8816f072b4f 100755 --- src/hg/utils/automation/AsmHub.pm +++ src/hg/utils/automation/AsmHub.pm @@ -1,35 +1,36 @@ # AsmHub: common routines for building assembly hubs # # DO NOT EDIT the /cluster/bin/scripts copy of this file -- # edit ~/kent/src/hg/utils/automation/AsmHub.pm instead. package AsmHub; use warnings; use strict; use Carp; +use File::Basename; use vars qw(@ISA @EXPORT_OK); use Exporter; @ISA = qw(Exporter); # This is a listing of the public methods and variables (which should be # treated as constants) exported by this module: @EXPORT_OK = ( # Support for common command line options: - qw( commify asmSize + qw( commify asmSize ncbiGeneDescription ), ); # from Perl Cookbook Recipe 2.17, print out large numbers with comma # delimiters, input is a large number with no commas: sub commify($) { my $text = reverse $_[0]; $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; return scalar reverse $text } # given an asmId.chrom.sizes, return the assembly size from the # sum of column 2: sub asmSize($) { my ($chromSizes) = @_; @@ -37,15 +38,131 @@ chomp $asmSize; return $asmSize; } # given a fully qualified asmId, e.g.: GCA_018504075.1_HG02723.alt.pat.f1_v2 # return the string representating the path: GCA/018/504/075 sub asmIdToPath($) { my ($asmId) = @_; my $gcX = substr($asmId, 0, 3); my $d0 = substr($asmId, 4, 3); my $d1 = substr($asmId, 7, 3); my $d2 = substr($asmId, 10, 3); my $ret = sprintf("%s/%s/%s/%s", $gcX, $d0, $d1, $d2); return $ret; } + +# Build the 'ncbiGene' track description HTML body. Used both by +# asmHubNcbiGene.pl for the live track, and by doNcbiGene.pl for the +# one-track archive hubs it writes under trackData/ncbiGene/archive/ -- +# which is why every path is taken explicitly rather than assumed from +# the usual trackData/ layout. $archiveNote, if given, is printed as a +# lead-in banner ahead of the normal description (used for the archived +# copies; the live page passes it undef). +sub ncbiGeneDescription($$$$$;$) { + my ($bbPath, $statsPath, $chromSizes, $namesFile, $ncbiAsmId, $archiveNote) = @_; + + if ( ! -s $bbPath ) { + printf STDERR "ERROR: can not find %s file\n", $bbPath; + exit 255; + } + + my @partNames = split('_', $ncbiAsmId); + my $ftpDirPath = sprintf("%s/%s/%s/%s/%s", $partNames[0], + substr($partNames[1],0,3), substr($partNames[1],3,3), + substr($partNames[1],6,3), $ncbiAsmId); + my $asmType = ($partNames[0] =~ m/GCA/) ? "genbank" : "refseq"; + + my $totalBases = asmSize($chromSizes); + my $geneStats = `cat $statsPath | awk '{printf "%d\\n", \$2}' | xargs echo`; + chomp $geneStats; + my ($itemCount, $basesCovered) = split('\s+', $geneStats); + my $percentCoverage = sprintf("%.3f", 100.0 * $basesCovered / $totalBases); + $itemCount = commify($itemCount); + $basesCovered = commify($basesCovered); + my $totalBasesText = commify($totalBases); + + my $em = ""; + my $noEm = ""; + my $assemblyDate = `grep -v "^#" $namesFile | cut -f9`; + chomp $assemblyDate; + my $organism = `grep -v "^#" $namesFile | cut -f5`; + chomp $organism; + + my $html = ""; + $html .= "

$archiveNote

\n\n" if ($archiveNote); + + if ( "${asmType}" eq "refseq" ) { + $html .= <<_EOF_; +

Description

+

+The NCBI Gene track for the $assemblyDate $em${organism}$noEm/$ncbiAsmId +genome assembly is constructed from the gff file ${ncbiAsmId}_genomic.gff.gz +supplied with the genome assembly at the FTP location:
+https://ftp.ncbi.nlm.nih.gov/genomes/all/$ftpDirPath/ +

+ +_EOF_ + } else { + $html .= <<_EOF_; +

Description

+

+The Gene model track for the $assemblyDate $em${organism}$noEm/$ncbiAsmId +genome assembly is constructed from the gff file ${ncbiAsmId}_genomic.gff.gz +supplied with the genome assembly at the FTP location:
+https://ftp.ncbi.nlm.nih.gov/genomes/all/$ftpDirPath/ +

+

+The gene models were constructed by the submitter of the assembly to the +NCBI assembly release system. +

+ +_EOF_ + } + + $html .= <<_EOF_; +

Track statistics summary

+

+Total genome size: $totalBasesText
+Gene count: $itemCount
+Bases in genes: $basesCovered
+Percent genome coverage: % $percentCoverage
+

+ +_EOF_ + + # Only the live page advertises archives (an archived page's own + # description already carries $archiveNote and has nothing further + # under it to list). Archived copies are published to hgdownload at + # https://hgdownload.soe.ucsc.edu/hubs///archive/ncbiGene//hub.txt + # -- one self-contained hub.txt per version -- regardless of where they + # sit in the local build tree, so we just need the version list. + if ( ! $archiveNote ) { + my $archiveBase = dirname($bbPath) . "/archive"; + my @versions = (); + if ( -d $archiveBase ) { + opendir(my $dh, $archiveBase); + if ($dh) { + @versions = sort { $b cmp $a } + grep { -d "$archiveBase/$_" && $_ !~ m/^\./ } readdir($dh); + closedir($dh); + } + } + if (@versions) { + my @parts = split('_', $ncbiAsmId); + my $accession = "$parts[0]_$parts[1]"; + my $hashedPath = asmIdToPath($ncbiAsmId); + $html .= "

Archived versions

\n

\n"; + $html .= "Earlier versions of this track, from before NCBI updated the source annotation, remain available as standalone track hubs:\n"; + $html .= "

\n
    \n"; + foreach my $version (@versions) { + my $url = "https://hgdownload.soe.ucsc.edu/hubs/$hashedPath/$accession/archive/ncbiGene/$version/hub.txt"; + $html .= "
  • $version
  • \n"; + } + $html .= "
\n\n"; + } + } + + return $html; +} # ncbiGeneDescription + +1;