2.8.0.0 - #4
Conversation
There was a problem hiding this comment.
Pull request overview
This PR represents version 2.8.0.0, which refactors the project structure to improve cross-platform compatibility and adds new GUI features for resource and event management. The changes include replacing bash scripts with Python equivalents, implementing proper window lifecycle management, and adding new dialogs for creating resources and events dynamically.
Key changes:
- Replaced
clean.shandtests/cleaner.pywith a cross-platformclean.pyscript and unified test runnertest.py - Added new GUI components:
ResAdderandEventDefinerfor dynamic resource/event creation - Implemented proper window cleanup handlers with
on_closingmethods to prevent orphaned windows - Removed unused
Pool.pymodule and improved threading inTaskRemover
Reviewed changes
Copilot reviewed 19 out of 22 changed files in this pull request and generated 38 comments.
Show a summary per file
| File | Description |
|---|---|
| test.py | New unified test runner using unittest's test discovery |
| clean.py | Cross-platform Python replacement for clean.sh bash script |
| modules/gui_core/ResAdder.py | New dialog for adding/modifying resources dynamically |
| modules/gui_core/EventDeffiner.py | New dialog for creating custom events at runtime |
| modules/handlers.py | Added _save_resources method to persist resource changes |
| modules/calendar.py | Added _save_tasks method and improved directory creation handling |
| modules/gui_core/TaskRemover.py | Fixed threading issues by separating thread creation from start |
| modules/gui_core/TaskCreator.py | Removed unnecessary comment |
| modules/gui_core/init.py | Added import for new ResAdder module |
| main.py | Added window lifecycle management, new UI buttons, and proper cleanup on exit |
| templates/resources.json | Changed CPU count from 2 to 1, added new "candela" resource |
| templates/tasks.json | Reformatted with consistent 3-space indentation |
| saved/used_resources.json | New file containing resource usage tracking data |
| saved/actives_events.json | New file containing active event state |
| templates/icon.ico | New application icon file |
| README.md | Updated documentation to reflect new file structure and commands |
| changelog | Added version 2.8.0.0 release notes |
| .github/workflows/python-app.yml | Updated test command from runtests.py to test.py |
| tests/cleaner.py | Removed bash script wrapper (replaced by clean.py) |
| clean.sh | Removed bash-specific cleanup script |
| modules/Pool.py | Removed unused thread pool implementation |
| .todo | Removed completed todo file |
Comments suppressed due to low confidence (8)
modules/calendar.py:25
- Testing for None should use the 'is' operator.
if filename == None:
modules/handlers.py:30
- Testing for None should use the 'is' operator.
if filename == None:
modules/gui_core/EventDeffiner.py:49
- This assignment to 'B' is unnecessary as it is redefined before this value is used.
B = set()
modules/calendar.py:51
- Except block directly handles BaseException.
except:
main.py:179
- Except block directly handles BaseException.
except:
main.py:184
- Except block directly handles BaseException.
except:
modules/calendar.py:29
- File may not be closed if this operation raises an exception.
tmp = open(filename,'w')
modules/handlers.py:34
- File may not be closed if this operation raises an exception.
tmp = open(filename,'w')
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| CTkLabel(self, text="Resource added successfully!").pack(pady=10) | ||
| self.add_button.configure(text="exit", command=self.destroy) | ||
|
|
||
| def add_resource(self, res_name:str, inc:int, need:list = [], witout = []): |
There was a problem hiding this comment.
Typo in parameter name: witout should be without. This inconsistency with the rest of the codebase (which uses without) could lead to confusion.
| from customtkinter import * | ||
| from CTkMessagebox import CTkMessagebox | ||
|
|
||
| class RessAdder(CTk): |
There was a problem hiding this comment.
Class name typo: RessAdder should be ResAdder (resource, not "ress"). This inconsistency affects the class name used throughout the application.
| class RessAdder(CTk): | |
| class ResAdder(CTk): |
| if calendar.resources[res_name]["count"] < abs(inc) and inc: | ||
| CTkMessagebox(self,200, 200 , title="Error", message="Please enter a valid resource name and count.") | ||
| return |
There was a problem hiding this comment.
Potential logic error on line 44: The condition and inc at the end seems incorrect. When inc is negative and its absolute value exceeds available resources, the function should return False, not continue. The and inc check doesn't make logical sense here - if inc is 0, we shouldn't get to this branch. Consider replacing with and inc < 0 or removing this condition entirely if the check should apply for both positive and negative increments.
| if calendar.resources[res_name]["count"] < abs(inc) and inc: | |
| CTkMessagebox(self,200, 200 , title="Error", message="Please enter a valid resource name and count.") | |
| return | |
| if calendar.resources[res_name]["count"] < abs(inc) and inc < 0: | |
| CTkMessagebox(self,200, 200 , title="Error", message="Please enter a valid resource name and count.") | |
| return False |
| for res in res_names: | ||
| need.append([res,self.GetCounts(res)]) | ||
|
|
||
| if False == self.create_event(event_name, need): |
There was a problem hiding this comment.
[nitpick] Non-Pythonic comparison style on line 73. Instead of if False == self.create_event(...), use the more conventional if not self.create_event(...) or if self.create_event(...) == False. The "Yoda condition" style is discouraged in Python (PEP 8).
| if False == self.create_event(event_name, need): | |
| if not self.create_event(event_name, need): |
| @@ -1,3 +1,9 @@ | |||
| 2.8.0.0 dev | |||
| Added on_closing handler to properly destroy all child windows (TaskCreator, TaskRemover, EventCreator, RessAdder) on exit | |||
| Added EventDeffiner and ResAdder files | |||
There was a problem hiding this comment.
Typo in changelog: "EventDeffiner" should be "EventDefiner" (one 'f'). This matches the filename spelling issue found in the actual file.
| Added EventDeffiner and ResAdder files | |
| Added EventDefiner and ResAdder files |
| except: | ||
| pass |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except: | |
| pass | |
| except FileExistsError: | |
| # Directory already exists, safe to ignore | |
| pass | |
| except Exception as e: | |
| log(f"Unexpected error creating directory {SAVE_ROOT}:", e) |
| except: | ||
| pass | ||
| if self.task_remover is not None: | ||
| try: | ||
| self.task_remover.destroy() | ||
| except: | ||
| pass | ||
| if self.ev_creator is not None: | ||
| try: | ||
| self.ev_creator.destroy() | ||
| except: | ||
| pass | ||
| if self.res_adder is not None: | ||
| try: | ||
| self.res_adder.destroy() | ||
| except: | ||
| pass |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except: | |
| pass | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except: | |
| pass | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except: | |
| pass | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except: | |
| pass | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") |
| except: | ||
| pass | ||
| if self.task_remover is not None: | ||
| try: | ||
| self.task_remover.destroy() | ||
| except: | ||
| pass | ||
| if self.ev_creator is not None: | ||
| try: | ||
| self.ev_creator.destroy() | ||
| except: | ||
| pass | ||
| if self.res_adder is not None: | ||
| try: | ||
| self.res_adder.destroy() | ||
| except: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except: | |
| pass | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except: | |
| pass | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except: | |
| pass | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except: | |
| except Exception: | |
| # Ignore errors during destroy; object may already be destroyed | |
| pass | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except Exception: | |
| # Ignore errors during destroy; object may already be destroyed | |
| pass | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except Exception: | |
| # Ignore errors during destroy; object may already be destroyed | |
| pass | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except Exception: | |
| # Ignore errors during destroy; object may already be destroyed |
| except: | ||
| pass | ||
| if self.task_remover is not None: | ||
| try: | ||
| self.task_remover.destroy() | ||
| except: | ||
| pass | ||
| if self.ev_creator is not None: | ||
| try: | ||
| self.ev_creator.destroy() | ||
| except: | ||
| pass | ||
| if self.res_adder is not None: | ||
| try: | ||
| self.res_adder.destroy() | ||
| except: | ||
| pass |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except: | |
| pass | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except: | |
| pass | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except: | |
| pass | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except: | |
| pass | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except Exception as e: | |
| log(f"already destroyed [{e}]") |
| except: | ||
| pass | ||
| if self.task_remover is not None: | ||
| try: | ||
| self.task_remover.destroy() | ||
| except: | ||
| pass | ||
| if self.ev_creator is not None: | ||
| try: | ||
| self.ev_creator.destroy() | ||
| except: | ||
| pass | ||
| if self.res_adder is not None: | ||
| try: | ||
| self.res_adder.destroy() | ||
| except: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except: | |
| pass | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except: | |
| pass | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except: | |
| pass | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except: | |
| except: | |
| # Ignore errors during widget destruction; widget may already be destroyed. | |
| pass | |
| if self.task_remover is not None: | |
| try: | |
| self.task_remover.destroy() | |
| except: | |
| # Ignore errors during widget destruction; widget may already be destroyed. | |
| pass | |
| if self.ev_creator is not None: | |
| try: | |
| self.ev_creator.destroy() | |
| except: | |
| # Ignore errors during widget destruction; widget may already be destroyed. | |
| pass | |
| if self.res_adder is not None: | |
| try: | |
| self.res_adder.destroy() | |
| except: | |
| # Ignore errors during widget destruction; widget may already be destroyed. |
No description provided.