AssemblyPfvManagerFactory.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import {
  2. RcsbFvModulePublicInterface
  3. } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
  4. import {
  5. ChainInfo, OperatorInfo,
  6. SaguaroPluginModelMapType, ViewerActionManagerInterface
  7. } from "../../../../RcsbFvStructure/StructureViewerInterface";
  8. import {
  9. InstanceSequenceConfig
  10. } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
  11. import {asyncScheduler} from "rxjs";
  12. import {buildInstanceSequenceFv, FeatureType, RcsbFvUI, RcsbRequestContextManager} from "@rcsb/rcsb-saguaro-app";
  13. import {RcsbFvDOMConstants} from "../../../../RcsbFvConstants/RcsbFvConstants";
  14. import {SelectOptionProps} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvComponents/SelectButton";
  15. import {ChainDisplayComponent} from "./AssemblyPfvManagerFactory/ChainDisplayComponent";
  16. import * as React from "react";
  17. import {AnnotationFeatures, Source, Type} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
  18. import {
  19. PolymerEntityInstanceInterface
  20. } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/DataCollectors/PolymerEntityInstancesCollector";
  21. import {
  22. InterfaceInstanceTranslate
  23. } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbUtils/Translators/InterfaceInstanceTranslate";
  24. import {DataContainer} from "../../../../Utils/DataContainer";
  25. import {
  26. BuildPfvInterface,
  27. AbstractPfvManager,
  28. PfvManagerFactoryConfigInterface,
  29. PfvManagerInterface,
  30. PfvManagerFactoryInterface
  31. } from "../PfvManagerFactoryInterface";
  32. import {ColorTheme} from "molstar/lib/mol-theme/color";
  33. import {PLDDTConfidenceColorThemeProvider} from "molstar/lib/extensions/model-archive/quality-assessment/color/plddt";
  34. interface AssemblyPfvManagerInterface<R> extends PfvManagerFactoryConfigInterface<R,undefined>{
  35. useOperatorsFlag: boolean | undefined;
  36. instanceSequenceConfig: InstanceSequenceConfig | undefined;
  37. }
  38. export class AssemblyPfvManagerFactory<R> implements PfvManagerFactoryInterface<{instanceSequenceConfig: InstanceSequenceConfig|undefined;useOperatorsFlag: boolean | undefined;},R,undefined> {
  39. public getPfvManager(config: AssemblyPfvManagerInterface<R>): PfvManagerInterface {
  40. return new AssemblyPfvManager<R>(config);
  41. }
  42. }
  43. class AssemblyPfvManager<R> extends AbstractPfvManager<{instanceSequenceConfig: InstanceSequenceConfig|undefined;useOperatorsFlag: boolean | undefined;},R,undefined> {
  44. private readonly instanceSequenceConfig: InstanceSequenceConfig|undefined;
  45. private readonly useOperatorsFlag:boolean | undefined;
  46. private readonly OPERATOR_DROPDOWN_TITLE: string = "Symmetry Partner";
  47. private module: RcsbFvModulePublicInterface | undefined = undefined;
  48. constructor(config: AssemblyPfvManagerInterface<R>) {
  49. super(config);
  50. this.instanceSequenceConfig = config.instanceSequenceConfig;
  51. this.useOperatorsFlag = config.useOperatorsFlag;
  52. }
  53. public async create(config: BuildPfvInterface): Promise<RcsbFvModulePublicInterface | undefined> {
  54. this.structureViewer.clearFocus();
  55. const onChangeCallback: Map<string, (x: PolymerEntityInstanceInterface)=>void> = new Map<string, (x: PolymerEntityInstanceInterface) => {}>();
  56. const assemblyInstances: Map<string, Set<string>> = new Map<string, Set<string>>();
  57. this.stateManager.assemblyModelSate.forEach((v,k)=>{
  58. assemblyInstances.set(v.entryId,new Set<string>(v.chains.map(d=>d.label)));
  59. onChangeCallback.set(v.entryId,(x)=>{
  60. this.stateManager.assemblyModelSate.set({entryId: v.entryId, labelAsymId: x.asymId, modelId: k});
  61. asyncScheduler.schedule(()=>{
  62. this.pfvChangeCallback(undefined);
  63. },100);
  64. });
  65. });
  66. const operatorNameContainer: DataContainer<string> = new DataContainer<string>(config.defaultOperatorName);
  67. if(this.stateManager.assemblyModelSate.get("entryId") != null) {
  68. this.module = await buildInstanceSequenceFv(
  69. this.rcsbFvDivId,
  70. RcsbFvDOMConstants.SELECT_BUTTON_PFV_ID,
  71. this.stateManager.assemblyModelSate.getString("entryId"),
  72. {
  73. ...this.instanceSequenceConfig,
  74. defaultValue: config.defaultAuthId,
  75. onChangeCallback: onChangeCallback.get(this.stateManager.assemblyModelSate.getString("entryId")),
  76. beforeChangeCallback: (x: PolymerEntityInstanceInterface)=>{
  77. this.stateManager.assemblyModelSate.set({entryId:x.entryId, labelAsymId: x.asymId});
  78. const entryMap:[string, {entryId: string, assemblyId: string, chains: ChainInfo[]}] | undefined = Array.from(this.stateManager.assemblyModelSate.entries()).find((e)=>(e[1].entryId === x.entryId));
  79. if(!entryMap){
  80. throw `Error: no modelId was found for ${x.entryId}`;
  81. }
  82. const operator: OperatorInfo|undefined = getOperator(this.stateManager.assemblyModelSate.getMap().get(entryMap[0])!, config.defaultAuthId, operatorNameContainer.get());
  83. this.addOperatorButton(operator?.name);
  84. this.stateManager.assemblyModelSate.setOperator(x.asymId,operator?.name);
  85. operatorNameContainer.set(undefined);
  86. if(typeof this.additionalConfig?.operatorChangeCallback === "function" && this.stateManager.assemblyModelSate.getOperator()){
  87. this.additionalConfig.operatorChangeCallback(this.stateManager.assemblyModelSate.getOperator()!);
  88. }
  89. if((this.stateManager.assemblyModelSate.getChainInfo()?.operators?.length ?? 0) > 1)
  90. return {
  91. operatorIds: operator?.ids
  92. }
  93. },
  94. filterInstances: assemblyInstances.get(this.stateManager.assemblyModelSate.getString("entryId")),
  95. selectButtonOptionProps: (props: SelectOptionProps) => (
  96. <div style={{display: 'flex'}}>
  97. <ChainDisplayComponent structureViewer={this.structureViewer} label={props.data.label}/>
  98. {props.children}
  99. </div>)
  100. },
  101. {
  102. ...this.additionalConfig,
  103. boardConfig: this.boardConfigContainer.get(),
  104. externalTrackBuilder:{
  105. filterFeatures: this.filterFeatures.bind(this)
  106. }
  107. }
  108. );
  109. }
  110. if(!config.defaultAuthId)
  111. await createComponents<R>(this.structureViewer, this.stateManager.assemblyModelSate.getMap());
  112. return this.module;
  113. }
  114. private addOperatorButton(operatorName?: string): void{
  115. const currentChainInfo: ChainInfo|undefined = this.stateManager.assemblyModelSate.getChainInfo();
  116. if(this.useOperatorsFlag && currentChainInfo && currentChainInfo.operators.length >1 ){
  117. this.stateManager.assemblyModelSate.setOperator(undefined,operatorName);
  118. RcsbFvUI.addSelectButton(
  119. this.rcsbFvDivId,
  120. RcsbFvDOMConstants.SELECT_BUTTON_PFV_ID,
  121. currentChainInfo.operators.map(op=>({
  122. label:`${op.ids.join("-")} (${op.name})`,
  123. optId:op.name,
  124. onChange: async ()=>{
  125. this.module?.getFv()?.reset();
  126. this.stateManager.assemblyModelSate.set({operator:op});
  127. await this.create({
  128. defaultAuthId: this.stateManager.assemblyModelSate.getChainInfo()?.auth,
  129. defaultOperatorName: op.name
  130. })
  131. }
  132. })),
  133. {
  134. defaultValue: this.stateManager.assemblyModelSate.getOperator()?.name,
  135. dropdownTitle:this.OPERATOR_DROPDOWN_TITLE
  136. }
  137. );
  138. }else{
  139. RcsbFvUI.clearSelectButton(this.rcsbFvDivId,RcsbFvDOMConstants.SELECT_BUTTON_PFV_ID);
  140. }
  141. }
  142. private filterFeatures(data: {annotations: Array<AnnotationFeatures>; rcsbContext:Partial<PolymerEntityInstanceInterface>}): Promise<Array<AnnotationFeatures>> {
  143. return new Promise<Array<AnnotationFeatures>>(async resolve => {
  144. let annotations: Array<AnnotationFeatures> = [];
  145. (await Promise.all(data.annotations.map(async ann=>{
  146. if(ann.source == Source.PdbInterface && ann.target_id && data.rcsbContext?.asymId) {
  147. const interfaceToInstance: InterfaceInstanceTranslate = await RcsbRequestContextManager.getInterfaceToInstance(ann.target_id);
  148. if(typeof ann.target_identifiers?.interface_partner_index === "number" && ann.target_identifiers.assembly_id === this.stateManager.assemblyModelSate.getString("assemblyId") && Array.isArray(interfaceToInstance.getOperatorIds(ann.target_id))) {
  149. const operatorIds:string[][] = interfaceToInstance.getOperatorIds(ann.target_id)[ann.target_identifiers.interface_partner_index];
  150. if(ann.features && this.stateManager.assemblyModelSate.getOperator() && operatorIds.map(o=>o.join("|")).includes( this.stateManager.assemblyModelSate.getOperator()!.ids.join("|") )){
  151. ann.features = ann.features.filter(f=>(f && f.type == FeatureType.BurialFraction));
  152. if(ann.features.length > 0)
  153. return ann;
  154. }
  155. }
  156. }else if(ann.source == Source.PdbInstance && ann.features){
  157. ann.features = ann.features?.filter(f=>(f?.type!==Type.Asa));
  158. return ann;
  159. }else if(ann.source != Source.PdbInterface){
  160. return ann;
  161. }
  162. }))).forEach((value,index,array)=>{
  163. if(value)
  164. annotations = annotations.concat(value);
  165. });
  166. resolve(annotations);
  167. });
  168. }
  169. }
  170. async function createComponents<R>(plugin: ViewerActionManagerInterface<R>, modelMap:SaguaroPluginModelMapType): Promise<void> {
  171. plugin.displayComponent("Water", false);
  172. await plugin.colorComponent("Polymer", 'chain-id');
  173. const chains: Array<{modelId: string; auth: string; label: string;}> = new Array<{modelId: string; auth: string; label: string;}>();
  174. modelMap.forEach((entry, modelId)=>{
  175. entry.chains.forEach(ch=>{
  176. if(ch.type === "polymer") {
  177. chains.push({modelId: modelId, auth: ch.auth, label: ch.label});
  178. }
  179. });
  180. });
  181. await plugin.removeComponent();
  182. plugin.clearFocus();
  183. //TODO improve colorTheme condition (PLDDTConfidenceColorThemeProvider.isApplicable)
  184. const colorTheme: ColorTheme.BuiltIn = (chains.length === 1 && chains[0].modelId.includes("AF_AF")) ? PLDDTConfidenceColorThemeProvider.name as ColorTheme.BuiltIn : "chain-id";
  185. for(const ch of chains) {
  186. const label: string = ch.auth === ch.label ? ch.label : `${ch.label} [auth ${ch.auth}]`;
  187. await plugin.createComponent(label, ch.modelId, ch.label, 'cartoon');
  188. await plugin.colorComponent(label, colorTheme);
  189. }
  190. await plugin.removeComponent("Polymer");
  191. }
  192. function getOperator(entryInfo: {entryId: string; assemblyId: string, chains:Array<ChainInfo>;}, defaultAuthId?: string, defaultOperatorName?:string): OperatorInfo | undefined{
  193. const chainInfo: ChainInfo | undefined = defaultAuthId ? entryInfo.chains.find(ch=>ch.auth === defaultAuthId) : entryInfo.chains[0];
  194. if(chainInfo){
  195. const operatorInfo: OperatorInfo | undefined = defaultOperatorName ? chainInfo.operators.find(op=>op.name === defaultOperatorName) : chainInfo.operators[0];
  196. if(operatorInfo)
  197. return operatorInfo;
  198. }
  199. return undefined;
  200. }