diff --git a/e2e/calculate-all-params.e2e-spec.ts b/e2e/calculate-all-params.e2e-spec.ts index e71105a5496733ae74e92a9956249268417cb4a5..49d6cc12c0ed94f86a31e88f7effe3e278f513e1 100644 --- a/e2e/calculate-all-params.e2e-spec.ts +++ b/e2e/calculate-all-params.e2e-spec.ts @@ -1,9 +1,5 @@ -import { AppPage } from "./app.po"; import { ListPage } from "./list.po"; import { CalculatorPage } from "./calculator.po"; -import { Navbar } from "./navbar.po"; -import { SideNav } from "./sidenav.po"; -import { browser } from "protractor"; /** * For all calculators, try to calculate every parameter: check that only one parameter @@ -32,11 +28,9 @@ describe("ngHyd − calculate all parameters of all calculators", () => { // get all parameters IDs const inputs = await calcPage.getParamInputsHavingCalcMode(); - // console.log("> Inputs having calc", inputs.length); if (inputs.length > 0) { // for each param for (const input of inputs) { - // console.log(">> Trying", await input.getAttribute("id")); // click "calc" mode button for this parameter await calcPage.setParamMode(input, "cal"); // check that only 1 button is in "calc" state @@ -45,7 +39,7 @@ describe("ngHyd − calculate all parameters of all calculators", () => { // check that "compute" button is active const calcButton = calcPage.getCalculateButton(); const disabledState = await calcButton.getAttribute("disabled"); - expect(disabledState).not.toBe("disabled"); + expect(disabledState).not.toBe("true"); // click "compute" button await calcButton.click(); // check that result is not empty diff --git a/e2e/calculate-linked-params.e2e-spec.ts b/e2e/calculate-linked-params.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..179b55f54a5877f9ffd1c0f99ba62cc6a44b1d9c --- /dev/null +++ b/e2e/calculate-linked-params.e2e-spec.ts @@ -0,0 +1,490 @@ +import { AppPage } from "./app.po"; +import { ListPage } from "./list.po"; +import { CalculatorPage } from "./calculator.po"; +import { Navbar } from "./navbar.po"; +import { SideNav } from "./sidenav.po"; +import { browser } from "protractor"; + +/** + * Uses an example configuration to calculate : + * - with a parameter linked to a single parameter + * - with a parameter linked to a single parameter, plus local variated parameter + * - with a parameter linked to a variated parameter + * - with a parameter linked to a single result + * - with a parameter linked to a single result, plus local variated parameter + * - with a parameter linked to a variated result + * - with a parameter linked to a single extra result + * - with a parameter linked to a single extra result, plus local variated parameter + * - with a parameter linked to a variated extra result + * + * => plus all those combinations in indeirect link mode (linked parameter target + * is also a linked parameter) + */ +describe("ngHyd − calculate with linked parameters", () => { + let listPage: ListPage; + let calcPage: CalculatorPage; + let navBar: Navbar; + let startPage: AppPage; + + beforeEach(() => { + listPage = new ListPage(); + calcPage = new CalculatorPage(); + navBar = new Navbar(); + startPage = new AppPage(); + + }); + + async function computeAndCheckPresenceOfResults() { + // check that "compute" button is active + const calcButton = calcPage.getCalculateButton(); + const disabledState = await calcButton.getAttribute("disabled"); + expect(disabledState).not.toBe("true"); + // click "compute" button + await calcButton.click(); + // check that result is not empty + const hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(true); + } + + it(" − direct links : parameter linked to a single parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (R uniforme) + const Y = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y, "link"); + const sel = await calcPage.getLinkedValueSelect(Y); + await calcPage.changeSelectValue(sel, 1); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a single parameter, plus local variated parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (R uniforme) + const Y = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y, "link"); + const sel = await calcPage.getLinkedValueSelect(Y); + await calcPage.changeSelectValue(sel, 1); + // vary W + const W = calcPage.getInputById("W"); + await calcPage.setParamMode(W, "var"); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a variated parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // vary Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "var"); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (R uniforme) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 1); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a single result", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // calculate Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "cal"); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (R uniforme) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 1); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a single result, plus local variated parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // calculate Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "cal"); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (R uniforme) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 1); + // vary W + const W = calcPage.getInputById("W"); + await calcPage.setParamMode(W, "var"); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a variated result", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // vary Q + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "var"); + // calculate Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "cal"); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (R uniforme) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 1); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a single extra result", async () => { + // create a Déversoirs dénoyés + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(9); + + // create a Régime uniforme + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(3); + // link Q to CvQT (Déversoirs dénoyés) + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "link"); + const sel = await calcPage.getLinkedValueSelect(Q); + await calcPage.changeSelectValue(sel, 1); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a single extra result, plus local variated parameter", async () => { + // create a Déversoirs dénoyés + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(9); + + // create a Régime uniforme + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(3); + // link Q to CvQT (Déversoirs dénoyés) + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "link"); + const sel = await calcPage.getLinkedValueSelect(Q); + await calcPage.changeSelectValue(sel, 1); + // vary YB + const YB = calcPage.getInputById("YB"); + await calcPage.setParamMode(YB, "var"); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − direct links : parameter linked to a variated extra result", async () => { + // create a Déversoirs dénoyés + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(9); + // vary BR + const BR = calcPage.getInputById("BR"); + await calcPage.setParamMode(BR, "var"); + + // create a Régime uniforme + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(3); + // link Q to CvQT (Déversoirs dénoyés) + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "link"); + const sel = await calcPage.getLinkedValueSelect(Q); + await calcPage.changeSelectValue(sel, 1); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a single parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Y to Y (R uniforme) + const Yproxy = calcPage.getInputById("Y"); + await calcPage.setParamMode(Yproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Yproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (Sec param) + const Y = calcPage.getInputById("Y"); + await calcPage.setParamMode(Yproxy, "link"); + const sel = await calcPage.getLinkedValueSelect(Yproxy); + await calcPage.changeSelectValue(selYproxy, 3); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a single parameter, plus local variated parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Y to Y (R uniforme) + const Yproxy = calcPage.getInputById("Y"); + await calcPage.setParamMode(Yproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Yproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (Sec param) + const Y = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y, "link"); + const sel = await calcPage.getLinkedValueSelect(Y); + await calcPage.changeSelectValue(sel, 3); + // vary W + const W = calcPage.getInputById("W"); + await calcPage.setParamMode(W, "var"); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a variated parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // vary Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "var"); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Y to Y (R uniforme) + const Yproxy = calcPage.getInputById("Y"); + await calcPage.setParamMode(Yproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Yproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (Sec param) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 3); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a single result", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // calculate Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "cal"); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Y to Y (R uniforme) + const Yproxy = calcPage.getInputById("Y"); + await calcPage.setParamMode(Yproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Yproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (Sec param) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 3); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a single result, plus local variated parameter", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // calculate Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "cal"); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Y to Y (R uniforme) + const Yproxy = calcPage.getInputById("Y"); + await calcPage.setParamMode(Yproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Yproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (Sec param) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 3); + // vary W + const W = calcPage.getInputById("W"); + await calcPage.setParamMode(W, "var"); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a variated result", async () => { + // create a Régime uniforme + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(3); + // vary Q + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "var"); + // calculate Y + const Y1 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y1, "cal"); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Y to Y (R uniforme) + const Yproxy = calcPage.getInputById("Y"); + await calcPage.setParamMode(Yproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Yproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a PAB : dimensions + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(5); + // link Y to Y (Sec param) + const Y2 = calcPage.getInputById("Y"); + await calcPage.setParamMode(Y2, "link"); + const sel = await calcPage.getLinkedValueSelect(Y2); + await calcPage.changeSelectValue(sel, 3); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a single extra result", async () => { + // create a Déversoirs dénoyés + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(9); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Q to CvQT (Déversoirs dénoyés) + const Qproxy = calcPage.getInputById("Q"); + await calcPage.setParamMode(Qproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Qproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a Régime uniforme + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(3); + // link Q to CvQT (Sec param) + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "link"); + const sel = await calcPage.getLinkedValueSelect(Q); + await calcPage.changeSelectValue(sel, 3); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a single extra result, plus local variated parameter", async () => { + // create a Déversoirs dénoyés + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(9); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Q to CvQT (Déversoirs dénoyés) + const Qproxy = calcPage.getInputById("Q"); + await calcPage.setParamMode(Qproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Qproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a Régime uniforme + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(3); + // link Q to CvQT (Sec param) + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "link"); + const sel = await calcPage.getLinkedValueSelect(Q); + await calcPage.changeSelectValue(sel, 3); + // vary YB + const YB = calcPage.getInputById("YB"); + await calcPage.setParamMode(YB, "var"); + + await computeAndCheckPresenceOfResults(); + }); + + it(" − indirect links : parameter linked to a variated extra result", async () => { + // create a Déversoirs dénoyés + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(9); + // vary BR + const BR = calcPage.getInputById("BR"); + await calcPage.setParamMode(BR, "var"); + + // create a Section paramétrée + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(2); + // link Q to CvQT (Déversoirs dénoyés) + const Qproxy = calcPage.getInputById("Q"); + await calcPage.setParamMode(Qproxy, "link"); + const selYproxy = await calcPage.getLinkedValueSelect(Qproxy); + await calcPage.changeSelectValue(selYproxy, 1); + + // create a Régime uniforme + await navBar.clickNewCalculatorButton(); + await listPage.clickMenuEntryForCalcType(3); + // link Q to CvQT (Sec param) + const Q = calcPage.getInputById("Q"); + await calcPage.setParamMode(Q, "link"); + const sel = await calcPage.getLinkedValueSelect(Q); + await calcPage.changeSelectValue(sel, 3); + + await computeAndCheckPresenceOfResults(); + }); + +}); diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts index 75d501000108cad0f8829f67d9638d5414f1ee2e..69cc7a7b04f36837903b10a36fcb7f6173faefd7 100644 --- a/e2e/calculator.po.ts +++ b/e2e/calculator.po.ts @@ -29,6 +29,10 @@ export class CalculatorPage { return element(by.css("mat-select#" + id)); } + async getSelectValueText(select: ElementFinder) { + return await select.element(by.css(".mat-select-value-text > span")).getText(); + } + getInputById(id: string) { return element(by.css("input#" + id)); } @@ -46,6 +50,14 @@ export class CalculatorPage { return element.all(by.css('mat-button-toggle.radio_cal[ng-reflect-checked="true"]')); } + getAddStructureButton() { + return element(by.css("fieldset-container .hyd-window-btns button.add-structure")); + } + + getAllLinkButtons() { + return element.all(by.css("mat-button-toggle.radio_link")); + } + scrollTo(elt: ElementFinder) { browser.controlFlow().execute(function() { browser.executeScript("arguments[0].scrollIntoView(true)", elt.getWebElement()); @@ -108,11 +120,23 @@ export class CalculatorPage { await button.click(); // for "var" mode, close the modal if (mode === "var") { - await browser.sleep(500); + await browser.sleep(500); // wait for the modal to appear await element(by.css("dialog-edit-param-values .mat-dialog-actions button")).click(); + await browser.sleep(500); // wait for the navbar to reappear after modal dismissal } } + /** + * @param elt an <input> element + */ + async getLinkedValueSelect(elt: ElementFinder): Promise<ElementFinder> { + // get parent (div.container) + const container = await this.findParentContainer(elt) as ElementFinder; + // find <select> + const select = container.element(by.css("param-link mat-select")); + return select; + } + /** * Returns an object containing all the calculator's inputs values, indexed * by parameter ID diff --git a/e2e/clone-calc.e2e-spec.ts b/e2e/clone-calc.e2e-spec.ts index 459b3d7677ad34afc3e41932b10226fc678d8eb6..b768d34f0585a7c6e742cad964bdaa86703e87c6 100644 --- a/e2e/clone-calc.e2e-spec.ts +++ b/e2e/clone-calc.e2e-spec.ts @@ -2,7 +2,6 @@ import { AppPage } from "./app.po"; import { ListPage } from "./list.po"; import { CalculatorPage } from "./calculator.po"; import { Navbar } from "./navbar.po"; -import { SideNav } from "./sidenav.po"; import { browser } from "protractor"; /** @@ -13,14 +12,12 @@ describe("ngHyd − clone a calculator", () => { let listPage: ListPage; let calcPage: CalculatorPage; let navbar: Navbar; - let sidenav: SideNav; beforeEach(() => { startPage = new AppPage(); listPage = new ListPage(); calcPage = new CalculatorPage(); navbar = new Navbar(); - sidenav = new SideNav(); }); it("when cloning a calculator, the clone should have the same values for all parameters", async () => { @@ -75,22 +72,7 @@ describe("ngHyd − clone a calculator", () => { const displayedVal = await calcPage.getInputById(k).getAttribute("value"); expect(displayedVal).toBe("" + v); }); - }); - - it("cloning a parallel-structures calculator should work", async () => { - await startPage.navigateTo(); - - // create source module to clone - await navbar.clickNewCalculatorButton(); - await listPage.clickMenuEntryForCalcType(8); // Lois d'ouvrages - await browser.sleep(500); - - // otherwise clickCloneCalcButton() fails with "Element is not clickable at point" - // await browser.executeScript("window.scrollTo(0, 0);"); - await calcPage.clickCloneCalcButton(); - await browser.sleep(500); - // check existence of the cloned module - expect(await navbar.getAllCalculatorTabs().count()).toBe(2); + // @TODO check linked value (see above) }); }); diff --git a/e2e/compute-reset-chained-links.e2e-spec.ts b/e2e/compute-reset-chained-links.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc72e70c07c07f8c04bfed9bb41286b03c22a127 --- /dev/null +++ b/e2e/compute-reset-chained-links.e2e-spec.ts @@ -0,0 +1,117 @@ +import { AppPage } from "./app.po"; +import { CalculatorPage } from "./calculator.po"; +import { Navbar } from "./navbar.po"; +import { SideNav } from "./sidenav.po"; +import { browser } from "protractor"; + +/** + * Load a session containing 3 calculators, having linked parameters + * from one to another, triggers computation from the top-most, triggers + * results reset from the bottom-most. + * Does it once with parameters linked to parameters, once with parameters + * linked to results. + */ +describe("ngHyd − compute then reset chained results", () => { + let startPage: AppPage; + let calcPage: CalculatorPage; + let navbar: Navbar; + let sidenav: SideNav; + + function init() { + startPage = new AppPage(); + calcPage = new CalculatorPage(); + navbar = new Navbar(); + sidenav = new SideNav(); + } + beforeEach(init); + + it("when loading session-cascade-params.json, computation should not be chained, but results reset should", async () => { + // load session file + await startPage.navigateTo(); + await navbar.clickMenuButton(); + await browser.sleep(200); + await sidenav.clickLoadSessionButton(); + await browser.sleep(200); + await sidenav.loadSessionFile("./session-cascade-params.json"); + await browser.sleep(500); + expect(await navbar.getAllCalculatorTabs().count()).toBe(3); + + // 1. get down-most module + await navbar.clickCalculatorTabForUid("dWs5bm"); + + // check that "compute" button is active + const calcButton = calcPage.getCalculateButton(); + const disabledState = await calcButton.getAttribute("disabled"); + expect(disabledState).not.toBe("true"); + // click "compute" button + await calcButton.click(); + + // only down-most module should have results + let hasResults = await calcPage.hasResults(); + // other two should not + await navbar.clickCalculatorTabForUid("OGFzOH"); + hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(false); + await navbar.clickCalculatorTabForUid("NWp1a3"); + hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(false); + + // 2. get up-most module + await navbar.clickCalculatorTabForUid("OGFzOH"); + + // modify any input (for ex. "Ks") + await calcPage.getInputById("Ks").clear(); + await calcPage.getInputById("Ks").sendKeys("42"); + + // check all 3 modules for absence of results + for (let i = 0; i < 3; i++) { + await navbar.clickCalculatorTab(i); + hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(false); + } + }); + + it("when loading session-cascade-results.json, computation and results reset should be chained", async () => { + // load session file + await startPage.navigateTo(); + await navbar.clickMenuButton(); + await browser.sleep(200); + await sidenav.clickLoadSessionButton(); + await browser.sleep(200); + await sidenav.loadSessionFile("./session-cascade-results.json"); + await browser.sleep(500); + expect(await navbar.getAllCalculatorTabs().count()).toBe(3); + + // 1. get down-most module (PAB Dimensions) + await navbar.clickCalculatorTabForUid("bGZqcz"); + + // check that "compute" button is active + const calcButton = calcPage.getCalculateButton(); + const disabledState = await calcButton.getAttribute("true"); + expect(disabledState).not.toBe("true"); + // click "compute" button + await calcButton.click(); + + // check all 3 modules for results + for (let i = 0; i < 3; i++) { + await navbar.clickCalculatorTab(i); + const hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(true); + } + + // 2. get up-most module (Macro-rugo) + await navbar.clickCalculatorTabForUid("dnRiY2"); + + // modify any input (for ex. "Ks") + await calcPage.getInputById("Ks").clear(); + await calcPage.getInputById("Ks").sendKeys("42"); + + // check all 3 modules for absence of results + for (let i = 0; i < 3; i++) { + await navbar.clickCalculatorTab(i); + const hasResults = await calcPage.hasResults(); + expect(hasResults).toBe(false); + } + }); + +}); diff --git a/e2e/link-parallel-devices.e2e-spec.ts b/e2e/link-parallel-devices.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5c73aa4a2a8142ea8af8b6963a3f864d42a4745 --- /dev/null +++ b/e2e/link-parallel-devices.e2e-spec.ts @@ -0,0 +1,47 @@ +import { AppPage } from "./app.po"; +import { ListPage } from "./list.po"; +import { CalculatorPage } from "./calculator.po"; + +/** + * Load a session containing 4 calculators, having multiple linked parameters + * from one to another + * @TODO les valeurs des Select sont comparées au français, pas très générique :/ + */ +describe("ngHyd − load session with multiple linked parameters − ", () => { + let startPage: AppPage; + let listPage: ListPage; + let calcPage: CalculatorPage; + + function init() { + startPage = new AppPage(); + calcPage = new CalculatorPage(); + listPage = new ListPage(); + } + beforeEach(init); + + it("when creating parallel structures, devices should be linkable to one another", async () => { + + // 1. check Lois d'ouvrages + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(8); + await calcPage.getAddStructureButton().click(); + const nb1 = await calcPage.getAllLinkButtons().count(); + expect(nb1).toBe(8); // link buttons on children but not on parent + + // 2. check Passe à bassin: Cloisons + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(10); + await calcPage.getAddStructureButton().click(); + const nb2 = await calcPage.getAllLinkButtons().count(); + expect(nb2).toBe(4); // link buttons on children but not on parent + + + // 3. check Lois de déversoirs dénoyés + await startPage.navigateTo(); + await listPage.clickMenuEntryForCalcType(9); + await calcPage.getAddStructureButton().click(); + const nb3 = await calcPage.getAllLinkButtons().count(); + expect(nb3).toBe(6); // link buttons on children but not on parent + + }); +}); diff --git a/e2e/load-linked-params.e2e-spec.ts b/e2e/load-linked-params.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b67e12b65d7869855d7d8af034ef52985175a30 --- /dev/null +++ b/e2e/load-linked-params.e2e-spec.ts @@ -0,0 +1,100 @@ +import { AppPage } from "./app.po"; +import { ListPage } from "./list.po"; +import { CalculatorPage } from "./calculator.po"; +import { Navbar } from "./navbar.po"; +import { SideNav } from "./sidenav.po"; +import { browser } from "protractor"; + +/** + * Load a session containing 4 calculators, having multiple linked parameters + * from one to another + * @TODO les valeurs des Select sont comparées au français, pas très générique :/ + */ +describe("ngHyd − load session with multiple linked parameters − ", () => { + let startPage: AppPage; + let listPage: ListPage; + let calcPage: CalculatorPage; + let navbar: Navbar; + let sidenav: SideNav; + + function init() { + startPage = new AppPage(); + calcPage = new CalculatorPage(); + navbar = new Navbar(); + sidenav = new SideNav(); + listPage = new ListPage(); + } + beforeEach(init); + + it("when loading session-liens-spaghetti.json, all links should point to the right target", async () => { + await startPage.navigateTo(); + + await navbar.clickMenuButton(); + await browser.sleep(200); + + await sidenav.clickLoadSessionButton(); + await browser.sleep(200); + + await sidenav.loadSessionFile("./session-liens-spaghetti.json"); + await browser.sleep(500); + + expect(await navbar.getAllCalculatorTabs().count()).toBe(5); + + // 1. check Section paramétrée + await navbar.clickCalculatorTab(0); + + // check target params values + const sp_lb = calcPage.getSelectById("linked_LargeurBerge"); + const sp_lbv = await calcPage.getSelectValueText(sp_lb); + expect(sp_lbv).toEqual("L (Ouvrages, ouvrage 3)"); + + // 2. check Passe à macro-rugosités + await navbar.clickCalculatorTab(1); + + // check target params values + const mr_b = calcPage.getSelectById("linked_B"); + const mr_bv = await calcPage.getSelectValueText(mr_b); + expect(mr_bv).toEqual("L (résultat de Ouvrages)"); + + const mr_q = calcPage.getSelectById("linked_Q"); + const mr_qv = await calcPage.getSelectValueText(mr_q); + expect(mr_qv).toEqual("Q (R. uniforme)"); + + // 3. check Lois d'ouvrages + await navbar.clickCalculatorTab(2); + + // check target params values + const lo_z1 = calcPage.getSelectById("linked_Z1"); + const lo_z1v = await calcPage.getSelectValueText(lo_z1); + expect(lo_z1v).toEqual("ZF1 (Macro-rugo.)"); + + const lo_l = calcPage.getSelectById("linked_L"); // attention ID non unique, voir nghyd#173 + const lo_lv = await calcPage.getSelectValueText(lo_l); + expect(lo_lv).toEqual("L (résultat de Ouvrages)"); + + const lo_w = calcPage.getSelectById("linked_W"); // attention ID non unique, voir nghyd#173 + const lo_wv = await calcPage.getSelectValueText(lo_w); + expect(lo_wv).toEqual("W (Ouvrages, ouvrage 2)"); + + const lo_cd = calcPage.getSelectById("linked_Cd"); // attention ID non unique, voir nghyd#173 + const lo_cdv = await calcPage.getSelectValueText(lo_cd); + expect(lo_cdv).toEqual("Cd (Ouvrages, ouvrage 1)"); + + // 4. check Régime uniforme + await navbar.clickCalculatorTab(3); + + // check target params values + const lo_q = calcPage.getSelectById("linked_Q"); + const lo_qv = await calcPage.getSelectValueText(lo_q); + expect(lo_qv).toEqual("CvQT (Déver. dénoyés, résultat complémentaire)"); + + // 5. check Déver. dénoyés + await navbar.clickCalculatorTab(4); + + // check target params values + const lo_br = calcPage.getSelectById("linked_BR"); + const lo_brv = await calcPage.getSelectValueText(lo_br); + expect(lo_brv).toEqual("LargeurBerge (Sec. param.)"); + }); + +}); diff --git a/e2e/navbar.po.ts b/e2e/navbar.po.ts index 2112ceecf8ae3666311cee6fe7627b09dc433b9a..621416ac95243f7d0fd7917d45e556b8cc73e4ff 100644 --- a/e2e/navbar.po.ts +++ b/e2e/navbar.po.ts @@ -5,6 +5,10 @@ export class Navbar { return element.all(by.css("#tabs-container > button.calculator-button")); } + getCalculatorTabForUid(uid: string) { + return element(by.css("#tabs-container > button.calculator-button.calculator-uid-" + uid)); + } + getNewCalculatorButton() { return element(by.css("#new-calculator")); } @@ -18,6 +22,11 @@ export class Navbar { await tabs.get(n).click(); } + async clickCalculatorTabForUid(uid: string) { + const tab = this.getCalculatorTabForUid(uid); + await tab.click(); + } + async clickRandomCalculatorTab(n: number) { const tabs = this.getAllCalculatorTabs(); const l = await tabs.count(); diff --git a/e2e/session-6-calc.test.json b/e2e/session-6-calc.test.json index 54df75d63bd35b630fa75b19a592a9d41be3e817..a24a0012f32b98e2ecd3461342b56bcbb33d764c 100644 --- a/e2e/session-6-calc.test.json +++ b/e2e/session-6-calc.test.json @@ -1 +1,342 @@ -{"session":[{"uid":"NHY0cX","props":{"calcType":5,"nodeType":0},"meta":{"title":"PAB : dimensions"},"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"L","mode":"SINGLE","value":2},{"symbol":"W","mode":"SINGLE","value":1},{"symbol":"Y","mode":"SINGLE","value":0.5},{"symbol":"V","mode":"CALCUL"}]},{"uid":"YzAwMW","props":{"calcType":11,"nodeType":0},"meta":{"title":"Macro-rugo."},"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"ZF1","mode":"SINGLE","value":12.5},{"symbol":"L","mode":"SINGLE","value":6},{"symbol":"B","mode":"SINGLE","value":1},{"symbol":"If","mode":"SINGLE","value":0.05},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Y","mode":"SINGLE","value":0.6},{"symbol":"Ks","mode":"SINGLE","value":0.01},{"symbol":"C","mode":"SINGLE","value":0.05},{"symbol":"PBD","mode":"SINGLE","value":0.5},{"symbol":"PBH","mode":"SINGLE","value":0.8},{"symbol":"Cd0","mode":"SINGLE","value":1.5}]},{"uid":"dGc5MD","props":{"calcType":8,"nodeType":0},"meta":{"title":"Ouvrages"},"structures":[{"uid":"NjZob3","props":{"calcType":7,"nodeType":5,"structureType":1,"loiDebit":1},"parameters":[{"symbol":"ZDV","mode":"SINGLE","value":100},{"symbol":"W","mode":"SINGLE","value":0.5},{"symbol":"L","mode":"SINGLE","value":2},{"symbol":"Cd","mode":"SINGLE","value":0.6}]}],"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"CALCUL"},{"symbol":"Z1","mode":"SINGLE","value":102},{"symbol":"Z2","mode":"SINGLE","value":101.5}]},{"uid":"OGZ4cm","props":{"varCalc":"Hs","calcType":2,"nodeType":2},"meta":{"title":"Sec. param."},"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Ks","mode":"SINGLE","value":40},{"symbol":"Q","mode":"SINGLE","value":1.2},{"symbol":"If","mode":"SINGLE","value":0.001},{"symbol":"YB","mode":"SINGLE","value":1},{"symbol":"Y","mode":"SINGLE","value":0.8},{"symbol":"LargeurBerge","mode":"SINGLE","value":2.5}]},{"uid":"ZTNvMD","props":{"methodeResolution":0,"calcType":4,"nodeType":2},"meta":{"title":"Remous"},"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Yamont","mode":"SINGLE","value":0.15},{"symbol":"Yaval","mode":"SINGLE","value":0.4},{"symbol":"Long","mode":"SINGLE","value":100},{"symbol":"Dx","mode":"SINGLE","value":5},{"symbol":"Ks","mode":"SINGLE","value":40},{"symbol":"Q","mode":"SINGLE","value":1.2},{"symbol":"If","mode":"SINGLE","value":0.001},{"symbol":"YB","mode":"SINGLE","value":1},{"symbol":"Y","mode":"SINGLE","value":0.2863766123093061},{"symbol":"LargeurBerge","mode":"SINGLE","value":2.5}]},{"uid":"eWllan","props":{"calcType":1,"nodeType":0},"meta":{"title":"Lechapt-Calmon"},"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Q","mode":"SINGLE","value":3},{"symbol":"D","mode":"SINGLE","value":1.2},{"symbol":"J","mode":"CALCUL"},{"symbol":"Lg","mode":"SINGLE","value":100},{"symbol":"L","mode":"SINGLE","value":"1.863"},{"symbol":"M","mode":"SINGLE","value":"2"},{"symbol":"N","mode":"SINGLE","value":"5.33"}]}]} \ No newline at end of file +{ + "session": [ + { + "uid": "NHY0cX", + "props": { + "calcType": 5, + "nodeType": 0 + }, + "meta": { + "title": "PAB : dimensions" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 2 + }, + { + "symbol": "W", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "V", + "mode": "CALCUL" + } + ] + }, + { + "uid": "YzAwMW", + "props": { + "calcType": 11, + "nodeType": 0 + }, + "meta": { + "title": "Macro-rugo." + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "ZF1", + "mode": "SINGLE", + "value": 12.5 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 6 + }, + { + "symbol": "B", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.05 + }, + { + "symbol": "Q", + "mode": "CALCUL" + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.6 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 0.01 + }, + { + "symbol": "C", + "mode": "SINGLE", + "value": 0.05 + }, + { + "symbol": "PBD", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "PBH", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "Cd0", + "mode": "SINGLE", + "value": 1.5 + } + ] + }, + { + "uid": "dGc5MD", + "props": { + "calcType": 8, + "nodeType": 0 + }, + "meta": { + "title": "Ouvrages" + }, + "structures": [ + { + "uid": "NjZob3", + "props": { + "calcType": 7, + "nodeType": 5, + "structureType": 1, + "loiDebit": 1 + }, + "parameters": [ + { + "symbol": "ZDV", + "mode": "SINGLE", + "value": 100 + }, + { + "symbol": "W", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 2 + }, + { + "symbol": "Cd", + "mode": "SINGLE", + "value": 0.6 + } + ] + } + ], + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Q", + "mode": "CALCUL" + }, + { + "symbol": "Z1", + "mode": "SINGLE", + "value": 102 + }, + { + "symbol": "Z2", + "mode": "SINGLE", + "value": 101.5 + } + ] + }, + { + "uid": "OGZ4cm", + "props": { + "varCalc": "Hs", + "calcType": 2, + "nodeType": 2 + }, + "meta": { + "title": "Sec. param." + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 1.2 + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "SINGLE", + "value": 2.5 + } + ] + }, + { + "uid": "ZTNvMD", + "props": { + "methodeResolution": 0, + "calcType": 4, + "nodeType": 2 + }, + "meta": { + "title": "Remous" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Yamont", + "mode": "SINGLE", + "value": 0.15 + }, + { + "symbol": "Yaval", + "mode": "SINGLE", + "value": 0.4 + }, + { + "symbol": "Long", + "mode": "SINGLE", + "value": 100 + }, + { + "symbol": "Dx", + "mode": "SINGLE", + "value": 5 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 1.2 + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.2863766123093061 + }, + { + "symbol": "LargeurBerge", + "mode": "SINGLE", + "value": 2.5 + } + ] + }, + { + "uid": "eWllan", + "props": { + "calcType": 1, + "nodeType": 0 + }, + "meta": { + "title": "Lechapt-Calmon" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 3 + }, + { + "symbol": "D", + "mode": "SINGLE", + "value": 1.2 + }, + { + "symbol": "J", + "mode": "CALCUL" + }, + { + "symbol": "Lg", + "mode": "SINGLE", + "value": 100 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": "1.863" + }, + { + "symbol": "M", + "mode": "SINGLE", + "value": "2" + }, + { + "symbol": "N", + "mode": "SINGLE", + "value": "5.33" + } + ] + } + ] +} \ No newline at end of file diff --git a/e2e/session-cascade-params.json b/e2e/session-cascade-params.json new file mode 100644 index 0000000000000000000000000000000000000000..773053d5409813c93e2adfe6811d03ac299945d3 --- /dev/null +++ b/e2e/session-cascade-params.json @@ -0,0 +1,146 @@ +{ + "session": [ + { + "uid": "OGFzOH", + "props": { + "varCalc": "Hs", + "calcType": 2, + "nodeType": 2 + }, + "meta": { + "title": "Sec. param." + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 1.2 + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "SINGLE", + "value": 2.5 + } + ] + }, + { + "uid": "NWp1a3", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "meta": { + "title": "R. uniforme" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "CALCUL" + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "LINK", + "targetNub": "OGFzOH", + "targetParam": "LargeurBerge" + } + ] + }, + { + "uid": "dWs5bm", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "meta": { + "title": "R. uniforme 1" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "CALCUL" + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "LINK", + "targetNub": "NWp1a3", + "targetParam": "LargeurBerge" + } + ] + } + ] +} \ No newline at end of file diff --git a/e2e/session-cascade-results.json b/e2e/session-cascade-results.json new file mode 100644 index 0000000000000000000000000000000000000000..3c144185070cf9e20ba23b314555d023eb9e6376 --- /dev/null +++ b/e2e/session-cascade-results.json @@ -0,0 +1,159 @@ +{ + "session": [ + { + "uid": "dnRiY2", + "props": { + "calcType": 11, + "nodeType": 0 + }, + "meta": { + "title": "Macro-rugo." + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "ZF1", + "mode": "SINGLE", + "value": 12.5 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 6 + }, + { + "symbol": "B", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "If", + "mode": "CALCUL" + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 1.57 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.6 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 0.01 + }, + { + "symbol": "C", + "mode": "SINGLE", + "value": 0.05 + }, + { + "symbol": "PBD", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "PBH", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "Cd0", + "mode": "SINGLE", + "value": 1.5 + } + ] + }, + { + "uid": "YjZyND", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "meta": { + "title": "R. uniforme" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "LINK", + "targetNub": "dnRiY2", + "targetParam": "Q_GuideTech" + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "CALCUL" + } + ] + }, + { + "uid": "bGZqcz", + "props": { + "calcType": 5, + "nodeType": 0 + }, + "meta": { + "title": "PAB : dimensions" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 2 + }, + { + "symbol": "W", + "mode": "LINK", + "targetNub": "YjZyND", + "targetParam": "LargeurBerge" + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "V", + "mode": "CALCUL" + } + ] + } + ] +} \ No newline at end of file diff --git a/e2e/session-liens-spaghetti.json b/e2e/session-liens-spaghetti.json new file mode 100644 index 0000000000000000000000000000000000000000..87b17d2a13cc46819f2620f39cf5ba412304773f --- /dev/null +++ b/e2e/session-liens-spaghetti.json @@ -0,0 +1,372 @@ +{ + "session": [ + { + "uid": "OW9rd3", + "props": { + "varCalc": "Hs", + "calcType": 2, + "nodeType": 2 + }, + "meta": { + "title": "Sec. param." + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 1.2 + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "LINK", + "targetNub": "dmh4Z3", + "targetParam": "L" + } + ] + }, + { + "uid": "dTB3ZG", + "props": { + "calcType": 11, + "nodeType": 0 + }, + "meta": { + "title": "Macro-rugo." + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "ZF1", + "mode": "SINGLE", + "value": 12.5 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 6 + }, + { + "symbol": "B", + "mode": "LINK", + "targetNub": "ZW9icn", + "targetParam": "L" + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.05 + }, + { + "symbol": "Q", + "mode": "LINK", + "targetNub": "OGhuZj", + "targetParam": "Q" + }, + { + "symbol": "Y", + "mode": "CALCUL" + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 0.01 + }, + { + "symbol": "C", + "mode": "SINGLE", + "value": 0.05 + }, + { + "symbol": "PBD", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "PBH", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "Cd0", + "mode": "MINMAX", + "min": 0.75, + "max": 3, + "step": 0.1125 + } + ] + }, + { + "uid": "ZW9icn", + "props": { + "calcType": 8, + "nodeType": 0 + }, + "meta": { + "title": "Ouvrages" + }, + "structures": [ + { + "uid": "cjdyYW", + "props": { + "calcType": 7, + "nodeType": 5, + "structureType": 1, + "loiDebit": 1 + }, + "parameters": [ + { + "symbol": "ZDV", + "mode": "SINGLE", + "value": 100 + }, + { + "symbol": "W", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "L", + "mode": "CALCUL" + }, + { + "symbol": "Cd", + "mode": "SINGLE", + "value": 0.6 + } + ] + }, + { + "uid": "Ynpnaj", + "props": { + "calcType": 7, + "nodeType": 5, + "structureType": 1, + "loiDebit": 1 + }, + "parameters": [ + { + "symbol": "ZDV", + "mode": "SINGLE", + "value": 100 + }, + { + "symbol": "W", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "L", + "mode": "LINK", + "targetNub": "ZW9icn", + "targetParam": "L" + }, + { + "symbol": "Cd", + "mode": "SINGLE", + "value": 0.6 + } + ] + }, + { + "uid": "dmh4Z3", + "props": { + "calcType": 7, + "nodeType": 5, + "structureType": 1, + "loiDebit": 1 + }, + "parameters": [ + { + "symbol": "ZDV", + "mode": "SINGLE", + "value": 100 + }, + { + "symbol": "W", + "mode": "LINK", + "targetNub": "Ynpnaj", + "targetParam": "W" + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 2 + }, + { + "symbol": "Cd", + "mode": "LINK", + "targetNub": "cjdyYW", + "targetParam": "Cd" + } + ] + } + ], + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 0.5 + }, + { + "symbol": "Z1", + "mode": "LINK", + "targetNub": "dTB3ZG", + "targetParam": "ZF1" + }, + { + "symbol": "Z2", + "mode": "SINGLE", + "value": 101.5 + } + ] + }, + { + "uid": "OGhuZj", + "props": { + "calcType": 3, + "nodeType": 2 + }, + "meta": { + "title": "R. uniforme" + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "LINK", + "targetNub": "NGlpMn", + "targetParam": "CvQT" + }, + { + "symbol": "If", + "mode": "MINMAX", + "min": 0.0005, + "max": 0.002, + "step": 0.00007500000000000001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "CALCUL" + } + ] + }, + { + "uid": "NGlpMn", + "props": { + "calcType": 9, + "nodeType": 0 + }, + "meta": { + "title": "Déver. dénoyés" + }, + "structures": [ + { + "uid": "M29rcW", + "props": { + "calcType": 7, + "nodeType": 5, + "structureType": 0, + "loiDebit": 7 + }, + "parameters": [ + { + "symbol": "ZDV", + "mode": "SINGLE", + "value": 100 + }, + { + "symbol": "L", + "mode": "SINGLE", + "value": 2 + }, + { + "symbol": "Cd", + "mode": "SINGLE", + "value": 0.4 + } + ] + } + ], + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Q", + "mode": "CALCUL" + }, + { + "symbol": "Z1", + "mode": "SINGLE", + "value": 102 + }, + { + "symbol": "BR", + "mode": "LINK", + "targetNub": "OW9rd3", + "targetParam": "LargeurBerge" + }, + { + "symbol": "ZR", + "mode": "SINGLE", + "value": 99 + } + ] + } + ] +} \ No newline at end of file diff --git a/e2e/session-optional-params.test.json b/e2e/session-optional-params.test.json index 804c559cb54b2dfa3f367b66a9ca29de7e953e4a..6fb0e40e804a24c0926dec69b77e432a75c46355 100644 --- a/e2e/session-optional-params.test.json +++ b/e2e/session-optional-params.test.json @@ -1 +1,57 @@ -{"session":[{"uid":"N2U4OH","props":{"varCalc":"Hs","calcType":2,"nodeType":4},"meta":{"title":"Sec. param."},"parameters":[{"symbol":"Pr","mode":"SINGLE","value":0.0001},{"symbol":"Ks","mode":"SINGLE","value":40},{"symbol":"Q","mode":"SINGLE","value":1.2},{"symbol":"If","mode":"SINGLE","value":0.001},{"symbol":"YB","mode":"SINGLE","value":1},{"symbol":"Y","mode":"SINGLE","value":0.8},{"symbol":"LargeurBerge","mode":"SINGLE","value":4},{"symbol":"k","mode":"SINGLE","value":0.5}]}]} \ No newline at end of file +{ + "session": [ + { + "uid": "N2U4OH", + "props": { + "varCalc": "Hs", + "calcType": 2, + "nodeType": 4 + }, + "meta": { + "title": "Sec. param." + }, + "parameters": [ + { + "symbol": "Pr", + "mode": "SINGLE", + "value": 0.0001 + }, + { + "symbol": "Ks", + "mode": "SINGLE", + "value": 40 + }, + { + "symbol": "Q", + "mode": "SINGLE", + "value": 1.2 + }, + { + "symbol": "If", + "mode": "SINGLE", + "value": 0.001 + }, + { + "symbol": "YB", + "mode": "SINGLE", + "value": 1 + }, + { + "symbol": "Y", + "mode": "SINGLE", + "value": 0.8 + }, + { + "symbol": "LargeurBerge", + "mode": "SINGLE", + "value": 4 + }, + { + "symbol": "k", + "mode": "SINGLE", + "value": 0.5 + } + ] + } + ] +} \ No newline at end of file diff --git a/jalhyd_branch b/jalhyd_branch index 1f7391f92b6a3792204e07e99f71f643cc35e7e1..6ba102133f195085ddf25826b3ac5dbc6162e06a 100644 --- a/jalhyd_branch +++ b/jalhyd_branch @@ -1 +1 @@ -master +54-ameliorer-le-filtre-de-choix-des-parametres-pouvant-etre-lies diff --git a/src/app/app.component.html b/src/app/app.component.html index f2863b8e4cdfe3487b6b04c8626fa864f7f60cb9..fc3d49453c11e6096b8b155b1bf1d34f08fb0ded 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -40,9 +40,9 @@ <!-- calculators list as a tabs bar--> <div id="tabs-container" [hidden]="! tabsFitInNavbar"> - <button mat-raised-button color="primary" *ngFor="let c of calculators" class="calculator-button" [title]="c.title" - [routerLink]="['/calculator/',c.uid]" [color]="c.active ? '' : 'primary'" [class.active]="c.active" - (click)="setActiveCalc(c.uid)"> + <button mat-raised-button color="primary" *ngFor="let c of calculators" class="" [title]="c.title" + [routerLink]="['/calculator/',c.uid]" [color]="c.active ? '' : 'primary'" (click)="setActiveCalc(c.uid)" + [ngClass]="['calculator-button', 'calculator-uid-' + c.uid, c.active ? 'active' : '' ]"> <span class="calc-name"> {{ c.title }} diff --git a/src/app/app.component.scss b/src/app/app.component.scss index c45d95ed140f69b985c28b720d717217d322a516..98e0c6c01190ec530e9de00bbd4607cee816e439 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -115,7 +115,7 @@ button:focus { border-bottom: none; } .calculator-button.mat-raised-button { - height: calc(#{$navbar_height} - 3px); + height: calc(#{$navbar_height} - 5px); &:not([class*="mat-elevation-z"]) { box-shadow: -1px -2px 2px 0 rgba(0,0,0,.4),1px -2px 5px 0 rgba(0,0,0,.1); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 4e9c48e531fd1469389d35ccac7ccb011f166064..e12e5260384b0dd74c73386f24e6389de43142df 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -18,6 +18,7 @@ import { nghydDateRev } from "../date_revision"; import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component"; import { DialogLoadSessionComponent } from "./components/dialog-load-session/dialog-load-session.component"; import { DialogSaveSessionComponent } from "./components/dialog-save-session/dialog-save-session.component"; +import { NotificationsService } from "./services/notifications/notifications.service"; @Component({ @@ -66,6 +67,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { private router: Router, private formulaireService: FormulaireService, private httpService: HttpService, + private notificationsService: NotificationsService, private confirmEmptySessionDialog: MatDialog, private saveSessionDialog: MatDialog, private loadSessionDialog: MatDialog, @@ -76,6 +78,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { ServiceFactory.instance.applicationSetupService = appSetupService; ServiceFactory.instance.i18nService = intlService; ServiceFactory.instance.formulaireService = formulaireService; + ServiceFactory.instance.notificationsService = notificationsService; this.router.events.subscribe((event: Event) => { // close side navigation when clicking a calculator tab @@ -217,16 +220,7 @@ export class AppComponent implements OnInit, OnDestroy, Observer { // interface Observer update(sender: any, data: any): void { - if (sender instanceof ErrorService) { - // on ouvre un dialogue avec le message d'erreur reçu - // if (this._displayErrorDialog) { - // let dialogRef = this.dialog.open(AlertDialog); - // let ad: AlertDialog = dialogRef.componentInstance; - // ad.text = String(data); - // } - // else - console.log(data); - } else if (sender instanceof FormulaireService) { + if (sender instanceof FormulaireService) { switch (data["action"]) { case "createForm": // add newly created form to calculators list diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2759ef9f56aad5027654e2911e2c776c4e34f6e5..059c34d50c5b202c8865a3ab6fe161f492104f85 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -19,6 +19,7 @@ import { MatCardModule, MatTableModule, MatSnackBarModule, + MatBadgeModule, ErrorStateMatcher, MatButtonToggleModule } from "@angular/material"; @@ -43,6 +44,7 @@ import { FormulaireService } from "./services/formulaire/formulaire.service"; import { I18nService } from "./services/internationalisation/internationalisation.service"; import { HttpService } from "./services/http/http.service"; import { ApplicationSetupService } from "./services/app-setup/app-setup.service"; +import { NotificationsService } from "./services/notifications/notifications.service"; import { AppComponent } from "./app.component"; import { NgParamInputComponent } from "./components/ngparam-input/ngparam-input.component"; @@ -52,7 +54,6 @@ import { ParamComputedComponent } from "./components/param-computed/param-comput import { ParamFieldLineComponent } from "./components/param-field-line/param-field-line.component"; import { ParamValuesComponent } from "./components/param-values/param-values.component"; import { SelectFieldLineComponent } from "./components/select-field-line/select-field-line.component"; -import { CheckFieldLineComponent } from "./components/check-field-line/check-field-line.component"; import { CalculatorResultsComponent } from "./components/calculator-results/calculator-results.component"; import { FixedVarResultsComponent } from "./components/fixedvar-results/fixedvar-results.component"; import { SectionResultsComponent } from "./components/section-results/section-results.component"; @@ -104,6 +105,7 @@ const appRoutes: Routes = [ ChartModule, HttpClientModule, FlexLayoutModule, + MatBadgeModule, MatButtonModule, MatButtonToggleModule, MatCardModule, @@ -140,7 +142,6 @@ const appRoutes: Routes = [ CalculatorListComponent, CalculatorNameComponent, CalculatorResultsComponent, - CheckFieldLineComponent, DialogConfirmCloseCalcComponent, DialogConfirmEmptySessionComponent, DialogEditParamComputedComponent, @@ -189,6 +190,7 @@ const appRoutes: Routes = [ FormulaireService, HttpService, I18nService, + NotificationsService, { provide: ErrorStateMatcher, useClass: ImmediateErrorStateMatcher diff --git a/src/app/components/app-setup/app-setup.component.ts b/src/app/components/app-setup/app-setup.component.ts index 0b373a0d2b671309579c175a9c31e5f0a750b199..87b96145b475d9387b827c0f3066d338f16a0793 100644 --- a/src/app/components/app-setup/app-setup.component.ts +++ b/src/app/components/app-setup/app-setup.component.ts @@ -83,7 +83,7 @@ export class ApplicationSetupComponent extends BaseComponent implements Observer public storePreferences() { this.appSetupService.saveValuesIntoLocalStorage(); this.snackBar.open(this.intlService.localizeText("INFO_SNACKBAR_SETTINGS_SAVED"), "OK", { - duration: 1200 + duration: 2500 }); } @@ -91,7 +91,7 @@ export class ApplicationSetupComponent extends BaseComponent implements Observer const text = this.intlService.localizeText("INFO_SNACKBAR_DEFAULT_SETTINGS_RESTORED"); this.appSetupService.restoreDefaultValues().then(() => { this.snackBar.open(text, "OK", { - duration: 1200 + duration: 2500 }); }); } diff --git a/src/app/components/base-param-input/base-param-input.component.ts b/src/app/components/base-param-input/base-param-input.component.ts index e04b0a4607e2d3632eab3e07700c7a8b2c0bb888..4c31d115f57d1680dc8dc8c005c555ff03983ba3 100644 --- a/src/app/components/base-param-input/base-param-input.component.ts +++ b/src/app/components/base-param-input/base-param-input.component.ts @@ -34,7 +34,7 @@ export class NgBaseParam extends Observable { } public checkValue(val: number) { - return this._param.checkValue(val); + return this._param.checkValueAgainstDomain(val); } public checkMin(min: number): boolean { @@ -71,11 +71,11 @@ export class NgBaseParam extends Observable { msg = ServiceFactory.instance.i18nService.localizeText("ERROR_PARAM_NULL"); } else { try { - this._param.checkValue(v); + this._param.checkValueAgainstDomain(v); valid = true; } catch (e) { if (e instanceof Message) { - // @TODO ici au début le service de localisation n'a pas encore chargé ses messages… + // ici au début le service de localisation n'a pas encore chargé ses messages… msg = ServiceFactory.instance.i18nService.localizeMessage(e); } else { msg = "invalid value"; @@ -92,8 +92,8 @@ export class NgBaseParam extends Observable { templateUrl: "../generic-input/generic-input.component.html", }) export class BaseParamInputComponent extends GenericInputComponent { - constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) { - super(cdRef); + constructor(intlService: I18nService, cdRef: ChangeDetectorRef) { + super(cdRef, intlService); } /** diff --git a/src/app/components/check-field-line/check-field-line.component.html b/src/app/components/check-field-line/check-field-line.component.html deleted file mode 100644 index f00f6fca5843423f7d52b41f5a509c5a70648fde..0000000000000000000000000000000000000000 --- a/src/app/components/check-field-line/check-field-line.component.html +++ /dev/null @@ -1,6 +0,0 @@ -<tr> - <td align="right">{{ check.label }}</td> - <td colspan="3"> - <input type="checkbox" [(ngModel)]=currentValue (ngModelChange)="onChange($event)"> - </td> -</tr> \ No newline at end of file diff --git a/src/app/components/check-field-line/check-field-line.component.ts b/src/app/components/check-field-line/check-field-line.component.ts deleted file mode 100644 index ae159f511062947071e1ccfefddfec5653ecb09e..0000000000000000000000000000000000000000 --- a/src/app/components/check-field-line/check-field-line.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component, Input, Output, EventEmitter } from "@angular/core"; - -import { CheckField } from "../../formulaire/check-field"; - -@Component({ - selector: "check-field-line", - templateUrl: "./check-field-line.component.html", -}) -export class CheckFieldLineComponent { - @Input() - public check: CheckField; - - public currentValue: boolean; - - constructor() { } - - public onChange(event: any) { - this.check.setValue(event); - } -} diff --git a/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.html b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.html index 8018436a73c3111ceb3ee4e5888067a83e049ac4..f1abc0500a58e9143591b98877d6575857a7fbcf 100644 --- a/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.html +++ b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.html @@ -1,7 +1,18 @@ <h1 mat-dialog-title [innerHTML]="uitextCloseCalcTitle"></h1> <div mat-dialog-content> - <p [innerHTML]="uitextCloseCalcBody"></p> + + <div *ngIf="dependingNubs.length"> + {{ uitextDependingModules }} + <mat-list role="list"> + <mat-list-item role="listitem" *ngFor="let dn of dependingNubs"> + {{ dn }} + </mat-list-item> + </mat-list> + </div> </div> + +<p [innerHTML]="uitextCloseCalcBody"></p> + <div mat-dialog-actions> <button mat-raised-button color="primary" [mat-dialog-close]="false" cdkFocusInitial> {{ uitextNo }} diff --git a/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.scss b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..268f18ad89927376209eddd80ee16d1a8065535f --- /dev/null +++ b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.scss @@ -0,0 +1,3 @@ +.mat-list-base .mat-list-item { + height: 24px; +} diff --git a/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.ts b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.ts index e8a1f44ca145c27fef780d49aeaead3e49706aa1..63cdc8fa4a9bb903145cce4501f13aabb90caca9 100644 --- a/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.ts +++ b/src/app/components/dialog-confirm-close-calc/dialog-confirm-close-calc.component.ts @@ -1,18 +1,30 @@ import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; import { Inject, Component } from "@angular/core"; +import { FormulaireService } from "../../services/formulaire/formulaire.service"; import { I18nService } from "../../services/internationalisation/internationalisation.service"; +import { Session } from "jalhyd"; @Component({ selector: "dialog-confirm-close-calc", templateUrl: "dialog-confirm-close-calc.component.html", + styleUrls: [ + "dialog-confirm-close-calc.component.scss" + ] }) export class DialogConfirmCloseCalcComponent { + private _dependingNubs: any[] = []; + constructor( public dialogRef: MatDialogRef<DialogConfirmCloseCalcComponent>, private intlService: I18nService, + private formService: FormulaireService, @Inject(MAT_DIALOG_DATA) public data: any - ) { } + ) { + this._dependingNubs = Session.getInstance().getDependingNubs(data.uid).map((n) => { + return this.formService.getFormulaireFromNubId(n.uid).calculatorName; + }); + } public get uitextYes() { return this.intlService.localizeText("INFO_OPTION_YES"); @@ -30,4 +42,12 @@ export class DialogConfirmCloseCalcComponent { return this.intlService.localizeText("INFO_CLOSE_DIALOGUE_TEXT"); } + public get uitextDependingModules() { + return this.intlService.localizeText("INFO_CLOSE_DIALOGUE_DEPENDING_MODULES"); + } + + public get dependingNubs() { + return this._dependingNubs; + } + } diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html index 9efaf288795c96ec273dc07655f1f13889cfad2a..517a0f0d1a378c689b6a7aabfef85ae47c944daf 100644 --- a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html +++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html @@ -4,7 +4,6 @@ <div mat-dialog-content> <ngparam-input [title]="param.title" ></ngparam-input> - <!-- (change)="onInputChange($event)" --> </div> <div mat-dialog-actions> diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html index c4ace21ec7c7b7c93417e7d709af024c2a9db285..8fbe604a524054fb39e7dc0727076d78e1e348b9 100644 --- a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html @@ -20,7 +20,7 @@ <form> <mat-form-field> <input matInput class="form-control" type="number" inputmode="numeric" name="min-value" step="0.01" - [placeholder]="uitextValeurMini" [(ngModel)]="param.minValue" #min="ngModel" name="min" + [placeholder]="uitextValeurMini" [(ngModel)]="minValue" #min="ngModel" name="min" [appJalhydModelValidationMin]="param" required pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"> <mat-error *ngIf="min.errors"> @@ -35,7 +35,7 @@ <mat-form-field> <input matInput class="form-control" type="number" inputmode="numeric" name="max-value" step="0.01" - [placeholder]="uitextValeurMaxi" [(ngModel)]="param.maxValue" #max="ngModel" name="max" + [placeholder]="uitextValeurMaxi" [(ngModel)]="maxValue" #max="ngModel" name="max" [appJalhydModelValidationMax]="param" required pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"> <mat-error *ngIf="max.errors"> @@ -50,7 +50,7 @@ <mat-form-field> <input matInput class="form-control" type="number" inputmode="numeric" name="step-value" step="0.01" - [placeholder]="uitextPasVariation" [(ngModel)]="param.stepValue" #step="ngModel" name="step" + [placeholder]="uitextPasVariation" [(ngModel)]="stepValue" #step="ngModel" name="step" [appJalhydModelValidationStep]="param" required pattern="^-?([0-9]*\.)?([0-9]+[Ee]-?)?[0-9]+$"> <mat-error *ngIf="step.errors"> diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss index 111eef7498f0d945ad5b49c5ca290bdfb9f6c426..0ffd355d6448f01b32eac82575ee4dba7d17a7d4 100644 --- a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.scss @@ -19,6 +19,7 @@ mat-form-field { .min-max-step-container { margin-top: -8px; + padding-bottom: 1em; // for possible mat-error on step field } .mat-dialog-content { diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts index 0d1ad08b82e61020358e4a1cce7c4fed4f917c66..bb9168725ae178f4ac4e7e95fbf6e51ce306831f 100644 --- a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts +++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts @@ -114,6 +114,32 @@ export class DialogEditParamValuesComponent implements OnInit { }; } + // proxy to model values + + public get minValue() { + return this.param.minValue; + } + + public set minValue(v) { + this.param.setMinValue(this, v); + } + + public get maxValue() { + return this.param.maxValue; + } + + public set maxValue(v) { + this.param.setMaxValue(this, v); + } + + public get stepValue() { + return this.param.stepValue; + } + + public set stepValue(v) { + this.param.setStepValue(this, v); + } + /** * regular expression pattern for values list validation (depends on decimal separator) */ @@ -172,7 +198,7 @@ export class DialogEditParamValuesComponent implements OnInit { vals.push(Number(e)); } }); - this.param.valueList = vals; + this.param.setValueList(this, vals); } public toggleViewChart() { @@ -280,24 +306,24 @@ export class DialogEditParamValuesComponent implements OnInit { // init min / max / step if (this.isMinMax) { if (this.param.minValue === undefined) { - this.param.minValue = this.param.getValue() / 2; + this.param.setMinValue(this, this.param.getValue() / 2); } if (this.param.maxValue === undefined) { - this.param.maxValue = this.param.getValue() * 2; + this.param.setMaxValue(this, this.param.getValue() * 2); } let step = this.param.stepValue; if (step === undefined) { step = (this.param.maxValue - this.param.minValue) / 20; } - this.param.stepValue = step; + this.param.setStepValue(this, step); } // init values list if (this.isListe) { if (this.param.valueList === undefined) { if (this.param.isDefined) { - this.param.valueList = [ this.param.getValue() ]; + this.param.setValueList(this, [ this.param.getValue() ]); } else { - this.param.valueList = []; + this.param.setValueList(this, []); } // set form control initial value this.valuesListForm.controls.valuesList.setValue(this.valuesList); diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.html b/src/app/components/dialog-load-session/dialog-load-session.component.html index 0473b4cdeb95ad579bf3a19d62aeb8fda50f2427..97e2d6e4296093ab83d1575c2dee75b65b934bae 100644 --- a/src/app/components/dialog-load-session/dialog-load-session.component.html +++ b/src/app/components/dialog-load-session/dialog-load-session.component.html @@ -13,7 +13,8 @@ </mat-form-field> <div class="cb-container"> - <mat-checkbox [name]="c.uid" *ngFor="let c of calculators" [(ngModel)]="c.selected" [ngModelOptions]="{standalone: true}"> + <mat-checkbox [name]="c.uid" *ngFor="let c of calculators" (change)="checkLinkedParamsDependencies()" + [(ngModel)]="c.selected" [ngModelOptions]="{standalone: true}"> {{ c.title }} </mat-checkbox> </div> @@ -26,6 +27,20 @@ {{ uitextNone }} </button> </div> + + <div class="dependencies-problems" *ngIf="dependenciesProblems.length > 0"> + <mat-list role="list"> + <mat-list-item role="listitem" *ngFor="let dp of dependenciesProblems"> + <mat-icon color="warn">error_outline</mat-icon> {{ dp.message }} + </mat-list-item> + </mat-list> + <p> + <button mat-raised-button (click)="fixDependencies()" + [matBadge]="dependenciesProblems.length" matBadgeColor="warn"> + {{ uitextFixMissingDependencies }} + </button> + </p> + </div> </div> <div mat-dialog-actions> diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.scss b/src/app/components/dialog-load-session/dialog-load-session.component.scss index e612ad2cf5490552d96d024c1122ef4cd1bbfc1f..f9cad276513dfef8f4fd62cf8ecc57e177dc538b 100644 --- a/src/app/components/dialog-load-session/dialog-load-session.component.scss +++ b/src/app/components/dialog-load-session/dialog-load-session.component.scss @@ -17,3 +17,24 @@ mat-form-field { margin-right: 5px; } } + +.dependencies-problems { + + .mat-list-base { + padding-top: 0; + + .mat-list-item { + height: 32px; + font-size: .9em; + + ::ng-deep .mat-list-item-content { + padding-left: 0; + + mat-icon { + transform: scale(0.8); + margin-right: 5px; + } + } + } + } +} diff --git a/src/app/components/dialog-load-session/dialog-load-session.component.ts b/src/app/components/dialog-load-session/dialog-load-session.component.ts index 3aa55c836a420dd0966dd5df2ae72687a382911e..b91673e4bfaf924ba1e5350971bf702c7c2f841f 100644 --- a/src/app/components/dialog-load-session/dialog-load-session.component.ts +++ b/src/app/components/dialog-load-session/dialog-load-session.component.ts @@ -17,6 +17,8 @@ export class DialogLoadSessionComponent { public loadSessionForm: FormGroup; + public dependenciesProblems: any[] = []; + constructor( public dialogRef: MatDialogRef<DialogLoadSessionComponent>, private intlService: I18nService, @@ -32,12 +34,84 @@ export class DialogLoadSessionComponent { for (const c of this.calculators) { c.selected = true; } + // re-run dependency checking + this.checkLinkedParamsDependencies(); } public selectNone() { for (const c of this.calculators) { c.selected = false; } + // re-run dependency checking + this.checkLinkedParamsDependencies(); + } + + public checkLinkedParamsDependencies() { + this.dependenciesProblems = []; + // for all checked Nubs + this.calculators.forEach((c) => { + if (c.selected) { + // do all required nubs are checked ? + c.requires.forEach((r) => { + if (! this.isCalculatorOrParentSelected(r)) { + const realUid = this.getUidOrParentUid(r); + const depTitle = this.getTitleFromUid(realUid); + this.dependenciesProblems.push({ + requiring: c.title, + required: depTitle, + requiredUid: realUid, + message: c.title + " " + this.intlService.localizeText("INFO_REQUIRES") + " " + depTitle + }); + } + }); + } + }); + } + + public fixDependencies() { + for (const dp of this.dependenciesProblems) { + this.selectRequiredModule(dp.requiredUid); + } + } + + private isCalculatorOrParentSelected(uid: string): boolean { + let isSelected = false; + this.calculators.forEach((c) => { + if (c.uid === uid || c.children.includes(uid)) { + isSelected = c.selected; + } + }); + return isSelected; + } + + private getUidOrParentUid(uid: string): string { + let realUid: string; + this.calculators.forEach((c) => { + if (c.uid === uid || c.children.includes(uid)) { + realUid = c.uid; + } + }); + return realUid; + } + + private getTitleFromUid(uid: string): string { + let title: string; + this.calculators.forEach((c) => { + if (c.uid === uid) { + title = c.title; + } + }); + return title; + } + + private selectRequiredModule(uid: string) { + this.calculators.forEach((c) => { + if (c.uid === uid) { + c.selected = true; + } + }); + // re-run dependency checking + this.checkLinkedParamsDependencies(); } public onFileSelected(event: any) { @@ -94,4 +168,8 @@ export class DialogLoadSessionComponent { public get uitextLoadSessionTitle() { return this.intlService.localizeText("INFO_DIALOG_LOAD_SESSION_TITLE"); } + + public get uitextFixMissingDependencies() { + return this.intlService.localizeText("INFO_DIALOG_FIX_MISSING_DEPENDENCIES"); + } } diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html index e048b39153d6154fea40eec191fbefd36abab6e6..b87bbe724705e0503c85490c8e4cbdabb19188d8 100644 --- a/src/app/components/field-set/field-set.component.html +++ b/src/app/components/field-set/field-set.component.html @@ -3,7 +3,7 @@ {{ title }} </mat-card-title> <div *ngIf="showButtons" class="hyd-window-btns"> - <button type="button" mat-icon-button (click)="onAddClick()"> + <button type="button" mat-icon-button (click)="onAddClick()" class="add-structure"> <mat-icon>add_box</mat-icon> </button> <button type="button" mat-icon-button [disabled]="! enableRemoveButton" (click)="onRemoveClick()"> @@ -25,7 +25,5 @@ <select-field-line *ngIf="isSelectField(p)" [_select]=p> </select-field-line> - - <check-field-line *ngIf="isCheckField(p)" [check]=p></check-field-line> </ng-template> </mat-card-content> diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts index 170ba7cda872e75c9591c4b2022933f40cec04cf..1541cba64a890778294eeaa5309ece21a2204796 100644 --- a/src/app/components/field-set/field-set.component.ts +++ b/src/app/components/field-set/field-set.component.ts @@ -6,7 +6,6 @@ import { ParamFieldLineComponent } from "../param-field-line/param-field-line.co import { Field } from "../../formulaire/field"; import { InputField } from "../../formulaire/input-field"; import { SelectField } from "../../formulaire/select-field"; -import { CheckField } from "../../formulaire/check-field"; @Component({ selector: "field-set", @@ -172,13 +171,6 @@ export class FieldSetComponent implements DoCheck { return f instanceof SelectField && f.isDisplayed; } - /** - * détermine si un Field est du type CheckField - */ - private isCheckField(f: Field): boolean { - return f instanceof CheckField && f.isDisplayed; - } - /* * gestion des événements clic sur les radios : * réception d'un message du composant enfant (param-field) diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.ts b/src/app/components/fixedvar-results/fixedvar-results.component.ts index d6dca7a41a47100383d7af0cc518c1190b680ad1..3392fb698cf71523bca8cd1a036c9f8380bfb0dc 100644 --- a/src/app/components/fixedvar-results/fixedvar-results.component.ts +++ b/src/app/components/fixedvar-results/fixedvar-results.component.ts @@ -98,6 +98,7 @@ export class FixedVarResultsComponent implements DoCheck { this.resultsGraphComponent.results = undefined; } + // set _doUpdate flag so that results are rebuilt on the next Angular display cycle this._doUpdate = false; if (this._fixedResults !== undefined) { this._doUpdate = this._fixedResults.hasResults || this._fixedResults.hasLog; diff --git a/src/app/components/generic-calculator/calc-name.component.ts b/src/app/components/generic-calculator/calc-name.component.ts index 208d11e9737a1484ac513de75271a67efc88c7fe..b4c109b6f308abd98de68791949468534bf8c2b3 100644 --- a/src/app/components/generic-calculator/calc-name.component.ts +++ b/src/app/components/generic-calculator/calc-name.component.ts @@ -1,6 +1,7 @@ -import { Component, Input } from "@angular/core"; +import { Component } from "@angular/core"; import { GenericInputComponent } from "../generic-input/generic-input.component"; import { FormulaireDefinition } from "../../formulaire/definition/form-definition"; +import { I18nService } from "../../services/internationalisation/internationalisation.service"; @Component({ selector: "calc-name", @@ -11,8 +12,8 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio }) export class CalculatorNameComponent extends GenericInputComponent { - constructor() { - super(null); + constructor(intlService: I18nService) { + super(null, intlService); } /** @@ -39,31 +40,11 @@ export class CalculatorNameComponent extends GenericInputComponent { this.updateAndValidateUI(); } - /** - * valide une valeur de modèle : est ce une valeur acceptable ? (par ex, nombre dans un intervalle, valeur dans une liste, ...) - * @param v valide la valeur du modèle - * @returns isValid : true si la valeur est valide, false sinon - * @returns message : message d'erreur - */ protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg; - let valid = false; - - if (!(typeof (v) === "string") || v.length < 1) { - msg = "Veuillez entrer un nom"; - } else { - valid = true; - } - - return { isValid: valid, message: msg }; + // no model validation for a simple string + return { isValid: true, message: undefined }; } - /** - * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...) - * @param ui valide la valeur saisie - * @returns isValid : true si la valeur est valide, false sinon - * @returns message : message d'erreur - */ protected validateUIValue(ui: string): { isValid: boolean, message: string } { let valid = false; let msg: string; @@ -77,10 +58,9 @@ export class CalculatorNameComponent extends GenericInputComponent { return { isValid: valid, message: msg }; } - /** - * convertit une valeur saisie dans l'UI en valeur affectable au modèle - */ - protected uiToModel(ui: string): any { - return ui; + public updateModelFromUI() { + if (this.validateUI()) { + this.setAndValidateModel(this, this.uiValue); // do NOT cast UI value to Number + } } } diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts index af807c10911ab4ab8b0269dd1066b9cc02d6d9b6..31842b9690c9e477dfd037eebcbe24686421144d 100644 --- a/src/app/components/generic-calculator/calculator.component.ts +++ b/src/app/components/generic-calculator/calculator.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren, QueryList, AfterViewChecked } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; -import { Observer, Session, ParallelStructure } from "jalhyd"; +import { Observer, Session, ParamValueMode } from "jalhyd"; import { FormulaireService } from "../../services/formulaire/formulaire.service"; import { I18nService } from "../../services/internationalisation/internationalisation.service"; @@ -122,7 +122,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, /** * détermine si un FormulaireElement est du type FieldsetContainer */ - private isFieldsetContainer(fe: any): boolean { + public isFieldsetContainer(fe: any): boolean { return fe instanceof FieldsetContainer; } @@ -164,9 +164,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, } ngDoCheck() { - if (this._formulaire !== undefined) { - this.isCalculateDisabled = !(this._formulaire.isValid && this._isUIValid); - } + this.isCalculateDisabled = ! this._isUIValid; } ngOnDestroy() { @@ -192,8 +190,10 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, /* * gestion des événements clic sur les radios */ - private onRadioClick(info: any) { - this.updateLinkedParameters(); + public onRadioClick(info: any) { + if (info.param.valueMode === ParamValueMode.LINK) { + this.updateLinkedParameters(); // only when switching to LINK mode + } this._pendingRadioClick = true; this._pendingRadioClickInfo = info; } @@ -246,11 +246,6 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, } } - private getFieldsetStyleDisplay(id: string) { - const isDisplayed: boolean = this._formulaire.isDisplayed(id); - return isDisplayed ? "block" : "none"; - } - private setForm(f: FormulaireDefinition) { if (this._formulaire !== undefined) { this._formulaire.removeObserver(this); @@ -313,11 +308,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, // accumulator (valeur précédente du résultat) acc, // currentValue (élément courant dans le tableau) - fieldset, - // currentIndex (indice courant dans le tableau) - currIndex, - // array (tableau parcouru) - array + fieldset ) => { return acc && fieldset.isValid; } @@ -331,11 +322,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, // accumulator (valeur précédente du résultat) acc, // currentValue (élément courant dans le tableau) - fieldsetContainer, - // currentIndex (indice courant dans le tableau) - currIndex, - // array (tableau parcouru) - array + fieldsetContainer ) => { return acc && fieldsetContainer.isValid; } @@ -344,25 +331,34 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, } } + public getFieldsetStyleDisplay(id: string) { + const isDisplayed: boolean = this._formulaire.isDisplayed(id); + return isDisplayed ? "block" : "none"; + } + /** * réception d'un événement de validité d'un FieldSetComponent */ - private OnFieldsetValid() { + public OnFieldsetValid() { this.updateUIValidity(); } /** * réception d'un événement de validité d'un FieldsetContainerComponent */ - private onFieldsetContainerValid() { + public onFieldsetContainerValid() { this.updateUIValidity(); } /** * réception d'un événement de changement de valeur d'un input */ - private onInputChange() { - this._formulaire.resetResults(); + public onInputChange() { + this._formulaire.resetResults([]); + } + + public openHelp() { + window.open("assets/docs-fr/calculators/" + this._formulaire.helpLink + "/", "_blank"); } /** @@ -375,10 +371,6 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, return false; } - private openHelp() { - window.open("assets/docs-fr/calculators/" + this._formulaire.helpLink + "/", "_blank"); - } - public saveCalculator() { this.formulaireService.saveForm(this._formulaire); } @@ -386,7 +378,12 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit, public closeCalculator() { const dialogRef = this.confirmCloseCalcDialog.open( DialogConfirmCloseCalcComponent, - { disableClose: true } + { + data: { + uid: this._formulaire.currentNub.uid + }, + disableClose: true + } ); dialogRef.afterClosed().subscribe(result => { if (result) { diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts index 4b34c81133b49c704c6fdb00f2427f0f1f47c1e4..1bac270188ab49a87be7951a421abad277d45947 100644 --- a/src/app/components/generic-input/generic-input.component.ts +++ b/src/app/components/generic-input/generic-input.component.ts @@ -4,6 +4,7 @@ import { BaseComponent } from "../base/base.component"; import { isNumeric } from "jalhyd"; import { FormulaireDefinition } from "../../formulaire/definition/form-definition"; import { NgParameter } from "../../formulaire/ngparam"; +import { I18nService } from "../../services/internationalisation/internationalisation.service"; /** * classe de gestion générique d'un champ de saisie avec titre, validation et message d'erreur @@ -88,7 +89,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC @ViewChild("inputControl") inputField: NgModel; - constructor(private cdRef: ChangeDetectorRef) { + constructor(private cdRef: ChangeDetectorRef, protected intlService: I18nService) { super(); } @@ -129,7 +130,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC } } - private validateUI() { + protected validateUI() { const { isValid, message } = this.validateUIValue(this._uiValue); this._errorMessageUI = message; this.detectChanges(); @@ -182,11 +183,10 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC * événement de changement de la valeur du modèle */ private emitModelChanged() { - // console.log("emit model change", this.constructor.name); this.change.emit({ "action": "model", "value": this.getModelValue() }); } - private setAndValidateModel(sender: any, v: any) { + protected setAndValidateModel(sender: any, v: any) { this.setModelValue(sender, v); this.emitModelChanged(); @@ -205,7 +205,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC * MAJ et validation de l'UI */ protected updateAndValidateUI() { - this._uiValue = this.modelToUI(this.getModelValue()); + this._uiValue = String(this.getModelValue()); this.validateUI(); } @@ -227,7 +227,7 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC */ public updateModelFromUI() { if (this.validateUI()) { - this.setAndValidateModel(this, this.uiToModel(this._uiValue)); + this.setAndValidateModel(this, +this._uiValue); // cast UI value to Number } } @@ -240,7 +240,6 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC * appelé quand les @Input changent */ public ngOnChanges() { - // console.log("ng on changes (generic input)", this.constructor.name); this.updateAll(); } @@ -272,13 +271,6 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC */ protected abstract validateModelValue(v: any): { isValid: boolean, message: string }; - /** - * convertit le modèle en valeur affichable par l'UI - */ - protected modelToUI(v: any): string { - return String(v); - } - /** * valide une valeur saisie dans l'UI (forme de la saisie : est ce bien une date, un nombre, ...) * @param ui saisie à valider @@ -290,113 +282,11 @@ export abstract class GenericInputComponent extends BaseComponent implements OnC let msg: string; if (! isNumeric(ui)) { - msg = "Veuillez entrer une valeur numérique"; + msg = this.intlService.localizeText("ERROR_PARAM_MUST_BE_A_NUMBER"); } else { valid = true; } return { isValid: valid, message: msg }; } - - /** - * convertit une valeur saisie dans l'UI en valeur affectable au modèle - */ - protected uiToModel(ui: string): any { - return +ui; - } -} - -/* - * exemples d'utilisation de GenericInputComponent - */ - -/* -import { Component } from "@angular/core"; - -import { isNumeric, Message } from "jalhyd"; - -// exemple où le modèle est un type simple (number) - -@Component({ - selector: "test-input", - template: `<div class="md-form form-sm"> - <input type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue"> - <label for="form1">{{title}}</label> - <small *ngIf="showError" class="text-danger">{{errorMessage}}</small> -</div>` -}) -export class TestInputComponent extends GenericInputComponent { - constructor() { - super(); - this._model = 0; - } - - protected getModelValue(): any { - return this._model; - } - - protected setModelValue(v: any) { - this._model = v; - } - - protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg = undefined; - let valid = false; - - if (v < 0) - msg = "La valeur n'est pas >= 0 "; - else - valid = true; - - return { isValid: valid, message: msg }; - } - - protected uiToModel(ui: string): any { - return +ui; - } -} - - -// exemple où le modèle est une classe dont on ne gère qu'un membre - -import { ParamDefinition } from "jalhyd"; - -@Component({ - selector: "test2-input", - template: `<div class="md-form form-sm"> - <input type="text" id="form1" class="form-control" [disabled]="isDisabled" [(ngModel)]="uiValue"> - <label for="form1">{{title}}</label> - <small *ngIf="showError" class="text-danger">{{errorMessage}}</small> -</div>` -}) -export class Test2InputComponent extends GenericInputComponent { - constructor() { - super(); - } - - // paramètre géré - private get _param(): ParamDefinition { - return this._model; - } - - protected getModelValue(): any { - return this._param.getValue(); - } - - protected setModelValue(v: any) { - this._param.setValue(v); - } - - protected validateModelValue(v: any): { isValid: boolean, message: string } { - let msg = undefined; - let valid = false; - - if (v < 0) - msg = "La valeur n'est pas >= 0 "; - else - valid = true; - - return { isValid: valid, message: msg }; - } } -/**/ diff --git a/src/app/components/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts index 7e1af55cead345ce13871671d3174e359cfbc07f..2717729d82fb4eb87e2ba8d810d656c9da122167 100644 --- a/src/app/components/ngparam-input/ngparam-input.component.ts +++ b/src/app/components/ngparam-input/ngparam-input.component.ts @@ -29,8 +29,8 @@ export class NgParamInputComponent extends GenericInputComponent implements Obse */ private _tmp: number; - constructor(private intlService: I18nService, cdRef: ChangeDetectorRef) { - super(cdRef); + constructor(intlService: I18nService, cdRef: ChangeDetectorRef) { + super(cdRef, intlService); } /** diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html index a9847d2b272656a448098d22908b4487672e131b..acea333d21ba24a32813148e8aa38eb7147670f6 100644 --- a/src/app/components/param-field-line/param-field-line.component.html +++ b/src/app/components/param-field-line/param-field-line.component.html @@ -10,23 +10,23 @@ <param-computed *ngIf="isRadioCalChecked" [title]="title" [param]="param"></param-computed> <!-- composant pour gérer le cas "paramètre à varier" (min-max/liste de valeurs) --> - <param-values *ngIf="isRadioVarChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-values> + <param-values *ngIf="isRadioVarChecked" [title]="title" [param]="param" (change)="onInputChange($event)" (valid)=onParamValuesValid($event)></param-values> <!-- composant pour gérer le cas "paramètre lié" --> - <param-link *ngIf="isRadioLinkChecked" [title]="title" [param]="param" (valid)=onParamValuesValid($event)></param-link> + <param-link *ngIf="isRadioLinkChecked" [title]="title" [param]="param" (change)="onInputChange($event)" (valid)=onParamValuesValid($event)></param-link> </div> <div class="toggle-group-container" fxFlex="0 0 auto"> <mat-button-toggle-group *ngIf="hasRadioFix() || hasRadioVar() || hasRadioCal() || hasRadioLink()"> <mat-button-toggle class="radio_fix" value="radio_fix" - (click)="onRadioClick('fix')" [checked]="isRadioFixChecked"> + (click)="onRadioClick('fix')" [checked]="isRadioFixChecked" [disabled]="! canExitCalcMode()"> <span fxHide.xxs>{{ uitextParamFixe }}</span> <span fxHide.gt-xxs>F</span> </mat-button-toggle> <mat-button-toggle class="radio_var" value="radio_var" *ngIf="hasRadioVar()" - (click)="onRadioClick('var')" [checked]="isRadioVarChecked"> + (click)="onRadioClick('var')" [checked]="isRadioVarChecked" [disabled]="! canExitCalcMode()"> <span fxHide.xxs>{{ uitextParamVarier }}</span> <span fxHide.gt-xxs>V</span> </mat-button-toggle> @@ -38,7 +38,7 @@ </mat-button-toggle> <mat-button-toggle class="radio_link" value="radio_link" *ngIf="hasRadioLink()" - (click)="onRadioClick('link')" [checked]="isRadioLinkChecked"> + (click)="onRadioClick('link')" [checked]="isRadioLinkChecked" [disabled]="! canExitCalcMode()"> <span fxHide.xxs>{{ uitextParamLie }}</span> <span fxHide.gt-xxs>L</span> </mat-button-toggle> diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts index 435f63cfbf3c00df06a53b986e5008c731abc8d3..06a78419f964544919d962f0dbe3c70903509b9d 100644 --- a/src/app/components/param-field-line/param-field-line.component.ts +++ b/src/app/components/param-field-line/param-field-line.component.ts @@ -4,7 +4,7 @@ import { I18nService } from "../../services/internationalisation/internationalis import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam"; import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component"; import { ServiceFactory } from "../../services/service-factory"; -import { ParamValueMode, CalculatorType, ParallelStructure } from "jalhyd"; +import { ParamValueMode, ParallelStructure, Nub } from "jalhyd"; import { FormulaireService } from "../../services/formulaire/formulaire.service"; import { ParamLinkComponent } from "../param-link/param-link.component"; import { ParamComputedComponent } from "../param-computed/param-computed.component"; @@ -199,14 +199,14 @@ export class ParamFieldLineComponent implements OnChanges { if (this._formService.formulaires.length > 0) { // au moins 2 modules de calcul ouverts if (this._formService.formulaires.length > 1) { - return this._formService.filterLinkableValues(this._formService.getLinkableValues(this.param)).length > 0; + return this._formService.getLinkableValues(this.param).length > 0; } // ou un seul module de calcul "ouvrages parallèles" - if (this._formService.formulaires[0].calculatorType === CalculatorType.ParallelStructure) { - const ps: ParallelStructure = this._formService.formulaires[0].currentNub as ParallelStructure; + if (this._formService.formulaires[0].currentNub instanceof ParallelStructure) { + const ps: ParallelStructure = this._formService.formulaires[0].currentNub; if (ps.structures.length > 1) { - return this._formService.filterLinkableValues(this._formService.getLinkableValues(this.param)).length > 0; + return this._formService.getLinkableValues(this.param).length > 0; } } @@ -214,13 +214,28 @@ export class ParamFieldLineComponent implements OnChanges { return false; } - private onRadioClick(option: string) { + /** + * Returns true if the current parameter is in CALC mode but no SINGLE + * parameter is available to take its place + */ + public canExitCalcMode() { + let ret = true; + if (this.param.paramDefinition.isCalculated) { + const nub = this.param.paramDefinition.parentNub; + const p = nub.findFirstSingleParameter(this.param.paramDefinition); + ret = (p !== undefined); + } + return ret; + } + + public onRadioClick(option: string) { const oldValue = this.param.valueMode; switch (option) { case "fix": this.param.valueMode = ParamValueMode.SINGLE; - // @WTF why do we reset the value here ? - this.param.setValue(this, this.param.paramDefinition.paramValues.singleValue); + // reset the value to avoid "undefined" after exiting CALC or LINK mode + // @TODO not always necessary; find out why + this.param.setValue(this, this.param.paramDefinition.singleValue); break; case "var": @@ -236,7 +251,7 @@ export class ParamFieldLineComponent implements OnChanges { break; case "cal": - this.param.valueMode = ParamValueMode.CALCUL; + this.param.setCalculated(); // sets mode to CALCUL and more break; case "link": @@ -277,7 +292,7 @@ export class ParamFieldLineComponent implements OnChanges { /** * réception d'un événement de validité de ParamValuesComponent */ - private onParamValuesValid(event: boolean) { + public onParamValuesValid(event: boolean) { this._isRangeValid = event; this.emitValidity(); } diff --git a/src/app/components/param-link/param-link.component.html b/src/app/components/param-link/param-link.component.html index c1a76465e33645de39ec1e72745b402a1f0bfc9d..9f8fce8bf91dae36d2053e6e08df9389756e4552 100644 --- a/src/app/components/param-link/param-link.component.html +++ b/src/app/components/param-link/param-link.component.html @@ -5,9 +5,6 @@ {{ selectItemLabel(e) }} </mat-option> </mat-select> - <mat-error> - {{ message }} - </mat-error> </mat-form-field> <mat-icon #tooltip="matTooltip" [matTooltip]="tooltipText" matTooltipClass="linked-param-tooltip" diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts index a382729a442cb38418794b05b04206a723860bce..7c3e8887e091705db9277d4bf60ac516f667322b 100644 --- a/src/app/components/param-link/param-link.component.ts +++ b/src/app/components/param-link/param-link.component.ts @@ -2,10 +2,11 @@ import { Component, Input, Output, EventEmitter, OnChanges, OnDestroy } from "@a import { NgParameter } from "../../formulaire/ngparam"; import { ServiceFactory } from "../../services/service-factory"; -import { ParamValueMode, Observer, Structure, ParallelStructure } from "jalhyd"; +import { LinkedValue, ParamValueMode, Observer, Structure } from "jalhyd"; import { FormulaireService } from "../../services/formulaire/formulaire.service"; import { I18nService } from "../../services/internationalisation/internationalisation.service"; import { FormulaireDefinition } from "../../formulaire/definition/form-definition"; +import { sprintf } from "sprintf-js"; @Component({ selector: "param-link", @@ -26,32 +27,18 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { private valid: EventEmitter<boolean>; /** - * indice actuel du paramètre sélectionné dans la liste + * événement signalant un changement de valeur du modèle + * @TODO l'utiliser aussi pour le changement de validité à + * la place de this.valid, comme dans GenericInputComponent */ - private _currentIndex = -1; + @Output() + protected change = new EventEmitter<any>(); - /** - * message affiché à côté du select des paramètres - */ - private _message: string; + /** indice actuel du paramètre sélectionné dans la liste */ + private _currentIndex = -1; - /** - * liste des paramètres liables sous la forme - * {"name":<étiquette>, "value":<valeur liable>, "nub":<Nub d'origine du paramètre>, "formTitle":<nom du module de calcul lié au nub>} - * - * l'étiquette "name" (cf. INubReference.defineReference dans jalhyd) est de la forme <n | ouvrage[n] | N1>[.[N2]] - * n : indice de de l'ouvrage dans le cas des ouvrages parallèles - * N1 : un nom de paramètre/résultat (dans le cas d'un résultat, il est suivi d'un point) - * N2 : nom de résultat complémentaire (optionnel) - * ex : - * Q, Z1 (paramètres) - * J. (résultat) - * .Yf (résultat complémentaire du résultat courant) - * Q.Yf (résultat complémentaire du résultat nommé "Q") - * 0.Q : paramètre Q du 1er ouvrage (ouvrages parallèles) - * ouvrage[1].Q_Mode : résultat complémentaire du 2ème ouvrage (ouvrages parallèles) - */ - private _linkableParams: any[]; + /** liste des paramètres liables */ + private _linkableParams: LinkedValue[]; private _formService: FormulaireService; @@ -71,24 +58,21 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { return this._linkableParams; } - public get message() { - return this._message; - } - public get label() { return this.intlService.localizeText("INFO_PARAMFIELD_PARAMLIE_LABEL"); } - public set currentLinkedParam(p: any) { + public set currentLinkedParam(p: LinkedValue) { for (let i = 0; i < this._linkableParams.length; i++) { - if (this._linkableParams[i].nub.uid === p.nub.uid) { + const li = this._linkableParams[i]; + if (li.equals(p)) { this.linkTo(i); break; } } } - public get currentLinkedParam() { + public get currentLinkedParam(): LinkedValue { if (this._linkableParams !== undefined) { if (this._currentIndex !== -1 && this._currentIndex < this._linkableParams.length) { return this._linkableParams[this._currentIndex]; @@ -117,65 +101,93 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { } /** - * attribut "label" d'une entrée du select des paramètres + * Label d'une entrée du select des paramètres liés */ - private selectItemLabel(i: any): string { - const s = i.name; // nom associé au paramètre/à la valeur - const c = i.formTitle; // nom du module de calcul + private selectItemLabel(i: LinkedValue): string { + const s = i.symbol; // nom associé au paramètre/à la valeur + const c = i.meta["formTitle"]; // nom du module de calcul - // pointeur direct vers une Structure dans un Nub de type parallèle - if (i.nub && i.nub instanceof Structure) { + // 1. Paramètre / résultat d'un ouvrage dans un Nub de type parallèle + if (i.nub instanceof Structure) { let p: number; p = i.nub.findPositionInParent(); - // forme xxx. (résultat) - const re4 = /([^\.]+)\.$/; - const match4 = re4.exec(s); - if (match4 !== null) { + if (i.isResult()) { // résultat d'ouvrage - return `${match4[1]} (résultat de ${c}, ouvrage ${p + 1})`; + return sprintf( + this.intlService.localizeText("INFO_LINKED_VALUE_DEVICE_RESULT"), + s, c, (p + 1) + ); } else { // paramètre d'ouvrage - return `${s} (${c}, ouvrage ${p + 1})`; + return sprintf( + this.intlService.localizeText("INFO_LINKED_VALUE_DEVICE"), + s, c, (p + 1) + ); } + } else + // 2. Résultat + if (i.isResult()) { + return sprintf( + this.intlService.localizeText("INFO_LINKED_VALUE_RESULT"), + s, c + ); + } else + // 3. Résultat complémentaire + if (i.isExtraResult()) { + if (i.meta["result"]) { + // @TODO not used ? + return sprintf( + this.intlService.localizeText("INFO_LINKED_VALUE_EXTRA_RESULT_OF"), + s, c, i.meta["result"] + ); + } else { + return sprintf( + this.intlService.localizeText("INFO_LINKED_VALUE_EXTRA_RESULT"), + s, c + ); + } + } else { + // 4. Paramètre (cas général) + return `${s} (${c})`; } - - const re1 = /([^\.]+)\.$/; // forme xxx. (résultat) - const match1 = re1.exec(s); - if (match1 !== null) { - return `${match1[1]} (résultat de ${c})`; - } - - const re2 = /([^\.]+)\.(.+)/; // forme xxx.yyy (résultat complémentaire) - const match2 = re2.exec(s); - if (match2 !== null) { - return `${match2[2]} (${c}, résultat complémentaire de ${match2[1]})`; - } - - const re3 = /^\.(.+)/; // forme .xxx (résultat complémentaire) - const match3 = re3.exec(s); - if (match3 !== null) { - return `${match3[1]} (${c}, résultat complémentaire)`; - } - - return `${s} (${c})`; // forme simple (paramètre) } /** - * lie le paramètre géré à un des paramètres liables de la liste + * lie le paramètre géré à un des paramètres liables de la liste; appelé + * systématiquement lorsqu'on construit le formulaire, même si le + * paramètre est déjà lié * @param index indice dans la liste */ private linkTo(index: number) { if (this._currentIndex !== index) { this._currentIndex = index; const lp = this._linkableParams[index]; - this.param.linkToParameter(lp.nub, lp.name); + this.param.linkToValue(lp); + // reset form results when a new target is selected + this.emitModelChanged(); + } + } + + /** + * événement de changement de la valeur du modèle + */ + private emitModelChanged() { + let value; + try { + value = this.currentLinkedParam.getValue(); + } catch (e) { + // console.log("undefined target value (pending calculation)"); } + this.change.emit({ + "action": "model", + "value": value + }); } public updateParamList() { // liste des paramètres liables if (this.param.valueMode === ParamValueMode.LINK) { - this._linkableParams = this._formService.filterLinkableValues(this._formService.getLinkableValues(this.param)); + this._linkableParams = this._formService.getLinkableValues(this.param); } else { this._linkableParams = []; } @@ -187,8 +199,8 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { if (this.param.valueMode === ParamValueMode.LINK && this._linkableParams !== undefined) { let i = 0; for (const e of this._linkableParams) { - if (this.param.paramDefinition.paramValues.referencedNub - && e.nub.uid === this.param.paramDefinition.paramValues.referencedNub["_uid"]) { + const refValue = this.param.paramDefinition.referencedValue; + if (refValue && e.equals(refValue)) { this._currentIndex = i; break; } else { @@ -198,10 +210,10 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { } this.linkTo(Math.max(this._currentIndex, 0)); // might still be -1 } - this._message = undefined; } else { this._currentIndex = -1; - this._message = "Aucun paramètre compatible trouvé"; + // back to SINGLE mode by default + this.param.valueMode = ParamValueMode.SINGLE; } } @@ -234,12 +246,12 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy { if ( lp.nub.uid === f.currentNub.uid || ( // repeatable structure inside parent module - lp.nub.parent instanceof ParallelStructure + lp.nub instanceof Structure && lp.nub.parent.uid === f.currentNub.uid ) ) { // update <select> option - lp.formTitle = data.value; + lp.meta["formTitle"] = data.value; } } break; diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts index d9020c604a97d39c8819f8ade23705c5dd447afe..95c9e3c71a6a8a95a7399f84c05c1bd35e04644c 100644 --- a/src/app/components/param-values/param-values.component.ts +++ b/src/app/components/param-values/param-values.component.ts @@ -1,10 +1,8 @@ -import { Component, Input, AfterViewInit } from "@angular/core"; +import { Component, Input, AfterViewInit, Output, EventEmitter } from "@angular/core"; import { NgParameter } from "../../formulaire/ngparam"; import { DialogEditParamValuesComponent } from "../dialog-edit-param-values/dialog-edit-param-values.component"; import { MatDialog } from "@angular/material"; -import { ParamValueMode } from "jalhyd"; -import { I18nService } from "../../services/internationalisation/internationalisation.service"; -import { ApplicationSetupService } from "../../services/app-setup/app-setup.service"; +import { ParamValueMode, Observer } from "jalhyd"; @Component({ selector: "param-values", @@ -13,7 +11,7 @@ import { ApplicationSetupService } from "../../services/app-setup/app-setup.serv "./param-values.component.scss" ] }) -export class ParamValuesComponent implements AfterViewInit { +export class ParamValuesComponent implements AfterViewInit, Observer { @Input() public param: NgParameter; @@ -21,11 +19,14 @@ export class ParamValuesComponent implements AfterViewInit { @Input() public title: string; + /** + * événement signalant un changement de valeur du modèle + */ + @Output() + protected change = new EventEmitter<any>(); constructor( - private editValuesDialog: MatDialog, - private intlService: I18nService, - private appSetupService: ApplicationSetupService + private editValuesDialog: MatDialog ) { } public get isMinMax() { @@ -62,5 +63,26 @@ export class ParamValuesComponent implements AfterViewInit { this.openDialog(); }); } + // subscribe to parameter values change (through dialog actions) + this.param.addObserver(this); } + + /** + * événement de changement de la valeur du modèle + */ + private emitModelChanged() { + this.change.emit({ "action": "model", "value": this.param.getValue() }); + } + + public update(sender: any, data: any): void { + if (sender instanceof DialogEditParamValuesComponent) { + switch (data.action) { + case "ngparamAfterValue": + // tell the form to clear the results + this.emitModelChanged(); + break; + } + } + } + } diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts index d62a814e06bf032b3120d17d9a4058c5cb91096c..a59ea0c96e29d13db57292e22e0e3674d2c2f131 100644 --- a/src/app/components/remous-results/remous-results.component.ts +++ b/src/app/components/remous-results/remous-results.component.ts @@ -350,7 +350,7 @@ export class RemousResultsComponent implements DoCheck { } private get abscisseIterator(): INumberIterator { - return this._remousResults.varResults.variatedParameter.paramDefinition.paramValues.getValuesIterator(); + return this._remousResults.varResults.variatedParameter.paramDefinition.valuesIterator; } private connectRessaut(lineFlu: LineData, lineTor: LineData) { diff --git a/src/app/components/section-results/section-results.component.ts b/src/app/components/section-results/section-results.component.ts index 34248eae2ec6dd6791f074dfb6a5397add5867df..40eb93801e81de9b4babf83ebb273ae8d69450bd 100644 --- a/src/app/components/section-results/section-results.component.ts +++ b/src/app/components/section-results/section-results.component.ts @@ -6,7 +6,6 @@ import { SectionCanvasComponent } from "../section-canvas/section-canvas.compone import { SectionResults } from "../../results/section-results"; import { ApplicationSetupService } from "../../services/app-setup/app-setup.service"; import { CalculatorResults } from "../../results/calculator-results"; -import { FixedResults } from "../../results/fixed-results"; @Component({ selector: "section-results", diff --git a/src/app/formulaire/check-field.ts b/src/app/formulaire/check-field.ts deleted file mode 100644 index 073816613239ed3eea16a1b7d68aa8ba4b7b89d3..0000000000000000000000000000000000000000 --- a/src/app/formulaire/check-field.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Field } from "./field"; -import { FormulaireNode } from "./formulaire-node"; - -export class CheckField extends Field { - private _value: boolean; - - constructor(parent: FormulaireNode) { - super(parent); - this._value = false; - } - - public getValue(): boolean { - return this._value; - } - - public setValue(val: boolean) { - this._value = val; - } - - public get isValid(): boolean { - return true; - } - - public parseConfig(json: {}, data?: {}) { - this._confId = json["id"]; - this.setValue(json["value"] === "true"); - } -} diff --git a/src/app/formulaire/definition/concrete/form-base.ts b/src/app/formulaire/definition/concrete/form-base.ts index 6b28a0a2f5c228bfe4b62389295b7470d6ab58d3..f1afa17ea1f9be2b22cbc48e1d3ec06b886f4650 100644 --- a/src/app/formulaire/definition/concrete/form-base.ts +++ b/src/app/formulaire/definition/concrete/form-base.ts @@ -1,24 +1,25 @@ -import { FormDefFixedVar } from "../form-def-fixedvar"; -import { FormResultFixedVar } from "../form-result-fixedvar"; import { FormulaireDefinition } from "../form-definition"; import { CalculatorResults } from "../../../results/calculator-results"; import { FormDefParamToCalculate } from "../form-def-paramcalc"; +import { FormResult } from "../form-result"; +import { FormCompute } from "../form-compute"; +import { FormResultFixedVar } from "../form-result-fixedvar"; import { FormComputeFixedVar } from "../form-compute-fixedvar"; +import { ServiceFactory } from "../../../services/service-factory"; export class FormulaireBase extends FormulaireDefinition { - private _formParamCalc: FormDefParamToCalculate; + protected _formParamCalc: FormDefParamToCalculate; - private _formCompute: FormComputeFixedVar; + protected _formCompute: FormCompute; - private _formResult: FormResultFixedVar; + protected _formResult: FormResult; constructor() { super(); - // this._formFixedVar = new FormDefFixedVar(this); this._formParamCalc = new FormDefParamToCalculate(this); this._formResult = new FormResultFixedVar(this, false); - this._formCompute = new FormComputeFixedVar(this, this._formResult); + this._formCompute = new FormComputeFixedVar(this, (this._formResult as FormResultFixedVar)); } protected completeParse(json: {}) { @@ -26,15 +27,17 @@ export class FormulaireBase extends FormulaireDefinition { } /** - * gestion du clic sur les radios "paramètre fixé, à varier, à calculer" + * Resets the form results, the results panel on screen, the model + * results, and does the same for all depending modules */ - public onRadioClick(info: string) { - super.onRadioClick(info); - this._formParamCalc.onRadioClick(info); - } - - public resetResults() { + public resetResults(visited: string[] = []) { + visited.push(this.currentNub.uid); + // reset GUI results this._formResult.resetResults(); + // reset model results + this.currentNub.resetResult(); + // reset the result panels of all forms depending on this one + ServiceFactory.instance.formulaireService.resetAllDependingFormsResults(this, visited); } public doCompute() { diff --git a/src/app/formulaire/definition/concrete/form-courbe-remous.ts b/src/app/formulaire/definition/concrete/form-courbe-remous.ts index e65fc2e3b4f99018c1458281f0b282f336fafb0c..99be68efbe0f6056950e05f519570a271993fbaf 100644 --- a/src/app/formulaire/definition/concrete/form-courbe-remous.ts +++ b/src/app/formulaire/definition/concrete/form-courbe-remous.ts @@ -3,16 +3,12 @@ import { IObservable, MethodeResolution } from "jalhyd"; import { FormResultRemous } from "../form-result-remous"; import { FormDefSection } from "../form-def-section"; import { FormComputeCourbeRemous } from "../form-compute-courbe-remous"; -import { FormulaireDefinition } from "../form-definition"; -import { CalculatorResults } from "../../../results/calculator-results"; import { FieldSet } from "../../fieldset"; +import { FormulaireBase } from "./form-base"; -export class FormulaireCourbeRemous extends FormulaireDefinition { - private _formSection: FormDefSection; - - private _formCompute: FormComputeCourbeRemous; +export class FormulaireCourbeRemous extends FormulaireBase { - private _formResult: FormResultRemous; + private _formSection: FormDefSection; /** * id du select configurant la méthode de résolution @@ -23,7 +19,7 @@ export class FormulaireCourbeRemous extends FormulaireDefinition { super(); this._formSection = new FormDefSection(this); this._formResult = new FormResultRemous(this); - this._formCompute = new FormComputeCourbeRemous(this, this._formSection, this._formResult); + this._formCompute = new FormComputeCourbeRemous(this, this._formSection, (this._formResult as FormResultRemous)); // default properties this._props["methodeResolution"] = MethodeResolution.Trapezes; this._props["varCalc"] = undefined; // important @@ -50,27 +46,12 @@ export class FormulaireCourbeRemous extends FormulaireDefinition { } } - public resetResults() { - this._formResult.resetResults(); - // prévenir les composants qu'il faut détecter les changements - this.notifyReset(); - } - - public doCompute() { - this._formCompute.doCompute(); - } - - public get hasResults(): boolean { - return this._formResult.hasResults; - } - - public get results(): CalculatorResults[] { - return this._formResult.results; - } - // interface Observer update(sender: IObservable, data: any) { + + super.update(sender, data); + if (sender instanceof FieldSet && data.action === "propertyChange") { switch (sender.id) { case "fs_section": diff --git a/src/app/formulaire/definition/concrete/form-lechapt-calmon.ts b/src/app/formulaire/definition/concrete/form-lechapt-calmon.ts index 69124f6461849b5fb874de3a0806496a761876e4..db299120666ff8b777715e0a25fe388e005c912a 100644 --- a/src/app/formulaire/definition/concrete/form-lechapt-calmon.ts +++ b/src/app/formulaire/definition/concrete/form-lechapt-calmon.ts @@ -2,9 +2,17 @@ import { Observer } from "jalhyd"; import { SelectField } from "../../select-field"; import { FormulaireBase } from "./form-base"; import { NgParamInputComponent } from "../../../components/ngparam-input/ngparam-input.component"; +import { FormResultFixedVar } from "../form-result-fixedvar"; +import { FormComputeFixedVar } from "../form-compute-fixedvar"; export class FormulaireLechaptCalmon extends FormulaireBase implements Observer { + constructor() { + super(); + this._formResult = new FormResultFixedVar(this, false); + this._formCompute = new FormComputeFixedVar(this, (this._formResult as FormResultFixedVar)); + } + protected completeParse(json: {}) { super.completeParse(json); // abonnement au changement de valeur du select de matériau @@ -18,6 +26,9 @@ export class FormulaireLechaptCalmon extends FormulaireBase implements Observer // interface Observer public update(sender: any, data: any) { + + super.update(sender, data); + // en cas de changement de valeur du select de matériau, effacement des résultats et MAJ des champs L,M,N if (sender instanceof SelectField) { if (data.action === "select") { diff --git a/src/app/formulaire/definition/concrete/form-parallel-structures.ts b/src/app/formulaire/definition/concrete/form-parallel-structures.ts index 2f3f6bd410c07cd00463391ab33f025f9a8a3a75..ec840a97075a5039d7851a883607fb9cb9346d2c 100644 --- a/src/app/formulaire/definition/concrete/form-parallel-structures.ts +++ b/src/app/formulaire/definition/concrete/form-parallel-structures.ts @@ -1,8 +1,5 @@ import { Structure, Nub, ParallelStructure, StructureType, LoiDebit, StructureProperties, Props, Session } from "jalhyd"; -import { FormulaireDefinition } from "../form-definition"; -import { CalculatorResults } from "../../../results/calculator-results"; -import { FormDefParamToCalculate } from "../form-def-paramcalc"; import { FormDefParallelStructures } from "../form-def-parallel-structures"; import { FormComputeParallelStructures } from "../form-compute-parallel-structures"; import { FormResultFixedVar } from "../form-result-fixedvar"; @@ -12,19 +9,12 @@ import { SelectField } from "../../select-field"; import { NgParameter } from "../../ngparam"; import { FieldsetTemplate } from "../../fieldset-template"; import { FormulaireNode } from "../../formulaire-node"; +import { FormulaireBase } from "./form-base"; -export class FormulaireParallelStructure extends FormulaireDefinition { - - // private _formFixedVar: FormDefFixedVar; +export class FormulaireParallelStructure extends FormulaireBase { private _formParallelStruct: FormDefParallelStructures; - private _formParamCalc: FormDefParamToCalculate; - - private _formCompute: FormComputeParallelStructures; - - private _formResult: FormResultFixedVar; - /** * id du select configurant le type d'ouvrage */ @@ -32,11 +22,9 @@ export class FormulaireParallelStructure extends FormulaireDefinition { constructor() { super(); - // this._formFixedVar = new FormDefFixedVar(this); - this._formParamCalc = new FormDefParamToCalculate(this); this._formResult = new FormResultFixedVar(this, false); this._formParallelStruct = new FormDefParallelStructures(); - this._formCompute = new FormComputeParallelStructures(this, this._formParallelStruct, this._formResult); + this._formCompute = new FormComputeParallelStructures(this, this._formParallelStruct, (this._formResult as FormResultFixedVar)); } private createStructNub(templ: FieldsetTemplate): Nub { @@ -168,14 +156,12 @@ export class FormulaireParallelStructure extends FormulaireDefinition { } public removeFieldset(fs: FieldSet) { - // console.log("==> FormParallelStructures removeFieldset", fs.uid); if (fs.nub instanceof Structure) { // suppression du sous-nub dans le Nub parent this.deleteNub(fs.nub); // suppression du fieldset this.fieldsetContainer.removeFieldset(fs); - // console.log("====> nb struct dans le parent après", (this.currentNub as ParallelStructure).structures.length); this.resetResults(); } else { super.removeFieldset(fs); } @@ -186,35 +172,6 @@ export class FormulaireParallelStructure extends FormulaireDefinition { this.subscribeFieldsetContainer(); } - /** - * gestion du clic sur les radios "paramètre fixé, à varier, à calculer" - */ - public onRadioClick(info: string) { - super.onRadioClick(info); - this._formParamCalc.onRadioClick(info); - } - - /** - * @return une chaîne représentant le "contexte" courant (ici, combinaison type d'ouvrage-loi de débit) - * @param fs FieldSet contenant les listes déroulantes type d'ouvrage et loi de débit - */ - - public resetResults() { - this._formResult.resetResults(); - } - - public doCompute() { - this._formCompute.doCompute(); - } - - public get hasResults(): boolean { - return this._formResult.hasResults; - } - - public get results(): CalculatorResults[] { - return this._formResult.results; - } - private get fieldsetContainer(): FieldsetContainer { const n = this.getFormulaireNodeById("struct_container"); if (n === undefined || !(n instanceof FieldsetContainer)) { @@ -381,6 +338,9 @@ export class FormulaireParallelStructure extends FormulaireDefinition { // interface Observer public update(sender: any, data: any) { + + super.update(sender, data); + if (sender instanceof FieldsetContainer) { switch (data.action) { case "newFieldset": diff --git a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts index 7e607af99ce8f91d2834f7575732ddef14479ede..3d846e31d8bfaebe117edd59cfcd77d1f0076d97 100644 --- a/src/app/formulaire/definition/concrete/form-regime-uniforme.ts +++ b/src/app/formulaire/definition/concrete/form-regime-uniforme.ts @@ -1,31 +1,19 @@ -import { FormDefFixedVar } from "../form-def-fixedvar"; import { IObservable, Observer } from "jalhyd"; import { FormResultFixedVar } from "../form-result-fixedvar"; -import { FormulaireDefinition } from "../form-definition"; import { FormDefSection } from "../form-def-section"; -import { CalculatorResults } from "../../../results/calculator-results"; -import { FormDefParamToCalculate } from "../form-def-paramcalc"; import { FieldSet } from "../../fieldset"; import { FormComputeFixedVar } from "../form-compute-fixedvar"; +import { FormulaireBase } from "./form-base"; -export class FormulaireRegimeUniforme extends FormulaireDefinition implements Observer { - private _formFixedVar: FormDefFixedVar; - - private _formParamCalc: FormDefParamToCalculate; +export class FormulaireRegimeUniforme extends FormulaireBase implements Observer { private _formSection: FormDefSection; - private _formCompute: FormComputeFixedVar; - - private _formResult: FormResultFixedVar; - constructor() { super(); - this._formFixedVar = new FormDefFixedVar(this); - this._formParamCalc = new FormDefParamToCalculate(this); this._formSection = new FormDefSection(this); this._formResult = new FormResultFixedVar(this, true); - this._formCompute = new FormComputeFixedVar(this, this._formResult); + this._formCompute = new FormComputeFixedVar(this, (this._formResult as FormResultFixedVar)); } protected parseOptions(json: {}) { @@ -42,43 +30,17 @@ export class FormulaireRegimeUniforme extends FormulaireDefinition implements Ob super.completeParse(json); } - /** - * gestion du clic sur les radios "paramètre fixé, à varier, à calculer" - */ - public onRadioClick(info: string) { - super.onRadioClick(info); - this._formParamCalc.onRadioClick(info); - } - - public resetResults() { - this._formResult.resetResults(); - } - - public doCompute() { - this._formCompute.doCompute(); - } - - public get hasResults(): boolean { - return this._formResult.hasResults; - } - - public get results(): CalculatorResults[] { - return this._formResult.results; - } - - public reset() { - super.reset(); - } - // interface Observer update(sender: IObservable, data: any) { + + super.update(sender, data); + // changement de propriété du FieldSet contenant le select de choix du type de section if (sender instanceof FieldSet && sender.id === "fs_section" && data.action === "propertyChange") { this.replaceCurrentNub(sender.properties); for (const fs of this.allFieldsets) { fs.setNub(this._currentNub); - this._formParamCalc.setDefault(); // treat the fieldset as new to re-subscribe to Nub properties change events this.afterParseFieldset(fs); } diff --git a/src/app/formulaire/definition/concrete/form-section-parametree.ts b/src/app/formulaire/definition/concrete/form-section-parametree.ts index 04508ccaffacc47426b4077ef424b9bed31a01c9..1a3ef62b123e5dbdd21200d20c06966c34da069f 100644 --- a/src/app/formulaire/definition/concrete/form-section-parametree.ts +++ b/src/app/formulaire/definition/concrete/form-section-parametree.ts @@ -3,26 +3,18 @@ import { IObservable } from "jalhyd"; import { FormResultSection } from "../form-result-section"; import { FormDefSection } from "../form-def-section"; import { FormComputeSectionParametree } from "../form-compute-section-parametree"; -import { FormulaireDefinition } from "../form-definition"; -import { CalculatorResults } from "../../../results/calculator-results"; -import { FormDefFixedVar } from "../form-def-fixedvar"; import { FieldSet } from "../../fieldset"; +import { FormulaireBase } from "./form-base"; -export class FormulaireSectionParametree extends FormulaireDefinition { - private _formFixedVar: FormDefFixedVar; +export class FormulaireSectionParametree extends FormulaireBase { private _formSection: FormDefSection; - private _formCompute: FormComputeSectionParametree; - - private _formSectionResult: FormResultSection; - constructor() { super(); - this._formFixedVar = new FormDefFixedVar(this); this._formSection = new FormDefSection(this); - this._formSectionResult = new FormResultSection(this, this._formSection); - this._formCompute = new FormComputeSectionParametree(this, this._formSection, this._formSectionResult); + this._formResult = new FormResultSection(this, this._formSection); + this._formCompute = new FormComputeSectionParametree(this, this._formSection, (this._formResult as FormResultSection)); // default properties this._props["varCalc"] = "Hs"; } @@ -36,33 +28,12 @@ export class FormulaireSectionParametree extends FormulaireDefinition { this._formSection.afterParseFieldset(fs); } - /** - * gestion du clic sur les radios "paramètre fixé, à varier, à calculer" - */ - public onRadioClick(info: string) { - super.onRadioClick(info); - this._formFixedVar.onRadioClick(info); - } - - public resetResults() { - this._formSectionResult.resetResults(); - } - - public doCompute() { - this._formCompute.doCompute(); - } - - public get hasResults(): boolean { - return this._formSectionResult.hasResults; - } - - public get results(): CalculatorResults[] { - return this._formSectionResult.results; - } - // interface Observer update(sender: IObservable, data: any) { + + super.update(sender, data); + // changement de propriété du FieldSet contenant le select de choix du type de section if (sender instanceof FieldSet && data.action === "propertyChange") { switch (sender.id) { diff --git a/src/app/formulaire/definition/form-compute-courbe-remous.ts b/src/app/formulaire/definition/form-compute-courbe-remous.ts index dc3a6a2e26789f4c9f2431fbb663e1150b4fbd81..b69986e83c954b0795184a17145b424b6c3e7958 100644 --- a/src/app/formulaire/definition/form-compute-courbe-remous.ts +++ b/src/app/formulaire/definition/form-compute-courbe-remous.ts @@ -7,6 +7,11 @@ import { FormCompute } from "./form-compute"; import { FormResultRemous } from "./form-result-remous"; export class FormComputeCourbeRemous extends FormCompute { + + private resultYn: Result; + + private resultYc: Result; + constructor(formBase: FormulaireDefinition, private _formSection: FormDefSection, formResult: FormResultRemous) { super(formBase, formResult); } @@ -21,8 +26,15 @@ export class FormComputeCourbeRemous extends FormCompute { const prmCR: CourbeRemousParams = cr.prms as CourbeRemousParams; const sect: acSection = prmCR.Sn; - const Yn: Result = sect.Calc("Yn"); // hauteur normale - const Yc: Result = sect.Calc("Yc"); // hauteur critique + this.resultYn = sect.Calc("Yn"); // hauteur normale + this.resultYc = sect.Calc("Yc"); // hauteur critique + + this.reaffectResultComponents(); + } + + protected reaffectResultComponents() { + const cr: CourbeRemous = this._formBase.currentNub as CourbeRemous; + const prmCR: CourbeRemousParams = cr.prms as CourbeRemousParams; this.remousResults.parameters = prmCR; @@ -33,8 +45,8 @@ export class FormComputeCourbeRemous extends FormCompute { this.remousResults.result = cr.calculRemous(this.remousResults.extraParamSymbol); // données du graphe - this.remousResults.hauteurNormale = Yn.resultElement; - this.remousResults.hauteurCritique = Yc.resultElement; + this.remousResults.hauteurNormale = this.resultYn.resultElement; + this.remousResults.hauteurCritique = this.resultYc.resultElement; if (this.remousResults.extraParamSymbol) { this.remousResults.extraGraph = ["Hs", "Hsc", "Yf", "Yt", "Yco"].indexOf(this.remousResults.extraParamSymbol) === -1; } else { diff --git a/src/app/formulaire/definition/form-compute-fixedvar.ts b/src/app/formulaire/definition/form-compute-fixedvar.ts index 151b875bfbae1e4c4178b8c09b228b54e2febc7f..205e9e9dc902ca3dda27fc0950ed57abe193d65f 100644 --- a/src/app/formulaire/definition/form-compute-fixedvar.ts +++ b/src/app/formulaire/definition/form-compute-fixedvar.ts @@ -1,4 +1,4 @@ -import { Nub, Result, ComputeNode, ParamValueMode } from "jalhyd"; +import { Nub, Result, ComputeNode } from "jalhyd"; import { FormCompute } from "./form-compute"; import { NgParameter, ParamRadioConfig } from "../ngparam"; @@ -45,22 +45,29 @@ export class FormComputeFixedVar extends FormCompute { protected compute() { const nub: Nub = this._formBase.currentNub; const computedParam: NgParameter = this.getComputedParameter(); + + const res: Result = this.runNubCalc(nub); + + this.reaffectResultComponents(); + } + + protected reaffectResultComponents() { + const nub: Nub = this._formBase.currentNub; + const computedParam: NgParameter = this.getComputedParameter(); + this.formResult.resetResults(); // to avoid adding fixed parameters more than once (see below) this.formResult.addFixedParameters(); const varParam: NgParameter = this.getVariatedParameter(); if (varParam === undefined) { // pas de paramètre à varier - const res: Result = this.runNubCalc(nub, computedParam); - this.formResult.fixedResults.result = res; + this.formResult.fixedResults.result = nub.result; this.formResult.fixedResults.calculatedParameter = computedParam; } else { // il y a un paramètre à varier - const res: Result = this.runNubCalc(nub, computedParam); - this.formResult.varResults.variatedParameter = varParam; this.formResult.varResults.calculatedParameter = computedParam; - this.formResult.varResults.result = res; + this.formResult.varResults.result = nub.result; this.formResult.varResults.update(false); } } diff --git a/src/app/formulaire/definition/form-compute-parallel-structures.ts b/src/app/formulaire/definition/form-compute-parallel-structures.ts index 9daa4cbd6cf61b2db18eb0e1173ee28dfc3ed49d..8f169e5e3e04cfb4ef366ed472fde9739a7377c2 100644 --- a/src/app/formulaire/definition/form-compute-parallel-structures.ts +++ b/src/app/formulaire/definition/form-compute-parallel-structures.ts @@ -1,4 +1,4 @@ -import { ComputeNode, ParallelStructure } from "jalhyd"; +import { ComputeNode, ParallelStructure, Structure, ParamDefinition } from "jalhyd"; import { FormComputeFixedVar } from "./form-compute-fixedvar"; import { FormResultFixedVar } from "./form-result-fixedvar"; @@ -38,15 +38,19 @@ export class FormComputeParallelStructures extends FormComputeFixedVar { } /** - * construit un identifiant de type "n.X" avec "n" l'index de l'ouvrage auquel appartient le paramètre et "X" son symbole + * construit un identifiant de type { uid: "abcdef", symbol: "X" } + * avec "abcdef" l'index de l'ouvrage et "X" son paramètre */ - protected getParameterRefid(p: NgParameter) { - const [fsc, fs, i] = this.structureParents(p); - if (i === -1) { + protected getParameterRefid(p: ParamDefinition): any { + const nub = p.parentComputeNode; + if (nub instanceof Structure) { + return { + uid: nub.uid, + symbol: p.symbol + }; + } else { return super.getParameterRefid(p); } - - return `${i}.${p.symbol}`; } protected setParameterValue(node: ComputeNode, p: NgParameter, val: number) { diff --git a/src/app/formulaire/definition/form-compute-section-parametree.ts b/src/app/formulaire/definition/form-compute-section-parametree.ts index fc41974d9505d7c5da73d60c060969968198c296..4c78233a03ba545ca5521449b61033ddcac7e726 100644 --- a/src/app/formulaire/definition/form-compute-section-parametree.ts +++ b/src/app/formulaire/definition/form-compute-section-parametree.ts @@ -12,6 +12,8 @@ import { FormulaireNode } from "../formulaire-node"; export class FormComputeSectionParametree extends FormCompute { + private tmpResult: Result; + constructor(formBase: FormulaireDefinition, private _formSection: FormDefSection, formResult: FormResult) { super(formBase, formResult); } @@ -49,7 +51,7 @@ export class FormComputeSectionParametree extends FormCompute { const computedParam: NgParameter = this.createParameter(computedParamInfo.symbol, this._formBase); this._varResults.calculatedParameter = computedParam; - this._varResults.result = this.runNubCalc(sectNub, computedParam); + this._varResults.result = this.runNubCalc(sectNub); this._varResults.update(false); } @@ -62,20 +64,25 @@ export class FormComputeSectionParametree extends FormCompute { const sectNub: SectionParametree = this._formBase.currentNub as SectionParametree; - const sect: acSection = sectNub.section; - this._sectionResults.section = sect; - - const tmpResult: Result = sectNub.CalcSerie( + this.tmpResult = sectNub.CalcSerie( undefined, // valeur initiale, non utilisée dans ce cas undefined // variable à calculer, non utilisée ); + this.reaffectResultComponents(); + } + + protected reaffectResultComponents() { + const sectNub: SectionParametree = this._formBase.currentNub as SectionParametree; + const sect: acSection = sectNub.section; + this._sectionResults.section = sect; + // résultats de section (avec le graphique de section) - this._sectionResults.result = tmpResult; + this._sectionResults.result = this.tmpResult; // résultats complémentaires des paramètres fixés this._formSectionResult.addSectionFixedParameters(false); - this._formSectionResult.fixedResults.result = tmpResult; + this._formSectionResult.fixedResults.result = this.tmpResult; } /** diff --git a/src/app/formulaire/definition/form-compute.ts b/src/app/formulaire/definition/form-compute.ts index 4e8494753862af7cb478da8204cf880959a5a5b5..5f077f0c282a6119bf66f6c0f15ce7551a57611b 100644 --- a/src/app/formulaire/definition/form-compute.ts +++ b/src/app/formulaire/definition/form-compute.ts @@ -1,11 +1,14 @@ -import { Nub, Result, ParamDomainValue } from "jalhyd"; +import { Nub, Result, ParamDomainValue, Observer, ParamDefinition } from "jalhyd"; import { FormResult } from "./form-result"; import { FormulaireDefinition } from "./form-definition"; import { NgParameter } from "../ngparam"; -export abstract class FormCompute { +export abstract class FormCompute implements Observer { + constructor(protected _formBase: FormulaireDefinition, protected _formResult: FormResult) { + // indirectly subscribe to Nub result updates + this._formBase.addObserver(this); } protected abstract compute(); @@ -18,19 +21,37 @@ export abstract class FormCompute { * retourne un identifiant du paramètre dans le formulaire * surchargé dans le cas des ouvrages // */ - protected getParameterRefid(p: NgParameter) { + protected getParameterRefid(p: ParamDefinition) { return p.symbol; } /** - * lance le calcul d'un paramètre en déterminant une valeur initiale + * Copies current Nub result into result components for display on page. + * Should be called every time the Nub result changes. + * Must be idempotent. + */ + protected abstract reaffectResultComponents(); + + /** + * Lance le calcul d'un paramètre en déterminant une valeur initiale. + * Si nécessaire déclenche un calcul en chaîne des modules en amont. */ - protected runNubCalc(nub: Nub, computedParam: NgParameter): Result { + protected runNubCalc(nub: Nub, computedParam?: ParamDefinition): Result { let init: number; + + // by default, use Nub's calculatedParam + if (computedParam === undefined) { + computedParam = nub.calculatedParam; + } + + // require chain computation; redundant with Nub.CalcSerie but required + // to get initial value here... + const computedParamValue = computedParam.getValue(); + switch (computedParam.domain.domain) { case ParamDomainValue.ANY: if (computedParam && computedParam.isDefined) { - init = computedParam.getValue(); + init = computedParamValue; } if (init === undefined) { init = 0; @@ -39,7 +60,7 @@ export abstract class FormCompute { case ParamDomainValue.POS_NULL: if (computedParam && computedParam.isDefined) { - init = Math.max(computedParam.getValue(), 0); + init = Math.max(computedParamValue, 0); } if (init === undefined) { init = 0; @@ -52,7 +73,7 @@ export abstract class FormCompute { case ParamDomainValue.NOT_NULL: if (computedParam && computedParam.isDefined) { - init = computedParam.getValue(); + init = computedParamValue; } if (init === undefined || init === 0) { init = 1e-8; @@ -61,7 +82,7 @@ export abstract class FormCompute { case ParamDomainValue.POS: if (computedParam && computedParam.isDefined) { - init = Math.max(computedParam.getValue(), 1e-8); + init = Math.max(computedParamValue, 1e-8); } if (init === undefined) { init = 1e-8; @@ -72,6 +93,9 @@ export abstract class FormCompute { return nub.CalcSerie(init, this.getParameterRefid(computedParam)); } + /** + * Triggers computation of the Nub, updates form results + */ public doCompute() { this._formResult.resetResults(); @@ -81,4 +105,17 @@ export abstract class FormCompute { "action": "resultsUpdated", }, this._formBase); } + + // interface Observer + + public update(sender: any, data: any): void { + if (sender instanceof Nub) { + switch (data.action) { + case "nubResultUpdated": + // forward Nub results update notification to FormCompute objects + this.reaffectResultComponents(); + break; + } + } + } } diff --git a/src/app/formulaire/definition/form-def-fixedvar.ts b/src/app/formulaire/definition/form-def-fixedvar.ts index d34f67110ac5f75bf50c5fc2e78637133d63ba95..efad7a577bc72be79b8b854a4897ff558149987b 100644 --- a/src/app/formulaire/definition/form-def-fixedvar.ts +++ b/src/app/formulaire/definition/form-def-fixedvar.ts @@ -1,180 +1,14 @@ -import { ParamValueMode } from "jalhyd"; - -import { ParamRadioConfig, NgParameter } from "../ngparam"; import { FormulaireDefinition } from "./form-definition"; /** * gestion des formulaires avec "paramètre fixé" et "paramètre à varier" */ export class FormDefFixedVar { + protected _formBase: FormulaireDefinition; constructor(base: FormulaireDefinition) { this._formBase = base; } - /** - * remet les radios de tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except" - */ - protected resetOtherRadio(me: NgParameter, except?: ParamRadioConfig) { - for (const p of this._formBase.allFormElements) { - if (p instanceof NgParameter) { - if (p !== me && p.radioState !== except - && p.radioState !== ParamRadioConfig.LINK - && p.radioConfig !== ParamRadioConfig.FIX) { - - p.valueMode = ParamValueMode.SINGLE; - } - } - } - } - - /** - * gère un changement de mode pour un paramètre - * règles : - * - 1 seul paramètre CAL à la fois - * - 1 seul paramètre multivalué (VAR) à la fois (directement ou par liaison) - * - plusieurs paramètres FIX à la fois possible - * - plusieurs paramètres LINK à la fois possible si les 2 1ères règles sont respectées - * - * analyse : - * ancien état nouvel état action(s) - * FIX VAR action1 - * FIX CAL action2 - * FIX LINK si paramètre lié FIX : aucune - * si paramètre lié VAR : action1 - * si paramètre lié CAL : si valeur unique : aucune - * si valeur multiple : action1 - * si paramètre lié LINK : recommencer ce cas avec le paramètre lié - * - * VAR FIX aucune - * VAR CAL action2 - * VAR LINK si paramètre lié FIX : aucune - * si paramètre lié VAR : aucune - * si paramètre lié CAL : si valeur unique : aucune - * si valeur multiple : aucune - * si paramètre lié LINK : recommencer ce cas avec le paramètre lié - * - * CAL FIX action5 - * CAL VAR action3 + action5 - * CAL LINK si paramètre lié FIX : aucune - * si paramètre lié VAR : action3 + action4|action5 - * si paramètre lié CAL : action3 + action4|action5 - * si paramètre lié LINK : recommencer ce cas avec le paramètre lié - * - * action1 : reset (à FIX) de tous les autres paramètres que celui modifié sauf celui à CAL - * action2 : reset (à FIX) de tous les autres paramètres que celui modifié sauf celui/ceux à VAR - * action3 : reset (à FIX) de tous les autres paramètres que celui modifié - * action4 : mettre le paramètre désigné par la conf comme "par défault" à CAL - * action5 : mettre le 1er paramètre du module de calcul à CAL - */ - protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamValueMode) { - switch (oldState) { - case ParamValueMode.SINGLE: // ancien état - switch (sourceParam.valueMode) { - case ParamValueMode.MINMAX: // nouvel état - case ParamValueMode.LISTE: - this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL); - break; - - case ParamValueMode.CALCUL: // nouvel état - this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR); - break; - - case ParamValueMode.LINK: // nouvel état - if (sourceParam.paramDefinition.hasMultipleValues) { - this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL); - } else { - const refParamValues = sourceParam.paramDefinition.referencedParamValues; - if (refParamValues !== undefined) { // cad si on référence un paramètre et non un Result par ex - if (refParamValues.valueMode === ParamValueMode.LINK) { - throw new Error(`références de paramètre en chaîne non pris en charge`); - } - } // cas à traiter - } - break; - } - break; - - case ParamValueMode.LISTE: // ancien état - case ParamValueMode.MINMAX: - switch (sourceParam.valueMode) { - case ParamValueMode.CALCUL: // nouvel état - this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR); - break; - - case ParamValueMode.LINK: // nouvel état - // mode du paramètre référencé - const refParamValues = sourceParam.paramDefinition.referencedParamValues; - if (refParamValues.valueMode === ParamValueMode.LINK) { - throw new Error(`références de paramètre en chaîne non pris en charge`); - } - break; - } - break; - - case ParamValueMode.LINK: // ancien état - switch (sourceParam.valueMode) { - case ParamValueMode.MINMAX: // nouvel état - case ParamValueMode.LISTE: - this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL); - break; - - case ParamValueMode.CALCUL: // nouvel état - this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR); - break; - } - break; - } - } - - /** - * modifie les boutons radio "fix", "var", "cal" de tous les paramètres - * en fonction de la modification de l'état d'un des paramètres - * @param uid id numérique unique du paramètre source - * @param option nouvel état "fix", "var" ou "cal" du paramètre source - */ - protected resetRadiosAndResults(sourceParam: NgParameter, oldState: ParamValueMode) { - this.processRadioStateChange(sourceParam, oldState); - - // on vérifie qu'il y a au moins un paramètre "à calculer" et sinon, on prend le 1er qui est à "fixé" - if (this._formBase.getDisplayedParamFromState(ParamRadioConfig.CAL) === undefined) { - let newCal: NgParameter; - - for (const p of this._formBase.allFormElements) { - if (p instanceof NgParameter) { - // change all radio button groups except the one that sent the event - if (p.radioConfig === ParamRadioConfig.CAL && p.radioState === ParamRadioConfig.FIX && p !== sourceParam) { - newCal = p; - break; - } - } - if (newCal) { - break; - } - } - // if the current calculated parameter was set to another mode, set a new param - // to calculated mode (there must always be at least one) - if (newCal) { - newCal.valueMode = ParamValueMode.CALCUL; - } - } - } - - private logParams() { - for (const fe of this._formBase.allFormElements) { - if (fe instanceof NgParameter) { - console.log(`${fe.paramDefinition.symbol} : ${ParamValueMode[fe.paramDefinition.valueMode]}`); - } - } - } - - /** - * gestion des événements clic sur les radios - */ - public onRadioClick(info: any) { - const param: NgParameter = info.param; // paramètre source de l'événement radio - const old: ParamValueMode = info.oldValueMode; // ancien état (radio) - this.resetRadiosAndResults(param, old); - } } diff --git a/src/app/formulaire/definition/form-def-parallel-structures.ts b/src/app/formulaire/definition/form-def-parallel-structures.ts index a4856994d88a189b00a3d07e67a66a25113147b6..5206d9cc0f74141f5474fb99eafef8d12dfd6427 100644 --- a/src/app/formulaire/definition/form-def-parallel-structures.ts +++ b/src/app/formulaire/definition/form-def-parallel-structures.ts @@ -1,7 +1,3 @@ -import { CalculatorType, StructureType, LoiDebit } from "jalhyd"; - -import { FieldSet } from "../fieldset"; - /** * gestion des formulaires "ouvrages parallèles" */ diff --git a/src/app/formulaire/definition/form-def-paramcalc.ts b/src/app/formulaire/definition/form-def-paramcalc.ts index cd867bde8188ebef8cc6f3861d886fdf774e8e2b..c16a0397eaf376fcb6c15c4a2b03db1d471aa282 100644 --- a/src/app/formulaire/definition/form-def-paramcalc.ts +++ b/src/app/formulaire/definition/form-def-paramcalc.ts @@ -1,6 +1,3 @@ -import { ParamValueMode } from "jalhyd"; - -import { NgParameter } from "../ngparam"; import { FormulaireDefinition } from "./form-definition"; import { FormDefFixedVar } from "./form-def-fixedvar"; @@ -8,97 +5,12 @@ import { FormDefFixedVar } from "./form-def-fixedvar"; * gestion des formulaires avec "paramètre à calculer" (conduite distributrice, Lechapt-Calmon, régime uniforme, passes à bassin) */ export class FormDefParamToCalculate extends FormDefFixedVar { - /** - * symbole du paramètre à calculer par défaut (cf config "idCal") - */ - private _defaultCalculatedParam: string; constructor(base: FormulaireDefinition) { super(base); } public parseOptions(json: {}) { - this._defaultCalculatedParam = undefined; - // browse config file to find "options" chapter - for (const k in json) { - const o = json[k]; - if (o.type === "options") { - // id du paramètre à calculer par défaut - this._defaultCalculatedParam = o["idCal"]; - // this._formBase - if (this._defaultCalculatedParam && ! this.findCalculatedParam()) { - const p = this.setDefault(); - p.isDefault = true; - } - } - } - } - - /** - * Find the parameter that is set to CALC mode - */ - private findCalculatedParam() { - for (const p of this._formBase.currentNub.parameterIterator) { - if (p.valueMode === ParamValueMode.CALCUL) { - return p; - } - } - } - - /** - * met le paramètre par défaut à CAL sauf si c'est "except" - * @param except paramètre à ne pas remettre à CAL - */ - public setDefault(except?: NgParameter): NgParameter { - const defaultParamCal: NgParameter = this._formBase.getParamFromSymbol(this._defaultCalculatedParam); - if (except === undefined || defaultParamCal.uid !== except.uid) { - defaultParamCal.valueMode = ParamValueMode.CALCUL; - } - return defaultParamCal; } - /** - * @see FormDefFixedVar.processRadioStateChange pour l'analyse - */ - protected processRadioStateChange(sourceParam: NgParameter, oldState: ParamValueMode) { - super.processRadioStateChange(sourceParam, oldState); - - switch (oldState) { - case ParamValueMode.CALCUL: // ancien état - switch (sourceParam.valueMode) { - case ParamValueMode.SINGLE: // nouvel état - this.setDefault(sourceParam); - break; - - case ParamValueMode.MINMAX: // nouvel état - case ParamValueMode.LISTE: - super.resetOtherRadio(sourceParam); - this.setDefault(sourceParam); - break; - - case ParamValueMode.LINK: // nouvel état - if (sourceParam.paramDefinition.hasMultipleValues) { - super.resetOtherRadio(sourceParam); - this.setDefault(); - } else { - // mode du paramètre référencé - const refParamValues = sourceParam.paramDefinition.referencedParamValues; - if (refParamValues !== undefined) { - switch (refParamValues.valueMode) { - case ParamValueMode.MINMAX: - case ParamValueMode.LISTE: - case ParamValueMode.CALCUL: - super.resetOtherRadio(sourceParam); - this.setDefault(); - break; - - case ParamValueMode.LINK: - throw new Error(`références de paramètre en chaîne non pris en charge`); // cas à traiter - } - } - } - break; - } - } - } } diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts index 37d42aeb2b53a5276fa2dcdbd23ee7e820cf0106..0c43a6bf980161dcaabb17989f6fc704c888135c 100644 --- a/src/app/formulaire/definition/form-definition.ts +++ b/src/app/formulaire/definition/form-definition.ts @@ -1,4 +1,4 @@ -import { CalculatorType, ComputeNodeType, Nub, Props, Observer, Session, ParallelStructure } from "jalhyd"; +import { CalculatorType, ComputeNodeType, Nub, Props, Observer, Session } from "jalhyd"; import { FormulaireElement } from "../formulaire-element"; import { NgParameter, ParamRadioConfig } from "../ngparam"; @@ -36,6 +36,9 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs /** clé-valeurs du fichier de localisation spécifique à ce module */ private _specificLocalisation: StringMap; + /** ISO 639-1 language code of the current language (to avoid unnecessary localisation reload) */ + private _currentLanguage: string; + constructor() { super(undefined); } @@ -54,6 +57,10 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs return this._specificLocalisation; } + public get currentLanguage() { + return this._currentLanguage; + } + public get calculatorType(): CalculatorType { const props = this._currentNub === undefined ? this.defaultProperties : (this._currentNub.properties as Props).props; return props["calcType"]; @@ -87,7 +94,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs } public initNub(props?: Props) { - this._currentNub = this.createNub(props ? props : new Props(this.defaultProperties)); + this.currentNub = this.createNub(props ? props : new Props(this.defaultProperties)); } public get currentNub(): Nub { @@ -101,7 +108,14 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs `Nub ${n.properties["calcType"]} incompatible avec le formulaire ${this._calculatorName} (${this._props["calcType"]})` ); } + // unsubscribe from old Nub + if (this._currentNub) { + this._currentNub.removeObserver(this); + } + // replace Nub this._currentNub = n; + // subscribe to new Nub (for result updates) + this._currentNub.addObserver(this); } /** @@ -155,7 +169,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs * @param uid id à rechercher */ public hasNubId(uid: string): boolean { - return this._currentNub.uid === uid; + return (this._currentNub && this._currentNub.uid === uid); } public moveFieldsetUp(fs: FieldSet) { @@ -269,17 +283,6 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs } this.completeParse(this._jsonConfig); - - // console.log("-----"); - - // for (const n of Session.getInstance().NubIterator) { - // console.log(n.nub); - // for (const p of n.nub.parameterIterator) - // console.log(`${p.symbol} uid ${p.uid} props ${n.properties} mode ${p.valueMode} val ${p.uncheckedValue}`); - // } - - // logObject(this._fieldSets, "fieldsets"); - // logObject(this._dependencies, "dependences"); this.parseDependencies(this._jsonConfig); } @@ -355,12 +358,22 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs }, this); } + /** + * Forwards Nub's result updated notification. + * Used by FormCompute to update results display + */ + protected notifyNubResultUpdated(sender) { + this.notifyObservers({ + action: "nubResultUpdated" + }, sender); + } + /** * réinitialisation du formulaire suite à un changement d'une valeur, d'une option, ... : * effacement des résultats, application des dépendances, ... */ public reset() { - this.resetResults(); + this.resetResults([]); this.applyDependencies(); // prévenir les composants qu'il faut détecter les changements @@ -393,19 +406,19 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs } } - public abstract resetResults(); + public abstract resetResults(visited: string[]); public abstract doCompute(); public abstract get hasResults(): boolean; public abstract get results(): CalculatorResults[]; - public updateLocalisation(localisation: StringMap) { + public updateLocalisation(localisation: StringMap, lang: string) { this._specificLocalisation = localisation; + this._currentLanguage = lang; for (const fe of this.topFormElements) { fe.updateLocalisation(localisation); } - if (this.hasResults) { - this.doCompute(); // pour mettre à jour la langue + this.doCompute(); // pour mettre à jour la langue des intitulés de résultats } } @@ -421,21 +434,14 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs return (<FormulaireElement>this.getFormulaireNodeById(id)).isDisplayed; } - public get isValid(): boolean { - let res = true; - for (const fs of this.allFieldsets) { - if (fs.isDisplayed) { - res = res && fs.isValid; - } - } - return res; - } - /** * gestion d'un clic sur les radios */ public onRadioClick(info: any) { - this.reset(); + // if mode changed, reset form results + if (info.oldValueMode !== info.param.valueMode) { + this.reset(); + } } /** @@ -477,28 +483,16 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs return undefined; } - /** - * @param json conf du formulaire - * @param uidMap table de correspondance uid dans le fichier de conf <-> uid en mémoire - */ - public updateParamsLinks(json: {}, uidMap: {}[]) { - for (const ks in json) { - switch (ks) { - case "elements": - let n = 0; - for (const e of json[ks]) { - if (Object.keys(e)[0] === "fieldset") { - this.getNthFieldset(n).updateParamsLinks(e["fieldset"], uidMap); - n++; - } - } - break; - } - } - } - // interface Observer public update(sender: any, data: any) { + if (sender instanceof Nub) { + switch (data.action) { + case "resultUpdated": + // forward Nub results update notification to FormCompute objects + this.notifyNubResultUpdated(sender); + break; + } + } } } diff --git a/src/app/formulaire/definition/form-result-fixedvar.ts b/src/app/formulaire/definition/form-result-fixedvar.ts index 291a6373a5fca0df5663b970d250b34256a400ed..be7137587ba266eb3ec4c47289d6393a0bcd95cf 100644 --- a/src/app/formulaire/definition/form-result-fixedvar.ts +++ b/src/app/formulaire/definition/form-result-fixedvar.ts @@ -1,5 +1,3 @@ -import { ResultElement, cLog, ParamValueMode } from "jalhyd"; - import { FixedResults } from "../../results/fixed-results"; import { GraphType, VarResults } from "../../results/var-results"; import { ParamRadioConfig, NgParameter } from "../ngparam"; diff --git a/src/app/formulaire/field.ts b/src/app/formulaire/field.ts index c8d2f84b60290e67cbf9e3b570156758cb9a59ec..3bdbef3c0a504e0364f48ced8fffdaaebc1cccc1 100644 --- a/src/app/formulaire/field.ts +++ b/src/app/formulaire/field.ts @@ -4,7 +4,6 @@ import { Dependency } from "./dependency/dependency"; import { isNumber } from "util"; export abstract class Field extends FormulaireElement { - public abstract get isValid(); public abstract getValue(): any; public abstract setValue(sender: any, val: any): void; diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts index 5dd19f2e5d86440901aa34acfc4c06a75c06677f..ff2789eb397c9c9e4cdd7f06657be1d9669e92d7 100644 --- a/src/app/formulaire/fieldset.ts +++ b/src/app/formulaire/fieldset.ts @@ -2,7 +2,6 @@ import { CalculatorType, ComputeNodeType, ParamDefinition, LoiDebit, StructureTy import { FormulaireElement } from "./formulaire-element"; import { Field } from "./field"; -import { CheckField } from "./check-field"; import { SelectField } from "./select-field"; import { NgParameter, ParamRadioConfig } from "./ngparam"; import { ServiceFactory } from "../services/service-factory"; @@ -75,24 +74,6 @@ export class FieldSet extends FormulaireElement implements Observer { } } - public get isValid(): boolean { - let res = true; - for (const f of this.kids) { - if (f instanceof Field) { - if (f.isDisplayed) { - res = res && f.isValid; - } - } - } - return res; - } - - private parse_check(json: {}): CheckField { - const res: CheckField = new CheckField(this); - res.parseConfig(json); - return res; - } - private parse_select(json: {}): SelectField { const res: SelectField = new SelectField(this); res.parseConfig(json); @@ -152,8 +133,6 @@ export class FieldSet extends FormulaireElement implements Observer { if (res) { res.parseConfig(json, { "radioConfig": default_radio_config }); - // set parent Nub on Parameter to ensure UID availability - res.paramDefinition.parent = this._nub; } return res; @@ -174,9 +153,6 @@ export class FieldSet extends FormulaireElement implements Observer { } else if (field["type"] === "select") { const param = this.parse_select(field); this.addField(param); - } else if (field["type"] === "check") { - const param = this.parse_check(field); - this.addField(param); } } } @@ -401,46 +377,4 @@ export class FieldSet extends FormulaireElement implements Observer { } } } - - /** - * MAJ des liens entre paramètres lors de la désérialisation - * @param json conf du fieldset issue du fichier - * @param uidMap table de correspondance uid dans le fichier de conf <-> uid en mémoire - */ - public updateParamsLinks(json: {}, uidMap: {}[]) { - for (const ks in json) { - switch (ks) { - case "elements": - for (const e of json[ks]) { - if (Object.keys(e)[0] === "param") { - const prm = e["param"]; - if (prm["values"]["mode"] === "LINK") { - // id du formulaire cible dans le fichier - const oldFormUid = +prm["values"]["form_uid"]; - - // correspondance avec l'objet mémoire - let newFormUid: string; - for (const m of uidMap) { - if (m["type"] === "form" && m["old"] === oldFormUid) { - newFormUid = m["new"]; - break; - } - } - - // formulaire dont le Nub est la cible du lien - const destForm: FormulaireDefinition - = ServiceFactory.instance.formulaireService.getFormulaireFromId(newFormUid); - - // paramètre source (celui qui est lié à une valeur) - const src: NgParameter = this.getFormulaireNodeById(prm["id"]) as NgParameter; - - // création du lien - src.paramDefinition.defineReference(destForm.currentNub, prm["values"]["ref"]); - } - break; - } - } - } - } - } } diff --git a/src/app/formulaire/ngparam.ts b/src/app/formulaire/ngparam.ts index 30ec5d0d52392f2c20621ba5b2deac71eadfdc0a..3f9f58711da6fb78f6f1b78e2f5ea7a6dd4ce287 100644 --- a/src/app/formulaire/ngparam.ts +++ b/src/app/formulaire/ngparam.ts @@ -1,5 +1,5 @@ import { Interval, ParamDefinition, ParamDomain, ParamValueMode, INumberIterator, - Nub, Observer, asObservable, ParamCalculability } from "jalhyd"; + Observer, asObservable, ParamCalculability, LinkedValue } from "jalhyd"; import { sprintf } from "sprintf-js"; @@ -11,24 +11,16 @@ import { ServiceFactory } from "../services/service-factory"; import { FormulaireNode } from "./formulaire-node"; export enum ParamRadioConfig { - /** - * pas de radio, paramètre modifiable à la main uniquement - */ + /** pas de radio, paramètre modifiable à la main uniquement */ FIX, - /** - * boutons radio "paramètre fixé" et "paramètre à varier" - */ + /** boutons radio "paramètre fixé" et "paramètre à varier" */ VAR, - /** - * boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer" - */ + /** boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer" */ CAL, - /** - * boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer", "paramètre lié" - */ + /** boutons radio "paramètre fixé", "paramètre à varier" et "paramètre à calculer", "paramètre lié" */ LINK } @@ -37,33 +29,30 @@ export enum ParamRadioConfig { */ export class NgParameter extends InputField implements Observer { - constructor(private _paramDef: ParamDefinition, parent: FormulaireNode) { - super(parent); - } public unit: string; public radioConfig: ParamRadioConfig; - /** - * true si ce paramètre est celui par défaut dans un formulaire - * (cf. fichier de conf des modules de calcul, objet "options", champ "idCal") - */ - public isDefault = false; // archi bug du langage ! si on relit cette propriété sans l'avoir modifiée entre-temps, elle vaut undefined ! + constructor(private _paramDef: ParamDefinition, parent: FormulaireNode) { + super(parent); + this.radioConfig = this.radioState; + } /** * Returns a text preview of the current value(s), depending on the value mode */ public static preview(p: ParamDefinition): string { let valuePreview: string; + const i18n = ServiceFactory.instance.i18nService; + const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits; switch (p.valueMode) { case ParamValueMode.SINGLE: - valuePreview = String(p.getValue()); + valuePreview = String(p.getValue().toFixed(nDigits)); break; case ParamValueMode.MINMAX: - const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits; - let min: any = p.paramValues.min; - let max: any = p.paramValues.max; - let step: any = p.paramValues.step; + let min: any = p.min; + let max: any = p.max; + let step: any = p.step; if (min) { min = min.toFixed(nDigits); } @@ -73,24 +62,73 @@ export class NgParameter extends InputField implements Observer { if (step) { step = step.toFixed(nDigits); } - valuePreview = ServiceFactory.instance.i18nService.localizeText("INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP"); + valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_MINMAXSTEP"); valuePreview = sprintf(valuePreview, min, max, step); break; case ParamValueMode.LISTE: - valuePreview = ServiceFactory.instance.i18nService.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES"); - const vals = p.paramValues.valueList || []; - valuePreview += " " + vals.slice(0, 5).join("; ") + "…"; + valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES"); + const vals = p.valueList || []; + valuePreview += " " + vals.slice(0, 5).map((v) => { + return v.toFixed(nDigits); + }).join("; ") + "…"; break; case ParamValueMode.CALCUL: - valuePreview = ServiceFactory.instance.i18nService.localizeText("INFO_PARAMFIELD_IN_CALCULATION"); + valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION"); if (p.calculability === ParamCalculability.DICHO) { - valuePreview += " (" + ServiceFactory.instance.i18nService.localizeText("INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE") - + ": " + p.getValue() + ")"; + valuePreview += " (" + i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE") + + ": " + p.getValue().toFixed(nDigits) + ")"; } break; case ParamValueMode.LINK: - // recursive call - valuePreview = NgParameter.preview(p.referencedObject as ParamDefinition); + if (p.isReferenceDefined()) { + if (p.referencedValue.isParameter()) { + const targetParam = (p.referencedValue.element as ParamDefinition); + // calculated param ? + if (targetParam.valueMode === ParamValueMode.CALCUL) { + // was the result already computed ? + // @WAARNING .result might be set but the computation might have failed (dichotomy for ex.) + if (p.referencedValue.nub.result) { + if (p.referencedValue.hasMultipleValues()) { + // like LIST mode + valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES"); + valuePreview += " " + p.referencedValue.nub.result.getCalculatedValues().map((v) => { + return v.toFixed(nDigits); + }).slice(0, 5).join("; ") + "…"; + } else { + const vCalc = p.referencedValue.nub.result.vCalc; + if (vCalc) { + valuePreview = String(vCalc.toFixed(nDigits)); + } else { + // computation has been run but has failed + valuePreview = i18n.localizeText("INFO_PARAMFIELD_CALCULATION_FAILED"); + } + } + } else { + valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION"); + } + } else { + // recursive call + valuePreview = NgParameter.preview(targetParam); + } + } else { + // was the result already computed ? + try { + const remoteValues = p.referencedValue.getParamValues(); + if (p.referencedValue.hasMultipleValues()) { + // like LIST mode + valuePreview = i18n.localizeText("INFO_PARAMFIELD_PARAMVARIER_VALUES"); + valuePreview += " " + remoteValues.valueList.slice(0, 5).map((v) => { + return v.toFixed(nDigits); + }).join("; ") + "…"; + } else { + // like SINGLE mode + valuePreview = String(remoteValues.currentValue.toFixed(nDigits)); + } + } catch (e) { + valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION"); + } + } + } } return valuePreview; @@ -101,7 +139,7 @@ export class NgParameter extends InputField implements Observer { } get domain(): ParamDomain { - return this._paramDef.getDomain(); + return this._paramDef.domain; } public set confId(id: string) { @@ -111,10 +149,6 @@ export class NgParameter extends InputField implements Observer { this._confId = id; } - private get _paramValues() { - return this._paramDef.paramValues; - } - public get paramDefinition() { return this._paramDef; } @@ -144,15 +178,19 @@ export class NgParameter extends InputField implements Observer { return this._paramDef.valueMode; } + /** + * Unlinks the parameter and updates its value when value mode changes + */ public set valueMode(m: ParamValueMode) { // undefined si on clique en dehors du select après l'avoir ouvert (cad sans avoir fait de sélection) // et au même niveau, cad à côté du bouton et non à côté du menu déroulant if (m !== undefined && this._paramDef.valueMode !== m) { + const nDigits = ServiceFactory.instance.applicationSetupService.displayDigits; this.unlinkParameter(); this._paramDef.valueMode = m; this.notifyObservers({ "action": "valueModeChange", - "value": this._paramDef.getValue() + "value": Number(this._paramDef.getValue().toFixed(nDigits)) }); } } @@ -162,39 +200,23 @@ export class NgParameter extends InputField implements Observer { } public get minValue() { - return this._paramValues.min; - } - - public set minValue(v: number) { - this._paramValues.min = v; + return this._paramDef.min; } public get maxValue() { - return this._paramValues.max; - } - - public set maxValue(v: number) { - this._paramValues.max = v; + return this._paramDef.max; } public get stepRefValue(): Interval { - return this._paramValues.stepRefValue; + return this._paramDef.stepRefValue; } public get stepValue() { - return this._paramValues.step; - } - - public set stepValue(v: number) { - this._paramValues.step = v; + return this._paramDef.step; } public get valueList() { - return this._paramValues.valueList; - } - - public set valueList(l: number[]) { - this._paramValues.valueList = l; + return this._paramDef.valueList; } public get isValid() { @@ -236,8 +258,19 @@ export class NgParameter extends InputField implements Observer { throw new Error("invalid parameter radio configuration " + s); } + /** + * Sets this parameter as the one to be computed + */ + public setCalculated() { + this.paramDefinition.setCalculated(); + } + + /** + * Asks the ParamDefinition for its current value + * @TODO replace with singleValue to avoid displaying computation results ? + */ public getValue() { - return this._paramDef.v; + return this._paramDef.getValue(); } /** @@ -265,11 +298,43 @@ export class NgParameter extends InputField implements Observer { this.notifyValueModified(sender); } + public setMinValue(sender: any, v: number) { + const changed = (this._paramDef.min !== v); + this._paramDef.min = v; + if (changed) { + this.notifyValueModified(sender); + } + } + + public setMaxValue(sender: any, v: number) { + const changed = (this._paramDef.max !== v); + this._paramDef.max = v; + if (changed) { + this.notifyValueModified(sender); + } + } + + public setStepValue(sender: any, v: number) { + const changed = (this._paramDef.step !== v); + this._paramDef.step = v; + if (changed) { + this.notifyValueModified(sender); + } + } + + public setValueList(sender: any, l: number[]) { + const changed = (JSON.stringify(this._paramDef.valueList) !== JSON.stringify(l)); + this._paramDef.valueList = l; + if (changed) { + this.notifyValueModified(sender); + } + } + /** * supprime un lien avec un paramètre */ private unlinkParameter() { - const o = asObservable(this._paramDef.referencedObject); + const o = asObservable(this._paramDef.referencedValue); if (this.valueMode === ParamValueMode.LINK) { this._paramDef.undefineReference(); if (o !== undefined) { @@ -281,34 +346,44 @@ export class NgParameter extends InputField implements Observer { /** * crée le lien avec un paramètre */ - public linkToParameter(n: Nub, ref: string) { + public linkToValue(target: LinkedValue) { const changed: boolean = - this._paramDef.valueMode !== ParamValueMode.LINK || this._paramDef.referencedNub !== n || - this._paramDef.referenceDefinition !== ref; + // changement de mode + ! this._paramDef.isReferenceDefined() + // ou changement de référence + || ! this._paramDef.referencedValue.equals(target); + if (changed) { - let o = asObservable(this._paramDef.referencedObject); + let o = asObservable(this._paramDef.referencedValue); if (o !== undefined) { o.removeObserver(this); } - this.valueMode = ParamValueMode.LINK; + // this.valueMode = ParamValueMode.LINK; // supposed to be done at model level by instruction below // changement de lien - this._paramDef.defineReference(n, ref); + this._paramDef.defineReferenceFromLinkedValue(target); - o = asObservable(this._paramDef.referencedObject); + o = asObservable(this._paramDef.referencedValue); if (o !== undefined) { o.addObserver(this); // pour être prévenu des changements de valeur de l'object référencé } + let value; + try { + value = this.getValue(); + } catch (e) { + // console.log("undefined target value (pending calculation)"); + } + this.notifyObservers({ "action": "valueLinkChange", - "value": this.getValue() + "value": value }); } } public checkValue(val: number) { - this._paramDef.checkValue(val); + this._paramDef.checkValueAgainstDomain(val); } public checkList(l: number[]) { @@ -339,8 +414,6 @@ export class NgParameter extends InputField implements Observer { this.setValue(this, +val); } this.radioConfig = NgParameter.getRadioConfig(radioConfig); - // tslint:disable-next-line:max-line-length - this.isDefault = false; // malgré le fait qu'il soit initialisé dans la déclaration de la classe NgParam à false, quand on relit sa valeur, il vaut undefined (merci Microsoft) } public verifiesDependency(d: Dependency): boolean { diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts index df078bfbf1a912061129b46786d72cb03e331b7d..c60b4dd506e3f122f01716f8ca95055251ad80ea 100644 --- a/src/app/formulaire/select-field.ts +++ b/src/app/formulaire/select-field.ts @@ -51,10 +51,6 @@ export class SelectField extends Field { } } - public get isValid(): boolean { - return true; - } - public getLabel() { if (this._selectedEntry) { return this._selectedEntry.label; diff --git a/src/app/results/var-results.ts b/src/app/results/var-results.ts index 752664fcb2e5f46fb8492fe82c4913ce98778963..51208365da42c0a35e6f15778fa47550f1f6ff55 100644 --- a/src/app/results/var-results.ts +++ b/src/app/results/var-results.ts @@ -116,7 +116,6 @@ export class VarResults extends CalculatedParamResults { * @param symbol parameter / result symbol (ex: "Q") */ public getValuesSeries(symbol: string) { - // console.log("GVS for symbol", symbol); const series = []; // 1. calculated param ? if (this._calculatedParam.symbol === symbol) { diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts index 066e82f7abe338d63e5ab8f387841dc84e66e679..79f728dcbe4404a21f454ebb9657b523d3fcbad1 100644 --- a/src/app/services/formulaire/formulaire.service.ts +++ b/src/app/services/formulaire/formulaire.service.ts @@ -3,7 +3,7 @@ import { Injectable } from "@angular/core"; import { decode } from "he"; import { saveAs } from "file-saver"; -import { CalculatorType, EnumEx, Observable, ParamDefinition, Session, Nub, ParallelStructure } from "jalhyd"; +import { CalculatorType, LinkedValue, Observable, ParamDefinition, Session, Nub, ParallelStructure } from "jalhyd"; import { HttpService } from "../../services/http/http.service"; import { I18nService } from "../../services/internationalisation/internationalisation.service"; @@ -11,7 +11,6 @@ import { FormulaireDefinition } from "../../formulaire/definition/form-definitio import { FormulaireElement } from "../../formulaire/formulaire-element"; import { InputField } from "../../formulaire/input-field"; import { SelectField } from "../../formulaire/select-field"; -import { CheckField } from "../../formulaire/check-field"; import { StringMap } from "../../stringmap"; import { FormulaireBase } from "../../formulaire/definition/concrete/form-base"; import { FormulaireLechaptCalmon } from "../../formulaire/definition/concrete/form-lechapt-calmon"; @@ -22,6 +21,7 @@ import { FormulaireParallelStructure } from "../../formulaire/definition/concret import { NgParameter } from "../../formulaire/ngparam"; import { FieldsetContainer } from "../..//formulaire/fieldset-container"; import { ApplicationSetupService } from "../app-setup/app-setup.service"; +import { NotificationsService } from "../notifications/notifications.service"; @Injectable() export class FormulaireService extends Observable { @@ -36,7 +36,9 @@ export class FormulaireService extends Observable { constructor( private i18nService: I18nService, private appSetupService: ApplicationSetupService, - private httpService: HttpService + private httpService: HttpService, + private intlService: I18nService, + private notificationsService: NotificationsService ) { super(); this._formulaires = []; @@ -97,27 +99,17 @@ export class FormulaireService extends Observable { } /** - * Met à jour la langue du formulaire - * @param formId id unique du formulaire - * @param localisation ensemble id-message traduit - */ - private updateFormulaireLocalisation(formId: string, localisation: StringMap) { - for (const f of this._formulaires) { - if (f.uid === formId) { - f.updateLocalisation(localisation); - break; - } - } - } - - /** - * charge la localisation et met à jour la langue du formulaire + * Loads localisation file corresponding to current language then updates all form strings, + * only if form language was not already set to current language */ public loadUpdateFormulaireLocalisation(f: FormulaireDefinition): Promise<FormulaireDefinition> { - return this.loadLocalisation(f.calculatorType).then(localisation => { - this.updateFormulaireLocalisation(f.uid, localisation); - return f; - }); + const requiredLang = this._intlService.currentLanguage; + if (requiredLang !== f.currentLanguage) { + return this.loadLocalisation(f.calculatorType).then(localisation => { + f.updateLocalisation(localisation, requiredLang); + return f; + }); + } } /** @@ -302,14 +294,6 @@ export class FormulaireService extends Observable { return <InputField>s; } - public getCheckField(formId: string, elemId: string): CheckField { - const s = this.getFormulaireElementById(formId, elemId); - if (!(s instanceof CheckField)) { - throw new Error("Form element with id '" + elemId + "' is not a checkbox"); - } - return <CheckField>s; - } - public getSelectField(formId: string, elemId: string): SelectField { const s = this.getFormulaireElementById(formId, elemId); if (!(s instanceof SelectField)) { @@ -413,8 +397,14 @@ export class FormulaireService extends Observable { "action": "closeForm", "form": form }); + + // reset the result panels of all forms depending on this one + // @TODO UI should detect model change instead of doing this manually + this.resetAllDependingFormsResults(form, [], false); } if (nub) { + // reset model results (important, also resets dependent Nubs results in chain) + form.currentNub.resetResult(); Session.getInstance().deleteNub(nub); } } @@ -456,16 +446,13 @@ export class FormulaireService extends Observable { private readSingleFile(file: File): Promise<any> { return new Promise<any>((resolve, reject) => { const fr = new FileReader(); - fr.onload = () => { resolve(fr.result); }; - fr.onerror = () => { fr.abort(); reject(new Error(`Erreur de lecture du fichier ${file.name}`)); }; - fr.readAsText(file); }); } @@ -512,10 +499,27 @@ export class FormulaireService extends Observable { // liste des noms de modules de calcul if (data.session && Array.isArray(data.session)) { data.session.forEach((e: any) => { - res.push({ - uid: e.uid, - title: e.meta && e.meta.title ? e.meta.title : undefined - }); + const nubInfo = { + uid: e.uid, + title: e.meta && e.meta.title ? e.meta.title : undefined, + requires: [], + children: [] + }; + // list linked params dependencies for each Nub + if (e.parameters) { + e.parameters.forEach((p) => { + if (p.targetNub && ! nubInfo.requires.includes(p.targetNub)) { + nubInfo.requires.push(p.targetNub); + } + }); + } + // list children nubs for each Nub + if (e.structures) { + e.structures.forEach((p) => { + nubInfo.children.push(p.uid); + }); + } + res.push(nubInfo); }); } return res; @@ -530,93 +534,36 @@ export class FormulaireService extends Observable { } /** - * met à jour les liens d'un formulaire - * @param json conf du formulaire - * @param formInfos métadonnées sur les formulaires chargés - * @param form formulaire dont on met à jour les liens - * @param uidMap table de correspondance uid dans le fichier de conf <-> uid en mémoire + * Demande à la Session JalHYd la liste des paramètres/résultats pouvant être liés au + * paramètre fourni */ - private updateFormLinks(json: {}, formInfos: any[], form: FormulaireDefinition, uidMap: {}[]) { - for (const i of formInfos) { - if (i["uid"] === json["uid"] && i["selected"]) { - form.updateParamsLinks(json, uidMap); - } - } - } - - /** - * MAJ des liens entre paramètres lors de la désérialisation - */ - /* private updateParamsLinks(json: {}, formInfos: any[], oldFormCount: number) { - // table de correspondance des uid fichier <-> objets mémoire - // forme : tableau d'objets de la forme : - // { "type" : <type de l'objet. "form" pour formulaire>, - // "old": <uid dans le fichier>, - // "new": <uid de l'objet mémoire>} - const uidMap = []; - for (const ks in json) { - switch (ks) { - case "elements": - let n = oldFormCount; - for (const e of json[ks]) { - if (Object.keys(e)[0] === "form") { - uidMap.push({ - "type": "form", - "old": e["form"]["uid"], - "new": this._formulaires[n].uid - }); - n++; - } - } - } - } - // MAJ liens - for (const ks in json) { - switch (ks) { - case "elements": - let n = 0; - for (const e of json[ks]) { - if (Object.keys(e)[0] === "form") { - this.updateFormLinks(e["form"], formInfos, this._formulaires[n + oldFormCount], uidMap); - n++; + public getLinkableValues(p: NgParameter): LinkedValue[] { + let linkableValues: LinkedValue[] = []; + if (p) { + linkableValues = Session.getInstance().getLinkableValues(p.paramDefinition); + // join form names to ease usage + for (let i = 0; i < linkableValues.length; i++) { + const lv = linkableValues[i]; + for (const f of this._formulaires) { + if (f.currentNub) { + if (f.currentNub.uid === lv.nub.uid) { + lv.meta["formTitle"] = f.calculatorName; + } else { + // child structures ? + if (f.currentNub instanceof ParallelStructure) { + for (const s of f.currentNub.structures) { + if (s.uid === lv.nub.uid) { + lv.meta["formTitle"] = f.calculatorName; + } + } + } } } - break; - - default: - throw new Error(`session file : invalid key '${ks}' in session object`); - } - } - } */ - - /** - * @returns liste des valeurs liables à un paramètre sous la forme d'un tableau d'objets - * {"param":<paramètre lié>, "nub":<Nub d'origine du paramètre lié>, "formTitle":<nom du module de calcul liée au nub>} - * @param p paramètre qui sert de clé de recherche des paramètres liables - */ - public getLinkableValues(p: NgParameter): any[] { - const res: any[] = []; - if (p !== undefined) { - for (const f of this._formulaires) { - // nub associé au formulaire - const sn = f.currentNub; - try { - // on vérifie que le paramètre en entrée appartient au nub - const np = sn.getParameter(p.symbol); - // si oui, on demande à exclure des valeurs retournées le résultat du même nom que le paramètre - const exclude = np !== undefined ? p.paramDefinition.uid === np.uid : false; - // valeurs liables - const ps = sn.getLinkableValues(p.paramDefinition, undefined, exclude); - for (const npp of ps) { - npp["formTitle"] = f.calculatorName; - res.push(npp); - } - } catch (e) { - // p.symbol n'existe pas dans le nub testé } } } - return res; + linkableValues = this.filterLinkableValues(linkableValues); + return linkableValues; } /** @@ -626,16 +573,12 @@ export class FormulaireService extends Observable { * @param values valeurs liables (modifié par la méthode) */ public filterLinkableValues(values: any[]): any[] { - // suppression des paramètres non affichés - for (let i = values.length - 1; i >= 0; i--) { const v = values[i].value; if (v instanceof ParamDefinition) { // pour chaque paramètre... const prm: ParamDefinition = v; - const parentForm: FormulaireDefinition = this.getParamdefParentForm(prm); - // ... on cherche s'il est affiché dans son parent let found = false; if (parentForm !== undefined) { @@ -648,13 +591,37 @@ export class FormulaireService extends Observable { } } } - if (!found) { values.splice(i, 1); } } } - return values; } + + /** + * Resets the results of all forms depending on the given form "f" + * @param f + */ + public resetAllDependingFormsResults(f: FormulaireDefinition, visited: string[] = [], notify: boolean = true) { + const dependingNubs = Session.getInstance().getDependingNubs(f.currentNub.uid); + for (const dn of dependingNubs) { + if (! visited.includes(dn.uid)) { + const form = this.getFormulaireFromNubId(dn.uid); + if (form) { + const hadResults = form.hasResults; + // form might not have a result, but still have another form depending on it ! + form.resetResults(visited); + if (hadResults) { + if (notify) { + this.notificationsService.notify( + this.intlService.localizeText("INFO_SNACKBAR_RESULTS_INVALIDATED") + " " + form.calculatorName, + 1500 + ); + } + } + } + } + } + } } diff --git a/src/app/services/notifications/notifications.service.ts b/src/app/services/notifications/notifications.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..9caf0d7ffb1a7832f671261980764a7e30a29046 --- /dev/null +++ b/src/app/services/notifications/notifications.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from "@angular/core"; +import { MatSnackBar } from "@angular/material"; + +/** + * Displays a notifications queue as consecutive snackbars + */ +@Injectable() +export class NotificationsService { + + /** FIFO queue for notifications */ + private notifications: any[]; + + private isOpen: boolean; + + public constructor( + private snackBar: MatSnackBar + ) { + this.notifications = []; + this.isOpen = false; + } + + /** Push a notification and display it as soon as possible */ + public notify(message: string, duration: number, action: string = "OK") { + this.notifications.push({ + message: message, + duration: duration, + action: action + }); + this.show(); + } + + /** Show all messages in the FIFO queue one after another */ + public show() { + if (! this.isOpen) { + // process next notification + if (this.notifications.length > 0) { + const notif = this.notifications.shift(); + this.isOpen = true; + const ref = this.snackBar.open(notif.message, notif.action, { + duration: notif.duration + }); + ref.afterDismissed().subscribe(() => { + this.isOpen = false; + this.show(); + }); + } + } + } +} diff --git a/src/app/services/service-factory.ts b/src/app/services/service-factory.ts index 0627bdf99a223a0bf9b7712945dcdb7849075c1a..838a00cfc468b4c98954a00f73d4b7d9cc6b9c37 100644 --- a/src/app/services/service-factory.ts +++ b/src/app/services/service-factory.ts @@ -2,6 +2,7 @@ import { ApplicationSetupService } from "./app-setup/app-setup.service"; import { FormulaireService } from "./formulaire/formulaire.service"; import { I18nService } from "./internationalisation/internationalisation.service"; import { HttpService } from "./http/http.service"; +import { NotificationsService } from "./notifications/notifications.service"; export class ServiceFactory { private static _instance: ServiceFactory; // instance pour le pattern singleton @@ -16,6 +17,8 @@ export class ServiceFactory { public httpService: HttpService; + public notificationsService: NotificationsService; + public static get instance() { if (ServiceFactory._instance === undefined) { ServiceFactory._instance = new ServiceFactory(); diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json index bed80e3329ae40f6b823691ce9dc656df5862bdb..66c9f9d6b2cad2a4dd1c6b03f91eefc3cb7ef127 100644 --- a/src/locale/messages.en.json +++ b/src/locale/messages.en.json @@ -22,6 +22,7 @@ "ERROR_PARAMDEF_VALUE_POS": "value %value% of '%symbol%' parameter is invalid (cannot be <=0)", "ERROR_PARAMDEF_VALUE_POSNULL": "value %value% of '%symbol%' parameter is invalid (cannot be <0)", "ERROR_PARAMDEF_VALUE_UNDEFINED": "value of '%symbol%' parameter is undefined", + "ERROR_PARAMDEF_LINKED_VALUE_UNDEFINED": "value of '%symbol%' linked parameter is undefined", "ERROR_PARAMDOMAIN_INTERVAL_BOUNDS": "invalid %minValue%/%maxValue% min/max boundaries for 'interval' parameter definition domain", "ERROR_PARAMDOMAIN_INVALID": "parameter '%symbol%: non supported '%domain%' definition domain", "ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT": "Upstream boundary condition < Critical elevation: no possible calculation from upstream", @@ -43,6 +44,7 @@ "INFO_CLOISONS_TITRE_COURT": "Cross walls", "INFO_CLOSE_DIALOGUE_TEXT": "Warning ! Parameters and results will be lost. Really close?", "INFO_CLOSE_DIALOGUE_TITRE": "Please confirm", + "INFO_CLOSE_DIALOGUE_DEPENDING_MODULES": "The following modules depend on the one you are closing:", "INFO_CONDUITEDISTRIBUTRICE_TITRE": "Distributor pipe", "INFO_CONDUITEDISTRIBUTRICE_TITRE_COURT": "D. pipe", "INFO_COURBEREMOUS_TITRE": "Backwater curves", @@ -60,6 +62,7 @@ "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Submerged", "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Zero flow", "INFO_DIALOG_COMPUTED_VALUE_TITLE": "Edit initial value", + "INFO_DIALOG_FIX_MISSING_DEPENDENCIES": "Fix missing dependencies", "INFO_DIALOG_LOAD_SESSION_FILENAME": "Choose a file", "INFO_DIALOG_LOAD_SESSION_TITLE": "Load calculator modules", "INFO_DIALOG_SAVE_SESSION_FILENAME": "File name", @@ -124,6 +127,11 @@ "INFO_LIB_ZDV": "Crest weir elevation or gate base", "INFO_LIB_ZRAM": "Upstream apron elevation", "INFO_LIB_ZT": "Triangle top elevation", + "INFO_LINKED_VALUE_DEVICE": "%s (%s, device %s)", + "INFO_LINKED_VALUE_RESULT": "%s (result of %s)", + "INFO_LINKED_VALUE_DEVICE_RESULT": "%s (result of %s, device %s)", + "INFO_LINKED_VALUE_EXTRA_RESULT": "%s (%s, extra result)", + "INFO_LINKED_VALUE_EXTRA_RESULT_OF": "%s (%s, extra result of %s)", "INFO_MACRORUGO_TITRE": "Rock-ramp fishpasses", "INFO_MACRORUGO_TITRE_COURT": "RR fishpasses", "INFO_MENU_HELP_TITLE": "Help", @@ -156,6 +164,7 @@ "INFO_PARAMFIELD_GRAPH_TYPE_HISTOGRAM": "Histogram", "INFO_PARAMFIELD_GRAPH_SELECT_X_AXIS": "Variable for X axis", "INFO_PARAMFIELD_GRAPH_SELECT_Y_AXIS": "Variable for Y axis", + "INFO_PARAMFIELD_CALCULATION_FAILED": "Calculation failed", "INFO_PARAMFIELD_IN_CALCULATION": "In calculation", "INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE": "initial value", "INFO_PARAMFIELD_PARAMCALCULER": "Calculate", @@ -194,12 +203,15 @@ "INFO_REMOUS_LARGEUR_BERGE": "Width at embankment level = %B% m", "INFO_REMOUS_RESSAUT_DEHORS": "Hydraulic jump detected %sens% abscissa %x% m", "INFO_REMOUS_RESSAUT_HYDRO": "Hydraulic jump detected between abscissa %xmin% and %xmax% m", + "INFO_REQUIRES": "requires", "INFO_SECTIONPARAMETREE_TITRE": "Parametric section", "INFO_SECTIONPARAMETREE_TITRE_COURT": "Param. section", "INFO_SETUP_NEWTON_MAX_ITER": "Newton iteration limit", "INFO_SETUP_PRECISION_AFFICHAGE": "Display accuracy", "INFO_SETUP_PRECISION_CALCUL": "Computation accuracy", "INFO_SETUP_TITLE": "Application setup", + "INFO_SNACKBAR_RESULTS_CALCULATED": "Results calculated for", + "INFO_SNACKBAR_RESULTS_INVALIDATED": "Results invalidated for", "INFO_SNACKBAR_SETTINGS_SAVED": "Settings saved on this device", "INFO_SNACKBAR_DEFAULT_SETTINGS_RESTORED": "Default settings restored", "INFO_THEME_CREDITS": "Credit", diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json index ea0bae031beaa5d1c775bd30ed78d428f35be3fa..c8d611b36384e93c205ce550552e61c34df6bfc1 100644 --- a/src/locale/messages.fr.json +++ b/src/locale/messages.fr.json @@ -22,6 +22,7 @@ "ERROR_PARAMDEF_VALUE_POS": "La valeur %value% du paramètre '%symbol%' est incorrecte (<=0)", "ERROR_PARAMDEF_VALUE_POSNULL": "La valeur %value% du paramètre '%symbol%' est incorrecte (<0)", "ERROR_PARAMDEF_VALUE_UNDEFINED": "La valeur du paramètre %symbol% n'est pas définie", + "ERROR_PARAMDEF_LINKED_VALUE_UNDEFINED": "La valeur du paramètre lié %symbol% n'est pas définie", "ERROR_PARAMDOMAIN_INTERVAL_BOUNDS": "Les bornes (%minValue%/%maxValue%) de l'intervalle sont incorrectes", "ERROR_PARAMDOMAIN_INVALID": "Paramètre '%symbol%' : le domaine de définition '%domain%' est incorrect", "ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT": "Condition limite amont > Hauteur critique : pas de calcul possible depuis l'amont", @@ -43,6 +44,7 @@ "INFO_CLOISONS_TITRE_COURT": "Cloisons", "INFO_CLOSE_DIALOGUE_TEXT": "Attention ! Les paramètres et résultats du module de calcul seront perdus. Vraiment fermer ?", "INFO_CLOSE_DIALOGUE_TITRE": "Confirmer la fermeture", + "INFO_CLOSE_DIALOGUE_DEPENDING_MODULES": "Les modules suivants dépendent de celui que vous êtes en train de fermer :", "INFO_CONDUITEDISTRIBUTRICE_TITRE": "Conduite distributrice", "INFO_CONDUITEDISTRIBUTRICE_TITRE_COURT": "Conduite distri.", "INFO_COURBEREMOUS_TITRE": "Courbes de remous", @@ -60,6 +62,7 @@ "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_2": "Noyé", "INFO_EXTRARES_ENUM_STRUCTUREFLOWREGIME_3": "Débit nul", "INFO_DIALOG_COMPUTED_VALUE_TITLE": "Modifier la valeur initiale", + "INFO_DIALOG_FIX_MISSING_DEPENDENCIES": "Résoudre les dépendances", "INFO_DIALOG_LOAD_SESSION_FILENAME": "Choisir un fichier", "INFO_DIALOG_LOAD_SESSION_TITLE": "Charger des modules de calcul", "INFO_DIALOG_SAVE_SESSION_FILENAME": "Nom de fichier", @@ -124,6 +127,11 @@ "INFO_LIB_ZDV": "Cote de la crête du déversoir ou du radier de la vanne", "INFO_LIB_ZRAM": "Cote du radier amont", "INFO_LIB_ZT": "Cote haute du triangle", + "INFO_LINKED_VALUE_DEVICE": "%s (%s, ouvrage %s)", + "INFO_LINKED_VALUE_RESULT": "%s (résultat de %s)", + "INFO_LINKED_VALUE_DEVICE_RESULT": "%s (résultat de %s, ouvrage %s)", + "INFO_LINKED_VALUE_EXTRA_RESULT": "%s (%s, résultat complémentaire)", + "INFO_LINKED_VALUE_EXTRA_RESULT_OF": "%s (%s, résultat complémentaire de %s)", "INFO_MENU_HELP_TITLE": "Aide", "INFO_MENU_LOAD_SESSION_TITLE": "Charger une session", "INFO_MENU_SAVE_SESSION_TITLE": "Enregistrer la session", @@ -156,6 +164,7 @@ "INFO_PARAMFIELD_GRAPH_TYPE_HISTOGRAM": "Histogramme", "INFO_PARAMFIELD_GRAPH_SELECT_X_AXIS": "Variable en abscisse", "INFO_PARAMFIELD_GRAPH_SELECT_Y_AXIS": "Variable en ordonnée", + "INFO_PARAMFIELD_CALCULATION_FAILED": "Échec du calcul", "INFO_PARAMFIELD_IN_CALCULATION": "En calcul", "INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE": "valeur initiale", "INFO_PARAMFIELD_PARAMCALCULER": "calculer", @@ -194,12 +203,15 @@ "INFO_REMOUS_LARGEUR_BERGE": "Largeur au niveau des berges = %B% m", "INFO_REMOUS_RESSAUT_DEHORS": "Ressaut hydraulique détecté à l'%sens% de l'abscisse %x% m", "INFO_REMOUS_RESSAUT_HYDRO": "Ressaut hydraulique détecté entre les abscisses %xmin% et %xmax% m", + "INFO_REQUIRES": "dépend de", "INFO_SECTIONPARAMETREE_TITRE": "Section paramétrée", "INFO_SECTIONPARAMETREE_TITRE_COURT": "Sec. param.", "INFO_SETUP_NEWTON_MAX_ITER": "Newton : nombre d'itérations maximum", "INFO_SETUP_PRECISION_AFFICHAGE": "Précision d'affichage", "INFO_SETUP_PRECISION_CALCUL": "Précision de calcul", "INFO_SETUP_TITLE": "Paramètres de l'application", + "INFO_SNACKBAR_RESULTS_CALCULATED": "Résultats calculés pour", + "INFO_SNACKBAR_RESULTS_INVALIDATED": "Résultats invalidés pour", "INFO_SNACKBAR_SETTINGS_SAVED": "Paramètres enregistrés sur cet appareil", "INFO_SNACKBAR_DEFAULT_SETTINGS_RESTORED": "Paramètres par défaut restaurés", "INFO_THEME_CREDITS": "Crédit",