molstar
Version:
A comprehensive macromolecular library.
346 lines • 15.3 kB
JavaScript
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { __awaiter, __generator } from "tslib";
import { Structure } from '../../../mol-model/structure';
import { PerformanceMonitor } from '../../../mol-util/performance-monitor';
import { Cache } from './cache';
import { ModelServerConfig as Config, mapSourceAndIdToFilename } from '../config';
import { CIF } from '../../../mol-io/reader/cif';
import * as util from 'util';
import * as fs from 'fs';
import * as zlib from 'zlib';
import { ConsoleLogger } from '../../../mol-util/console-logger';
import { trajectoryFromMmCIF } from '../../../mol-model-formats/structure/mmcif';
import { fetchRetry } from '../utils/fetch-retry';
import { Task } from '../../../mol-task';
require('util.promisify').shim();
export var StructureSourceType;
(function (StructureSourceType) {
StructureSourceType[StructureSourceType["File"] = 0] = "File";
StructureSourceType[StructureSourceType["Cache"] = 1] = "Cache";
})(StructureSourceType || (StructureSourceType = {}));
export function createStructureWrapperFromJobEntry(entry, propertyProvider, allowCache) {
if (allowCache === void 0) { allowCache = true; }
return __awaiter(this, void 0, void 0, function () {
var ret_1, ret;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (allowCache && Config.cacheMaxSizeInBytes > 0) {
ret_1 = StructureCache.get(entry.key);
if (ret_1)
return [2 /*return*/, ret_1];
}
return [4 /*yield*/, readStructureWrapper(entry.key, entry.sourceId, entry.entryId, entry.job.id, propertyProvider)];
case 1:
ret = _a.sent();
if (allowCache && Config.cacheMaxSizeInBytes > 0) {
StructureCache.add(ret);
}
return [2 /*return*/, ret];
}
});
});
}
export var StructureCache = new Cache(function (s) { return s.key; }, function (s) { return s.approximateSize; });
var perf = new PerformanceMonitor();
var readFileAsync = util.promisify(fs.readFile);
var unzipAsync = util.promisify(zlib.unzip);
function readFile(filename) {
return __awaiter(this, void 0, void 0, function () {
var isGz, input, data, i, data, _a;
var _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
isGz = /\.gz$/i.test(filename);
if (!filename.match(/\.bcif/)) return [3 /*break*/, 4];
return [4 /*yield*/, readFileAsync(filename)];
case 1:
input = _c.sent();
if (!isGz) return [3 /*break*/, 3];
return [4 /*yield*/, unzipAsync(input)];
case 2:
input = _c.sent();
_c.label = 3;
case 3:
data = new Uint8Array(input.byteLength);
for (i = 0; i < input.byteLength; i++)
data[i] = input[i];
return [2 /*return*/, { data: data, isBinary: true }];
case 4:
if (!isGz) return [3 /*break*/, 7];
_a = unzipAsync;
return [4 /*yield*/, readFileAsync(filename)];
case 5: return [4 /*yield*/, _a.apply(void 0, [_c.sent()])];
case 6:
data = _c.sent();
return [2 /*return*/, { data: data.toString('utf8'), isBinary: false }];
case 7:
_b = {};
return [4 /*yield*/, readFileAsync(filename, 'utf8')];
case 8: return [2 /*return*/, (_b.data = _c.sent(), _b.isBinary = false, _b)];
}
});
});
}
function parseCif(data) {
return __awaiter(this, void 0, void 0, function () {
var comp, parsed;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
comp = CIF.parse(data);
return [4 /*yield*/, comp.run()];
case 1:
parsed = _a.sent();
if (parsed.isError)
throw parsed;
return [2 /*return*/, parsed.result];
}
});
});
}
export function readDataAndFrame(filename, key) {
return __awaiter(this, void 0, void 0, function () {
var data, isBinary, read, e_1, frame;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
perf.start('read');
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, readFile(filename)];
case 2:
read = _a.sent();
data = read.data;
isBinary = read.isBinary;
return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
ConsoleLogger.error(key || filename, '' + e_1);
throw new Error("Could not read the file for '" + (key || filename) + "' from disk.");
case 4:
perf.end('read');
perf.start('parse');
return [4 /*yield*/, parseCif(data)];
case 5:
frame = (_a.sent()).blocks[0];
perf.end('parse');
return [2 /*return*/, { data: data, frame: frame, isBinary: isBinary }];
}
});
});
}
function fetchDataAndFrame(jobId, uri, format, key) {
return __awaiter(this, void 0, void 0, function () {
var isBinary, data, response, input, _a, i, _b, _c, e_2, frame;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
perf.start('read');
isBinary = format.startsWith('bcif');
_d.label = 1;
case 1:
_d.trys.push([1, 11, , 12]);
ConsoleLogger.logId(jobId, 'Fetch', "" + uri);
return [4 /*yield*/, fetchRetry(uri, 500, 3, function () { return ConsoleLogger.logId(jobId, 'Fetch', "Retrying to fetch '" + uri + "'"); })];
case 2:
response = _d.sent();
if (!format.endsWith('.gz')) return [3 /*break*/, 5];
_a = unzipAsync;
return [4 /*yield*/, response.arrayBuffer()];
case 3: return [4 /*yield*/, _a.apply(void 0, [_d.sent()])];
case 4:
input = _d.sent();
if (isBinary) {
data = new Uint8Array(input.byteLength);
for (i = 0; i < input.byteLength; i++)
data[i] = input[i];
}
else {
data = input.toString('utf8');
}
return [3 /*break*/, 10];
case 5:
if (!isBinary) return [3 /*break*/, 7];
_c = Uint8Array.bind;
return [4 /*yield*/, response.arrayBuffer()];
case 6:
_b = new (_c.apply(Uint8Array, [void 0, _d.sent()]))();
return [3 /*break*/, 9];
case 7: return [4 /*yield*/, response.text()];
case 8:
_b = _d.sent();
_d.label = 9;
case 9:
data = _b;
_d.label = 10;
case 10: return [3 /*break*/, 12];
case 11:
e_2 = _d.sent();
ConsoleLogger.error(key || uri, '' + e_2);
throw new Error("Could not fetch the file for '" + (key || uri) + "'.");
case 12:
perf.end('read');
perf.start('parse');
return [4 /*yield*/, parseCif(data)];
case 13:
frame = (_d.sent()).blocks[0];
perf.end('parse');
return [2 /*return*/, { data: data, frame: frame, isBinary: isBinary }];
}
});
});
}
function readOrFetch(jobId, key, sourceId, entryId) {
var mapped = sourceId === '_local_' ? [entryId] : mapSourceAndIdToFilename(sourceId, entryId);
if (!mapped)
throw new Error("Cound not map '" + key + "' for a resource.");
var uri = mapped[0].toLowerCase();
if (uri.startsWith('http://') || uri.startsWith('https://') || uri.startsWith('ftp://')) {
return fetchDataAndFrame(jobId, mapped[0], (mapped[1] || 'cif').toLowerCase(), key);
}
if (!fs.existsSync(mapped[0]))
throw new Error("Could not find source file for '" + key + "'.");
return readDataAndFrame(mapped[0], key);
}
export function readStructureWrapper(key, sourceId, entryId, jobId, propertyProvider) {
return __awaiter(this, void 0, void 0, function () {
var _a, data, frame, isBinary, trajectory, models, modelMap, i, m, ret;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, readOrFetch(jobId || '', key, sourceId, entryId)];
case 1:
_a = _b.sent(), data = _a.data, frame = _a.frame, isBinary = _a.isBinary;
perf.start('createModel');
return [4 /*yield*/, trajectoryFromMmCIF(frame).run()];
case 2:
trajectory = _b.sent();
perf.end('createModel');
models = [];
modelMap = new Map();
i = 0;
_b.label = 3;
case 3:
if (!(i < trajectory.frameCount)) return [3 /*break*/, 6];
return [4 /*yield*/, Task.resolveInContext(trajectory.getFrameAtIndex(i))];
case 4:
m = _b.sent();
models.push(m);
modelMap.set(m.modelNum, m);
_b.label = 5;
case 5:
i++;
return [3 /*break*/, 3];
case 6:
ret = {
info: {
sourceType: StructureSourceType.File,
readTime: perf.time('read'),
parseTime: perf.time('parse'),
createModelTime: perf.time('createModel'),
attachPropsTime: 0,
sourceId: sourceId,
entryId: entryId
},
isBinary: isBinary,
key: key,
approximateSize: typeof data === 'string' ? 2 * data.length : data.length,
models: models,
modelMap: modelMap,
structureModelMap: new Map(),
cifFrame: frame,
propertyProvider: propertyProvider,
cache: Object.create(null)
};
return [2 /*return*/, ret];
}
});
});
}
export function resolveStructure(wrapper, modelNum) {
return __awaiter(this, void 0, void 0, function () {
var model, structure, modelProps, _i, modelProps_1, p;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (typeof modelNum === 'undefined')
modelNum = wrapper.models[0].modelNum;
if (wrapper.structureModelMap.has(modelNum))
return [2 /*return*/, wrapper.structureModelMap.get(modelNum)];
if (!wrapper.modelMap.has(modelNum)) {
return [2 /*return*/, void 0];
}
model = wrapper.modelMap.get(modelNum);
structure = Structure.ofModel(model);
if (!wrapper.propertyProvider) return [3 /*break*/, 4];
modelProps = wrapper.propertyProvider(model, wrapper.cache);
_i = 0, modelProps_1 = modelProps;
_a.label = 1;
case 1:
if (!(_i < modelProps_1.length)) return [3 /*break*/, 4];
p = modelProps_1[_i];
return [4 /*yield*/, tryAttach(wrapper.key, p)];
case 2:
_a.sent();
_a.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/, structure];
}
});
});
}
export function resolveStructures(wrapper, modelNums) {
return __awaiter(this, void 0, void 0, function () {
var ret, _i, _a, n, s;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
ret = [];
_i = 0, _a = modelNums || wrapper.models.map(function (m) { return m.modelNum; });
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
n = _a[_i];
return [4 /*yield*/, resolveStructure(wrapper, n)];
case 2:
s = _b.sent();
if (s)
ret.push(s);
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/, ret];
}
});
});
}
function tryAttach(key, promise) {
return __awaiter(this, void 0, void 0, function () {
var e_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, promise];
case 1:
_a.sent();
return [3 /*break*/, 3];
case 2:
e_3 = _a.sent();
ConsoleLogger.errorId(key, 'Custom prop:' + e_3);
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
});
}
//# sourceMappingURL=structure-wrapper.js.map