From be1a079104cef8bb84dff84d0855f42f28faef5c Mon Sep 17 00:00:00 2001 From: Fazorath Date: Wed, 28 Oct 2026 23:43:22 +0000 Subject: [PATCH] Edit old wallet runs as profit and minutes; remove wallet chaining (#49) --- EveProfitManager/core.py | 84 +++++------------------------ EveProfitManager/gui.py | 8 ++- EveProfitManager/test_cargo_page.py | 12 +++-- EveProfitManager/test_cli_abyss.py | 4 +- EveProfitManager/test_core.py | 81 +++++++++++++--------------- EveProfitManager/test_export.py | 6 +-- 6 files changed, 66 insertions(+), 129 deletions(-) diff --git a/EveProfitManager/core.py b/EveProfitManager/core.py index 7b32e48..57bba2d 100644 --- a/EveProfitManager/core.py +++ b/EveProfitManager/core.py @@ -311,24 +311,6 @@ def latest_cargo_after(self, session_id): ).fetchone() return row["cargo_after"] if row else None - def log_abyss_run(self, session_id, isk_before, isk_after, time_left): - """ - Record an abyssal run from wallet amounts and the timer, the way runs were logged - before cargo runs. Nothing logs new runs this way now; kept for old wallet-based data. - - Args: - session_id (int): Session to add the run to. - isk_before (float): Wallet before the run. - isk_after (float): Wallet after the run. - time_left (float): Minutes left on the abyssal timer when leaving. - - Returns: - Run: The recorded run. - """ - _require_finite(isk_before, isk_after) - _require_time_left(time_left) - return self._add_run(session_id, isk_after - isk_before, MAXTIME - time_left, isk_after) - def log_incursion_site(self, session_id, profit, minutes): """Record one incursion site.""" _require_finite(profit) @@ -395,52 +377,30 @@ def runs(self, session_id): return [_run(r) for r in rows] def delete_run(self, run_id): - """ - Delete a run. For abyssal runs the next run's profit is re-based on the - wallet before the deleted run, so the session still matches the wallet change. - """ - run = self.run(run_id) - if run is None: - return + """Delete a run. Every other run keeps its profit.""" with self.db: - if run.isk_after is not None: - after = self._next_abyss_run(run) - if after: - before = run.isk_after - run.profit - self.db.execute( - "UPDATE runs SET profit = ? WHERE id = ?", (after["isk_after"] - before, after["id"]) - ) self.db.execute("DELETE FROM runs WHERE id = ?", (run_id,)) - def edit_abyss_run(self, run_id, isk_after, time_left): + def edit_wallet_run(self, run_id, profit, minutes): """ - Correct an abyssal run's wallet or timer. The wallet before the run stays - as it was, and the next run's profit is re-based on the corrected wallet. + Correct an old wallet run's profit or time. Its wallet stays as it was + and no other run changes. Args: - run_id (int): Abyssal run to change. - isk_after (float): Corrected wallet after the run. - time_left (float): Corrected minutes left on the abyssal timer. + run_id (int): Old wallet run (wallet after set, no cargo values) to change. + profit (float): Corrected profit in millions. + minutes (float): Corrected run time. Returns: Run: The updated run. """ - _require_finite(isk_after) - _require_time_left(time_left) + _require_finite(profit) + _require_minutes(minutes) run = self.run(run_id) - if run is None or run.isk_after is None: - raise ValueError("Only abyssal runs have a wallet to correct") - before = run.isk_after - run.profit + if run is None or run.isk_after is None or run.cargo_after is not None: + raise ValueError("Only old wallet runs have a profit to correct this way") with self.db: - self.db.execute( - "UPDATE runs SET profit = ?, minutes = ?, isk_after = ? WHERE id = ?", - (isk_after - before, MAXTIME - time_left, isk_after, run_id), - ) - after = self._next_abyss_run(run) - if after: - self.db.execute( - "UPDATE runs SET profit = ? WHERE id = ?", (after["isk_after"] - isk_after, after["id"]) - ) + self.db.execute("UPDATE runs SET profit = ?, minutes = ? WHERE id = ?", (profit, minutes, run_id)) return self.run(run_id) def edit_cargo_run(self, run_id, cargo_before, cargo_after, time_left): @@ -480,26 +440,6 @@ def edit_incursion_site(self, run_id, profit, minutes): self.db.execute("UPDATE runs SET profit = ?, minutes = ? WHERE id = ?", (profit, minutes, run_id)) return self.run(run_id) - def _next_abyss_run(self, run): - """The abyssal run after this one in its session, whose profit chains off its wallet.""" - return self.db.execute( - "SELECT id, isk_after FROM runs WHERE session_id = ? AND id > ? " - "AND isk_after IS NOT NULL ORDER BY id LIMIT 1", - (run.session_id, run.id), - ).fetchone() - - def current_isk(self, session_id): - """Wallet after the latest abyssal run, or the starting wallet if none yet.""" - row = self.db.execute( - "SELECT isk_after FROM runs WHERE session_id = ? AND isk_after IS NOT NULL " - "ORDER BY id DESC LIMIT 1", - (session_id,), - ).fetchone() - if row: - return row["isk_after"] - session = self.session(session_id) - return session.starting_isk if session else None - # --- stats ---------------------------------------------------------- def totals(self, kind=None): diff --git a/EveProfitManager/gui.py b/EveProfitManager/gui.py index 9699b54..6c5d332 100644 --- a/EveProfitManager/gui.py +++ b/EveProfitManager/gui.py @@ -1041,15 +1041,13 @@ def after_log(self): def edit_fields(self, run): left = ("Time left (min)", f"{MAXTIME - run.minutes:g}") if run.cargo_after is None: # an old wallet run - return [("Wallet after run (M)", f"{run.isk_after:g}"), left] + return [("Profit (M)", f"{run.profit:g}"), ("Minutes", f"{run.minutes:g}")] return [("Cargo before (M)", f"{run.cargo_before:g}"), ("Cargo after (M)", f"{run.cargo_after:g}"), left] def save_edit(self, run, *values): if run.cargo_after is None: - isk_after, time_left = values - return self.tracker.edit_abyss_run( - run.id, parse_isk(isk_after, "wallet after"), parse_minutes(time_left, "time left") - ) + profit, minutes = values + return self.tracker.edit_wallet_run(run.id, parse_isk(profit, "profit"), parse_minutes(minutes, "minutes")) before, after, time_left = values return self.tracker.edit_cargo_run( run.id, parse_isk(before, "cargo before"), parse_isk(after, "cargo after"), diff --git a/EveProfitManager/test_cargo_page.py b/EveProfitManager/test_cargo_page.py index e2da006..edcb090 100644 --- a/EveProfitManager/test_cargo_page.py +++ b/EveProfitManager/test_cargo_page.py @@ -96,7 +96,7 @@ def test_resuming_prefills_cargo_before_from_the_latest_cargo_run(self): def test_resuming_a_wallet_only_session_leaves_cargo_before_empty(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000, weather="Gamma", tier=2) - self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) + self.tracker._add_run(sid, 30, 10, 1030) page = self.open_app() self.assertEqual(page.cargo_before.get(), "") self.type_run("0", "40", "10") @@ -134,12 +134,16 @@ def test_a_bad_cargo_edit_keeps_the_dialog_open_and_changes_nothing(self): self.assertIn("between 0 and 20", dialog.message.cget("text")) self.assertEqual(self.tracker.run(run.id), run) - def test_an_old_wallet_run_keeps_the_wallet_edit_dialog(self): + def test_an_old_wallet_run_edits_profit_and_minutes(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000, weather="Gamma", tier=2) - run = self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) + run = self.tracker._add_run(sid, 30, 10, 1030) self.open_app() dialog = self.open_edit(run) - self.assertEqual(self.texts(dialog)[:2], ["Wallet after run (M)", "Time left (min)"]) + self.assertEqual(self.texts(dialog)[:2], ["Profit (M)", "Minutes"]) + self.assertEqual([e.get() for e in dialog.entries], ["30", "10"]) + self.gui.set_entry(dialog.entries[0], "35") + dialog.save() + self.assertEqual((self.tracker.run(run.id).profit, self.tracker.run(run.id).isk_after), (35, 1030)) def test_settings_copy_no_longer_asks_for_the_wallet_for_abyssal_runs(self): self.open_app() diff --git a/EveProfitManager/test_cli_abyss.py b/EveProfitManager/test_cli_abyss.py index 80c2e61..c2d1f82 100644 --- a/EveProfitManager/test_cli_abyss.py +++ b/EveProfitManager/test_cli_abyss.py @@ -80,8 +80,8 @@ def test_bad_input_is_asked_again(self): self.assertIn("Please enter a number", out) def test_the_cli_no_longer_logs_wallet_runs(self): - with patch.object(Tracker, "log_abyss_run", side_effect=AssertionError("wallet run")): - session, _, _ = self.drive(["1", "1", "100", "130", "5", "0"]) + self.assertFalse(hasattr(Tracker, "log_abyss_run")) + session, _, _ = self.drive(["1", "1", "100", "130", "5", "0"]) self.assertEqual(session.runs, 1) diff --git a/EveProfitManager/test_core.py b/EveProfitManager/test_core.py index e381f2d..913e3df 100644 --- a/EveProfitManager/test_core.py +++ b/EveProfitManager/test_core.py @@ -72,16 +72,16 @@ def test_bad_cargo_runs_are_rejected_with_the_validator_errors(self): def test_latest_cargo_after_skips_wallet_runs_and_other_sessions(self): old = self.tracker.start_session(ABYSS, starting_isk=1000) - self.tracker.log_abyss_run(old, 1000, 1030, time_left=10) + self.tracker._add_run(old, 30, 10, 1030) self.assertIsNone(self.tracker.latest_cargo_after(old)) # wallet runs only self.tracker.log_cargo_run(old, 10, 70, time_left=10) - self.tracker.log_abyss_run(old, 1030, 1040, time_left=10) # a wallet run after it + self.tracker._add_run(old, 10, 10, 1040) # a wallet run after it self.assertEqual(self.tracker.latest_cargo_after(old), 70) self.assertIsNone(self.tracker.latest_cargo_after(self.tracker.start_session(ABYSS))) def test_a_session_open_across_the_update_mixes_wallet_and_cargo_runs(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000, weather="Dark", tier=3) - self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) # logged before the update + self.tracker._add_run(sid, 30, 10, 1030) # logged before the update self.assertIsNone(self.tracker.latest_cargo_after(sid)) # its first cargo before is empty self.tracker.log_cargo_run(sid, 50, 90, time_left=12) self.tracker.log_cargo_run(sid, 90, 80, time_left=5) @@ -96,16 +96,11 @@ def test_a_session_open_across_the_update_mixes_wallet_and_cargo_runs(self): def test_sessions_do_not_leak_profit(self): for _ in range(2): sid = self.tracker.start_session(ABYSS, starting_isk=100) - self.tracker.log_abyss_run(sid, 100, 130, time_left=0) + self.tracker._add_run(sid, 30, 20, 130) self.tracker.end_session(sid) self.assertEqual(self.tracker.session(sid).profit, 30) self.assertEqual(self.tracker.totals().profit, 60) - def test_time_left_is_validated(self): - sid = self.tracker.start_session(ABYSS, starting_isk=100) - with self.assertRaises(ValueError): - self.tracker.log_abyss_run(sid, 100, 120, time_left=25) - def test_active_session_and_empty_end(self): sid = self.tracker.start_session(INCURSION) self.assertEqual(self.tracker.active_session(INCURSION).id, sid) @@ -129,42 +124,42 @@ def test_delete_run(self): self.tracker.delete_run(run.id) self.assertEqual(self.tracker.session(sid).runs, 0) - def test_deleting_middle_abyss_run_keeps_wallet_gain(self): + def test_deleting_a_wallet_or_cargo_run_leaves_the_other_runs_alone(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000) - self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) - middle = self.tracker.log_abyss_run(sid, 1030, 1060, time_left=10) - self.tracker.log_abyss_run(sid, 1060, 1100, time_left=10) + first = self.tracker._add_run(sid, 30, 10, 1030) + middle = self.tracker._add_run(sid, 30, 10, 1060) + last = self.tracker._add_run(sid, 40, 10, 1100) + cargo = self.tracker.log_cargo_run(sid, 10, 25, time_left=5) self.tracker.delete_run(middle.id) - session = self.tracker.session(sid) - self.assertEqual((session.runs, session.profit), (2, 100)) - self.assertEqual(self.tracker.current_isk(sid), 1100) + self.tracker.delete_run(cargo.id) + self.assertEqual(self.tracker.runs(sid), [first, last]) + self.assertEqual(self.tracker.session(sid).profit, 70) - def test_editing_abyss_run_wallet_rebases_next_run(self): + def test_editing_a_wallet_run_changes_only_its_profit_and_minutes(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000) - self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) - typo = self.tracker.log_abyss_run(sid, 1030, 1600, time_left=10) # meant 1060 - last = self.tracker.log_abyss_run(sid, 1600, 1100, time_left=10) - edited = self.tracker.edit_abyss_run(typo.id, 1060, time_left=4) - self.assertEqual((edited.profit, edited.minutes, edited.isk_after), (30, 16, 1060)) - self.assertEqual(self.tracker.run(last.id).profit, 40) + first = self.tracker._add_run(sid, 30, 10, 1030) + typo = self.tracker._add_run(sid, 570, 10, 1600) # meant 30 + last = self.tracker._add_run(sid, -500, 10, 1100) + edited = self.tracker.edit_wallet_run(typo.id, 30, 16) + self.assertEqual((edited.profit, edited.minutes, edited.isk_after), (30, 16, 1600)) + for run in (first, last): + self.assertEqual(self.tracker.run(run.id), run) session = self.tracker.session(sid) - self.assertEqual((session.runs, session.profit, session.minutes), (3, 100, 36)) - self.assertEqual(self.tracker.current_isk(sid), 1100) + self.assertEqual((session.runs, session.profit, session.minutes), (3, -440, 36)) - def test_bad_abyss_edits_are_rejected(self): + def test_bad_wallet_edits_are_rejected(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000) - run = self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) + run = self.tracker._add_run(sid, 30, 10, 1030) + cargo = self.tracker.log_cargo_run(sid, 10, 25, time_left=5) site = self.tracker.log_incursion_site(self.tracker.start_session(INCURSION), 31, 10) - for run_id, isk_after, time_left in ( - (run.id, 1030, 25), - (run.id, 1030, -1), - (run.id, float("nan"), 5), - (site.id, 1030, 5), + for run_id, profit, minutes in ( + (run.id, 30, -1), (run.id, float("nan"), 5), (run.id, 30, float("inf")), + (cargo.id, 30, 5), (site.id, 30, 5), (999, 30, 5), ): with self.assertRaises(ValueError): - self.tracker.edit_abyss_run(run_id, isk_after, time_left) - self.assertEqual(self.tracker.run(run.id), run) - self.assertEqual(self.tracker.run(site.id), site) + self.tracker.edit_wallet_run(run_id, profit, minutes) + for unchanged in (run, cargo, site): + self.assertEqual(self.tracker.run(unchanged.id), unchanged) def test_editing_a_cargo_run_recomputes_its_profit_and_minutes(self): sid = self.tracker.start_session(ABYSS) @@ -176,7 +171,7 @@ def test_editing_a_cargo_run_recomputes_its_profit_and_minutes(self): def test_editing_a_cargo_run_leaves_the_runs_around_it_alone(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000) - wallet = self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) + wallet = self.tracker._add_run(sid, 30, 10, 1030) first = self.tracker.log_cargo_run(sid, 50, 90, time_left=12) middle = self.tracker.log_cargo_run(sid, 90, 900, time_left=6) last = self.tracker.log_cargo_run(sid, 900, 120, time_left=5) @@ -188,7 +183,7 @@ def test_editing_a_cargo_run_leaves_the_runs_around_it_alone(self): def test_bad_cargo_edits_are_rejected_like_logging(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000) run = self.tracker.log_cargo_run(sid, 100, 131, time_left=5) - wallet = self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) + wallet = self.tracker._add_run(sid, 30, 10, 1030) site = self.tracker.log_incursion_site(self.tracker.start_session(INCURSION), 31, 10) inf, nan = float("inf"), float("nan") for run_id, before, after, left, error in ( @@ -214,7 +209,7 @@ def test_editing_incursion_site(self): def test_bad_incursion_edits_are_rejected(self): site = self.tracker.log_incursion_site(self.tracker.start_session(INCURSION), 31, 10) sid = self.tracker.start_session(ABYSS, starting_isk=1000) - run = self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) + run = self.tracker._add_run(sid, 30, 10, 1030) for run_id, profit, minutes in ((site.id, 31, -1), (site.id, float("inf"), 10), (run.id, 31, 10)): with self.assertRaises(ValueError): self.tracker.edit_incursion_site(run_id, profit, minutes) @@ -226,8 +221,8 @@ def test_non_finite_values_are_rejected(self): inc = self.tracker.start_session(INCURSION) nan, inf = float("nan"), float("inf") for call in ( - lambda: self.tracker.log_abyss_run(abyss, 100, inf, time_left=5), - lambda: self.tracker.log_abyss_run(abyss, 100, 120, time_left=nan), + lambda: self.tracker.log_cargo_run(abyss, 100, inf, time_left=5), + lambda: self.tracker.log_cargo_run(abyss, 100, 120, time_left=nan), lambda: self.tracker.log_incursion_site(inc, nan, 10), lambda: self.tracker.log_incursion_site(inc, 31, inf), ): @@ -264,7 +259,7 @@ def test_totals_by_weather(self): (None, None, 10, 10), # before weather was recorded ): sid = self.tracker.start_session(ABYSS, starting_isk=100, weather=weather, tier=tier) - self.tracker.log_abyss_run(sid, 100, 100 + profit, time_left) + self.tracker.log_cargo_run(sid, 100, 100 + profit, time_left) self.tracker.start_session(ABYSS, starting_isk=100, weather="Dark", tier=1) # no runs yet inc = self.tracker.start_session(INCURSION) self.tracker.log_incursion_site(inc, 31, 10) @@ -280,7 +275,7 @@ def test_winning_sessions_count_session_profit(self): for wallets in ((100, 150, 120), (100, 80, 130), (100, 90)): # +20, +30 with a losing run, -10 sid = self.tracker.start_session(ABYSS, starting_isk=wallets[0]) for before, after in zip(wallets, wallets[1:]): - self.tracker.log_abyss_run(sid, before, after, time_left=5) + self.tracker.log_cargo_run(sid, before, after, time_left=5) inc = self.tracker.start_session(INCURSION) self.tracker.log_incursion_site(inc, 0, 10) # break-even isn't a win @@ -366,7 +361,7 @@ def test_an_old_database_with_a_loot_table_still_opens_and_keeps_it(self): reopened = Tracker(path) self.assertIn("loot", self.tables(reopened.db)) self.assertEqual(reopened.session(sid).starting_isk, 1000) - reopened.log_abyss_run(sid, 1000, 1050, 10) + reopened.log_cargo_run(sid, 1000, 1050, 10) self.assertEqual(len(reopened.runs(sid)), 1) reopened.close() diff --git a/EveProfitManager/test_export.py b/EveProfitManager/test_export.py index bac1b2b..1cdf933 100644 --- a/EveProfitManager/test_export.py +++ b/EveProfitManager/test_export.py @@ -21,8 +21,8 @@ def rows(self, path): def seed(self): a = self.tracker.start_session(ABYSS, starting_isk=1000, weather="Dark", tier=3) - self.tracker.log_abyss_run(a, 1000, 1031, time_left=5) - self.tracker.log_abyss_run(a, 1031, 1050, time_left=8) + self.tracker._add_run(a, 31, 15, 1031) + self.tracker._add_run(a, 19, 12, 1050) self.tracker.end_session(a) i = self.tracker.start_session(INCURSION) self.tracker.log_incursion_site(i, 93, 12) @@ -49,7 +49,7 @@ def test_csv_has_one_row_per_run_with_its_session(self): def test_csv_shows_the_cargo_values_of_cargo_runs_and_leaves_them_empty_for_wallet_runs(self): sid = self.tracker.start_session(ABYSS, starting_isk=1000, weather="Dark", tier=3) - self.tracker.log_abyss_run(sid, 1000, 1030, time_left=10) + self.tracker._add_run(sid, 30, 10, 1030) self.tracker.log_cargo_run(sid, 50, 92.5, time_left=12) out = self.dir / "out.csv" self.tracker.export_csv(out)