Skip to content

Commit

Permalink
feat(core/reports): add report functions for dynamic form table and p…
Browse files Browse the repository at this point in the history
…aginated lists
  • Loading branch information
sara-gnucoop authored and robzan8 committed Jul 14, 2022
1 parent 027762e commit ddb66fa
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 15 deletions.
149 changes: 148 additions & 1 deletion projects/core/models/src/utils/expression-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import * as dateFns from 'date-fns';
import {parseScript} from 'meriyah';
import * as numbroMod from 'numbro';
import {AjfTableCell} from '@ajf/core/table';

import {AjfValidationFn} from '../interface/validation-function';

let execContext: any = {};
Expand Down Expand Up @@ -132,10 +131,13 @@ export class AjfExpressionUtils {
REPEAT: {fn: REPEAT},
EVALUATE: {fn: EVALUATE},
buildDataset: {fn: buildDataset},
buildFormDataset: {fn: buildFormDataset},
buildWidgetDataset: {fn: buildWidgetDataset},
FILTER_BY: {fn: FILTER_BY},
IS_BEFORE: {fn: IS_BEFORE},
IS_AFTER: {fn: IS_AFTER},
IS_WITHIN_INTERVAL: {fn: IS_WITHIN_INTERVAL},
COMPARE_DATE: {fn: COMPARE_DATE},
APPLY: {fn: APPLY},
TODAY: {fn: TODAY},
GET_AGE: {fn: GET_AGE},
Expand Down Expand Up @@ -1042,6 +1044,12 @@ export function MODE(forms: (Form | MainForm)[], fieldName: string): number[] {
.map(v => +v);
}

/**
* Build a dataset for ajf dynamic table
* @param dataset the dataset for the table
* @param colspans colspan for each value in the dataset
* @returns An AjfTableCell list
*/
export function buildDataset(
dataset: (string | number | string[] | number[])[],
colspans: number[],
Expand Down Expand Up @@ -1077,6 +1085,106 @@ export function buildDataset(
return res;
}

/**
* Build a dataset based on a list of Forms, for ajf dynamic table
* @param dataset the dataset for the table
* @param fields the list of fields name for each row
* @param backgroundColorA the first backgroud color
* @param backgroundColorB the second backgroud color
* @returns An AjfTableCell list
*/
export function buildFormDataset(
dataset: MainForm[],
fields: string[],
backgroundColorA?: string,
backgroundColorB?: string,
): AjfTableCell[][] {
const res: AjfTableCell[][] = [];
if (backgroundColorA == null) {
backgroundColorA = 'white';
}
if (backgroundColorB == null) {
backgroundColorB = '#ddd';
}
if (dataset) {
dataset.forEach((data: MainForm, index: number) => {
if (data) {
const row: AjfTableCell[] = [];
fields.forEach((field: string) => {
row.push({
value: data[field],
colspan: 1,
rowspan: 1,
style: {
textAlign: 'center',
color: 'black',
backgroundColor: index % 2 === 0 ? backgroundColorA : backgroundColorB,
},
});
});
res.push(row);
}
});
}
return res;
}

/**
* create a widget dataset into a content list, based on a list of Forms, for paginated widget
*
* @param dataset the dataset for the widgets
* @param fields the list of fields name for each row
* @param backgroundColor the backgroud color
* @returns An AjfWidget list
*/
export function buildWidgetDataset(
dataset: MainForm[],
fields: string[],
backgroundColor?: string,
): any[] {
const res: {[key: string]: any}[] = [];
if (backgroundColor == null) {
backgroundColor = '#ddd';
}
if (dataset) {
dataset.forEach((data: MainForm, index: number) => {
if (data) {
const row: {[key: string]: any} = {
styles: {
'text-align': 'right',
'border-collapse': 'collapse',
},
visibility: {condition: 'true'},
widgetType: 5,
dataset: [[]] as any[][],
cellStyles: {'border-top': '1px solid grey'},
};

fields.forEach((field: string) => {
row['dataset'][0].push({
label: '',
style: {
textAlign: 'center',
color: 'black',
backgroundColor: index % 2 === 0 ? 'white' : backgroundColor,
},
formula: {
formula: '"' + data[field] + '"',
},
colspan: 1,
rowspan: 1,
aggregation: {
aggregation: 0,
},
});
});
res.push(row);
}
});
}
return res;
}

/**
*
* @param forms the form data
Expand Down Expand Up @@ -1510,6 +1618,45 @@ export function IS_WITHIN_INTERVAL(date: string, dateStart: string, dateEnd: str
return dateFns.isWithinInterval(dateToCompare, interval);
}

/**
* compare a date with two dates interval. Return '-1' (or the first element of labels array) if date
* is before the dateStart, '1' (or the second element) if date is after the dateEnd
* or '0' (or the last element) if date is within inteval.
*
* @export
* @param {string} date
* @param {string} dateStart
* @param {string} dateEnd
* @param {string[]} labels an optional array of string for the output values
* @return {*} {string}
*/
export function COMPARE_DATE(
date: string,
dateStart: string,
dateEnd: string,
labels?: string[],
): string {
let res = '';
const dateToCompare: Date = dateFns.parseISO(date);
const dateA: Date = dateFns.parseISO(dateStart);
const dateB: Date = dateFns.parseISO(dateEnd);
const interval: Interval = {
start: dateA,
end: dateB,
};
if (labels == null) {
labels = ['-1', '1', '0'];
}
if (dateFns.isBefore(dateToCompare, dateA)) {
res = labels[0];
} else if (dateFns.isAfter(dateToCompare, dateB)) {
res = labels[1];
} else if (dateFns.isWithinInterval(dateToCompare, interval)) {
res = labels[2];
}
return res;
}

/**
* this function extend formsA dataset.
* search all match of keyA in formsB, if found if merge formA and formB.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
*
*/

import {AjfWidgetWithContent} from './widget-with-content';
import {AjfWidgetType} from './widget-type';
import {AjfFormula} from '@ajf/core/models';
import {AjfWidgetWithContent} from './widget-with-content';

export interface AjfPaginatedListWidget extends AjfWidgetWithContent {
widgetType: AjfWidgetType.PaginatedList;
title: string;
pageSize: number;
contentDefinition: AjfFormula;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @license
* Copyright (C) Gnucoop soc. coop.
*
* This file is part of the Advanced JSON forms (ajf).
*
* Advanced JSON forms (ajf) is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Advanced JSON forms (ajf) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Advanced JSON forms (ajf).
* If not, see http://www.gnu.org/licenses/.
*
*/

import {AjfPaginatedListWidgetInstance} from '../../interface/widgets-instances/paginated-list-widget-instance';
import {AjfWidgetInstance} from '../../interface/widgets-instances/widget-instance';
import {isPaginatedListWidget} from '../widgets/is-paginated-list-widget';

export const isPaginatedListWidgetInstance = (
instance: AjfWidgetInstance,
): instance is AjfPaginatedListWidgetInstance => {
return instance != null && isPaginatedListWidget(instance.widget);
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
*/

import {AjfColumnWidgetInstance} from '../../interface/widgets-instances/column-widget-instance';
import {AjfDialogWidgetInstance} from '../../interface/widgets-instances/dialog-widget-instance';
import {AjfLayoutWidgetInstance} from '../../interface/widgets-instances/layout-widget-instance';
import {AjfWidgetInstance} from '../../interface/widgets-instances/widget-instance';
import {isWidgetWithContent} from '../widgets/is-widget-with-content';

export const isWidgetWithContentInstance = (
instance: AjfWidgetInstance,
): instance is AjfColumnWidgetInstance | AjfLayoutWidgetInstance => {
): instance is AjfColumnWidgetInstance | AjfLayoutWidgetInstance | AjfDialogWidgetInstance => {
return instance != null && isWidgetWithContent(instance.widget);
};
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {isMapWidget} from '../widgets/is-map-widget';
import {isWidgetWithContent} from '../widgets/is-widget-with-content';
import {isTableWidget} from '../widgets/is-table-widget';
import {isTextWidget} from '../widgets/is-text-widget';
import {isPaginatedListWidget} from '../widgets/is-paginated-list-widget';
import {componentsMap} from '../widgets/widgets-map';
import {isChartWidgetInstance} from '../widgets-instances/is-chart-widget-instance';
import {isDialogWidgetInstance} from '../widgets-instances/is-dialog-widget-instance';
Expand All @@ -56,6 +57,7 @@ import {isMapWidgetInstance} from '../widgets-instances/is-map-widget-instance';
import {isTableWidgetInstance} from '../widgets-instances/is-table-widget-instance';
import {isTextWidgetInstance} from '../widgets-instances/is-text-widget-instance';
import {isWidgetWithContentInstance} from '../widgets-instances/is-widget-with-content-instance';
import {isPaginatedListWidgetInstance} from '../widgets-instances/is-paginated-list-widget-instance';

import {createWidgetInstance} from './create-widget-instance';
import {evaluateProperty, trFormula} from './widget-instance-utils';
Expand Down Expand Up @@ -84,6 +86,9 @@ export function widgetToWidgetInstance(
}
wi.content = content;
});
if (isDialogWidget(widget) && isDialogWidgetInstance(wi)) {
wi.toggle = widgetToWidgetInstance(widget.toggle, context, ts, variables);
}
} else if (isChartWidget(widget) && isChartWidgetInstance(wi)) {
if (widget.options == null) {
widget.options = {};
Expand Down Expand Up @@ -226,6 +231,14 @@ export function widgetToWidgetInstance(
};
});
wi.data = header.length === 0 ? [...dataset] : [[...header], ...dataset];
} else if (isPaginatedListWidget(widget) && isPaginatedListWidgetInstance(wi)) {
let contentDefinition: AjfWidget[] =
evaluateExpression(widget.contentDefinition.formula, context) || [];
let content: AjfWidgetInstance[] = [];
contentDefinition.forEach(c => {
content.push(widgetToWidgetInstance(c, context, ts, variables));
});
wi.content = content;
} else if (isImageWidget(widget) && isImageWidgetInstance(wi)) {
if (widget.flag) {
wi.flag = evaluateExpression(widget.flag.formula, context);
Expand Down Expand Up @@ -275,8 +288,6 @@ export function widgetToWidgetInstance(
return node as AjfGraphNode;
});
}
} else if (isDialogWidget(widget) && isDialogWidgetInstance(wi)) {
wi.toggle = widgetToWidgetInstance(widget.toggle, context, ts, variables);
} else if (isHeatMapWidget(widget) && isHeatMapWidgetInstance(wi)) {
wi.idProp = widget.idProp || 'id';
wi.features = (typeof widget.features === 'string'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @license
* Copyright (C) Gnucoop soc. coop.
*
* This file is part of the Advanced JSON forms (ajf).
*
* Advanced JSON forms (ajf) is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Advanced JSON forms (ajf) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Advanced JSON forms (ajf).
* If not, see http://www.gnu.org/licenses/.
*
*/

import {AjfPaginatedListWidget} from '../../interface/widgets/paginated-list-widget';
import {AjfWidget} from '../../interface/widgets/widget';
import {AjfWidgetType} from '../../interface/widgets/widget-type';

export const isPaginatedListWidget = (widget: AjfWidget): widget is AjfPaginatedListWidget => {
return widget != null && widget.widgetType === AjfWidgetType.PaginatedList;
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@
*/

import {AjfColumnWidget} from '../../interface/widgets/column-widget';
import {AjfDialogWidget} from '../../interface/widgets/dialog-widget';
import {AjfLayoutWidget} from '../../interface/widgets/layout-widget';
import {AjfWidget} from '../../interface/widgets/widget';
import {AjfWidgetType} from '../../interface/widgets/widget-type';

export const isWidgetWithContent = (
widget: AjfWidget,
): widget is AjfColumnWidget | AjfLayoutWidget => {
): widget is AjfColumnWidget | AjfLayoutWidget | AjfDialogWidget => {
return (
widget != null &&
(widget.widgetType === AjfWidgetType.Column || widget.widgetType === AjfWidgetType.Layout)
(widget.widgetType === AjfWidgetType.Column ||
widget.widgetType === AjfWidgetType.Layout ||
widget.widgetType === AjfWidgetType.Dialog)
);
};

0 comments on commit ddb66fa

Please sign in to comment.