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:
|
finally:
|
||||||
conn.close()
|
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__':
|
if __name__ == '__main__':
|
||||||
logger.info(f"Starting Ploughshares v{VERSION}")
|
logger.info(f"Starting Ploughshares v{VERSION}")
|
||||||
bootstrap_database()
|
bootstrap_database()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
-- Drop tables if they exist
|
-- Drop tables if they exist
|
||||||
DROP TABLE IF EXISTS transaction_documents CASCADE;
|
DROP TABLE IF EXISTS transaction_documents CASCADE;
|
||||||
DROP TABLE IF EXISTS transactions CASCADE;
|
DROP TABLE IF EXISTS transactions CASCADE;
|
||||||
|
DROP TABLE IF EXISTS sources CASCADE;
|
||||||
|
|
||||||
-- Create transactions table
|
-- Create transactions table
|
||||||
CREATE TABLE IF NOT EXISTS transactions (
|
CREATE TABLE IF NOT EXISTS transactions (
|
||||||
|
@ -41,6 +42,13 @@ CREATE TABLE transaction_documents (
|
||||||
upload_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
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 indexes for better performance
|
||||||
CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions(transaction_type);
|
CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions(transaction_type);
|
||||||
CREATE INDEX IF NOT EXISTS idx_transactions_division ON transactions(company_division);
|
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>
|
<span class="badge bg-warning rounded-pill" id="pending-count"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{{ url_for('api_docs') }}" aria-current="{% if request.endpoint == 'api_docs' %}page{% endif %}">
|
<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
|
<i class="bi bi-file-earmark-text" aria-hidden="true"></i> API Docs
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('view_sources') }}">
|
||||||
|
Sources
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</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