8 Commits

5 changed files with 80 additions and 55 deletions

View File

@@ -5,6 +5,24 @@ Alle wichtigen Änderungen an diesem Projekt werden in dieser Datei dokumentiert
Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/), Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/),
und dieses Projekt adhäriert zu [Semantic Versioning](https://semver.org/lang/de/). und dieses Projekt adhäriert zu [Semantic Versioning](https://semver.org/lang/de/).
## [1.2.6] - 2024-03-19
### Geändert
- Verbesserte Suchfunktion: Keine Ergebnisse mehr bei leeren Suchfeldern
- Optimiertes Highlighting der Suchergebnisse für alle Suchfelder
- Fachrichtung wird jetzt in den Suchergebnissen hervorgehoben
## [1.2.5] - 2024-03-19
### Hinzugefügt
- Neues Suchfeld für Fachrichtung
- Index für das Fachrichtung-Feld in der Datenbank
- Fachrichtung in der allgemeinen Suche integriert
## [1.2.4] - 2024-03-19
### Geändert
- Performance-Optimierung: Indizes für alle Suchfelder hinzugefügt
- Verbesserte Suchgeschwindigkeit durch optimierte Datenbankindizes
- Zusammengesetzter Index für die häufigste Suchkombination (Name + Ort) hinzugefügt
## [1.2.3] - 2024-03-19 ## [1.2.3] - 2024-03-19
### Geändert ### Geändert
- Performance-Optimierung: Entfernung aller console.log Anweisungen - Performance-Optimierung: Entfernung aller console.log Anweisungen

View File

@@ -51,7 +51,7 @@ Die Anwendung unterstützt CIDR-Notation für IP-Bereiche. Beispiele:
## Version ## Version
Aktuelle Version: 1.2.1 Aktuelle Version: v1.2.6
## Lizenz ## Lizenz
@@ -103,4 +103,4 @@ curl "http://localhost:5001/search?fachrichtung=Zahnarzt&ort=Berlin&name=Schmidt
## Version ## Version
Aktuelle Version: [v1.2.0](CHANGELOG.md#v120---2024-03-17) Aktuelle Version: [v1.2.4](CHANGELOG.md#v124---2024-03-19)

39
app.py
View File

@@ -50,16 +50,24 @@ def init_db():
telefon TEXT, telefon TEXT,
mobil TEXT, mobil TEXT,
email TEXT, email TEXT,
bemerkung TEXT bemerkung TEXT,
fachrichtung TEXT
) )
''') ''')
# Erstelle Indizes für häufig durchsuchte Spalten # Erstelle Indizes für alle Suchfelder
c.execute('CREATE INDEX IF NOT EXISTS idx_name ON customers(name)') c.execute('CREATE INDEX IF NOT EXISTS idx_customers_nummer ON customers(nummer)')
c.execute('CREATE INDEX IF NOT EXISTS idx_ort ON customers(ort)') c.execute('CREATE INDEX IF NOT EXISTS idx_customers_name ON customers(name)')
c.execute('CREATE INDEX IF NOT EXISTS idx_nummer ON customers(nummer)') c.execute('CREATE INDEX IF NOT EXISTS idx_customers_strasse ON customers(strasse)')
c.execute('CREATE INDEX IF NOT EXISTS idx_telefon ON customers(telefon)') c.execute('CREATE INDEX IF NOT EXISTS idx_customers_plz ON customers(plz)')
c.execute('CREATE INDEX IF NOT EXISTS idx_email ON customers(email)') c.execute('CREATE INDEX IF NOT EXISTS idx_customers_ort ON customers(ort)')
c.execute('CREATE INDEX IF NOT EXISTS idx_customers_telefon ON customers(telefon)')
c.execute('CREATE INDEX IF NOT EXISTS idx_customers_mobil ON customers(mobil)')
c.execute('CREATE INDEX IF NOT EXISTS idx_customers_email ON customers(email)')
c.execute('CREATE INDEX IF NOT EXISTS idx_customers_fachrichtung ON customers(fachrichtung)')
# Erstelle einen zusammengesetzten Index für die häufigste Suchkombination
c.execute('CREATE INDEX IF NOT EXISTS idx_customers_name_ort ON customers(name, ort)')
conn.commit() conn.commit()
conn.close() conn.close()
@@ -91,8 +99,8 @@ def import_csv():
# Importiere die Daten # Importiere die Daten
for _, row in df.iterrows(): for _, row in df.iterrows():
c.execute(''' c.execute('''
INSERT INTO customers (nummer, name, strasse, plz, ort, telefon, mobil, email, bemerkung) INSERT INTO customers (nummer, name, strasse, plz, ort, telefon, mobil, email, bemerkung, fachrichtung)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', ( ''', (
row['Nummer'], row['Nummer'],
row['name'], row['name'],
@@ -102,7 +110,8 @@ def import_csv():
row['Tel'], row['Tel'],
row['Handy'], row['Handy'],
row['mail'], row['mail'],
f"Fachrichtung: {row['Fachrichtung']}" f"Fachrichtung: {row['Fachrichtung']}",
row['Fachrichtung']
)) ))
conn.commit() conn.commit()
@@ -126,8 +135,8 @@ def search_customers(search_params):
# Allgemeine Suche über alle Felder # Allgemeine Suche über alle Felder
if search_params.get('q'): if search_params.get('q'):
search_term = f"%{search_params['q']}%" search_term = f"%{search_params['q']}%"
query += " AND (name LIKE ? OR ort LIKE ? OR nummer LIKE ? OR telefon LIKE ? OR mobil LIKE ? OR email LIKE ? OR bemerkung LIKE ?)" query += " AND (name LIKE ? OR ort LIKE ? OR nummer LIKE ? OR telefon LIKE ? OR mobil LIKE ? OR email LIKE ? OR bemerkung LIKE ? OR fachrichtung LIKE ?)"
params.extend([search_term] * 7) params.extend([search_term] * 8)
# Spezifische Suche für einzelne Felder # Spezifische Suche für einzelne Felder
if search_params.get('name'): if search_params.get('name'):
@@ -163,7 +172,8 @@ def search_customers(search_params):
'telefon': row[6], 'telefon': row[6],
'mobil': row[7], 'mobil': row[7],
'email': row[8], 'email': row[8],
'bemerkung': row[9] 'bemerkung': row[9],
'fachrichtung': row[10]
} }
customers.append(customer) customers.append(customer)
@@ -264,7 +274,8 @@ def search():
'plz': request.args.get('plz', ''), 'plz': request.args.get('plz', ''),
'telefon': request.args.get('telefon', ''), 'telefon': request.args.get('telefon', ''),
'email': request.args.get('email', ''), 'email': request.args.get('email', ''),
'q': request.args.get('q', '') 'q': request.args.get('q', ''),
'fachrichtung': request.args.get('fachrichtung', '')
} }
# Führe die Suche in der Datenbank durch # Führe die Suche in der Datenbank durch

