Add sources config
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
jChenvan 2025-09-02 17:13:31 -04:00
parent d6e10b1b51
commit 725f028d69
5 changed files with 175 additions and 5 deletions

View File

@ -970,6 +970,90 @@ def api_delete_transaction(id):
finally:
conn.close()
@app.route('/sources')
def view_sources():
conn = get_db_connection()
if conn is None:
flash("Database connection error", "error")
return render_template('view_sources.html', sources=[], version=VERSION)
try:
with conn.cursor() as cur:
cur.execute('SELECT * FROM sources ORDER BY src_id DESC')
sources = cur.fetchall()
except Exception as e:
logger.error(f"Database error: {e}")
flash(f"Database error: {e}", "error")
sources = []
finally:
conn.close()
return render_template('view_sources.html', sources=sources, version=VERSION)
@app.route('/api/source', methods=['POST'])
def api_create_source():
"""API endpoint to create a source"""
data = request.form.to_dict()
# Validate required fields
required_fields = ['title', 'link', 'type']
for field in required_fields:
if field not in data or not data[field]:
return jsonify({"error": f"Missing required field: {field}"}), 400
conn = get_db_connection()
if conn is None:
return jsonify({"error": "Database connection error"}), 500
try:
with conn.cursor() as cur:
cur.execute(
"""
INSERT INTO sources (
title, link, type
) VALUES (
%(title)s, %(link)s, %(type)s
) RETURNING src_id
""",
{
'title': data['title'],
'link': data['link'],
'type': data['type']
}
)
result = cur.fetchone()
if result and 'src_id' in result:
conn.commit()
except Exception as e:
logger.error(f"Error creating source via API: {e}")
finally:
conn.close()
return redirect(url_for("view_sources"))
@app.route('/api/source/<int:id>', methods=['DELETE'])
def api_delete_source(id):
"""API endpoint to delete a source"""
conn = get_db_connection()
if conn is None:
return jsonify({"error": "Database connection error"}), 500
try:
with conn.cursor() as cur:
# Check if transaction exists
cur.execute('SELECT src_id FROM sources WHERE src_id = %s', (id,))
if cur.fetchone() is None:
return jsonify({"error": "Source not found"}), 404
# Delete the transaction
cur.execute('DELETE FROM sources WHERE src_id = %s', (id,))
conn.commit()
return jsonify({"message": "Source deleted successfully"}), 200
except Exception as e:
logger.error(f"Error deleting transaction via API: {e}")
return jsonify({"error": f"Error deleting source: {str(e)}"}), 500
finally:
conn.close()
if __name__ == '__main__':
logger.info(f"Starting Ploughshares v{VERSION}")
bootstrap_database()

View File

@ -1,6 +1,7 @@
-- Drop tables if they exist
DROP TABLE IF EXISTS transaction_documents CASCADE;
DROP TABLE IF EXISTS transactions CASCADE;
DROP TABLE IF EXISTS sources CASCADE;
-- Create transactions table
CREATE TABLE IF NOT EXISTS transactions (
@ -41,6 +42,13 @@ CREATE TABLE transaction_documents (
upload_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE sources (
src_id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
link VARCHAR(255) NOT NULL,
type VARCHAR(255) NOT NULL
);
-- Create indexes for better performance
CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions(transaction_type);
CREATE INDEX IF NOT EXISTS idx_transactions_division ON transactions(company_division);

View File

@ -59,11 +59,16 @@
<span class="badge bg-warning rounded-pill" id="pending-count"></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('api_docs') }}" aria-current="{% if request.endpoint == 'api_docs' %}page{% endif %}">
<i class="bi bi-file-earmark-text" aria-hidden="true"></i> API Docs
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('api_docs') }}" aria-current="{% if request.endpoint == 'api_docs' %}page{% endif %}">
<i class="bi bi-file-earmark-text" aria-hidden="true"></i> API Docs
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('view_sources') }}">
Sources
</a>
</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,73 @@
{% extends "base.html" %}
{% block title %}Sources - Project Ploughshares{% endblock %}
{% block content %}
<div class="container-fluid mt-4">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h2>Sources</h2>
</div>
<div class="card-body">
<form action="{{ url_for('api_create_source') }}" method="post" class="needs-validation" novalidate>
<h1>Add Source</h1>
<label>
title
<input type="text" name="title">
</label>
<label>
link
<input type="text" name="link">
</label>
<label>
type
<select name="type">
<option value="Google Alert">Google Alert</option>
<option value="Crawler Startpoint">Crawler Startpoint</option>
</select>
</label>
<button>add</button>
</form>
<div class="table-responsive">
<table class="table table-hover">
<thead class="thead-light">
<tr>
<th>Source No.</th>
<th>Title</th>
<th>Link</th>
<th>Type</th>
<th></th>
</tr>
</thead>
<tbody>
{% for source in sources %}
<tr>
<td>{{ source['src_id'] }}</td>
<td>{{ source['title'] }}</td>
<td><a href="{{ source['link'] }}">{{ source['link'] }}</a></td>
<td>{{ source['type'] }}</td>
<td><button id="{{ source['src_id'] }}">delete</button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script nonce="{{ csp_nonce() }}">
document.body.addEventListener("click", e=>{
if (e.target.tagName === "BUTTON") {
const button = e.target;
const id = Number(button.id);
if (isNaN(id)) return;
fetch(`api/source/${id}`, {
method: "DELETE",
}).then(()=>window.location.reload());
}
});
</script>
{% endblock %}