Importuj aktywności z pliku CSV

'; // Handle the form submission if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['statpress_csv_import_nonce_field'] ) ) { statpress_handle_csv_import(); } // Display the form ?>

Instrukcje i formularz importu

Aby zaimportować dane, możesz wgrać plik CSV LUB wkleić dane bezpośrednio w pole tekstowe poniżej. Ta druga opcja jest zalecana, jeśli napotykasz błędy z plikiem.

Wymagane kolumny: Data, Tytuł, Dystans oraz Typ aktywności (lub Kategoria).

Opcjonalne kolumny: Czas (format HH:MM:SS), Kalorie, Średnie tętno, Maksymalne tętno, Średnia prędkość, Maksymalna prędkość, Średni rytm pedałowania, Maksymalny rytm pedałowania, Całkowity wznios, Całkowity spadek, Minimalna wysokość, Maksymalna wysokość, Sprzęt, Typ wydarzenia.

Ważne:


'; } function statpress_handle_csv_import() { global $wpdb; if ( ! isset( $_POST['statpress_csv_import_nonce_field'] ) || ! wp_verify_nonce( $_POST['statpress_csv_import_nonce_field'], 'statpress_csv_import_nonce' ) ) { echo '

Błąd weryfikacji bezpieczeństwa.

'; return; } if ( ! current_user_can( 'manage_options' ) ) { echo '

Nie masz wystarczających uprawnień.

'; return; } // Unify input source: prefer textarea, fall back to file upload. $csv_content = ''; if ( ! empty( $_POST['statpress_csv_data'] ) ) { $csv_content = stripslashes( $_POST['statpress_csv_data'] ); } elseif ( ! empty( $_FILES['statpress_csv_file']['tmp_name'] ) && UPLOAD_ERR_OK === $_FILES['statpress_csv_file']['error'] ) { $csv_content = file_get_contents( $_FILES['statpress_csv_file']['tmp_name'] ); } if ( empty( trim( $csv_content ) ) ) { echo '

Nie podano danych do importu. Wgraj plik lub wklej dane w pole tekstowe.

'; return; } // Mapowanie polskich i angielskich nazw kolumn na wewnętrzne klucze $column_map = array( // Polish => English 'typ aktywności' => 'category_name', 'data' => 'date', 'tytuł' => 'title', 'dystans' => 'distance', 'kalorie' => 'calories', 'czas' => 'duration', 'średnie tętno' => 'avg_heart_rate', 'maksymalne tętno' => 'max_heart_rate', 'średnia prędkość' => 'avg_speed', 'maksymalna prędkość' => 'max_speed', 'średni rytm pedałowania' => 'avg_cadence', 'maksymalny rytm pedałowania' => 'max_cadence', 'całkowity wznios' => 'total_elevation_gain', 'całkowity spadek' => 'total_elevation_loss', 'minimalna wysokość' => 'min_altitude', 'maksymalna wysokość' => 'max_altitude', 'sprzęt' => 'equipment_name', 'typ wydarzenia' => 'event_type_name', 'komentarz' => 'comment', 'link do strava' => 'strava_url', // English keys for compatibility 'category_name' => 'category_name', 'date' => 'date', 'title' => 'title', 'distance' => 'distance', 'calories' => 'calories', 'duration' => 'duration', 'avg_heart_rate' => 'avg_heart_rate', 'max_heart_rate' => 'max_heart_rate', 'avg_speed' => 'avg_speed', 'max_speed' => 'max_speed', 'avg_cadence' => 'avg_cadence', 'max_cadence' => 'max_cadence', 'total_elevation_gain' => 'total_elevation_gain', 'total_elevation_loss' => 'total_elevation_loss', 'min_altitude' => 'min_altitude', 'max_altitude' => 'max_altitude', 'equipment_name' => 'equipment_name', 'event_type_name' => 'event_type_name', 'comment' => 'comment', 'strava_url' => 'strava_url', ); // --- START: Robust, case-insensitive lookup --- $table_categories = $wpdb->prefix . 'statpress_categories'; $table_event_types = $wpdb->prefix . 'statpress_event_types'; $table_equipment = $wpdb->prefix . 'statpress_equipment'; $create_lookup = function( $table_name ) use ( $wpdb ) { $items = $wpdb->get_results( "SELECT id, name FROM {$table_name}" ); $lookup = array(); if ( is_array( $items ) ) { foreach ( $items as $item ) { // Use a robust trim to handle various whitespace characters and make it case-insensitive $clean_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $item->name ); $lookup[ mb_strtolower( $clean_name, 'UTF-8' ) ] = $item->id; } } return $lookup; }; $categories_lookup = $create_lookup( $table_categories ); $event_types_lookup = $create_lookup( $table_event_types ); $equipment_lookup = $create_lookup( $table_equipment ); // --- END: Robust, case-insensitive lookup --- // Process the CSV file $table_activities = $wpdb->prefix . 'statpress_activities'; $imported_count = 0; $skipped_details = array(); $row_number = 1; // Header is row 1 // Normalize line endings and split into lines $lines = str_replace( array( "\r\n", "\r" ), "\n", $csv_content ); $lines = explode( "\n", $lines ); if ( empty( $lines ) || empty( trim( $lines[0] ) ) ) { echo '

