Skip to content

Commit

Permalink
Merge pull request #339 from gm3dmo/fts
Browse files Browse the repository at this point in the history
Adding SQLite Full text search table of scripts and contents"
  • Loading branch information
gm3dmo authored Feb 28, 2025
2 parents 84d7938 + df896a2 commit 865039a
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 120 deletions.
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

0 comments on commit 865039a

Please sign in to comment.