Migrate hidden field to database

This commit is contained in:
Mathieu Comandon 2020-08-09 23:36:18 -07:00 committed by Mathieu Comandon
parent e6e6818eec
commit b3236a99d3
9 changed files with 75 additions and 92 deletions

View file

@ -12,22 +12,27 @@ PGA_DB = settings.PGA_DB
def get_games(
name_filter=None,
extra_filters=None,
filters=None,
excludes=None,
sorts=None
):
"""Get the list of every game in database."""
query = "select * from games"
params = []
filters = []
sql_filters = []
if name_filter:
filters.append("name LIKE ?")
sql_filters.append("name LIKE ?")
params.append("%" + name_filter + "%")
for field in extra_filters or {}:
if extra_filters[field]:
filters.append("%s = ?" % field)
params.append(extra_filters[field])
if filters:
query += " WHERE " + " AND ".join(filters)
for field in filters or {}:
if filters[field]:
sql_filters.append("%s = ?" % field)
params.append(filters[field])
for field in excludes or {}:
if excludes[field]:
sql_filters.append("%s IS NOT ?" % field)
params.append(excludes[field])
if sql_filters:
query += " WHERE " + " AND ".join(sql_filters)
if sorts:
query += " ORDER BY %s" % ", ".join(
["%s %s" % (sort[0], sort[1]) for sort in sorts]
@ -201,15 +206,6 @@ def get_used_runners():
return [result[0] for result in results if result[0]]
def get_used_runners_game_count():
"""Return a dictionary listing for each runner in use, how many games are using it."""
with sql.db_cursor(PGA_DB) as cursor:
query = "select runner, count(*) from games where runner is not null group by runner order by runner"
rows = cursor.execute(query)
results = rows.fetchall()
return {result[0]: result[1] for result in results if result[0]}
def get_used_platforms():
"""Return a list of platforms currently in use"""
with sql.db_cursor(PGA_DB) as cursor:
@ -220,36 +216,3 @@ def get_used_platforms():
rows = cursor.execute(query)
results = rows.fetchall()
return [result[0] for result in results if result[0]]
def get_used_platforms_game_count():
"""Return a dictionary listing for each platform in use, how many games are using it."""
with sql.db_cursor(PGA_DB) as cursor:
# The extra check for 'installed is 1' is needed because
# the platform lists don't show uninstalled games, but the platform of a game
# is remembered even after the game is uninstalled.
query = (
"select platform, count(*) from games "
"where platform is not null and platform is not '' and installed is 1 "
"group by platform "
"order by platform"
)
rows = cursor.execute(query)
results = rows.fetchall()
return {result[0]: result[1] for result in results if result[0]}
def get_hidden_ids():
"""Return a list of game IDs to be excluded from the library view"""
# Load the ignore string and filter out empty strings to prevent issues
ignores_raw = settings.read_setting("library_ignores", section="lutris", default="").split(",")
ignores = [ignore for ignore in ignores_raw if not ignore == ""]
# Turn the strings into integers
return [int(game_id) for game_id in ignores]
def set_hidden_ids(games):
"""Writes a list of game IDs that are to be hidden into the config file"""
ignores_str = [str(game_id) for game_id in games]
settings.write_setting("library_ignores", ','.join(ignores_str), section="lutris")

View file

@ -90,6 +90,10 @@ DATABASE = {
"name": "playtime",
"type": "REAL"
},
{
"name": "hidden",
"type": "INTEGER"
},
],
"store_games": [
{

View file

@ -68,6 +68,7 @@ class Game(GObject.Object):
self.game_config_id = game_data.get("configpath") or ""
self.is_installed = bool(game_data.get("installed") and self.game_config_id)
self.is_hidden = bool(game_data.get("hidden"))
self.platform = game_data.get("platform") or ""
self.year = game_data.get("year") or ""
self.lastplayed = game_data.get("lastplayed") or 0
@ -128,25 +129,15 @@ class Game(GObject.Object):
categories_db.remove_category_from_game(self.id, favorite["id"])
self.emit("game-updated")
@property
def is_hidden(self):
"""Is the game hidden in the UI?"""
return self.id in games_db.get_hidden_ids()
def hide(self):
"""Do not show this game in the UI"""
# Append the new hidden ID and save it
ignores = games_db.get_hidden_ids() + [self.id]
games_db.set_hidden_ids(ignores)
self.emit("game-updated")
self.is_hidden = True
self.save()
def unhide(self):
"""Remove the game from hidden games"""
# Remove the ID to unhide and save it
ignores = games_db.get_hidden_ids()
ignores.remove(self.id)
games_db.set_hidden_ids(ignores)
self.emit("game-updated")
self.is_hidden = False
self.save()
@property
def log_buffer(self):
@ -301,6 +292,7 @@ class Game(GObject.Object):
steamid=self.steamid,
id=self.id,
playtime=self.playtime,
hidden=self.is_hidden,
)
self.emit("game-updated")

View file

@ -43,6 +43,9 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
default_height = 600
__gtype_name__ = "LutrisWindow"
__gsignals__ = {
"view-updated": (GObject.SIGNAL_RUN_LAST, None, ()),
}
main_box = GtkTemplate.Child()
games_scrollwindow = GtkTemplate.Child()
@ -65,8 +68,6 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
viewtype_icon = GtkTemplate.Child()
def __init__(self, application, **kwargs):
# pylint: disable=too-many-statements
# TODO: refactor
width = int(settings.read_setting("width") or self.default_width)
height = int(settings.read_setting("height") or self.default_height)
super().__init__(
@ -234,18 +235,8 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
def hidden_state_change(self, action, value):
"""Hides or shows the hidden games"""
action.set_state(value)
# Add or remove hidden games
ignores = games_db.get_hidden_ids()
settings.write_setting("show_hidden_games", str(self.show_hidden_games).lower(), section="lutris")
# If we have to show the hidden games now, we need to add them back to
# the view. If we need to hide them, we just remove them from the view
if value:
self.game_store.add_games(games_db.get_games_by_ids(ignores))
else:
for game_id in ignores:
self.game_store.remove_game(game_id)
self.emit("view-updated")
@property
def current_view_type(self):
@ -342,6 +333,7 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
game_ids = categories_db.get_game_ids_for_category(self.filters["category"])
return games_db.get_games_by_ids(game_ids)
sql_filters = {}
sql_excludes = {}
if self.filters.get("runner"):
sql_filters["runner"] = self.filters["runner"]
if self.filters.get("platform"):
@ -352,9 +344,12 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
search_query = self.filters["text"]
else:
search_query = None
if not self.show_hidden_games:
sql_excludes["hidden"] = 1
games = games_db.get_games(
name_filter=search_query,
extra_filters=sql_filters,
filters=sql_filters,
excludes=sql_excludes,
sorts=self.sort_params
)
logger.info("Returned %s games from %s, %s", len(games), self.filters, self.view_sorting)
@ -364,10 +359,10 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
"""Return an instance of the game store"""
return GameStore([], self.icon_type)
def update_store(self, games=None):
def update_store(self, *_args, **_kwargs):
self.game_store.games = []
self.game_store.store.clear()
games = games or self.get_games_from_filters()
games = self.get_games_from_filters()
for game in games:
self.game_store.add_game(game)
self.no_results_overlay.props.visible = not bool(games)
@ -429,6 +424,7 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
This must be called each time the view is rebuilt.
"""
self.connect("view-updated", self.update_store)
self.view.connect("game-selected", self.game_selection_changed)
self.view.connect("game-activated", self.on_game_activated)
@ -498,8 +494,6 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
self.games_scrollwindow.add(self.view)
self._connect_signals()
GLib.idle_add(self.update_store)
self.zoom_adjustment.props.value = list(IMAGE_SIZES.keys()).index(self.icon_type)
if view_type:
@ -507,6 +501,7 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
settings.write_setting("view_type", view_type)
self.view.show_all()
self.emit("view-updated")
def set_viewtype_icon(self, view_type):
self.viewtype_icon.set_from_icon_name("view-%s-symbolic" % view_type, Gtk.IconSize.BUTTON)
@ -640,12 +635,13 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
"""Callback to handle uninstalled game filter switch"""
action.set_state(value)
self.set_show_installed_state(value.get_boolean())
self.emit("view-updated")
def set_show_installed_state(self, filter_installed):
"""Shows or hide uninstalled games"""
settings.write_setting("filter_installed", bool(filter_installed))
self.filters["installed"] = filter_installed
self.update_store()
self.emit("view-updated")
@GtkTemplate.Callback
def on_search_entry_changed(self, entry):
@ -695,7 +691,8 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
except ValueError:
self.game_store.add_games(games_db.get_games_by_ids([game.id]))
self.game_panel.refresh()
GLib.idle_add(self.game_panel.refresh)
self.emit("view-updated")
return True
def game_selection_changed(self, _widget, game):
@ -767,12 +764,12 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
self.actions["view-sorting"].set_state(value)
value = str(value).strip("'")
settings.write_setting("view_sorting", value)
GLib.idle_add(self.update_store)
self.emit("view-updated")
def on_view_sorting_direction_change(self, action, value):
self.actions["view-sorting-ascending"].set_state(value)
settings.write_setting("view_sorting_ascending", bool(value))
GLib.idle_add(self.update_store)
self.emit("view-updated")
def on_left_side_panel_state_change(self, action, value):
"""Callback to handle left side panel toggle"""
@ -804,7 +801,7 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
self.filters.pop(filter_type)
if row:
self.filters[row.type] = row.id
self.update_store()
self.emit("view-updated")
def show_invalid_credential_warning(self):
dialogs.ErrorDialog(_("Could not connect to your Lutris account. Please sign in again."))

View file

@ -99,7 +99,7 @@ class LutrisStatusIcon:
@staticmethod
def add_games():
"""Adds installed games in order of last use"""
installed_games = get_games(extra_filters={"installed": "1"})
installed_games = get_games(filters={"installed": 1})
installed_games.sort(
key=lambda game: max(game["lastplayed"] or 0, game["installed_at"] or 0),
reverse=True,

View file

@ -5,7 +5,7 @@ import importlib
from lutris import settings
from lutris.util.log import logger
MIGRATION_VERSION = 8 # Never decrease this number
MIGRATION_VERSION = 9 # Never decrease this number
# Replace deprecated migrations with empty lists
MIGRATIONS = [
@ -17,6 +17,7 @@ MIGRATIONS = [
["d9vk_to_dxvk"],
["fix_playtime_type"],
["mess_to_mame"],
["migrate_hidden_ids"],
]

View file

@ -0,0 +1,26 @@
"""Move hidden games from settings to database"""
from lutris import settings
from lutris.game import Game
def get_hidden_ids():
"""Return a list of game IDs to be excluded from the library view"""
# Load the ignore string and filter out empty strings to prevent issues
ignores_raw = settings.read_setting("library_ignores", section="lutris", default="").split(",")
ignores = [ignore for ignore in ignores_raw if not ignore == ""]
# Turn the strings into integers
return [int(game_id) for game_id in ignores]
def migrate():
"""Run migration"""
try:
game_ids = get_hidden_ids()
except:
print("Failed to read hidden game IDs")
return []
for game_id in game_ids:
game = Game(game_id)
game.hide()
settings.write_setting("library_ignores", '', section="lutris")

View file

@ -136,7 +136,7 @@ def fill_missing_platforms():
"""Sets the platform on games where it's missing.
This should never happen.
"""
pga_games = get_games(extra_filters={"installed": "1"})
pga_games = get_games(filters={"installed": 1})
for pga_game in pga_games:
if pga_game.get("platform") or not pga_game["runner"]:
continue

View file

@ -54,7 +54,7 @@ class TestPersonnalGameArchive(DatabaseTester):
def test_can_filter_by_installed_games(self):
games_db.add_game(name="installed_game", runner="Linux", installed=1)
games_db.add_game(name="bang", runner="Linux", installed=0)
game_list = games_db.get_games(filter_installed=True)
game_list = games_db.get_games(filters={'installed': 1})
self.assertEqual(len(game_list), 1)
self.assertEqual(game_list[0]['name'], 'installed_game')