Podane dane CSV są puste.

'; return; } // --- Delimiter and BOM detection --- $first_line = $lines[0]; $delimiter = ( substr_count( $first_line, ';' ) > substr_count( $first_line, ',' ) ) ? ';' : ','; // --- BOM removal from first header element --- $bom = "\xEF\xBB\xBF"; if ( substr( $first_line, 0, 3 ) === $bom ) { $lines[0] = substr( $first_line, 3 ); } $header_raw = str_getcsv( array_shift( $lines ), $delimiter ); $header_raw = array_map( 'trim', $header_raw ); // Translate header from Polish/English to internal keys $header = array(); foreach ( $header_raw as $col ) { $header[] = $column_map[ strtolower( $col ) ] ?? 'ignored_' . uniqid(); } $required_internal_keys = array( 'date', 'title', 'category_name', 'distance' ); $missing_keys = array_diff( $required_internal_keys, $header ); if ( ! empty( $missing_keys ) ) { $key_to_polish_map = array( 'date' => 'Data', 'title' => 'Tytuł', 'category_name' => 'Kategoria / Typ aktywności', 'distance' => 'Dystans', ); $missing_polish_names = array_map( fn( $key ) => $key_to_polish_map[ $key ] ?? $key, $missing_keys ); echo '

Brak wymaganych kolumn w danych CSV: ' . esc_html( implode( ', ', $missing_polish_names ) ) . '

