diff --git a/SpanshRouter/SpanshRouter.py b/SpanshRouter/SpanshRouter.py index 577d2b1..eb41dc6 100644 --- a/SpanshRouter/SpanshRouter.py +++ b/SpanshRouter/SpanshRouter.py @@ -8,6 +8,7 @@ import json import re import requests import io +import ast from time import sleep from monitor import monitor from . import AutoCompleter @@ -16,18 +17,7 @@ from .updater import SpanshUpdater import tkinter as tk import tkinter.filedialog as filedialog import tkinter.messagebox as confirmDialog -import logging - -from config import appname - -# This could also be returned from plugin_start3() -plugin_name = os.path.basename(os.path.dirname(__file__)) - -# A Logger is used per 'found' plugin to make it easy to include the plugin's -# folder name in the logging output format. -# NB: plugin_name here *must* be the plugin's folder name as per the preceding -# code, else the logger won't be properly set up. -logger = logging.getLogger(f'{appname}.{plugin_name}') +from tkinter import * class SpanshRouter(): def __init__(self, plugin_dir): @@ -36,10 +26,13 @@ class SpanshRouter(): self.plugin_version = version_fd.read() self.update_available = False + self.roadtoriches = False self.next_stop = "No route planned" self.route = [] self.next_wp_label = "Next waypoint: " self.jumpcountlbl_txt = "Estimated jumps left: " + self.bodieslbl_txt = "Bodies to scan at: " + self.bodies = "" self.parent = None self.plugin_dir = plugin_dir self.save_route_path = os.path.join(plugin_dir, 'route.csv') @@ -50,6 +43,8 @@ class SpanshRouter(): self.error_txt = tk.StringVar() self.plot_error = "Error while trying to plot a route, please try again." self.system_header = "System Name" + self.bodyname_header = "Body Name" + self.bodysubtype_header = "Body Subtype" self.jumps_header = "Jumps" # -- GUI part -- @@ -60,9 +55,10 @@ class SpanshRouter(): # Route info self.waypoint_prev_btn = tk.Button(self.frame, text="^", command=self.goto_prev_waypoint) - self.waypoint_btn = tk.Button(self.frame, text=self.next_wp_label + self.next_stop, command=self.copy_waypoint) + self.waypoint_btn = tk.Button(self.frame, text=self.next_wp_label + '\n' + self.next_stop, command=self.copy_waypoint) self.waypoint_next_btn = tk.Button(self.frame, text="v", command=self.goto_next_waypoint) self.jumpcounttxt_lbl = tk.Label(self.frame, text=self.jumpcountlbl_txt + str(self.jumps_left)) + self.bodies_lbl = tk.Label(self.frame, justify=LEFT, text=self.bodieslbl_txt + self.bodies) self.error_lbl = tk.Label(self.frame, textvariable=self.error_txt) # Plotting GUI @@ -86,6 +82,8 @@ class SpanshRouter(): row += 1 self.waypoint_next_btn.grid(row=row, columnspan=2) row += 1 + self.bodies_lbl.grid(row=row, columnspan=2, sticky=tk.W) + row += 1 self.source_ac.grid(row=row,columnspan=2, pady=(10,0)) # The AutoCompleter takes two rows to show the list when needed, so we skip one row += 2 self.dest_ac.grid(row=row,columnspan=2, pady=(10,0)) @@ -118,6 +116,7 @@ class SpanshRouter(): self.waypoint_btn.grid_remove() self.waypoint_next_btn.grid_remove() self.jumpcounttxt_lbl.grid_remove() + self.bodies_lbl.grid_remove() self.export_route_btn.grid_remove() self.clear_route_btn.grid_remove() @@ -131,6 +130,7 @@ class SpanshRouter(): self.waypoint_btn.grid_remove() self.waypoint_next_btn.grid_remove() self.jumpcounttxt_lbl.grid_remove() + self.bodies_lbl.grid_remove() self.export_route_btn.grid_remove() self.clear_route_btn.grid_remove() @@ -177,16 +177,23 @@ class SpanshRouter(): self.waypoint_btn.grid_remove() self.waypoint_next_btn.grid_remove() self.jumpcounttxt_lbl.grid_remove() + self.bodies_lbl.grid_remove() self.export_route_btn.grid_remove() self.clear_route_btn.grid_remove() else: - self.waypoint_btn["text"] = self.next_wp_label + self.next_stop + self.waypoint_btn["text"] = self.next_wp_label + '\n' + self.next_stop if self.jumps_left > 0: self.jumpcounttxt_lbl["text"] = self.jumpcountlbl_txt + str(self.jumps_left) self.jumpcounttxt_lbl.grid() else: self.jumpcounttxt_lbl.grid_remove() + if self.roadtoriches: + self.bodies_lbl["text"] = self.bodieslbl_txt + self.bodies + self.bodies_lbl.grid() + else: + self.bodies_lbl.grid_remove() + self.waypoint_prev_btn.grid() self.waypoint_btn.grid() self.waypoint_next_btn.grid() @@ -277,6 +284,7 @@ class SpanshRouter(): self.jumps_left += int(row[1]) self.next_stop = self.route[self.offset][0] + self.update_bodies_text() self.copy_waypoint() self.update_gui() @@ -319,6 +327,8 @@ class SpanshRouter(): self.update_gui() else: self.next_stop = self.route[self.offset][0] + self.update_bodies_text() + self.update_gui() self.copy_waypoint() self.save_offset() @@ -350,6 +360,7 @@ class SpanshRouter(): if ftype_supported: self.offset = 0 self.next_stop = self.route[0][0] + self.update_bodies_text() self.copy_waypoint() self.update_gui() self.save_all_route() @@ -363,23 +374,86 @@ class SpanshRouter(): self.show_error("An error occured while reading the file.") def plot_csv(self, filename, clear_previous_route=True): - with io.open(filename, 'r', encoding='utf-8-sig', newline='') as csvfile: - route_reader = csv.DictReader(csvfile) - + with io.open(filename, 'r', encoding='utf-8-sig', newline='') as csvfile: + self.roadtoriches = False if clear_previous_route: self.clear_route(False) - for row in route_reader: - # Skip adjacent duplicates for the Road to Riches CSVs - if self.route.__len__() > 0 and row[self.system_header] == self.route[-1][0]: - continue - if row not in (None, "", []): - self.route.append([ - row[self.system_header], - row.get(self.jumps_header, "") # Jumps column is optional - ]) - if row.get(self.jumps_header): # Jumps column is optional + + route_reader = csv.DictReader(csvfile) + + # Get column header names as string + headerline = ','.join(route_reader.fieldnames) + + # Define the differnt internal formats based on the CSV header row + internalbasicheader1 = "System Name" + internalbasicheader2 = "System Name,Jumps" + internalrichesheader = "System Name,Jumps,Body Name,Body Subtype" + # Define the differnt import file formats based on the CSV header row + neutronimportheader = "System Name,Distance To Arrival,Distance Remaining,Neutron Star,Jumps" + road2richesimportheader = "System Name,Body Name,Body Subtype,Is Terraformable,Distance To Arrival,Estimated Scan Value,Estimated Mapping Value,Jumps" + + if (headerline == internalbasicheader1) or (headerline == internalbasicheader2) or (headerline == neutronimportheader): + for row in route_reader: + if row not in (None, "", []): + self.route.append([ + row[self.system_header], + row.get(self.jumps_header, "") # Jumps column is optional + ]) + if row.get(self.jumps_header): # Jumps column is optional + self.jumps_left += int(row[self.jumps_header]) + + elif headerline == internalrichesheader: + self.roadtoriches = True + + for row in route_reader: + if row not in (None, "", []): + # Convert string representations of lists to actual Lists + bodynames = ast.literal_eval(row[self.bodyname_header]) + bodysubtypes = ast.literal_eval(row[self.bodysubtype_header]) + + self.route.append([ + row[self.system_header], + row[self.jumps_header], + bodynames, + bodysubtypes + ]) self.jumps_left += int(row[self.jumps_header]) + elif headerline == road2richesimportheader: + self.roadtoriches = True + + bodynames = [] + bodysubtypes = [] + + for row in route_reader: + bodyname = row[self.bodyname_header] + bodysubtype = row[self.bodysubtype_header] + + # Update the current system with additional bodies from new CSV row + if self.route.__len__() > 0 and row[self.system_header] == self.route[-1][0]: + self.route[-1][2].append(bodyname) + self.route[-1][3].append(bodysubtype) + continue + + if row not in (None, "", []): + bodynames.append(bodyname) + bodysubtypes.append(bodysubtype) + + self.route.append([ + row[self.system_header], + row[self.jumps_header], + bodynames.copy(), + bodysubtypes.copy() + ]) + # Clear bodies for next system + bodynames.clear() + bodysubtypes.clear() + + self.jumps_left += int(row[self.jumps_header]) + + else: + self.show_error("Could not detect file format") + def plot_route(self): self.hide_error() try: @@ -527,6 +601,7 @@ class SpanshRouter(): self.route = [] self.next_waypoint = "" self.jumps_left = 0 + self.roadtoriches = False try: os.remove(self.save_route_path) except: @@ -545,10 +620,19 @@ class SpanshRouter(): def save_route(self): if self.route.__len__() != 0: with open(self.save_route_path, 'w', newline='') as csvfile: - fieldnames = [self.system_header, self.jumps_header] - writer = csv.writer(csvfile) - writer.writerow(fieldnames) - writer.writerows(self.route) + if not self.roadtoriches: + # Write output: System, Jumps + fieldnames = [self.system_header, self.jumps_header] + writer = csv.writer(csvfile) + writer.writerow(fieldnames) + writer.writerows(self.route) + else: + # Write output: System, Jumps, Bodies[], BodySubTypes[] + fieldnames = [self.system_header, self.jumps_header, self.bodyname_header, self.bodysubtype_header] + writer = csv.writer(csvfile) + writer.writerow(fieldnames) + for row in self.route: + writer.writerow(row) else: try: os.remove(self.save_route_path) @@ -565,6 +649,47 @@ class SpanshRouter(): except: print("No offset to delete") + def update_bodies_text(self): + if not self.roadtoriches: return + + # For the bodies to scan use the current system, which is one before the next stop + lastsystemoffset = self.offset - 1 + if lastsystemoffset < 0: + lastsystemoffset = 0 # Display bodies of the first system + + lastsystem = self.route[lastsystemoffset][0] + bodynames = self.route[lastsystemoffset][2] + bodysubtypes = self.route[lastsystemoffset][3] + + waterbodies = [] + rockybodies = [] + metalbodies = [] + earthlikebodies = [] + unknownbodies = [] + + for num, name in enumerate(bodysubtypes): + shortbodyname = bodynames[num].replace(lastsystem + " ", "") + if name.lower() == "high metal content world": + metalbodies.append(shortbodyname) + elif name.lower() == "rocky body": + rockybodies.append(shortbodyname) + elif name.lower() == "earth-like world": + earthlikebodies.append(shortbodyname) + elif name.lower() == "water world": + waterbodies.append(shortbodyname) + else: + unknownbodies.append(shortbodyname) + + bodysubtypeandname = "" + if len(metalbodies) > 0: bodysubtypeandname += f"\n Metal: " + ', '.join(metalbodies) + if len(rockybodies) > 0: bodysubtypeandname += f"\n Rocky: " + ', '.join(rockybodies) + if len(earthlikebodies) > 0: bodysubtypeandname += f"\n Earth: " + ', '.join(earthlikebodies) + if len(waterbodies) > 0: bodysubtypeandname += f"\n Water: " + ', '.join(waterbodies) + if len(unknownbodies) > 0: bodysubtypeandname += f"\n Unknown: " + ', '.join(unknownbodies) + + self.bodies = f"\n{lastsystem}:{bodysubtypeandname}" + + def check_range(self, name, index, mode): value = self.range_entry.var.get() if value.__len__() > 0 and value != self.range_entry.placeholder: