[open-ils-commits] [GIT] Evergreen ILS branch master updated. 7fea3dac33d9be540fd938729101381fd5161278

Evergreen Git git at git.evergreen-ils.org
Wed Feb 26 14:28:17 EST 2020


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  7fea3dac33d9be540fd938729101381fd5161278 (commit)
       via  faf06e1fc43ff91bf4a7d52f175fc351f9646123 (commit)
      from  05a0118911e9d94d47e0038330487e6d35419a05 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 7fea3dac33d9be540fd938729101381fd5161278
Author: Mike Risher <mrisher at catalyte.io>
Date:   Tue Sep 24 17:41:27 2019 +0000

    lp1845240 port of Surveys UI from DOJO to Angular
    
    idlClass asv holds the surveys, asvq holds their questions, and
    asva holds the answers to those questions. The surveys are in
    their own module and are lazy loaded
    
    Signed-off-by: Mike Risher <mrisher at catalyte.io>
    
    Modernize the survey create API by migrating it to cstore.
    Additionally, make it possible to modify an existing survey top-level
    object by setting 'ischanged' to the inbound survey.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    
    LP1845240 Migrate survey create API to cstore
    
    Modernize the survey create API by migrating it to cstore.
    New API supports full range of isnew / ischanged / isdeleted actions on
    the survey, questions, and answers.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    
    LP1845240 Survey API returns updated fleshed survey
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>
    
     Changes to be committed:
            modified:   Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
            modified:   Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
            new file:   Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.html
            new file:   Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.ts
            new file:   Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-routing.module.ts
            new file:   Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.html
            new file:   Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.ts
            new file:   Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.module.ts
            modified:   Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Survey.pm
            modified:   Open-ILS/tests/datasets/sql/surveys.sql
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
index e051d37c42..223d1817cc 100644
--- a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
@@ -63,11 +63,10 @@
     <eg-link-table-link i18n-label label="Statistical Popularity Badges" 
       routerLink="/staff/admin/local/rating/badge"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Surveys" 
-      url="/eg/staff/admin/local/action/survey"></eg-link-table-link>
+      routerLink="/staff/admin/local/action/survey"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Transit List" 
       url="/eg/staff/circ/transits/list"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Work Log" 
       url="/eg/staff/admin/workstation/log"></eg-link-table-link>
-
   </eg-link-table>
 </div>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
