Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search updates #8570

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5cf317d
Use DjangoJSONEncoder for bootstrapping plugin data to allow datetimes.
rtibbles Oct 30, 2021
f4bad16
Bootstrap full available channel data into the learn page.
rtibbles Oct 30, 2021
e917afb
Update useChannels composable to use bootstrapped channel data.
rtibbles Oct 30, 2021
85688cb
Consolidate search logic in useSearch composable.
rtibbles Oct 30, 2021
80ea591
Don't show channels selector on within channel search.
rtibbles Oct 30, 2021
d0d8065
Fix channel breadcrumbs display.
rtibbles Oct 30, 2021
07bac8d
Generalize channel specificity for search to topic specificity.
rtibbles Oct 30, 2021
dd679dc
Simplify routes. Add topics search route.
rtibbles Oct 30, 2021
661012e
Fix search chips display. Add translation for no categories.
rtibbles Oct 30, 2021
1dd4670
Fix search clearing.
rtibbles Oct 30, 2021
f07bd2e
Consolidate library page handler into a single function.
rtibbles Oct 30, 2021
e9bd98a
Fix breadcrumbs for route changes.
rtibbles Oct 30, 2021
e3f4884
Clean up unused constants, components and functionality.
rtibbles Oct 30, 2021
dcdf284
Upgrade kolibri-constants.
rtibbles Oct 30, 2021
5aed660
Add code comments to convoluted categories nesting code.
rtibbles Nov 1, 2021
67d1f18
Handle search clearing properly in all cases.
rtibbles Nov 1, 2021
0c1ea94
Rename tree viewset parameter to avoid collision with contentnode fil…
rtibbles Nov 1, 2021
9331bfd
Add redirect to handle historic channels page.
rtibbles Nov 1, 2021
d5ffc8e
Add full test coverage to useSearch module. Fix bugs and cleanup.
rtibbles Nov 2, 2021
13d0e19
Don't display chips for null or empty keywords value.
rtibbles Nov 2, 2021
22d330f
Update searchInputValue to always be empty string for no value.
rtibbles Nov 2, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions kolibri/core/assets/src/mixins/commonCoreStrings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,9 @@ const nonconformingKeys = {
MATERIALS: 'NeedsMaterials',
FOR_BEGINNERS: 'ForBeginners',
digitalLiteracy: 'digitialLiteracy',
BASIC_SKILLS: 'allLevelsBasicSkills',
FOUNDATIONS: 'basicSkills',
FOUNDATIONS_LOGIC_AND_CRITICAL_THINKING: 'logicAndCriticalThinking',
};

