Add sources config
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
d6e10b1b51
commit
725f028d69
Binary file not shown.
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
Loading…
Reference in New Issue