Compare commits

...

13 Commits

Author SHA1 Message Date
Jannat Patel
699c821edd fix: spacing and widths 2024-08-05 11:12:34 +05:30
Jannat Patel
6820dfc820 fix: make question attachments public 2024-08-01 13:02:20 +05:30
Jannat Patel
e0855a2c1b fix: quiz question population issue 2024-07-31 22:30:29 +05:30
Jannat Patel
6a0b37a4d4 chore: changed release branch to develop 2024-07-31 12:43:04 +05:30
Jannat Patel
f7fd6916e2 chore: release notes action 2024-07-31 12:15:24 +05:30
Jannat Patel
30e61f4b7c Merge pull request #947 from pateljannat/semantic-release
chore: semantic release action
2024-07-31 11:45:42 +05:30
Jannat Patel
48b37d58d8 chore: semantic release action 2024-07-31 11:10:47 +05:30
Jannat Patel
e96f18df7c Merge pull request #946 from pateljannat/issues-27
fix: quiz shuffle issue
2024-07-30 18:04:26 +05:30
Jannat Patel
7d15527831 fix: quiz shuffle issue 2024-07-30 16:58:09 +05:30
Jannat Patel
794c0e760b Merge pull request #945 from pateljannat/issues-26
fix: eval request issues
2024-07-30 15:26:46 +05:30
Jannat Patel
e46a60d00a chore: linters 2024-07-30 15:05:24 +05:30
Jannat Patel
819aac70fd fix: linters 2024-07-30 14:54:21 +05:30
Jannat Patel
ed7db2d7c5 fix: eval request issues 2024-07-30 14:17:17 +05:30
14 changed files with 509 additions and 259 deletions

32
.github/workflows/on_release.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Generate Semantic Release
on:
workflow_dispatch:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Entire Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup dependencies
run: |
npm install @semantic-release/git @semantic-release/exec --no-save
- name: Create Release
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GIT_AUTHOR_NAME: "Frappe PR Bot"
GIT_AUTHOR_EMAIL: "developers@frappe.io"
GIT_COMMITTER_NAME: "Frappe PR Bot"
GIT_COMMITTER_EMAIL: "developers@frappe.io"
run: npx semantic-release

39
.github/workflows/release_notes.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
# This action:
#
# 1. Generates release notes using github API.
# 2. Strips unnecessary info like chore/style etc from notes.
# 3. Updates release info.
name: 'Release Notes'
on:
workflow_dispatch:
inputs:
tag_name:
description: 'Tag of release like v2.0.0'
required: true
type: string
release:
types: [released]
permissions:
contents: read
jobs:
regen-notes:
name: 'Regenerate release notes'
runs-on: ubuntu-latest
steps:
- name: Update notes
run: |
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/lms/releases/generate-notes -f tag_name=$RELEASE_TAG \
| jq -r '.body' \
| sed -E '/^\* (chore|ci|test|docs|style)/d' \
| sed -E 's/by @mergify //'
)
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/lms/releases/tags/$RELEASE_TAG | jq -r '.id')
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/lms/releases/$RELEASE_ID -f body="$NEW_NOTES"
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}

21
.releaserc Normal file
View File

@@ -0,0 +1,21 @@
{
"branches": ["develop"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular"
},
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec", {
"prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" lms/__init__.py'
}
],
[
"@semantic-release/git", {
"assets": ["lms/__init__.py"],
"message": "chore(release): Bumped to Version ${nextRelease.version}"
}
],
"@semantic-release/github"
]
}

Submodule frappe-ui deleted from aa44431c18

View File