/**
Expand Down Expand Up @@ -1080,8 +1083,8 @@ export default {
* string mapping to the values to be passed for those arguments.
*/
coreString(key, args) {
if (key === 'None of the above') {
return noneOfTheAboveTranslator.$tr(key, args);
if (key === 'None of the above' || key === METADATA.NoCategories) {
return noneOfTheAboveTranslator.$tr('None of the above', args);
}

const metadataKey = get(MetadataLookup, key, null);
Expand Down
32 changes: 19 additions & 13 deletions kolibri/core/content/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from django_filters.rest_framework import ChoiceFilter
from django_filters.rest_framework import DjangoFilterBackend
from django_filters.rest_framework import FilterSet
from django_filters.rest_framework import NumberFilter
from django_filters.rest_framework import UUIDFilter
from le_utils.constants import content_kinds
from le_utils.constants import languages
Expand Down Expand Up @@ -185,6 +186,8 @@ class ContentNodeFilter(IdFilter):
channels = UUIDInFilter(name="channel_id")
languages = CharInFilter(name="lang_id")
categories__isnull = BooleanFilter(field_name="categories", lookup_expr="isnull")
lft__gt = NumberFilter(field_name="lft", lookup_expr="gt")
rght__lt = NumberFilter(field_name="rght", lookup_expr="lt")

class Meta:
model = models.ContentNode
Expand All @@ -211,6 +214,9 @@ class Meta:
"keywords",
"channels",
"languages",
"tree_id",
"lft__gt",
"rght__lt",
]

def filter_kind(self, queryset, name, value):
Expand Down Expand Up @@ -819,7 +825,7 @@ class ContentNodeTreeViewset(BaseContentNodeMixin, BaseValuesViewset):

def validate_and_return_params(self, request):
depth = request.query_params.get("depth", 2)
lft__gt = request.query_params.get("lft__gt")
next__gt = request.query_params.get("next__gt")

try:
depth = int(depth)
Expand All @@ -828,16 +834,16 @@ def validate_and_return_params(self, request):
except ValueError:
raise ValidationError("Depth query parameter must have the value 1 or 2")

if lft__gt is not None:
if next__gt is not None:
try:
lft__gt = int(lft__gt)
if 1 > lft__gt:
next__gt = int(next__gt)
if 1 > next__gt:
raise ValueError
except ValueError:
raise ValidationError(
"lft__gt query parameter must be a positive integer if specified"
"next__gt query parameter must be a positive integer if specified"
)
return depth, lft__gt
return depth, next__gt

def get_grandchild_ids(self, child_ids, depth):
if depth == 2:
Expand Down Expand Up @@ -868,13 +874,13 @@ def retrieve(self, request, **kwargs):
GET parameters on request can be:
depth - a value of either 1 or 2 indicating the depth to recurse the tree, either 1 or 2 levels
if this parameter is missing it will default to 2.
lft__gt - a value to return child nodes with a lft value greater than this, if missing defaults to None
next__gt - a value to return child nodes with a lft value greater than this, if missing defaults to None

The pagination object returned for "children" will have this form:
results - a list of serialized children, that can also have their own nested children attribute.
more - a dictionary or None, if a dictionary, will have an id key that is the id of the parent object
for these children, and a params key that is a dictionary of the required query parameters to query more
children for this parent - at a minimum this will include lft__gt and depth, but may also include
children for this parent - at a minimum this will include next__gt and depth, but may also include
other query parameters for filtering content nodes.

The "more" property describes the "id" required to do URL reversal on this endpoint, and the params that should
Expand All @@ -890,12 +896,12 @@ def retrieve(self, request, **kwargs):
# use for the `get_ancestors` MPTT method.
parent_model = self.get_object()

depth, lft__gt = self.validate_and_return_params(request)
depth, next__gt = self.validate_and_return_params(request)

# Get a list of child_ids of the parent node up to the pagination limit
child_qs = self.get_queryset().filter(parent=parent_model)
if lft__gt is not None:
child_qs = child_qs.filter(lft__gt=lft__gt)
if next__gt is not None:
child_qs = child_qs.filter(lft__gt=next__gt)
child_ids = child_qs.values_list("id", flat=True).order_by("lft")[
0 : self.page_size
]
Expand Down Expand Up @@ -956,13 +962,13 @@ def retrieve(self, request, **kwargs):
if len(desc_parent["children"]["results"]) == self.page_size:
# Any subsequent queries to get siblings of this node can restrict themselves
# to looking for nodes with lft greater than the rght value of this descendant
lft__gt = desc["rght"]
next__gt = desc["rght"]
# If the rght value of this descendant is exactly 1 less than the rght value of
# its parent, then there are no more children that can be queried.
# So only in this instance do we update the more URL
if desc["rght"] + 1 < desc_parent["rght"]:
params = request.query_params.copy()
params["lft__gt"] = lft__gt
params["next__gt"] = next__gt
params["depth"] = more_depth
desc_parent["children"]["more"] = {
"id": desc_parent["id"],
Expand Down
8 changes: 4 additions & 4 deletions kolibri/core/content/test/test_content_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def _recurse_and_assert(self, data, nodes, recursion_depth=0):
self.assertGreater(len(child_nodes), len(children["results"]))
self.assertEqual(children["more"]["id"], expected.id)
self.assertEqual(
children["more"]["params"]["lft__gt"], child_nodes[24].rght
children["more"]["params"]["next__gt"], child_nodes[24].rght
)
self.assertEqual(
children["more"]["params"]["depth"], 2 - recursion_depth
Expand Down Expand Up @@ -444,15 +444,15 @@ def test_contentnode_tree_depth_1(self):
== "django.db.backends.postgresql",
"Skipping postgres as not as vulnerable to large queries and large insertions are less performant",
)
def test_contentnode_tree_lft__gt(self):
def test_contentnode_tree_next__gt(self):
builder = ChannelBuilder(levels=2, num_children=30)
builder.insert_into_default_db()
content.ContentNode.objects.all().update(available=True)
root = content.ContentNode.objects.get(id=builder.root_node["id"])
lft__gt = content.ContentNode.objects.filter(parent=root)[24].rght
next__gt = content.ContentNode.objects.filter(parent=root)[24].rght
response = self.client.get(
reverse("kolibri:core:contentnode_tree-detail", kwargs={"pk": root.id}),
data={"lft__gt": lft__gt},
data={"next__gt": next__gt},
)
self.assertEqual(len(response.data["children"]["results"]), 5)
self.assertIsNone(response.data["children"]["more"])
Expand Down
2 changes: 1 addition & 1 deletion kolibri/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"hammerjs": "^2.0.8",
"intl": "^1.2.4",
"knuth-shuffle-seeded": "^1.0.6",
"kolibri-constants": "^0.1.32-beta1",
"kolibri-constants": "^0.1.34",
"kolibri-design-system": "git://github.com/learningequality/kolibri-design-system#632240f0b11f37c8b18de5ed930a4bf66b7c3678",
"lockr": "0.8.4",
"lodash": "^4.17.21",
Expand Down
6 changes: 5 additions & 1 deletion kolibri/core/webpack/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from django.conf import settings
from django.contrib.staticfiles.finders import find as find_staticfiles
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.serializers.json import DjangoJSONEncoder
from django.utils.functional import cached_property
from django.utils.safestring import mark_safe
from django.utils.six.moves.urllib.request import url2pathname
Expand Down Expand Up @@ -276,7 +277,10 @@ def plugin_data_tag(self):
bundle=self.unique_id,
plugin_data=json.dumps(
json.dumps(
self.plugin_data, separators=(",", ":"), ensure_ascii=False
self.plugin_data,
separators=(",", ":"),
ensure_ascii=False,
cls=DjangoJSONEncoder,
)
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

const MOCK_DEFAULTS = {
channels: [],
fetchChannels: jest.fn(),
channelsMap: {},
};

export function useChannelsMock(overrides = {}) {
Expand Down