• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eweitz / ideogram / 12131714885

03 Dec 2024 02:21AM UTC coverage: 82.273% (-1.1%) from 83.356%
12131714885

push

github

web-flow
Merge pull request #375 from eweitz/smooth-repeat-hover

Avoid hiding tooltip on first hover of previous clicked annotation

2359 of 3225 branches covered (73.15%)

Branch coverage included in aggregate %.

10 of 15 new or added lines in 3 files covered. (66.67%)

455 existing lines in 18 files now uncovered.

5410 of 6218 relevant lines covered (87.01%)

27653.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.0
/src/js/init/caches/cache-lib.js
1
/** @fileoverview Functions used by multiple content-specific cache modules */
2

3
import {decompressSync, strFromU8} from 'fflate';
4
import {createPartialResponse} from 'workbox-range-requests';
5

6
import version from '../../version';
7
import {getEarlyTaxid, slug, getDir} from '../../lib';
8
import {organismMetadata} from '../organism-metadata';
9

10
async function fetchByteRangesByName(url) {
11
  const byteRangesByName = {};
1✔
12

13
  const path = `${url.replace('.tsv.gz', '')}.tsv.li.gz`;
1✔
14

15
  const response = await cacheFetch(path);
1✔
16
  const text = await response.text();
1✔
17
  const lines = text.split('\n');
1✔
18
  for (let i = 0; i < lines.length - 1; i++) {
1✔
19
    const [gene, rawOffset] = lines[i].split('\t');
40,412✔
20
    if (gene[0] === '#') continue;
40,412✔
21
    const offset = parseInt(rawOffset);
40,411✔
22
    const offsetEnd = parseInt(lines[i + 1].split('\t')[1]);
40,411✔
23
    byteRangesByName[gene] = [offset, offsetEnd];
40,411✔
24
  }
25

26
  return byteRangesByName;
1✔
27
}
28

29
/** Reports if current organism has a gene structure cache */
30
export function supportsCache(orgName, cacheName) {
31
  const metadata = parseOrgMetadata(orgName);
103✔
32
  const cacheProp = 'has' + cacheName + 'Cache';
103✔
33
  return metadata[cacheProp] && metadata[cacheProp] === true;
103✔
34
}
35

36
/** Get URL for gene structure cache file */
37
export function getCacheUrl(orgName, cacheDir, cacheType, fileType='tsv') {
×
38
  const organism = slug(orgName);
584✔
39
  if (!cacheDir) {
584✔
40
    cacheDir = getDir('cache/' + cacheType + '/');
6✔
41
  } else {
42
    cacheDir += cacheType + '/';
578✔
43
  }
44

45
  const cacheUrl =
46
    cacheDir + organism + '-' + cacheType + '.' + fileType + '.gz';
584✔
47

48
  return cacheUrl;
584✔
49
}
50

51
/**
52
 * Build full ID from prefix (e.g. ENSG or IPR) and slim ID (e.g. 223972)
53
 *
54
 * Example output ID: ENSG00000223972
55
 * */
56
export function getFullId(prefix, slimId, fullNumLength=11) {
148,643✔
57

58
  // C. elegans (prefix: WBGene) has special IDs, e.g. WBGene00197333
59
  if (prefix === 'WBGene') fullNumLength = 8;
148,643!
60

61
  // Zero-pad the slim ID, e.g. 223972 -> 00000223972
62
  const zeroPaddedId = slimId.padStart(fullNumLength, '0');
148,643✔
63

64
  return prefix + zeroPaddedId;
148,643✔
65
}
66

67

68
async function getServiceWorkerCache() {
69
  const currentIdeogram = `ideogram-${version}`;
585✔
70

71
  // Delete other versions of Ideogram cache; there should be 1 per dodmain
72
  const cacheNames = await caches.keys();
585✔
73
  cacheNames.forEach(name => {
585✔
74
    if (name.startsWith('ideogram-') && name !== currentIdeogram) {
578!
UNCOV
75
      caches.delete(name);
×
76
    }
77
  });
78

79
  const cache = await caches.open(currentIdeogram);
585✔
80

81
  return cache;
585✔
82
}
83

