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

Widget Dashboard Queries using extracted Search Parameters #179

Merged
merged 59 commits into from
Jul 8, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
c16f32d
adding frontend storybook generator.
AnalogJ May 15, 2023
4c45f25
adding tests to increase backend coverage.
AnalogJ May 15, 2023
aa391fb
delete unnecessary docker directory.
AnalogJ May 15, 2023
4f3b2e1
skip broken JWT tests.
AnalogJ May 16, 2023
b5f5650
Merge pull request #149 from fastenhealth/test_coverage
AnalogJ May 16, 2023
0a0e263
Binary
AnalogJ May 17, 2023
eccbb4a
fixing medication component.
AnalogJ May 17, 2023
12acd90
adding test for medicationrequest.
AnalogJ May 17, 2023
015d0c5
update involved in care to display practitioner cards.
AnalogJ May 17, 2023
2681df8
adding dashboard widget query to retrive data from database.
AnalogJ May 17, 2023
2d6cd8f
introducing a query language (FQL-like) for creating queries for use …
AnalogJ May 19, 2023
0cce2f9
working querying for widget data.
AnalogJ May 19, 2023
e53d5bc
updating gridstack version. Better Widget UI/UX.
AnalogJ May 19, 2023
e1ba9d5
using gridstack native Component constructor to populate grid dynamic…
AnalogJ May 20, 2023
b8da83c
update chartjs and ng2-charts.
AnalogJ May 22, 2023
991732b
dynamically extract values from objects instead of from array of numb…
AnalogJ May 22, 2023
f53cde4
re-enable dynamic values in simple line chart.
AnalogJ May 22, 2023
e08dfa1
fixed ERROR Error: NG0301 by increasing error budget.
AnalogJ May 22, 2023
4a4eb2b
patient vitals panel is dynamic.
AnalogJ May 24, 2023
17797e3
handle dataset customization.
AnalogJ May 25, 2023
f860e4b
fixing query aggregations.
AnalogJ May 25, 2023
ffeffe9
working on better labels.
AnalogJ May 26, 2023
a92282c
updates to dashboard widgets
AnalogJ May 27, 2023
caba4af
adding loading and empty states for dashboard widgets.
AnalogJ Jun 1, 2023
7fdfa2b
fixing tests.
AnalogJ Jun 1, 2023
30e3b8b
fixing tests.
AnalogJ Jun 1, 2023
7eb894c
Merge pull request #157 from fastenhealth/widget_query_support
AnalogJ Jun 1, 2023
fa1596c
adding ability to dynamically generate DB Models.
AnalogJ Jun 14, 2023
e804ad2
Generated models for each FHIR Resource that we're tracking.
AnalogJ Jun 17, 2023
e06a041
make sure database fields in models are sorted (otherwise the generat…
AnalogJ Jun 18, 2023
318f42d
make sure database fields in models are sorted (otherwise the generat…
AnalogJ Jun 18, 2023
0116ee9
make sure every usage of field is sorted.
AnalogJ Jun 18, 2023
eae98ee
working query resources functionality.
AnalogJ Jun 18, 2023
9ac34ed
adding TableName overrides for FHIR Resources (so they are not plural…
AnalogJ Jun 18, 2023
948faa8
working Token clause.
AnalogJ Jun 18, 2023
e0d3f6c
make sure all conditions are in braces, so we can join them with OR o…
AnalogJ Jun 18, 2023
9101798
GoJa does not correctly support dynamic data types (type casting) in …
AnalogJ Jun 18, 2023
b428170
preprocess `token` search parameters (since theres so many different …
AnalogJ Jun 18, 2023
71016e1
simplified queries now that we've preprocessed the token columns.
AnalogJ Jun 18, 2023
e6ec557
added tests for FROM clause (json_each processing)
AnalogJ Jun 18, 2023
60e1ecd
token parameter has been simplified, we can now support multiple toke…
AnalogJ Jun 19, 2023
25014fb
make sure we can support AND and OR clauses for the same searchparame…
AnalogJ Jun 19, 2023
c4fe677
create a temporary model for use when queryin data from the database.
AnalogJ Jun 19, 2023
000bf1b
header fixes.
AnalogJ Jun 19, 2023
9e5c5f2
adding a GetTableNameByResourceType method to consistently lookup the…
AnalogJ Jun 20, 2023
abb1615
fixing ListResources endpoint (now supports listing all resources acr…
AnalogJ Jun 20, 2023
d8ba730
making sure that queryResources is cancellable.
AnalogJ Jun 20, 2023
6494f4e
migrating to ResourceBase model as base for all FHIR Resources.
AnalogJ Jun 21, 2023
6cebe00
remove PreloadRelated functionality from ListResource (doesnt work wi…
AnalogJ Jun 21, 2023
70c10c1
adding test files for composition and manual condition create.
AnalogJ Jun 21, 2023
d5f3405
adding storybook builder to CI.
AnalogJ Jun 22, 2023
9195cf3
fixing working directory.
AnalogJ Jun 22, 2023
70415d5
fixing build script name.
AnalogJ Jun 22, 2023
cc4b61e
adding ability to specify custom dashboards.
AnalogJ Jun 23, 2023
4a7ac67
fixing dashboard
AnalogJ Jun 23, 2023
dde82d6
trying to fix tests.
AnalogJ Jun 24, 2023
1d348d9
trying to fix storybook tests.
AnalogJ Jul 8, 2023
48db249
trying to fix storybook tests.
AnalogJ Jul 8, 2023
ac15ec7
Merge pull request #177 from fastenhealth/dynamic_db_models
AnalogJ Jul 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
using gridstack native Component constructor to populate grid dynamic…
…allly (from JSON spec)

change all widgets to depend on dashboard-widget.
  • Loading branch information
AnalogJ committed May 20, 2023
commit e1ba9d54ce21b9bd2699726d808bcee585d70bbb
36 changes: 34 additions & 2 deletions frontend/src/app/components/gridstack/gridstack.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ import { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode, GridSta

import { GridItemCompHTMLElement, GridstackItemComponent } from './gridstack-item.component';
import {CommonModule} from '@angular/common';
import {WidgetsModule} from '../../widgets/widgets.module';
import {ComplexLineWidgetComponent} from '../../widgets/complex-line-widget/complex-line-widget.component';
import {DashboardWidgetComponent} from '../../widgets/dashboard-widget/dashboard-widget.component';
import {DonutChartWidgetComponent} from '../../widgets/donut-chart-widget/donut-chart-widget.component';
import {DualGaugesWidgetComponent} from '../../widgets/dual-gauges-widget/dual-gauges-widget.component';
import {GroupedBarChartWidgetComponent} from '../../widgets/grouped-bar-chart-widget/grouped-bar-chart-widget.component';
import {PatientVitalsWidgetComponent} from '../../widgets/patient-vitals-widget/patient-vitals-widget.component';
import {SimpleLineChartWidgetComponent} from '../../widgets/simple-line-chart-widget/simple-line-chart-widget.component';
import {TableWidgetComponent} from '../../widgets/table-widget/table-widget.component';
import {DashboardWidgetComponentInterface} from '../../widgets/dashboard-widget-component-interface';

/** events handlers emitters signature for different events */
export type eventCB = {event: Event};
Expand All @@ -21,6 +31,7 @@ export type droppedCB = {event: Event, previousNode: GridStackNode, newNode: Gri
/** extends to store Ng Component selector, instead/inAddition to content */
export interface NgGridStackWidget extends GridStackWidget {
type?: string; // component type to create as content
widgetConfig?: any;
}
export interface NgGridStackNode extends GridStackNode {
type?: string; // component type to create as content
Expand All @@ -43,7 +54,7 @@ export type SelectorToType = {[key: string]: Type<Object>};
*/
@Component({
standalone: true,
imports: [CommonModule, GridstackItemComponent],
imports: [CommonModule, GridstackItemComponent, WidgetsModule],
selector: 'gridstack',
template: `
<!-- content to show when when grid is empty, like instructions on how to add widgets -->
Expand Down Expand Up @@ -124,6 +135,22 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
// private readonly cd: ChangeDetectorRef,
private readonly elementRef: ElementRef<GridCompHTMLElement>,
) {

// register all our dynamic components created in the grid
GridstackComponent.addComponentToSelectorType([
ComplexLineWidgetComponent,
DashboardWidgetComponent,
DonutChartWidgetComponent,
DualGaugesWidgetComponent,
GroupedBarChartWidgetComponent,
PatientVitalsWidgetComponent,
SimpleLineChartWidgetComponent,
TableWidgetComponent,
]);
// set globally our method to create the right widget type
GridStack.addRemoveCB = gsCreateNgComponents;
GridStack.saveCB = gsSaveAdditionalNgInfo;

this.el._gridComp = this;
}

Expand Down Expand Up @@ -226,7 +253,12 @@ export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, w:
const selector = (w as NgGridStackWidget).type;
const type = selector ? GridstackComponent.selectorToType[selector] : undefined;
if (!w.subGridOpts && type) {
gridItem?.container?.createComponent(type);
let componentRef = gridItem?.container?.createComponent<DashboardWidgetComponentInterface>(type as Type<DashboardWidgetComponentInterface>);
let widgetConfig = (w as NgGridStackWidget)?.widgetConfig
if(widgetConfig){
componentRef.instance.widgetConfig = widgetConfig
}
// componentRef.instance.markForCheck()
}

return gridItem?.el;
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/app/models/widget/dashboard-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {DashboardWidgetConfig} from './dashboard-widget-config';

export class DashboardConfig {
id: string
schema_version: string //increment this number when the config schema changes, not controlled by user.
title: string
description?: string

widgets: DashboardWidgetConfig[]
}
8 changes: 3 additions & 5 deletions frontend/src/app/models/widget/dashboard-widget-config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {DashboardWidgetQuery} from './dashboard-widget-query';

export class DashboardWidgetConfig {
schema_version: number //increment this number when the config schema changes, not controlled by user.
id: string
item_type: "bar_chart" | "bubble_chart" | "doughnut_chart" | "line_chart" | "pie_chart" | "scatter_chart" | "calendar" | "striped_table" | "basic_table"

item_type: "complex-line-widget" | "donut-chart-widget" | "dual-gauges-widget" | "grouped-bar-chart-widget" | "patient-vitals-widget" | "simple-line-chart-widget" | "table-widget"

title_text: string
description_text: string
Expand All @@ -21,8 +19,8 @@ export class DashboardWidgetConfig {


//used for display purposes within the Dashboard, not for the actual chart
minWidth?: number
minHeight?: number
// minWidth?: number
// minHeight?: number
width: number
height: number
x?: number
Expand Down
47 changes: 1 addition & 46 deletions frontend/src/app/pages/dashboard/dashboard.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,52 +55,7 @@ <h6>All Sources</h6>
</div>
</div>

<gridstack id="gridstack" #gridComp [options]="gridOptions">
<gridstack-item class="grid-stack-item" gs-w="8" gs-h="5" gs-x="0" gs-y="0">
<app-complex-line-widget></app-complex-line-widget>
</gridstack-item>


<gridstack-item class="grid-stack-item" gs-w="2" gs-h="2" gs-x="8" gs-y="0">
<app-simple-line-chart-widget></app-simple-line-chart-widget>
</gridstack-item>

<gridstack-item class="grid-stack-item" gs-w="2" gs-h="2" gs-x="10" gs-y="0">
<app-simple-line-chart-widget [widgetConfig]="heightWidgetConfig"></app-simple-line-chart-widget>
</gridstack-item>

<gridstack-item class="grid-stack-item" gs-w="4" gs-h="3" gs-x="8" gs-y="2">
<app-grouped-bar-chart-widget></app-grouped-bar-chart-widget>
</gridstack-item>

<gridstack-item class="grid-stack-item" gs-w="4" gs-h="5" gs-x="0" gs-y="5">
<app-patient-vitals-widget></app-patient-vitals-widget>
</gridstack-item>

<gridstack-item class="grid-stack-item" gs-w="8" gs-h="5" gs-x="4" gs-y="5">
<app-donut-chart-widget></app-donut-chart-widget>
</gridstack-item>

<gridstack-item class="grid-stack-item" gs-w="4" gs-h="2" gs-x="0" gs-y="10">
<app-dual-gauges-widget></app-dual-gauges-widget>
</gridstack-item>

<gridstack-item class="grid-stack-item" gs-w="8" gs-h="4" gs-x="4" gs-y="10">
<app-table-widget></app-table-widget>
</gridstack-item>


<gridstack-item *ngFor="let item of dashboardItems" class="grid-stack-item"
[attr.gs-w]="item.width"
[attr.gs-h]="item.height"
[attr.gs-x]="item.x"
[attr.gs-y]="item.y"
[attr.gs-min-w]="item.minWidth"
[attr.gs-min-h]="item.minHeight"
>
<app-dashboard-widget [widgetConfig]="item"></app-dashboard-widget>
</gridstack-item>
</gridstack>
<gridstack id="gridstack" #gridComp [options]="gridOptions"></gridstack>


</div><!-- az-content-body -->
Expand Down
99 changes: 29 additions & 70 deletions frontend/src/app/pages/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {FastenApiService} from '../../services/fasten-api.service';
import {Summary} from '../../models/fasten/summary';
import {LighthouseService} from '../../services/lighthouse.service';
import { GridStack, GridStackOptions, GridStackWidget } from 'gridstack';
import { GridstackComponent } from '../../components/gridstack/gridstack.component';
import {GridstackComponent, NgGridStackOptions} from '../../components/gridstack/gridstack.component';
import {DashboardWidgetComponent} from '../../widgets/dashboard-widget/dashboard-widget.component';
import {DashboardWidgetConfig} from '../../models/widget/dashboard-widget-config';
import exampleDashboardConfig from "./example_dashboard.json";
import {DashboardConfig} from '../../models/widget/dashboard-config';


// unique ids sets for each item for correct ngFor updating
Expand All @@ -32,29 +34,6 @@ export class DashboardComponent implements OnInit {

metadataSource: { [name: string]: MetadataSource }

heightWidgetConfig: DashboardWidgetConfig = {
description_text: '',
height: 0,
item_type: undefined,
queries: [
//https://healthedata1.github.io/IG-Sampler/Observation-example.html
{
q: {
// use?: string
select: ["valueQuantity.value as data"],
from: "Observation",
where: [
"(code.coding.where(system = 'https://loinc.org' and code = '29463-7') | code.coding.where(system = 'https://loinc.org' and code = '3141-9')).exists()"
]
}
}
],
schema_version: 0,
title_text: 'Custom Height',
width: 0,
id: "1"
}

@ViewChild(GridstackComponent) gridComp?: GridstackComponent;

constructor(
Expand Down Expand Up @@ -102,6 +81,17 @@ export class DashboardComponent implements OnInit {
});


(exampleDashboardConfig as DashboardConfig).widgets.forEach((widgetConfig) => {
this.gridOptions.children.push({
x: widgetConfig.x,
y: widgetConfig.y,
w: widgetConfig.width,
h: widgetConfig.height,
type: widgetConfig.item_type,
widgetConfig: widgetConfig.item_type == "simple-line-chart-widget" ? widgetConfig : undefined,
})
})

}

selectSource(selectedSource: Source){
Expand Down Expand Up @@ -131,7 +121,8 @@ export class DashboardComponent implements OnInit {

public gridEditDisabled = true;

public gridOptions: GridStackOptions = {

public gridOptions: NgGridStackOptions = {
margin: 5,
float: false,
minRow: 1,
Expand All @@ -141,11 +132,7 @@ export class DashboardComponent implements OnInit {
//these 2 options can be used to enable/disable editability
// disableDrag: true,
// disableResize: true
// children: [
// // {x: 0, y: 0, minW: 2},
// // {x: 1, y: 1},
// // {x: 2, y: 2},
// ],
children: [],
}

public toggleEditableGrid() {
Expand All @@ -156,51 +143,23 @@ export class DashboardComponent implements OnInit {

}

public dashboardItems: DashboardWidgetConfig[] = []
/**
* TEST dynamic grid operations - uses grid API directly (since we don't track structure that gets out of sync)
*/
public add(gridComp: GridstackComponent) {
// TODO: BUG the content doesn't appear until widget is moved around (or another created). Need to force
// angular detection changes...
// gridComp.grid?.addWidget({x:3, y:0, w:2, content:`item ${ids}`, id:String(ids++)});

this.dashboardItems.push({x:3, y:0, width:4, height:3, id:String(ids++)} as DashboardWidgetConfig)

// this.makeWidget(gridComp);
}
public delete(gridComp: GridstackComponent) {
gridComp.grid?.removeWidget(gridComp.grid.engine.nodes[0]?.el!);
}
public modify(gridComp: GridstackComponent) {
gridComp.grid?.update(gridComp.grid.engine.nodes[0]?.el!, {w:3})
}
public newLayout(gridComp: GridstackComponent) {
this.dashboardItems = [
{x:0, y:0, id:'1', width:4, height: 4, item_type: "calendar"}, // new size/constrain
{x:4, y:0, id:'2', width:4, height: 4, item_type: "basic_table"},
{x:8, y:0, id:'3', width:3, height: 3, item_type: "line_chart"}, // delete item
{x:3, y:5, w:2, width:4, height:3}, // new item
] as DashboardWidgetConfig[];
}


// public add(gridComp: GridstackComponent) {
// // TODO: BUG the content doesn't appear until widget is moved around (or another created). Need to force
// // angular detection changes...
// // gridComp.grid?.addWidget({x:3, y:0, w:2, content:`item ${ids}`, id:String(ids++)});
//
// getRootNodeFromParsedComponent(component: any) {
// const componentFactory =
// this.componentFactoryResolver.resolveComponentFactory(component);
// let ref = this.vcRef?.createComponent(componentFactory);
// this.dashboardItems.push({x:3, y:0, width:4, height:3, id:String(ids++)} as DashboardWidgetConfig)
//
// const hostView = <EmbeddedViewRef<any>>ref?.hostView;
// return hostView.rootNodes[0];
// // this.makeWidget(gridComp);
// }
//
// makeWidget(gridComp: GridstackComponent) {
// console.log('called makeWidget');
//
// gridComp.grid?.el.appendChild(
// this.getRootNodeFromParsedComponent(DashboardItemComponent)
// );
// gridComp.grid?.makeWidget('#widget1');
// public delete(gridComp: GridstackComponent) {
// gridComp.grid?.removeWidget(gridComp.grid.engine.nodes[0]?.el!);
// }
// public modify(gridComp: GridstackComponent) {
// gridComp.grid?.update(gridComp.grid.engine.nodes[0]?.el!, {w:3})
// }

}
Loading