Skip to content

Commit

Permalink
4.1.0. Cleaner markup, test urls, logging level, keyboard affordances…
Browse files Browse the repository at this point in the history
… for assistant
  • Loading branch information
chrisclark committed Apr 23, 2024
1 parent 86bc056 commit 7861ad7
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 76 deletions.
10 changes: 6 additions & 4 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ Change Log
This document records all notable changes to `django-sql-explorer <https://github.com/chrisclark/django-sql-explorer>`_.
This project adheres to `Semantic Versioning <https://semver.org/>`_.

`4.1.0b2`_ (2024-04-17)
`4.1.0`_ (2024-04-23)
===========================
* SQL Assistant: Built in query help via OpenAI (or LLM of choice), with relevant schema
automatically injected into the prompt. Enable via setting EXPLORER_AI_API_KEY.
* Anonymous usage telemetry. Can be disabled by setting EXPLORER_ENABLE_ANONYMOUS_STATS to False
* `#594`_: Eliminate <script> tags to prevent potential Content Security Policy issues
automatically injected into the prompt. Enable by setting EXPLORER_AI_API_KEY.
* Anonymous usage telemetry. Disable by setting EXPLORER_ENABLE_ANONYMOUS_STATS to False.
* Refactor pip requirements to make 'extras' more robust and easier to manage.
* `#592`_: Support user models with no email fields
* `#594`_: Eliminate <script> tags to prevent potential Content Security Policy issues.

`4.0.2`_ (2024-02-06)
===========================
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ create a new virtualenv, cd into ``test_project`` and run ``start.sh`` (or
walk through the steps yourself) to get a test instance of the app up
and running.

You can now navigate to 127.0.0.1:8000/ and begin exploring!
You can now navigate to 127.0.0.1:8000/explorer/ and begin exploring!
2 changes: 1 addition & 1 deletion docs/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ create a new virtualenv, cd into ``test_project`` and run ``start.sh`` (or
walk through the steps yourself) to get a test instance of the app up
and running.

You can now navigate to 127.0.0.1:8000/ and begin exploring!
You can now navigate to 127.0.0.1:8000/explorer/ and begin exploring!

Installing From Source
----------------------
Expand Down
4 changes: 2 additions & 2 deletions explorer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"major": 4,
"minor": 1,
"patch": 0,
"releaselevel": "beta",
"serial": 5
"releaselevel": "final",
"serial": 0
}


Expand Down
100 changes: 54 additions & 46 deletions explorer/src/js/assistant.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import * as bootstrap from 'bootstrap';
import $ from "jquery";
import List from "list.js";

const spinner = "<div class=\"spinner-border text-primary\" role=\"status\"><span class=\"visually-hidden\">Loading...</span></div>";

function getErrorMessage() {
const errorElement = document.querySelector('.alert-danger.db-error');
return errorElement ? errorElement.textContent.trim() : null;
}

function assistantSearchFocus() {
document.getElementById("search_assistant_tables").focus();
}

export function setUpAssistant(expand = false) {

const error = getErrorMessage();
Expand All @@ -23,12 +25,7 @@ export function setUpAssistant(expand = false) {
});
bsCollapse.show();
if(error) {
const textarea = document.getElementById('id_assistant_input');
textarea.value = "Please help me fix the error(s) in this query.";
const newDiv = document.createElement('div');
newDiv.textContent = 'Error messages are automatically included in the prompt. Just hit "Ask Assistant" and all relevant context will be injected to the LLM request.'; // Add any text or HTML content
newDiv.className = 'text-secondary small';
textarea.parentNode.insertBefore(newDiv, textarea.nextSibling);
document.getElementById('id_error_help_message').classList.remove('d-none');
}
}

Expand Down Expand Up @@ -83,6 +80,7 @@ export function setUpAssistant(expand = false) {
additionalTableContainer.classList.remove('d-none');
assistantInputWrapper.classList.remove('col-12');
assistantInputWrapper.classList.add('col-9');
assistantSearchFocus();
} else {
additionalTableContainer.classList.add('d-none');
assistantInputWrapper.classList.remove('col-9');
Expand All @@ -94,46 +92,56 @@ export function setUpAssistant(expand = false) {
});
showHideExtraTables(checkbox.checked);

document.getElementById('ask_assistant_btn').addEventListener('click', function() {

const selectedTables = Array.from(
document.querySelectorAll('.table-checkbox:checked')
).map(cb => cb.value);
document.getElementById('id_assistant_input').addEventListener('keydown', function(event) {
if ((event.ctrlKey || event.metaKey) && (event.key === 'Enter')) {
event.preventDefault();
submitAssistantAsk();
}
});

const data = {
sql: window.editor?.state.doc.toString() ?? null,
connection: document.getElementById("id_connection")?.value ?? null,
assistant_request: document.getElementById("id_assistant_input")?.value ?? null,
selected_tables: selectedTables,
db_error: getErrorMessage()
};
document.getElementById('ask_assistant_btn').addEventListener('click', submitAssistantAsk);
}

document.getElementById("response_block").style.display = "block";
document.getElementById("assistant_response").innerHTML = spinner;

fetch('../assistant/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
const output = DOMPurify.sanitize(marked.parse(data.message));
document.getElementById("response_block").style.display = "block";
document.getElementById("assistant_response").innerHTML = output;
setUpCopyButtons();
})
.catch(error => {
console.error('Error:', error);
});
function submitAssistantAsk() {

const selectedTables = Array.from(
document.querySelectorAll('.table-checkbox:checked')
).map(cb => cb.value);

const data = {
sql: window.editor?.state.doc.toString() ?? null,
connection: document.getElementById("id_connection")?.value ?? null,
assistant_request: document.getElementById("id_assistant_input")?.value ?? null,
selected_tables: selectedTables,
db_error: getErrorMessage()
};

document.getElementById("assistant_response").innerHTML = '';
document.getElementById("response_block").classList.remove('d-none');
document.getElementById("assistant_spinner").classList.remove('d-none');

fetch('../assistant/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
const output = DOMPurify.sanitize(marked.parse(data.message));
document.getElementById("assistant_response").innerHTML = output;
document.getElementById("assistant_spinner").classList.add('d-none');
setUpCopyButtons();
})
.catch(error => {
console.error('Error:', error);
});
}

