data.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { PluginStateTransform } from '../objects';
  8. import { PluginStateObject as SO } from '../objects';
  9. import { Task } from '../../../mol-task';
  10. import { CIF } from '../../../mol-io/reader/cif'
  11. import { PluginContext } from '../../../mol-plugin/context';
  12. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  13. import { StateTransformer } from '../../../mol-state';
  14. import { readFromFile, ajaxGetMany } from '../../../mol-util/data-source';
  15. import * as CCP4 from '../../../mol-io/reader/ccp4/parser'
  16. import * as DSN6 from '../../../mol-io/reader/dsn6/parser'
  17. import * as PLY from '../../../mol-io/reader/ply/parser'
  18. export { Download }
  19. type Download = typeof Download
  20. const Download = PluginStateTransform.BuiltIn({
  21. name: 'download',
  22. display: { name: 'Download', description: 'Download string or binary data from the specified URL' },
  23. from: [SO.Root],
  24. to: [SO.Data.String, SO.Data.Binary],
  25. params: {
  26. url: PD.Text('https://www.ebi.ac.uk/pdbe/static/entry/1cbs_updated.cif', { description: 'Resource URL. Must be the same domain or support CORS.' }),
  27. label: PD.Optional(PD.Text('')),
  28. isBinary: PD.Optional(PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)' }))
  29. }
  30. })({
  31. apply({ params: p }, globalCtx: PluginContext) {
  32. return Task.create('Download', async ctx => {
  33. const data = await globalCtx.fetch({ url: p.url, type: p.isBinary ? 'binary' : 'string' }).runInContext(ctx);
  34. return p.isBinary
  35. ? new SO.Data.Binary(data as Uint8Array, { label: p.label ? p.label : p.url })
  36. : new SO.Data.String(data as string, { label: p.label ? p.label : p.url });
  37. });
  38. },
  39. update({ oldParams, newParams, b }) {
  40. if (oldParams.url !== newParams.url || oldParams.isBinary !== newParams.isBinary) return StateTransformer.UpdateResult.Recreate;
  41. if (oldParams.label !== newParams.label) {
  42. b.label = newParams.label || newParams.url;
  43. return StateTransformer.UpdateResult.Updated;
  44. }
  45. return StateTransformer.UpdateResult.Unchanged;
  46. }
  47. });
  48. export { DownloadBlob }
  49. type DownloadBlob = typeof DownloadBlob
  50. const DownloadBlob = PluginStateTransform.BuiltIn({
  51. name: 'download-blob',
  52. display: { name: 'Download Blob', description: 'Download multiple string or binary data from the specified URLs.' },
  53. from: SO.Root,
  54. to: SO.Data.Blob,
  55. params: {
  56. sources: PD.ObjectList({
  57. id: PD.Text('', { label: 'Unique ID' }),
  58. url: PD.Text('https://www.ebi.ac.uk/pdbe/static/entry/1cbs_updated.cif', { description: 'Resource URL. Must be the same domain or support CORS.' }),
  59. isBinary: PD.Optional(PD.Boolean(false, { description: 'If true, download data as binary (string otherwise)' })),
  60. canFail: PD.Optional(PD.Boolean(false, { description: 'Indicate whether the download can fail and not be included in the blob as a result.' }))
  61. }, e => `${e.id}: ${e.url}`),
  62. maxConcurrency: PD.Optional(PD.Numeric(4, { min: 1, max: 12, step: 1 }, { description: 'The maximum number of concurrent downloads.' }))
  63. }
  64. })({
  65. apply({ params }, plugin: PluginContext) {
  66. return Task.create('Download Blob', async ctx => {
  67. const entries: SO.Data.BlobEntry[] = [];
  68. const data = await ajaxGetMany(ctx, params.sources, params.maxConcurrency || 4);
  69. for (let i = 0; i < data.length; i++) {
  70. const r = data[i], src = params.sources[i];
  71. if (r.kind === 'error') plugin.log.warn(`Download ${r.id} (${src.url}) failed: ${r.error}`);
  72. else {
  73. entries.push(src.isBinary
  74. ? { id: r.id, kind: 'binary', data: r.result as Uint8Array }
  75. : { id: r.id, kind: 'string', data: r.result as string });
  76. }
  77. }
  78. return new SO.Data.Blob(entries, { label: 'Data Blob', description: `${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}` });
  79. });
  80. },
  81. // TODO: ??
  82. // update({ oldParams, newParams, b }) {
  83. // return 0 as any;
  84. // // if (oldParams.url !== newParams.url || oldParams.isBinary !== newParams.isBinary) return StateTransformer.UpdateResult.Recreate;
  85. // // if (oldParams.label !== newParams.label) {
  86. // // (b.label as string) = newParams.label || newParams.url;
  87. // // return StateTransformer.UpdateResult.Updated;
  88. // // }
  89. // // return StateTransformer.UpdateResult.Unchanged;
  90. // }
  91. });
  92. export { ReadFile }
  93. type ReadFile = typeof ReadFile
  94. const ReadFile = PluginStateTransform.BuiltIn({
  95. name: 'read-file',
  96. display: { name: 'Read File', description: 'Read string or binary data from the specified file' },
  97. from: SO.Root,
  98. to: [SO.Data.String, SO.Data.Binary],
  99. params: {
  100. file: PD.File(),
  101. label: PD.Optional(PD.Text('')),
  102. isBinary: PD.Optional(PD.Boolean(false, { description: 'If true, open file as as binary (string otherwise)' }))
  103. }
  104. })({
  105. apply({ params: p }) {
  106. return Task.create('Open File', async ctx => {
  107. const data = await readFromFile(p.file, p.isBinary ? 'binary' : 'string').runInContext(ctx);
  108. return p.isBinary
  109. ? new SO.Data.Binary(data as Uint8Array, { label: p.label ? p.label : p.file.name })
  110. : new SO.Data.String(data as string, { label: p.label ? p.label : p.file.name });
  111. });
  112. },
  113. update({ oldParams, newParams, b }) {
  114. if (oldParams.label !== newParams.label) {
  115. (b.label as string) = newParams.label || oldParams.file.name;
  116. return StateTransformer.UpdateResult.Updated;
  117. }
  118. return StateTransformer.UpdateResult.Unchanged;
  119. },
  120. isSerializable: () => ({ isSerializable: false, reason: 'Cannot serialize user loaded files.' })
  121. });
  122. export { ParseBlob }
  123. type ParseBlob = typeof ParseBlob
  124. const ParseBlob = PluginStateTransform.BuiltIn({
  125. name: 'parse-blob',
  126. display: { name: 'Parse Blob', description: 'Parse multiple data enties' },
  127. from: SO.Data.Blob,
  128. to: SO.Format.Blob,
  129. params: {
  130. formats: PD.ObjectList({
  131. id: PD.Text('', { label: 'Unique ID' }),
  132. format: PD.Select<'cif'>('cif', [['cif', 'cif']])
  133. }, e => `${e.id}: ${e.format}`)
  134. }
  135. })({
  136. apply({ a, params }, plugin: PluginContext) {
  137. return Task.create('Parse Blob', async ctx => {
  138. const map = new Map<string, string>();
  139. for (const f of params.formats) map.set(f.id, f.format);
  140. const entries: SO.Format.BlobEntry[] = [];
  141. for (const e of a.data) {
  142. if (!map.has(e.id)) continue;
  143. const parsed = await (e.kind === 'string' ? CIF.parse(e.data) : CIF.parseBinary(e.data)).runInContext(ctx);
  144. if (parsed.isError) throw new Error(`${e.id}: ${parsed.message}`);
  145. entries.push({ id: e.id, kind: 'cif', data: parsed.result });
  146. }
  147. return new SO.Format.Blob(entries, { label: 'Format Blob', description: `${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}` });
  148. });
  149. },
  150. // TODO: ??
  151. // update({ oldParams, newParams, b }) {
  152. // return 0 as any;
  153. // // if (oldParams.url !== newParams.url || oldParams.isBinary !== newParams.isBinary) return StateTransformer.UpdateResult.Recreate;
  154. // // if (oldParams.label !== newParams.label) {
  155. // // (b.label as string) = newParams.label || newParams.url;
  156. // // return StateTransformer.UpdateResult.Updated;
  157. // // }
  158. // // return StateTransformer.UpdateResult.Unchanged;
  159. // }
  160. });
  161. export { ParseCif }
  162. type ParseCif = typeof ParseCif
  163. const ParseCif = PluginStateTransform.BuiltIn({
  164. name: 'parse-cif',
  165. display: { name: 'Parse CIF', description: 'Parse CIF from String or Binary data' },
  166. from: [SO.Data.String, SO.Data.Binary],
  167. to: SO.Format.Cif
  168. })({
  169. apply({ a }) {
  170. return Task.create('Parse CIF', async ctx => {
  171. const parsed = await (SO.Data.String.is(a) ? CIF.parse(a.data) : CIF.parseBinary(a.data)).runInContext(ctx);
  172. if (parsed.isError) throw new Error(parsed.message);
  173. return new SO.Format.Cif(parsed.result);
  174. });
  175. }
  176. });
  177. export { ParsePly }
  178. type ParsePly = typeof ParsePly
  179. const ParsePly = PluginStateTransform.BuiltIn({
  180. name: 'parse-ply',
  181. display: { name: 'Parse PLY', description: 'Parse PLY from String data' },
  182. from: [SO.Data.String],
  183. to: SO.Format.Ply
  184. })({
  185. apply({ a }) {
  186. return Task.create('Parse PLY', async ctx => {
  187. const parsed = await PLY.parse(a.data).runInContext(ctx);
  188. if (parsed.isError) throw new Error(parsed.message);
  189. return new SO.Format.Ply(parsed.result, { label: parsed.result.comments[0] || 'PLY Data' });
  190. });
  191. }
  192. });
  193. export { ParseCcp4 }
  194. type ParseCcp4 = typeof ParseCcp4
  195. const ParseCcp4 = PluginStateTransform.BuiltIn({
  196. name: 'parse-ccp4',
  197. display: { name: 'Parse CCP4/MRC/MAP', description: 'Parse CCP4/MRC/MAP from Binary data' },
  198. from: [SO.Data.Binary],
  199. to: SO.Format.Ccp4
  200. })({
  201. apply({ a }) {
  202. return Task.create('Parse CCP4/MRC/MAP', async ctx => {
  203. const parsed = await CCP4.parse(a.data).runInContext(ctx);
  204. if (parsed.isError) throw new Error(parsed.message);
  205. return new SO.Format.Ccp4(parsed.result);
  206. });
  207. }
  208. });
  209. export { ParseDsn6 }
  210. type ParseDsn6 = typeof ParseDsn6
  211. const ParseDsn6 = PluginStateTransform.BuiltIn({
  212. name: 'parse-dsn6',
  213. display: { name: 'Parse DSN6/BRIX', description: 'Parse CCP4/BRIX from Binary data' },
  214. from: [SO.Data.Binary],
  215. to: SO.Format.Dsn6
  216. })({
  217. apply({ a }) {
  218. return Task.create('Parse DSN6/BRIX', async ctx => {
  219. const parsed = await DSN6.parse(a.data).runInContext(ctx);
  220. if (parsed.isError) throw new Error(parsed.message);
  221. return new SO.Format.Dsn6(parsed.result);
  222. });
  223. }
  224. });
  225. export { ImportString }
  226. type ImportString = typeof ImportString
  227. const ImportString = PluginStateTransform.BuiltIn({
  228. name: 'import-string',
  229. display: { name: 'Import String', description: 'Import given data as a string' },
  230. from: SO.Root,
  231. to: SO.Data.String,
  232. params: {
  233. data: PD.Value(''),
  234. label: PD.Optional(PD.Text('')),
  235. }
  236. })({
  237. apply({ params: { data, label } }) {
  238. return new SO.Data.String(data, { label: label || '' });
  239. },
  240. update({ oldParams, newParams, b }) {
  241. if (oldParams.data !== newParams.data) return StateTransformer.UpdateResult.Recreate;
  242. if (oldParams.label !== newParams.label) {
  243. b.label = newParams.label || '';
  244. return StateTransformer.UpdateResult.Updated;
  245. }
  246. return StateTransformer.UpdateResult.Unchanged;
  247. },
  248. isSerializable: () => ({ isSerializable: false, reason: 'Cannot serialize user imported strings.' })
  249. });
  250. export { ImportJson }
  251. type ImportJson = typeof ImportJson
  252. const ImportJson = PluginStateTransform.BuiltIn({
  253. name: 'import-json',
  254. display: { name: 'Import JSON', description: 'Import given data as a JSON' },
  255. from: SO.Root,
  256. to: SO.Format.Json,
  257. params: {
  258. data: PD.Value<any>({}),
  259. label: PD.Optional(PD.Text('')),
  260. }
  261. })({
  262. apply({ params: { data, label } }) {
  263. return new SO.Format.Json(data, { label: label || '' });
  264. },
  265. update({ oldParams, newParams, b }) {
  266. if (oldParams.data !== newParams.data) return StateTransformer.UpdateResult.Recreate;
  267. if (oldParams.label !== newParams.label) {
  268. b.label = newParams.label || '';
  269. return StateTransformer.UpdateResult.Updated;
  270. }
  271. return StateTransformer.UpdateResult.Unchanged;
  272. },
  273. isSerializable: () => ({ isSerializable: false, reason: 'Cannot serialize user imported JSON.' })
  274. });
  275. export { ParseJson }
  276. type ParseJson = typeof ParseJson
  277. const ParseJson = PluginStateTransform.BuiltIn({
  278. name: 'parse-json',
  279. display: { name: 'Parse JSON', description: 'Parse JSON from String data' },
  280. from: [SO.Data.String],
  281. to: SO.Format.Json
  282. })({
  283. apply({ a }) {
  284. return Task.create('Parse JSON', async ctx => {
  285. const json = await (new Response(a.data)).json(); // async JSON parsing via fetch API
  286. return new SO.Format.Json(json)
  287. });
  288. }
  289. });