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

Adding SQLite Full text search table of scripts and contents" #339

Merged
merged 4 commits into from
Feb 28, 2025
Merged
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ environment.json

shell-profile

hooks.db
*.db

requirements-pwrhook.txt

63 changes: 63 additions & 0 deletions powerindex/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import string
import re
import subprocess
import sqlite3
from flask import Flask, render_template, request, jsonify
from pygments import highlight
from pygments.lexers import BashLexer, JsonLexer
Expand Down Expand Up @@ -35,6 +36,44 @@ def update_curl_flags():
# Run the update immediately
update_curl_flags()

# Initialize the database
def init_db():
db_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'the-power.db')
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Create FTS5 table
cursor.execute('''
CREATE VIRTUAL TABLE IF NOT EXISTS scripts_fts USING fts5(
script,
body
)
''')

# Clear existing entries
cursor.execute('DELETE FROM scripts_fts')

# Load all scripts into the database
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
shell_scripts = glob.glob(os.path.join(parent_dir, '*.sh'))

for script_path in shell_scripts:
script_name = os.path.basename(script_path)
try:
with open(script_path, 'r') as file:
content = file.read()
cursor.execute('INSERT INTO scripts_fts (script, body) VALUES (?, ?)',
(script_name, content))
except Exception as e:
print(f"Error loading script {script_name}: {str(e)}")

conn.commit()
conn.close()
print(f"Database initialized at {db_path}")

# Add database initialization before Flask app creation
init_db()

app = Flask(__name__)

# Add this at the top level for use in the template
Expand Down Expand Up @@ -232,5 +271,29 @@ def highlight_search(text, search):
pattern = re.compile(f'({re.escape(search)})', re.IGNORECASE)
return pattern.sub(r'<span class="highlight">\1</span>', text)

@app.route('/search', methods=['GET'])
def search():
query = request.args.get('q', '')
results = []

if query:
# Use the same database path as init_db()
db_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'the-power.db')
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Use FTS5 match to find results and highlight matching text
cursor.execute('''
SELECT script, snippet(scripts_fts, 1, '<mark>', '</mark>', '...', 50)
FROM scripts_fts
WHERE body MATCH ?
ORDER BY rank
''', (query,))

results = cursor.fetchall()
conn.close()

return render_template('search.html', query=query, results=results)

