feat: evaluator unavailability
This commit is contained in:
@@ -25,7 +25,7 @@
|
|||||||
<div class="mb-1.5 text-sm text-gray-600">
|
<div class="mb-1.5 text-sm text-gray-600">
|
||||||
{{ __('Date') }}
|
{{ __('Date') }}
|
||||||
</div>
|
</div>
|
||||||
<DatePicker v-model="evaluation.date" />
|
<FormControl type="date" v-model="evaluation.date" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="slots.data?.length">
|
<div v-if="slots.data?.length">
|
||||||
<div class="mb-1.5 text-sm text-gray-600">
|
<div class="mb-1.5 text-sm text-gray-600">
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Dialog, createResource, Select, DatePicker } from 'frappe-ui'
|
import { Dialog, createResource, Select, FormControl } from 'frappe-ui'
|
||||||
import { defineModel, reactive, watch, inject } from 'vue'
|
import { defineModel, reactive, watch, inject } from 'vue'
|
||||||
import { createToast, formatTime } from '@/utils/'
|
import { createToast, formatTime } from '@/utils/'
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ watch(
|
|||||||
() => evaluation.date,
|
() => evaluation.date,
|
||||||
(date) => {
|
(date) => {
|
||||||
evaluation.start_time = ''
|
evaluation.start_time = ''
|
||||||
if (date) {
|
if (date && evaluation.course) {
|
||||||
slots.submit(evaluation)
|
slots.submit(evaluation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ const profile = createResource({
|
|||||||
|
|
||||||
const setActiveTab = () => {
|
const setActiveTab = () => {
|
||||||
let fragments = route.path.split('/')
|
let fragments = route.path.split('/')
|
||||||
let sections = ['certificates', 'settings', 'evaluator']
|
let sections = ['certificates', 'roles', 'evaluations']
|
||||||
sections.forEach((section) => {
|
sections.forEach((section) => {
|
||||||
if (fragments.includes(section)) {
|
if (fragments.includes(section)) {
|
||||||
activeTab.value = convertToTitleCase(section)
|
activeTab.value = convertToTitleCase(section)
|
||||||
@@ -130,8 +130,8 @@ watchEffect(() => {
|
|||||||
let route = {
|
let route = {
|
||||||
About: { name: 'ProfileAbout' },
|
About: { name: 'ProfileAbout' },
|
||||||
Certificates: { name: 'ProfileCertificates' },
|
Certificates: { name: 'ProfileCertificates' },
|
||||||
Settings: { name: 'ProfileSettings' },
|
Roles: { name: 'ProfileRoles' },
|
||||||
Evaluato: { name: 'ProfileEvaluator' },
|
Evaluations: { name: 'ProfileEvaluator' },
|
||||||
}[activeTab.value]
|
}[activeTab.value]
|
||||||
router.push(route)
|
router.push(route)
|
||||||
}
|
}
|
||||||
@@ -147,9 +147,9 @@ const isSessionUser = () => {
|
|||||||
|
|
||||||
const getTabButtons = () => {
|
const getTabButtons = () => {
|
||||||
let buttons = [{ label: 'About' }, { label: 'Certificates' }]
|
let buttons = [{ label: 'About' }, { label: 'Certificates' }]
|
||||||
if ($user.data?.is_moderator) buttons.push({ label: 'Settings' })
|
if ($user.data?.is_moderator) buttons.push({ label: 'Roles' })
|
||||||
if (isSessionUser() && $user.data?.is_evaluator)
|
if (isSessionUser() && $user.data?.is_evaluator)
|
||||||
buttons.push({ label: 'Evaluation Slots' })
|
buttons.push({ label: 'Evaluations' })
|
||||||
|
|
||||||
return buttons
|
return buttons
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
:key="certificate.name"
|
:key="certificate.name"
|
||||||
class="bg-white shadow rounded-lg p-3 cursor-pointer"
|
class="bg-white shadow rounded-lg p-3 cursor-pointer"
|
||||||
>
|
>
|
||||||
<div class="font-medium">
|
<div class="font-medium leading-5">
|
||||||
{{ certificate.course_title }}
|
{{ certificate.course_title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
|
|||||||
@@ -1,2 +1,278 @@
|
|||||||
<template>Evaluator</template>
|
<template>
|
||||||
<script setup></script>
|
<div class="mt-7 mb-20">
|
||||||
|
<h2 class="mb-4 text-lg font-semibold text-gray-900">
|
||||||
|
{{ __('My availability') }}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="w-3/4">
|
||||||
|
<div class="grid grid-cols-4 gap-4 text-sm text-gray-700 mb-4">
|
||||||
|
<div>
|
||||||
|
{{ __('Day') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ __('Start Time') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ __('End Time') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="slots.data"
|
||||||
|
v-for="slot in slots.data.schedule"
|
||||||
|
class="grid grid-cols-4 gap-4 mb-4 group"
|
||||||
|
>
|
||||||
|
<FormControl
|
||||||
|
type="select"
|
||||||
|
:options="days"
|
||||||
|
v-model="slot.day"
|
||||||
|
@focusout.stop="update(slot.name, 'day', slot.day)"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="time"
|
||||||
|
v-model="slot.start_time"
|
||||||
|
@focusout.stop="update(slot.name, 'start_time', slot.start_time)"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="time"
|
||||||
|
v-model="slot.end_time"
|
||||||
|
@focusout.stop="update(slot.name, 'end_time', slot.end_time)"
|
||||||
|
/>
|
||||||
|
<X
|
||||||
|
@click="deleteRow(slot.name)"
|
||||||
|
class="w-6 h-auto stroke-1.5 text-red-900 rounded-md cursor-pointer p-1 bg-red-100 hidden group-hover:block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-4 gap-4 mb-4" v-show="showSlotsTemplate">
|
||||||
|
<FormControl
|
||||||
|
type="select"
|
||||||
|
:options="days"
|
||||||
|
v-model="newSlot.day"
|
||||||
|
@focusout.stop="add()"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="time"
|
||||||
|
v-model="newSlot.start_time"
|
||||||
|
@focusout.stop="add()"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="time"
|
||||||
|
v-model="newSlot.end_time"
|
||||||
|
@focusout.stop="add()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button @click="showSlotsTemplate = 1">
|
||||||
|
<template #prefix>
|
||||||
|
<Plus class="w-4 h-4 stroke-1.5 text-gray-700" />
|
||||||
|
</template>
|
||||||
|
{{ __('Add Slot') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div class="mt-10 w-3/4">
|
||||||
|
<h2 class="mb-4 text-lg font-semibold text-gray-900">
|
||||||
|
{{ __('I am unavailable') }}
|
||||||
|
</h2>
|
||||||
|
<div class="grid grid-cols-4 gap-4">
|
||||||
|
<FormControl
|
||||||
|
type="date"
|
||||||
|
:label="__('From')"
|
||||||
|
v-model="from"
|
||||||
|
@change.stop="
|
||||||
|
() => {
|
||||||
|
updateUnavailability.submit({
|
||||||
|
field: 'unavailable_from',
|
||||||
|
value: from,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="date"
|
||||||
|
:label="__('To')"
|
||||||
|
v-model="to"
|
||||||
|
@change.stop="
|
||||||
|
() => {
|
||||||
|
updateUnavailability.submit({
|
||||||
|
field: 'unavailable_to',
|
||||||
|
value: to,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { createResource, FormControl, Button } from 'frappe-ui'
|
||||||
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
import { showToast, convertToTitleCase } from '@/utils'
|
||||||
|
import { Plus, X } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
profile: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const showSlotsTemplate = ref(0)
|
||||||
|
const from = ref(null)
|
||||||
|
const to = ref(null)
|
||||||
|
|
||||||
|
const newSlot = reactive({
|
||||||
|
day: '',
|
||||||
|
start_time: '',
|
||||||
|
end_time: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const slots = createResource({
|
||||||
|
url: 'lms.lms.api.get_evaluator_details',
|
||||||
|
params: {
|
||||||
|
evaluator: props.profile.data?.name,
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createSlot = createResource({
|
||||||
|
url: 'frappe.client.insert',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
doc: {
|
||||||
|
doctype: 'Evaluator Schedule',
|
||||||
|
parent: slots.data?.name,
|
||||||
|
parentfield: 'schedule',
|
||||||
|
parenttype: 'Course Evaluator',
|
||||||
|
...newSlot,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess() {
|
||||||
|
showToast('Success', 'Slot added successfully', 'check')
|
||||||
|
slots.reload()
|
||||||
|
showSlotsTemplate.value = 0
|
||||||
|
newSlot.day = ''
|
||||||
|
newSlot.start_time = ''
|
||||||
|
newSlot.end_time = ''
|
||||||
|
},
|
||||||
|
onError(err) {
|
||||||
|
showToast('Error', err.messages?.[0] || err, 'x')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateSlot = createResource({
|
||||||
|
url: 'frappe.client.set_value',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
doctype: 'Evaluator Schedule',
|
||||||
|
name: values.name,
|
||||||
|
fieldname: values.field,
|
||||||
|
value: values.value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess() {
|
||||||
|
showToast('Success', 'Availability updated successfully', 'check')
|
||||||
|
},
|
||||||
|
onError(err) {
|
||||||
|
showToast('Error', err.messages?.[0] || err, 'x')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleteSlot = createResource({
|
||||||
|
url: 'frappe.client.delete',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
doctype: 'Evaluator Schedule',
|
||||||
|
name: values.name,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess() {
|
||||||
|
showToast('Success', 'Slot deleted successfully', 'check')
|
||||||
|
slots.reload()
|
||||||
|
},
|
||||||
|
onError(err) {
|
||||||
|
showToast('Error', err.messages?.[0] || err, 'x')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateUnavailability = createResource({
|
||||||
|
url: 'frappe.client.set_value',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
doctype: 'Course Evaluator',
|
||||||
|
name: slots.data?.name,
|
||||||
|
fieldname: values.field,
|
||||||
|
value: values.value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess() {
|
||||||
|
showToast('Success', 'Unavailability updated successfully', 'check')
|
||||||
|
},
|
||||||
|
onError(err) {
|
||||||
|
showToast('Error', err.messages?.[0] || err, 'x')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const update = (name, field, value) => {
|
||||||
|
updateSlot.submit(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
field,
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validate() {
|
||||||
|
if (!value) {
|
||||||
|
return `Please enter a value for ${convertToTitleCase(field)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const add = () => {
|
||||||
|
if (!newSlot.day || !newSlot.start_time || !newSlot.end_time) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
createSlot.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRow = (name) => {
|
||||||
|
deleteSlot.submit({ name })
|
||||||
|
}
|
||||||
|
|
||||||
|
const days = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Monday',
|
||||||
|
value: 'Monday',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tuesday',
|
||||||
|
value: 'Tuesday',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Wednesday',
|
||||||
|
value: 'Wednesday',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Thursday',
|
||||||
|
value: 'Thursday',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Friday',
|
||||||
|
value: 'Friday',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Saturday',
|
||||||
|
value: 'Saturday',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sunday',
|
||||||
|
value: 'Sunday',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -73,13 +73,13 @@ const routes = [
|
|||||||
component: () => import('@/pages/ProfileCertificates.vue'),
|
component: () => import('@/pages/ProfileCertificates.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ProfileSettings',
|
name: 'ProfileRoles',
|
||||||
path: 'settings',
|
path: 'roles',
|
||||||
component: () => import('@/pages/ProfileSettings.vue'),
|
component: () => import('@/pages/ProfileRoles.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ProfileEvaluator',
|
name: 'ProfileEvaluator',
|
||||||
path: 'evaluator',
|
path: 'evaluations',
|
||||||
component: () => import('@/pages/ProfileEvaluator.vue'),
|
component: () => import('@/pages/ProfileEvaluator.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -298,3 +298,16 @@ def get_unsplash_photos(keyword=None):
|
|||||||
return get_by_keyword(keyword)
|
return get_by_keyword(keyword)
|
||||||
|
|
||||||
return frappe.cache().get_value("unsplash_photos", generator=get_list)
|
return frappe.cache().get_value("unsplash_photos", generator=get_list)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_evaluator_details(evaluator):
|
||||||
|
frappe.only_for("Batch Evaluator")
|
||||||
|
|
||||||
|
if frappe.db.exists("Course Evaluator", {"evaluator": evaluator}):
|
||||||
|
return frappe.get_doc("Course Evaluator", evaluator, as_dict=1)
|
||||||
|
else:
|
||||||
|
doc = frappe.new_doc("Course Evaluator")
|
||||||
|
doc.evaluator = evaluator
|
||||||
|
doc.insert()
|
||||||
|
return doc.as_dict()
|
||||||
|
|||||||
@@ -8,7 +8,11 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"evaluator",
|
"evaluator",
|
||||||
"schedule"
|
"schedule",
|
||||||
|
"unavailability_section",
|
||||||
|
"unavailable_from",
|
||||||
|
"column_break_ahzi",
|
||||||
|
"unavailable_to"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -23,11 +27,30 @@
|
|||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Schedule",
|
"label": "Schedule",
|
||||||
"options": "Evaluator Schedule"
|
"options": "Evaluator Schedule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "unavailability_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Unavailability"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_ahzi",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "unavailable_from",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "From"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "unavailable_to",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "To"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-15 11:21:52.182338",
|
"modified": "2024-04-15 18:45:08.614466",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "Course Evaluator",
|
"name": "Course Evaluator",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from frappe import _
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from lms.lms.utils import get_evaluator
|
from lms.lms.utils import get_evaluator
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from frappe.utils import get_time
|
||||||
|
|
||||||
|
|
||||||
class CourseEvaluator(Document):
|
class CourseEvaluator(Document):
|
||||||
@@ -14,7 +15,7 @@ class CourseEvaluator(Document):
|
|||||||
|
|
||||||
def validate_time_slots(self):
|
def validate_time_slots(self):
|
||||||
for schedule in self.schedule:
|
for schedule in self.schedule:
|
||||||
if schedule.start_time >= schedule.end_time:
|
if get_time(schedule.start_time) >= get_time(schedule.end_time):
|
||||||
frappe.throw(_("Start Time cannot be greater than End Time"))
|
frappe.throw(_("Start Time cannot be greater than End Time"))
|
||||||
|
|
||||||
self.validate_overlaps(schedule)
|
self.validate_overlaps(schedule)
|
||||||
@@ -26,11 +27,21 @@ class CourseEvaluator(Document):
|
|||||||
overlap = False
|
overlap = False
|
||||||
|
|
||||||
for slot in same_day_slots:
|
for slot in same_day_slots:
|
||||||
if schedule.start_time <= slot.start_time < schedule.end_time:
|
if (
|
||||||
|
get_time(schedule.start_time)
|
||||||
|
<= get_time(slot.start_time)
|
||||||
|
< get_time(schedule.end_time)
|
||||||
|
):
|
||||||
overlap = True
|
overlap = True
|
||||||
if schedule.start_time < slot.end_time <= schedule.end_time:
|
if (
|
||||||
|
get_time(schedule.start_time)
|
||||||
|
< get_time(slot.end_time)
|
||||||
|
<= get_time(schedule.end_time)
|
||||||
|
):
|
||||||
overlap = True
|
overlap = True
|
||||||
if slot.start_time < schedule.start_time and schedule.end_time < slot.end_time:
|
if get_time(slot.start_time) < get_time(schedule.start_time) and get_time(
|
||||||
|
schedule.end_time
|
||||||
|
) < get_time(slot.end_time):
|
||||||
overlap = True
|
overlap = True
|
||||||
|
|
||||||
if overlap:
|
if overlap:
|
||||||
|
|||||||
@@ -39,8 +39,6 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "course.evaluator",
|
|
||||||
"fetch_if_empty": 1,
|
|
||||||
"fieldname": "evaluator",
|
"fieldname": "evaluator",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Evaluator",
|
"label": "Evaluator",
|
||||||
@@ -109,7 +107,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-15 11:23:03.933035",
|
"modified": "2024-04-16 11:01:28.336807",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certificate Request",
|
"name": "LMS Certificate Request",
|
||||||
|
|||||||
@@ -11,10 +11,35 @@ from lms.lms.utils import get_evaluator
|
|||||||
|
|
||||||
class LMSCertificateRequest(Document):
|
class LMSCertificateRequest(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.set_evaluator()
|
||||||
|
self.validate_unavailability()
|
||||||
self.validate_slot()
|
self.validate_slot()
|
||||||
self.validate_if_existing_requests()
|
self.validate_if_existing_requests()
|
||||||
self.validate_evaluation_end_date()
|
self.validate_evaluation_end_date()
|
||||||
|
|
||||||
|
def set_evaluator(self):
|
||||||
|
if not self.evaluator:
|
||||||
|
self.evaluator = get_evaluator(self.course, self.batch_name)
|
||||||
|
|
||||||
|
def validate_unavailability(self):
|
||||||
|
unavailable = frappe.db.get_value(
|
||||||
|
"Course Evaluator", self.evaluator, ["unavailable_from", "unavailable_to"], as_dict=1
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
unavailable.unavailable_from
|
||||||
|
and unavailable.unavailable_to
|
||||||
|
and getdate(self.date) >= unavailable.unavailable_from
|
||||||
|
and getdate(self.date) <= unavailable.unavailable_to
|
||||||
|
):
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Evaluator is unavailable from {0} to {1}. Please select a date after {1}"
|
||||||
|
).format(
|
||||||
|
format_date(unavailable.unavailable_from, "medium"),
|
||||||
|
format_date(unavailable.unavailable_to, "medium"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def validate_slot(self):
|
def validate_slot(self):
|
||||||
if frappe.db.exists(
|
if frappe.db.exists(
|
||||||
"LMS Certificate Request",
|
"LMS Certificate Request",
|
||||||
|
|||||||
@@ -1798,6 +1798,6 @@ def get_roles(name):
|
|||||||
return {
|
return {
|
||||||
"moderator": has_course_moderator_role(name),
|
"moderator": has_course_moderator_role(name),
|
||||||
"course_creator": has_course_instructor_role(name),
|
"course_creator": has_course_instructor_role(name),
|
||||||
"class_evaluator": has_course_evaluator_role(name),
|
"batch_evaluator": has_course_evaluator_role(name),
|
||||||
"lms_student": has_student_role(name),
|
"lms_student": has_student_role(name),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user