Сделай чтобы я мог нажать на ячейку столбца "Номер" в ней же изменил значение нажал enter , данные сохранились и это значение обновилось старого нету, есть новое.
import os
import asyncio
import pywebio
import threading
import subprocess
import sqlite3
import datetime
from pywebio import start_server
from pywebio import output, pin
from pywebio.input import input, input_group
from pywebio.output import put_table, put_html, clear, put_button, popup
from pywebio.session import run_js, set_env, run_async
from pywebio.pin import *
from collections import defaultdict
from pywebio.input import input 



DB_PATH = r'C:\\Users\\B-ZONE\\Desktop\\Main\\pydatabase.db'
# Добавляем блокировку для безопасного доступа к общим ресурсам
db_lock = threading.Lock()
# Для хранения активных сессий
active_sessions = defaultdict(set)
last_update = []
stored_value = ''



def remove_footer():
    run_js('''function removeFooter() {
        var footer = document.querySelector('.footer');
        if (footer) {
            footer.remove();
        }
    }
    
    setTimeout(removeFooter, 1000);
    ''')

async def edit_cell_handler():
    while True:
        try:
            changed = await pin_wait_change('edit_cell_data')
            if changed:
                data = eval(changed)
                user_id = data['userId']
                new_number = data['newValue']
                print(f"Received edit request for user {user_id} with new number {new_number}")  # Debug message
                result = edit_cell(user_id, new_number)
                if result:
                    await update_table()  # Обновляем таблицу после изменения
                pin['edit_cell_result'] = 'success' if result else 'error'
        except Exception as e:
            print(f"Error in edit_cell_handler: {e}")
            pin['edit_cell_result'] = 'error'




def edit_cell(user_id, new_number):
    try:
        with db_lock:
            conn = sqlite3.connect(DB_PATH)
            c = conn.cursor()
            c.execute("UPDATE users SET number = ? WHERE id = ?", (new_number, user_id))
            conn.commit()
            print(f"Updated user {user_id} with new number {new_number}")  # Debug message
            conn.close()
        return True
    except Exception as e:
        print(f"Error updating number: {e}")
        return False




def create_display_cell(user_id, number):
    return put_html(f'''
        <div class="editable-cell" data-user-id="{user_id}" data-number="{number}">
            <span class="display-value" onclick="startEdit(this)">{number}</span>
            <input type="text" class="edit-input" style="display:none" value="{number}"
                   onblur="finishEdit(this)" onkeypress="handleKeyPress(event, this)">
        </div>
    ''')




def create_database():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    
    c.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='users'")
    table_exists = c.fetchone()
    
    if not table_exists:
        c.execute('''CREATE TABLE users(
                      id INTEGER PRIMARY KEY AUTOINCREMENT,
                      timestamp TEXT,
                      number TEXT,
                      test1 TEXT,
                      test2 TEXT,
                      name TEXT,
                      packing_time TEXT,
                      last_graf TEXT,
                      is_duplicate INTEGER DEFAULT 0)''')  # New column for duplicates
    conn.commit()
    conn.close()


def add_js_handlers():
    run_js("""
    window.startEdit = function(element) {
        var cell = element.closest('.editable-cell');
        var input = cell.querySelector('.edit-input');
        var display = cell.querySelector('.display-value');
        
        display.style.display = 'none';
        input.style.display = 'block';
        input.focus();
        input.select();
    }

    window.finishEdit = function(input) {
    var cell = input.closest('.editable-cell');
    var display = cell.querySelector('.display-value');
    var userId = cell.getAttribute('data-user-id');
    var newValue = input.value;

    if (!isNaN(newValue) && newValue.trim() !== '') {
        display.textContent = newValue;
        display.style.display = 'block';
        input.style.display = 'none';

        // Сохраняем новое значение в базе данных через PyWebIO
        WebIO.pin.set('edit_cell_data', JSON.stringify({
            userId: userId, 
            newValue: newValue
        }));
    } else {
        alert('Пожалуйста, введите корректное число');
        input.value = display.textContent;
        display.style.display = 'block';
        input.style.display = 'none';
    }
}


    window.handleKeyPress = function(event, input) {
        if (event.key === 'Enter') {
            finishEdit(input);
        }
    }

    // Call loadValues when the page loads
    window.onload = function() {
        loadValues();
    }
    """)



