Skip to content

Commit

Permalink
Switched to use ProfileWrappers for UI, can now save Profile Parents …
Browse files Browse the repository at this point in the history
…to DB, profile origin maintained through lifecycle
  • Loading branch information
Rexeh committed Feb 20, 2024
1 parent b90c23e commit 78d4e74
Show file tree
Hide file tree
Showing 19 changed files with 576 additions and 130 deletions.
102 changes: 59 additions & 43 deletions joystick_diagrams/app_state.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging
from copy import deepcopy

from joystick_diagrams.input.profile import Profile_
from joystick_diagrams.input.profile_collection import ProfileCollection
from joystick_diagrams.plugin_manager import ParserPluginManager
from joystick_diagrams.plugin_wrapper import PluginWrapper
from joystick_diagrams.profile_wrapper import ProfileWrapper

_logger = logging.getLogger(__name__)

Expand All @@ -22,15 +23,58 @@ def __new__(cls, *args, **kwargs):

def _init(self, plugin_manager: ParserPluginManager) -> None:
self.plugin_manager: ParserPluginManager = plugin_manager
self.profileObjectMapping: dict[str, Profile_] = {}

# Profile map for Plugin Profiles for lookups
self.plugin_profile_map: dict[str, Profile_] = {}

# Profile wrappers for use by app
self.profile_wrappers: list[ProfileWrapper] = []

self.profileParentMapping: dict[str, list[str]] = {}
self.processedProfileObjectMapping: dict[str, Profile_] = {}
self.update_processed_profiles()
self.process_profile_collection_updates()
self.process_profiles_from_collections()

def process_profiles_from_collections(self):
plugin_collections = self.get_plugin_wrapper_collections()

# Create profile map from raw profiles
_logger.debug(
f"Processing profiles from plugins with {len(plugin_collections)} plugin collections"
)
self.create_plugin_profile_map(plugin_collections)

# Create profile wrappers for use in app
self.create_profile_wrappers(self.plugin_manager.get_enabled_plugin_wrappers())

# Initialise wrappers / restoring state and customisation
self.initialise_profile_wrappers()

def initialise_profile_wrappers(self):
_logger.debug(f"Initialising {len(self.profile_wrappers)} profile wrappers ")

for wrapper in self.profile_wrappers:
wrapper.initialise_wrapper()

def create_profile_wrappers(self, plugin_wrappers: list[PluginWrapper]):
# Clear Existing Wrappers
self.profile_wrappers.clear()

for plugin in plugin_wrappers:
# Get pluugins only

if not plugin.plugin_profile_collection:
continue

profiles = plugin.plugin_profile_collection.profiles

_logger.debug(f"{len(profiles)} profiles detected for {plugin}")

for profile in profiles.values():
self.profile_wrappers.append(ProfileWrapper(profile, plugin))

## Temp code for handling Plugin Wrapper changes > TODO REFACTOR
def process_profile_collection_updates(self):
self.process_loaded_plugins(self.get_plugin_wrapper_collections())
_logger.debug(
f"Processing profiles from plugins with {plugin} plugin collections"
)

def get_plugin_wrapper_collections(self) -> dict[str, ProfileCollection]:
"""Returns a list of Profile Collections that are tagged with the Plugin Name where the plugin is enabled"""
Expand All @@ -40,7 +84,9 @@ def get_plugin_wrapper_collections(self) -> dict[str, ProfileCollection]:
if x.enabled and x.plugin_profile_collection
}

def process_loaded_plugins(self, profile_collections: dict[str, ProfileCollection]):
def create_plugin_profile_map(
self, profile_collections: dict[str, ProfileCollection]
):
"""Processes the **raw** profilee collections from all loaded and enabled plugins, into a new dictionary mapping
Key = Plugin Name - Profile Name
Expand All @@ -50,19 +96,17 @@ def process_loaded_plugins(self, profile_collections: dict[str, ProfileCollectio
"""

# Clear existing processed profiles
self.profileObjectMapping.clear()
self.plugin_profile_map.clear()