index 39c6be7179..15a9153201 100644
--- a/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
@@ -23,6 +23,9 @@ const routes: Routes = [{
     path: 'config/standing_penalty',
     component: StandingPenaltyComponent
 }, {
+    path: 'action/survey',
+    loadChildren: '@eg/staff/admin/local/survey/survey.module#SurveyModule'
+}, {
     path: ':schema/:table',
     component: BasicAdminPageComponent
 }];
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.html
new file mode 100644
index 0000000000..86f2f20d81
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.html
@@ -0,0 +1,124 @@
+<eg-staff-banner bannerText="Survey ID # {{surveyId}}" i18n-bannerText
+                class="mb-3"></eg-staff-banner>
+<ngb-tabset #surveyTabs [activeId]="surveyTab" (tabChange)="onTabChange($event)" class="mb-3">
+    <ngb-tab title="Edit Survey" i18n-title id="edit">
+        <ng-template ngbTabContent>
+            <div class="col-lg-6 offset-lg-3 mt-3">
+                <div style="text-align: center;">
+                    <button class="p-2 mb-3 btn btn-danger btn-lg" 
+                    (click)="endSurvey()" i18n>
+                        End Survey Now
+                    </button>
+                </div>
+                <eg-fm-record-editor displayMode="inline" 
+                    hiddenFieldsList="id"
+                    datetimeFieldsList="start_date,end_date"
+                    idlClass="asv" 
+                    mode="update" 
+                    [record]="surveyObj">
+                </eg-fm-record-editor>
+            </div>
+        </ng-template>
+    </ngb-tab>
+    <ngb-tab title="Questions and Answers" i18n-title id="qanda">
+        <ng-template ngbTabContent>
+            <div class="col-lg-8 offset-lg-2 mt-3">
+                <eg-staff-banner bannerText="Questions & Answers" i18n-bannerText>
+                    </eg-staff-banner>
+                <div *ngFor="let question of localArray; let questionIndex = index;">
+                    <div class="mb-3 mt-3 p-2 bg-light input-group">
+                        <label class="input-group-text">
+                            <b>Question</b>
+                        </label>
+                        <input type="text" [(ngModel)]="question.words" class="form-control"
+                            name="question-{{questionIndex}}">
+                        <span class="input-group-append">
+                            <button class="ml-2 btn btn-info" 
+                                (click)="updateQuestion(question)" i18n>
+                                Save
+                            </button>
+                            <button class="ml-1 btn btn-danger"
+                                (click)="deleteQuestion(question)" i18n>
+                                Delete Question & Answers
+                            </button>
+                        </span>
+                    </div>
+                    <div *ngFor="let answer of question.answers; let answerIndex = index;" 
+                        class="mb-2 input-group">
+                        <input class="form-control" type="text" 
+                            [(ngModel)]="answer.words"
+                            name="answer-{{questionIndex}}-{{answerIndex}}">
+                        <span class="input-group-append">
+                            <button class="ml-2 btn btn-info" 
+                                (click)="updateAnswer(answer, question, questionIndex, answerIndex)"
+                                i18n>
+                                Save
+                            </button>
+                            <button class="ml-1 btn btn-danger" (click)="deleteAnswer(answer)"
+                                i18n>
+                                Delete
+                            </button>
+                        </span>
+                    </div>
+                    <div class="mb-2 input-group">
+                        <input class="form-control" type="text" 
+                            [(ngModel)]="newAnswerArray[questionIndex].inputText"
+                                value="">
+                        <span class="input-group-append">
+                            <button class="ml-2 btn btn-info" 
+                                (click)="createAnswer(newAnswerArray[questionIndex].inputText, question)"
+                                i18n>
+                                Add Answer
+                            </button>
+                        </span>
+                    </div>
+                </div>
+                <div class="mb-3 mt-3 p-2 bg-light input-group">
+                    <label class="input-group-text">
+                        <b>New Question</b>
+                    </label>
+                    <input #newQuestionInput 
+                        class="form-control" 
+                        type="text" 
+                        [(ngModel)]="newQuestionText"
+                        name="question-new" value="">
+                    <span class="input-group-append">
+                        <button class="ml-2 btn btn-info"
+                            (click)="createQuestion(newQuestionText)" i18n>
+                            Save Question & Add Answer
+                        </button>
+                    </span>
+                </div>
+            </div>
+        </ng-template>
+    </ngb-tab>
+</ngb-tabset>
+
+<eg-string #createAnswerString i18n-text text="New Answer Added"></eg-string>
+<eg-string #createAnswerErrString i18n-text text="Failed to Create New Answer">
+    </eg-string>
+<eg-string #createQuestionString i18n-text text="New Question Added"></eg-string>
+<eg-string #createQuestionErrString i18n-text text="Failed to Create New Question">
+    </eg-string>
+<eg-string #delAnswerSuccessStr i18n-text text="Survey Answer deleted">
+    </eg-string>
+<eg-string #delAnswerFailStr i18n-text text="Survey Answer deletion failed">
+    </eg-string>
+<eg-string #delQuestionSuccessStr i18n-text text="Survey Question deleted">
+    </eg-string>
+<eg-string #delQuestionFailStr i18n-text text="Survey Question deletion failed">
+    </eg-string>
+<eg-string #updateAnswerSuccessStr i18n-text text="Survey Answer updated">
+    </eg-string>
+<eg-string #updateAnswerFailStr i18n-text text="Survey Answer update failed">
+    </eg-string>
+<eg-string #updateQuestionSuccessStr i18n-text text="Survey Question updated">
+    </eg-string>
+<eg-string #updateQuestionFailStr i18n-text text="Survey Question update failed">
+    </eg-string>
+<eg-string #endSurveyFailedString i18n-text 
+    text="Ending Survey failed or was not allowed"></eg-string>
+<eg-string #endSurveySuccessString i18n-text text="Survey ended"></eg-string>
+<eg-string #questionAlreadyStartedErrString i18n-text 
+    text="The survey Start Date must be set for the future to add new questions or modify existing questions.">
+    </eg-string>
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.ts
new file mode 100644
index 0000000000..73b3a7bca6
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.ts
@@ -0,0 +1,307 @@
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {IdlObject, IdlService } from '@eg/core/idl.service';
+import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+
+ at Component({
+    templateUrl: './survey-edit.component.html'
+})
+
+export class SurveyEditComponent implements OnInit {
+    surveyId: number;
+    surveyObj: IdlObject;
+    localArray: any;
+    newAnswerArray: object[];
+    newQuestionText: string;
+    surveyTab: string;
+
+    @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+
+    @ViewChild('createAnswerString', { static: true })
+        createAnswerString: StringComponent;
+    @ViewChild('createAnswerErrString', { static: true })
+        createAnswerErrString: StringComponent;
+    @ViewChild('createQuestionString', { static: true })
+        createQuestionString: StringComponent;
+    @ViewChild('createQuestionErrString', { static: true })
+        createQuestionErrString: StringComponent;
+
+    @ViewChild('updateQuestionSuccessStr', { static: true })
+        updateQuestionSuccessStr: StringComponent;
+    @ViewChild('updateQuestionFailStr', { static: true })
+        updateQuestionFailStr: StringComponent;
+    @ViewChild('updateAnswerSuccessStr', { static: true })
+        updateAnswerSuccessStr: StringComponent;
+    @ViewChild('updateAnswerFailStr', { static: true })
+        updateAnswerFailStr: StringComponent;
+
+    @ViewChild('delAnswerSuccessStr', { static: true })
+        delAnswerSuccessStr: StringComponent;
+    @ViewChild('delAnswerFailStr', { static: true })
+        delAnswerFailStr: StringComponent;
+    @ViewChild('delQuestionSuccessStr', { static: true })
+        delQuestionSuccessStr: StringComponent;
+    @ViewChild('delQuestionFailStr', { static: true })
+        delQuestionFailStr: StringComponent;
+
+    @ViewChild('endSurveyFailedString', { static: true })
+        endSurveyFailedString: StringComponent;
+    @ViewChild('endSurveySuccessString', { static: true })
+        endSurveySuccessString: StringComponent;
+    @ViewChild('questionAlreadyStartedErrString', { static: true })
+        questionAlreadyStartedErrString: StringComponent;
+
+    constructor(
+        private auth: AuthService,
+        private net: NetService,
+        private route: ActivatedRoute,
+        private toast: ToastService,
+        private idl: IdlService,
+    ) {
+    }
+
+    ngOnInit() {
+        this.surveyId = parseInt(this.route.snapshot.paramMap.get('id'), 10);
+        this.updateData();
+    }
+
+    updateData() {
+        this.newQuestionText = '';
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.fleshed.retrieve',
+            this.surveyId
+        ).subscribe(res => {
+            this.surveyObj = res;
+            this.buildLocalArray(res);
+            return res;
+        });
+    }
+
+    onTabChange(event: NgbTabChangeEvent) {
+        this.surveyTab = event.nextId;
+    }
+
+    buildLocalArray(res) {
+        this.localArray = [];
+        this.newAnswerArray = [];
+        const allQuestions = res.questions();
+        allQuestions.forEach((question, index) => {
+            this.newAnswerArray.push({inputText: ''});
+            question.words = question.question();
+            question.answers = question.answers();
+            this.localArray.push(question);
+            question.answers.forEach(answer => {
+                answer.words = answer.answer();
+            });
+            this.sortAnswers(index);
+        });
+        this.sortQuestions();
+    }
+
+    sortQuestions() {
+        this.localArray.sort(function(a, b) {
+            const q1 = a.question().toUpperCase();
+            const q2 = b.question().toUpperCase();
+            return (q1 < q2) ? -1 : (q1 > q2) ? 1 : 0;
+        });
+    }
+
+    sortAnswers(questionIndex) {
+        this.localArray[questionIndex].answers.sort(function(a, b) {
+            const a1 = a.answer().toUpperCase();
+            const a2 = b.answer().toUpperCase();
+            return (a1 < a2) ? -1 : (a1 > a2) ? 1 : 0;
+        });
+    }
+
+    updateQuestion(questionToChange) {
+        if (this.surveyHasBegun()) {
+            return;
+        }
+        questionToChange.question(questionToChange.words);
+        questionToChange.ischanged(true);
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.update',
+            this.auth.token(), this.surveyObj
+        ).subscribe(res => {
+            if (res.debug) {
+                this.updateQuestionFailStr.current().then(msg => this.toast.warning(msg));
+                return res;
+            } else {
+                this.surveyObj = res;
+                this.buildLocalArray(this.surveyObj);
+                this.updateQuestionSuccessStr.current().then(msg => this.toast.success(msg));
+                return res;
+            }
+        });
+    }
+
+    deleteQuestion(questionToDelete) {
+        if (this.surveyHasBegun()) {
+            return;
+        }
+        questionToDelete.isdeleted(true);
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.update',
+            this.auth.token(), this.surveyObj
+        ).subscribe(res => {
+            if (res.debug) {
+                this.delQuestionFailStr.current().then(msg => this.toast.warning(msg));
+                return res;
+            } else {
+                this.surveyObj = res;
+                this.buildLocalArray(this.surveyObj);
+                this.delQuestionSuccessStr.current().then(msg => this.toast.success(msg));
+                return res;
+            }
+
+        });
+    }
+
+    createQuestion(newQuestionText) {
+        if (this.surveyHasBegun()) {
+            return;
+        }
+        const newQuestion = this.idl.create('asvq');
+        newQuestion.question(newQuestionText);
+        newQuestion.isnew(true);
+        let questionObjects = [];
+        questionObjects = this.surveyObj.questions();
+        questionObjects.push(newQuestion);
+        this.surveyObj.questions(questionObjects);
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.update',
+            this.auth.token(), this.surveyObj
+        ).subscribe(res => {
+            if (res.debug) {
+                this.newQuestionText = '';
+                this.createQuestionErrString.current().then(msg => this.toast.warning(msg));
+                return res;
+            } else {
+                this.surveyObj = res;
+                this.buildLocalArray(this.surveyObj);
+                this.newQuestionText = '';
+                this.createQuestionString.current().then(msg => this.toast.success(msg));
+                return res;
+            }
+
+        });
+    }
+
+    deleteAnswer(answerObj) {
+        if (this.surveyHasBegun()) {
+            return;
+        }
+        answerObj.isdeleted(true);
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.update',
+            this.auth.token(), this.surveyObj
+        ).subscribe(res => {
+            if (res.debug) {
+                this.delAnswerFailStr.current().then(msg => this.toast.warning(msg));
+                return res;
+            } else {
+                this.surveyObj = res;
+                this.buildLocalArray(this.surveyObj);
+                this.delAnswerSuccessStr.current().then(msg => this.toast.success(msg));
+                return res;
+            }
+        });
+    }
+
+    updateAnswer(answerObj) {
+        if (this.surveyHasBegun()) {
+            return;
+        }
+        answerObj.answer(answerObj.words);
+        answerObj.ischanged(true);
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.update',
+            this.auth.token(), this.surveyObj
+        ).subscribe(res => {
+            if (res.debug) {
+                this.updateAnswerFailStr.current().then(msg => this.toast.warning(msg));
+                return res;
+            } else {
+                this.surveyObj = res;
+                this.buildLocalArray(this.surveyObj);
+                this.updateAnswerSuccessStr.current().then(msg => this.toast.success(msg));
+                return res;
+            }
+        });
+    }
+
+    createAnswer(newAnswerText, questionObj) {
+        // Create answer *is* allowed if survey has already begun
+        const questionId = questionObj.id();
+        const newAnswer = this.idl.create('asva');
+        newAnswer.answer(newAnswerText);
+        newAnswer.question(questionId);
+        newAnswer.isnew(true);
+        questionObj.answers.push(newAnswer);
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.update',
+            this.auth.token(), this.surveyObj
+        ).subscribe(res => {
+            if (res.debug) {
+                this.createAnswerErrString.current().then(msg => this.toast.warning(msg));
+                return res;
+            } else {
+                this.surveyObj = res;
+                this.buildLocalArray(this.surveyObj);
+                this.createAnswerString.current().then(msg => this.toast.success(msg));
+                return res;
+            }
+        });
+    }
+
+    endSurvey() {
+        const today = new Date().toISOString();
+        this.surveyObj.end_date(today);
+        this.surveyObj.ischanged(true);
+        // to get fm-editor to display changed date we need to set
+        // this.surveyObj to null temporarily
+        const surveyClone = this.idl.clone(this.surveyObj);
+        this.surveyObj = null;
+        this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.survey.update',
+            this.auth.token(), surveyClone
+        ).subscribe(res => {
+            if (res.debug) {
+                this.endSurveyFailedString.current().then(msg => this.toast.warning(msg));
+                return res;
+            } else {
+                this.surveyObj = res;
+                this.surveyObj.ischanged(false);
+                this.buildLocalArray(this.surveyObj);
+                this.endSurveySuccessString.current().then(msg => this.toast.success(msg));
+                return res;
+            }
+        });
+    }
+
+    surveyHasBegun() {
+        const surveyStartDate = new Date(this.surveyObj.start_date());
+        const now = new Date();
+        if (surveyStartDate <= now) {
+            this.questionAlreadyStartedErrString.current().then(msg =>
+                this.toast.warning(msg));
+            return true;
+        }
+        return false;
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-routing.module.ts
new file mode 100644
index 0000000000..cd36869e70
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-routing.module.ts
@@ -0,0 +1,22 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {SurveyComponent} from './survey.component';
+import {SurveyEditComponent} from './survey-edit.component';
+
+const routes: Routes = [{
+    path: '',
+    component: SurveyComponent
+}, {
+    path: ':id',
+    component: SurveyEditComponent
+}];
+
+
+ at NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class SurveyRoutingModule {}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.html
new file mode 100644
index 0000000000..394d837c16
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.html
@@ -0,0 +1,37 @@
+<eg-staff-banner bannerText="Survey Configuration" i18n-bannerText>
+</eg-staff-banner>
+
+<eg-grid #grid idlClass="asv" [dataSource]="gridDataSource" 
+[sortable]="true">
+    <eg-grid-toolbar-button label="New Survey" i18n-label [action]="createNew">
+    </eg-grid-toolbar-button>
+    <eg-grid-toolbar-action label="Edit Selected" i18n-label [action]="editSelected">
+    </eg-grid-toolbar-action>
+    <eg-grid-toolbar-action label="Delete Selected" i18n-label 
+    (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+    <eg-grid-toolbar-action label="End Survey Now" i18n-label 
+    (onClick)="endSurvey($event)"></eg-grid-toolbar-action>
+</eg-grid>
+
+<eg-fm-record-editor 
+    datetimeFieldsList="start_date,end_date"
+    hiddenFieldsList="id"
+    #editDialog 
+    idlClass="asv">
+</eg-fm-record-editor>
+
+<eg-string #createString i18n-text text="New Survey Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New Survey">
+    </eg-string>
+<eg-string #endSurveyFailedString i18n-text 
+    text="Ending Survey failed or was not allowed"></eg-string>
+<eg-string #endSurveySuccessString i18n-text text="Survey ended">
+    </eg-string>
+<eg-string #deleteFailedString i18n-text 
+    text="Delete of Survey failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Survey succeeded">
+    </eg-string>
+<eg-string #successString i18n-text text="Update of Survey succeeded">
+    </eg-string>
+<eg-string #updateFailedString i18n-text text="Update of Survey succeeded">
+    </eg-string>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.ts
new file mode 100644
index 0000000000..9865a4b3e6
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.ts
@@ -0,0 +1,122 @@
+import {Pager} from '@eg/share/util/pager';
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {Router} from '@angular/router';
+import {IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+
+ at Component({
+    templateUrl: './survey.component.html'
+})
+
+export class SurveyComponent implements OnInit {
+
+    gridDataSource: GridDataSource;
+
+    @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+    @ViewChild('grid', { static: true }) grid: GridComponent;
+    @ViewChild('successString', { static: true }) successString: StringComponent;
+    @ViewChild('createString', { static: true }) createString: StringComponent;
+    @ViewChild('createErrString', { static: true }) createErrString: StringComponent;
+    @ViewChild('updateFailedString', { static: true }) updateFailedString: StringComponent;
+    @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;
+    @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
+    @ViewChild('endSurveyFailedString', { static: true }) endSurveyFailedString: StringComponent;
+    @ViewChild('endSurveySuccessString', { static: true }) endSurveySuccessString: StringComponent;
+
+    @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+    constructor(
+        private auth: AuthService,
+        private net: NetService,
+        private pcrud: PcrudService,
+        private toast: ToastService,
+        private router: Router
+    ) {
+        this.gridDataSource = new GridDataSource();
+    }
+
+    ngOnInit() {
+        this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+            return this.pcrud.retrieveAll('asv', {});
+        };
+
+        this.grid.onRowActivate.subscribe(
+            (idlThing: IdlObject) => {
+                const idToEdit = idlThing.id();
+                this.navigateToEditPage(idToEdit);
+            }
+        );
+    }
+
+    showEditDialog(idlThing: IdlObject): Promise<any> {
+        return;
+    }
+
+    editSelected = (surveys: IdlObject[]) => {
+        const idToEdit = surveys[0].id();
+        this.navigateToEditPage(idToEdit);
+    }
+
+    endSurvey = (surveys: IdlObject[]) => {
+        const today = new Date().toISOString();
+        for (let i = 0; i < surveys.length; i++) {
+            surveys[i].end_date(today);
+            this.pcrud.update(surveys[i]).toPromise().then(
+                async (ok) => {
+                    this.toast.success(await this.endSurveySuccessString.current());
+                },
+                async (err) => {
+                    this.toast.warning(await this.endSurveyFailedString.current());
+                }
+            );
+        }
+    }
+
+    deleteSelected = (surveys: IdlObject[]) => {
+        for (let i = 0; i < surveys.length; i++) {
+            const idToDelete = surveys[i].id();
+            this.net.request(
+                'open-ils.circ',
+                'open-ils.circ.survey.delete.cascade.override',
+                this.auth.token(), idToDelete
+            ).subscribe(res => {
+                this.deleteSuccessString.current()
+                    .then(str => this.toast.success(str));
+                this.grid.reload();
+                return res;
+            }, (err) => {
+                this.deleteFailedString.current()
+                    .then(str => this.toast.success(str));
+            });
+        }
+    }
+
+    navigateToEditPage(id: any) {
+        this.router.navigate(['/staff/admin/local/action/survey/' + id]);
+    }
+
+    createNew = () => {
+        this.editDialog.mode = 'create';
+        this.editDialog.datetimeFields = 'start_date,end_date';
+        this.editDialog.open({size: this.dialogSize}).subscribe(
+            ok => {
+                this.createString.current()
+                    .then(str => this.toast.success(str));
+                this.grid.reload();
+            },
+            rejection => {
+                if (!rejection.dismissed) {
+                    this.createErrString.current()
+                        .then(str => this.toast.danger(str));
+                }
+            }
+        );
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.module.ts
new file mode 100644
index 0000000000..21ba53f64e
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.module.ts
@@ -0,0 +1,25 @@
+import {NgModule} from '@angular/core';
+import {AdminCommonModule} from '@eg/staff/admin/common.module';
+import {SurveyComponent} from './survey.component';
+import {FormsModule} from '@angular/forms';
+import {SurveyEditComponent} from './survey-edit.component';
+import {SurveyRoutingModule} from './survey-routing.module';
+
+ at NgModule({
+  declarations: [
+    SurveyComponent,
+    SurveyEditComponent
+  ],
+  imports: [
+    AdminCommonModule,
+    SurveyRoutingModule,
+    FormsModule,
+  ],
+  exports: [
+  ],
+  providers: [
+  ]
+})
+
+export class SurveyModule {
+}
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Survey.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Survey.pm
index 3b83e36a2b..2f684c4b32 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Survey.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Survey.pm
@@ -22,9 +22,155 @@ use Data::Dumper;
 use OpenILS::Event;
 use Time::HiRes qw(time);
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
+use OpenSRF::Utils::Logger qw/$logger/;
 
 my $apputils = "OpenILS::Application::AppUtils";
 
+__PACKAGE__->register_method(
+    method => "update_survey",
+    api_name => "open-ils.circ.survey.update",
+    signature => {
+        desc => q/Create, update, delete surveys, survey questions, and
+            survey answers.  Relies on isnew ; isnchanged ; isdeleted
+            attributes of provided objects to determine outcome.
+        /,
+        params => [
+            {desc => 'Authtoken', type => 'string'},
+            {desc => 'Fleshed survey (asv) object', type => 'object'}
+        ],
+        return => '1 on success, event on error'
+    }
+);
+
+
+sub update_survey {
+    my ($self, $client, $auth, $survey) = @_;
+
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
+    return $e->die_event unless $e->allowed('ADMIN_SURVEY', $survey->owner);
+
+    my $questions = $survey->questions || [];
+
+    if ($survey->isdeleted) {
+
+        $questions = $e->search_action_survey_question({survey => $survey->id});
+        $_->isdeleted(1) for @$questions;
+        $survey->questions($questions);
+
+        # Remove dependent data first.
+        return $e->die_event if update_questions($e, $survey);
+        return $e->die_event unless $e->delete_action_survey($survey);
+
+    } else {
+
+        if ($survey->isnew) {
+
+            $survey->clear_id;
+            return $e->die_event unless $e->create_action_survey($survey);
+
+            $_->isnew(1) for @$questions;
+
+        } elsif ($survey->ischanged) {
+
+            return $e->die_event unless $e->update_action_survey($survey);
+        }
+
+        return $e->die_event if update_questions($e, $survey);
+    }
+
+    $e->commit;
+
+    $e->xact_begin;
+    $survey = $e->retrieve_action_survey([
+        $survey->id, 
+        {   flesh => 2, 
+            flesh_fields => {asv => ['questions'], 'asvq' => ['answers']}
+        }
+    ]);
+    $e->rollback;
+
+    return $survey;
+}
+
+# returns undef on success, event on error
+sub update_questions {
+    my ($e, $survey) = @_;
+
+    for my $question (@{$survey->questions}) {
+
+        if ($question->isdeleted) {
+
+            # Get the full set
+            my $answers = 
+                $e->search_action_survey_answer({question => $question->id});
+            $_->isdeleted(1) for @$answers;
+            $question->answers($answers);
+
+            # Delete linked objects first.
+            return 1 if update_answers($e, $question);
+            return $e->die_event 
+                unless $e->delete_action_survey_question($question);
+
+        } else {
+
+            if ($question->ischanged) {
+
+                return $e->die_event 
+                    unless $e->update_action_survey_question($question);
+
+            } elsif ($question->isnew) {
+
+                $question->survey($survey->id);
+                $question->clear_id;
+
+                return $e->die_event unless $e->create_action_survey_question($question);
+            }
+
+            return 1 if update_answers($e, $question);
+        }
+    }
+
+    return undef;
+}
+
+sub update_answers {
+    my ($e, $question) = @_;
+
+    return undef unless $question->answers;
+
+    for my $answer (@{$question->answers}) {
+
+        if ($answer->isdeleted) {
+            my $responses = 
+                $e->search_action_survey_response({answer => $answer->id});
+
+            for my $response (@$responses) {
+                return $e->die_event unless 
+                    $e->delete_action_survey_response($response);
+            }
+
+            return $e->die_event unless $e->delete_action_survey_answer($answer);
+
+        } elsif ($answer->isnew) {
+
+            $answer->clear_id;
+            $answer->question($question->id);
+
+            return $e->die_event 
+                unless $e->create_action_survey_answer($answer);
+
+        } elsif ($answer->ischanged) {
+            return $e->die_event
+                unless $e->update_action_survey_answer($answer);
+        }
+    }
+
+    return undef;
+}
+
+
+
 # - creates a new survey
 # expects a survey complete with questions and answers
 __PACKAGE__->register_method(
@@ -78,10 +224,6 @@ sub _add_survey {
     return $survey;
 }
 
-sub _update_survey {
-    my($session, $survey) = @_;
-}
-
 sub _add_questions {
     my($session, $survey) = @_;
 
@@ -423,4 +565,4 @@ sub delete_survey {
 
 
 
-1;
+1;
\ No newline at end of file

commit faf06e1fc43ff91bf4a7d52f175fc351f9646123
Author: Chris Sharp <csharp at georgialibraries.org>
Date:   Fri Feb 21 13:58:15 2020 -0500

    LP#1863929 - Fix sample survey data.
    
    Since we enter the surveys, questions, and answers with specified ID values
    but don't set the values to one higher, testers were unable to enter survey
    data.
    
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/tests/datasets/sql/surveys.sql b/Open-ILS/tests/datasets/sql/surveys.sql
index f45de8d441..c9c6566463 100644
--- a/Open-ILS/tests/datasets/sql/surveys.sql
+++ b/Open-ILS/tests/datasets/sql/surveys.sql
@@ -26,6 +26,10 @@ INSERT INTO action.survey_answer (id, question, answer) VALUES (16, 3, 'Redshirt
 INSERT INTO action.survey_answer (id, question, answer) VALUES (17, 3, 'TARDIS blue.');
 INSERT INTO action.survey_answer (id, question, answer) VALUES (18, 3, 'This is getting too silly - I quit.');
 
+SELECT SETVAL('action.survey_id_seq'::TEXT, 100);
+SELECT SETVAL('action.survey_question_id_seq'::TEXT, 100);
+SELECT SETVAL('action.survey_answer_id_seq'::TEXT, 100);
+
 /** for every user with an id not evenly divisible by 6, 
  *  add a randomized response for every question in the survey
  */

-----------------------------------------------------------------------

Summary of changes:
 .../admin/local/admin-local-splash.component.html  |   3 +-
 .../src/app/staff/admin/local/routing.module.ts    |   3 +
 .../admin/local/survey/survey-edit.component.html  | 124 +++++++++
 .../admin/local/survey/survey-edit.component.ts    | 307 +++++++++++++++++++++
 .../admin/local/survey/survey-routing.module.ts    |  22 ++
 .../staff/admin/local/survey/survey.component.html |  37 +++
 .../staff/admin/local/survey/survey.component.ts   | 122 ++++++++
 .../app/staff/admin/local/survey/survey.module.ts  |  25 ++
 .../lib/OpenILS/Application/Circ/Survey.pm         | 152 +++++++++-
 Open-ILS/tests/datasets/sql/surveys.sql            |   4 +
 10 files changed, 792 insertions(+), 7 deletions(-)
 create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-edit.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey-routing.module.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.html
 create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.component.ts
 create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/survey/survey.module.ts


hooks/post-receive
-- 
Evergreen ILS



More information about the open-ils-commits mailing list