6 Commits

4 changed files with 120 additions and 160 deletions

View File

@@ -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
View File

@@ -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

53
app.py
View File

@@ -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.4" 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)
if name: # Spezifische Suchkriterien anwenden
name_mask = ( if name:
df['Vorname'].str.contains(name, case=False, na=False) | name_mask = (
df['Nachname'].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)
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")

View File

@@ -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>