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 history new feature #23

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions djangoql/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.contrib import messages
from django.contrib.admin.views.main import ChangeList
from django.core.exceptions import FieldError, ValidationError
from django.http.response import JsonResponse
from django.forms import Media
from django.http import HttpResponse
from django.views.generic import TemplateView
Expand All @@ -12,6 +13,9 @@
from .exceptions import DjangoQLError
from .queryset import apply_search
from .schema import DjangoQLSchema
from .models import history

import sys


DJANGOQL_SEARCH_MARKER = 'q-l'
Expand Down Expand Up @@ -57,10 +61,9 @@ def get_search_results(self, request, queryset, search_term):
if not search_term:
return queryset, use_distinct
try:
return (
apply_search(queryset, search_term, self.djangoql_schema),
use_distinct,
)
qs = apply_search(queryset, search_term, self.djangoql_schema)
self.new_history_item(request.user, search_term)
return ( qs, use_distinct, )
except (DjangoQLError, ValueError, FieldError) as e:
msg = text_type(e)
except ValidationError as e:
Expand Down Expand Up @@ -101,6 +104,11 @@ def get_urls(self):
self.model._meta.model_name,
),
),
url(
r'^djangoql-history/$',
self.admin_site.admin_view(self.get_history),
name='history',
),
url(
r'^djangoql-syntax/$',
TemplateView.as_view(
Expand All @@ -117,3 +125,20 @@ def introspect(self, request):
content=json.dumps(response, indent=2),
content_type='application/json; charset=utf-8',
)

def new_history_item(self,user,search_term):
history_item = history()
history_item.user = user
history_item.query = search_term
history_item.save()

def get_history(self, request):
reverse_history_qs = history.objects.filter(user=request.user)
history_qs = reverse_history_qs.order_by('-pk')
lastQueries = []
for i,q in enumerate(history_qs):
if i < 200: #limit history to 200 items per user
lastQueries.append(q.query)
else:
q.delete()
return JsonResponse({'history':lastQueries})
25 changes: 25 additions & 0 deletions djangoql/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.0.6 on 2018-06-15 20:22

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='history',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('query', models.CharField(max_length=2000)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
Empty file added djangoql/migrations/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions djangoql/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.db import models
from django.contrib.auth.models import User

class history(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
query = models.CharField(max_length=2000)
175 changes: 164 additions & 11 deletions djangoql/static/djangoql/js/completion.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
return {
currentModel: null,
models: {},
currentHistory: null,
historyEnabled: false,

token: token,
lexer: lexer,
Expand All @@ -130,13 +132,18 @@

init: function (options) {
var syntaxHelp;
var history;
var history_enabled = false;

// Initialization
if (!this.isObject(options)) {
this.logError('Please pass an object with initialization parameters');
return;
}

this.loadIntrospections(options.introspections);
this.loadHistory(options.history)

this.textarea = document.querySelector(options.selector);
if (!this.textarea) {
this.logError('Element not found by selector: ' + options.selector);
Expand Down Expand Up @@ -199,6 +206,17 @@
document.querySelector('body').appendChild(this.completion);
this.completionUL = document.createElement('ul');
this.completion.appendChild(this.completionUL);

history = document.createElement('p');
history.className = 'syntax-help';
history.innerHTML = '<a id="history_enabler" href="">Search History</a>';
history.addEventListener('mousedown', function (e) {
this.historyEnabled = true;
this.loadHistory(options.history);
this.renderHistory();
}.bind(this));
this.completion.appendChild(history);

if (typeof options.syntaxHelp === 'string') {
syntaxHelp = document.createElement('p');
syntaxHelp.className = 'syntax-help';
Expand All @@ -223,6 +241,35 @@
this.hideCompletion();
},

loadHistory: function (history) {
var onLoadError;
var request;

onLoadError = function (history) {
this.logError('failed to load history from' + history);
}.bind(this);
request = new XMLHttpRequest();
request.open('GET', history, true);
request.onload = function () {
var data;
if (request.status === 200) {
data = JSON.parse(request.responseText);
this.currentHistory = data.history;
} else {
onLoadError();
}
}.bind(this);
request.ontimeout = onLoadError;
request.onerror = onLoadError;
/* eslint-disable max-len */
// Workaround for IE9, see
// https://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/
/* eslint-enable max-len */
request.onprogress = function () {};
window.setTimeout(request.send.bind(request));

},

loadIntrospections: function (introspections) {
var onLoadError;
var request;
Expand Down Expand Up @@ -318,7 +365,12 @@
},

onCompletionMouseClick: function (e) {
this.selectCompletion(parseInt(e.target.getAttribute('data-index'), 10));
if (!this.historyEnabled) {
this.selectCompletion(parseInt(e.target.getAttribute('data-index'), 10));
} else {
this.textarea.value = e.target.textContent;
document.getElementById('changelist-search').submit();
}
},

onCompletionMouseDown: function (e) {
Expand All @@ -327,16 +379,21 @@
},

onCompletionMouseOut: function () {
this.selected = null;
this.debouncedRenderCompletion();
if (!this.historyEnabled) {
this.selected = null;
this.debouncedRenderCompletion();
}
},

onCompletionMouseOver: function (e) {
this.selected = parseInt(e.target.getAttribute('data-index'), 10);
this.debouncedRenderCompletion();
if (!this.historyEnabled) {
this.selected = parseInt(e.target.getAttribute('data-index'), 10);
this.debouncedRenderCompletion();
}
},

onKeydown: function (e) {

switch (e.keyCode) {
case 38: // up arrow
if (this.suggestions.length) {
Expand Down Expand Up @@ -419,8 +476,10 @@
},

popupCompletion: function () {
this.generateSuggestions();
this.renderCompletion();
if (!this.historyEnabled) {
this.generateSuggestions();
this.renderCompletion();
}
},

selectCompletion: function (index) {
Expand All @@ -445,13 +504,17 @@
this.textareaResize();
}
this.generateSuggestions(this.textarea);
this.renderCompletion();
if (!this.historyEnabled) {
this.renderCompletion();
}
},

hideCompletion: function () {
this.selected = null;
if (this.completion) {
this.completion.style.display = 'none';
if (!this.historyEnabled) {
this.selected = null;
if (this.completion) {
this.completion.style.display = 'none';
}
}
},

Expand All @@ -472,6 +535,80 @@
'<b>$1</b>');
},

renderHistory: function (dontForceDisplay) {
var currentLi;
var i;
var completionRect;
var currentLiRect;
var inputRect;
var li;
var liLen;
var historyLen;

if (!this.completionEnabled) {
this.hideCompletion();
return;
}

if (dontForceDisplay && this.completion.style.display === 'none') {
return;
}
if (!this.currentHistory.length) {
this.hideCompletion();
return;
}

historyLen = this.currentHistory.length;
li = [].slice.call(this.completionUL.querySelectorAll('li'));
liLen = li.length;

// Update or create necessary elements
for (i = 0; i < historyLen; i++) {
if (i < liLen) {
currentLi = li[i];
} else {
currentLi = document.createElement('li');
currentLi.setAttribute('data-index', i);
this.completionUL.appendChild(currentLi);
currentLi.addEventListener('click', this.onCompletionMouseClick);
currentLi.addEventListener('mousedown', this.onCompletionMouseDown);
currentLi.addEventListener('mouseout', this.onCompletionMouseOut);
currentLi.addEventListener('mouseover', this.onCompletionMouseOver);
}
currentLi.textContent = this.currentHistory[i];

if (this.currentHistory[i] == this.selected) {
currentLi.className = 'active';
currentLiRect = currentLi.getBoundingClientRect();
completionRect = this.completionUL.getBoundingClientRect();
if (currentLiRect.bottom > completionRect.bottom) {
this.completionUL.scrollTop = this.completionUL.scrollTop + 2 +
(currentLiRect.bottom - completionRect.bottom);
} else if (currentLiRect.top < completionRect.top) {
this.completionUL.scrollTop = this.completionUL.scrollTop - 2 -
(completionRect.top - currentLiRect.top);
}
} else {
currentLi.className = '';
}
}
// Remove redundant elements
while (liLen > historyLen) {
liLen--;
li[liLen].removeEventListener('click', this.onCompletionMouseClick);
li[liLen].removeEventListener('mousedown', this.onCompletionMouseDown);
li[liLen].removeEventListener('mouseout', this.onCompletionMouseOut);
li[liLen].removeEventListener('mouseover', this.onCompletionMouseOver);
this.completionUL.removeChild(li[liLen]);
}

inputRect = this.textarea.getBoundingClientRect();
this.completion.style.top = window.pageYOffset + inputRect.top +
inputRect.height + 'px';
this.completion.style.left = inputRect.left + 'px';
this.completion.style.display = 'block';
},

renderCompletion: function (dontForceDisplay) {
var currentLi;
var i;
Expand All @@ -482,6 +619,11 @@
var liLen;
var suggestionsLen;

if (this.historyEnabled) {
this.historyEnabled = false;
return
}

if (!this.completionEnabled) {
this.hideCompletion();
return;
Expand All @@ -495,6 +637,10 @@
return;
}

if (!this.currentHistory) {
return;
}

suggestionsLen = this.suggestions.length;
li = [].slice.call(this.completionUL.querySelectorAll('li'));
liLen = li.length;
Expand Down Expand Up @@ -669,6 +815,7 @@
return { prefix: prefix, scope: scope, model: model, field: field };
},


generateSuggestions: function () {
var input = this.textarea;
var context;
Expand All @@ -681,6 +828,12 @@
var textBefore;
var textAfter;


if (this.historyEnabled) {
this.historyEnabled = false;
return
}

if (!this.completionEnabled) {
this.prefix = '';
this.suggestions = [];
Expand Down
Loading