def add_styles():
    put_html("""
    <style>
    .editable-cell {
        cursor: pointer;
        padding: 5px;
    }
    .editable-cell:hover {
        background-color: #5b92e5;
    }
    .edit-input {
        width: 100%;
        padding: 4px;
        border: 1px solid #ddd;
        border-radius: 4px;
    }
    .display-value {
        display: inline-block;
        min-width: 30px;
    }
    </style>
    """)


def save_user(name, number, test1):
    with db_lock:
        print(f"Saving user: name={name}, number={number}, test1={test1}")
        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        
        # Check if the number already exists
        c.execute("SELECT COUNT(*) FROM users WHERE number = ?", (number,))
        count = c.fetchone()[0]
        
        # If it exists, mark it as a duplicate
        if count > 0:
            is_duplicate = 1
        else:
            is_duplicate = 0
        
        current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        c.execute("INSERT INTO users (name, number, test1, timestamp, packing_time, last_graf, is_duplicate) VALUES (?,?,?,?,?,?,?)", 
                 (name, number, test1, current_time, '', '0', is_duplicate))
        conn.commit()
        conn.close()



def get_users():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT id, timestamp, number, test1, test2, name, packing_time, last_graf FROM users")
    users = c.fetchall()
    conn.close()
    return users

async def update_packing_time(user_id):
    try:
        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        c.execute("UPDATE users SET packing_time = ? WHERE id = ?", (current_time, user_id))
        conn.commit()
        conn.close()
        
      
        await update_table()  # Обновляем таблицу после упаковки
        return current_time
    except Exception as e:
        print(f"Error in update_packing_time: {e}")
        return None

def update_user_packing_time(user_id):
    update_packing_time(user_id)
    run_async(update_table())


def update_test2(user_id, test2_value):
    with db_lock:
        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        c.execute("UPDATE users SET test2 =? WHERE id =?", (test2_value, user_id))
        conn.commit()
        conn.close()


def get_last_graf():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT last_graf FROM users ORDER BY id ASC LIMIT 1")
    try:
        last_graf_value = c.fetchone()[0]
    except TypeError:
        last_graf_value = 0
    conn.close()
    return last_graf_value


async def gen_rafin_number(user_id):
    try:
        start = get_last_graf()
        test2_value = str(int(start)+1)
        while len(test2_value) < 6:
            test2_value = "0"+test2_value


        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        c.execute("UPDATE users SET test2 = ? WHERE id = ?", ("DL"+test2_value, user_id))
        c.execute("SELECT id FROM users ORDER BY id ASC LIMIT 1")
        first_user_id = c.fetchone()[0]
        c.execute("UPDATE users SET last_graf =? WHERE id =?", (str(int(start)+1), first_user_id))
        # c.execute("UPDATE users SET last_graf = ? WHERE id = 1", (start, user_id))
        conn.commit()
        conn.close()
        
        await update_table()  # Обновляем таблицу после упаковки
        return "DL"+test2_value
    except Exception as e:
        print(f"Error in update_packing_time: {e}")
        return None



async def update_number(user_id, new_number):
    try:
        with db_lock:
            conn = sqlite3.connect(DB_PATH)
            c = conn.cursor()
            c.execute("UPDATE users SET number = ? WHERE id = ?", (new_number, user_id))
            conn.commit()
            conn.close()
        await update_table()  # Обновляем таблицу после изменения
        return True
    except Exception as e:
        print(f"Error updating number: {e}")
        return False




async def add_record(user_name):
    data = await input_group("", [
        input('Имя', name='name', value=user_name, readonly=True),
        input('Номер', name='number'),
        input('Тест 1', name='test1')
    ])
    save_user(user_name, data['number'], data['test1'])
    remove_footer()


def count_total_users():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT COUNT(*) FROM users WHERE packing_time IS NOT NULL AND packing_time != ''")
    total_count = c.fetchone()[0]
    conn.close()
    return total_count


def count_users_last_hour():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    one_hour_ago = (datetime.datetime.now() - datetime.timedelta(hours=1)).strftime("%Y-%m-%d %H:%M:%S")
    c.execute("""
        SELECT COUNT(*) FROM users 
        WHERE packing_time IS NOT NULL 
        AND packing_time != '' 
        AND packing_time >= ?
    """, (one_hour_ago,))
    hour_count = c.fetchone()[0]
    conn.close()
    return hour_count

def add_scroll_handler():
    run_js('''
        function updateScroll() {
            var container = document.querySelector('.table');
            if (container && window.lastScrollPosition) {
                container.scrollTop = window.lastScrollPosition;
            }
        }
        
        // Обновляем позицию прокрутки при любых изменениях DOM
        var observer = new MutationObserver(function(mutations) {
            updateScroll();
        });
        
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    ''')



async def update_table():
    global last_update
    while True: 
        users = get_users()
        if users != last_update:
            run_js('''
                var container = document.querySelector('table');
                if (container) {
                    window.lastScrollPosition = container.scrollTop;
                }
            ''')
            clear()
          
            put_html('''
                <div id="main-container">
                    <h1 style="text-align: center; margin-bottom: 20px; color: #333;">Рабочая таблица</h1>
                    <div style="text-align: center; position: relative;">
                        <strong style="position: relative; margin-left: 700px">Общее количество записей: {}</strong><br>
                        <strong style="position: relative; margin-left: 620px">Количество записей за последний час: {}</strong>
                    </div>
                </div>
            '''.format(count_total_users(), count_users_last_hour()))

            # Создаем таблицу
            table = [['№', 'Время отправки', 'Номер', 'Serial', 'Графа', 'Имя отправителя', 'Время упаковки', 'last_graf']]
            
            # Множество для хранения уникальных номеров
            unique_numbers = set()
            duplicate_numbers = set()

            for user in users:
                user_id, timestamp, number, test1, test2, name, packing_time, last_graf = user
                if number in unique_numbers:
                    duplicate_numbers.add(number)
                else:
                    unique_numbers.add(number)

            for user in users:
                user_id, timestamp, number, test1, test2, name, packing_time , last_graf = user
                if not packing_time:
                    packing_button = put_button(
                        "Упаковать", 
                        onclick=lambda uid=user_id: run_async(update_packing_time(uid))
                    )
                else:
                    packing_button = packing_time

                if test2:
                    test2_cell = test2
                else:
                    test2_cell = put_button("Генерация", 
                                            
                                          onclick=lambda user_id=user_id: run_async(gen_rafin_number(user_id)))

          
                if number in duplicate_numbers:
                    number_cell = put_html(f'<div style="background-color: #ffcccc; padding: 5px; border-radius: 4px;">{number}</div>')
                else:
                    number_cell = create_display_cell(user_id, number)

                
                table.append([
                    user_id, 
                    timestamp, 
                    number_cell, 
                    test1, 
                    test2_cell, 
                    name, 
                    packing_button, 
                    last_graf
                ])
            put_html('''
                     <style>
                body {
                    display: flex;
                    justify-content: center;
                    align-items: flex-start;
                    min-height: 100vh;
                    margin-left: 0px;
                    padding: 20px;
                    box-sizing: border-box;
                }
             .container {
                    width: 100%;
                    max-width: 1000px;
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                }
             .centered-table {
                    position: relative;
                    width: 100%;
                    max-height: 400px; /* Ограничение высоты таблицы */
                    overflow-y: scroll; /* Включение вертикальной прокрутки */
                    margin-bottom: 20px;
                }
             .pywebio-table {
                    width: 100%;
                    border-collapse: collapse;
                }
             .pywebio-table th,.pywebio-table td {
                    border: 1px solid #ddd;
                    padding: 8px;
                    text-align: left;
                }
             .pywebio-table th {
                    background-color: #f2f2f2;
                }
                #data-form {
                    width: 100%;
                    max-width: 800px;
                }
             .input-container {
                    display: flex;
                    flex-direction: row;
                    justify-content: space-between;
                    margin-bottom: 15px;
                }
             .form-group {
                    flex: 1;
                    margin-right: 15px;
                }
             .form-group:last-child {
                    margin-right: 0;
                }
             .form-control {
                    width: 100%;
                    padding: 5px;
                    border: 1px solid #ccc;
                    border-radius: 4px;
                }
             .ws-form-submit-btns {
                    display: flex;
                    justify-content: flex-start;
                }
             .btn {
                    margin-right: 10px;
                    padding: 5px 10px;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                }
             .btn-primary {
                    background-color: #007bff;
                    color: white;
                }
             .btn-warning {
                    background-color: #ffc107;
                    color: black;
                }
                #pywebio-scope-ROOT table {
                    width: 100%;
                    height: 500px;
                    table-layout: fixed;
                    border-collapse: collapse;
                }
                #pywebio-scope-ROOT th,
                #pywebio-scope-ROOT td {
                    width: 1500px;
                    padding: 8px;
                    border: 1px solid #ddd;
                    text-align: left;
                    word-wrap: break-word;
                    overflow-wrap: break-word;
                }
                .editable-cell {
                    position: relative;
                    width: 100%;
                    height: 100%;
                }
                .editable-cell input {
                    width: 100%;
                    height: 100%;
                    padding: 5px;
                    box-sizing: border-box;
                }
                .editable-cell input:focus {
                    outline: 2px solid #007bff;
                    background: white;
                }
            </style>
            <div class="container">
                <div class="centered-table">''')
            put_html('''
                <div class="container">
                    <div class="centered-table" id="table-container">''')
            put_table(table)  # Обновляем только таблицу
            run_js('''
                setTimeout(function() {
                    var container = document.querySelector('table');
                    if (container && window.lastScrollPosition) {
                        container.scrollTop = window.lastScrollPosition;
                    }
                }, 50);
            ''')
    

            
            remove_footer()
            last_update = users
    
        await asyncio.sleep(0.1)
    