84
export async function cacheFetch(url) {
85

86
  const cache = await getServiceWorkerCache();
11✔
87
  window.ideoCache = cache;
11✔
88
  window.createPartialResponse = createPartialResponse;
11✔
89

90
  const decompressedUrl = url.replace('.gz', '');
11✔
91
  const response = await cache.match(decompressedUrl);
11✔
92
  if (typeof response === 'undefined') {
11!
93
    // If cache miss, then fetch, decompress, and put response in cache
94
    const rawResponse = await fetch(url);
11✔
95
    const blob = await rawResponse.blob();
11✔
96
    const uint8Array = new Uint8Array(await blob.arrayBuffer());
11✔
97
    const data = strFromU8(decompressSync(uint8Array));
11✔
98
    const contentLength = data.length;
11✔
99
    const decompressedResponse = new Response(
11✔
100
      new Blob([data], {type: 'text/tab-separated-values'}),
101
      {headers: new Headers({'Content-Length': contentLength})}
102
    );
103
    await cache.put(decompressedUrl, decompressedResponse);
11✔
104
    return await cache.match(decompressedUrl);
11✔
105
  }
UNCOV
106
  return await cache.match(decompressedUrl);
×
107
}
108

109
export async function cacheRangeFetch(url, byteRange) {
110
  url = url.replace('.gz', '');
574✔
111

112
  // +/- 1 to trim newlines
113
  const rangeStart = byteRange[0] + 1;
574✔
114
  const rangeEnd = byteRange[1] - 1;
574✔
115

116
  const headers = new Headers({
574✔
117
    'content-type': 'multipart/byteranges',
118
    'range': `bytes=${rangeStart}-${rangeEnd}`
119
  });
120

121
  const request = new Request(url, {headers});
574✔
122

123
  const cache = await getServiceWorkerCache();
574✔
124

125
  const fullResponse = await cache.match(request);
574✔
126
  const partialResponse = await createPartialResponse(request, fullResponse);
574✔
127

128
  const text = await partialResponse.text();
574✔
129

130
  return text;
574✔
131
}
132

133
window.cacheRangeFetch = cacheRangeFetch;
1✔
134

135
/** Get organism's metadata fields */
136
export function parseOrgMetadata(orgName) {
137
  const taxid = getEarlyTaxid(orgName);
103✔
138
  return organismMetadata[taxid] || {};
103!
139
}
140

141
/** Fetch URL from service worker cache, call given parsing function */
142
export async function fetchAndParse(
143
  cacheUrl, perfTimes, parseFn, orgName=null
×
144
) {
145
  const fetchStartTime = performance.now();
10✔
146
  const response = await cacheFetch(cacheUrl);
10✔
147
  let data;
148
  if (cacheUrl.includes('.json')) {
10✔
149
    data = await response.json();
1✔
150
  } else {
151
    data = await response.text();
9✔
152
  }
153
  const fetchEndTime = performance.now();
10✔
154
  perfTimes.fetch = Math.round(fetchEndTime - fetchStartTime);
10✔
155

156
  let parsedCache;
157
  if (cacheUrl.includes('tissue')) {
10✔
158
    const byteRangesByName = await fetchByteRangesByName(cacheUrl);
1✔
159
    parsedCache = parseFn(data, perfTimes, byteRangesByName);
1✔
160
  } else {
161
    parsedCache = parseFn(data, perfTimes, orgName);
9✔
162
  }
163

164
  perfTimes.parseCache = Math.round(performance.now() - fetchEndTime);
10✔
165

166
  return [parsedCache, perfTimes];
10✔
167
}
168

169
/** Print size and time of given parsed cache */
170
export function inspectWorker(cacheName, json) {
UNCOV
171
  const size = new TextEncoder().encode(JSON.stringify(json)).length;
×
UNCOV
172
  const kiloBytes = size / 1024;
×
UNCOV
173
  const megaBytes = kiloBytes / 1024;
×
UNCOV
174
  console.log(`Parsed ${cacheName}Cache size: ${megaBytes} MiB`);
×
UNCOV
175
  console.timeEnd(`${cacheName}CacheWorker`);
×
176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc