last updates
This commit is contained in:
7
app.py
7
app.py
@@ -1,7 +1,7 @@
|
|||||||
from flask import Flask, render_template, request, send_file, jsonify
|
from flask import Flask, render_template, request, send_file, jsonify
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from scraper import get_google_first_page
|
from api import get_google_first_page
|
||||||
import io, json, csv, yaml, os
|
import io, json, csv, yaml, os, requests
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
@@ -10,6 +10,7 @@ app = Flask(__name__)
|
|||||||
|
|
||||||
@app.route("/", methods=["GET"])
|
@app.route("/", methods=["GET"])
|
||||||
def index():
|
def index():
|
||||||
|
|
||||||
return render_template("index.html", results=[])
|
return render_template("index.html", results=[])
|
||||||
|
|
||||||
@app.route("/search", methods=["POST"])
|
@app.route("/search", methods=["POST"])
|
||||||
@@ -17,6 +18,7 @@ def search():
|
|||||||
query = request.form.get("q", "").strip()
|
query = request.form.get("q", "").strip()
|
||||||
if not query:
|
if not query:
|
||||||
return render_template("index.html", error="Zadejte dotaz.")
|
return render_template("index.html", error="Zadejte dotaz.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
results = get_google_first_page(query) # list of dicts
|
results = get_google_first_page(query) # list of dicts
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -32,6 +34,7 @@ def search():
|
|||||||
@app.route("/export", methods=["POST"])
|
@app.route("/export", methods=["POST"])
|
||||||
def export():
|
def export():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
|
||||||
ext = data.get("format", "json")
|
ext = data.get("format", "json")
|
||||||
|
|
||||||
results = data.get("results", [])
|
results = data.get("results", [])
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ services:
|
|||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "python -m pytest || exit 1"]
|
test: ["CMD-SHELL", "python -m pytest || exit 1"]
|
||||||
interval: 1m
|
interval: 1m
|
||||||
timeout: 20s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 1
|
||||||
start_period: 20s
|
start_period: 10s
|
||||||
|
|||||||
6
pytest.ini
Normal file
6
pytest.ini
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[pytest]
|
||||||
|
# Show logs during test runs (not only on failures)
|
||||||
|
log_cli = true
|
||||||
|
log_cli_level = INFO
|
||||||
|
log_format = %(asctime)s %(levelname)s %(name)s - %(message)s
|
||||||
|
log_date_format = %H:%M:%S
|
||||||
@@ -6,25 +6,42 @@
|
|||||||
<title>Výsledky pro "{{ query }}" - vontor.cz</title>
|
<title>Výsledky pro "{{ query }}" - vontor.cz</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
<script>
|
<script>
|
||||||
function download(fmt) {
|
function download(format) {
|
||||||
const payload = { format: fmt, results: JSON.parse(document.getElementById('json-data').textContent) };
|
const resultsJsonElement = document.getElementById('json-data');
|
||||||
|
const results = JSON.parse(resultsJsonElement.textContent);
|
||||||
|
const requestPayload = { format, results };
|
||||||
|
|
||||||
fetch('/export', {
|
fetch('/export', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(requestPayload)
|
||||||
}).then(r => {
|
})
|
||||||
if (r.ok) return r.blob();
|
.then(function(response) {
|
||||||
return r.json().then(j=>{throw j});
|
if (response.ok){
|
||||||
}).then(blob=>{
|
return response.blob();
|
||||||
const url = window.URL.createObjectURL(blob);
|
}
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
//error
|
||||||
a.download = 'results.' + fmt;
|
return response.json().then(function(errorBody) { throw errorBody; });
|
||||||
document.body.appendChild(a);
|
})
|
||||||
a.click();
|
.then(function(fileBlob) {
|
||||||
a.remove();
|
//blob
|
||||||
window.URL.revokeObjectURL(url);
|
const objectUrl = window.URL.createObjectURL(fileBlob);
|
||||||
}).catch(e=>{ alert('Export failed: ' + JSON.stringify(e));});
|
|
||||||
|
const downloadLink = document.createElement('a');
|
||||||
|
|
||||||
|
downloadLink.href = objectUrl;
|
||||||
|
downloadLink.download = 'results.' + format;
|
||||||
|
document.body.appendChild(downloadLink);
|
||||||
|
|
||||||
|
downloadLink.click();
|
||||||
|
downloadLink.remove();
|
||||||
|
|
||||||
|
window.URL.revokeObjectURL(objectUrl);
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
alert('Export failed: ' + JSON.stringify(error));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
import pytest
|
import pytest
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
import scraper
|
import api
|
||||||
|
|
||||||
|
|
||||||
def test_env_variables_present():
|
def test_env_variables_present():
|
||||||
@@ -13,6 +14,10 @@ def test_env_variables_present():
|
|||||||
|
|
||||||
key = (os.getenv("GOOGLE_DEVELOPER_KEY") or "").strip()
|
key = (os.getenv("GOOGLE_DEVELOPER_KEY") or "").strip()
|
||||||
cx = (os.getenv("GOOGLE_CSE_ID") or "").strip()
|
cx = (os.getenv("GOOGLE_CSE_ID") or "").strip()
|
||||||
|
|
||||||
|
# Log presence without exposing secrets
|
||||||
|
logging.info("GOOGLE_DEVELOPER_KEY present: %s", bool(key))
|
||||||
|
logging.info("GOOGLE_CSE_ID present: %s", bool(cx))
|
||||||
assert key, "GOOGLE_DEVELOPER_KEY is missing or empty"
|
assert key, "GOOGLE_DEVELOPER_KEY is missing or empty"
|
||||||
assert cx, "GOOGLE_CSE_ID is missing or empty"
|
assert cx, "GOOGLE_CSE_ID is missing or empty"
|
||||||
|
|
||||||
@@ -27,17 +32,24 @@ def test_integration_search_youtube(monkeypatch):
|
|||||||
cx = (os.getenv("GOOGLE_CSE_ID") or "").strip()
|
cx = (os.getenv("GOOGLE_CSE_ID") or "").strip()
|
||||||
|
|
||||||
if not key or not cx:
|
if not key or not cx:
|
||||||
|
logging.warning("Skipping integration test: missing GOOGLE_DEVELOPER_KEY or GOOGLE_CSE_ID")
|
||||||
pytest.skip("Integration test skipped: GOOGLE_DEVELOPER_KEY/GOOGLE_CSE_ID not set")
|
pytest.skip("Integration test skipped: GOOGLE_DEVELOPER_KEY/GOOGLE_CSE_ID not set")
|
||||||
|
|
||||||
# Speed up: don't wait during the test
|
# Speed up: don't wait during the test
|
||||||
monkeypatch.setattr(scraper, "RATE_SECONDS", 0)
|
monkeypatch.setattr(api, "RATE_SECONDS", 0)
|
||||||
monkeypatch.setattr(scraper, "last_api_call", 0)
|
monkeypatch.setattr(api, "last_api_call", 0)
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
try:
|
try:
|
||||||
results = scraper.get_google_first_page("youtube")
|
logging.info("Calling get_google_first_page for query 'youtube'")
|
||||||
|
results = api.get_google_first_page("youtube")
|
||||||
|
logging.info("Received %d results. First title: %s", len(results), (results[0].get("title") if results else None))
|
||||||
|
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
|
logging.error("API error during integration test: %s", e)
|
||||||
return pytest.skip(f"Integration test skipped due to API error: {e}")
|
return pytest.skip(f"Integration test skipped due to API error: {e}")
|
||||||
|
|
||||||
assert isinstance(results, list) and len(results) > 0
|
assert isinstance(results, list) and len(results) > 0
|
||||||
assert any("youtube.com" in (item.get("link") or "") for item in results)
|
has_youtube = any("youtube.com" in (item.get("link") or "") for item in results)
|
||||||
|
logging.info("Contains youtube.com link: %s", has_youtube)
|
||||||
|
assert has_youtube
|
||||||
Reference in New Issue
Block a user