async def initialize_interface():
    clear()
    put_html('''
        <h1 style="text-align: center; margin-bottom: 20px; color: #333;">Рабочая таблица</h1>
    ''')
    run_js('''
                var container = document.querySelector('table');
                if (container) {
                    window.lastScrollPosition = container.scrollTop;
                }
            ''')
    users = get_users()  # Получаем всех пользователей из базы данных
    total_users = count_total_users()
    users_last_hour = count_users_last_hour()


    put_html(f'''
        <div style="text-align: center; margin-bottom: 20px;">
            <strong style="position: relative; margin-left: 700px">Общее количество записей: {total_users}</strong><br>
            <strong style="position: relative; margin-left: 620px">Количество записей за последний час: {users_last_hour}</strong>
        </div>
    ''')

    # Создаем таблицу
    table = [['№', 'Время отправки', 'Номер', 'Serial', 'Графа', 'Имя отправителя', 'Время упаковки', 'last_graf']]

    for user in users:
        user_id, timestamp, number, test1, test2, name, packing_time, last_graf = user
        if not packing_time:
            packing_button = put_button(
                "Упаковать", 
                onclick=lambda uid=user_id: run_async(update_packing_time(uid))
            )
        else:
            packing_button = packing_time

        if test2:
            test2_cell = test2
        else:
            test2_cell = put_button("Генерация", 
                                    onclick=lambda user_id=user_id: run_async(gen_rafin_number(user_id)))

        number_cell = create_display_cell(number, user_id)

        table.append([user_id, timestamp, number_cell, test1, test2_cell, name, packing_button, last_graf])
    put_html('''<style>
                body {
                    display: flex;
                    justify-content: center;
                    align-items: flex-start;
                    min-height: 100vh;
                    margin-left: 0px;
                    padding: 20px;
                    box-sizing: border-box;
                }
             .container {
                    width: 100%;
                    max-width: 1000px;
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                }
             .centered-table {
                    width: 100%;
                    max-height: 400px; /* Ограничение высоты таблицы */
                    overflow-y: auto; /* Включение вертикальной прокрутки */
                    margin-bottom: 20px;
                }
             .pywebio-table {
                    width: 100%;
                    border-collapse: collapse;
                }
             .pywebio-table th,.pywebio-table td {
                    border: 1px solid #ddd;
                    padding: 8px;
                    text-align: left;
                }
             .pywebio-table th {
                    background-color: #f2f2f2;
                }
                #data-form {
                    width: 100%;
                    max-width: 800px;
                }
             .input-container {
                    display: flex;
                    flex-direction: row;
                    justify-content: space-between;
                    margin-bottom: 15px;
                }
             .form-group {
                    flex: 1;
                    margin-right: 15px;
                }
             .form-group:last-child {
                    margin-right: 0;
                }
             .form-control {
                    width: 100%;
                    padding: 5px;
                    border: 1px solid #ccc;
                    border-radius: 4px;
                }
             .ws-form-submit-btns {
                    display: flex;
                    justify-content: flex-start;
                }
             .btn {
                    margin-right: 10px;
                    padding: 5px 10px;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                }
             .btn-primary {
                    background-color: #007bff;
                    color: white;
                }
             .btn-warning {
                    background-color: #ffc107;
                    color: black;
                }
                #pywebio-scope-ROOT table {
                    width: 100%;
                    height: 500px;
                    table-layout: fixed;
                    border-collapse: collapse;
                }
                #pywebio-scope-ROOT th,
                #pywebio-scope-ROOT td {
                    width: 1500px;
                    padding: 8px;
                    border: 1px solid #ddd;
                    text-align: left;
                    word-wrap: break-word;
                    overflow-wrap: break-word;
                }
                .editable-cell {
                    position: relative;
                    width: 100%;
                    height: 100%;
                }
                .editable-cell input {
                    width: 100%;
                    height: 100%;
                    padding: 5px;
                    box-sizing: border-box;
                }
                .editable-cell input:focus {
                    outline: 2px solid #007bff;
                    background: white;
                }
            </style>
            ''')

    put_table(table)  # Отображаем таблицу
    run_js('''
                setTimeout(function() {
                    var container = document.querySelector('table');
                    if (container && window.lastScrollPosition) {
                        container.scrollTop = window.lastScrollPosition;
                    }
                }, 50);
            ''')
    return users



