Edit and remove devices from menu now

This commit is contained in:
Profitroll 2023-01-25 14:01:13 +01:00
parent 6add3ac976
commit 3666db25c6
4 changed files with 223 additions and 44 deletions

View File

@ -0,0 +1,10 @@
# from typing import Union
# from ttkthemes import ThemedTk
# from classes.custom.toplevel_dialog import ToplevelDialog
# def askyesno(parent: ThemedTk, title: Union[str, None], message: Union[str, None]):
# toplevel = ToplevelDialog(parent, length=250, width=150)
# toplevel.grab_set()

View File

@ -0,0 +1,39 @@
# import sv_ttk
# from classes.custom.themed_frame import ThemedFrame
# from classes.custom.themed_toplevel import ThemedToplevel
# from modules.theme_titlebar import theme_title_bar
# from modules.utils import resize_window, set_icon, use_dark_mode
# class ToplevelDialog(ThemedToplevel):
# def __init__(self, parent, length: int, width: int):
# super().__init__(parent)
# resize_window(self, length, width)
# self.overrideredirect(True)
# self.resizable(False, False)
# sv_ttk.init_theme(self)
# if use_dark_mode():
# theme_title_bar(self, mode="dark")
# self.update()
# set_icon(self)
# self.focus_set()
# self.grid_columnconfigure(0, weight=1)
# self.window_frame = ThemedFrame(self)
# self.window_frame.grid(column=0, row=0, sticky=NSEW)
# # self.window_frame["borderwidth"] = 1
# # self.window_frame["relief"] = "solid"
# self.window_frame.grid_columnconfigure(0, weight=1)

View File

