Przeglądaj źródła

admin show test and send objection and scores

ali 2 lat temu
rodzic
commit
567ee29b72

+ 1 - 0
src/api/models/AnswerBook.js

@@ -10,6 +10,7 @@ export default class AnswerBookAPI extends APIRepository {
       byId: (id) => '/lma/answer_books/' + id,
       confirmAnswers: (id) => '/lma/answer_books/' + id + '/confirm_answers',
       setGraderToMe: (id) => '/lma/answer_books/' + id + '/set_grader_to_me',
+      submitObjectionResult: (id) => '/lma/answer_books/' + id + '/submit_objection_result',
       submitObjectionRequest: (id) => '/lma/answer_books/' + id + '/submit_objection_request',
       getTestQuestions: (id) => '/lma/answer_books/' + id + '/get_test_questions',
       submitOverallAnswer: (id) => '/lma/answer_books/' + id + '/submit_overall_answer'

+ 247 - 0
src/components/Widgets/Classroom/AdminClassroomAnswerBookList/AdminClassroomAnswerBookList.vue

@@ -0,0 +1,247 @@
+<template>
+  <div class="AdminClassroomAnswerBookList">
+    <entity-index v-if="mounted"
+                  ref="entityIndex"
+                  v-model:value="inputs"
+                  :title="'لیست ' + classroomTypeTitle"
+                  :api="api"
+                  :table="table"
+                  :table-keys="tableKeys"
+                  :show-expand-button="false"
+                  :show-reload-button="false"
+                  :show-search-button="false">
+      <template v-slot:entity-index-table-cell="{inputData}">
+        <template v-if="inputData.col.name === 'number'">
+          {{ inputData.rowNumber }}
+        </template>
+        <template v-else-if="inputData.col.name === 'actions'">
+          <div class="action-column-entity-index">
+            <q-btn size="md"
+                   color="primary"
+                   label="جزییات"
+                   :to="{name: 'Admin.Classroom.Test.AnswerBooks.ShowGrade', params: {classroom_id: $route.params.classroom_id, test_id: $route.params.test_id, answer_book_id: inputData.props.row.id}}"
+                   class="q-mr-md" />
+          </div>
+        </template>
+        <template v-else>
+          {{ inputData.col.value }}
+        </template>
+      </template>
+    </entity-index>
+  </div>
+</template>
+
+<script>
+import { shallowRef } from 'vue'
+import { EntityIndex } from 'quasar-crud'
+import { Test } from 'src/models/Test.js'
+import Enums from 'src/assets/Enums/Enums.js'
+import { mixinWidget, mixinAuth } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+// import ShamsiDate from 'src/assets/ShamsiDate.js'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import BtnControl from 'src/components/Control/btn.vue'
+import { UnitCategory } from 'src/models/UnitCategory.js'
+
+const BtnControlComp = shallowRef(BtnControl)
+
+export default {
+  name: 'AdminClassroomAnswerBookList',
+  components: {
+    EntityIndex
+  },
+  mixins: [mixinWidget, mixinAuth],
+  data () {
+    return {
+      mounted: false,
+      defaultOptions: {
+        classroomType: 'TRAINING'
+      },
+      api: APIGateway.answerBook.APIAdresses.base,
+      inputs: [
+        {
+          type: 'select',
+          name: 'status',
+          options: [
+            { label: 'جاری', value: 'CREATED,FETCHED,SUBMITTED' },
+            { label: 'در حال تصحیح', value: 'GRADING' },
+            { label: 'تصحیح شده', value: 'GRADED' }
+          ],
+          label: 'وضعیت',
+          placeholder: ' ',
+          value: null,
+          col: 'col-md-4 col-12'
+        },
+        { type: 'hidden', name: 'test', value: this.$route.params.test_id },
+        { type: 'hidden', name: 'test__classroom', value: this.$route.params.classroom_id },
+        { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: () => {}, col: 'col-md-2 col-12' }
+      ],
+      table: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.id
+          },
+          {
+            name: 'owner_info.fullname',
+            required: true,
+            label: 'نام و نام خانوادگی',
+            align: 'left',
+            field: row => row.owner_info.firstname + ' ' + row.owner_info.lastname
+          },
+          {
+            name: 'owner_info.mobile_number',
+            required: true,
+            label: 'موبایل',
+            align: 'left',
+            field: row => row.owner_info.mobile_number
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'عنوان آزمون',
+            align: 'left',
+            field: row => row.test_info.title
+          },
+          {
+            name: 'test_set_questions_length',
+            required: true,
+            label: 'تعداد سوالات',
+            align: 'left',
+            field: row => row.test_info.test_set_questions_length
+          },
+          // {
+          //   name: 'level',
+          //   required: true,
+          //   label: 'درچه سختی',
+          //   align: 'left',
+          //   field: row => (new Test(row.test_set_info)).level_info.label
+          // },
+          {
+            name: 'test_info_status_info',
+            required: true,
+            label: 'وضعیت آزمون',
+            align: 'left',
+            field: row => (new Test(row.test_info)).status_info.label
+          },
+          {
+            name: 'actions',
+            required: true,
+            label: 'عملیات',
+            align: 'left',
+            field: ''
+          }
+        ]
+      },
+      tableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      }
+    }
+  },
+  computed: {
+    classroomTypeTitle () {
+      const unitCategory = new UnitCategory({ type: this.localOptions.classroomType })
+      return unitCategory.type_info.label
+    },
+    createPageRouteName () {
+      if (this.localOptions.classroomType === 'TRAINING') {
+        return 'Admin.Classroom.Create'
+      }
+      if (this.localOptions.classroomType === 'EVENT') {
+        return 'Admin.Event.Create'
+      }
+      return 'Admin.Classroom.Create'
+    },
+    showPageRouteName () {
+      if (this.localOptions.classroomType === 'TRAINING') {
+        return 'Admin.Classroom.Show'
+      }
+      if (this.localOptions.classroomType === 'EVENT') {
+        return 'Admin.Event.Show'
+      }
+      return 'Admin.Classroom.Show'
+    }
+  },
+  mounted () {
+    this.setActionBtn()
+    this.setClassroomTypeOfInputs()
+    this.mounted = true
+  },
+  methods: {
+    setActionBtn () {
+      FormBuilderAssist.setAttributeByName(this.inputs, 'btn', 'atClick', this.search)
+    },
+    setClassroomTypeOfInputs () {
+      this.table.columns.forEach(col => {
+        if (col.name === 'title') {
+          col.label = 'نام ' + this.classroomTypeTitle
+        }
+        if (col.name === 'professor_info') {
+          col.label = (this.localOptions.classroomType === 'TRAINING') ? 'استاد مربوطه' : 'برگزار کننده'
+        }
+      })
+    },
+    search () {
+      this.$refs.entityIndex.search()
+    },
+    getSelectOptions (result, value, label) {
+      return result.map(item => {
+        return {
+          value: item[value],
+          label: item[label]
+        }
+      })
+    },
+    getRemoveMessage (row) {
+      return 'آیا از حذف ' + row.title + ' اطمینان دارید؟'
+    },
+    getClassroomHoldingTypeTitle (type) {
+      const target = Enums.classroomHoldingTypes.find(item => item.value === type)
+      if (!target) {
+        return '-'
+      }
+
+      return target.label
+    },
+    getClassroomStatusesTitle (type) {
+      const target = Enums.classroomStatuses.find(item => item.value === type)
+      if (!target) {
+        return '-'
+      }
+
+      return target.label
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.AdminClassroomAnswerBookList {
+  .add-new-action-row {
+    display: flex;
+    flex-flow: row;
+    align-items: center;
+    justify-content: flex-end;
+    margin-bottom: 22px;
+  }
+  .action-column-entity-index {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+  }
+}
+</style>

+ 28 - 0
src/components/Widgets/Classroom/AdminClassroomAnswerBookList/OptionPanel.vue

@@ -0,0 +1,28 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <q-option-group v-model="localOptions.classroomType"
+                      :options="classroomTypeOptions" />
+    </template>
+  </option-panel-tabs>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { UnitCategory } from 'src/models/UnitCategory.js'
+import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
+import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
+
+export default defineComponent({
+  name: 'OptionPanel',
+  components: {
+    OptionPanelTabs
+  },
+  mixins: [mixinOptionPanel],
+  data () {
+    return {
+      classroomTypeOptions: (new UnitCategory()).typeEnums
+    }
+  }
+})
+</script>

+ 2 - 2
src/components/Widgets/Classroom/AdminClassroomShow/components/TestList.vue

@@ -26,8 +26,8 @@
           <q-btn size="md"
                  outline
                  color="grey"
-                 label="تصحیح" />
-          <!--          :to="{name: 'Admin.Classroom.TestSet.Test.AnswerBooks.ShowGrade', params: {classroom_id: classroomId, answer_book_id: inputData.props.row.id}}"-->
+                 label="تصحیح"
+                 :to="{name: 'Admin.Classroom.Test.AnswerBooks.Index', params: {classroom_id: classroomId, test_id: inputData.props.row.id}}" />
         </div>
       </template>
       <template v-else-if="inputData.col.name === 'actions'">

+ 34 - 34
src/components/Widgets/Test/AdminAnswerBookShow/AdminAnswerBookShow.vue

@@ -10,12 +10,12 @@
         وضعیت آزمون:
         {{ answerBook.status_info.label }}
       </q-banner>
-      <q-btn v-if="!this.answerBook.grader"
-             color="primary"
-             class="full-width"
-             @click="setGraderToMe">
-        تصحیح توسط شما
-      </q-btn>
+      <q-banner v-if="answerBook.grader">
+        مصحح:
+        {{ answerBook.grader_info.firstname }}
+        {{ answerBook.grader_info.lastname }}
+        ({{ answerBook.grader_info.mobile_number }})
+      </q-banner>
       <q-separator />
       <q-banner>
         <div class="row">
@@ -51,15 +51,15 @@
                            :show-reload-button="false"
                            :default-layout="false" />
             </q-item-label>
-            <q-item-label v-if="currentUserIsGrader">
-              <q-input v-model="questionInputs[answerSheetIndex].score"
-                       :loading="questionInputs[answerSheetIndex].loading"
+            <q-item-label>
+              <q-input v-model="scores[answerSheetIndex].score"
+                       :loading="scores[answerSheetIndex].loading"
                        label="نمره">
                 <template v-slot:append>
                   <q-btn color="primary"
                          flat
                          label="ثبت نمره"
-                         :loading="questionInputs[answerSheetIndex].loading"
+                         :loading="scores[answerSheetIndex].loading"
                          @click="submitScore(answerSheetIndex)" />
                 </template>
               </q-input>
@@ -94,13 +94,19 @@
                      :show-reload-button="false"
                      :default-layout="false" />
       </div>
-      <template v-if="$route.query.send_objection && $route.query.send_objection.toString() === '1'">
+      <template v-if="answerBook.objection_request">
+        <div>
+          متن اعتراض:
+        </div>
+        <div v-html="answerBook.objection_request" />
+      </template>
+      <template v-if="answerBook.objection_request && !answerBook.objection_result">
         <q-separator class="q-my-xl" />
         <q-card-section>
           <entity-edit v-if="mounted"
                        ref="objectionEntityEdit"
                        v-model:value="objectionInputs"
-                       title="ثبت اعتراض"
+                       title="پاسخ به اعتراض"
                        :api="objectionApi"
                        :loaded-data="{}"
                        :show-close-button="false"
@@ -118,13 +124,7 @@
           </entity-edit>
         </q-card-section>
       </template>
-      <template v-else-if="answerBook.objection_request">
-        <div>
-          متن اعتراض:
-        </div>
-        <div v-html="answerBook.objection_request" />
-      </template>
-      <template v-if="answerBook.objection_result">
+      <template v-else-if="answerBook.objection_result">
         <div>
           نتیجه اعتراض:
         </div>
@@ -151,7 +151,6 @@ export default {
   data () {
     return {
       mounted: false,
-      currentUserIsGrader: false,
       overallAnswerInput: [
         { type: 'input', name: 'overall_answer_text', responseKey: 'overall_answer_text', label: 'متن پاسخ جامع', placeholder: ' ', inputType: 'textarea', col: 'col-12' },
         { type: 'file', name: 'overall_answer_attachment', responseKey: 'overall_answer_attachment', label: 'فایل پیوست جامع', placeholder: ' ', col: 'col-12' }
@@ -161,14 +160,12 @@ export default {
         { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'فایل پیوست', placeholder: ' ', col: 'col-12' }
       ],
       questionInputs: [],
+      scores: [],
       objectionInputs: [
-        { type: 'input', name: 'objection_request', responseKey: 'objection_request', label: 'متن اعتراض', placeholder: ' ', inputType: 'textarea', col: 'col-12' }
+        { type: 'input', name: 'objection_result', responseKey: 'objection_result', label: 'متن پاسخ', placeholder: ' ', inputType: 'textarea', col: 'col-12' }
       ],
-      objectionApi: APIGateway.answerBook.APIAdresses.submitObjectionRequest(this.$route.params.answer_book_id),
-      participateType: null,
-      participateTypeDialog: false,
-      answerBook: new AnswerBook(),
-      timerInterval: null
+      objectionApi: APIGateway.answerBook.APIAdresses.submitObjectionResult(this.$route.params.answer_book_id),
+      answerBook: new AnswerBook()
     }
   },
   mounted () {
@@ -180,10 +177,10 @@ export default {
       this.answerBook.loading = true
       this.$refs.objectionEntityEdit.editEntity(false)
         .then(() => {
-          this.getTestQuestions()
+          this.getAnswerBook()
         })
         .catch(() => {
-          this.getTestQuestions()
+          this.getAnswerBook()
         })
     },
     toShamsi (miladi) {
@@ -191,10 +188,13 @@ export default {
     },
     loadAnswerBook (answerBook) {
       this.answerBook = new AnswerBook(answerBook)
-      this.answerBook.answer_sheet_info.list.forEach(() => {
+      this.answerBook.answer_sheet_info.list.forEach((answerSheetItem) => {
         this.questionInputs.push(this.questionInput)
+        this.scores.push({
+          score: answerSheetItem.score,
+          loading: false
+        })
       })
-      this.currentUserIsGrader = this.answerBook.grader === this.user.id
     },
     setGraderToMe () {
       this.answerBook.loading = true
@@ -209,14 +209,14 @@ export default {
         })
     },
     submitScore (index) {
-      this.questionInputs[index].loading = true
-      APIGateway.answerSheet.submitScore(this.$route.params.answer_book_id, this.questionInputs[index])
+      this.scores[index].loading = true
+      APIGateway.answerSheet.submitScore(this.$route.params.answer_book_id, this.scores[index].score)
         .then(() => {
-          this.questionInputs[index].loading = false
+          this.scores[index].loading = false
           this.getAnswerBook()
         })
         .catch(() => {
-          this.questionInputs[index].loading = false
+          this.scores[index].loading = false
         })
     },
     getAnswerBook () {

+ 16 - 11
src/components/Widgets/Test/ShowTestGradeForGrader/ShowTestGradeForGrader.vue

@@ -10,7 +10,7 @@
         وضعیت آزمون:
         {{ answerBook.status_info.label }}
       </q-banner>
-      <q-btn v-if="!this.answerBook.grader"
+      <q-btn v-if="!answerBook.grader"
              color="primary"
              class="full-width"
              @click="setGraderToMe">
@@ -52,14 +52,14 @@
                            :default-layout="false" />
             </q-item-label>
             <q-item-label v-if="currentUserIsGrader">
-              <q-input v-model="questionInputs[answerSheetIndex].score"
-                       :loading="questionInputs[answerSheetIndex].loading"
+              <q-input v-model="scores[answerSheetIndex].score"
+                       :loading="scores[answerSheetIndex].loading"
                        label="نمره">
                 <template v-slot:append>
                   <q-btn color="primary"
                          flat
                          label="ثبت نمره"
-                         :loading="questionInputs[answerSheetIndex].loading"
+                         :loading="scores[answerSheetIndex].loading"
                          @click="submitScore(answerSheetIndex)" />
                 </template>
               </q-input>
@@ -161,6 +161,7 @@ export default {
         { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'فایل پیوست', placeholder: ' ', col: 'col-12' }
       ],
       questionInputs: [],
+      scores: [],
       objectionInputs: [
         { type: 'input', name: 'objection_request', responseKey: 'objection_request', label: 'متن اعتراض', placeholder: ' ', inputType: 'textarea', col: 'col-12' }
       ],
@@ -180,10 +181,10 @@ export default {
       this.answerBook.loading = true
       this.$refs.objectionEntityEdit.editEntity(false)
         .then(() => {
-          this.getTestQuestions()
+          this.getAnswerBook()
         })
         .catch(() => {
-          this.getTestQuestions()
+          this.getAnswerBook()
         })
     },
     toShamsi (miladi) {
@@ -191,8 +192,12 @@ export default {
     },
     loadAnswerBook (answerBook) {
       this.answerBook = new AnswerBook(answerBook)
-      this.answerBook.answer_sheet_info.list.forEach(() => {
+      this.answerBook.answer_sheet_info.list.forEach((answerSheetItem) => {
         this.questionInputs.push(this.questionInput)
+        this.scores.push({
+          score: answerSheetItem.score,
+          loading: false
+        })
       })
       this.currentUserIsGrader = this.answerBook.grader === this.user.id
     },
@@ -209,14 +214,14 @@ export default {
         })
     },
     submitScore (index) {
-      this.questionInputs[index].loading = true
-      APIGateway.answerSheet.submitScore(this.$route.params.answer_book_id, this.questionInputs[index])
+      this.scores[index].loading = true
+      APIGateway.answerSheet.submitScore(this.$route.params.answer_book_id, this.scores[index].score)
         .then(() => {
-          this.questionInputs[index].loading = false
+          this.scores[index].loading = false
           this.getAnswerBook()
         })
         .catch(() => {
-          this.questionInputs[index].loading = false
+          this.scores[index].loading = false
         })
     },
     getAnswerBook () {

+ 1 - 1
src/pages/Admin/Classroom/ClassroomAnswerBooks.vue

@@ -13,7 +13,7 @@ export default {
   mixins: [mixinPageOptions],
   data: () => {
     return {
-      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'ClassroomAnswerBookList' }] }] }] } }]
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'AdminClassroomAnswerBookList' }] }] }] } }]
     }
   },
   created() {

+ 23 - 0
src/pages/Admin/Classroom/ClassroomAnswerBooksForGrader.vue

@@ -0,0 +1,23 @@
+<template>
+  <q-page-builder v-model:sections="currenSections"
+                  v-model:options="pageConfig"
+                  :editable="pageBuilderEditable"
+                  :loading="pageBuilderLoading" />
+</template>
+
+<script>
+import { mixinPageOptions } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'UserPanel.Classroom.MyAsGraderClassroom.AnswerBooks',
+  mixins: [mixinPageOptions],
+  data: () => {
+    return {
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'ClassroomAnswerBookList' }] }] }] } }]
+    }
+  },
+  created() {
+    this.currenSections = this.sections
+  }
+}
+</script>