async def main():
    add_scroll_handler()
    add_js_handlers()
    asyncio.create_task(edit_cell_handler())
    add_styles()
    global last_update
    remove_footer()
    set_env(output_animation=False)
    
    os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
    create_database()
    
    put_html('<div class="container">')
    user_name = await input("Введите ваше имя")
    last_update = []
    users = await initialize_interface()
    last_update = users
    # Запускаем обновление таблицы
    run_async(update_table())
    

    while True:
        try:
            run_js('''
                var scrollPosition = 0;
                var tableElement = document.querySelector('table');
                if (tableElement) {
                    scrollPosition = tableElement.scrollTop;
                    window.savedScrollPosition = scrollPosition;
                }
            ''')
            data = await input_group("Введите данные", [
                input('Имя', name='name', value=user_name, readonly=True),
                input('Номер', name='number'),
                input('Тест 1', name='test1')
            ], cancelable=True)

            if data is None:  # Если пользователь отменил ввод
                continue

            save_user(user_name, data['number'], data['test1'])
            await asyncio.sleep(0.1)  # Добавляем небольшую задержку для обновления таблицы
            run_js('''
                function restoreScroll() {
                    var tableElement = document.querySelector('table');
                    if (tableElement && window.savedScrollPosition) {
                        tableElement.scrollTop = window.savedScrollPosition;
                    }
                }
                // Пытаемся восстановить несколько раз с интервалом
                setTimeout(restoreScroll, 100);
            ''')
        except Exception as e:
            print(f"Error: {e}")
            await popup('Ошибка', f'Произошла ошибка: {str(e)}')


def kill_process_using_port(port):
    try:
        # Проверяем, запущен ли скрипт от имени root
        if os.geteuid() != 0:
            print("Этот скрипт должен быть запущен с правами суперпользователя (root).")
            return

        # Находим PID процесса, использующего указанный порт
        result = subprocess.run(['lsof', '-t', '-i', f':{port}'], capture_output=True, text=True)
        
        # Если процесс найден, убиваем его
        if result.stdout:
            pids = result.stdout.strip().split('\n')
            for pid in pids:
                os.kill(int(pid), 9)
                print(f'Процесс с PID {pid} на порту {port} был убит.')
        else:
            print(f'Нет процессов, использующих порт {port}.')
    
    except Exception as e:
        print(f'Ошибка при попытке убить процесс: {e}')

# Использование функции
kill_process_using_port(8080)


if __name__ == '__main__':
    port = 8080  # Измените порт, если 8080 занят
    kill_process_using_port(port)  # Убиваем процесс на порту перед запуском сервера
    pywebio.config(title="Рабочая таблица")
    start_server(main, port, debug=True, cdn=False)