Initial web UI, smart search, book import command
This commit is contained in:
parent
9ac7a56135
commit
b653ec8e25
|
@ -37,6 +37,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.postgres',
|
||||
|
||||
'django_hstore',
|
||||
'tree',
|
||||
|
@ -59,7 +60,7 @@ ROOT_URLCONF = 'spejstore.urls'
|
|||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'DIRS': ['templates/'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
|
@ -126,3 +127,7 @@ USE_TZ = True
|
|||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, "static"),
|
||||
]
|
||||
|
|
|
@ -2,20 +2,15 @@
|
|||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/1.10/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.conf.urls import url, include
|
||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls import url
|
||||
from django.conf.urls import url, include
|
||||
from django.contrib import admin
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
]
|
||||
|
||||
url(r'^', include('storage.urls')),
|
||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
from django.core.management.base import BaseCommand, CommandError
|
||||
from storage.models import Item
|
||||
from io import StringIO
|
||||
import csv
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Imports book library from specified wiki page dump'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('parent')
|
||||
parser.add_argument('file')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
with open(options['file']) as fd:
|
||||
sio = StringIO(fd.read())
|
||||
reader = csv.reader(sio, delimiter='|')
|
||||
parent = Item.objects.get(pk=options['parent'])
|
||||
for line in reader:
|
||||
line = list(map(str.strip, line))
|
||||
item = Item(parent=parent)
|
||||
item.name = line[2]
|
||||
item.props['author'] = line[1]
|
||||
item.props['owner'] = line[3]
|
||||
item.props['can_borrow'] = line[4]
|
||||
item.props['borrowed_by'] = line[5]
|
||||
item.save()
|
||||
|
||||
self.stdout.write(self.style.NOTICE('Book added: %r') % item)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Successfully imported data'))
|
|
@ -46,7 +46,11 @@ class Item(models.Model, TreeModelMixin):
|
|||
objects = hstore.HStoreManager()
|
||||
|
||||
def __str__(self):
|
||||
return '-' * (self.get_level() or 0) + ' ' +self.name
|
||||
return '- ' * (self.get_level() or 0) + self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
from django.urls import reverse
|
||||
return reverse('item-display', kwargs={'pk': str(self.pk)})
|
||||
|
||||
class Meta:
|
||||
ordering = ('path',)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<form action="/search">
|
||||
<div class="input-group input-group-lg">
|
||||
<input type="text" class="form-control" name="q" placeholder="search term">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit"><i class="glyphicon glyphicon-search"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<input type="checkbox" id="smartsearch" name="smartsearch">
|
||||
<label for="smartsearch">SmartSearch</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="/item/">Everything</a></li>
|
||||
{% for ancestor in ancestors %}
|
||||
<li><a href="{{ ancestor.get_absolute_url }}">{{ ancestor.name }}</a></li>
|
||||
{% endfor %}
|
||||
<li class="active">{{ item.name }}</li>
|
||||
</ol>
|
||||
<h2>{{ item.name }} <small>{{ item.pk }}</small></h2>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
{% if item.props %}
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>key</th>
|
||||
<th>value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for k, v in item.props.items %}
|
||||
<tr><td>{{ k }}</td><td>{{ v }}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if children %}
|
||||
<h3>Children</h3>
|
||||
<table class="table table-striped table-hover">
|
||||
{% for child in children %}
|
||||
<tr><td><a href="{{ child.get_absolute_url }}">{{ child.name }}</a></td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<table class="table table-striped table-hover">
|
||||
{% for item in results %}
|
||||
<tr>
|
||||
<td>
|
||||
{% for parent in item.get_ancestors %}
|
||||
{{ parent.name }} »
|
||||
{% endfor %}
|
||||
<a href="{{ item.get_absolute_url }}">{{ item.name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
from django.conf.urls import include, url
|
||||
from storage.views import index, search, item_display
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', index),
|
||||
url(r'^search$', search),
|
||||
url(r'^item/(?P<pk>.*)$', item_display, name='item-display'),
|
||||
]
|
|
@ -1,3 +1,64 @@
|
|||
from django.shortcuts import render
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from storage.models import Item
|
||||
from django.contrib.postgres.search import SearchVector
|
||||
import shlex
|
||||
|
||||
# Create your views here.
|
||||
def apply_smart_search(query, objects):
|
||||
general_term = []
|
||||
|
||||
filters = {}
|
||||
|
||||
for prop in shlex.split(query):
|
||||
if ':' not in prop:
|
||||
general_term.append(prop)
|
||||
else:
|
||||
key, value = prop.split(':', 1)
|
||||
if hasattr(Item, key):
|
||||
filters[key + '__search'] = value
|
||||
|
||||
elif key == 'prop' or value:
|
||||
if key == 'prop':
|
||||
key, value = value.split(':', 1)
|
||||
|
||||
if 'props__contains' not in filters:
|
||||
filters['props__contains'] = {}
|
||||
filters['props__contains'] = {key: value}
|
||||
|
||||
else:
|
||||
# "Whatever:"
|
||||
general_term.append(prop)
|
||||
|
||||
if general_term:
|
||||
objects = objects.annotate(
|
||||
search=SearchVector('name', 'description', 'props'),
|
||||
)
|
||||
filters['search'] = ' '.join(general_term)
|
||||
|
||||
objects = objects.filter(**filters)
|
||||
|
||||
return objects
|
||||
|
||||
def index(request):
|
||||
return render(request, 'index.html')
|
||||
|
||||
def search(request):
|
||||
query = request.GET.get('q', '')
|
||||
|
||||
results = apply_smart_search(query, Item.objects)
|
||||
return render(request, 'results.html', {
|
||||
'query': query,
|
||||
'results': results.all(),
|
||||
})
|
||||
|
||||
def item_display(request, pk):
|
||||
if not pk:
|
||||
return render(request, 'results.html', {
|
||||
'results': Item.get_roots()
|
||||
})
|
||||
item = get_object_or_404(Item, pk=pk)
|
||||
|
||||
return render(request, 'item.html', {
|
||||
'item': item,
|
||||
'ancestors': item.get_ancestors(),
|
||||
'children': item.get_children(),
|
||||
})
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hackerspace Storage System</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}" media="screen">
|
||||
<style>
|
||||
.btn {
|
||||
font-size: 1.4em;
|
||||
text-transform: none;
|
||||
margin-bottom: 10px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.btn .glyphicon {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.btn-alt {
|
||||
background: #4b176d;
|
||||
color:white;
|
||||
border-color: #2d0d42; /*#301934;*/
|
||||
}
|
||||
.btn-alt:hover, .btn-alt:active, .btn-alt:link, .btn-alt:visited {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<h1 class="page-header">Warsaw Hackerspace <small class="hidden-sm hidden-xs">Enjoy your stay</small></h1>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue