Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New design for dg-predictor and pathway-search #39

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { TabViewModule } from "primeng/tabview";
import { ScrollPanelModule } from "primeng/scrollpanel";
import { MultiSelectModule } from "primeng/multiselect";
import { InputNumberModule } from "primeng/inputnumber";
import { InputSwitchModule } from "primeng/inputswitch";
import { SliderModule } from "primeng/slider";

import { LandingPageComponent } from "./components/landing-page/landing-page.component";
Expand Down Expand Up @@ -74,6 +75,7 @@ import { MainLayoutComponent } from './components/novostoic/main-layout/main-lay
import { CenterLayoutComponent } from './components/novostoic/center-layout/center-layout.component';
import { MoleculeImageComponent } from './components/novostoic/molecule-image/molecule-image.component';
import { ChemicalInputComponent } from './components/novostoic/chemical-input/chemical-input.component';
import { MoleculeInfoOverlayComponent } from './components/novostoic/molecule-info-overlay/molecule-info-overlay.component';

const initAppFn = (envService: EnvironmentService) => {
return () => envService.loadEnvConfig("/assets/config/envvars.json");
Expand Down Expand Up @@ -103,6 +105,7 @@ const initAppFn = (envService: EnvironmentService) => {
CenterLayoutComponent,
MoleculeImageComponent,
ChemicalInputComponent,
MoleculeInfoOverlayComponent,
// FileDragNDropDirective,
// ConfigurationComponent,
// ResultsComponent,
Expand Down Expand Up @@ -135,6 +138,7 @@ const initAppFn = (envService: EnvironmentService) => {
TableModule,
TabViewModule,
InputTextModule,
InputSwitchModule,
ListboxModule,
OverlayPanelModule,
SidebarModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ <h6 class="text-text-secondary leading-lg">
></app-loading>

<div class="mt-6" [class.hidden]="isLoading$ | async">
<p-panel header="Results">
<p-panel header="Results" styleClass="w-full">
<p-table
#resultsTable
dataKey="id"
[columns]="columnsForExport"
[value]="(response$ | async) || []"
[scrollable]="true"
scrollHeight="600px"
[paginator]="true"
[rows]="10"
[showCurrentPageReport]="true"
Expand All @@ -59,32 +62,96 @@ <h6 class="text-text-secondary leading-lg">
>
<ng-template pTemplate="header">
<tr>
<th>No.</th>
<th><span class="px-4"></span>Reactions</th>
<th pSortableColumn="gibbsEnergy">
Gibbs Free Energy<p-sortIcon
ΔG<p-sortIcon
field="gibbsEnergy"
></p-sortIcon>
</th>
<th>Actions</th>
<th>Status<p-sortIcon
field="gibbsEnergy"
></p-sortIcon></th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-row>
<ng-template pTemplate="body" let-row let-rowIndex="rowIndex" let-expanded="expanded">
<tr>
<td>{{ rowIndex + 1 }}</td>
<td>
<span class="px-4"></span>{{ row.reaction }}
</td>
<td>{{ row.gibbsEnergy | number:"1.0-2" }} ± {{ row.std | number:"1.1-1" }} kJ/mol</td>
<td>
<div class="flex gap-4 item-center">
<button><i class="pi pi-eye"></i></button>
<button>
<p-chip styleClass="p-1 px-3"
>Enzyme selection
<i
class="ml-2 pi pi-arrow-right"
></i
></p-chip>
</button>
<span class="text-xl font-bold">{{ row.gibbsEnergy > 0 ? '+' : ''}}{{ row.gibbsEnergy | number:"1.0-2" }} </span>
<span class="text-xs">± {{ row.std | number:"1.1-1" }} kJ/mol</span></td>
<td>
<p-chip *ngIf="row.gibbsEnergy < 20" styleClass="bg-blue-50 text-[#326FD1]">
<div class="flex items-center gap-2 py-2">
<i class="pi pi-check-circle"></i>
<div>Feasible</div>
</div>
</p-chip>
<p-chip *ngIf="row.gibbsEnergy > 20" styleClass="bg-yellow-50 text-yellow-600">
<div class="flex items-center gap-2 py-2">
<i class="pi pi-exclamation-triangle"></i>
<div>Infeasible</div>
</div>
</p-chip>
</td>
<td>
<button class="p-2 rounded-full bg-primary w-[26px] h-[26px]" [pRowToggler]="row">
<i class="text-white pi"
[class.pi-chevron-down]="expanded"
[class.pi-chevron-right]="!expanded"
></i>
</button>
</td>
</tr>
</ng-template>
<ng-template pTemplate="rowexpansion" let-row>
<tr>
<td colspan="5" class="relative h-48 overflow-x-scroll no-scrollbar bg-blue-50">
<div class="absolute flex items-stretch justify-around min-w-[96%] gap-2">

<ng-container *ngFor="let reactant of row.reactants; let i = index">
<i *ngIf="i !== 0" class="flex items-center pi pi-plus"></i>
<div class="flex items-center p-2 bg-white border border-solid rounded-md"
[class.border-[--surface-d]]="reactant.is_cofactor"
[class.border-black]="!reactant.is_cofactor"
>
<span *ngIf="reactant.is_cofactor">
{{ reactant.name }}
</span>
<app-molecule-image
*ngIf="!reactant.is_cofactor"
[molecule]="reactant.structure!"
[width]="200"
[height]="120"
>
</app-molecule-image>
</div>
</ng-container>

<i class="flex items-center pi pi-arrow-right"></i>

<ng-container *ngFor="let product of row.products; let i = index">
<div class="flex items-center p-2 bg-white border border-solid rounded-md"
[class.border-[--surface-d]]="product.is_cofactor"
[class.border-black]="!product.is_cofactor"
>
<span *ngIf="product.is_cofactor">
{{ product.name }}
</span>
<app-molecule-image
*ngIf="!product.is_cofactor"
[molecule]="product.structure!"
[width]="200"
[height]="120"
>
</app-molecule-image>
</div>
<i *ngIf="i !== row.products.length - 1" class="flex items-center pi pi-plus"></i>
</ng-container>
</div>
</td>
</tr>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { BehaviorSubject, filter, map, of, skipUntil, switchMap, takeWhile, tap, timer } from "rxjs";
import { JobType, JobStatus } from "~/app/api/mmli-backend/v1";
import { ThermodynamicalFeasibilityResponse } from "~/app/models/dg-predictor";
import { BehaviorSubject, combineLatest, filter, forkJoin, map, of, switchMap, take, tap } from "rxjs";
import { JobType } from "~/app/api/mmli-backend/v1";
import { JobResult } from "~/app/models/job-result";
import { NovostoicService } from "~/app/services/novostoic.service";

Expand All @@ -26,7 +25,19 @@ export class DgPredictorResultComponent extends JobResult {
},
];

response$ = this.jobResultResponse$;
response$ = this.jobResultResponse$.pipe(
map((data) => data.map((d: any, i: number) => ({
...d,
id: i,
reactants: Object.values(d.molecules)
.filter((val: any) => val['amount'] < 0)
.sort((a: any, b: any) => a['is_cofactor'] ? 1 : b['is_cofactor'] ? -1 : 0),
products: Object.values(d.molecules)
.filter((val: any) => val['amount'] > 0)
.sort((a: any, b: any) => a['is_cofactor'] ? -1 : b['is_cofactor'] ? 1 : 0),
}))),
tap(console.log)
);

constructor(
private route: ActivatedRoute,
Expand Down
110 changes: 69 additions & 41 deletions src/app/components/novostoic/dg-predictor/dg-predictor.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,55 +72,83 @@ <h6 class="mt-1 leading-lg">
</div>
<div
*ngIf="subform.controls['type'].value === 'keggId'"
class="flex p-4 border border-dotted rounded-md bg-gray-50"
class="flex flex-col p-5 border border-dotted rounded-md bg-gray-50"
>
<div class="grow">
<div class="mb-2 font-semibold">
Assign a number to the new molecule that
does not have a Kegg ID
</div>
<div class="mb-4 p-inputgroup">
<span class="p-inputgroup-addon">
N
</span>
<input
pInputText
placeholder="000001"
class="max-w-24"
formControlName="moleculeNumber"
/>
</div>

<div class="mb-2 font-semibold">
Enter the new molecule’s SMILES or InChl
</div>
<input
pInputText
placeholder="SMILES or Inchi"
class="w-full mb-4"
formControlName="moleculeInchiOrSmiles"
/>

<div class="mb-2 font-semibold">
Enter Reactions with Kegg ID and/or the
unique number you assign to the new
molecule
</div>
<input
pInputText
placeholder="Kegg ID"
class="w-full"
formControlName="reactionKeggId"
/>
</div>
<div>
<div class="flex justify-between mb-3">
<h5 class="mt-0 opacity-100">Reaction {{ i + 1 }}</h5>
<button
type="button"
(click)="request.removeReaction(i)"
>
<i class="pi pi-times"></i>
</button>
</div>
<section class="lg:max-w-[80%]">
<div class="flex justify-between gap-2 mb-3">
<h6 class="flex items-center gap-2 mt-0 font-semibold opacity-100">
Have new molecule(s) not listed in KEGG database and MetaNetX database
<!-- TODO: add tooltip -->
<i class="pi pi-info-circle text-[--surface-500]"></i>
</h6>
<p-inputSwitch formControlName="containNovelMolecule"/>
</div>
<ng-container *ngIf="subform.controls['containNovelMolecule']!.value">
<div class="flex justify-between gap-2 mb-3">
<h6 class="flex items-center gap-2 mb-2 font-semibold opacity-100">
Enter the new molecule's SMILES or InChl
<!-- TODO: add tooltip -->
<i class="pi pi-info-circle text-[--surface-500]"></i>
</h6>
<button (click)="request.addNovelMolecule(subform)">
<i class="pi pi-plus-circle text-[--text-color-secondary] !text-xl"></i>
</button>
</div>

<ng-container formArrayName="novelMolecules">
<div *ngFor="let novelMoleculeInput of subform.controls['novelMolecules']!.controls; let i = index"
[formGroupName]="i"
class="mb-3 grow">
<div class="flex justify-between gap-2">
<div class="flex gap-3 grow">
<input
pInputText
placeholder="O=C(CCO)O"
class="w-full"
formControlName="value"
/>
<div class="px-[.66rem] py-[.33rem] bg-[--surface-d] text-[--surface-500] my-1 text-base flex items-center justify-center rounded-md"
[class.bg-primary]="novelMoleculeInput.valid"
[class.text-white]="novelMoleculeInput.valid"
>
{{ novelMoleculeInput.controls['alias'].value }}
</div>
</div>
<button
[disabled]="novelMoleculeInput.invalid"
(click)="request.removeNovelMolecule(subform, i)">
<i class="pi pi-times-circle text-[--text-color-secondary] !text-xl"
[class.text-bluegray-100]="novelMoleculeInput.invalid"
></i>
</button>
</div>
<small class="font-semibold text-orange-400" *ngIf="novelMoleculeInput.controls['moleculeExisted'].value">
Note: This new molecule already has a unique number assigned; use that same number for this entry.
</small>
</div>
</ng-container>
</ng-container>
<div class="grow">
<h6 class="mb-2 font-semibold opacity-100">
Enter a reaction with database IDs of known compounds.
</h6>
<input
pInputText
placeholder="e.g. C00001 + C00002 -> C00003 + C00004"
class="w-full"
formControlName="reactionKeggId"
/>
</div>
</section>
</div>
</div>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<app-sidebar [openInNewPage]="false"></app-sidebar>
<div class="grow flex flex-col px-4 items-center">
<div class="flex px-4 grow">
<router-outlet></router-outlet>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Component } from '@angular/core';
templateUrl: './main-layout.component.html',
styleUrls: ['./main-layout.component.scss'],
host: {
class: 'grow flex'
class: 'grow flex w-screen max-w-screen'
}
})
export class MainLayoutComponent {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<p-overlayPanel #overlay>
<div class="w-[300px] p-4">
<div
class="flex justify-center p-2 mb-2 border border-black border-solid rounded-md"
[class]="imageBorderStyle"
>
<app-molecule-image [width]="260" [height]="180" [molecule]="molecule.structure!"></app-molecule-image>
</div>
<div class="flex flex-col">
<ng-container
*ngTemplateOutlet="moleculeInfo; context: {
$implicit: molecule,
showSmiles: showSmiles,
}"
></ng-container>
</div>
</div>
</p-overlayPanel>

<ng-template #moleculeInfo let-molecule let-showSmiles="showSmiles">
<div class="flex justify-between mt-2">
<span class="leading-base opacity-60">Common Name</span>
<span class="font-semibold text-end"
>{{ molecule.name }}</span
>
</div>
<div *ngIf="showSmiles" class="flex justify-between mt-2">
<span class="leading-base opacity-60">SMILES</span>
<span class="font-semibold text-end">{{ molecule.smiles }}</span>
</div>
<div class="flex justify-between mt-2">
<span class="leading-base opacity-60">KEGG ID</span>
<span class="font-semibold text-end">{{ molecule.kegg_id }}</span>
</div>
</ng-template>
Loading
Loading