AssemblyPfvManagerFactory.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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 {ChainDisplay} from "./ChainDisplay";
  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.assemblyModelSate.setMap(config.modelMap);
  55. this.plugin.clearFocus();
  56. const onChangeCallback: Map<string, (x: PolymerEntityInstanceInterface)=>void> = new Map<string, (x: PolymerEntityInstanceInterface) => {}>();
  57. const assemblyInstances: Map<string, Set<string>> = new Map<string, Set<string>>();
  58. this.assemblyModelSate.forEach((v,k)=>{
  59. assemblyInstances.set(v.entryId,new Set<string>(v.chains.map(d=>d.label)));
  60. onChangeCallback.set(v.entryId,(x)=>{
  61. this.assemblyModelSate.set({entryId: v.entryId, labelAsymId: x.asymId, modelId: k});
  62. asyncScheduler.schedule(()=>{
  63. this.selectorManager.setLastSelection('select', null);
  64. this.pfvChangeCallback(undefined);
  65. },100);
  66. });
  67. });
  68. const operatorNameContainer: DataContainer<string> = new DataContainer<string>(config.defaultOperatorName);
  69. if(this.assemblyModelSate.get("entryId") != null) {
  70. this.module = await buildInstanceSequenceFv(
  71. this.rcsbFvDivId,
  72. RcsbFvDOMConstants.SELECT_BUTTON_PFV_ID,
  73. this.assemblyModelSate.getString("entryId"),
  74. {
  75. ...this.instanceSequenceConfig,
  76. defaultValue: config.defaultAuthId,
  77. onChangeCallback: onChangeCallback.get(this.assemblyModelSate.getString("entryId")),
  78. beforeChangeCallback: (x: PolymerEntityInstanceInterface)=>{
  79. this.assemblyModelSate.set({entryId:x.entryId, labelAsymId: x.asymId});
  80. const entryMap:[string, {entryId: string, assemblyId: string, chains: ChainInfo[]}] | undefined = Array.from(this.assemblyModelSate.entries()).find((e)=>(e[1].entryId === x.entryId));
  81. if(!entryMap){
  82. throw `Error: no modelId was found for ${x.entryId}`;
  83. }
  84. const operator: OperatorInfo|undefined = getOperator(this.assemblyModelSate.getMap().get(entryMap[0])!, config.defaultAuthId, operatorNameContainer.get());
  85. this.addOperatorButton(operator?.name);
  86. this.assemblyModelSate.setOperator(x.asymId,operator?.name);
  87. operatorNameContainer.set(undefined);
  88. if(typeof this.additionalConfig?.operatorChangeCallback === "function" && this.assemblyModelSate.getOperator()){
  89. this.additionalConfig.operatorChangeCallback(this.assemblyModelSate.getOperator()!);
  90. }
  91. if((this.assemblyModelSate.getChainInfo()?.operators?.length ?? 0) > 1)
  92. return {
  93. operatorIds: operator?.ids
  94. }
  95. },
  96. filterInstances: assemblyInstances.get(this.assemblyModelSate.getString("entryId")),
  97. selectButtonOptionProps: (props: SelectOptionProps) => (
  98. <div style={{display: 'flex'}}>
  99. <ChainDisplay plugin={this.plugin} label={props.data.label}/>
  100. {props.children}
  101. </div>)
  102. },
  103. {
  104. ...this.additionalConfig,
  105. boardConfig: this.boardConfigContainer.get(),
  106. externalTrackBuilder:{
  107. filterFeatures: this.filterFeatures.bind(this)
  108. }
  109. }
  110. );
  111. }
  112. if(!config.defaultAuthId)
  113. await createComponents<R>(this.plugin, this.assemblyModelSate.getMap());
  114. return this.module;
  115. }
  116. private addOperatorButton(operatorName?: string): void{
  117. const currentChainInfo: ChainInfo|undefined = this.assemblyModelSate.getChainInfo();
  118. if(this.useOperatorsFlag && currentChainInfo && currentChainInfo.operators.length >1 ){
  119. this.assemblyModelSate.setOperator(undefined,operatorName);
  120. RcsbFvUI.addSelectButton(
  121. this.rcsbFvDivId,
  122. RcsbFvDOMConstants.SELECT_BUTTON_PFV_ID,
  123. currentChainInfo.operators.map(op=>({
  124. label:`${op.ids.join("-")} (${op.name})`,
  125. optId:op.name,
  126. onChange: async ()=>{
  127. this.module?.getFv()?.reset();
  128. this.assemblyModelSate.set({operator:op});
  129. await this.create({
  130. modelMap: this.assemblyModelSate.getMap(),
  131. defaultAuthId: this.assemblyModelSate.getChainInfo()?.auth,
  132. defaultOperatorName: op.name
  133. })
  134. }
  135. })),
  136. {
  137. defaultValue: this.assemblyModelSate.getOperator()?.name,
  138. dropdownTitle:this.OPERATOR_DROPDOWN_TITLE
  139. }
  140. );
  141. }
  142. }
  143. private filterFeatures(data: {annotations: Array<AnnotationFeatures>; rcsbContext:Partial<PolymerEntityInstanceInterface>}): Promise<Array<AnnotationFeatures>> {
  144. return new Promise<Array<AnnotationFeatures>>(async resolve => {
  145. let annotations: Array<AnnotationFeatures> = [];
  146. (await Promise.all(data.annotations.map(async ann=>{
  147. if(ann.source == Source.PdbInterface && ann.target_id && data.rcsbContext?.asymId) {
  148. const interfaceToInstance: InterfaceInstanceTranslate = await RcsbRequestContextManager.getInterfaceToInstance(ann.target_id);
  149. if(typeof ann.target_identifiers?.interface_partner_index === "number" && ann.target_identifiers.assembly_id === this.assemblyModelSate.getString("assemblyId")) {
  150. const operatorIds:string[][] = interfaceToInstance.getOperatorIds(ann.target_id)[ann.target_identifiers.interface_partner_index];
  151. if(ann.features && this.assemblyModelSate.getOperator() && operatorIds.map(o=>o.join("|")).includes( this.assemblyModelSate.getOperator()!.ids.join("|") )){
  152. ann.features = ann.features.filter(f=>(f && f.type == FeatureType.BurialFraction));
  153. if(ann.features.length > 0)
  154. return ann;
  155. }
  156. }
  157. }else if(ann.source == Source.PdbInstance && ann.features){
  158. ann.features = ann.features?.filter(f=>(f?.type!==Type.Asa));
  159. return ann;
  160. }else if(ann.source != Source.PdbInterface){
  161. return ann;
  162. }
  163. }))).forEach((value,index,array)=>{
  164. if(value)
  165. annotations = annotations.concat(value);
  166. });
  167. resolve(annotations);
  168. });
  169. }
  170. }
  171. async function createComponents<R>(plugin: ViewerActionManagerInterface<R>, modelMap:SaguaroPluginModelMapType): Promise<void> {
  172. plugin.displayComponent("Water", false);
  173. await plugin.colorComponent("Polymer", 'chain-id');
  174. const chains: Array<{modelId: string; auth: string; label: string;}> = new Array<{modelId: string; auth: string; label: string;}>();
  175. modelMap.forEach((entry, modelId)=>{
  176. entry.chains.forEach(ch=>{
  177. if(ch.type === "polymer") {
  178. chains.push({modelId: modelId, auth: ch.auth, label: ch.label});
  179. }
  180. });
  181. });
  182. await plugin.removeComponent();
  183. plugin.clearFocus();
  184. //TODO improve colorTheme condition (PLDDTConfidenceColorThemeProvider.isApplicable)
  185. const colorTheme: ColorTheme.BuiltIn = (chains.length === 1 && chains[0].modelId.includes("AF_AF")) ? PLDDTConfidenceColorThemeProvider.name as ColorTheme.BuiltIn : "chain-id";
  186. for(const ch of chains) {
  187. const label: string = ch.auth === ch.label ? ch.label : `${ch.label} [auth ${ch.auth}]`;
  188. await plugin.createComponent(label, ch.modelId, ch.label, 'cartoon');
  189. await plugin.colorComponent(label, colorTheme);
  190. }
  191. await plugin.removeComponent("Polymer");
  192. }
  193. function getOperator(entryInfo: {entryId: string; assemblyId: string, chains:Array<ChainInfo>;}, defaultAuthId?: string, defaultOperatorName?:string): OperatorInfo | undefined{
  194. const chainInfo: ChainInfo | undefined = defaultAuthId ? entryInfo.chains.find(ch=>ch.auth === defaultAuthId) : entryInfo.chains[0];
  195. if(chainInfo){
  196. const operatorInfo: OperatorInfo | undefined = defaultOperatorName ? chainInfo.operators.find(op=>op.name === defaultOperatorName) : chainInfo.operators[0];
  197. if(operatorInfo)
  198. return operatorInfo;
  199. }
  200. return undefined;
  201. }