@ -1,14 +1,159 @@
from datetime import datetime from datetime import datetime
from functools import partial from functools import partial
from tkinter import LEFT, NSEW, E, W, ttk import platform
from tkinter.messagebox import askyesno from tkinter import LEFT, NSEW, E, W, Misc, StringVar, ttk
from tkinter import messagebox
from tkinter.messagebox import askyesno, showinfo
from tkinter.simpledialog import askstring
from urllib.parse import quote, urlencode
import requests import requests
from ttkthemes import ThemedTk from ttkthemes import ThemedTk
from classes.custom.scrollable_frame import ScrollableFrame from classes.custom.scrollable_frame import ScrollableFrame
from classes.custom.themed_frame import ThemedFrame from classes.custom.themed_frame import ThemedFrame
from modules.utils import configGet from modules.utils import configGet, configSet
from modules.logger import logger
class FrameDevice(ThemedFrame):
def __init__(self, master: Misc, device_dict: str, **kwargs) -> None:
super().__init__(master, style="Card.TFrame", **kwargs)
self["borderwidth"] = 1
self["relief"] = "solid"
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=3)
self.grid_columnconfigure(2, weight=3)
self.name = device_dict["name"]
self.title = ttk.Label(self, text=self.name, font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
self.title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
last_upload = "N/A" if device_dict["last_save"] == 0 else datetime.utcfromtimestamp(device_dict["last_save"]).strftime("%d.%m.%Y %H:%M")
self.description = ttk.Label(self, text=f'OS: {device_dict["os"]}\nClient: {device_dict["client"]}\nLast upload: {last_upload}', width=46)
self.description.grid(column=0, row=1, padx=9, pady=9, sticky=W)
self.buttons = ThemedFrame(self)
self.buttons.grid(column=0, columnspan=2, row=2, sticky=NSEW, padx=9, pady=9)
self.buttons.grid_columnconfigure(0, weight=1)
self.button_device_rename_action = partial(self.rename)
self.button_device_rename = ttk.Button(self.buttons, text="Rename", width=11, command=self.button_device_rename_action)
self.button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
self.button_device_delete_action = partial(self.delete)
self.button_device_delete = ttk.Button(self.buttons, text="Delete", style="Accent.TButton", width=11, command=self.button_device_delete_action)
self.button_device_delete.grid(column=1, row=0, sticky=W)
if self.name == configGet("name"):
# button_device_rename.state(["disabled"])
self.button_device_delete.state(["disabled"])
def rename(self):
self.rename_entry = ttk.Entry(self, font=("SunValleyBodyFont", 12), justify=LEFT, width=27)
self.rename_entry.insert(0, self.name)
self.rename_entry.grid(column=0, row=0, padx=9, pady=9, sticky=W)
button_device_cancel_action = partial(self.rename_cancel)
button_device_cancel = ttk.Button(self.buttons, text="Cancel", width=11, command=button_device_cancel_action)
button_device_cancel.grid(column=0, row=0, padx=9, sticky=E)
button_device_save_action = partial(self.rename_verify)
button_device_save = ttk.Button(self.buttons, text="Save", style="Accent.TButton", width=11, command=button_device_save_action)
button_device_save.grid(column=1, row=0, sticky=W)
def rename_verify(self):
self.name_before = configGet("name")
if (self.rename_entry.get().strip() == "") or ("?" in self.rename_entry.get().strip()) or ("/" in self.rename_entry.get().strip()):
logger.error(f"Name {self.rename_entry.get().strip()} is not a valid name")
messagebox.showerror(title="Name error", message="Provided device name is not valid. Please provide a valid one.")
return
try:
quote(self.rename_entry.get().strip())
except:
logger.error(f"Name {self.rename_entry.get().strip()} is not a valid name")
messagebox.showerror(title="Name error", message="Provided device name is not valid. Please provide a valid one.")
return
existing_device_before = requests.get(f'{configGet("address")}/devices/{self.name}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
if existing_device_before.status_code == 200:
response = requests.patch(f'{configGet("address")}/devices/{self.name}?{urlencode({"new_name": self.rename_entry.get().strip(), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.master.master.master.__version__}"})}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
if response.status_code != 204:
logger.error(f"Name {self.rename_entry.get().strip()} could not be set because server returned {response.status_code}")
messagebox.showerror(title="Name error", message=f"Provided device name is not valid.\n\nServer response: {response.json()}")
return
else:
logger.error(f"Tried to rename {self.name} into {self.rename_entry.get().strip()} but server returned {existing_device_before.status_code}")
messagebox.showerror(title="Rename error", message="It seems like this device no longer exists.")
return
self.name = self.rename_entry.get().strip()
for widget in self.winfo_children():
if isinstance(widget, ttk.Entry):
widget.destroy()
device_title = ttk.Label(self, text=self.name, font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
button_device_rename_action = partial(self.rename)
button_device_rename = ttk.Button(self.buttons, text="Rename", width=11, command=button_device_rename_action)
button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
button_device_delete_action = partial(self.delete)
button_device_delete = ttk.Button(self.buttons, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action)
button_device_delete.grid(column=1, row=0, sticky=W)
if self.name_before == configGet("name"):
configSet(["name"], self.name)
if self.name == configGet("name"):
button_device_delete.state(["disabled"])
def rename_cancel(self):
for widget in self.winfo_children():
if isinstance(widget, ttk.Entry):
widget.destroy()
device_title = ttk.Label(self, text=self.name, font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
button_device_rename_action = partial(self.rename)
button_device_rename = ttk.Button(self.buttons, text="Rename", width=11, command=button_device_rename_action)
button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
button_device_delete_action = partial(self.delete)
button_device_delete = ttk.Button(self.buttons, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action)
button_device_delete.grid(column=1, row=0, sticky=W)
if self.name == configGet("name"):
button_device_delete.state(["disabled"])
def delete(self):
decision = askyesno(title="Device removal", message=f"You are about to remove the device '{self.name}' and this will also remove all the save files uploaded by this device. Are you sure you want to continue?")
if decision is False:
return
requests.delete(f'{configGet("address")}/devices/{self.name}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
for k in range(len(self.master.devices)):
if self.master.devices[k]["name"] == self.name:
del self.master.devices[k]
break
self.destroy()
class FrameDevices(ScrollableFrame): class FrameDevices(ScrollableFrame):
@ -36,37 +181,38 @@ class FrameDevices(ScrollableFrame):
i = 0 i = 0
for device in self.devices: for device in self.devices:
device_frame = ThemedFrame(self, style="Card.TFrame") device_frame = FrameDevice(self, device_dict=device)
device_frame.grid(column=0, row=i, pady=9, padx=9, sticky=NSEW) device_frame.grid(column=0, row=i, pady=9, padx=9, sticky=NSEW)
# self.device_frame["borderwidth"] = 1 # self.device_frame["borderwidth"] = 1
# self.device_frame["relief"] = "solid" # self.device_frame["relief"] = "solid"
device_frame.grid_columnconfigure(0, weight=1) # device_frame.grid_columnconfigure(0, weight=1)
device_frame.grid_columnconfigure(1, weight=3) # device_frame.grid_columnconfigure(1, weight=3)
device_frame.grid_columnconfigure(2, weight=3) # device_frame.grid_columnconfigure(2, weight=3)
device_title = ttk.Label(device_frame, text=device["name"], font=("SunValleyBodyFont", 12), justify=LEFT, width=46) # device_title = ttk.Label(device_frame, text=device["name"], font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W) # device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
last_upload = "N/A" if device["last_save"] == 0 else datetime.utcfromtimestamp(device["last_save"]).strftime("%d.%m.%Y %H:%M") # last_upload = "N/A" if device["last_save"] == 0 else datetime.utcfromtimestamp(device["last_save"]).strftime("%d.%m.%Y %H:%M")
device_description = ttk.Label(device_frame, text=f'OS: {device["os"]}\nClient: {device["client"]}\nLast upload: {last_upload}', width=46) # device_description = ttk.Label(device_frame, text=f'OS: {device["os"]}\nClient: {device["client"]}\nLast upload: {last_upload}', width=46)
device_description.grid(column=0, row=1, padx=9, pady=9, sticky=W) # device_description.grid(column=0, row=1, padx=9, pady=9, sticky=W)
buttons_frame = ThemedFrame(device_frame) # buttons_frame = ThemedFrame(device_frame)
buttons_frame.grid(column=0, columnspan=2, row=2, sticky=NSEW, padx=9, pady=9) # buttons_frame.grid(column=0, columnspan=2, row=2, sticky=NSEW, padx=9, pady=9)
buttons_frame.grid_columnconfigure(0, weight=1) # buttons_frame.grid_columnconfigure(0, weight=1)
button_device_rename = ttk.Button(buttons_frame, text="Rename", width=11) # button_device_rename_action = partial(self.device_rename, device_frame, buttons_frame, device["name"])
button_device_rename.grid(column=0, row=0, padx=9, sticky=E) # button_device_rename = ttk.Button(buttons_frame, text="Rename", width=11, command=button_device_rename_action)
# button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
button_device_delete_action = partial(self.device_delete, device["name"]) # button_device_delete_action = partial(self.device_delete, device["name"])
button_device_delete = ttk.Button(buttons_frame, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action) # button_device_delete = ttk.Button(buttons_frame, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action)
button_device_delete.grid(column=1, row=0, sticky=W) # button_device_delete.grid(column=1, row=0, sticky=W)
if device["name"] == configGet("name"): # if device["name"] == configGet("name"):
# button_device_rename.state(["disabled"]) # # button_device_rename.state(["disabled"])
button_device_delete.state(["disabled"]) # button_device_delete.state(["disabled"])
i += 1 i += 1
@ -75,26 +221,6 @@ class FrameDevices(ScrollableFrame):
divider.grid(column=0, row=i+1, pady=9) divider.grid(column=0, row=i+1, pady=9)
i += 1 i += 1
def device_delete(self, name: str):
decision = askyesno(title="Device removal", message=f"You are about to remove the device '{name}' and this will also remove all the save files uploaded by this device. Are you sure you want to continue?")
if decision is False:
return
requests.delete(f'{configGet("address")}/devices/{name}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
for k in range(len(self.devices)):
print(k)
if self.devices[k]["name"] == name:
del self.devices[k]
break
for widget in self.winfo_children():
widget.destroy()
self.render_devices()
class FrameDevicesEmpty(ThemedFrame): class FrameDevicesEmpty(ThemedFrame):
def __init__(self, master: ThemedTk, **kwargs) -> None: def __init__(self, master: ThemedTk, **kwargs) -> None:

View File

@ -101,7 +101,11 @@ class ToplevelWelcome(ThemedToplevel):
self.stage_entry_1.insert(0, self.address_text[:-1]) self.stage_entry_1.insert(0, self.address_text[:-1])
try: try:
requests.get(self.stage_entry_1.get()+"/check", verify=not bool(self.stage_checkbox_var.get())) response_check = requests.get(self.stage_entry_1.get()+"/check", verify=not bool(self.stage_checkbox_var.get()))
if response_check.status_code != 200:
logger.error(f"Could not connect to '{self.stage_entry_1.get()}' because it returned {response_check.status_code}")
messagebox.showerror(title="Connection error", message=f"Server response is {response_check.status_code}. Check if your API server and it's reverse proxy (if there's one) properly configured and then try again.")
return
except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema, requests.exceptions.MissingSchema) as exp: except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema, requests.exceptions.MissingSchema) as exp:
logger.error(f"Could not validate '{self.stage_entry_1.get()}' due to {exp}") logger.error(f"Could not validate '{self.stage_entry_1.get()}' due to {exp}")
messagebox.showerror(title="Invalid address", message="Address entered does not seem to be correct one. Please provide API address starting with http:// or https:// \n\nFor example:\n- https://your-api.com:8043\n- https://your-api.com") messagebox.showerror(title="Invalid address", message="Address entered does not seem to be correct one. Please provide API address starting with http:// or https:// \n\nFor example:\n- https://your-api.com:8043\n- https://your-api.com")