@datagrok/sequence-translator 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.2.3",
4
+ "version": "1.2.5",
5
5
  "author": {
6
6
  "name": "Alexey Choposky",
7
7
  "email": "achopovsky@datagrok.ai"
package/src/package.ts CHANGED
@@ -51,6 +51,7 @@ async function buildLayout(appName: string): Promise<void> {
51
51
 
52
52
  //name: Oligo Toolkit
53
53
  //meta.icon: img/icons/toolkit.png
54
+ //meta.browsePath: Oligo
54
55
  //tags: app
55
56
  export async function oligoToolkitApp(): Promise<void> {
56
57
  await initSequenceTranslatorLibData();
@@ -63,6 +64,7 @@ export async function oligoToolkitApp(): Promise<void> {
63
64
 
64
65
  //name: Oligo Translator
65
66
  //meta.icon: img/icons/translator.png
67
+ //meta.browsePath: Oligo
66
68
  //tags: app
67
69
  export async function oligoTranslatorApp(): Promise<void> {
68
70
  await buildLayout(APP.TRANSLATOR);
@@ -70,6 +72,7 @@ export async function oligoTranslatorApp(): Promise<void> {
70
72
 
71
73
  //name: Oligo Pattern
72
74
  //meta.icon: img/icons/pattern.png
75
+ //meta.browsePath: Oligo
73
76
  //tags: app
74
77
  export async function oligoPatternApp(): Promise<void> {
75
78
  await buildLayout(APP.PATTERN);
@@ -77,6 +80,7 @@ export async function oligoPatternApp(): Promise<void> {
77
80
 
78
81
  //name: Oligo Structure
79
82
  //meta.icon: img/icons/structure.png
83
+ //meta.browsePath: Oligo
80
84
  //tags: app
81
85
  export async function oligoStructureApp(): Promise<void> {
82
86
  await buildLayout(APP.STRUCTRE);
@@ -5,7 +5,7 @@ import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import {axolabsStyleMap} from '../../model/data-loading-utils/json-loader';
7
7
  import {
8
- DEFAULT_PTO, DEFAULT_SEQUENCE_LENGTH, MAX_SEQUENCE_LENGTH, USER_STORAGE_KEY, EXAMPLE_MIN_WIDTH, SS, AS, STRAND_NAME, STRANDS, TERMINAL, TERMINAL_KEYS, THREE_PRIME, FIVE_PRIME, JSON_FIELD as FIELD
8
+ DEFAULT_PTO, DEFAULT_SEQUENCE_LENGTH, MAX_SEQUENCE_LENGTH, USER_STORAGE_KEY, SS, AS, STRAND_NAME, STRANDS, TERMINAL, TERMINAL_KEYS, THREE_PRIME, FIVE_PRIME, JSON_FIELD as FIELD
9
9
  } from '../../model/pattern-app/const';
10
10
  import {isOverhang} from '../../model/pattern-app/helpers';
11
11
  import {generateExample, translateSequence, getShortName, isCurrentUserCreatedThisPattern, findDuplicates, addColumnWithIds, addColumnWithTranslatedSequences} from '../../model//pattern-app/oligo-pattern';
@@ -107,7 +107,7 @@ export class PatternLayoutHandler {
107
107
 
108
108
  function updateInputExamples() {
109
109
  STRANDS.forEach((s) => {
110
- if (inputStrandColumn[s].value === '')
110
+ if (strandColumnInput[s].value === '')
111
111
  inputExample[s].value = generateExample(strandLengthInput[s].value!, sequenceBase.value!);
112
112
  });
113
113
  }
@@ -211,7 +211,7 @@ export class PatternLayoutHandler {
211
211
  }
212
212
 
213
213
  function allColumnValuesOfEqualLength(colName: string): boolean {
214
- const col = tables.value!.getCol(colName);
214
+ const col = tableInput.value!.getCol(colName);
215
215
  let allLengthsAreTheSame = true;
216
216
  for (let i = 1; i < col.length; i++) {
217
217
  if (col.get(i - 1).length !== col.get(i).length && col.get(i).length !== 0) {
@@ -226,14 +226,14 @@ export class PatternLayoutHandler {
226
226
  .add(ui.divText('The sequence length should match the number of Raw sequences in the input file'))
227
227
  .add(ui.divText('\'ADD COLUMN\' to see sequences lengths'))
228
228
  .addButton('ADD COLUMN', () => {
229
- tables.value!.columns.addNewInt('Sequences lengths in ' + colName).init((j: number) => col.get(j).length);
230
- grok.shell.info('Column with lengths added to \'' + tables.value!.name + '\'');
229
+ tableInput.value!.columns.addNewInt('Sequences lengths in ' + colName).init((j: number) => col.get(j).length);
230
+ grok.shell.info('Column with lengths added to \'' + tableInput.value!.name + '\'');
231
231
  dialog.close();
232
- grok.shell.v = grok.shell.getTableView(tables.value!.name);
232
+ grok.shell.v = grok.shell.getTableView(tableInput.value!.name);
233
233
  })
234
234
  .show();
235
235
  }
236
- if (col.get(0) !== strandLengthInput[SS].value) {
236
+ if (col.get(0).length !== strandLengthInput[SS].value) {
237
237
  const d = ui.dialog('Length was updated by value to from imported file');
238
238
  d.add(ui.divText('Latest modifications may not take effect during translation'))
239
239
  .onOK(() => grok.shell.info('Lengths changed')).show();
@@ -367,25 +367,26 @@ export class PatternLayoutHandler {
367
367
 
368
368
  function validateStrandColumn(colName: string, strand: string): void {
369
369
  const allLengthsAreTheSame: boolean = allColumnValuesOfEqualLength(colName);
370
- const firstSequence = tables.value!.getCol(colName).get(0);
370
+ const firstSequence = tableInput.value!.getCol(colName).get(0);
371
371
  if (allLengthsAreTheSame && firstSequence.length !== strandLengthInput[strand].value)
372
- strandLengthInput[strand].value = tables.value!.getCol(colName).get(0).length;
372
+ strandLengthInput[strand].value = tableInput.value!.getCol(colName).get(0).length;
373
373
  inputExample[strand].value = firstSequence;
374
374
  }
375
375
 
376
376
  function validateIdsColumn(colName: string) {
377
- const col = tables.value!.getCol(colName);
377
+ const col = tableInput.value!.getCol(colName);
378
378
  if (col.type !== DG.TYPE.INT)
379
379
  grok.shell.error('Column should contain integers only');
380
+ //@ts-ignore
380
381
  else if (col.categories.filter((e) => e !== '').length < col.toList().filter((e) => e !== '').length) {
381
382
  const duplicates = findDuplicates(col.getRawData());
382
383
  ui.dialog('Non-unique IDs')
383
384
  .add(ui.divText('Press \'OK\' to select rows with non-unique values'))
384
385
  .onOK(() => {
385
- const selection = tables.value!.selection;
386
+ const selection = tableInput.value!.selection;
386
387
  selection.init((i: number) => duplicates.indexOf(col.get(i)) > -1);
387
- grok.shell.v = grok.shell.getTableView(tables.value!.name);
388
- grok.shell.info('Rows are selected in table \'' + tables.value!.name + '\'');
388
+ grok.shell.v = grok.shell.getTableView(tableInput.value!.name);
389
+ grok.shell.info('Rows are selected in table \'' + tableInput.value!.name + '\'');
389
390
  })
390
391
  .show();
391
392
  }
@@ -435,23 +436,16 @@ export class PatternLayoutHandler {
435
436
  return [strand, input];
436
437
  }));
437
438
  const strandVar = Object.fromEntries(STRANDS.map((strand) => [strand, '']));
438
- // todo: rename to strandColumnInputDiv
439
- const inputStrandColumnDiv = Object.fromEntries(STRANDS.map(
440
- (strand) => [strand, ui.div([])]
441
- ));
442
439
  const inputExample = Object.fromEntries(STRANDS.map(
443
440
  (strand) => [strand, ui.textInput(
444
441
  ``, generateExample(strandLengthInput[strand].value!, sequenceBase.value!))
445
442
  ]));
446
443
 
447
- // todo: rename to strandColumnInput
448
- const inputStrandColumn = Object.fromEntries(STRANDS.map((strand) => {
449
- const input: StringInput = ui.choiceInput(`${STRAND_NAME[strand]} column`, '', [], (colName: string) => {
444
+ const strandColumnInput = Object.fromEntries(STRANDS.map((strand) => {
445
+ const input = ui.choiceInput(`${STRAND_NAME[strand]} column`, '', [], (colName: string) => {
450
446
  validateStrandColumn(colName, strand);
451
447
  strandVar[strand] = colName;
452
448
  });
453
- inputStrandColumnDiv[strand].append(input.root);
454
- //input.addPostfix(``);
455
449
  return [strand, input];
456
450
  }));
457
451
 
@@ -464,8 +458,6 @@ export class PatternLayoutHandler {
464
458
  input.captionLabel.style.maxWidth = '100px';
465
459
  input.captionLabel.style.minWidth = '40px';
466
460
  input.captionLabel.style.width = 'auto';
467
-
468
-
469
461
 
470
462
  return [strand, input];
471
463
  }));
@@ -508,9 +500,7 @@ export class PatternLayoutHandler {
508
500
  STRANDS.forEach((s) => {
509
501
 
510
502
  inputExample[s].input.style.resize = 'none';
511
- //inputExample[s].input.style.minWidth = EXAMPLE_MIN_WIDTH;
512
503
  outputExample[s].input.style.resize = 'none';
513
- //outputExample[s].input.styl
514
504
  inputExample[s].input.style.minWidth = 'none';
515
505
  inputExample[s].input.style.flexGrow = '1';
516
506
  outputExample[s].input.style.minWidth = 'none';
@@ -527,10 +517,9 @@ export class PatternLayoutHandler {
527
517
  );
528
518
  })
529
519
 
530
- const inputIdColumnDiv = ui.div([]);
520
+ // const inputIdColumnDiv = ui.div([]);
531
521
  const svgDiv = ui.div([]);
532
522
  const asExampleDiv = ui.div([], 'ui-form ui-form-wide');
533
- const appAxolabsDescription = ui.div([]);
534
523
  const loadPatternDiv = ui.div([]);
535
524
  const asModificationDiv = ui.form([]);
536
525
  const isEnumerateModificationsDiv = ui.divH([
@@ -550,25 +539,45 @@ export class PatternLayoutHandler {
550
539
 
551
540
  const asLengthDiv = ui.div([strandLengthInput[AS].root]);
552
541
 
553
- const tables = ui.tableInput('Tables', grok.shell.tables[0], grok.shell.tables, (t: DG.DataFrame) => {
554
- STRANDS.forEach((strand) => {
555
- inputStrandColumn[strand] = ui.choiceInput(`${strand} column`, '', t.columns.names(), (colName: string) => {
556
- validateStrandColumn(colName, strand);
557
- strandVar[strand] = colName;
558
- });
559
- inputStrandColumnDiv[strand].innerHTML = '';
560
- inputStrandColumnDiv[strand].append(inputStrandColumn[strand].root);
561
- })
542
+ function getTableInput(tableList: DG.DataFrame[]): DG.InputBase {
543
+ const tableInput = ui.tableInput('Tables', tableList[0], tableList, () => {
544
+ const table = tableInput.value;
545
+ if (table === null) {
546
+ console.warn('Table is null');
547
+ return;
548
+ }
549
+ const tableName = table!.name;
550
+ if (!grok.shell.tableNames.includes(tableName)) {
551
+ const view = grok.shell.v;
552
+ grok.shell.addTableView(table!);
553
+ grok.shell.v = view;
554
+ }
555
+ const columnNames = table.columns.names();
556
+
557
+ STRANDS.forEach((strand) => {
558
+ const defaultColumn = columnNames[0];
559
+ validateStrandColumn(defaultColumn, strand);
560
+ strandVar[strand] = defaultColumn;
561
+ const input = ui.choiceInput(`${STRAND_NAME[strand]} column`, defaultColumn, columnNames, (colName: string) => {
562
+ validateStrandColumn(colName, strand);
563
+ strandVar[strand] = colName;
564
+ console.log(`clicked ${strand} var:`, strandVar[strand]);
565
+ });
566
+ $(strandColumnInput[strand].root).replaceWith(input.root);
567
+ })
562
568
 
563
- // todo: unify with inputStrandColumn
564
- const inputIdColumn = ui.choiceInput('ID column', '', t.columns.names(), (colName: string) => {
565
- validateIdsColumn(colName);
566
- idVar = colName;
569
+ idVar = columnNames[0];
570
+ // todo: unify with inputStrandColumn
571
+ const idInput = ui.choiceInput('ID column', columnNames[0], columnNames, (colName: string) => {
572
+ validateIdsColumn(colName);
573
+ idVar = colName;
574
+ });
575
+ $(inputIdColumn.root).replaceWith(idInput.root);
567
576
  });
568
- inputIdColumnDiv.innerHTML = '';
569
- inputIdColumnDiv.append(inputIdColumn.root);
570
- });
577
+ return tableInput;
578
+ }
571
579
 
580
+ const tableInput = getTableInput([]);
572
581
 
573
582
  // todo: unify with strandVar
574
583
  let idVar = '';
@@ -576,13 +585,13 @@ export class PatternLayoutHandler {
576
585
  validateIdsColumn(colName);
577
586
  idVar = colName;
578
587
  });
579
- inputIdColumnDiv.append(inputIdColumn.root);
588
+ // inputIdColumnDiv.append(inputIdColumn.root);
580
589
 
581
590
  updatePatternsList();
582
591
 
583
592
  const createAsStrand = ui.boolInput('Anti sense strand', true, (v: boolean) => {
584
593
  modificationSection[AS].hidden = !v;
585
- inputStrandColumnDiv[AS].hidden = !v;
594
+ strandColumnInput[AS].root.hidden = !v;
586
595
  asLengthDiv.hidden = !v;
587
596
  asModificationDiv.hidden = !v;
588
597
  asExampleDiv.hidden = !v;
@@ -619,6 +628,7 @@ export class PatternLayoutHandler {
619
628
 
620
629
  const convertSequenceButton = ui.bigButton('Convert', () => {
621
630
  const condition = [true, createAsStrand.value];
631
+ console.log(`strand vars:`, Object.values(strandVar));
622
632
  if (STRANDS.some((s, i) => condition[i] && strandVar[s] === ''))
623
633
  grok.shell.info('Please select table and columns on which to apply pattern');
624
634
  else if (STRANDS.some((s) => strandLengthInput[s].value !== inputExample[s].value.length)) {
@@ -628,24 +638,24 @@ export class PatternLayoutHandler {
628
638
  .add(ui.divText('Length of sequences in columns doesn\'t match entered length. Update length value?'))
629
639
  .addButton('YES', () => {
630
640
  STRANDS.forEach((s) => {
631
- strandLengthInput[s].value = tables.value!.getCol(inputStrandColumn[s].value!).getString(0).length;
641
+ strandLengthInput[s].value = tableInput.value!.getCol(strandColumnInput[s].value!).getString(0).length;
632
642
  })
633
643
  dialog.close();
634
644
  })
635
645
  .show();
636
646
  } else {
637
647
  if (idVar !== '')
638
- addColumnWithIds(tables.value!.name, idVar, getShortName(saveAs.value));
648
+ addColumnWithIds(tableInput.value!.name, idVar, getShortName(saveAs.value));
639
649
  const condition = [true, createAsStrand.value];
640
650
  STRANDS.forEach((strand, i) => {
641
651
  if (condition[i])
642
652
  addColumnWithTranslatedSequences(
643
- tables.value!.name, strandVar[strand], baseInputsObject[strand], ptoLinkages[strand],
653
+ tableInput.value!.name, strandVar[strand], baseInputsObject[strand], ptoLinkages[strand],
644
654
  terminalModification[strand][FIVE_PRIME], terminalModification[strand][THREE_PRIME], firstPto[strand].value!);
645
655
  })
646
- grok.shell.v = grok.shell.getTableView(tables.value!.name);
656
+ grok.shell.v = grok.shell.getTableView(tableInput.value!.name);
647
657
  grok.shell.info(((createAsStrand.value) ? 'Columns were' : 'Column was') +
648
- ' added to table \'' + tables.value!.name + '\'');
658
+ ' added to table \'' + tableInput.value!.name + '\'');
649
659
  updateOutputExamples();
650
660
  }
651
661
  });
@@ -664,10 +674,10 @@ export class PatternLayoutHandler {
664
674
 
665
675
  const inputsSection = ui.block50([
666
676
  ui.h1('Convert options'),
667
- tables.root,
668
- inputStrandColumnDiv[SS],
669
- inputStrandColumnDiv[AS],
670
- inputIdColumnDiv,
677
+ tableInput.root,
678
+ strandColumnInput[SS].root,
679
+ strandColumnInput[AS].root,
680
+ inputIdColumn.root,
671
681
  ui.buttonsInput([
672
682
  convertSequenceButton,
673
683
  ]),
@@ -678,7 +688,7 @@ export class PatternLayoutHandler {
678
688
  {backgroundColor: 'white'}), 'Download pattern as PNG image', '');
679
689
 
680
690
  const editPattern = ui.link('Edit pattern', ()=>{
681
- ui.dialog('Edit patter')
691
+ ui.dialog('Edit pattern')
682
692
  .add(ui.divV([
683
693
  ui.h1('PTO'),
684
694
  ui.divH([
@@ -695,56 +705,6 @@ export class PatternLayoutHandler {
695
705
  .show()
696
706
  }, 'Edit pattern', '');
697
707
 
698
- const mainSection = ui.panel([
699
- ui.block([
700
- svgDiv,
701
- ], {style: {overflowX: 'scroll'}}),
702
- downloadButton,
703
- isEnumerateModificationsDiv,
704
- ui.div([
705
- ui.div([
706
- ui.divH([
707
- ui.h1('Pattern options'),
708
- ]),
709
- ui.divH([
710
- ui.block([
711
- strandLengthInput[SS].root,
712
- asLengthDiv,
713
- sequenceBase.root,
714
- comment.root,
715
- loadPatternDiv,
716
- saveAs.root,
717
- ui.buttonsInput([
718
- //savePatternButton,
719
- ]),
720
- ], 'ui-form'),
721
- ui.block([
722
- createAsStrand.root,
723
- fullyPto.root,
724
- firstPto[SS].root,
725
- firstPto[AS].root,
726
- terminalModification[SS][FIVE_PRIME].root,
727
- terminalModification[SS][THREE_PRIME].root,
728
- asModificationDiv,
729
- ], 'ui-form'),
730
- ]),
731
- ], 'ui-form'),
732
- inputsSection,
733
- exampleSection,
734
- ], {style: {flexWrap: 'wrap'}}),
735
- ]);
736
-
737
- const info = ui.info(
738
- [
739
- ui.divText('\n How to define new pattern:', {style: {'font-weight': 'bolder'}}),
740
- ui.divText('1. Choose table and columns with sense and antisense strands'),
741
- ui.divText('2. Choose lengths of both strands by editing checkboxes below'),
742
- ui.divText('3. Choose basis and PTO status for each nucleotide'),
743
- ui.divText('4. Set additional modifications for sequence edges'),
744
- ui.divText('5. Press \'Convert Sequences\' button'),
745
- ui.divText('This will add the result column(s) to the right of the table'),
746
- ], 'Create and apply Axolabs translation patterns.',
747
- );
748
708
  strandLengthInput[SS].addCaption('Length');
749
709
 
750
710
  return ui.splitH([
@@ -759,9 +719,9 @@ export class PatternLayoutHandler {
759
719
  loadPatternDiv,
760
720
  saveAs.root,
761
721
  ui.h1('Convert'),
762
- tables.root,
763
- inputStrandColumn[SS],
764
- inputStrandColumn[AS],
722
+ tableInput.root,
723
+ strandColumnInput[SS],
724
+ strandColumnInput[AS],
765
725
  inputIdColumn.root,
766
726
  ui.buttonsInput([
767
727
  convertSequenceButton,
@@ -126,9 +126,10 @@ export class StructureLayoutHandler {
126
126
  const clearBlock = Object.fromEntries(
127
127
  STRANDS.map(
128
128
  (key) => {
129
- const icon = ui.iconFA('eraser', () => {coloredInput[key].inputBase.value = ''});
130
- ui.tooltip.bind(icon, `Clear ${key.toUpperCase()}`);
131
- return [key, icon];
129
+ const clearIcon = ui.icons.delete(() => { coloredInput[key].inputBase.value = '' });
130
+ const clearButton = ui.button(clearIcon, () => {});
131
+ ui.tooltip.bind(clearButton, `Clear ${key.toUpperCase()}`);
132
+ return [key, clearIcon];
132
133
  }
133
134
  ));
134
135
 
@@ -5,7 +5,6 @@ import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import * as rxjs from 'rxjs';
7
7
  import '../style/translator-app.css';
8
- import $ from 'cash-dom';
9
8
 
10
9
  import {highlightInvalidSubsequence} from '../utils/colored-input/input-painters';
11
10
  import {ColoredTextInput} from '../utils/colored-input/colored-text-input';
@@ -74,12 +73,19 @@ export class TranslatorLayoutHandler {
74
73
 
75
74
  const formatChoiceInput = ui.div([this.formatChoiceInput]);
76
75
 
76
+ const clearButton = ui.button(
77
+ ui.icons.delete(() => { sequenceColoredInput.inputBase.value = '' }),
78
+ () => {}
79
+ );
80
+ ui.tooltip.bind(clearButton, 'Clear input');
81
+
77
82
  const inputTableRow = {
78
83
  format: formatChoiceInput,
79
84
  textInput: sequenceColoredInput.root,
85
+ clearBtn: clearButton
80
86
  };
81
87
  const upperBlock = ui.table(
82
- [inputTableRow], (item) => [item.format, item.textInput]
88
+ [inputTableRow], (item) => [item.format, item.textInput, item.clearBtn]
83
89
  );
84
90
  upperBlock.classList.add('st-translator-input-table');
85
91
 
@@ -1,11 +1,11 @@
1
1
  /* Naming convention: class names should begin with st and app name to avoid naming collitions */
2
2
  .st-translator-input-table {
3
- width: 100%;
3
+ /* width: 100%; */
4
+ margin-right: 20px;
4
5
  }
5
6
 
6
7
  .st-translator-input-table td:has(textarea) {
7
8
  width: 100%;
8
- padding-right: 20px;
9
9
  }
10
10
 
11
11
  .st-translator-input-table td:has(select) {