validation.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /**
  2. * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
  5. */
  6. import { CollapsableControls, CollapsableState } from 'molstar/lib/mol-plugin-ui/base';
  7. import { StructureHierarchyManager } from 'molstar/lib/mol-plugin-state/manager/structure/hierarchy';
  8. import { ValidationReport } from 'molstar/lib/extensions/rcsb/validation-report/prop';
  9. import { ValidationReportGeometryQualityPreset } from 'molstar/lib/extensions/rcsb/validation-report/behavior';
  10. import { ActionMenu } from 'molstar/lib/mol-plugin-ui/controls/action-menu';
  11. import { Model } from 'molstar/lib/mol-model/structure/model';
  12. import { MmcifFormat } from 'molstar/lib/mol-model-formats/structure/mmcif';
  13. import { AlphaFoldConfidence } from '../helpers/af-confidence/prop';
  14. import { AlphaFoldConfidenceColorThemeProvider } from '../helpers/af-confidence/color';
  15. interface ValidationReportState extends CollapsableState {
  16. errorStates: Set<string>
  17. }
  18. const ValidationReportTag = 'validation-report';
  19. const _QualityIcon = <svg width='50px' height='50px' viewBox='0 0 38 47'>
  20. <g strokeWidth='4' fill='none'>
  21. <path d='m19 4.8c-3.7 3.6-9 5.8-15 5.8v4.3c0 25 14 29 14 29s16-4.5 16-29v-4.3c-6 0-11-2.3-15-5.8z' stroke='#000' strokeLinecap='square' strokeMiterlimit='10'/>
  22. <path d='m13 23 3.5 3.5 9.4-9.4' stroke='#000'/>
  23. </g>
  24. </svg>;
  25. export function QualityIconSvg() { return _QualityIcon; }
  26. /**
  27. * A high-level component that gives access to the validation report preset.
  28. */
  29. export class ValidationReportControls extends CollapsableControls<{}, ValidationReportState> {
  30. protected defaultState() {
  31. return {
  32. header: 'Quality Assessment',
  33. isCollapsed: false,
  34. isHidden: true,
  35. errorStates: new Set<string>(),
  36. brand: { accent: 'cyan' as const, svg: QualityIconSvg }
  37. };
  38. }
  39. componentDidMount() {
  40. this.subscribe(this.plugin.managers.structure.hierarchy.behaviors.selection, () => {
  41. const { errorStates, description } = this.state;
  42. const nextDescription = StructureHierarchyManager.getSelectedStructuresDescription(this.plugin);
  43. this.setState({
  44. isHidden: !this.canEnable(),
  45. // if structure is unchanged then keep old error states
  46. errorStates: nextDescription === description ? errorStates : new Set<string>(),
  47. description: nextDescription
  48. });
  49. });
  50. }
  51. get pivot() {
  52. return this.plugin.managers.structure.hierarchy.selection.structures[0];
  53. }
  54. canEnable() {
  55. const { selection } = this.plugin.managers.structure.hierarchy;
  56. if (selection.structures.length !== 1) return false;
  57. const pivot = this.pivot.cell;
  58. if (!pivot.obj || pivot.obj.data.models.length !== 1) return false;
  59. const model = pivot.obj.data.models[0];
  60. // all supported options must be registered here
  61. return ValidationReport.isApplicable(model) || AlphaFoldConfidence.isApplicable(model);
  62. }
  63. get noValidationReport() {
  64. const structure = this.pivot.cell.obj?.data;
  65. if (!structure || structure.models.length !== 1) return true;
  66. const model = structure.models[0];
  67. return !model || !this.isFromPdbArchive(model);
  68. }
  69. get alphaFoldData() {
  70. const structure = this.pivot.cell.obj?.data;
  71. if (!structure || structure.models.length !== 1) return false;
  72. const model = structure.models[0];
  73. return AlphaFoldConfidence.isApplicable(model);
  74. }
  75. isFromPdbArchive(model: Model) {
  76. if (!MmcifFormat.is(model.sourceData)) return false;
  77. return model.entryId.match(/^[1-9][a-z0-9]{3}$/i) !== null ||
  78. model.entryId.match(/^pdb_[0-9]{4}[1-9][a-z0-9]{3}$/i) !== null;
  79. }
  80. requestValidationReportPreset = async () => {
  81. try {
  82. await ValidationReportGeometryQualityPreset.apply(this.pivot.cell, Object.create(null), this.plugin);
  83. } catch (err) {
  84. // happens e.g. for 2W4S
  85. this.setState(({ errorStates }) => {
  86. const errors = new Set(errorStates);
  87. errors.add(ValidationReportTag);
  88. return { errorStates: errors };
  89. });
  90. }
  91. };
  92. requestAlphaFoldConfidenceColoring = async () => {
  93. await this.plugin.managers.structure.component.updateRepresentationsTheme(this.pivot.components, { color: AlphaFoldConfidenceColorThemeProvider.name as any });
  94. };
  95. get actions(): ActionMenu.Items {
  96. const noValidationReport = this.noValidationReport;
  97. const validationReportError = this.state.errorStates.has(ValidationReportTag);
  98. const out: ActionMenu.Items = [
  99. {
  100. kind: 'item',
  101. label: validationReportError ? 'Failed to Obtain Validation Report' : (noValidationReport ? 'No Validation Report Available' : 'RCSB PDB Validation Report'),
  102. value: this.requestValidationReportPreset,
  103. disabled: noValidationReport || validationReportError
  104. },
  105. ];
  106. if (this.alphaFoldData) {
  107. out.push({
  108. kind: 'item',
  109. label: 'AlphaFold Confidence Scores',
  110. value: this.requestAlphaFoldConfidenceColoring
  111. });
  112. }
  113. return out;
  114. }
  115. selectAction: ActionMenu.OnSelect = item => {
  116. if (!item) return;
  117. (item?.value as any)();
  118. };
  119. renderControls() {
  120. return <ActionMenu items={this.actions} onSelect={this.selectAction} />;
  121. }
  122. }