From 7410f8c607d57a2f80a890ec1d2fc994bea45464 Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Thu, 1 Jan 2026 15:24:42 +0100 Subject: [PATCH] Search now also includes Files and URLs --- argocd/deployment.yaml | 2 +- boxes/tests.py | 90 ++++++++++++++++++++++++++++++++++++++++++ boxes/views.py | 22 ++++++++++- 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/argocd/deployment.yaml b/argocd/deployment.yaml index 2b346b3..b41ec8f 100644 --- a/argocd/deployment.yaml +++ b/argocd/deployment.yaml @@ -27,7 +27,7 @@ spec: mountPath: /data containers: - name: web - image: git.baumann.gr/adebaumann/labhelper:0.044 + image: git.baumann.gr/adebaumann/labhelper:0.045 imagePullPolicy: Always ports: - containerPort: 8000 diff --git a/boxes/tests.py b/boxes/tests.py index bfd9543..8b1b255 100644 --- a/boxes/tests.py +++ b/boxes/tests.py @@ -1610,3 +1610,93 @@ class ThingFileAndLinkCRUDTests(AuthTestCase): self.assertEqual(response.status_code, 200) self.assertContains(response, 'Upload File') self.assertContains(response, 'Add Link') + + def test_search_api_includes_files(self): + """Search API results should include files.""" + ThingFile.objects.create( + thing=self.thing, + title='Datasheet', + file='datasheets/test.pdf' + ) + response = self.client.get('/search/api/?q=ard') + self.assertEqual(response.status_code, 200) + results = response.json()['results'] + self.assertEqual(len(results), 1) + self.assertEqual(len(results[0]['files']), 1) + self.assertEqual(results[0]['files'][0]['title'], 'Datasheet') + self.assertIn('filename', results[0]['files'][0]) + + def test_search_api_includes_links(self): + """Search API results should include links.""" + ThingLink.objects.create( + thing=self.thing, + title='Documentation', + url='https://docs.example.com' + ) + response = self.client.get('/search/api/?q=ard') + self.assertEqual(response.status_code, 200) + results = response.json()['results'] + self.assertEqual(len(results), 1) + self.assertEqual(len(results[0]['links']), 1) + self.assertEqual(results[0]['links'][0]['title'], 'Documentation') + self.assertEqual(results[0]['links'][0]['url'], 'https://docs.example.com') + + def test_search_api_shows_empty_files_and_links(self): + """Search API should show empty arrays for things without files/links.""" + response = self.client.get('/search/api/?q=ard') + self.assertEqual(response.status_code, 200) + results = response.json()['results'] + self.assertEqual(len(results), 1) + self.assertEqual(results[0]['files'], []) + self.assertEqual(results[0]['links'], []) + + def test_search_api_searches_by_file_title(self): + """Search API should find things by file title.""" + ThingFile.objects.create( + thing=self.thing, + title='Datasheet PDF', + file='datasheets/test.pdf' + ) + response = self.client.get('/search/api/?q=Datasheet') + self.assertEqual(response.status_code, 200) + results = response.json()['results'] + self.assertEqual(len(results), 1) + self.assertEqual(results[0]['name'], self.thing.name) + + def test_search_api_searches_by_filename(self): + """Search API should find things by filename.""" + ThingFile.objects.create( + thing=self.thing, + title='Test File', + file='models/part123.stl' + ) + response = self.client.get('/search/api/?q=stl') + self.assertEqual(response.status_code, 200) + results = response.json()['results'] + self.assertEqual(len(results), 1) + self.assertEqual(results[0]['name'], self.thing.name) + + def test_search_api_searches_by_link_title(self): + """Search API should find things by link title.""" + ThingLink.objects.create( + thing=self.thing, + title='Documentation Link', + url='https://docs.example.com/manual.pdf' + ) + response = self.client.get('/search/api/?q=Documentation') + self.assertEqual(response.status_code, 200) + results = response.json()['results'] + self.assertEqual(len(results), 1) + self.assertEqual(results[0]['name'], self.thing.name) + + def test_search_api_searches_by_link_url(self): + """Search API should find things by link URL.""" + ThingLink.objects.create( + thing=self.thing, + title='Manual', + url='https://arduino.cc/products/uno' + ) + response = self.client.get('/search/api/?q=arduino') + self.assertEqual(response.status_code, 200) + results = response.json()['results'] + self.assertGreater(len(results), 0) diff --git a/boxes/views.py b/boxes/views.py index 8e6a225..40a61e6 100644 --- a/boxes/views.py +++ b/boxes/views.py @@ -143,8 +143,12 @@ def search_api(request): things = Thing.objects.filter( Q(name__icontains=query) | Q(description__icontains=query) | - Q(thing_type__name__icontains=query) - ).select_related('thing_type', 'box')[:50] + Q(thing_type__name__icontains=query) | + Q(files__title__icontains=query) | + Q(files__file__icontains=query) | + Q(links__title__icontains=query) | + Q(links__url__icontains=query) + ).prefetch_related('files', 'links').select_related('thing_type', 'box').distinct()[:50] results = [ { @@ -153,6 +157,20 @@ def search_api(request): 'type': thing.thing_type.name, 'box': thing.box.id, 'description': thing.description[:100] if thing.description else '', + 'files': [ + { + 'title': f.title, + 'filename': f.filename(), + } + for f in thing.files.all() + ], + 'links': [ + { + 'title': l.title, + 'url': l.url, + } + for l in thing.links.all() + ], } for thing in things ]