Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
6cfc2e0162 | |||
bbcd04fd21 | |||
89a5152ada | |||
e6f43ca10a | |||
da3ef358d2 |
66
CHANGELOG.md
66
CHANGELOG.md
@@ -5,49 +5,37 @@ 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.0.2] - 2024-03-19
|
## [1.0.6] - 2024-03-17
|
||||||
|
|
||||||
### Hinzugefügt
|
|
||||||
- Wetterinformationen für jeden Suchtreffer
|
|
||||||
- Integration der OpenWeather API
|
|
||||||
- Wetter-Icons und Temperaturanzeige
|
|
||||||
- Umgebungsvariablen für API-Keys
|
|
||||||
|
|
||||||
### Geändert
|
### Geändert
|
||||||
- Anpassung der API-Antwortstruktur
|
- Verbesserte Suchfunktion: Kombinierte Suche über mehrere Felder möglich
|
||||||
- Verbesserte Fehlerbehandlung für API-Anfragen
|
- Dokumentation: Beispiele für kombinierte Suche hinzugefügt
|
||||||
|
|
||||||
## [1.0.1] - 2024-03-19
|
|
||||||
|
|
||||||
### Hinzugefügt
|
|
||||||
- Neues Suchfeld für Telefonnummer
|
|
||||||
- Reset-Icons für alle Suchfelder
|
|
||||||
- Trefferzähler für Suchergebnisse
|
|
||||||
|
|
||||||
|
## [1.0.5] - 2024-03-17
|
||||||
### Geändert
|
### Geändert
|
||||||
- Verbesserte Positionierung der UI-Elemente
|
- Verbesserte Suchfunktion: Ergebnisliste wird gelöscht, wenn alle Suchfelder leer sind
|
||||||
- Optimierte Suchlogik im Backend
|
|
||||||
- CSV-Datei in data-Verzeichnis verschoben
|
|
||||||
- Allgemeine Suche um Telefonnummer erweitert
|
|
||||||
|
|
||||||
### Behoben
|
## [1.0.4] - 2024-03-17
|
||||||
- Korrektur der Icon-Anzeige in Suchfeldern
|
### Geändert
|
||||||
- Verbesserte Fehlerbehandlung beim Laden der CSV-Datei
|
- Verbesserte Adressanzeige: Location-Icon neben der Adresse
|
||||||
|
- Entfernung des Google Maps Links aus dem Adresstext
|
||||||
|
|
||||||
## [1.0.0] - 2024-03-18
|
## [1.0.3] - 2024-03-17
|
||||||
|
### Geändert
|
||||||
|
- Hervorhebung der Suchbegriffe in den Ergebnissen
|
||||||
|
- Verbesserte Benutzeroberfläche
|
||||||
|
|
||||||
|
## [1.0.2] - 2024-03-17
|
||||||
|
### Geändert
|
||||||
|
- Entfernung der Wetterinformationen
|
||||||
|
- Optimierung der Suchfunktion
|
||||||
|
|
||||||
|
## [1.0.1] - 2024-03-17
|
||||||
### Hinzugefügt
|
### Hinzugefügt
|
||||||
- Grundlegende Suchfunktionalität
|
- Wetterinformationen für Kundensitz
|
||||||
- Spezifische Suchfelder für:
|
- Caching für Wetterdaten
|
||||||
- Name
|
|
||||||
- Ort
|
## [1.0.0] - 2024-03-17
|
||||||
- Kundennummer
|
### Hinzugefügt
|
||||||
- Fachrichtung
|
- Erste Version der Kundensuche
|
||||||
- Allgemeine Suche über alle Felder
|
- Grundlegende Suchfunktionen
|
||||||
- Klickbare Links für:
|
- Responsive Design
|
||||||
- Telefonnummern
|
|
||||||
- E-Mail-Adressen
|
|
||||||
- Google Maps Integration
|
|
||||||
- Share-Funktion für Suchergebnisse
|
|
||||||
- Responsive Design mit Bootstrap
|
|
||||||
- Live-Suche während der Eingabe
|
|
120
README.md
120
README.md
@@ -1,23 +1,27 @@
|
|||||||
# medisoftware Kundensuche
|
# medisoftware Kundensuche
|
||||||
|
|
||||||
Eine Webanwendung zur Suche in Kundendaten der medisoftware.
|
Eine webbasierte Kundensuche für medisoftware mit erweiterten Suchfunktionen.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Live-Suche in Kundendaten
|
- Schnelle und präzise Kundensuche
|
||||||
- Spezifische Suchfelder für:
|
- Mehrere Suchfelder für gezielte Suche:
|
||||||
- Name
|
- Name (Vor- und Nachname)
|
||||||
- Ort
|
- Ort
|
||||||
- Kundennummer
|
- Kundennummer
|
||||||
- Fachrichtung
|
- Fachrichtung
|
||||||
- Telefonnummer
|
- Telefon
|
||||||
- Allgemeine Suche über alle Felder
|
- Allgemeine Suche über alle Felder
|
||||||
- Klickbare Telefonnummern
|
- Kombinierte Suche über mehrere Felder
|
||||||
- Klickbare E-Mail-Adressen
|
- Hervorhebung der Suchbegriffe in den Ergebnissen
|
||||||
- Google Maps Integration für Adressen
|
- Direkte Links zu:
|
||||||
- Share-Funktion für Suchergebnisse
|
- medisoftware Kundenkartei (Kundennummer)
|
||||||
- Trefferzähler
|
- Google Maps (Adresse)
|
||||||
- Reset-Funktion für alle Suchfelder
|
- Telefon (Klick zum Anrufen)
|
||||||
|
- E-Mail (Klick zum Mailen)
|
||||||
|
- Responsive Design für alle Geräte
|
||||||
|
- Automatische Aktualisierung der Ergebnisse
|
||||||
|
- Leere Ergebnisliste bei leeren Suchfeldern
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -27,81 +31,69 @@ git clone https://gitea.elpatron.me/elpatron/medi-customers.git
|
|||||||
cd medi-customers
|
cd medi-customers
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Python-Abhängigkeiten installieren:
|
2. Docker Container starten:
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
3. CSV-Datei in das `data`-Verzeichnis kopieren:
|
3. Die Anwendung ist unter `http://localhost:5001` erreichbar.
|
||||||
```bash
|
|
||||||
mkdir data
|
|
||||||
cp spezexpo.csv data/customers.csv
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Anwendung starten:
|
## Entwicklung
|
||||||
```bash
|
|
||||||
python app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Die Anwendung ist dann unter `http://localhost:5001` erreichbar.
|
- Python 3.11
|
||||||
|
- Flask
|
||||||
|
- Docker
|
||||||
|
- Bootstrap 5
|
||||||
|
- Font Awesome
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
Alle Rechte vorbehalten. © 2025 medisoftware
|
||||||
|
|
||||||
## API-Beispiele
|
## API-Beispiele
|
||||||
|
|
||||||
Die Such-API unterstützt folgende Parameter:
|
### Suche nach Name
|
||||||
|
|
||||||
### Spezifische Suche
|
|
||||||
```bash
|
```bash
|
||||||
# Nach Name suchen
|
curl "http://localhost:5001/search?name=Mustermann"
|
||||||
curl "http://localhost:5001/search?name=Schmidt"
|
```
|
||||||
|
|
||||||
# Nach Ort suchen
|
### Suche nach Ort
|
||||||
|
```bash
|
||||||
curl "http://localhost:5001/search?ort=Berlin"
|
curl "http://localhost:5001/search?ort=Berlin"
|
||||||
|
```
|
||||||
|
|
||||||
# Nach Kundennummer suchen
|
### Suche nach Kundennummer
|
||||||
|
```bash
|
||||||
curl "http://localhost:5001/search?kundennummer=12345"
|
curl "http://localhost:5001/search?kundennummer=12345"
|
||||||
|
```
|
||||||
|
|
||||||
# Nach Fachrichtung suchen
|
### Suche nach Fachrichtung
|
||||||
curl "http://localhost:5001/search?fachrichtung=Allgemeinmedizin"
|
```bash
|
||||||
|
curl "http://localhost:5001/search?fachrichtung=Zahnarzt"
|
||||||
|
```
|
||||||
|
|
||||||
# Nach Telefonnummer suchen
|
### Suche nach Telefon
|
||||||
|
```bash
|
||||||
curl "http://localhost:5001/search?telefon=030"
|
curl "http://localhost:5001/search?telefon=030"
|
||||||
|
|
||||||
# Kombinierte Suche
|
|
||||||
curl "http://localhost:5001/search?name=Schmidt&ort=Berlin&fachrichtung=Allgemeinmedizin"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Allgemeine Suche
|
### Allgemeine Suche
|
||||||
```bash
|
```bash
|
||||||
# Suche in allen Feldern
|
curl "http://localhost:5001/search?q=Suchbegriff"
|
||||||
curl "http://localhost:5001/search?q=Schmidt"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Beispiel-Response
|
### Kombinierte Suche
|
||||||
```json
|
```bash
|
||||||
[
|
# Suche nach Fachrichtung und Ort
|
||||||
{
|
curl "http://localhost:5001/search?fachrichtung=Zahnarzt&ort=Berlin"
|
||||||
"Vorname": "Max",
|
|
||||||
"Nachname": "Mustermann",
|
# Suche nach Name und Telefon
|
||||||
"Nummer": "12345",
|
curl "http://localhost:5001/search?name=Mustermann&telefon=030"
|
||||||
"Ort": "Berlin",
|
|
||||||
"Fachrichtung": "Allgemeinmedizin",
|
# Suche nach mehreren Kriterien
|
||||||
"Tel": "030123456",
|
curl "http://localhost:5001/search?fachrichtung=Zahnarzt&ort=Berlin&name=Schmidt"
|
||||||
"Email": "max@example.com"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Versionen
|
## Version
|
||||||
|
|
||||||
### v1.0.1
|
Aktuelle Version: [1.0.5](CHANGELOG.md#105---2024-03-17)
|
||||||
- Telefonnummer-Suchfeld hinzugefügt
|
|
||||||
- Reset-Icons für alle Suchfelder
|
|
||||||
- Verbesserte Positionierung der UI-Elemente
|
|
||||||
- Optimierte Suchlogik
|
|
||||||
- CSV-Datei in data-Verzeichnis verschoben
|
|
||||||
|
|
||||||
### v1.0.0
|
|
||||||
- Erste Version
|
|
||||||
- Grundlegende Suchfunktionalität
|
|
||||||
- Klickbare Links für Telefon, E-Mail und Adressen
|
|
||||||
- Share-Funktion für Suchergebnisse
|
|
61
app.py
61
app.py
@@ -13,7 +13,7 @@ logging.basicConfig(level=logging.DEBUG)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Version der Anwendung
|
# Version der Anwendung
|
||||||
VERSION = "1.0.5"
|
VERSION = "1.0.6"
|
||||||
|
|
||||||
# Pfad zur CSV-Datei
|
# Pfad zur CSV-Datei
|
||||||
CSV_FILE = "data/customers.csv"
|
CSV_FILE = "data/customers.csv"
|
||||||
@@ -68,10 +68,12 @@ def search():
|
|||||||
telefon = request.args.get('telefon', '').strip()
|
telefon = request.args.get('telefon', '').strip()
|
||||||
query = request.args.get('q', '').strip()
|
query = request.args.get('q', '').strip()
|
||||||
|
|
||||||
# Wenn keine spezifischen Suchkriterien angegeben sind, aber eine allgemeine Suche
|
# Initialisiere die Maske für die Filterung
|
||||||
if not any([name, ort, kundennummer, fachrichtung, telefon]) and query:
|
mask = pd.Series(True, index=df.index)
|
||||||
# Suche in allen relevanten Feldern
|
|
||||||
mask = (
|
# Wenn eine allgemeine Suche angegeben ist
|
||||||
|
if query:
|
||||||
|
query_mask = (
|
||||||
df['Vorname'].str.contains(query, case=False, na=False) |
|
df['Vorname'].str.contains(query, case=False, na=False) |
|
||||||
df['Nachname'].str.contains(query, case=False, na=False) |
|
df['Nachname'].str.contains(query, case=False, na=False) |
|
||||||
df['Ort'].str.contains(query, case=False, na=False) |
|
df['Ort'].str.contains(query, case=False, na=False) |
|
||||||
@@ -79,32 +81,31 @@ def search():
|
|||||||
df['Fachrichtung'].str.contains(query, case=False, na=False) |
|
df['Fachrichtung'].str.contains(query, case=False, na=False) |
|
||||||
df['Tel'].astype(str).str.contains(query, case=False, na=False)
|
df['Tel'].astype(str).str.contains(query, case=False, na=False)
|
||||||
)
|
)
|
||||||
else:
|
mask &= query_mask
|
||||||
# Spezifische Suche
|
|
||||||
mask = pd.Series(True, index=df.index)
|
# Spezifische Suchkriterien anwenden
|
||||||
|
if name:
|
||||||
if name:
|
name_mask = (
|
||||||
name_mask = (
|
df['Vorname'].str.contains(name, case=False, na=False) |
|
||||||
df['Vorname'].str.contains(name, case=False, na=False) |
|
df['Nachname'].str.contains(name, case=False, na=False)
|
||||||
df['Nachname'].str.contains(name, case=False, na=False)
|
)
|
||||||
)
|
mask &= name_mask
|
||||||
mask &= name_mask
|
|
||||||
|
if ort:
|
||||||
if ort:
|
ort_mask = df['Ort'].str.contains(ort, case=False, na=False)
|
||||||
ort_mask = df['Ort'].str.contains(ort, case=False, na=False)
|
mask &= ort_mask
|
||||||
mask &= ort_mask
|
|
||||||
|
if kundennummer:
|
||||||
if kundennummer:
|
kundennummer_mask = df['Nummer'].astype(str).str.contains(kundennummer, case=False, na=False)
|
||||||
kundennummer_mask = df['Nummer'].astype(str).str.contains(kundennummer, case=False, na=False)
|
mask &= kundennummer_mask
|
||||||
mask &= kundennummer_mask
|
|
||||||
|
if fachrichtung:
|
||||||
if fachrichtung:
|
fachrichtung_mask = df['Fachrichtung'].str.contains(fachrichtung, case=False, na=False)
|
||||||
fachrichtung_mask = df['Fachrichtung'].str.contains(fachrichtung, case=False, na=False)
|
mask &= fachrichtung_mask
|
||||||
mask &= fachrichtung_mask
|
|
||||||
|
|
||||||
if telefon:
|
if telefon:
|
||||||
telefon_mask = df['Tel'].astype(str).str.contains(telefon, case=False, na=False)
|
telefon_mask = df['Tel'].astype(str).str.contains(telefon, case=False, na=False)
|
||||||
mask &= telefon_mask
|
mask &= telefon_mask
|
||||||
|
|
||||||
results = df[mask].to_dict('records')
|
results = df[mask].to_dict('records')
|
||||||
logger.info(f"{len(results)} Ergebnisse gefunden")
|
logger.info(f"{len(results)} Ergebnisse gefunden")
|
||||||
|
@@ -139,18 +139,6 @@
|
|||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.weather-info {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-left: 10px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
.weather-info img {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -246,10 +234,7 @@
|
|||||||
|
|
||||||
function createCustomerLink(customerNumber) {
|
function createCustomerLink(customerNumber) {
|
||||||
if (!customerNumber) return 'N/A';
|
if (!customerNumber) return 'N/A';
|
||||||
return `<a href="medisw:openkkbefe/P${customerNumber}?NetGrp=4"
|
return `<a href="medisw:openkkbefe/P${customerNumber}?NetGrp=4" class="customer-link">${customerNumber}</a>`;
|
||||||
class="customer-link" target="_blank" rel="noopener noreferrer">
|
|
||||||
${customerNumber}
|
|
||||||
</a>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showCopyFeedback() {
|
function showCopyFeedback() {
|
||||||
@@ -326,22 +311,16 @@
|
|||||||
data.results.forEach(customer => {
|
data.results.forEach(customer => {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'card mb-3';
|
card.className = 'card mb-3';
|
||||||
|
const customerLink = createCustomerLink(customer.Nummer);
|
||||||
|
console.log('Customer:', customer); // Debug-Ausgabe
|
||||||
|
console.log('Customer link:', customerLink); // Debug-Ausgabe
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">${customer.Vorname} ${customer.Nachname}</h5>
|
<h5 class="card-title">${customer.Vorname} ${customer.Nachname}</h5>
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
<strong>Kundennummer:</strong> ${customer.Nummer}<br>
|
<strong>Kundennummer:</strong> ${customerLink}<br>
|
||||||
<strong>Fachrichtung:</strong> ${customer.Fachrichtung || 'N/A'}<br>
|
<strong>Fachrichtung:</strong> ${customer.Fachrichtung || 'N/A'}<br>
|
||||||
<strong>Adresse:</strong> ${createAddressLink(customer.Strasse, customer.PLZ, customer.Ort)}
|
<strong>Adresse:</strong> ${createAddressLink(customer.Strasse, customer.PLZ, customer.Ort)}<br>
|
||||||
${customer.weather ? `
|
|
||||||
<span class="weather-info">
|
|
||||||
<img src="http://openweathermap.org/img/wn/${customer.weather.icon}@2x.png"
|
|
||||||
alt="${customer.weather.description}"
|
|
||||||
title="${customer.weather.description}">
|
|
||||||
${customer.weather.temperature}°C
|
|
||||||
</span>
|
|
||||||
` : ''}
|
|
||||||
<br>
|
|
||||||
<strong>Telefon:</strong> ${createPhoneLink(customer.Tel)}<br>
|
<strong>Telefon:</strong> ${createPhoneLink(customer.Tel)}<br>
|
||||||
<strong>E-Mail:</strong> ${createEmailLink(customer.mail)}
|
<strong>E-Mail:</strong> ${createEmailLink(customer.mail)}
|
||||||
</p>
|
</p>
|
||||||
|
Reference in New Issue
Block a user