import streamlit as st
import pandas as pd
import psycopg2
import re

def get_db_connection():
    """Подключение к PostgreSQL"""
    try:
        conn = psycopg2.connect(
            dbname=st.secrets['postgres_oko']['SQL_BD'],
            user=st.secrets['postgres_oko']['SQL_LOGIN'],
            password=st.secrets['postgres_oko']['SQL_PASS'],
            host=st.secrets['postgres_oko']['SQL_HOST'],
            port=st.secrets['postgres_oko']['SQL_PORT']
        )
        return conn
    except Exception as e:
        st.error(f"Ошибка подключения к базе данных: {e}")
        return None

def process_input(courier_id):
    """Обработка введенного ID курьера"""
    return courier_id.strip()

def get_courier_data(courier_id):
    """Получение данных о заказах курьера"""
    try:
        conn = get_db_connection()
        if not conn:
            return pd.DataFrame()

        query = """
        SELECT 
            create_dt as "Время создания",
            complete_dt as "Время завершения",
            courier_employee_id as "ID сотрудника",
            wb_courier_id as "ID курьера WB",
            address_str as "Адрес доставки",
            wbuser_id as "ID пользователя",
            srid as "SRID",
            price as "Стоимость",
            shk_id as "ШК"
        FROM public.courier_completed_task_result 
        WHERE wb_courier_id = %s OR courier_employee_id = %s
        ORDER BY create_dt DESC
        """

        df = pd.read_sql(query, conn, params=(courier_id, courier_id))
        return df

    except Exception as e:
        st.error(f"Ошибка выполнения запроса: {str(e)}")
        return pd.DataFrame()
    finally:
        if conn:
            conn.close()

def display_results(df):
    """Отображение результатов"""
    if df.empty:
        st.info("Данные не найдены")
        return

    # Форматирование временных полей
    for col in ["Время создания", "Время завершения"]:
        df[col] = pd.to_datetime(df[col]).dt.strftime('%Y-%m-%d %H:%M:%S')

    # Форматирование стоимости
    df["Стоимость"] = df["Стоимость"].round(2).apply(lambda x: f"{x:,.2f} ₽")

    # Основная статистика
    with st.expander("📊 Статистика"):
        col1, col2, col3 = st.columns(3)
        with col1:
            st.metric("Всего заказов", len(df))
        with col2:
            st.metric("Уникальных адресов", df["Адрес доставки"].nunique())
        with col3:
            total_price = df["Стоимость"].str.replace(' ₽', '').str.replace(',', '').astype(float).sum()
            st.metric("Общая стоимость", f"{total_price:,.2f} ₽")

    # Таблица с данными
    st.subheader("📋 Детали заказов")
    st.dataframe(
        df,
        hide_index=True,
        column_config={
            "Время создания": st.column_config.DatetimeColumn("Время создания", format="DD.MM.YYYY HH:mm:ss"),
            "Время завершения": st.column_config.DatetimeColumn("Время завершения", format="DD.MM.YYYY HH:mm:ss"),
            "Стоимость": st.column_config.TextColumn("Стоимость", width="medium"),
            "Адрес доставки": st.column_config.TextColumn("Адрес доставки", width="large"),
        },
        use_container_width=True
    )

    # Кнопка выгрузки
    csv = df.to_csv(index=False, encoding='utf-8-sig')
    st.download_button(
        label="📥 Скачать результаты",
        data=csv,
        file_name='courier_data.csv',
        mime='text/csv'
    )

def app():
    st.title("🔍 Поиск курьера")
    st.markdown("---")

    courier_id = st.text_input(
        "Введите ID курьера",
        placeholder="Пример: 123456",
        key="courier_input"
    )

    if st.button("🔎 Найти", key="search_button"):
        if not courier_id.strip():
            st.warning("⚠️ Введите ID курьера")
            return

        try:
            with st.spinner("🔄 Загрузка данных..."):
                # Получаем и отображаем данные
                courier_data = get_courier_data(courier_id)
                display_results(courier_data)

        except Exception as e:
            st.error(f"❌ Ошибка при выполнении запроса: {str(e)}")

Исправляем и доделываем модуль cour_shk:

В clients.py есть -
class RedashAPIClient:
    def __init__(self, source):
        self.base_url = st.secrets[source]["url"]
        self.api_key = st.secrets[source]["api_key"]

    @st.cache_data(ttl=3600, show_spinner="Выполнение API-запроса...")
    def execute_query(_self, query_id, params):
        headers = {'Authorization': f'Key {_self.api_key}'}
        try:
            response = requests.post(
                f"{_self.base_url}/api/queries/{query_id}/results",
                json={'parameters': params},
                headers=headers,
                timeout=30
            )
            response.raise_for_status()
            return _self._process_job(response.json(), headers)
        except requests.exceptions.RequestException as e:
            st.error(f"API Error: {str(e)}")
            logger.error(f"API Error: {str(e)}")
            return None

    def _process_job(self, job_data, headers):
        if job_data.get('query_result'):
            return job_data['query_result']['data']['rows']
        if job_data.get('job'):
            return self._poll_job(job_data['job']['id'], headers)
        st.error("Неверный формат ответа API")
        logger.error("Неверный формат ответа API")
        return None

    def _poll_job(self, job_id, headers):
        for _ in range(15):
            time.sleep(2)
            try:
                job_response = requests.get(
                    f"{self.base_url}/api/jobs/{job_id}",
                    headers=headers,
                    timeout=10
                )
                job_data = job_response.json()
            except Exception as e:
                st.error(f"Ошибка получения статуса задачи: {str(e)}")
                logger.error(f"Ошибка получения статуса задачи: {str(e)}")
                return None

            status = job_data.get('job', {}).get('status')
            if status == 3:
                return self._fetch_result(job_data['job']['query_result_id'], headers)
            elif status == 4:
                st.error("Ошибка выполнения запроса")
                logger.error("Ошибка выполнения запроса")
                return None
        st.error("Превышено время ожидания результата")
        logger.error("Превышено время ожидания результата")
        return None

    def _fetch_result(self, result_id, headers):
        try:
            result_response = requests.get(
                f"{self.base_url}/api/query_results/{result_id}",
                headers=headers,
                timeout=10
            )
            return result_response.json()['query_result']['data']['rows']
        except Exception as e:
            st.error(f"Ошибка получения результатов: {str(e)}")
            logger.error(f"Ошибка получения результатов: {str(e)}")
            return None

в secrets.toml :
[ext_delivery]
url = "http://ext-delivery-redash-svc-1.el.wb.ru"
api_key = "xxx"

URL запроса
http://ext-delivery-redash-svc-1.el.wb.ru/queries/6873/

тело запроса
select wb_courier_id,phone_number,supplier_id,courier_type_id,first_name,middle_name,last_name
from ext_delivery.courier_data
where wb_courier_id = '44426804'

Из полученного ответа из редаш собрать паспорт курьера.

 query = """
        SELECT 
            create_dt as "Время создания",
            complete_dt as "Время завершения",
            courier_employee_id as "ID сотрудника",
            wb_courier_id as "ID курьера WB",
            address_str as "Адрес доставки",
            wbuser_id as "ID пользователя",
            srid as "SRID",
            price as "Стоимость",
            shk_id as "ШК"
        FROM public.courier_completed_task_result 
        WHERE wb_courier_id = %s OR courier_employee_id = %s
        ORDER BY create_dt DESC
        """

При загрузке заказов нужно по create_dt сделать фильтр по умолчанию неделю от текущего момента, с возможностью последующего изменения, для загрузки за более длинный период.