Skip to content

Commit

Permalink
Merge pull request #16 from CMDR-WDX/15-sumwing-sum-total-miscounting
Browse files Browse the repository at this point in the history
Resolved after confirmation in #15
  • Loading branch information
CMDR-WDX authored Dec 20, 2022
2 parents 665c2e0 + 75726bf commit 14f7b93
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 85 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
venv
.idea
__pycache__
__pycache__
pyrightconfig.json
15 changes: 8 additions & 7 deletions load.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import datetime
import os
import tkinter
from typing import Optional
from typing import Any, Optional
from os.path import basename, dirname

from massacre.mission_aggregation_helper import get_missions_for_all_cmdrs
Expand All @@ -20,7 +19,7 @@ def plugin_app(parent: tkinter.Frame) -> tkinter.Frame:
return parent


def plugin_start3(_path: str) -> str:
def plugin_start3(_: str) -> str:
logger.info("Stating Massacre Plugin")

if configuration.check_updates:
Expand All @@ -47,7 +46,7 @@ def notify_ui_on_outdated(is_outdated: bool):


def journal_entry(cmdr: str, _is_beta: bool, _system: str,
_station: str, entry: dict[str, any], _state: dict[str, any]):
_station: str, entry: dict[str, Any], _state: dict[str, Any]):
if entry["event"] == "Missions":
# Fetch the currently active missions and pass them to the Mission Registry
active_mission_uuids = map(lambda x: int(x["MissionID"]), entry["Active"])
Expand All @@ -57,16 +56,18 @@ def journal_entry(cmdr: str, _is_beta: bool, _system: str,
elif entry["event"] == "MissionAccepted":
# A new mission has been accepted. The Mission Repository should be notified about this
from massacre.mission_repository import mission_repository
mission_repository.notify_about_new_mission_accepted(entry, cmdr)
if mission_repository is not None:
mission_repository.notify_about_new_mission_accepted(entry, cmdr)

elif entry["event"] in ["MissionAbandoned", "MissionCompleted"]: # TODO: What about MissionRedirected?
# Mission has been completed or failed -> It is no longer active
mission_uuid = entry["MissionID"]
from massacre.mission_repository import mission_repository
mission_repository.notify_about_mission_gone(mission_uuid)
if mission_repository is not None:
mission_repository.notify_about_mission_gone(mission_uuid)


def plugin_prefs(parent: any, _cmdr: str, _is_beta: bool):
def plugin_prefs(parent: Any, _cmdr: str, _is_beta: bool):
return build_settings_ui(parent)


Expand Down
79 changes: 34 additions & 45 deletions massacre/massacre_mission_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,37 @@
"""
from typing import Callable
from massacre.logger_factory import logger
from dataclasses import dataclass

import massacre.mission_repository


@dataclass
class MassacreMission:
"""
Class defining a Massacre Mission.
This class is used in the UI to generate a data view
"""
def __init__(self, target_faction: str, count: int, reward: int, target_system: str, target_type: str,
source_faction: str, mission_id: int, wing: bool):
self._target_faction: str = target_faction
self._count: int = count
self._reward: int = reward
self._target_system: str = target_system
self._target_type: str = target_type
self._source_faction: str = source_faction
self._is_wing: bool = wing
self._id: int = mission_id

@property
def target_type(self):
return self._target_type

@property
def mission_id(self):
return self._id

@property
def target_faction(self):
return self._target_faction

@property
def count(self):
return self._count

@property
def reward(self):
return self._reward

@property
def target_system(self):
return self._target_system

@property
def source_faction(self):
return self._source_faction

@property
def is_wing(self):
return self._is_wing
target_faction: str
count: int
reward: int
target_system: str
target_type: str
source_faction: str
is_wing: bool
id: int

def as_dict(self):
as_dict = {
"target_type": self.target_type,
"mission_id": self.id,
"target_faction": self.target_faction,
"count": self.count,
"reward": self.reward,
"target_system": self.target_system,
"source_faction": self.source_faction,
"is_wing": self.is_wing
}
return as_dict


def __build_from_event(event: dict) -> MassacreMission:
Expand All @@ -68,7 +48,16 @@ def __build_from_event(event: dict) -> MassacreMission:
source_faction: str = event["Faction"]
mission_id: int = event["MissionID"]
wing: bool = event["Wing"]
return MassacreMission(target_faction, count, reward, target_system, target_type, source_faction, mission_id, wing)
return MassacreMission(
target_faction,
count,
reward,
target_system,
target_type,
source_faction,
wing,
mission_id
)


massacre_mission_listeners: list[Callable[[dict[int, MassacreMission]], None]] = []
Expand Down Expand Up @@ -100,7 +89,7 @@ def __handle_new_missions_state(data: dict[int, dict]):
# Push new Mission State to the Massacre Mission Store
_massacre_mission_store.clear()
for mission in relevant_missions:
_massacre_mission_store[mission.mission_id] = mission
_massacre_mission_store[mission.id] = mission

# Emit Event
for listener in massacre_mission_listeners:
Expand Down
4 changes: 2 additions & 2 deletions massacre/massacre_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def build_settings_ui(root: nb.Notebook) -> tk.Frame:
checkbox_offset = 10
title_offset = 20

frame = nb.Frame(root)
frame = nb.Frame(root) #type: ignore
frame.columnconfigure(1, weight=1)
__setting_changes.clear()
__setting_changes["check_updates"] = \
Expand All @@ -127,7 +127,7 @@ def build_settings_ui(root: nb.Notebook) -> tk.Frame:
nb.Checkbutton(frame, text="Display Summary-Row",
variable=__setting_changes["display_ratio_and_cr_per_kill_row"])
]
for i, entry in enumerate(ui_settings_checkboxes):
for entry in ui_settings_checkboxes:
entry.grid(columnspan=2, padx=checkbox_offset, sticky=tk.W)

nb.Label(frame, text="Other", pady=10, padx=title_offset).grid(sticky=tk.W)
Expand Down
3 changes: 1 addition & 2 deletions massacre/mission_aggregation_helper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json
import datetime as dt
from datetime import datetime
from pathlib import Path
from config import config
from massacre.logger_factory import logger
Expand All @@ -12,7 +11,7 @@
file_location = config.get_str("journaldir")
else:
# noinspection SpellCheckingInspection
file_location = config.get("journaldir")
file_location = config.get("journaldir") #type: ignore
if file_location is None or file_location == "":
file_location = config.default_journal_dir

Expand Down
111 changes: 85 additions & 26 deletions massacre/ui.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import json
import tkinter as tk
from typing import Optional
from dataclasses import dataclass

import massacre.massacre_settings
from massacre.massacre_mission_state import massacre_mission_listeners, MassacreMission
Expand All @@ -14,39 +16,92 @@ class MassacreMissionData:
Creates a "data-view" for the UI from all massacre missions. Will be used to create a table-like UI
Done to split the calculations from the UI.
"""

@dataclass
class FactionState:
killcount: int
reward: int
shareable_reward: int


def __init__(self, massacre_state: dict[int, MassacreMission]):
self.warnings: list[str] = []
# if Log Level is set to DEBUG, this will output the current Massacre Mission State to the Log File.
# for easy searching, you can Ctrl+F for "MASSACRE_MISSION_DATA_INPUT" and get the line below that.
logger.debug("MassacreMissionData input below: MASSACRE_MISSION_DATA_INPUT")
try:
debug_message_state: dict[int, dict] = {}
for k in massacre_state.keys():
v = massacre_state[k]
debug_message_state[k] = v.as_dict()
logger.debug(json.dumps(debug_message_state))
except Exception:
logger.error("Failed to Log debug_message_state")
pass
# Faction -> <Count, Reward, ShareableReward, DistanceToMax>
target_factions: list[str] = []
"""
A list containing all Target Factions, as in Factions you are meant to
kill as part of the mission. This is used to warn the User that they
have multiple targets and should recheck their stack.
"""
target_types: list[str] = []
"""
List of all target types (like Civilian, Pirates, etc). Will warn the User if they
have separate stacks.
"""
target_systems: list[str] = []
self.faction_to_count_lookup: dict[str, tuple[int, int, int]] = {}
"""
List of all target systems - as in locations where the targets need to be killed.
This will warn the player that they should recheck their stack.
"""
self.faction_to_count_lookup: dict[str, MassacreMissionData.FactionState] = {}
self.stack_height = 0
# This is the second-highest value. This is used to display the second-largest value in the delta-Field using
# a negative.
"""
The highest amount of kills needed per faction in this stack.
"""
self.before_stack_height = 0
"""
The SECOND-highest amount of kills needed per faction in this stack.
This is used for the delta-Column of the highest Stack to show the negative
delta towards the second-highest stack.
"""
self.target_sum = 0
"""
The amount of total mission kills (not total required kills (see stack_height))
"""
self.reward = 0
"""
How much the player should expect in Wing- and Non-Wing Missions
"""
self.shareable_reward = 0
"""
How much the player should expect in Wing-Missions
"""

for mission in massacre_state.values():
mission_giver = mission.source_faction
"""This is the Faction that handed out the mission"""

if mission_giver not in self.faction_to_count_lookup.keys():
self.faction_to_count_lookup[mission_giver] = 0, 0, 0

kill_count, reward, shareable_reward = self.faction_to_count_lookup[mission_giver]
kill_count += mission.count
"""If no Mission from that Faction is known yet, it will first be initialized"""
self.faction_to_count_lookup[mission_giver] = MassacreMissionData.FactionState(0, 0, 0)

faction_state = self.faction_to_count_lookup[mission_giver]
"""
Get the currently summed kill count and rewards from this faction. This might contain data
from previous Missions from that faction, or 0,0,0 if this is the first mission.
"""
faction_state.killcount += mission.count
self.target_sum += mission.count
reward += mission.reward
faction_state.reward += mission.reward
# Only wing missions are considered for shareable rewards
if mission.is_wing:
shareable_reward += mission.reward

self.faction_to_count_lookup[mission_giver] = kill_count, reward, shareable_reward

self.shareable_reward += shareable_reward
self.reward += reward
faction_state.shareable_reward += mission.reward

### Add Faction, Target Type and Target System to the list if they are not
### yet present. This will be later used to generate a warning if more than
### one of a type is present. See "Check for Warnings block below"
if mission.target_faction not in target_factions:
target_factions.append(mission.target_faction)

Expand All @@ -56,8 +111,13 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
if mission.target_system not in target_systems:
target_systems.append(mission.target_system)

if kill_count > self.stack_height:
self.stack_height = kill_count
if faction_state.killcount > self.stack_height:
self.stack_height = faction_state.killcount

# After all Missions have been handled, iterate through the faction_to_count_lookup to calculate the Total Rewards
for faction_state in self.faction_to_count_lookup.values():
self.reward += faction_state.reward
self.shareable_reward += faction_state.shareable_reward

# Check for Warnings
if len(target_factions) > 1:
Expand All @@ -68,9 +128,9 @@ def __init__(self, massacre_state: dict[int, MassacreMission]):
self.warnings.append(f"Multiple Target Systems: {', '.join(target_systems)}!")

# Calculate before_stack_height
for count, _a, _b in self.faction_to_count_lookup.values():
if count > self.before_stack_height and count != self.stack_height:
self.before_stack_height = count
for faction_state in self.faction_to_count_lookup.values():
if faction_state.killcount > self.before_stack_height and faction_state.killcount != self.stack_height:
self.before_stack_height = faction_state.killcount
if self.before_stack_height == 0: # No other elements. All at max value.
self.before_stack_height = self.stack_height

Expand Down Expand Up @@ -130,24 +190,23 @@ def __display_data_header(frame: tk.Frame, settings: GridUiSettings, row=0):
item.grid(row=row, column=i, sticky=tk.W)


def __display_row(frame: tk.Frame, faction: str, data: tuple[int, int, int], max_count: int,
def __display_row(frame: tk.Frame, faction: str, data: MassacreMissionData.FactionState, max_count: int,
settings: GridUiSettings, row: int, second_largest_count: int):
"""
Draw one Data-Row for the Table
"""
count, reward, shareable_reward = data
reward_str = "{:.1f}".format(float(reward) / 1_000_000)
shareable_reward_str = "{:.1f}".format(float(shareable_reward) / 1_000_000)
reward_str = "{:.1f}".format(float(data.reward) / 1_000_000)
shareable_reward_str = "{:.1f}".format(float(data.shareable_reward) / 1_000_000)

faction_label = tk.Label(frame, text=faction)
kills_label = tk.Label(frame, text=count)
kills_label = tk.Label(frame, text=data.killcount)
payout_label = tk.Label(frame, text=f"{reward_str} ({shareable_reward_str})")

ui_elements = [faction_label, kills_label, payout_label]

if settings.delta:
# Calculate difference
delta = max_count - count
delta = max_count - data.killcount
text = delta if delta > 0 else second_largest_count - max_count
delta_label = tk.Label(frame, text=str(text))
ui_elements.append(delta_label)
Expand Down Expand Up @@ -285,7 +344,7 @@ def update_ui(self):
# To be called from thread
def notify_version_outdated(self):
self.__display_outdated_version = True
self.__frame.event_generate("<<Refresh>>")
self.__frame.event_generate("<<Refresh>>") # type: ignore

# To be called from Button
def notify_version_outdated_dismissed(self):
Expand Down
2 changes: 1 addition & 1 deletion massacre/version_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def open_download_page():
if platform == "darwin":
subprocess.Popen(["open", download_url])
elif platform == "win32":
os.startfile(download_url)
os.startfile(download_url) # type: ignore
else:
try:
subprocess.Popen(["xdg-open", download_url])
Expand Down
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.2
2.0.3

0 comments on commit 14f7b93

Please sign in to comment.