@@ -3,7 +3,7 @@
class="flex flex-col shadow hover:bg-gray-100 rounded-md p-4 h-full" class="flex flex-col shadow hover:bg-gray-100 rounded-md p-4 h-full"
style="min-height: 150px" style="min-height: 150px"
> >
<div class="text-xl font-semibold mb-2"> <div class="text-lg leading-5 font-semibold mb-2">
{{ batch.title }} {{ batch.title }}
</div> </div>
<Badge <Badge
@@ -22,18 +22,17 @@
> >
{{ __('Sold Out') }} {{ __('Sold Out') }}
</Badge> </Badge>
<div class="short-introduction"> <div class="short-introduction text-sm text-gray-700">
{{ batch.description }} {{ batch.description }}
</div> </div>
<div v-if="batch.amount" class="font-semibold mb-4">
{{ batch.price }}
</div>
<div class="flex flex-col space-y-2 mt-auto"> <div class="flex flex-col space-y-2 mt-auto">
<div v-if="batch.amount" class="font-semibold text-lg">
{{ batch.price }}
</div>
<DateRange <DateRange
:startDate="batch.start_date" :startDate="batch.start_date"
:endDate="batch.end_date" :endDate="batch.end_date"
class="text-sm text-gray-700 mb-3" class="text-sm text-gray-700"
/> />
<div class="flex items-center text-sm text-gray-700"> <div class="flex items-center text-sm text-gray-700">
<Clock class="h-4 w-4 stroke-1.5 mr-2 text-gray-700" /> <Clock class="h-4 w-4 stroke-1.5 mr-2 text-gray-700" />
@@ -50,18 +49,21 @@
{{ batch.timezone }} {{ batch.timezone }}
</span> </span>
</div> </div>
<div v-if="batch.instructors?.length" class="flex avatar-group overlap"> </div>
<div <div
class="h-6 mr-1" v-if="batch.instructors?.length"
:class="{ 'avatar-group overlap': batch.instructors.length > 1 }" class="flex avatar-group overlap mt-4"
> >
<UserAvatar <div
v-for="instructor in batch.instructors" class="h-6 mr-1"
:user="instructor" :class="{ 'avatar-group overlap': batch.instructors.length > 1 }"
/> >
</div> <UserAvatar
<CourseInstructors :instructors="batch.instructors" /> v-for="instructor in batch.instructors"
:user="instructor"
/>
</div> </div>
<CourseInstructors :instructors="batch.instructors" />
</div> </div>
</div> </div>
</template> </template>
@@ -88,7 +90,7 @@ const props = defineProps({
text-overflow: ellipsis; text-overflow: ellipsis;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
margin: 0.25rem 0 1.25rem; margin: 0.25rem 0 1rem;
line-height: 1.5; line-height: 1.5;
} }

View File

@@ -72,7 +72,7 @@
{{ course.title }} {{ course.title }}
</div> </div>
<div class="short-introduction"> <div class="short-introduction text-gray-700 text-sm">
{{ course.short_introduction }} {{ course.short_introduction }}
</div> </div>

View File

