diff --git a/.github/workflows/make_release_pr.yml b/.github/workflows/make_release_pr.yml index b8064ea2..8530727f 100644 --- a/.github/workflows/make_release_pr.yml +++ b/.github/workflows/make_release_pr.yml @@ -1,8 +1,7 @@ name: Create weekly release on: schedule: - # 13:00 UTC -> 7pm IST on every Wednesday - - cron: '30 4 * * 3' + - cron: '30 4 15 * *' workflow_dispatch: jobs: diff --git a/frontend/src/components/JobCard.vue b/frontend/src/components/JobCard.vue index 51b84b95..64cd0e01 100644 --- a/frontend/src/components/JobCard.vue +++ b/frontend/src/components/JobCard.vue @@ -1,10 +1,7 @@ @@ -99,13 +123,13 @@ import { Avatar, Breadcrumbs, Button, + call, createListResource, FormControl, Select, usePageMeta, } from 'frappe-ui' -import { computed, onMounted, ref } from 'vue' -import { updateDocumentTitle } from '@/utils' +import { computed, inject, onMounted, ref } from 'vue' import { BookOpen, GraduationCap } from 'lucide-vue-next' import { sessionStore } from '../stores/session' @@ -113,6 +137,8 @@ const currentCategory = ref('') const filters = ref({}) const nameFilter = ref('') const { brand } = sessionStore() +const memberCount = ref(0) +const dayjs = inject('$dayjs') onMounted(() => { updateParticipants() @@ -126,6 +152,12 @@ const participants = createListResource({ pageLength: 30, }) +const count = call('lms.lms.api.get_count_of_certified_members').then( + (data) => { + memberCount.value = data + } +) + const categories = createListResource({ doctype: 'LMS Certificate', url: 'lms.lms.api.get_certification_categories', @@ -161,14 +193,14 @@ const updateFilters = () => { const breadcrumbs = computed(() => [ { - label: __('Certified Participants'), + label: __('Certified Members'), route: { name: 'CertifiedParticipants' }, }, ]) usePageMeta(() => { return { - title: __('Certified Participants'), + title: __('Certified Members'), icon: brand.favicon, } }) diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js index edce83cb..b3f87c97 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.js @@ -479,7 +479,7 @@ export function getSidebarLinks() { activeFor: ['Batches', 'BatchDetail', 'Batch', 'BatchForm'], }, { - label: 'Certified Participants', + label: 'Certified Members', icon: 'GraduationCap', to: 'CertifiedParticipants', activeFor: ['CertifiedParticipants'], diff --git a/lms/lms/api.py b/lms/lms/api.py index eb6d3ca5..cc8f7df0 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -19,6 +19,8 @@ from frappe.utils import ( format_date, date_diff, ) +from frappe.query_builder import DocType +from pypika.functions import DistinctOptionFunction from lms.lms.utils import get_average_rating, get_lesson_count from xml.dom.minidom import parseString from lms.lms.doctype.course_lesson.course_lesson import save_progress @@ -411,29 +413,50 @@ def get_certified_participants(filters=None, start=0, page_length=30): or_filters["course_title"] = ["like", f"%{category}%"] or_filters["batch_title"] = ["like", f"%{category}%"] - participants = frappe.get_all( + participants = frappe.db.get_all( "LMS Certificate", filters=filters, or_filters=or_filters, - fields=["member"], + fields=["member", "issue_date"], group_by="member", - order_by="creation desc", + order_by="issue_date desc", start=start, page_length=page_length, ) for participant in participants: + count = frappe.db.count("LMS Certificate", {"member": participant.member}) details = frappe.db.get_value( "User", participant.member, ["full_name", "user_image", "username", "country", "headline"], as_dict=1, ) + details["certificate_count"] = count participant.update(details) return participants +class CountDistinct(DistinctOptionFunction): + def __init__(self, field): + super().__init__("COUNT", field, distinct=True) + + +@frappe.whitelist(allow_guest=True) +def get_count_of_certified_members(): + Certificate = DocType("LMS Certificate") + + query = ( + frappe.qb.from_(Certificate) + .select(CountDistinct(Certificate.member).as_("total")) + .where(Certificate.published == 1) + ) + + result = query.run(as_dict=True) + return result[0]["total"] if result else 0 + + @frappe.whitelist(allow_guest=True) def get_certification_categories(): categories = [] @@ -655,13 +678,13 @@ def get_categories(doctype, filters): @frappe.whitelist() def get_members(start=0, search=""): """Get members for the given search term and start index. - Args: start (int): Start index for the query. + Args: start (int): Start index for the query. <<<<<<< HEAD - search (str): Search term to filter the results. + search (str): Search term to filter the results. ======= - search (str): Search term to filter the results. + search (str): Search term to filter the results. >>>>>>> 4869bba7bbb2fb38477d6fc29fb3b5838e075577 - Returns: List of members. + Returns: List of members. """ filters = {"enabled": 1, "name": ["not in", ["Administrator", "Guest"]]} diff --git a/lms/www/lms.py b/lms/www/lms.py index 97f520a5..6e76c58b 100644 --- a/lms/www/lms.py +++ b/lms/www/lms.py @@ -29,7 +29,6 @@ def get_context(): def get_meta(app_path, title, favicon, description): meta = frappe._dict() - if app_path: meta = get_meta_from_document(app_path) @@ -286,4 +285,11 @@ def get_meta_from_document(app_path): "link": "/programs", } + if app_path == "certified-participants": + return { + "title": _("Certified Participants"), + "keywords": "All Certified Participants, Certified Participants, Learn, Certification", + "link": "/certified-participants", + } + return {}