Expand Down
1 change: 0 additions & 1 deletion explorer/src/js/explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export class ExplorerEditor {
}

this.queryId = queryId;
this.$table = $("#preview");
this.$rows = $("#rows");
this.$form = $("form");
this.$snapshotField = $("#id_snapshot");
Expand Down
18 changes: 13 additions & 5 deletions explorer/templates/explorer/assistant.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
<div class="row assistant_input_parent">
<div class="mt-3 col-12" id="assistant_input_wrapper">
<textarea
class="form-control" id="id_assistant_input"
class="form-control mb-4" id="id_assistant_input"
name="sql_assistant" rows="3" placeholder="What do you need help with?"></textarea>
<label for="id_assistant_input" class="form-label" id="id_assistant_input_label"></label>
<label for="id_assistant_input" class="form-label d-none" id="id_assistant_input_label">Assistant prompt</label>
<div id="id_error_help_message" class="d-none text-secondary small">
Hit "Ask Assistant" to try and fix the issue. The assistant is automatically aware of error messages & context.
</div>
</div>
<div id="additional_table_container" class="d-none col-3">
<input id="search_assistant_tables" class="search" placeholder="{% trans "Search Tables" %}" />
Expand All @@ -29,7 +32,7 @@
<input type="checkbox" class="form-check-input" name="assistant-include-other-tables" id="include_other_tables" autocomplete="off">
<label class="form-check-label" for="include_other_tables">Include Other Tables (<a class="small text-secondary" style="cursor: pointer;"
data-bs-toggle="tooltip" data-bs-placement="top"
data-bs-title="SQL Assistant builds a prompt with your query, your request, relevant schema (tables referenced in your query) and sample data from those tables. You can optionally include other tables (schema + data sample) that you'd like SQL Assistant to know about.">What is this?</a>)</label>
data-bs-title="SQL Assistant builds a prompt with your query, your request, relevant schema (tables referenced in your query) and sample data from those tables. You can optionally include other tables (schema + data sample) that you'd like SQL Assistant to know about.">?</a>)</label>
</div>
</div>
<div class="col-6 text-end">
Expand All @@ -40,8 +43,13 @@
</div>


<div id="response_block" style="display: none" class="position-relative">
<div class="mt-3 p-2 pt-3 rounded-2 border bg-light" id="assistant_response"></div>
<div id="response_block" class="position-relative d-none">
<div class="mt-3 p-2 pt-4 rounded-2 border bg-light">
<div id="assistant_response"></div>
<p class="spinner-border text-primary d-none" id="assistant_spinner" role="status">
<span class="visually-hidden">Loading...</span>
</p>
</div>
</div>
</div>
</div>
Expand Down
14 changes: 0 additions & 14 deletions explorer/tests/urls.py

This file was deleted.

5 changes: 4 additions & 1 deletion explorer/tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from django.db.migrations.recorder import MigrationRecorder
from django.conf import settings

import explorer

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -84,7 +86,7 @@ def _send(data):
data=data,
headers={"Content-Type": "application/json"})
except Exception as e:
logger.exception("Failed to send stats: %s" % e)
logger.warning("Failed to send stats: %s" % e)


def gather_summary_stats():
Expand Down Expand Up @@ -117,6 +119,7 @@ def gather_summary_stats():
"unsafe_rendering": app_settings.UNSAFE_RENDERING,
"transform_count": len(app_settings.EXPLORER_TRANSFORMS),
"assistant_enabled": app_settings.EXPLORER_AI_API_KEY is not None,
"version": explorer.get_version()
}
except Exception as e:
return {"error": "error gathering stats: %s" % e}
2 changes: 1 addition & 1 deletion test_project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
}
EXPLORER_DEFAULT_CONNECTION = "default"

ROOT_URLCONF = "explorer.tests.urls"
ROOT_URLCONF = "test_project.urls"

TEMPLATES = [
{
Expand Down
17 changes: 17 additions & 0 deletions test_project/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path, include

# Installing to /explorer/ better mimics likely production setups
# Explorer is probably *not* running at the Django project root
urlpatterns = [
path("explorer/", include("explorer.urls"))
]

admin.autodiscover()

urlpatterns += [
path("admin/", admin.site.urls),
]

urlpatterns += staticfiles_urlpatterns()

0 comments on commit 7861ad7

Please sign in to comment.