+ 11 - 4
src/router/routes.js

@@ -343,7 +343,7 @@ const routes = [
                 component: () => import('src/layouts/bareLayout.vue'),
                 children: [
                   { name: 'UserPanel.Classroom.MyAsGraderClassroom.Index', path: '', component: () => import('src/pages/Admin/Classroom/MyAsGraderClassrooms.vue') },
-                  { name: 'UserPanel.Classroom.MyAsGraderClassroom.AnswerBooks', path: ':classroom_id/answer_books', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBooks.vue') },
+                  { name: 'UserPanel.Classroom.MyAsGraderClassroom.AnswerBooks', path: ':classroom_id/answer_books', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBooksForGrader.vue') },
                   { name: 'UserPanel.Classroom.MyAsGraderClassroom.AnswerBooks.ShowGrade', path: ':classroom_id/answer_books/:answer_book_id/show_grades', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBookShowGradeForGrader.vue') },
                   { name: 'UserPanel.Classroom.MyAsGraderClassroom.AnswerBooks.SubmitGrade', path: ':classroom_id/answer_books/:answer_book_id/submit_grade', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBookSubmitGrade.vue') }
                 ]
@@ -680,9 +680,16 @@ const routes = [
                     children: [
                       { name: 'Admin.Classroom.TestSet.Index', path: '', component: () => import('src/pages/Admin/TestSet/Index.vue') },
                       { name: 'Admin.Classroom.TestSet.Test.Show', path: ':test_set_id/test/:id', component: () => import('src/pages/Admin/Test/Show.vue') },
-                      { name: 'Admin.Classroom.TestSet.Test.Create', path: ':test_set_id/test/create', component: () => import('src/pages/Admin/Test/Create.vue') },
-                      { name: 'Admin.Classroom.TestSet.Test.AnswerBooks.Index', path: 'answer_books', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBookShowGrade.vue') },
-                      { name: 'Admin.Classroom.TestSet.Test.AnswerBooks.ShowGrade', path: 'answer_books/:answer_book_id/show_grades', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBookShowGrade.vue') }
+                      { name: 'Admin.Classroom.TestSet.Test.Create', path: ':test_set_id/test/create', component: () => import('src/pages/Admin/Test/Create.vue') }
+                    ]
+                  },
+                  {
+                    name: 'Admin.Classroom.TestSet',
+                    path: 'test',
+                    component: () => import('src/layouts/bareLayout.vue'),
+                    children: [
+                      { name: 'Admin.Classroom.Test.AnswerBooks.Index', path: ':test_id/answer_books', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBooks.vue') },
+                      { name: 'Admin.Classroom.Test.AnswerBooks.ShowGrade', path: ':test_id/answer_books/:answer_book_id/show_grades', component: () => import('src/pages/Admin/Classroom/ClassroomAnswerBookShowGrade.vue') }
                     ]
                   }
                 ]