for profile_source, profiles in profile_collections.items():
for profile_name, profile_obj in profiles.profiles.items():
combined_name = f"{profile_source} - {profile_name}"
self.profileObjectMapping[combined_name] = profile_obj
for profile_obj in profiles.profiles.values():
composite_key = f"{profile_source.lower().strip()}_{profile_obj.name.lower().strip()}"
self.plugin_profile_map[composite_key] = profile_obj

_logger.debug(
f"Loaded plugins resulted in the following profiles being detected {self.profileObjectMapping}"
f"Loaded plugins resulted in the following profiles being detected {self.plugin_profile_map}"
)

self.update_processed_profiles()

def get_processed_profile(self, profile_identifier: str) -> Profile_:
"""Return inherited profile for given Profile Identifier."""
return self.processedProfileObjectMapping[profile_identifier]
Expand All @@ -78,34 +122,6 @@ def update_parent_profile_map(self, key: str, values: list) -> None:
"""
self.profileParentMapping[key] = values
self.update_processed_profiles()

def update_processed_profiles(self) -> None:
"""Applies any PARENT relationships on top of profiles"""
for profile_key, profile_obj in self.profileObjectMapping.items():
self.processedProfileObjectMapping[profile_key] = deepcopy(profile_obj)

for profile, parents in self.profileParentMapping.items():
profile_copy = deepcopy(self.profileObjectMapping[profile])

if not parents:
self.processedProfileObjectMapping[profile] = profile_copy
continue

if parents:
parents.reverse() # Reverse list to flip obj >> parent
merged_profiles = deepcopy(self.profileObjectMapping[parents[0]])

for parent in parents[:1]:
obj = deepcopy(self.profileObjectMapping[parent])
merged_profiles = merged_profiles.merge_profiles(obj)

self.processedProfileObjectMapping[
profile
] = merged_profiles.merge_profiles(profile_copy)
_logger.debug(
f"Updated processed profiles {self.processedProfileObjectMapping}"
)


if __name__ == "__main__":
Expand Down
69 changes: 69 additions & 0 deletions joystick_diagrams/db/db_profile_parents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import logging
import os
import sqlite3
from sqlite3 import connect

DB_DIR = "data"
DB_NAME = "joystick_diagrams.db"
TABLE_NAME = "profile_parents"

_logger = logging.getLogger(__name__)


def create_new_db_if_not_exist():
path = os.path.join(os.getcwd(), DB_DIR, DB_NAME)
connection = connect(path)
connection.execute("PRAGMA foreign_keys = 1")
cur = connection.cursor()
cur.execute(
f"CREATE TABLE IF NOT EXISTS {TABLE_NAME}(\
parent_profile_key TEXT NOT NULL,\
ordering INT NOT NULL,\
profile_key TEXT NOT NULL,\
PRIMARY KEY(parent_profile_key, profile_key),\
FOREIGN KEY(profile_key) REFERENCES profiles(profile_key) \
)"
)


def add_parents_to_profile(profile_key: str, parents: list):
path = os.path.join(os.getcwd(), DB_DIR, DB_NAME)
connection = connect(path)
connection.execute("PRAGMA foreign_keys = 1")
cur = connection.cursor()

query = "SELECT * from profiles WHERE profile_key = ?"
params = (profile_key,)

cur.execute(query, params)
result = cur.fetchone()

if not result:
_logger.error(
f"Tried to add a parent to a profile that does not exist in the DB {profile_key=}, and {parents=}"
)

if result:
# Delete existing relationships
query = "DELETE FROM profile_parents where profile_key = ?"
params = (profile_key,)
cur.execute(query, params)

try:
for index, parent in enumerate(parents, 1):
query = "INSERT INTO profile_parents (parent_profile_key, ordering, profile_key) VALUES(?,?,?)"
params = (parent, index, result[0])
cur.execute(query, params)

except sqlite3.IntegrityError:
_logger.error(
f"Integrity errors when inserting which suggests the {profile_key=} no longer exists in profiles"
)

connection.commit()


if __name__ == "__main__":
create_new_db_if_not_exist()

add_parents_to_profile("my_profile_key", ["profile_parent_1", "profile_parent_2"])
72 changes: 72 additions & 0 deletions joystick_diagrams/db/db_profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import os
from sqlite3 import connect

DB_DIR = "data"
DB_NAME = "joystick_diagrams.db"
TABLE_NAME = "profiles"


def create_new_db_if_not_exist():
path = os.path.join(os.getcwd(), DB_DIR, DB_NAME)
connection = connect(path)
cur = connection.cursor()
cur.execute(
f"CREATE TABLE IF NOT EXISTS {TABLE_NAME}(profile_key TEXT PRIMARY KEY)"
)


def get_profile(profile_key: str) -> list[str]:
path = os.path.join(os.getcwd(), DB_DIR, DB_NAME)
connection = connect(path)
cur = connection.cursor()

query = "SELECT * from profiles WHERE profile_key = ?"
params = (profile_key,)

cur.execute(query, params)
result = cur.fetchone()

if not result:
return add_profile(profile_key)

return result[0]


def add_profile(profile_key: str) -> list[str]:
path = os.path.join(os.getcwd(), DB_DIR, DB_NAME)
connection = connect(path)
cur = connection.cursor()

query = "INSERT OR IGNORE INTO profiles (profile_key) VALUES(?)"
params = (profile_key,)
cur.execute(query, params)

connection.commit()

query = "SELECT * from profiles WHERE profile_key = ?"
params = (profile_key,)

cur.execute(query, params)
result = cur.fetchall()
return result[0]


def get_profile_parents(profile_key: str):
path = os.path.join(os.getcwd(), DB_DIR, DB_NAME)
connection = connect(path)
cur = connection.cursor()

query = "SELECT parent_profile_key,ordering from profile_parents WHERE profile_key = ? ORDER BY ordering asc"
params = (profile_key,)

cur.execute(query, params)
result = cur.fetchall()

return result


if __name__ == "__main__":
create_new_db_if_not_exist()

data = get_profile_parents("my_profile_key")
print(data)
2 changes: 1 addition & 1 deletion joystick_diagrams/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

def export(export_device: ExportDevice, output_directory: str):
try:
profile_name = export_device.description
profile_name = export_device.profile_wrapper.profile_name

# Get Profile Devices, that have valid templates
_logger.debug(f"Getting device templates for {export_device} object")
Expand Down
3 changes: 2 additions & 1 deletion joystick_diagrams/export_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from joystick_diagrams.input import device
from joystick_diagrams.input.device import Device_
from joystick_diagrams.profile_wrapper import ProfileWrapper
from joystick_diagrams.template import Template


Expand All @@ -11,7 +12,7 @@ class ExportDevice:

device: Device_
_template: Template | None
description: str
profile_wrapper: ProfileWrapper
errors: set = field(default_factory=set, init=False)

@property
Expand Down
2 changes: 1 addition & 1 deletion joystick_diagrams/input/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def get_inputs(self) -> dict[str, dict[str | int, Input_]]:
"""
return self.inputs

def get_combined_inputs(self) -> dict:
def get_combined_inputs(self) -> dict[str | int, Input_]:
"""Returns a flattened input dictionary
Returns dict
Expand Down
4 changes: 4 additions & 0 deletions joystick_diagrams/plugin_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ def create_plugin_wrappers(self):
for plugin in self.get_available_plugins():
self.plugin_wrappers.append(PluginWrapper(plugin))

def get_enabled_plugin_wrappers(self):
"Returns plugin wrappers where the plugin is enabled"
return [x for x in self.plugin_wrappers if x.enabled is True]

def load_discovered_plugins(self) -> None:
"""Load and validate the plugins that were found during iniitalisation.
Expand Down
15 changes: 15 additions & 0 deletions joystick_diagrams/plugin_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from joystick_diagrams import app_state

# Run each plugin
# Get the profiles from all the executed plugins
# Find/Restore and Merge any Profile Configurations
# Find/Restore any XYZ i.e. Layer in NAME edits / Profile Name Changes


def run_parser_plugins():
"""Run the parser plugins available"""
_state = app_state.AppState()

for wrapper in _state.plugin_manager.plugin_wrappers:
if wrapper.ready:
wrapper.process()
Loading

0 comments on commit 78d4e74

Please sign in to comment.