@@ -3,9 +3,7 @@
<div class="bg-blue-100 py-2 px-2 mb-4 rounded-md text-sm text-blue-800"> <div class="bg-blue-100 py-2 px-2 mb-4 rounded-md text-sm text-blue-800">
<div class="leading-relaxed"> <div class="leading-relaxed">
{{ {{
__('This quiz consists of {0} questions.').format( __('This quiz consists of {0} questions.').format(questions.length)
quiz.data.questions.length
)
}} }}
</div> </div>
<div v-if="quiz.data.passing_percentage" class="leading-relaxed"> <div v-if="quiz.data.passing_percentage" class="leading-relaxed">
@@ -59,7 +57,7 @@
</div> </div>
</div> </div>
<div v-else-if="!quizSubmission.data"> <div v-else-if="!quizSubmission.data">
<div v-for="(question, qtidx) in quiz.data.questions"> <div v-for="(question, qtidx) in questions">
<div <div
v-if="qtidx == activeQuestion - 1 && questionDetails.data" v-if="qtidx == activeQuestion - 1 && questionDetails.data"
class="border rounded-md p-5" class="border rounded-md p-5"
@@ -166,7 +164,7 @@
{{ {{
__('Question {0} of {1}').format( __('Question {0} of {1}').format(
activeQuestion, activeQuestion,
quiz.data.questions.length questions.length
) )
}} }}
</div> </div>
@@ -179,7 +177,7 @@
</span> </span>
</Button> </Button>
<Button <Button
v-else-if="activeQuestion != quiz.data.questions.length" v-else-if="activeQuestion != questions.length"
@click="nextQuetion()" @click="nextQuetion()"
> >
<span> <span>
@@ -250,6 +248,7 @@ const activeQuestion = ref(0)
const currentQuestion = ref('') const currentQuestion = ref('')
const selectedOptions = reactive([0, 0, 0, 0]) const selectedOptions = reactive([0, 0, 0, 0])
const showAnswers = reactive([]) const showAnswers = reactive([])
let questions = reactive([])
const possibleAnswer = ref(null) const possibleAnswer = ref(null)
const props = defineProps({ const props = defineProps({
@@ -270,15 +269,30 @@ const quiz = createResource({
cache: ['quiz', props.quizName], cache: ['quiz', props.quizName],
auto: true, auto: true,
onSuccess(data) { onSuccess(data) {
if (data.shuffle_questions) { populateQuestions()
data.questions = data.questions.sort(() => Math.random() - 0.5)
}
if (data.limit_questions_to) {
data.questions = data.questions.slice(0, data.limit_questions_to)
}
}, },
}) })
const populateQuestions = () => {
let data = quiz.data
if (data.shuffle_questions) {
questions = shuffleArray(data.questions)
if (data.limit_questions_to) {
questions = questions.slice(0, data.limit_questions_to)
}
} else {
questions = data.questions
}
}
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[array[i], array[j]] = [array[j], array[i]]
}
return array
}
const attempts = createResource({ const attempts = createResource({
url: 'frappe.client.get_list', url: 'frappe.client.get_list',
makeParams(values) { makeParams(values) {
@@ -310,7 +324,7 @@ const attempts = createResource({
watch( watch(
() => quiz.data, () => quiz.data,
() => { () => {
if (quiz.data) { if (quiz.data && quiz.data.max_attempts) {
attempts.reload() attempts.reload()
resetQuiz() resetQuiz()
} }
@@ -464,7 +478,7 @@ const submitQuiz = () => {
const createSubmission = () => { const createSubmission = () => {
quizSubmission.reload().then(() => { quizSubmission.reload().then(() => {
attempts.reload() if (quiz.data && quiz.data.max_attempts) attempts.reload()
}) })
} }
@@ -473,6 +487,7 @@ const resetQuiz = () => {
selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0]) selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0])
showAnswers.length = 0 showAnswers.length = 0
quizSubmission.reset() quizSubmission.reset()
populateQuestions()
} }
const getSubmissionColumns = () => { const getSubmissionColumns = () => {

View File

@@ -50,9 +50,9 @@
</Button> </Button>
</div> </div>
</header> </header>
<div v-if="job.data" class="w-3/4 mx-auto"> <div v-if="job.data" class="max-w-3xl mx-auto">
<div class="p-4"> <div class="p-4">
<div class="flex mb-4"> <div class="flex mb-10">
<img <img
:src="job.data.company_logo" :src="job.data.company_logo"
class="w-16 h-16 rounded-lg object-contain mr-4" class="w-16 h-16 rounded-lg object-contain mr-4"
@@ -62,40 +62,36 @@
<div class="text-2xl font-semibold mb-4"> <div class="text-2xl font-semibold mb-4">
{{ job.data.job_title }} {{ job.data.job_title }}
</div> </div>
<div class="grid grid-cols-3 gap-8"> <div
<div class="grid grid-cols-1 gap-2"> class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-2 md:gap-y-4"
<div class="flex items-center space-x-2"> >
<Building2 class="h-4 w-4 stroke-1.5" /> <div class="flex items-center space-x-2">
<span>{{ job.data.company_name }}</span> <Building2 class="h-4 w-4 stroke-1.5" />
</div> <span>{{ job.data.company_name }}</span>
<div class="flex items-center space-x-2">
<MapPin class="h-4 w-4 stroke-1.5" />
<span>{{ job.data.location }}</span>
</div>
</div> </div>
<div class="grid grid-cols-1 gap-2"> <div class="flex items-center space-x-2">
<div class="flex items-center space-x-2"> <MapPin class="h-4 w-4 stroke-1.5" />
<ClipboardType class="h-4 w-4 stroke-1.5" /> <span>{{ job.data.location }}</span>
<span>{{ job.data.type }}</span>
</div>
<div class="flex items-center space-x-2">
<CalendarDays class="h-4 w-4 stroke-1.5" />
<span>{{
dayjs(job.data.creation).format('DD MMM YYYY')
}}</span>
</div>
</div> </div>
<div class="grid grid-cols-1 h-fit"> <div class="flex items-center space-x-2">
<div <ClipboardType class="h-4 w-4 stroke-1.5" />
v-if="applicationCount.data" <span>{{ job.data.type }}</span>
class="flex items-center space-x-2" </div>
<div class="flex items-center space-x-2">
<CalendarDays class="h-4 w-4 stroke-1.5" />
<span>
{{ dayjs(job.data.creation).format('DD MMM YYYY') }}
</span>
</div>
<div
v-if="applicationCount.data"
class="flex items-center space-x-2"
>
<SquareUserRound class="h-4 w-4 stroke-1.5" />
<span
>{{ applicationCount.data }}
{{ __('applications received') }}</span
> >
<SquareUserRound class="h-4 w-4 stroke-1.5" />
<span
>{{ applicationCount.data }}
{{ __('applications received') }}</span
>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -176,7 +176,10 @@ update_website_context = [
] ]
jinja = { jinja = {
"methods": ["lms.lms.utils.get_signup_optin_checks"], "methods": [
"lms.lms.utils.get_signup_optin_checks",
"lms.lms.utils.get_tags",
],
"filters": [], "filters": [],
} }
## Specify the additional tabs to be included in the user profile page. ## Specify the additional tabs to be included in the user profile page.

View File

@@ -56,6 +56,7 @@ class LMSCertificateRequest(Document):
"evaluator": self.evaluator, "evaluator": self.evaluator,
"date": self.date, "date": self.date,
"start_time": self.start_time, "start_time": self.start_time,
"member": ["!=", self.member],
}, },
): ):
frappe.throw(_("The slot is already booked by another participant.")) frappe.throw(_("The slot is already booked by another participant."))

View File

@@ -195,7 +195,8 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-05-24 16:12:26.331351", "make_attachments_public": 1,
"modified": "2024-08-01 13:01:55.000072",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Question", "name": "LMS Question",

View File

@@ -5,5 +5,5 @@
<p> {{ _("Hey {0}").format(doc.member_name) }} </p> <p> {{ _("Hey {0}").format(doc.member_name) }} </p>
<p> {{ _('Your evaluation for the course {0} has been scheduled on {1} at {2} {3}.').format(title, frappe.utils.format_date(doc.date, "medium"), frappe.utils.format_time(doc.start_time, "short"), timezone) }}</p> <p> {{ _('Your evaluation for the course {0} has been scheduled on {1} at {2} {3}.').format(title, frappe.utils.format_date(doc.date, "medium"), frappe.utils.format_time(doc.start_time, "short"), timezone) }}</p>
<p> {{ _("Your evaluator is {0}").format(evaluator_name) }} <p> {{ _("Your evaluator is {0}").format(evaluator_name) }} </p>
<p> {{ _("Please prepare well and be on time for the evaluations.") }} </p> <p> {{ _("Please prepare well and be on time for the evaluations.") }} </p>

View File

@@ -26,5 +26,8 @@
"devDependencies": { "devDependencies": {
"cypress": "^13.9.0", "cypress": "^13.9.0",
"cypress-file-upload": "^5.0.8" "cypress-file-upload": "^5.0.8"
},
"dependencies": {
"pre-commit": "^1.2.2"
} }
} }

514
yarn.lock

File diff suppressed because it is too large Load Diff