View File

@@ -58,6 +58,14 @@
<i class="fas fa-search search-icon"></i> <i class="fas fa-search search-icon"></i>
</div> </div>
</div> </div>
<div class="search-field">
<div class="input-group">
<input type="text" id="fachrichtungInput" class="form-control" placeholder="Fachrichtung" oninput="searchCustomers()">
<i class="fas fa-times reset-icon" onclick="clearInput('fachrichtungInput')"></i>
<i class="fas fa-search search-icon"></i>
</div>
</div>
</div> </div>
<div class="result-counts"> <div class="result-counts">
@@ -81,8 +89,8 @@
<footer class="footer"> <footer class="footer">
<div class="footer-content"> <div class="footer-content">
Made with ❤️ and 🍺 by <a href="https://www.medisoftware.de" target="_blank" class="footer-link">medisoftware</a> Proudly made with ❤️ and 🍺 by <a href="https://www.medisoftware.de" target="_blank" class="footer-link">medisoftware</a>
<div style="font-size: 0.8em;">Version: v1.2.3</div> <div style="font-size: 0.8em;">Version: v1.2.6</div>
</div> </div>
</footer> </footer>
@@ -225,20 +233,33 @@
return; return;
} }
const searchTerm = document.getElementById('q').value; // Hole alle Suchbegriffe
const searchTerms = {
general: document.getElementById('q').value,
name: document.getElementById('nameInput').value,
ort: document.getElementById('ortInput').value,
nummer: document.getElementById('nummerInput').value,
plz: document.getElementById('plzInput').value,
fachrichtung: document.getElementById('fachrichtungInput').value
};
results.forEach(customer => { results.forEach(customer => {
const card = document.createElement('div'); const card = document.createElement('div');
card.className = 'customer-card'; card.className = 'customer-card';
card.innerHTML = ` card.innerHTML = `
<div class="customer-info"> <div class="customer-info">
<h5 class="mb-1">${highlightText(customer.name, searchTerm)}</h5> <h5 class="mb-1">${highlightText(customer.name, searchTerms.general || searchTerms.name)}</h5>
<p class="mb-1 customer-number">${createCustomerLink(customer.nummer)}</p> <p class="mb-1 customer-number">${createCustomerLink(customer.nummer)}</p>
<p class="mb-1">${createAddressLink(customer.strasse, customer.plz, customer.ort)}</p> <p class="mb-1">${createAddressLink(
customer.strasse,
highlightText(customer.plz, searchTerms.general || searchTerms.plz),
highlightText(customer.ort, searchTerms.general || searchTerms.ort)
)}</p>
<p class="mb-1">Tel: ${createPhoneLink(customer.telefon)}</p> <p class="mb-1">Tel: ${createPhoneLink(customer.telefon)}</p>
${customer.mobil ? `<p class="mb-1">Mobil: ${createPhoneLink(customer.mobil)}</p>` : ''} ${customer.mobil ? `<p class="mb-1">Mobil: ${createPhoneLink(customer.mobil)}</p>` : ''}
${customer.email ? `<p class="mb-1">E-Mail: ${createEmailLink(customer.email)}</p>` : ''} ${customer.email ? `<p class="mb-1">E-Mail: ${createEmailLink(customer.email)}</p>` : ''}
${customer.bemerkung ? `<p class="mb-1">Bemerkung: ${customer.bemerkung}</p>` : ''} ${customer.bemerkung ? `<p class="mb-1">Bemerkung: ${customer.bemerkung}</p>` : ''}
${customer.fachrichtung ? `<p class="mb-1">Fachrichtung: ${highlightText(customer.fachrichtung, searchTerms.general || searchTerms.fachrichtung)}</p>` : ''}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<button class="share-button" onclick="copyCustomerLink('${customer.nummer}')"> <button class="share-button" onclick="copyCustomerLink('${customer.nummer}')">
@@ -256,6 +277,7 @@
const ort = document.getElementById('ortInput').value; const ort = document.getElementById('ortInput').value;
const nummer = document.getElementById('nummerInput').value; const nummer = document.getElementById('nummerInput').value;
const plz = document.getElementById('plzInput').value; const plz = document.getElementById('plzInput').value;
const fachrichtung = document.getElementById('fachrichtungInput').value;
// Zeige das Lade-Icon // Zeige das Lade-Icon
document.getElementById('loading').style.display = 'block'; document.getElementById('loading').style.display = 'block';
@@ -267,6 +289,7 @@
if (ort) params.append('ort', ort); if (ort) params.append('ort', ort);
if (nummer) params.append('nummer', nummer); if (nummer) params.append('nummer', nummer);
if (plz) params.append('plz', plz); if (plz) params.append('plz', plz);
if (fachrichtung) params.append('fachrichtung', fachrichtung);
// Führe die Suche durch // Führe die Suche durch
fetch('/search?' + params.toString()) fetch('/search?' + params.toString())
@@ -281,36 +304,7 @@
lastResults = data; lastResults = data;
updateResultCounts(); updateResultCounts();
displayResults(data);
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = '';
if (data.length === 0) {
resultsDiv.innerHTML = '<p class="text-center text-muted">Keine Ergebnisse gefunden</p>';
return;
}
data.forEach(customer => {
const card = document.createElement('div');
card.className = 'customer-card';
card.innerHTML = `
<div class="customer-info">
<h5 class="mb-1">${highlightText(customer.name, q || name)}</h5>
<p class="mb-1 customer-number">${createCustomerLink(customer.nummer)}</p>
<p class="mb-1">${createAddressLink(customer.strasse, customer.plz, customer.ort)}</p>
<p class="mb-1">Tel: ${createPhoneLink(customer.telefon)}</p>
${customer.mobil ? `<p class="mb-1">Mobil: ${createPhoneLink(customer.mobil)}</p>` : ''}
${customer.email ? `<p class="mb-1">E-Mail: ${createEmailLink(customer.email)}</p>` : ''}
${customer.bemerkung ? `<p class="mb-1">Bemerkung: ${customer.bemerkung}</p>` : ''}
</div>
<div class="card-actions">
<button class="share-button" onclick="copyCustomerLink('${customer.nummer}')">
<i class="fas fa-share-alt"></i> Teilen
</button>
</div>
`;
resultsDiv.appendChild(card);
});
}) })
.catch(error => { .catch(error => {
document.getElementById('loading').style.display = 'none'; document.getElementById('loading').style.display = 'none';
@@ -323,7 +317,8 @@
document.getElementById('nameInput'), document.getElementById('nameInput'),
document.getElementById('ortInput'), document.getElementById('ortInput'),
document.getElementById('nummerInput'), document.getElementById('nummerInput'),
document.getElementById('plzInput') document.getElementById('plzInput'),
document.getElementById('fachrichtungInput')
]; ];
const resetIcons = [ const resetIcons = [
@@ -331,7 +326,8 @@
document.querySelector('.reset-icon[onclick="clearInput(\'nameInput\')"]'), document.querySelector('.reset-icon[onclick="clearInput(\'nameInput\')"]'),
document.querySelector('.reset-icon[onclick="clearInput(\'ortInput\')"]'), document.querySelector('.reset-icon[onclick="clearInput(\'ortInput\')"]'),
document.querySelector('.reset-icon[onclick="clearInput(\'nummerInput\')"]'), document.querySelector('.reset-icon[onclick="clearInput(\'nummerInput\')"]'),
document.querySelector('.reset-icon[onclick="clearInput(\'plzInput\')"]') document.querySelector('.reset-icon[onclick="clearInput(\'plzInput\')"]'),
document.querySelector('.reset-icon[onclick="clearInput(\'fachrichtungInput\')"]')
]; ];
searchInputs.forEach((input, index) => { searchInputs.forEach((input, index) => {

View File

@@ -52,8 +52,8 @@
</div> </div>
<footer class="footer"> <footer class="footer">
<div class="footer-content"> <div class="footer-content">
Made with ❤️ and 🍺 by <a href="https://www.medisoftware.de" target="_blank" class="footer-link">medisoftware</a> Proudly made with ❤️ and 🍺 by <a href="https://www.medisoftware.de" target="_blank" class="footer-link">medisoftware</a>
<div style="font-size: 0.8em;">Version: v1.2.0</div> <div style="font-size: 0.8em;">Version: v1.2.3</div>
</div> </div>
</footer> </footer>
</body> </body>