Skip to content
Snippets Groups Projects
formulaire.service.ts 19 KiB
Newer Older
mathias.chouet's avatar
mathias.chouet committed
import {map} from "rxjs/operators";
import { Injectable } from "@angular/core";
import { Response } from "@angular/http";
import { Observable as rxObservable } from "rxjs";
import "rxjs/add/operator/toPromise";
import { saveAs } from "file-saver";
import { CalculatorType, EnumEx, Observable, ParamDefinition, ParamValueMode } from "jalhyd";
import { ServiceFactory } from "../service-factory";
import { HttpService } from "../../services/http/http.service";
import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
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 { FormulaireConduiteDistributrice } from "../../formulaire/definition/concrete/form-cond-distri";
import { FormulaireLechaptCalmon } from "../../formulaire/definition/concrete/form-lechapt-calmon";
import { FormulaireSectionParametree } from "../../formulaire/definition/concrete/form-section-parametree";
import { FormulaireCourbeRemous } from "../../formulaire/definition/concrete/form-courbe-remous";
import { FormulaireRegimeUniforme } from "../../formulaire/definition/concrete/form-regime-uniforme";
import { FormulairePasseBassinDimensions } from "../../formulaire/definition/concrete/form-passe-bassin-dim";
import { FormulairePasseBassinPuissance } from "../../formulaire/definition/concrete/form-passe-bassin-puissance";
import { FormulaireParallelStructure } from "../../formulaire/definition/concrete/form-parallel-structures";
import { NgParameter } from "../../formulaire/ngparam";

@Injectable()
export class FormulaireService extends Observable {
    private _formulaires: FormulaireDefinition[];

    private _currentFormId = -1;