if __name__ == '__main__':
app.run(debug=False , host='localhost', port=8001)
57 changes: 57 additions & 0 deletions powerindex/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Power Index{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
<script>
// Check and set dark mode before anything renders
(function() {
if (localStorage.getItem('darkMode') === 'true') {
document.documentElement.setAttribute('data-theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
}
})();
</script>
</head>
<body>
<div class="container">
<header>
<div class="title-container">
<button class="title-link" onclick="toggleDarkMode(event)" style="cursor: pointer; border: none; background: none; padding: 0; display: flex; align-items: center;">
<img src="{{ url_for('static', filename='noun-aardvark.svg') }}" alt="Power Logo" class="title-logo">
<h1>{% block header %}Power Index{% endblock %}</h1>
</button>
</div>
{% include 'navbar.html' %}
</header>

<main>
{% block content %}
{% endblock %}
</main>
</div>

{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
document.documentElement.classList.add('ready');
});

function toggleDarkMode(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
const html = document.documentElement;
const isDark = html.getAttribute('data-theme') === 'dark';
html.setAttribute('data-theme', isDark ? 'light' : 'dark');
localStorage.setItem('darkMode', !isDark);
return false;
}
</script>
{% endblock %}
</body>
</html>
228 changes: 109 additions & 119 deletions powerindex/templates/index.html
Original file line number Diff line number Diff line change
@@ -1,135 +1,125 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Power Index</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
<style>
{{ highlight_style }}
</style>
</head>
<body>
<div class="container">
<div class="title-container">
<img src="{{ url_for('static', filename='noun-aardvark.svg') }}" alt="Aardvark logo" class="title-logo">
<h1>The Power Viewer</h1>
</div>

<form method="GET" action="/" class="search-form">
<input type="text"
name="search"
placeholder="Search scripts..."
value="{{ search_query }}"
autofocus>
<button type="submit">Search</button>
</form>
<div class="content-wrapper">
<ul class="script-list">
{% for script in scripts %}
<li class="script-item">
<a href="/?script={{ script }}{% if search_query %}&search={{ search_query }}{% endif %}"
{% if script == selected_script %}class="selected"{% endif %}>
{% if search_query %}
{{ script | highlight_search(search_query) | safe }}
{% else %}
{{ script }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
{% if script_content %}
<div class="script-content">
<h3 data-script-name="{{ selected_script }}">
<button class="execute-button" onclick="executeScript('{{ selected_script }}')">Execute</button>
<button class="copy-button" onclick="copyToClipboard('{{ doc_url }}')">Copy Docs URL</button>
<a href="{{ doc_url }}" target="_blank" rel="noopener noreferrer">{{ selected_script }}</a>
</h3>
<div class="script-boxes">
<div class="script-box">
<div class="script-box-header">
<h4>Original Script</h4>
</div>
<div class="highlight-wrapper" id="original-script">
{{ highlighted_content | safe }}
</div>
{% extends "base.html" %}

{% block title %}The Power Viewer{% endblock %}

{% block header %}The Power Viewer{% endblock %}

{% block content %}
<form method="GET" action="/" class="search-form">
<input type="text"
name="search"
placeholder="Search scripts..."
value="{{ search_query }}"
autofocus>
<button type="submit">Search</button>
</form>
<div class="content-wrapper">
<ul class="script-list">
{% for script in scripts %}
<li class="script-item">
<a href="/?script={{ script }}{% if search_query %}&search={{ search_query }}{% endif %}"
{% if script == selected_script %}class="selected"{% endif %}>
{% if search_query %}
{{ script | highlight_search(search_query) | safe }}
{% else %}
{{ script }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
{% if script_content %}
<div class="script-content">
<h3 data-script-name="{{ selected_script }}">
<button class="execute-button" onclick="executeScript('{{ selected_script }}')">Execute</button>
<button class="copy-button" onclick="copyToClipboard('{{ doc_url }}')">Copy Docs URL</button>
<a href="{{ doc_url }}" target="_blank" rel="noopener noreferrer">{{ selected_script }}</a>
</h3>
<div class="script-boxes">
<div class="script-box">
<div class="script-box-header">
<h4>Original Script</h4>
</div>
<div class="highlight-wrapper" id="original-script">
{{ highlighted_content | safe }}
</div>
</div>
<div class="script-boxes">
<div class="script-box">
<div class="script-box-header">
<h4>Rendered Script</h4>
<button class="copy-button" onclick="copyScriptContent('rendered-script')">Copy</button>
</div>
<div class="highlight-wrapper" id="rendered-script">
{{ rendered_content | safe }}
</div>
</div>
<div class="script-boxes">
<div class="script-box">
<div class="script-box-header">
<h4>Rendered Script</h4>
<button class="copy-button" onclick="copyScriptContent('rendered-script')">Copy</button>
</div>
<div class="highlight-wrapper" id="rendered-script">
{{ rendered_content | safe }}
</div>
</div>
<div class="output-boxes">
<div class="output-box">
<div class="output-header">
<div class="header-title">
<h4>Standard Output</h4>
<button class="copy-button stdout-copy" onclick="copyScriptContent('stdout')">Copy</button>
</div>
<input type="text"
id="stdoutSearch"
class="output-search"
placeholder="Search output..."
onkeyup="searchOutput('stdout')">
</div>
<div class="output-boxes">
<div class="output-box">
<div class="output-header">
<div class="header-title">
<h4>Standard Output</h4>
<button class="copy-button stdout-copy" onclick="copyScriptContent('stdout')">Copy</button>
</div>
<pre id="stdout"></pre>
<input type="text"
id="stdoutSearch"
class="output-search"
placeholder="Search output..."
onkeyup="searchOutput('stdout')">
</div>
<div class="output-box">
<h4>Standard Error</h4>
<button class="copy-button stderr-copy" onclick="copyScriptContent('stderr')">Copy</button>
<div class="highlight-wrapper">
<div class="highlight">
<pre id="stderr">{{ stderr }}</pre>
</div>
<pre id="stdout"></pre>
</div>
<div class="output-box">
<h4>Standard Error</h4>
<button class="copy-button stderr-copy" onclick="copyScriptContent('stderr')">Copy</button>
<div class="highlight-wrapper">
<div class="highlight">
<pre id="stderr">{{ stderr }}</pre>
</div>
</div>
<div class="output-box">
<h4>Headers</h4>
<button class="copy-button headers-copy" onclick="copyScriptContent('headers')">Copy</button>
<input type="text" id="headersSearch" onkeyup="searchOutput('headers')" placeholder="Search headers...">
<div class="highlight-wrapper">
<div class="highlight">
<pre id="headers">{{ headers }}</pre>
</div>
</div>
<div class="output-box">
<h4>Headers</h4>
<button class="copy-button headers-copy" onclick="copyScriptContent('headers')">Copy</button>
<input type="text" id="headersSearch" onkeyup="searchOutput('headers')" placeholder="Search headers...">
<div class="highlight-wrapper">
<div class="highlight">
<pre id="headers">{{ headers }}</pre>
</div>
</div>
</div>

<h2>Configuration</h2>
<input type="text"
id="configSearch"
class="config-search"
placeholder="Search configuration..."
onkeyup="searchConfig()">
<table id="configTable">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for key, value in config.items() %}
<tr>
<td>{{ key }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}

<h2>Configuration</h2>
<input type="text"
id="configSearch"
class="config-search"
placeholder="Search configuration..."
onkeyup="searchConfig()">
<table id="configTable">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for key, value in config.items() %}
<tr>
<td>{{ key }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% endblock %}

{% block scripts %}
{{ super() }}
<script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>
{% endblock %}
Loading