Merge pull request #1449 from pateljannat/assignment-issues
refactor: enhanced assignment form
This commit is contained in:
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -16,6 +16,7 @@ declare module 'vue' {
|
|||||||
AssessmentPlugin: typeof import('./src/components/AssessmentPlugin.vue')['default']
|
AssessmentPlugin: typeof import('./src/components/AssessmentPlugin.vue')['default']
|
||||||
Assessments: typeof import('./src/components/Assessments.vue')['default']
|
Assessments: typeof import('./src/components/Assessments.vue')['default']
|
||||||
Assignment: typeof import('./src/components/Assignment.vue')['default']
|
Assignment: typeof import('./src/components/Assignment.vue')['default']
|
||||||
|
AssignmentForm: typeof import('./src/components/Modals/AssignmentForm.vue')['default']
|
||||||
AudioBlock: typeof import('./src/components/AudioBlock.vue')['default']
|
AudioBlock: typeof import('./src/components/AudioBlock.vue')['default']
|
||||||
Autocomplete: typeof import('./src/components/Controls/Autocomplete.vue')['default']
|
Autocomplete: typeof import('./src/components/Controls/Autocomplete.vue')['default']
|
||||||
BatchCard: typeof import('./src/components/BatchCard.vue')['default']
|
BatchCard: typeof import('./src/components/BatchCard.vue')['default']
|
||||||
|
|||||||
147
frontend/src/components/Modals/AssignmentForm.vue
Normal file
147
frontend/src/components/Modals/AssignmentForm.vue
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model="show"
|
||||||
|
:options="{
|
||||||
|
title:
|
||||||
|
assignmentID === 'new'
|
||||||
|
? __('Create an Assignment')
|
||||||
|
: __('Edit Assignment'),
|
||||||
|
size: 'lg',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: __('Save'),
|
||||||
|
variant: 'solid',
|
||||||
|
onClick: (close) => saveAssignment(close),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #body-content>
|
||||||
|
<div class="space-y-4 text-base max-h-[65vh] overflow-y-auto">
|
||||||
|
<FormControl
|
||||||
|
v-model="assignment.title"
|
||||||
|
:label="__('Title')"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
v-model="assignment.type"
|
||||||
|
type="select"
|
||||||
|
:options="assignmentOptions"
|
||||||
|
:label="__('Submission Type')"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-ink-gray-5 mb-2">
|
||||||
|
{{ __('Question') }}
|
||||||
|
<span class="text-ink-red-3">*</span>
|
||||||
|
</div>
|
||||||
|
<TextEditor
|
||||||
|
:content="assignment.question"
|
||||||
|
@change="(val) => (assignment.question = val)"
|
||||||
|
:editable="true"
|
||||||
|
:fixedMenu="true"
|
||||||
|
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Dialog, FormControl, TextEditor } from 'frappe-ui'
|
||||||
|
import { computed, reactive, watch } from 'vue'
|
||||||
|
import { showToast } from '@/utils'
|
||||||
|
|
||||||
|
const show = defineModel('show')
|
||||||
|
const assignments = defineModel<Assignments>('assignments')
|
||||||
|
|
||||||
|
interface Assignment {
|
||||||
|
title: string
|
||||||
|
type: string
|
||||||
|
question: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Assignments {
|
||||||
|
data: Assignment[]
|
||||||
|
get: (params: { doctype: string; name: string }) => Promise<Assignment>
|
||||||
|
insert: {
|
||||||
|
submit: (params: Assignment, options: { onSuccess: () => void }) => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignment = reactive({
|
||||||
|
title: '',
|
||||||
|
type: '',
|
||||||
|
question: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
assignmentID: {
|
||||||
|
type: String,
|
||||||
|
default: 'new',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.assignmentID,
|
||||||
|
(val) => {
|
||||||
|
if (val !== 'new') {
|
||||||
|
assignments.value?.data.forEach((row) => {
|
||||||
|
if (row.name === val) {
|
||||||
|
assignment.title = row.title
|
||||||
|
assignment.type = row.type
|
||||||
|
assignment.question = row.question
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ flush: 'post' }
|
||||||
|
)
|
||||||
|
|
||||||
|
const saveAssignment = (close) => {
|
||||||
|
if (props.assignmentID == 'new') {
|
||||||
|
assignments.value.insert.submit(
|
||||||
|
{
|
||||||
|
...assignment,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess() {
|
||||||
|
close()
|
||||||
|
showToast(
|
||||||
|
__('Success'),
|
||||||
|
__('Assignment created successfully'),
|
||||||
|
'check'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
assignments.value.setValue.submit(
|
||||||
|
{
|
||||||
|
...assignment,
|
||||||
|
name: props.assignmentID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess() {
|
||||||
|
close()
|
||||||
|
showToast(
|
||||||
|
__('Success'),
|
||||||
|
__('Assignment updated successfully'),
|
||||||
|
'check'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignmentOptions = computed(() => {
|
||||||
|
return [
|
||||||
|
{ label: 'PDF', value: 'PDF' },
|
||||||
|
{ label: 'Image', value: 'Image' },
|
||||||
|
{ label: 'Document', value: 'Document' },
|
||||||
|
{ label: 'Text', value: 'Text' },
|
||||||
|
{ label: 'URL', value: 'URL' },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -315,12 +315,6 @@ const tabsStructure = computed(() => {
|
|||||||
doctype: 'Email Template',
|
doctype: 'Email Template',
|
||||||
type: 'Link',
|
type: 'Link',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Assignment Submission Template',
|
|
||||||
name: 'assignment_submission_template',
|
|
||||||
doctype: 'Email Template',
|
|
||||||
type: 'Link',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="flex items-center text-sm space-x-2">
|
<div class="flex items-center text-sm space-x-2">
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-center rounded border border-outline-gray-modals w-[10rem] py-5"
|
class="flex items-center justify-center rounded border border-outline-gray-modals bg-white w-[10rem] py-5"
|
||||||
>
|
>
|
||||||
<img :src="data[field.name]?.file_url" class="h-6 rounded" />
|
<img :src="data[field.name]?.file_url" class="h-6 rounded" />
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<X
|
<X
|
||||||
@click="data[field.name] = null"
|
@click="data[field.name] = null"
|
||||||
class="bg-surface-gray-5 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
class="border text-ink-gray-7 border-outline-gray-3 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { FormControl, FileUploader, Button, Switch } from 'frappe-ui'
|
import { FormControl, FileUploader, Button, Switch } from 'frappe-ui'
|
||||||
import { computed, onMounted } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { getFileSize, validateFile } from '@/utils'
|
import { getFileSize, validateFile } from '@/utils'
|
||||||
import { X } from 'lucide-vue-next'
|
import { X } from 'lucide-vue-next'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
<template>
|
|
||||||
<header
|
|
||||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
|
||||||
>
|
|
||||||
<Breadcrumbs :items="breadcrumbs" />
|
|
||||||
<div class="space-x-2">
|
|
||||||
<router-link
|
|
||||||
v-if="assignment.doc?.name"
|
|
||||||
:to="{
|
|
||||||
name: 'AssignmentSubmissionList',
|
|
||||||
query: {
|
|
||||||
assignmentID: assignment.doc.name,
|
|
||||||
},
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<Button>
|
|
||||||
{{ __('Submission List') }}
|
|
||||||
</Button>
|
|
||||||
</router-link>
|
|
||||||
<Button variant="solid" @click="saveAssignment()">
|
|
||||||
{{ __('Save') }}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div class="w-3/4 mx-auto py-5">
|
|
||||||
<div class="font-semibold mb-4">
|
|
||||||
{{ __('Details') }}
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-2 gap-5 mt-4 mb-8">
|
|
||||||
<FormControl
|
|
||||||
v-model="model.title"
|
|
||||||
:label="__('Title')"
|
|
||||||
:required="true"
|
|
||||||
/>
|
|
||||||
<FormControl
|
|
||||||
v-model="model.type"
|
|
||||||
type="select"
|
|
||||||
:options="assignmentOptions"
|
|
||||||
:label="__('Type')"
|
|
||||||
:required="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="text-xs text-ink-gray-5 mb-2">
|
|
||||||
{{ __('Question') }}
|
|
||||||
<span class="text-ink-red-3">*</span>
|
|
||||||
</div>
|
|
||||||
<TextEditor
|
|
||||||
:content="model.question"
|
|
||||||
@change="(val) => (model.question = val)"
|
|
||||||
:editable="true"
|
|
||||||
:fixedMenu="true"
|
|
||||||
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import {
|
|
||||||
Breadcrumbs,
|
|
||||||
Button,
|
|
||||||
createDocumentResource,
|
|
||||||
createResource,
|
|
||||||
FormControl,
|
|
||||||
TextEditor,
|
|
||||||
usePageMeta,
|
|
||||||
} from 'frappe-ui'
|
|
||||||
import {
|
|
||||||
computed,
|
|
||||||
inject,
|
|
||||||
onMounted,
|
|
||||||
onBeforeUnmount,
|
|
||||||
reactive,
|
|
||||||
watch,
|
|
||||||
} from 'vue'
|
|
||||||
import { sessionStore } from '../stores/session'
|
|
||||||
import { showToast } from '@/utils'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const user = inject('$user')
|
|
||||||
const router = useRouter()
|
|
||||||
const { brand } = sessionStore()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
assignmentID: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const model = reactive({
|
|
||||||
title: '',
|
|
||||||
type: 'PDF',
|
|
||||||
question: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (
|
|
||||||
props.assignmentID == 'new' &&
|
|
||||||
!user.data?.is_moderator &&
|
|
||||||
!user.data?.is_instructor
|
|
||||||
) {
|
|
||||||
router.push({ name: 'Courses' })
|
|
||||||
}
|
|
||||||
if (props.assignmentID !== 'new') {
|
|
||||||
assignment.reload()
|
|
||||||
}
|
|
||||||
window.addEventListener('keydown', keyboardShortcut)
|
|
||||||
})
|
|
||||||
|
|
||||||
const keyboardShortcut = (e) => {
|
|
||||||
if (e.key === 's' && (e.ctrlKey || e.metaKey)) {
|
|
||||||
saveAssignment()
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('keydown', keyboardShortcut)
|
|
||||||
})
|
|
||||||
|
|
||||||
const assignment = createDocumentResource({
|
|
||||||
doctype: 'LMS Assignment',
|
|
||||||
name: props.assignmentID,
|
|
||||||
auto: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const newAssignment = createResource({
|
|
||||||
url: 'frappe.client.insert',
|
|
||||||
makeParams(values) {
|
|
||||||
return {
|
|
||||||
doc: {
|
|
||||||
doctype: 'LMS Assignment',
|
|
||||||
...values,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess(data) {
|
|
||||||
router.push({ name: 'AssignmentForm', params: { assignmentID: data.name } })
|
|
||||||
},
|
|
||||||
onError(err) {
|
|
||||||
showToast(__('Error'), __(err.messages?.[0] || err), 'x')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const saveAssignment = () => {
|
|
||||||
if (props.assignmentID == 'new') {
|
|
||||||
newAssignment.submit({
|
|
||||||
...model,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
assignment.setValue.submit(
|
|
||||||
{
|
|
||||||
...model,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess(data) {
|
|
||||||
showToast(__('Success'), __('Assignment saved successfully'), 'check')
|
|
||||||
assignment.reload()
|
|
||||||
},
|
|
||||||
onError(err) {
|
|
||||||
showToast(__('Error'), __(err.messages?.[0] || err), 'x')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(assignment, () => {
|
|
||||||
Object.keys(assignment.doc).forEach((key) => {
|
|
||||||
model[key] = assignment.doc[key]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const breadcrumbs = computed(() => [
|
|
||||||
{
|
|
||||||
label: __('Assignments'),
|
|
||||||
route: { name: 'Assignments' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: assignment.doc ? assignment.doc.title : __('New Assignment'),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const assignmentOptions = computed(() => {
|
|
||||||
return [
|
|
||||||
{ label: 'PDF', value: 'PDF' },
|
|
||||||
{ label: 'Image', value: 'Image' },
|
|
||||||
{ label: 'Document', value: 'Document' },
|
|
||||||
{ label: 'Text', value: 'Text' },
|
|
||||||
{ label: 'URL', value: 'URL' },
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
usePageMeta(() => {
|
|
||||||
return {
|
|
||||||
title: assignment.doc ? assignment.doc.title : __('New Assignment'),
|
|
||||||
icon: brand.favicon,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@@ -3,21 +3,20 @@
|
|||||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
class="sticky top-0 z-10 flex items-center justify-between border-b bg-surface-white px-3 py-2.5 sm:px-5"
|
||||||
>
|
>
|
||||||
<Breadcrumbs :items="breadcrumbs" />
|
<Breadcrumbs :items="breadcrumbs" />
|
||||||
<router-link
|
<Button
|
||||||
:to="{
|
variant="solid"
|
||||||
name: 'AssignmentForm',
|
@click="
|
||||||
params: {
|
() => {
|
||||||
assignmentID: 'new',
|
assignmentID = 'new'
|
||||||
},
|
showAssignmentForm = true
|
||||||
}"
|
}
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<Button variant="solid">
|
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<Plus class="w-4 h-4" />
|
<Plus class="w-4 h-4" />
|
||||||
</template>
|
</template>
|
||||||
{{ __('New') }}
|
{{ __('New') }}
|
||||||
</Button>
|
</Button>
|
||||||
</router-link>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="md:w-3/4 md:mx-auto py-5 mx-5">
|
<div class="md:w-3/4 md:mx-auto py-5 mx-5">
|
||||||
@@ -38,12 +37,10 @@
|
|||||||
:options="{
|
:options="{
|
||||||
showTooltip: false,
|
showTooltip: false,
|
||||||
selectable: false,
|
selectable: false,
|
||||||
getRowRoute: (row) => ({
|
onRowClick: (row) => {
|
||||||
name: 'AssignmentForm',
|
assignmentID = row.name
|
||||||
params: {
|
showAssignmentForm = true
|
||||||
assignmentID: row.name,
|
|
||||||
},
|
},
|
||||||
}),
|
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
</ListView>
|
</ListView>
|
||||||
@@ -72,6 +69,11 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AssignmentForm
|
||||||
|
v-model="showAssignmentForm"
|
||||||
|
v-model:assignments="assignments"
|
||||||
|
:assignmentID="assignmentID"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
@@ -86,11 +88,14 @@ import { computed, inject, onMounted, ref, watch } from 'vue'
|
|||||||
import { Plus, Pencil } from 'lucide-vue-next'
|
import { Plus, Pencil } from 'lucide-vue-next'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { sessionStore } from '../stores/session'
|
import { sessionStore } from '../stores/session'
|
||||||
|
import AssignmentForm from '@/components/Modals/AssignmentForm.vue'
|
||||||
|
|
||||||
const user = inject('$user')
|
const user = inject('$user')
|
||||||
const dayjs = inject('$dayjs')
|
const dayjs = inject('$dayjs')
|
||||||
const titleFilter = ref('')
|
const titleFilter = ref('')
|
||||||
const typeFilter = ref('')
|
const typeFilter = ref('')
|
||||||
|
const showAssignmentForm = ref(false)
|
||||||
|
const assignmentID = ref('new')
|
||||||
const { brand } = sessionStore()
|
const { brand } = sessionStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -136,7 +141,7 @@ const assignmentFilter = computed(() => {
|
|||||||
|
|
||||||
const assignments = createListResource({
|
const assignments = createListResource({
|
||||||
doctype: 'LMS Assignment',
|
doctype: 'LMS Assignment',
|
||||||
fields: ['name', 'title', 'type', 'creation'],
|
fields: ['name', 'title', 'type', 'creation', 'question'],
|
||||||
orderBy: 'modified desc',
|
orderBy: 'modified desc',
|
||||||
cache: ['assignments'],
|
cache: ['assignments'],
|
||||||
transform(data) {
|
transform(data) {
|
||||||
@@ -166,7 +171,7 @@ const assignmentColumns = computed(() => {
|
|||||||
label: __('Created'),
|
label: __('Created'),
|
||||||
key: 'creation',
|
key: 'creation',
|
||||||
width: 1,
|
width: 1,
|
||||||
align: 'center',
|
align: 'right',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -199,12 +199,6 @@ const routes = [
|
|||||||
name: 'Assignments',
|
name: 'Assignments',
|
||||||
component: () => import('@/pages/Assignments.vue'),
|
component: () => import('@/pages/Assignments.vue'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/assignments/:assignmentID',
|
|
||||||
name: 'AssignmentForm',
|
|
||||||
component: () => import('@/pages/AssignmentForm.vue'),
|
|
||||||
props: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/assignment-submission/:assignmentID/:submissionName',
|
path: '/assignment-submission/:assignmentID/:submissionName',
|
||||||
name: 'AssignmentSubmission',
|
name: 'AssignmentSubmission',
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
allowedHosts: ['fs', 'onb2'],
|
allowedHosts: ['fs', 'bs'],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import validate_url, validate_email_address
|
from frappe.utils import validate_url
|
||||||
from frappe.email.doctype.email_template.email_template import get_email_template
|
|
||||||
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
||||||
|
|
||||||
|
|
||||||
@@ -15,14 +14,6 @@ class LMSAssignmentSubmission(Document):
|
|||||||
self.validate_url()
|
self.validate_url()
|
||||||
self.validate_status()
|
self.validate_status()
|
||||||
|
|
||||||
def after_insert(self):
|
|
||||||
if not frappe.flags.in_test:
|
|
||||||
outgoing_email_account = frappe.get_cached_value(
|
|
||||||
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
|
|
||||||
)
|
|
||||||
if outgoing_email_account or frappe.conf.get("mail_login"):
|
|
||||||
self.send_mail()
|
|
||||||
|
|
||||||
def validate_duplicates(self):
|
def validate_duplicates(self):
|
||||||
if frappe.db.exists(
|
if frappe.db.exists(
|
||||||
"LMS Assignment Submission",
|
"LMS Assignment Submission",
|
||||||
@@ -39,38 +30,6 @@ class LMSAssignmentSubmission(Document):
|
|||||||
if self.type == "URL" and not validate_url(self.answer):
|
if self.type == "URL" and not validate_url(self.answer):
|
||||||
frappe.throw(_("Please enter a valid URL."))
|
frappe.throw(_("Please enter a valid URL."))
|
||||||
|
|
||||||
def send_mail(self):
|
|
||||||
subject = _("New Assignment Submission")
|
|
||||||
template = "assignment_submission"
|
|
||||||
custom_template = frappe.db.get_single_value(
|
|
||||||
"LMS Settings", "assignment_submission_template"
|
|
||||||
)
|
|
||||||
|
|
||||||
args = {
|
|
||||||
"member_name": self.member_name,
|
|
||||||
"assignment_name": self.assignment,
|
|
||||||
"assignment_title": self.assignment_title,
|
|
||||||
"submission_name": self.name,
|
|
||||||
}
|
|
||||||
|
|
||||||
moderators = frappe.get_all("Has Role", {"role": "Moderator"}, pluck="parent")
|
|
||||||
for moderator in moderators:
|
|
||||||
if not validate_email_address(moderator):
|
|
||||||
moderators.remove(moderator)
|
|
||||||
|
|
||||||
if custom_template:
|
|
||||||
email_template = get_email_template(custom_template, args)
|
|
||||||
subject = email_template.get("subject")
|
|
||||||
content = email_template.get("message")
|
|
||||||
frappe.sendmail(
|
|
||||||
recipients=moderators,
|
|
||||||
subject=subject,
|
|
||||||
template=template if not custom_template else None,
|
|
||||||
content=content if custom_template else None,
|
|
||||||
args=args,
|
|
||||||
header=[subject, "green"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_status(self):
|
def validate_status(self):
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
doc_before_save = self.get_doc_before_save()
|
doc_before_save = self.get_doc_before_save()
|
||||||
|
|||||||
@@ -58,7 +58,6 @@
|
|||||||
"certification_template",
|
"certification_template",
|
||||||
"batch_confirmation_template",
|
"batch_confirmation_template",
|
||||||
"column_break_uwsp",
|
"column_break_uwsp",
|
||||||
"assignment_submission_template",
|
|
||||||
"payment_reminder_template",
|
"payment_reminder_template",
|
||||||
"seo_tab",
|
"seo_tab",
|
||||||
"meta_description"
|
"meta_description"
|
||||||
@@ -238,12 +237,6 @@
|
|||||||
"fieldtype": "Tab Break",
|
"fieldtype": "Tab Break",
|
||||||
"label": "Email Templates"
|
"label": "Email Templates"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "assignment_submission_template",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Assignment Submission Template",
|
|
||||||
"options": "Email Template"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_uwsp",
|
"fieldname": "column_break_uwsp",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
@@ -383,7 +376,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-04-10 16:17:00.658698",
|
"modified": "2025-04-17 21:58:30.365876",
|
||||||
"modified_by": "sayali@frappe.io",
|
"modified_by": "sayali@frappe.io",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Settings",
|
"name": "LMS Settings",
|
||||||
|
|||||||
Reference in New Issue
Block a user