'; return; } $parse_and_round_int = fn( $val ) => round( floatval( str_replace( ',', '.', $val ) ) ); $null_if_empty = fn( $value ) => '' !== $value ? $value : null; foreach ( $lines as $line ) { $row_number++; if ( empty( trim( $line ) ) ) { continue; // Skip empty lines } $data = str_getcsv( $line, $delimiter ); if ( count( $data ) !== count( $header ) ) { $skipped_details[] = array( 'row' => $row_number, 'reason' => 'Nieprawidłowa liczba kolumn (oczekiwano ' . count( $header ) . ', otrzymano ' . count( $data ) . ').', 'data' => $line, ); continue; } $row_data = array_combine( $header, $data ); // Detailed validation $validation_errors = array(); if ( empty( $row_data['date'] ) ) { $validation_errors[] = 'brak daty'; } if ( empty( $row_data['title'] ) ) { $validation_errors[] = 'brak tytułu'; } if ( ! isset( $row_data['distance'] ) || '' === $row_data['distance'] ) { $validation_errors[] = 'brak dystansu'; } $category_name = $row_data['category_name'] ?? ''; $clean_category_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $category_name ); $category_id = $categories_lookup[ mb_strtolower( $clean_category_name, 'UTF-8' ) ] ?? null; if ( empty( $clean_category_name ) ) { $validation_errors[] = 'brak nazwy kategorii'; } elseif ( is_null( $category_id ) ) { $available_categories_from_db = $wpdb->get_col( "SELECT name FROM $table_categories ORDER BY name" ); $validation_errors[] = 'nieznana kategoria: "' . esc_html( $category_name ) . '". Sprawdź, czy nazwa jest poprawna. Dostępne w bazie: "' . esc_html( implode( '", "', $available_categories_from_db ) ) . '".'; } if ( ! empty( $validation_errors ) ) { $skipped_details[] = array( 'row' => $row_number, 'reason' => ucfirst( implode( ', ', $validation_errors ) ) . '.', 'data' => $line, ); continue; } // Get IDs for optional fields using the same robust method $get_id = function( $name, $lookup_table ) { $clean_name = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $name ); return $lookup_table[ mb_strtolower( $clean_name, 'UTF-8' ) ] ?? null; }; $equipment_id = $get_id( $row_data['equipment_name'] ?? '', $equipment_lookup ); $event_type_id = $get_id( $row_data['event_type_name'] ?? '', $event_types_lookup ); $insert_data = array( 'date' => sanitize_text_field( $row_data['date'] ), 'title' => sanitize_text_field( $row_data['title'] ), 'category_id' => $category_id, 'distance' => floatval( str_replace( ',', '.', $row_data['distance'] ) ), 'duration' => isset( $row_data['duration'] ) ? sanitize_text_field( $row_data['duration'] ) : '00:00:00', 'calories' => isset( $row_data['calories'] ) ? intval( str_replace( ',', '.', $row_data['calories'] ) ) : 0, 'comment' => isset( $row_data['comment'] ) ? sanitize_textarea_field( $row_data['comment'] ) : null, 'strava_url' => isset( $row_data['strava_url'] ) ? $null_if_empty( esc_url_raw( $row_data['strava_url'] ) ) : null, 'avg_heart_rate' => isset( $row_data['avg_heart_rate'] ) ? $null_if_empty( $parse_and_round_int( $row_data['avg_heart_rate'] ) ) : null, 'max_heart_rate' => isset( $row_data['max_heart_rate'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_heart_rate'] ) ) : null, 'avg_speed' => isset( $row_data['avg_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $row_data['avg_speed'] ) ) ) : null, 'max_speed' => isset( $row_data['max_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $row_data['max_speed'] ) ) ) : null, 'avg_cadence' => isset( $row_data['avg_cadence'] ) ? $null_if_empty( $parse_and_round_int( $row_data['avg_cadence'] ) ) : null, 'max_cadence' => isset( $row_data['max_cadence'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_cadence'] ) ) : null, 'total_elevation_gain' => isset( $row_data['total_elevation_gain'] ) ? $null_if_empty( $parse_and_round_int( $row_data['total_elevation_gain'] ) ) : null, 'total_elevation_loss' => isset( $row_data['total_elevation_loss'] ) ? $null_if_empty( $parse_and_round_int( $row_data['total_elevation_loss'] ) ) : null, 'min_altitude' => isset( $row_data['min_altitude'] ) ? $null_if_empty( $parse_and_round_int( $row_data['min_altitude'] ) ) : null, 'max_altitude' => isset( $row_data['max_altitude'] ) ? $null_if_empty( $parse_and_round_int( $row_data['max_altitude'] ) ) : null, 'equipment_id' => $equipment_id, 'event_type_id' => $event_type_id, ); if ( $wpdb->insert( $table_activities, $insert_data ) ) { $imported_count++; } else { $skipped_details[] = array( 'row' => $row_number, 'reason' => 'Błąd zapisu do bazy danych. (' . esc_html( $wpdb->last_error ) . ')', 'data' => $line, ); } } if ( $imported_count > 0 ) { echo '

Pomyślnie zaimportowano ' . esc_html( $imported_count ) . ' aktywności.

';} if ( ! empty( $skipped_details ) ) { echo '
'; echo '

Pominięto ' . count( $skipped_details ) . ' wierszy z powodu błędów

'; echo '
'; echo ''; foreach ( $skipped_details as $error ) { echo ''; echo ''; echo ''; echo ''; echo ''; } echo '
WierszPowód pominięciaDane wiersza
' . esc_html( $error['row'] ) . '' . esc_html( $error['reason'] ) . '' . esc_html( wp_trim_words( $error['data'], 25, '...' ) ) . '
'; echo '
'; } if ( 0 === $imported_count && empty( $skipped_details ) && $row_number > 1 ) { echo '

Dane CSV nie zawierały żadnych poprawnych wierszy do importu.

'; } elseif ( 1 === $row_number ) { echo '

Dane CSV były puste lub zawierały tylko nagłówek.

';} }