Scanner: 0 Treffer durch Anführungszeichen in Schlagwörtern behoben

- Anführungszeichen um Begriffe beim Eingeben, Speichern und Lesen
  entfernen; der Agent vom 29.09. suchte nach "Interim" samt Zeichen
- Umlaute in Such-URLs umschreiben, karriere.at-404 = keine Treffer
- Filterleiste mit festen Breiten, bricht um statt überzulaufen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-29 13:19:16 +02:00
co-authored by Claude Opus 5.5
parent 33c9dbdb2b
commit b34a1968df
7 changed files with 51 additions and 16 deletions
+2 -1
View File
@@ -4,6 +4,7 @@ import { requireAdmin, requireAuth } from '../middleware/auth.js';
import { logHistory } from '../history.js';
import { SOURCES, SOURCE_IDS, sourceReady } from '../scanner/sources.js';
import { parseList, runScan, scanStatus } from '../scanner/pipeline.js';
import { stripQuotes } from '../scanner/matching.js';
import {
getLastScanAt,
loadScannerSecrets,
@@ -25,7 +26,7 @@ router.use(requireAuth);
const STATUSES = ['neu', 'gesehen', 'in_bearbeitung', 'verworfen'];
const cleanList = (value) =>
[...new Set((Array.isArray(value) ? value : []).map((item) => String(item).trim()).filter(Boolean))].slice(0, 50);
[...new Set((Array.isArray(value) ? value : []).map(stripQuotes).filter(Boolean))].slice(0, 50);
const agentOut = (row) => ({
id: row.id,
+11
View File
@@ -10,6 +10,17 @@
* Gewicht 2 = im Titel, 1 = nur im Text
*/
/*
* Anfuehrungszeichen um Begriffe entfernen: wer "Interim", "Vakanz" eingibt,
* meint die Woerter, nicht die Zeichen. Mit Zeichen fand die Bewertung am
* 29.09.2026 in 178 Anzeigen keinen einzigen Treffer.
*/
export const stripQuotes = (text) =>
String(text ?? '')
.trim()
.replace(/^["'„“”‚‘’«»]+|["'„“”‚‘’«»]+$/g, '')
.trim();
const normalize = (text) => String(text ?? '').normalize('NFC').toLowerCase();
const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+2 -2
View File
@@ -1,6 +1,6 @@
import { pool } from '../db.js';
import { SOURCES, sourceReady } from './sources.js';
import { findCompany, normalizeCompany, scoreJob } from './matching.js';
import { findCompany, normalizeCompany, scoreJob, stripQuotes } from './matching.js';
import { loadScannerSecrets, loadScannerSettings, setLastScanAt } from './settings.js';
/*
@@ -142,7 +142,7 @@ async function execute(trigger) {
export function parseList(value) {
try {
const list = JSON.parse(value ?? '[]');
return Array.isArray(list) ? list.map((item) => String(item).trim()).filter(Boolean) : [];
return Array.isArray(list) ? list.map(stripQuotes).filter(Boolean) : [];
} catch {
return [];
}
+15 -2
View File
@@ -114,10 +114,15 @@ export function parseRelativeDate(text, now = new Date()) {
return null;
}
// Umlaute umschreiben: karriere.at antwortet auf /jobs/interimslösung mit 404.
const slug = (keyword) =>
clean(keyword)
.toLowerCase()
.replace(/[^a-z0-9äöüß]+/g, '-')
.replace(/ä/g, 'ae')
.replace(/ö/g, 'oe')
.replace(/ü/g, 'ue')
.replace(/ß/g, 'ss')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
/* ------------------------------------------------------------------------
@@ -214,7 +219,15 @@ async function fetchStepstone(keyword) {
* ---------------------------------------------------------------------- */
async function fetchKarriere(keyword) {
const base = 'https://www.karriere.at';
const html = await request(`${base}/jobs/${encodeURIComponent(slug(keyword))}`);
let html;
try {
html = await request(`${base}/jobs/${encodeURIComponent(slug(keyword))}`);
} catch (err) {
// Fuer Begriffe ohne eigene Suchseite antwortet karriere.at mit 404 -
// das heisst "nichts gefunden", nicht "Quelle kaputt".
if (err.message === 'HTTP 404') return [];
throw err;
}
const $ = cheerio.load(html);
const jobs = [];