Some bugs (box-management didn't work); Tags now on search and in box content
All checks were successful
Build containers when image tags change / build-if-image-changed (., web, containers, main container, git.baumann.gr/adebaumann/labhelper) (push) Successful in 1m3s
Build containers when image tags change / build-if-image-changed (data-loader, loader, initContainers, init-container, git.baumann.gr/adebaumann/labhelper-data-loader) (push) Successful in 12s
All checks were successful
Build containers when image tags change / build-if-image-changed (., web, containers, main container, git.baumann.gr/adebaumann/labhelper) (push) Successful in 1m3s
Build containers when image tags change / build-if-image-changed (data-loader, loader, initContainers, init-container, git.baumann.gr/adebaumann/labhelper-data-loader) (push) Successful in 12s
This commit is contained in:
@@ -18,7 +18,7 @@ spec:
|
||||
fsGroupChangePolicy: "OnRootMismatch"
|
||||
initContainers:
|
||||
- name: loader
|
||||
image: git.baumann.gr/adebaumann/labhelper-data-loader:0.013
|
||||
image: git.baumann.gr/adebaumann/labhelper-data-loader:0.014
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
command: [ "sh","-c","if [ ! -f /data/db.sqlite3 ] || [ ! -s /data/db.sqlite3 ]; then cp preload/preload.sqlite3 /data/db.sqlite3 && echo 'Database copied from preload'; else echo 'Existing database preserved'; fi" ]
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
mountPath: /data
|
||||
containers:
|
||||
- name: web
|
||||
image: git.baumann.gr/adebaumann/labhelper:0.048
|
||||
image: git.baumann.gr/adebaumann/labhelper:0.049
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<tr style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Picture</th>
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Name</th>
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Tags</th>
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -55,6 +56,15 @@
|
||||
<td style="padding: 15px 20px;">
|
||||
<a href="{% url 'thing_detail' thing.id %}" style="color: #667eea; text-decoration: none; font-weight: 500;">{{ thing.name }}</a>
|
||||
</td>
|
||||
<td style="padding: 15px 20px;">
|
||||
{% if thing.tags.all %}
|
||||
{% for tag in thing.tags.all %}
|
||||
<span style="display: inline-block; padding: 3px 8px; margin: 2px; border-radius: 12px; font-size: 11px; background: {{ tag.facet.color }}20; color: {{ tag.facet.color }}; border: 1px solid {{ tag.facet.color }}40;">{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<span style="color: #999; font-style: italic; font-size: 13px;">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="padding: 15px 20px; color: #777;">{{ thing.description|default:"-" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<thead>
|
||||
<tr style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Name</th>
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Type</th>
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Tags</th>
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Box</th>
|
||||
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Description</th>
|
||||
</tr>
|
||||
@@ -85,9 +85,16 @@ function performSearch(query) {
|
||||
const row = document.createElement('tr');
|
||||
row.style.borderBottom = '1px solid #e0e0e0';
|
||||
row.style.transition = 'background 0.2s';
|
||||
|
||||
let tagsHtml = thing.tags.length > 0
|
||||
? thing.tags.map(tag =>
|
||||
'<span style="display: inline-block; padding: 3px 8px; margin: 2px; border-radius: 12px; font-size: 11px; background: ' + escapeHtml(tag.color) + '20; color: ' + escapeHtml(tag.color) + '; border: 1px solid ' + escapeHtml(tag.color) + '40;">' + escapeHtml(tag.name) + '</span>'
|
||||
).join('')
|
||||
: '<span style="color: #999; font-style: italic; font-size: 13px;">-</span>';
|
||||
|
||||
row.innerHTML =
|
||||
'<td style="padding: 15px 20px;"><a href="/thing/' + thing.id + '/">' + escapeHtml(thing.name) + '</a></td>' +
|
||||
'<td style="padding: 15px 20px; color: #555;">' + escapeHtml(thing.type) + '</td>' +
|
||||
'<td style="padding: 15px 20px;">' + tagsHtml + '</td>' +
|
||||
'<td style="padding: 15px 20px;"><a href="/box/' + escapeHtml(thing.box) + '/">' + escapeHtml(thing.box) + '</a></td>' +
|
||||
'<td style="padding: 15px 20px; color: #777;" class="description">' + escapeHtml(thing.description) + '</td>';
|
||||
|
||||
|
||||
@@ -706,12 +706,12 @@ class SearchApiTests(AuthTestCase):
|
||||
results = response.json()['results']
|
||||
self.assertEqual(len(results), 50)
|
||||
|
||||
def test_search_api_includes_type_and_box(self):
|
||||
"""Search API results should include type and box info."""
|
||||
def test_search_api_includes_tags_and_box(self):
|
||||
"""Search API results should include tags and box info."""
|
||||
response = self.client.get('/search/api/?q=ard')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
results = response.json()['results']
|
||||
self.assertEqual(results[0]['type'], 'Electronics')
|
||||
self.assertEqual(results[0]['tags'], [])
|
||||
self.assertEqual(results[0]['box'], 'BOX001')
|
||||
|
||||
def test_search_api_requires_login(self):
|
||||
|
||||
@@ -40,7 +40,7 @@ def index(request):
|
||||
def box_detail(request, box_id):
|
||||
"""Display contents of a box."""
|
||||
box = get_object_or_404(Box, pk=box_id)
|
||||
things = box.things.all()
|
||||
things = box.things.prefetch_related('tags').all()
|
||||
return render(request, 'boxes/box_detail.html', {
|
||||
'box': box,
|
||||
'things': things,
|
||||
@@ -179,7 +179,7 @@ def search_api(request):
|
||||
things = Thing.objects.filter(
|
||||
Q(tags__facet__name__icontains=facet_name) &
|
||||
Q(tags__name__icontains=tag_name)
|
||||
).prefetch_related('files', 'links').select_related('box').distinct()[:50]
|
||||
).prefetch_related('files', 'links', 'tags').select_related('box').distinct()[:50]
|
||||
else:
|
||||
# Normal search
|
||||
things = Thing.objects.filter(
|
||||
@@ -191,7 +191,7 @@ def search_api(request):
|
||||
Q(links__url__icontains=query) |
|
||||
Q(tags__name__icontains=query) |
|
||||
Q(tags__facet__name__icontains=query)
|
||||
).prefetch_related('files', 'links').select_related('box').distinct()[:50]
|
||||
).prefetch_related('files', 'links', 'tags').select_related('box').distinct()[:50]
|
||||
|
||||
results = [
|
||||
{
|
||||
@@ -199,6 +199,13 @@ def search_api(request):
|
||||
'name': thing.name,
|
||||
'box': thing.box.id,
|
||||
'description': thing.description[:100] if thing.description else '',
|
||||
'tags': [
|
||||
{
|
||||
'name': tag.name,
|
||||
'color': tag.facet.color,
|
||||
}
|
||||
for tag in thing.tags.all()
|
||||
],
|
||||
'files': [
|
||||
{
|
||||
'title': f.title,
|
||||
|
||||
Binary file not shown.
@@ -22,11 +22,14 @@ from django.contrib.auth import views as auth_views
|
||||
|
||||
from boxes.views import (
|
||||
add_box,
|
||||
add_box_type,
|
||||
add_things,
|
||||
box_detail,
|
||||
box_management,
|
||||
delete_box,
|
||||
delete_box_type,
|
||||
edit_box,
|
||||
edit_box_type,
|
||||
index,
|
||||
search,
|
||||
search_api,
|
||||
@@ -38,6 +41,9 @@ urlpatterns = [
|
||||
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
||||
path('', index, name='index'),
|
||||
path('box-management/', box_management, name='box_management'),
|
||||
path('box-type/add/', add_box_type, name='add_box_type'),
|
||||
path('box-type/<int:type_id>/edit/', edit_box_type, name='edit_box_type'),
|
||||
path('box-type/<int:type_id>/delete/', delete_box_type, name='delete_box_type'),
|
||||
path('box/add/', add_box, name='add_box'),
|
||||
path('box/<str:box_id>/edit/', edit_box, name='edit_box'),
|
||||
path('box/<str:box_id>/delete/', delete_box, name='delete_box'),
|
||||
|
||||
Reference in New Issue
Block a user