mirror of
https://github.com/pdxlocations/contact.git
synced 2026-08-09 18:33:00 +02:00
Database Refactor (#103)
* update sql db if nodedb is different * db refactor * don't insert dummy row * more refactoring * cleanup
This commit is contained in:
+114
-140
@@ -1,8 +1,7 @@
|
||||
|
||||
import sqlite3
|
||||
import time
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from utilities.utils import decimal_to_hex
|
||||
import default_config as config
|
||||
@@ -14,26 +13,22 @@ def get_table_name(channel):
|
||||
quoted_table_name = f'"{table_name}"' # Quote the table name becuase we begin with numerics and contain spaces
|
||||
return quoted_table_name
|
||||
|
||||
|
||||
def save_message_to_db(channel, user_id, message_text):
|
||||
"""Save messages to the database, ensuring the table exists."""
|
||||
try:
|
||||
quoted_table_name = get_table_name(channel)
|
||||
|
||||
schema = '''
|
||||
user_id TEXT,
|
||||
message_text TEXT,
|
||||
timestamp INTEGER,
|
||||
ack_type TEXT
|
||||
'''
|
||||
ensure_table_exists(quoted_table_name, schema)
|
||||
|
||||
with sqlite3.connect(config.db_file_path) as db_connection:
|
||||
db_cursor = db_connection.cursor()
|
||||
|
||||
quoted_table_name = get_table_name(channel)
|
||||
|
||||
# Ensure the table exists
|
||||
create_table_query = f'''
|
||||
CREATE TABLE IF NOT EXISTS {quoted_table_name} (
|
||||
user_id TEXT,
|
||||
message_text TEXT,
|
||||
timestamp INTEGER,
|
||||
ack_type TEXT
|
||||
)
|
||||
'''
|
||||
|
||||
db_cursor.execute(create_table_query)
|
||||
|
||||
timestamp = int(time.time())
|
||||
|
||||
# Insert the message
|
||||
@@ -48,10 +43,10 @@ def save_message_to_db(channel, user_id, message_text):
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logging.error(f"SQLite error in save_message_to_db: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error in save_message_to_db: {e}")
|
||||
|
||||
|
||||
def update_ack_nak(channel, timestamp, message, ack):
|
||||
try:
|
||||
with sqlite3.connect(config.db_file_path) as db_connection:
|
||||
@@ -74,15 +69,12 @@ def update_ack_nak(channel, timestamp, message, ack):
|
||||
logging.error(f"Unexpected error in update_ack_nak: {e}")
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
def load_messages_from_db():
|
||||
"""Load messages from the database for all channels and update globals.all_messages and globals.channel_list."""
|
||||
try:
|
||||
with sqlite3.connect(config.db_file_path) as db_connection:
|
||||
db_cursor = db_connection.cursor()
|
||||
|
||||
# Retrieve all table names that match the pattern
|
||||
query = "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE ?"
|
||||
db_cursor.execute(query, (f"{str(globals.myNodeNum)}_%_messages",))
|
||||
tables = [row[0] for row in db_cursor.fetchall()]
|
||||
@@ -105,7 +97,7 @@ def load_messages_from_db():
|
||||
# Extract the channel name from the table name
|
||||
channel = table_name.split("_")[1]
|
||||
|
||||
# Convert the channel to an integer if it's numeric, otherwise keep it as a string
|
||||
# Convert the channel to an integer if it's numeric, otherwise keep it as a string (nodenum vs channel name)
|
||||
channel = int(channel) if channel.isdigit() else channel
|
||||
|
||||
# Add the channel to globals.channel_list if not already present
|
||||
@@ -153,139 +145,121 @@ def load_messages_from_db():
|
||||
def init_nodedb():
|
||||
"""Initialize the node database and update it with nodes from the interface."""
|
||||
try:
|
||||
with sqlite3.connect(config.db_file_path) as db_connection:
|
||||
db_cursor = db_connection.cursor()
|
||||
if not globals.interface.nodes:
|
||||
return # No nodes to initialize
|
||||
|
||||
# Table name construction
|
||||
table_name = f"{str(globals.myNodeNum)}_nodedb"
|
||||
nodeinfo_table = f'"{table_name}"' # Quote the table name because it might begin with numerics
|
||||
ensure_node_table_exists() # Ensure the table exists before insertion
|
||||
|
||||
# Create the table if it doesn't exist
|
||||
create_table_query = f'''
|
||||
CREATE TABLE IF NOT EXISTS {nodeinfo_table} (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
long_name TEXT,
|
||||
short_name TEXT,
|
||||
hw_model TEXT,
|
||||
is_licensed TEXT,
|
||||
role TEXT,
|
||||
public_key TEXT
|
||||
)
|
||||
'''
|
||||
db_cursor.execute(create_table_query)
|
||||
# Insert or update all nodes
|
||||
for node in globals.interface.nodes.values():
|
||||
update_node_info_in_db(
|
||||
user_id=node['num'],
|
||||
long_name=node['user'].get('longName', ''),
|
||||
short_name=node['user'].get('shortName', ''),
|
||||
hw_model=node['user'].get('hwModel', ''),
|
||||
is_licensed=node['user'].get('isLicensed', '0'),
|
||||
role=node['user'].get('role', 'CLIENT'),
|
||||
public_key=node['user'].get('publicKey', '')
|
||||
)
|
||||
|
||||
# Iterate over nodes and insert them into the database
|
||||
if globals.interface.nodes:
|
||||
for node in globals.interface.nodes.values():
|
||||
role = node['user'].get('role', 'CLIENT')
|
||||
is_licensed = node['user'].get('isLicensed', '0')
|
||||
public_key = node['user'].get('publicKey', '')
|
||||
|
||||
insert_query = f'''
|
||||
INSERT OR IGNORE INTO {nodeinfo_table} (user_id, long_name, short_name, hw_model, is_licensed, role, public_key)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
|
||||
db_cursor.execute(insert_query, (
|
||||
node['num'],
|
||||
node['user']['longName'],
|
||||
node['user']['shortName'],
|
||||
node['user']['hwModel'],
|
||||
is_licensed,
|
||||
role,
|
||||
public_key
|
||||
))
|
||||
|
||||
db_connection.commit()
|
||||
logging.info("Node database initialized successfully.")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logging.error(f"SQLite error in init_nodedb: {e}")
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error in init_nodedb: {e}")
|
||||
|
||||
|
||||
def maybe_store_nodeinfo_in_db(packet):
|
||||
"""Save nodeinfo unless that record is already there."""
|
||||
"""Save nodeinfo unless that record is already there, updating if necessary."""
|
||||
try:
|
||||
with sqlite3.connect(config.db_file_path) as db_connection:
|
||||
|
||||
table_name = f"{str(globals.myNodeNum)}_nodedb"
|
||||
nodeinfo_table = f'"{table_name}"' # Quote the table name becuase we might begin with numerics
|
||||
db_cursor = db_connection.cursor()
|
||||
|
||||
# Check if a record with the same user_id already exists
|
||||
existing_record = db_cursor.execute(f'SELECT * FROM {nodeinfo_table} WHERE user_id=?', (packet['from'],)).fetchone()
|
||||
|
||||
if existing_record is None:
|
||||
role = packet['decoded']['user'].get('role', 'CLIENT')
|
||||
is_licensed = packet['decoded']['user'].get('isLicensed', '0')
|
||||
public_key = packet['decoded']['user'].get('publicKey', '')
|
||||
|
||||
# No existing record, insert the new record
|
||||
insert_query = f'''
|
||||
INSERT INTO {nodeinfo_table} (user_id, long_name, short_name, hw_model, is_licensed, role, public_key)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
|
||||
db_cursor.execute(insert_query, (
|
||||
packet['from'],
|
||||
packet['decoded']['user']['longName'],
|
||||
packet['decoded']['user']['shortName'],
|
||||
packet['decoded']['user']['hwModel'],
|
||||
is_licensed,
|
||||
role,
|
||||
public_key
|
||||
))
|
||||
|
||||
db_connection.commit()
|
||||
|
||||
else:
|
||||
# Check if values are different, update if necessary
|
||||
# Extract existing values
|
||||
existing_long_name = existing_record[1]
|
||||
existing_short_name = existing_record[2]
|
||||
existing_is_licensed = existing_record[4]
|
||||
existing_role = existing_record[5]
|
||||
existing_public_key = existing_record[6]
|
||||
|
||||
# Extract new values from the packet
|
||||
new_long_name = packet['decoded']['user']['longName']
|
||||
new_short_name = packet['decoded']['user']['shortName']
|
||||
new_is_licensed = packet['decoded']['user'].get('isLicensed', '0')
|
||||
new_role = packet['decoded']['user'].get('role', 'CLIENT')
|
||||
new_public_key = packet['decoded']['user'].get('publicKey', '')
|
||||
|
||||
# Check for any differences
|
||||
if (
|
||||
existing_long_name != new_long_name or
|
||||
existing_short_name != new_short_name or
|
||||
existing_is_licensed != new_is_licensed or
|
||||
existing_role != new_role or
|
||||
existing_public_key != new_public_key
|
||||
):
|
||||
# Perform necessary updates
|
||||
update_query = f'''
|
||||
UPDATE {nodeinfo_table}
|
||||
SET long_name = ?, short_name = ?, is_licensed = ?, role = ?, public_key = ?
|
||||
WHERE user_id = ?
|
||||
'''
|
||||
db_cursor.execute(update_query, (
|
||||
new_long_name,
|
||||
new_short_name,
|
||||
new_is_licensed,
|
||||
new_role,
|
||||
new_public_key,
|
||||
packet['from']
|
||||
))
|
||||
|
||||
db_connection.commit()
|
||||
|
||||
# TODO display new node name in nodelist
|
||||
user_id = packet['from']
|
||||
long_name = packet['decoded']['user']['longName']
|
||||
short_name = packet['decoded']['user']['shortName']
|
||||
hw_model = packet['decoded']['user']['hwModel']
|
||||
is_licensed = packet['decoded']['user'].get('isLicensed', '0')
|
||||
role = packet['decoded']['user'].get('role', 'CLIENT')
|
||||
public_key = packet['decoded']['user'].get('publicKey', '')
|
||||
|
||||
update_node_info_in_db(user_id, long_name, short_name, hw_model, is_licensed, role, public_key)
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logging.error(f"SQLite error in maybe_store_nodeinfo_in_db: {e}")
|
||||
finally:
|
||||
db_connection.close()
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error in maybe_store_nodeinfo_in_db: {e}")
|
||||
|
||||
|
||||
def update_node_info_in_db(user_id, long_name=None, short_name=None, hw_model=None, is_licensed=None, role=None, public_key=None):
|
||||
"""Update or insert node information into the database, preserving unchanged fields."""
|
||||
try:
|
||||
ensure_node_table_exists() # Ensure the table exists before any operation
|
||||
|
||||
with sqlite3.connect(config.db_file_path) as db_connection:
|
||||
db_cursor = db_connection.cursor()
|
||||
table_name = f'"{globals.myNodeNum}_nodedb"' # Quote in case of numeric names
|
||||
|
||||
# Fetch existing values to preserve unchanged fields
|
||||
db_cursor.execute(f'SELECT * FROM {table_name} WHERE user_id = ?', (user_id,))
|
||||
existing_record = db_cursor.fetchone()
|
||||
|
||||
if existing_record:
|
||||
existing_long_name, existing_short_name, existing_hw_model, existing_is_licensed, existing_role, existing_public_key = existing_record[1:]
|
||||
|
||||
long_name = long_name if long_name is not None else existing_long_name
|
||||
short_name = short_name if short_name is not None else existing_short_name
|
||||
hw_model = hw_model if hw_model is not None else existing_hw_model
|
||||
is_licensed = is_licensed if is_licensed is not None else existing_is_licensed
|
||||
role = role if role is not None else existing_role
|
||||
public_key = public_key if public_key is not None else existing_public_key
|
||||
|
||||
# Upsert logic
|
||||
upsert_query = f'''
|
||||
INSERT INTO {table_name} (user_id, long_name, short_name, hw_model, is_licensed, role, public_key)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
long_name = excluded.long_name,
|
||||
short_name = excluded.short_name,
|
||||
hw_model = excluded.hw_model,
|
||||
is_licensed = excluded.is_licensed,
|
||||
role = excluded.role,
|
||||
public_key = excluded.public_key
|
||||
'''
|
||||
db_cursor.execute(upsert_query, (user_id, long_name, short_name, hw_model, is_licensed, role, public_key))
|
||||
db_connection.commit()
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logging.error(f"SQLite error in update_node_info_in_db: {e}")
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error in update_node_info_in_db: {e}")
|
||||
|
||||
|
||||
def ensure_node_table_exists():
|
||||
"""Ensure the node database table exists."""
|
||||
table_name = f'"{globals.myNodeNum}_nodedb"' # Quote for safety
|
||||
schema = '''
|
||||
user_id TEXT PRIMARY KEY,
|
||||
long_name TEXT,
|
||||
short_name TEXT,
|
||||
hw_model TEXT,
|
||||
is_licensed TEXT,
|
||||
role TEXT,
|
||||
public_key TEXT
|
||||
'''
|
||||
ensure_table_exists(table_name, schema)
|
||||
|
||||
|
||||
def ensure_table_exists(table_name, schema):
|
||||
"""Ensure the given table exists in the database."""
|
||||
try:
|
||||
with sqlite3.connect(config.db_file_path) as db_connection:
|
||||
db_cursor = db_connection.cursor()
|
||||
create_table_query = f"CREATE TABLE IF NOT EXISTS {table_name} ({schema})"
|
||||
db_cursor.execute(create_table_query)
|
||||
db_connection.commit()
|
||||
except sqlite3.Error as e:
|
||||
logging.error(f"SQLite error in ensure_table_exists({table_name}): {e}")
|
||||
except Exception as e:
|
||||
logging.error(f"Unexpected error in ensure_table_exists({table_name}): {e}")
|
||||
|
||||
|
||||
def get_name_from_database(user_id, type="long"):
|
||||
|
||||
+10
-4
@@ -2,6 +2,8 @@ from meshtastic.protobuf import channel_pb2
|
||||
from google.protobuf.message import Message
|
||||
import logging
|
||||
import base64
|
||||
from db_handler import update_node_info_in_db
|
||||
import globals
|
||||
|
||||
def save_changes(interface, menu_path, modified_settings):
|
||||
"""
|
||||
@@ -29,16 +31,20 @@ def save_changes(interface, menu_path, modified_settings):
|
||||
logging.info(f"Updated {config_category} with Latitude: {lat} and Longitude {lon} and Altitude {alt}")
|
||||
return
|
||||
|
||||
elif menu_path[1] == "User Settings": # for user configs
|
||||
config_category = "User Settings"
|
||||
elif menu_path[1] == "User Settings": # for user configs
|
||||
config_category = "User Settings"
|
||||
long_name = modified_settings.get("longName")
|
||||
short_name = modified_settings.get("shortName")
|
||||
is_licensed = modified_settings.get("isLicensed")
|
||||
is_licensed = is_licensed == "True" or is_licensed is True
|
||||
is_licensed = is_licensed == "True" or is_licensed is True # Normalize boolean
|
||||
|
||||
node.setOwner(long_name, short_name, is_licensed)
|
||||
|
||||
logging.info(f"Updated {config_category} with Long Name: {long_name} and Short Name {short_name} and Licensed Mode {is_licensed}")
|
||||
# Update only the changed fields and preserve others
|
||||
update_node_info_in_db(globals.myNodeNum, long_name=long_name, short_name=short_name, is_licensed=is_licensed)
|
||||
|
||||
logging.info(f"Updated {config_category} with Long Name: {long_name}, Short Name: {short_name}, Licensed Mode: {is_licensed}")
|
||||
|
||||
return
|
||||
|
||||
elif menu_path[1] == "Channels": # for channel configs
|
||||
|
||||
Reference in New Issue
Block a user