diff --git a/frontend/src/pages/Search/Search.vue b/frontend/src/pages/Search/Search.vue
index 2a0ee6ef..d14760d4 100644
--- a/frontend/src/pages/Search/Search.vue
+++ b/frontend/src/pages/Search/Search.vue
@@ -79,7 +79,10 @@
@@ -87,7 +90,8 @@
dayjs(
result.published_on ||
result.start_date ||
- result.creation
+ result.creation ||
+ result.modified
).format('DD MMM YYYY')
}}
@@ -164,10 +168,22 @@ const generateSearchResults = () => {
searchResults.value.push(item)
})
})
- searchResults.value.sort((a, b) => b.score - a.score)
+ sortResults()
}
}
+const sortResults = () => {
+ searchResults.value.sort((a, b) => {
+ const dateA = new Date(
+ a.published_on || a.start_date || a.creation || a.modified
+ ).getTime()
+ const dateB = new Date(
+ b.published_on || b.start_date || b.creation || b.modified
+ ).getTime()
+ return dateB - dateA
+ })
+}
+
const navigate = (result: any) => {
if (result.doctype == 'LMS Course') {
router.push({
diff --git a/lms/command_palette.py b/lms/command_palette.py
index 0a182cee..986e1780 100644
--- a/lms/command_palette.py
+++ b/lms/command_palette.py
@@ -17,9 +17,20 @@ def search_sqlite(query: str):
def prepare_search_results(result):
+ groups = get_grouped_results(result)
+
+ out = []
+ for key in groups:
+ groups[key] = remove_duplicates(groups[key])
+ groups[key].sort(key=lambda x: x.get("modified"), reverse=True)
+ out.append({"title": key, "items": groups[key]})
+
+ return out
+
+
+def get_grouped_results(result):
roles = frappe.get_roles()
groups = {}
-
for r in result["results"]:
doctype = r["doctype"]
if doctype == "LMS Course" and can_access_course(r, roles):
@@ -31,12 +42,17 @@ def prepare_search_results(result):
elif doctype == "Job Opportunity" and can_access_job(r, roles):
r["author_info"] = get_instructor_info(doctype, r)
groups.setdefault("Job Opportunities", []).append(r)
+ return groups
- out = []
- for key in groups:
- out.append({"title": key, "items": groups[key]})
- return out
+def remove_duplicates(items):
+ seen = set()
+ unique_items = []
+ for item in items:
+ if item["name"] not in seen:
+ seen.add(item["name"])
+ unique_items.append(item)
+ return unique_items
def can_access_course(course, roles):
@@ -73,10 +89,14 @@ def get_instructor_info(doctype, record):
instructors = frappe.get_all(
"Course Instructor", filters={"parenttype": doctype, "parent": record.get("name")}, pluck="instructor"
)
-
instructor = record.get("author")
if len(instructors):
- instructor = instructors[0]
+ for ins in instructors:
+ if ins.split("@")[0] in record.get("content"):
+ instructor = ins
+ break
+ if not instructor:
+ instructor = instructors[0]
return frappe.db.get_value(
"User",
diff --git a/lms/lms/doctype/course_evaluator/test_course_evaluator.py b/lms/lms/doctype/course_evaluator/test_course_evaluator.py
index 61e3dc35..605bb553 100644
--- a/lms/lms/doctype/course_evaluator/test_course_evaluator.py
+++ b/lms/lms/doctype/course_evaluator/test_course_evaluator.py
@@ -52,13 +52,17 @@ class TestCourseEvaluator(UnitTestCase):
return first_date
def calculated_last_date_of_schedule(self, first_date):
- last_day = add_days(first_date, 56)
+ last_day = add_days(getdate(), 56)
offset_monday = (0 - last_day.weekday() + 7) % 7 # 0 for Monday
offset_wednesday = (2 - last_day.weekday() + 7) % 7 # 2 for Wednesday
- if offset_monday > offset_wednesday and offset_monday < 4:
+
+ if offset_monday < offset_wednesday and offset_monday <= 4:
last_day = add_days(last_day, offset_monday)
- else:
+ elif offset_wednesday <= 4:
last_day = add_days(last_day, offset_wednesday)
+ else:
+ last_day = add_days(last_day, min(offset_monday, offset_wednesday) + 7)
+
return last_day
def test_unavailability_dates(self):
diff --git a/lms/sqlite.py b/lms/sqlite.py
index 36ad7a4a..680677b1 100644
--- a/lms/sqlite.py
+++ b/lms/sqlite.py
@@ -2,7 +2,7 @@ from contextlib import suppress
import frappe
from frappe.search.sqlite_search import SQLiteSearch, SQLiteSearchIndexMissingError
-from frappe.utils import nowdate
+from frappe.utils import get_datetime, getdate, nowdate
class LearningSearch(SQLiteSearch):
@@ -17,6 +17,8 @@ class LearningSearch(SQLiteSearch):
"status",
"company_name",
"creation",
+ "parent",
+ "parenttype",
],
"tokenizer": "unicode61 remove_diacritics 2 tokenchars '-_'",
}
@@ -60,38 +62,66 @@ class LearningSearch(SQLiteSearch):
{"modified": "creation"},
],
},
+ "Course Instructor": {
+ "fields": [
+ "name",
+ {"title": "instructor"},
+ {"content": "instructor"},
+ "parent",
+ "parenttype",
+ "modified",
+ ]
+ },
}
+ COURSE_FIELDS = [
+ "name",
+ "title",
+ "description",
+ "short_introduction",
+ "category",
+ "published",
+ "published_on",
+ "creation",
+ "modified",
+ "owner",
+ ]
+
+ BATCH_FIELDS = [
+ "name",
+ "title",
+ "description",
+ "batch_details",
+ "category",
+ "start_date",
+ "creation",
+ "modified",
+ "owner",
+ "published",
+ ]
+
+ JOB_FIELDS = [
+ "name",
+ "job_title",
+ "company_name",
+ "description",
+ "creation",
+ "modified",
+ "owner",
+ ]
+
+ INSTRUCTOR_FIELDS = [
+ "name",
+ "instructor",
+ "parent",
+ "parenttype",
+ ]
+
DOCTYPE_FIELDS = {
- "LMS Course": [
- "name",
- "title",
- "description",
- "short_introduction",
- "category",
- "creation",
- "modified",
- "owner",
- ],
- "LMS Batch": [
- "name",
- "title",
- "description",
- "batch_details",
- "category",
- "creation",
- "modified",
- "owner",
- ],
- "Job Opportunity": [
- "name",
- "job_title",
- "company_name",
- "description",
- "creation",
- "modified",
- "owner",
- ],
+ "LMS Course": COURSE_FIELDS,
+ "LMS Batch": BATCH_FIELDS,
+ "Job Opportunity": JOB_FIELDS,
+ "Course Instructor": INSTRUCTOR_FIELDS,
}
def build_index(self):
@@ -103,6 +133,58 @@ class LearningSearch(SQLiteSearch):
def get_search_filters(self):
return {}
+ def prepare_document(self, doc):
+ document = super().prepare_document(doc)
+ if not document:
+ return None
+
+ if doc.doctype == "Course Instructor":
+ document = self.get_instructor_details(doc, document)
+ else:
+ if not document.get("modified"):
+ self.set_modified_date(doc, doc.doctype, document)
+
+ return document
+
+ def get_instructor_details(self, doc, document):
+ instructor = frappe.db.get_value("User", doc.instructor, "full_name")
+ fields = self.COURSE_FIELDS if doc.parenttype == "LMS Course" else self.BATCH_FIELDS
+ details = frappe.db.get_value(doc.parenttype, doc.parent, fields, as_dict=True)
+
+ if details:
+ document["doctype"] = doc.parenttype
+ document["name"] = doc.parent
+ document["title"] = self._process_content(details.title)
+ document["published"] = details.get("published", 0)
+ document["content"] = self._process_content(
+ f"Instructor: {instructor}\n{details.description}\n{doc.instructor}"
+ )
+ self.set_modified_date(details, doc.parenttype, document)
+ if doc.parenttype == "LMS Course":
+ document["published_on"] = details.get("published_on")
+ elif doc.parenttype == "LMS Batch":
+ document["start_date"] = details.get("start_date")
+
+ return document
+
+ def set_modified_date(self, details, doctype, document):
+ modified_value = None
+ if doctype == "LMS Course":
+ modified_value = details.get("published_on")
+ elif doctype == "LMS Batch":
+ modified_value = details.get("start_date")
+
+ if not modified_value:
+ modified_value = frappe.db.get_value(doctype, details.name, "creation")
+
+ modified_value = get_datetime(modified_value)
+ if doctype == "LMS Course":
+ document["published_on"] = getdate(modified_value)
+ elif doctype == "LMS Batch":
+ document["start_date"] = getdate(modified_value)
+
+ document["modified"] = modified_value.timestamp()
+
@SQLiteSearch.scoring_function
def get_doctype_boost(self, row, query, query_words):
doctype = row["doctype"]