        super();
        this._formulaires = [];
    }

    private get _intlService(): InternationalisationService {
        return ServiceFactory.instance.internationalisationService;
    }

    private get _httpService(): HttpService {
        return ServiceFactory.instance.httpService;
    }

    public get formulaires(): FormulaireDefinition[] {
        return this._formulaires;
    }

    private loadLocalisation(calc: CalculatorType): Promise<any> {
mathias.chouet's avatar
mathias.chouet committed
        const f: string = this.getConfigPathPrefix(calc) + this._intlService.currentLanguage.tag + ".json";
        const prom = this._httpService.httpGetRequestPromise(undefined, undefined, undefined, f);
        return prom.then((j) => {
            return j as StringMap;
    }

    /**
     * met à jour la langue du formulaire
     * @param formId id unique du formulaire
     * @param localisation ensemble id-message traduit
     */
    private updateFormulaireLocalisation(formId: number, localisation: StringMap) {
mathias.chouet's avatar
mathias.chouet committed
        for (const f of this._formulaires) {
David Dorchies's avatar
David Dorchies committed
            if (f.uid === formId) {
                f.updateLocalisation(localisation);
                break;
            }
    /**
     * charge la localisation et met à jour la langue du formulaire
     */
    private loadUpdateFormulaireLocalisation(f: FormulaireDefinition): Promise<FormulaireDefinition> {
        return this.loadLocalisation(f.calculatorType)
            .then(localisation => {
                this.updateFormulaireLocalisation(f.uid, localisation);
            });
    }

    public updateLocalisation() {
        for (const c of EnumEx.getValues(CalculatorType)) {
            const prom: Promise<StringMap> = this.loadLocalisation(c);
            prom.then(loc => {
David Dorchies's avatar
David Dorchies committed
                for (const f of this._formulaires) {
                    if (f.calculatorType === c) {
                        this.updateFormulaireLocalisation(f.uid, loc);
    public getLocalisedTitleFromCalculatorType(type: CalculatorType) {
        const sCalculator: string = CalculatorType[type].toUpperCase();
        return this._intlService.localizeText(`INFO_${sCalculator}_TITRE`);
    public loadConfig(ct: CalculatorType): Promise<any> {
        const f: string = this.getConfigPathPrefix(ct) + "config.json";
        return this._httpService.httpGetRequestPromise(undefined, undefined, undefined, f);
    private newFormulaire(ct: CalculatorType, jsonState?: {}): FormulaireDefinition {
        let f: FormulaireDefinition;
        switch (ct) {
            case CalculatorType.ConduiteDistributrice:
                f = new FormulaireConduiteDistributrice();
                f = new FormulairePasseBassinDimensions();
                f = new FormulairePasseBassinPuissance();
            case CalculatorType.Dever:
            case CalculatorType.Cloisons:
                f = new FormulaireParallelStructure();
mathias.chouet's avatar
mathias.chouet committed
                throw new Error(`FormulaireService.newFormulaire() : type de calculette ${CalculatorType[ct]} non pris en charge`);
        f.defaultProperties["calcType"] = ct;

        return f;
    }

    /**
     * crée un formulaire d'un type donné
     * @param ct type de formulaire
     */
    public createFormulaire(ct: CalculatorType, jsonState?: {}): Promise<FormulaireDefinition> {
        const f: FormulaireDefinition = this.newFormulaire(ct);
        this._formulaires.push(f);
mathias.chouet's avatar
mathias.chouet committed
        const prom: Promise<any> = this.loadConfig(ct);
        return prom.then(s => {
            f.preparseConfig(JSON.parse(s));
            return f;
        }).then(f => {
            if (jsonState === undefined) {
                f.calculatorName = decode(this.getLocalisedTitleFromCalculatorType(ct) + " (" + f.uid + ")");
            }
            return f;
        }).then(f => {
            f.parseConfig(undefined);
            return f;
        }).then(f => {
            if (jsonState !== undefined) {
                f.deserialiseJSON(jsonState);
                const props = jsonState["props"];
            }
            return f;
        }).then(f => {
            // la méthode loadUpdateFormulaireLocalisation retourne une Promise; le fait de retourner une Promise dans un then
            // fait que le then suivant est exécuté juste après.
            return this.loadUpdateFormulaireLocalisation(f);
        }).then(f => {
            f.applyDependencies();
            return f;
        }).then(f => {
                    "action": "createForm",
            return f;
        });
    public getFormulaireFromId(uid: number): FormulaireDefinition {
mathias.chouet's avatar
mathias.chouet committed
        for (const f of this._formulaires) {
            if (f.uid === uid) {
                return f;
mathias.chouet's avatar
mathias.chouet committed
            }
        }
    public getInputField(formId: number, elemId: string): InputField {
        const s = this.getFormulaireElementById(formId, elemId);
mathias.chouet's avatar
mathias.chouet committed
        if (!(s instanceof InputField)) {
            throw new Error("Form element with id '" + elemId + "' is not an input");
        }
        return <InputField>s;
    }

    public getCheckField(formId: number, elemId: string): CheckField {
        const s = this.getFormulaireElementById(formId, elemId);
mathias.chouet's avatar
mathias.chouet committed
        if (!(s instanceof CheckField)) {
            throw new Error("Form element with id '" + elemId + "' is not a checkbox");
mathias.chouet's avatar
mathias.chouet committed
        }
        return <CheckField>s;
    public getSelectField(formId: number, elemId: string): SelectField {
        const s = this.getFormulaireElementById(formId, elemId);
mathias.chouet's avatar
mathias.chouet committed
        if (!(s instanceof SelectField)) {
            throw new Error("Form element with id '" + elemId + "' is not a select");
mathias.chouet's avatar
mathias.chouet committed
        }
        return <SelectField>s;
    private getFormulaireElementById(formId: number, elemId: string): FormulaireElement {
mathias.chouet's avatar
mathias.chouet committed
        for (const f of this._formulaires) {
David Dorchies's avatar
David Dorchies committed
            if (f.uid === formId) {
                const s = f.getFormulaireNodeById(elemId);
mathias.chouet's avatar
mathias.chouet committed
                if (s !== undefined) {
mathias.chouet's avatar
mathias.chouet committed
                }
mathias.chouet's avatar
mathias.chouet committed
        }
    public getParamdefParentForm(prm: ParamDefinition): FormulaireDefinition {
mathias.chouet's avatar
mathias.chouet committed
        for (const f of this._formulaires) {
            for (const p of f.allFormElements) {
                if (p instanceof NgParameter) {
                    if (p.paramDefinition.uid === prm.uid) {
    public getConfigPathPrefix(ct: CalculatorType): string {
mathias.chouet's avatar
mathias.chouet committed
        if (ct === undefined) {
            throw new Error("FormulaireService.getConfigPathPrefix() : invalid undefined CalculatorType");
        }

        switch (ct) {
            case CalculatorType.ConduiteDistributrice:
                return "app/calculators/cond_distri/cond_distri.";

            case CalculatorType.LechaptCalmon:
                return "app/calculators/lechapt-calmon/lechapt-calmon.";

            case CalculatorType.SectionParametree:
                return "app/calculators/section-param/section-param.";

            case CalculatorType.RegimeUniforme:
                return "app/calculators/regime-uniforme/regime-uniforme.";

            case CalculatorType.CourbeRemous:
                return "app/calculators/remous/remous.";

            case CalculatorType.PabDimensions:
                return "app/calculators/pab-dimensions/pab-dimensions.";

            case CalculatorType.PabPuissance:
                return "app/calculators/pab-puissance/pab-puissance.";

francois.grand's avatar
francois.grand committed
            case CalculatorType.Structure:
                return "app/calculators/ouvrages/ouvrages.";

            case CalculatorType.ParallelStructure:
                return "app/calculators/parallel-structures/parallel-structures.";

            case CalculatorType.Dever:
                return "app/calculators/dever/dever.";

            case CalculatorType.Cloisons:
                return "app/calculators/cloisons/cloisons.";

mathias.chouet's avatar
mathias.chouet committed
                throw new Error("FormulaireService.getConfigPathPrefix() : valeur de CalculatorType " + ct + " non implémentée");

    public requestCloseForm(uid: number) {
        const form = this.getFormulaireFromId(uid);
David Dorchies's avatar
David Dorchies committed
        if (form !== undefined) {
            this._formulaires = this._formulaires.filter(f => f.uid !== uid);

            this.notifyObservers({
                "action": "closeForm",
            });
        }
    }

    public get currentFormId() {
        return this._currentFormId;
    }

    public setCurrentForm(formId: number) {
        const form = this.getFormulaireFromId(formId);
David Dorchies's avatar
David Dorchies committed
        if (form === undefined) {
            this._currentFormId = -1;
            this.notifyObservers({
                "action": "invalidFormId",
                "formId": formId
            });
David Dorchies's avatar
David Dorchies committed
        } else {
            this._currentFormId = formId;
            this.notifyObservers({
                "action": "currentFormChanged",
                "formId": formId

    private get currentForm(): FormulaireDefinition {
        return this.getFormulaireFromId(this._currentFormId);
    }

    public get currentFormHasResults(): boolean {
        const form = this.currentForm;
mathias.chouet's avatar
mathias.chouet committed
        if (form !== undefined) {
mathias.chouet's avatar
mathias.chouet committed
        }
    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);
        });
    }

    /**
     * charge une session en tenant compte des calculettes sélectionnées
     * @param f fichier session
     * @param formInfos infos sur les calculettes @see LoadCalculatorComponent._calculators
     */
    public loadSession(f: File, formInfos: any[]) {
        this.readSingleFile(f).then(s => {
            const session = JSON.parse(s);
mathias.chouet's avatar
mathias.chouet committed
            for (const k in session) {
                switch (k) {
                    case "session":
                        this.deserialiseSession(session[k], formInfos);
                        break;

                    default:
                        throw new Error(`session file : invalid key '${k}'`);
                }
mathias.chouet's avatar
mathias.chouet committed
            }
        }).catch(err => {
            throw err;
        });
    public saveSession(session: {}, filename?: string) {
        const blob = new Blob([JSON.stringify(session)], { type: "text/plain;charset=utf-8" });
    /**
     * obtient des infos (nom, uid des calculettes) d'un fichier session
     * @param f fichier session
     */
    public calculatorInfosFromSessionFile(f: File): Promise<any[]> {
        return this.readSingleFile(f).then(s => {
            const res: any[] = [];
            const session = JSON.parse(s);

            // liste des noms de calculettes
mathias.chouet's avatar
mathias.chouet committed
            for (const k in session) {
                switch (k) {
                    case "session":
                        const sess = session[k];
                        const elems = sess["elements"];
mathias.chouet's avatar
mathias.chouet committed
                        for (const e of elems) {
                            for (const k in e) {
                                if (k === "form") {
                                    const form = e[k];
                                    res.push({ "uid": form["uid"], "title": form["id"] });
                                }
mathias.chouet's avatar
mathias.chouet committed
                            }
                        }
                        break;

                    default:
                        throw new Error(`session file : invalid key '${k}'`);
                }
mathias.chouet's avatar
mathias.chouet committed
            }
    public saveForm(f: FormulaireDefinition) {
        this.notifyObservers({
            "action": "saveForm",
            "form": f
        });
    }

    private deserialiseForm(elements: {}) {
        const props = elements["props"];
        const ct: CalculatorType = props["calcType"];
        this.createFormulaire(ct, elements);
    }

    private deserialiseSessionElement(element: {}, formInfos: any[]) {
        const keys = Object.keys(element);
mathias.chouet's avatar
mathias.chouet committed
        if (keys.length !== 1) {
            throw new Error(`session file : invalid session object '${element}'`);
mathias.chouet's avatar
mathias.chouet committed
        }
mathias.chouet's avatar
mathias.chouet committed
                for (const i of formInfos) {
                    if (i["uid"] === form["uid"] && i["selected"]) {
mathias.chouet's avatar
mathias.chouet committed
                    }
                }
                break;

            default:
                throw new Error(`session file : invalid key '${keys[0]}' in session object`);
        }
    }

    private deserialiseSession(elements: {}, formInfos: any[]) {
mathias.chouet's avatar
mathias.chouet committed
        for (const ks in elements) {
mathias.chouet's avatar
mathias.chouet committed
                    for (const e of elements[ks]) {
                        this.deserialiseSessionElement(e, formInfos);
mathias.chouet's avatar
mathias.chouet committed
                    }
                    break;

                default:
                    throw new Error(`session file : invalid key '${ks}' in session object`);
            }
mathias.chouet's avatar
mathias.chouet committed
        }
     * @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 de la calculette 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[] = [];
mathias.chouet's avatar
mathias.chouet committed
        if (p !== undefined) {
                try {
                    // on vérifie que le paramètre en entrée appartient au nub
                    const np = sn.nub.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, exclude);
                    for (const np of ps) {
                        np["formTitle"] = f.calculatorName;
                        res.push(np);
                    }
mathias.chouet's avatar
mathias.chouet committed
                } catch (e) {
mathias.chouet's avatar
mathias.chouet committed
        }

    /**
     * filtre les valeurs liables à un paramètre :
     * - supprime les valeurs non affichées dans leur parent (ce n'est pas le cas par ex pour Q, Z1, Z2 dans les ouvrages enfants des ouvrages //)
     * @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;
mathias.chouet's avatar
mathias.chouet committed
                if (parentForm !== undefined) {
                    for (const fe of parentForm.allFormElements) {
                        if (fe instanceof NgParameter) {
                            if (fe.paramDefinition.uid === prm.uid) {
                                found = true;
                                break;
                            }
mathias.chouet's avatar
mathias.chouet committed
                if (!found) {
mathias.chouet's avatar
mathias.chouet committed
                }