diff --git a/.gitignore b/.gitignore index ba74660..9b04030 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ +# Created by .ignore support plugin (hsz.mobi) +### Python template # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] +*$py.class # C extensions *.so @@ -11,6 +14,7 @@ env/ build/ develop-eggs/ dist/ +builds/ downloads/ eggs/ .eggs/ @@ -42,6 +46,7 @@ htmlcov/ nosetests.xml coverage.xml *,cover +.hypothesis/ # Translations *.mo @@ -49,9 +54,105 @@ coverage.xml # Django stuff: *.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject +### VirtualEnv template +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +.Python +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +.idea/ + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# For approvals +*.received.txt + +#for mob timer sessions +/Infrastructure/Sessions/ \ No newline at end of file diff --git a/.idea/MobTimer.iml b/.idea/MobTimer.iml deleted file mode 100644 index 2d874a9..0000000 --- a/.idea/MobTimer.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/dictionaries/Chris.xml b/.idea/dictionaries/Chris.xml deleted file mode 100644 index 2b33936..0000000 --- a/.idea/dictionaries/Chris.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - mobber - mobbers - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 97626ba..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index fc41869..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 3b31283..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 97db8bc..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 627b78f..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index af12eab..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,1466 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Python - - - - - PyTypeCheckerInspection - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1448960251969 - - - 1448960352426 - - - 1449128502810 - - - 1449208527550 - - - 1449217011568 - - - 1449560317680 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e4a0637 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#263205", + "titleBar.activeBackground": "#354507", + "titleBar.activeForeground": "#F6FCE5" + }, + "python.testing.unittestArgs": [ + "-v", + "-s", + "./tests", + "-p", + "*test*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/BuildMobTimer.py b/BuildMobTimer.py new file mode 100644 index 0000000..996e71b --- /dev/null +++ b/BuildMobTimer.py @@ -0,0 +1,26 @@ +import os + +import subprocess +from shutil import copy, copytree, make_archive, rmtree +import time + +dist_path = './dist' +rmtree(dist_path) +py_installer_command = 'pyinstaller --clean --onefile --windowed MobTimer.spec'.split(" ") + +subprocess.run(py_installer_command) + +copy('./MobTimer.cfg', './dist/MobTimer.cfg') +copy('./time-bomb.ico', './dist/time-bomb.ico') +copytree('./Themes', './dist/Themes') +copytree('./Tips', './dist/Tips') +copytree('./Images', './dist/Images') + +time_for_filename = time.strftime("%Y%m%d-%H%M%S") +output_filename = 'MobTimer' + time_for_filename + +builds_path = './builds/' +if not os.path.exists(builds_path): + os.makedirs(builds_path) + +make_archive(builds_path + output_filename, 'zip', dist_path) diff --git a/Frames/MobTimerController.py b/Frames/MobTimerController.py index 9c434bf..79bc4d1 100644 --- a/Frames/MobTimerController.py +++ b/Frames/MobTimerController.py @@ -2,12 +2,15 @@ import os import uuid from tkinter import * +import sys from Frames.MinimalScreenBlockerFrame import MinimalScreenBlockerFrame from Frames.OuterFrame import OuterFrame from Frames.ScreenBlockerFrame import ScreenBlockerFrame from Frames.TransparentCountdownFrame import TransparentCountdownFrame from Infrastructure.CountdownManager import CountdownManager +from Infrastructure.DateTimeUtility import DateTimeUtility +from Infrastructure.DojoManager import DojoManager from Infrastructure.MobberManager import MobberManager from Infrastructure.PlatformUtility import PlatformUtility from Infrastructure.ScreenUtility import ScreenUtility @@ -16,16 +19,22 @@ from Infrastructure.ThemeManager import ThemeManager from Infrastructure.TimeSettingsManager import TimeSettingsManager from Infrastructure.TipsManager import TipsManager +from Infrastructure.EventLoggingManager import EventLoggingManager +from Infrastructure.FileUtilities import FileUtilities class MobTimerController(Tk): def __init__(self, *args, **kwargs): Tk.__init__(self, *args, **kwargs) + self.toggle_transparent_frame_position_function = self.toggle_transparent_frame_position_enabled self.transparent_frame_monitor_index = 0 self.transparent_frame_position_index = 0 self.settings_manager = SettingsManager() self.tips_manager = TipsManager() self.time_options_manager = TimeSettingsManager() + self.date_time_utility = DateTimeUtility() + self.file_utilities = FileUtilities() + self.event_logging_manager = EventLoggingManager(self.file_utilities, self.date_time_utility) self.mobber_manager = MobberManager(self.settings_manager.get_randomize_randomize_next_driver()) self.countdown_manager = CountdownManager(self) self.session_manager = SessionManager(uuid) @@ -36,7 +45,9 @@ def __init__(self, *args, **kwargs): self.quit_and_destroy_session() self.session_manager.create_session() - self.iconbitmap(default='time-bomb.ico') + if sys.platform != 'darwin': + self.iconbitmap(default='time-bomb.ico') + self.countdown_manager.subscribe_to_time_changes(self.show_screen_blocker_when_session_interupted) self.theme_manager = ThemeManager() @@ -65,7 +76,8 @@ def __init__(self, *args, **kwargs): container.grid_columnconfigure(0, weight=1) for frame_type in self.frame_types: frame_instance = frame_type(container, self, self.time_options_manager, self.mobber_manager, - self.countdown_manager, self.settings_manager, self.tips_manager, self.theme_manager) + self.countdown_manager, self.settings_manager, self.tips_manager, + self.theme_manager) self.frames[frame_type].append(frame_instance) frame_instance.grid(row=0, column=0, sticky=(N, S, E, W)) frame_instance.grid_rowconfigure(0, weight=1) @@ -77,9 +89,22 @@ def __init__(self, *args, **kwargs): self.transparent_frame_position = 0 self.title("Mob Timer") self.bind_all("", self.launch_transparent_countdown_if_blocking) - self.time_options_manager.set_countdown_time(self.settings_manager.get_timer_minutes(), self.settings_manager.get_timer_seconds()) + self.time_options_manager.set_countdown_time(self.settings_manager.get_timer_minutes(), + self.settings_manager.get_timer_seconds()) + + self.dojo_manager = DojoManager(self) + + if self.settings_manager.get_event_logging_enabled(): + self.mobber_manager.subscribe_to_mobber_add(self.add_mobber_callback) + self.mobber_manager.subscribe_to_mobber_remove(self.remove_mobber_callback) + + def remove_mobber_callback(self, mobber_name): + self.event_logging_manager.log(f'Removed: {mobber_name}') + + def add_mobber_callback(self, mobber_name): + self.event_logging_manager.log(f'Added: {mobber_name}') - def launch_transparent_countdown_if_blocking(self, event): + def launch_transparent_countdown_if_blocking(self, event=None): if self.frame_is_screen_blocking(): self.show_transparent_countdown_frame() @@ -87,6 +112,8 @@ def frame_is_screen_blocking(self): return self.last_frame == ScreenBlockerFrame or self.last_frame == MinimalScreenBlockerFrame def show_minimal_screen_blocker_frame(self): + self.toggle_transparent_frame_position_function = self.toggle_transparent_frame_position_enabled + self.theme_manager.reset_flashing_background_colors_to_normal() if self.last_frame != MinimalScreenBlockerFrame: self.launch_blocking_Frame(MinimalScreenBlockerFrame) self.mobber_manager.switch_next_driver() @@ -118,7 +145,8 @@ def show_frame(self, frame_class): container.deiconify() else: container.withdraw() - + container.focus_force() + container.focus_set() return switched_frames def show_screen_blocker_frame(self): @@ -143,7 +171,7 @@ def show_transparent_countdown_frame(self, extend_minutes=None, extend_seconds=N def get_current_window_geometry(self): return "{0}x{1}+0+0".format( - self.winfo_screenwidth(), self.winfo_screenheight()) + self.winfo_screenwidth(), self.winfo_screenheight()) def disable_resizing(self): for container in self.containers: @@ -156,6 +184,7 @@ def remove_title_bar(self): container.master.overrideredirect(1) def set_always_on_top(self): + for container in self.containers: container.master.wm_attributes("-topmost", True) if PlatformUtility.platform_is_mac(): @@ -165,6 +194,7 @@ def set_always_on_top(self): container.master.focus_force() def set_full_screen_always_on_top(self): + self.set_always_on_top() self.remove_title_bar() self.disable_resizing() @@ -173,7 +203,8 @@ def set_full_screen_always_on_top(self): monitor_string = "{}x{}+{}+{}".format(monitor.width, monitor.height, monitor.x, monitor.y) container.master.geometry(monitor_string) if not PlatformUtility.platform_is_mac(): - container.master.wait_visibility(container.master) # Mac removing this prevented the issue with the continue screen visibility + container.master.wait_visibility( + container.master) # Mac removing this prevented the issue with the continue screen visibility container.master.attributes("-alpha", 1) def set_partial_screen_transparent(self): @@ -195,28 +226,37 @@ def set_partial_screen_transparent(self): def fade_app(self): for controller in self.containers: - controller.master.attributes("-alpha", self.settings_manager.get_continue_screen_blocker_window_alpha_percent()) + controller.master.attributes("-alpha", + self.settings_manager.get_continue_screen_blocker_window_alpha_percent()) def unfade_app(self): for controller in self.containers: controller.master.attributes("-alpha", 1) + def flash_unobtrusive_transparent_countdown_frame(self): + self.toggle_transparent_frame_position_function = self.toggle_transparent_frame_position_disabled + for container in self.containers: + container.master.attributes("-alpha", 1) + container.focus_force() + container.focus_set() + + def toggle_transparent_frame_position_disabled(self): + pass + def toggle_transparent_frame_position(self, e=None): if self.state() == "withdrawn": return + self.toggle_transparent_frame_position_function() + def toggle_transparent_frame_position_enabled(self): monitors = ScreenUtility.get_monitors_or_default(self) monitor = monitors[self.transparent_frame_monitor_index] - screenwidth = monitor.width screenheight = monitor.height - self.set_always_on_top() self.remove_title_bar() self.disable_resizing() - size_percentage = self.settings_manager.get_transparent_window_screen_size_percent() - window_width = int(screenwidth * size_percentage) window_height = int(screenheight * size_percentage) if self.transparent_frame_position_index == 0: @@ -225,16 +265,15 @@ def toggle_transparent_frame_position(self, e=None): else: self.transparent_frame_position = monitor.x + 0 self.transparent_frame_position_index = (self.transparent_frame_position_index + 1) % 2 - - bottom_left_screen = "{}x{}+{}+{}".format(window_width, window_height, self.transparent_frame_position, monitor.y + + bottom_left_screen = "{}x{}+{}+{}".format(window_width, window_height, self.transparent_frame_position, + monitor.y + screenheight - window_height) self.geometry(bottom_left_screen) - def rewind_and_extend(self,minutes, seconds): + def rewind_and_extend(self, minutes, seconds): self.extensions_used += 1 self.mobber_manager.rewind_driver() result = self.show_transparent_countdown_frame(minutes, seconds) for minimal_frame in self.frames[MinimalScreenBlockerFrame]: minimal_frame.show_extend_time_button() return result - diff --git a/Frames/ScreenBlockerFrame.py b/Frames/ScreenBlockerFrame.py index 96abd12..316ad4b 100644 --- a/Frames/ScreenBlockerFrame.py +++ b/Frames/ScreenBlockerFrame.py @@ -35,15 +35,19 @@ def mobber_list_change_callback(self, mobber_list, driver_index, navigator_index for index in range(0, mobber_list.__len__()): tags = () name = mobber_list[index] - if index == driver_index: - tags = (TAGNAME_CURRENT_MOBBER) - name += " <= Current" - if index == navigator_index: - name += " <= Next" + if self.controller.settings_manager.get_dojo_enabled(): + if self.controller.dojo_manager.station_drivers.__contains__(name): + name += " <= {}".format(self.controller.dojo_manager.station_drivers[name]) + else: + if index == driver_index: + tags = TAGNAME_CURRENT_MOBBER + name += " <= Current" + if index == navigator_index: + name += " <= Next" self.names_list.insert('', END, text=name, tags=tags) - def time_change_callback(self, time, minutes, seconds): + def time_change_callback(self, time, minutes, seconds, origin_station_name=None): self.label_minutes['text'] = "{0:0>2}".format(minutes) self.label_seconds['text'] = "{0:0>2}".format(seconds) @@ -199,11 +203,9 @@ def remove_mobber_if_screen_blocking(self, event): def add_default_team(self, event): team = self.settings_manager.get_general_team().split(',') randomize_team = self.settings_manager.get_randomize_team() - self.mobber_manager.clear() if randomize_team: random.shuffle(team) - for member in team: - self.mobber_manager.add_mobber(member) + self.mobber_manager.set_mobber_list(team) def focus_mobber_entry(self): self.add_mobber_entry.focus_set() diff --git a/Frames/TransparentCountdownFrame.py b/Frames/TransparentCountdownFrame.py index 08583a6..7b1ee8e 100644 --- a/Frames/TransparentCountdownFrame.py +++ b/Frames/TransparentCountdownFrame.py @@ -6,7 +6,7 @@ class TransparentCountdownFrame(ttk.Frame): def __init__(self, master, controller, time_options_manager, mobber_manager, countdown_manager, settings_manager, - tips_manager,theme_manager, + tips_manager, theme_manager, **kwargs): super().__init__(master, **kwargs) self.master = master @@ -18,11 +18,24 @@ def __init__(self, master, controller, time_options_manager, mobber_manager, cou self.mobber_manager.subscribe_to_mobber_list_change(self.mobber_list_change_callback) self.countdown_manager = countdown_manager self.countdown_manager.subscribe_to_time_changes(self.update_time_change_callback) + self.unobtrusive_mode_enabled = self.settings_manager.get_general_enable_unobtrusive_mode() - def update_time_change_callback(self, days, minutes, seconds): - if (days < 0 or minutes < 0 or seconds < 0) and not self.controller.frame_is_screen_blocking(): + + def reset_theme_and_continue_mobbing(self): + if self.unobtrusive_mode_enabled: self.controller.show_minimal_screen_blocker_frame() + self.controller.theme_manager.reset_flashing_background_colors_to_normal() + + def update_time_change_callback(self, days, minutes, seconds): self.label_time['text'] = "{0:0>2}:{1:0>2}".format(minutes, seconds) + if (days < 0 or minutes < 0 or seconds < 0) and not self.controller.frame_is_screen_blocking(): + if self.unobtrusive_mode_enabled: + self.controller.flash_unobtrusive_transparent_countdown_frame() + self.controller.theme_manager.toggle_flashing_background_style() + self.label_time['text'] = "Click&Rotate!" + else: + self.controller.show_minimal_screen_blocker_frame() + def mobber_list_change_callback(self, mobber_list, driver_index, navigator_index): mobber_count = mobber_list.__len__() @@ -59,6 +72,12 @@ def create_frame_content(self): self.label_driver.pack() row_index += 1 + + self.bind("", lambda event: self.reset_theme_and_continue_mobbing()) + self.label_time.bind("", lambda event: self.reset_theme_and_continue_mobbing()) + self.label_navigator.bind("", lambda event: self.reset_theme_and_continue_mobbing()) + self.label_driver.bind("", lambda event: self.reset_theme_and_continue_mobbing()) + def get_navigator_text(self, name): return "Next: " + name diff --git a/Infrastructure/CountdownManager.py b/Infrastructure/CountdownManager.py index ec038e6..7fed3d5 100644 --- a/Infrastructure/CountdownManager.py +++ b/Infrastructure/CountdownManager.py @@ -8,7 +8,7 @@ def __init__(self, root_tk_app): self.minutes = 0 self.seconds = 0 self.time_change_callbacks = [] - self.count_down_total = datetime.timedelta(minutes=10, seconds=0) + self.count_down_total = datetime.timedelta(days=-1, minutes=0, seconds=0) self.root_tk_app = root_tk_app self.refresh_timer() diff --git a/Infrastructure/DateTimeUtility.py b/Infrastructure/DateTimeUtility.py new file mode 100644 index 0000000..8202849 --- /dev/null +++ b/Infrastructure/DateTimeUtility.py @@ -0,0 +1,6 @@ +import datetime + + +class DateTimeUtility: + def get_timestamp(self): + return datetime.datetime.now().timestamp() diff --git a/Infrastructure/DojoManager.py b/Infrastructure/DojoManager.py new file mode 100644 index 0000000..6741dc9 --- /dev/null +++ b/Infrastructure/DojoManager.py @@ -0,0 +1,149 @@ +import _thread +import random +import uuid +import paho.mqtt.publish as mqtt_pub +import paho.mqtt.client as mqtt +import json + +TOPIC_MOBBER_LIST = "MobberList" + +TOPIC_TIME_CHANGE = "TimeChange" + +TOPIC_SAY_HELLO = "SayHello" + +TOPIC_START_TIMER = "StartTimer" + + +class DojoManager(object): + def __init__(self, controller): + if not controller.settings_manager.get_dojo_enabled(): + return + + self.time_change_increment = 0 + self.mobber_change_increment = 0 + self.session_id = uuid.uuid4().__str__() + + self.switch_dictionary = { + TOPIC_MOBBER_LIST: self.sub_mobber_list, + TOPIC_TIME_CHANGE: self.sub_time_change, + TOPIC_SAY_HELLO: self.sub_say_hello, + TOPIC_START_TIMER: self.sub_start_timer, + } + self.station_drivers = {} + self.other_stations = [] + self.controller = controller + self.dojo_broker = self.controller.settings_manager.get_dojo_broker() + self.dojo_port = self.controller.settings_manager.get_dojo_port() + self.dojo_station_uuid = uuid.uuid4().__str__() + self.dojo_mob_station_name = self.controller.settings_manager.get_dojo_mob_station_name() + self.dojo_session_id = self.controller.settings_manager.get_dojo_session_id() + self.dojo_topic_root = self.controller.settings_manager.get_dojo_topic_root() + self.controller.mobber_manager.subscribe_to_mobber_list_change(self.publish_mobber_list_changes) + self.controller.time_options_manager.subscribe_to_timechange(self.publish_time_change) + self.controller.countdown_manager.subscribe_to_time_changes(self.publish_countdown_change) + self.subscribe_to_time_change() + self.say_hello() + + def say_hello(self): + topic = self.generate_topic(TOPIC_SAY_HELLO) + self.publish(topic, "") + + def publish_countdown_change(self, negative_if_time_expired, minutes, seconds, origin_station_name=None): + if negative_if_time_expired < 0: return + print("publish_countdown_change" , minutes, seconds) + topic = self.generate_topic(TOPIC_START_TIMER) + self.publish(topic, "") + + def publish_time_change(self, time_string, minutes, seconds, origin_station_name=None): + topic = self.generate_topic(TOPIC_TIME_CHANGE) + station_name = self.dojo_mob_station_name + station_uuid = self.dojo_station_uuid + if origin_station_name is not None: + return + payload_dictionary = { + "minutes": minutes, + "seconds": seconds, + "station_name": station_name, + "station_uuid": station_uuid + } + payload = json.dumps(payload_dictionary) + self.publish(topic, payload) + + def publish_mobber_list_changes(self, mobber_list, driver_index, next_driver_index): + topic = self.generate_topic(TOPIC_MOBBER_LIST) + payload_object = { + "driver_index": driver_index, + "next_driver_index": next_driver_index, + "mobber_list": mobber_list + } + payload = json.dumps(payload_object) + self.publish(topic, payload) + + def publish(self, topic, payload): + _thread.start_new_thread(self.thread_publish, (topic, payload)) + + def thread_publish(self, topic, payload): + mqtt_pub.single(topic, hostname=self.dojo_broker, port=self.dojo_port, payload=payload) + + def subscribe_to_time_change(self): + _thread.start_new_thread(self.subscribe_to_dojo, ()) + + def on_connect(self, client, userdata, flags, rc): + topic = "{}/{}/#".format(self.dojo_topic_root, self.dojo_session_id) + client.subscribe(topic) + + def on_message(self, client, userdata, msg): + topic_parts = msg.topic.split('/') + topic_root = topic_parts[0] + session_id = topic_parts[1] + station_uuid = topic_parts[2] + station_name = topic_parts[3] + message_type = topic_parts[4] + print("on_message", msg.topic, "station_uuid", station_uuid) + self.switch_statement_dictionary_trick(station_uuid, station_name, message_type, msg.payload) + + def switch_statement_dictionary_trick(self, station_uuid, station_name, message_type, payload): + self.switch_dictionary[message_type](station_uuid, station_name, message_type, payload) + + def sub_mobber_list(self, station_uuid, station_name, message_type, payload): + if not station_uuid == self.dojo_station_uuid: + payload_dictionary = json.loads(payload.decode("utf-8")) + mobber_list = payload_dictionary["mobber_list"] + print("sub_mobber_list", mobber_list) + self.controller.mobber_manager.set_mobber_list(mobber_list) + + def generate_topic(self, message_type): + topic = "{}/{}/{}/{}/{}".format(self.dojo_topic_root, self.dojo_session_id, self.dojo_station_uuid, + self.dojo_mob_station_name, + message_type) + print("generate_topic", topic) + return topic + + def sub_time_change(self, station_uuid, station_name, message_type, payload): + print(payload) + if not station_uuid == self.dojo_station_uuid: + payload_dictionary = json.loads(payload.decode("utf-8")) + minutes = payload_dictionary["minutes"] + seconds = payload_dictionary["seconds"] + origin_station_name = payload_dictionary["station_name"] + if not ( + self.controller.time_options_manager.minutes == minutes and self.controller.time_options_manager.seconds == seconds): + self.controller.time_options_manager.set_countdown_time(minutes, seconds, origin_station_name) + + def sub_say_hello(self, station_uuid, station_name, message_type, payload): + if not station_uuid == self.dojo_station_uuid: + if not self.other_stations.__contains__(station_name): + self.other_stations.append(station_name) + topic = self.generate_topic(TOPIC_SAY_HELLO) + self.publish(topic, "") + + def sub_start_timer(self, station_uuid, station_name, message_type, payload): + if not station_uuid == self.dojo_station_uuid: + self.controller.launch_transparent_countdown_if_blocking() + + def subscribe_to_dojo(self): + client = mqtt.Client() + client.on_connect = self.on_connect + client.on_message = self.on_message + client.connect(self.dojo_broker, self.dojo_port, 60) + client.loop_forever() diff --git a/Infrastructure/EventLoggingManager.py b/Infrastructure/EventLoggingManager.py new file mode 100644 index 0000000..a6bf60f --- /dev/null +++ b/Infrastructure/EventLoggingManager.py @@ -0,0 +1,16 @@ +from Infrastructure.FileUtilities import FileUtilities + + +class EventLoggingManager: + + def __init__(self, file_utility, date_time_utility): + self.file_utility = file_utility + self.file_path = self.file_utility.go_up_dirs(self.file_utility.get_root_path(), 1) + "\\MobTimerEvents.log" + + self.date_time_utility = date_time_utility + + if not self.file_utility.file_exists(self.file_path): + self.file_utility.create_file(self.file_path) + + def log(self, data): + self.file_utility.append(self.file_path, f'\n{self.date_time_utility.get_timestamp()} {data}') diff --git a/Infrastructure/FileUtilities.py b/Infrastructure/FileUtilities.py index 3ae8bb5..382980a 100644 --- a/Infrastructure/FileUtilities.py +++ b/Infrastructure/FileUtilities.py @@ -1,17 +1,35 @@ import imp import os import sys +from os.path import exists class FileUtilities(object): @staticmethod def main_is_frozen(): - return (hasattr(sys, "frozen") or # new py2exe - hasattr(sys, "importers") # old py2exe - or imp.is_frozen("__main__")) # tools/freeze + return (hasattr(sys, "frozen") or # new py2exe + hasattr(sys, "importers") # old py2exe + or imp.is_frozen("__main__")) # tools/freeze - @staticmethod - def get_root_path(): + def get_root_path(self): if FileUtilities.main_is_frozen(): return os.path.dirname(sys.executable) - return os.path.dirname(os.path.realpath(__file__)) \ No newline at end of file + return os.path.dirname(os.path.realpath(__file__)) + + def file_exists(self, file_path): + return exists(file_path) + + def create_file(self, file_path): + f = open(file_path, "w") + f.close() + + def append(self, file_path, data): + f = open(file_path, "a") + f.write(data) + f.close() + + def go_up_dirs(self, root_directory, levels): + if '\\' in root_directory: + return "/".join(root_directory.split('\\')[:-levels]) + else: + return "/".join(root_directory.split('/')[:-levels]) \ No newline at end of file diff --git a/Infrastructure/MobberManager.py b/Infrastructure/MobberManager.py index 9d98dc5..c8eb20e 100644 --- a/Infrastructure/MobberManager.py +++ b/Infrastructure/MobberManager.py @@ -7,43 +7,76 @@ def __init__(self, randomize=False): self.next_driver_index = 1 self.mobber_list = [] self.mobber_list_change_callbacks = [] + self.mobber_add_callbacks = [] + self.mobber_remove_callbacks = [] self.randomize = randomize def mobber_count(self): return self.mobber_list.__len__() def add_mobber(self, mobber_name): - if str(mobber_name).strip() != "": + clean_mobber_name = str(mobber_name).strip() + if clean_mobber_name != "" and not self.mobber_list.__contains__(clean_mobber_name): self.mobber_list.append(mobber_name) - self.fire_time_change_callbacks() + self.fire_mobber_add_callbacks(mobber_name) + self.fire_mobber_list_change_callbacks() + + def remove_mobber(self, remove_mobber_index): + if self.mobber_count() == 0: + return + self.fire_mobber_remove_callbacks(self.mobber_list[remove_mobber_index]) + del self.mobber_list[remove_mobber_index] + self.fire_mobber_list_change_callbacks() + + def set_mobber_list(self, mobber_list): + if self.mobber_list != mobber_list: + for index in range(0, mobber_list.__len__()): + self.remove_mobber(0) + for mobber_name in mobber_list: + clean_mobber_name = str(mobber_name).strip() + if clean_mobber_name != "" and not self.mobber_list.__contains__(clean_mobber_name): + self.add_mobber(clean_mobber_name) + + def subscribe_to_mobber_add(self, mobber_add_callback): + self.mobber_add_callbacks.append(mobber_add_callback) + + def fire_mobber_add_callbacks(self, mobber_name): + for mobber_add_callback in self.mobber_add_callbacks: + if mobber_add_callback: + mobber_add_callback(mobber_name) def get_mobbers(self): return self.mobber_list - def remove_mobber(self, remove_mobber_index): - if self.mobber_count() == 0: return - del self.mobber_list[remove_mobber_index] - self.fire_time_change_callbacks() + + + def subscribe_to_mobber_remove(self, mobber_remove_callback): + self.mobber_remove_callbacks.append(mobber_remove_callback) + + def fire_mobber_remove_callbacks(self, mobber_name): + for mobber_remove_callback in self.mobber_remove_callbacks: + if mobber_remove_callback: + mobber_remove_callback(mobber_name) def move_mobber_up(self, swap_index): if self.mobber_count() == 0: return destination_index = swap_index - 1 self.mobber_list[swap_index], self.mobber_list[destination_index] = self.mobber_list[destination_index], \ self.mobber_list[swap_index] - self.fire_time_change_callbacks() + self.fire_mobber_list_change_callbacks() def move_mobber_down(self, swap_index): if self.mobber_count() == 0: return destination_index = (swap_index + 1) % self.mobber_list.__len__() self.mobber_list[swap_index], self.mobber_list[destination_index] = self.mobber_list[destination_index], \ self.mobber_list[swap_index] - self.fire_time_change_callbacks() + self.fire_mobber_list_change_callbacks() def subscribe_to_mobber_list_change(self, mobber_list_change_callback): self.mobber_list_change_callbacks.append(mobber_list_change_callback) - self.fire_time_change_callbacks() + self.fire_mobber_list_change_callbacks() - def fire_time_change_callbacks(self): + def fire_mobber_list_change_callbacks(self): self.update_next_driver_index() for mobber_list_change_callback in self.mobber_list_change_callbacks: if mobber_list_change_callback: @@ -51,7 +84,7 @@ def fire_time_change_callbacks(self): def clear(self): self.mobber_list = [] - self.fire_time_change_callbacks() + self.fire_mobber_list_change_callbacks() def switch_next_driver(self): mobber_count = self.mobber_list.__len__() @@ -60,7 +93,7 @@ def switch_next_driver(self): self.driver_index = self.next_driver_index else: self.driver_index = (self.driver_index + 1) % mobber_count - self.fire_time_change_callbacks() + self.fire_mobber_list_change_callbacks() def update_next_driver_index(self): mobber_count = self.mobber_list.__len__() @@ -83,4 +116,7 @@ def rewind_driver(self): if self.driver_index < 0: self.driver_index = mobber_count - 1 self.update_next_driver_index() - self.fire_time_change_callbacks() + self.fire_mobber_list_change_callbacks() + + + diff --git a/Infrastructure/SessionManager.py b/Infrastructure/SessionManager.py index 12cf7db..fedddce 100644 --- a/Infrastructure/SessionManager.py +++ b/Infrastructure/SessionManager.py @@ -7,6 +7,7 @@ class SessionManager(object): def __init__(self, uuid_generator): self.uuid_generator = uuid_generator + self.file_utilities = FileUtilities() def create_session(self): session_id = self.uuid_generator.uuid1() @@ -16,7 +17,7 @@ def create_session(self): file = open(directory + session_id.__str__(), 'w+') def get_sessions_path(self): - directory = (FileUtilities.get_root_path() + "/Sessions/") + directory = (self.file_utilities.get_root_path() + "/Sessions/") if not os.path.exists(directory): os.makedirs(directory) return directory diff --git a/Infrastructure/SettingsManager.py b/Infrastructure/SettingsManager.py index 404d9b3..9186801 100644 --- a/Infrastructure/SettingsManager.py +++ b/Infrastructure/SettingsManager.py @@ -7,6 +7,8 @@ class SettingsManager(object): TIMER_SETTINGS = "TIMER SETTINGS" CONTINUE_SCREEN_BLOCKER_SETTINGS = "CONTINUE SCREEN BLOCKER SETTINGS" SCREEN_BLOCKER_SETTINGS = "SCREEN BLOCKER SETTINGS" + CODE_DOJO = "CODE DOJO" + EVENT_LOGGING = "EVENT LOGGING" def __init__(self): self.config = configparser.ConfigParser() @@ -16,6 +18,8 @@ def __init__(self): self.screen_blocker_settings = self.config[SettingsManager.SCREEN_BLOCKER_SETTINGS] self.general_settings_ = self.config[SettingsManager.GENERAL_SETTINGS] self.timer_settings_ = self.config[SettingsManager.TIMER_SETTINGS] + self.code_dojo = self.config[SettingsManager.CODE_DOJO] + self.event_logging = self.config[SettingsManager.EVENT_LOGGING] def get_transparent_window_screen_size_percent(self): return self.window_settings_.getfloat("size percentage", 0.3) @@ -84,4 +88,31 @@ def get_timer_extension_count(self): return self.timer_settings_.getint("extension count", 2) def get_general_enable_tips(self): - return self.general_settings_.getboolean("enable tips", True) \ No newline at end of file + return self.general_settings_.getboolean("enable tips", True) + + def get_general_enable_unobtrusive_mode(self): + return self.general_settings_.getboolean("enable unobtrusive mode", False) + + def get_dojo_enabled(self): + return self.code_dojo.getboolean("enable dojo mode", False) + + def get_dojo_broker(self): + return self.code_dojo.get("broker", "iot.eclipse.org") + + def get_dojo_port(self): + return self.code_dojo.getint("port", 1883) + + def get_dojo_mob_station_name(self): + return self.code_dojo.get("mob station name", "NewName") + + def get_dojo_session_id(self): + return self.code_dojo.get("session id", "1337") + + def get_dojo_topic_root(self): + return self.code_dojo.get("topic root", "MobTimer") + + def get_event_logging_enabled(self): + return self.event_logging.getboolean("enabled", True) + + def get_general_enable_unobtrusive_mode_bouncing_screen(self): + return self.general_settings_.getboolean("enable unobtrusive mode bouncing screen", False) diff --git a/Infrastructure/ThemeManager.py b/Infrastructure/ThemeManager.py index fb46af2..4c5cd1d 100644 --- a/Infrastructure/ThemeManager.py +++ b/Infrastructure/ThemeManager.py @@ -28,6 +28,24 @@ def __init__(self): self.button_color = "#FFFFFF" self.text_color = "#000000" self.highlight_color = "#aaaaaa" + self.normal_background_flash_color = True + + def toggle_flashing_background_style(self): + style = ttk.Style() + style.theme_use('default') + self.normal_background_flash_color = not self.normal_background_flash_color + if self.normal_background_flash_color: + style.configure('TFrame', background= self.background_color) + style.configure('TLabel', background=self.background_color, foreground=self.text_color) + else: + style.configure('TFrame', background= self.highlight_color) + style.configure('TLabel', background=self.highlight_color, foreground=self.background_color) + + def reset_flashing_background_colors_to_normal(self): + style = ttk.Style() + style.theme_use('default') + style.configure('TFrame', background= self.background_color) + style.configure('TLabel', background=self.background_color, foreground=self.text_color) def set_theme(self, theme_name): cfg_file = "Themes/{}.cfg".format(theme_name) diff --git a/Infrastructure/TimeSettingsManager.py b/Infrastructure/TimeSettingsManager.py index 6e94b01..b2e498d 100644 --- a/Infrastructure/TimeSettingsManager.py +++ b/Infrastructure/TimeSettingsManager.py @@ -29,12 +29,12 @@ def subscribe_to_timechange(self, time_change_callback): self.time_change_callbacks.append(time_change_callback) self.fire_time_change_callbacks() - def fire_time_change_callbacks(self): + def fire_time_change_callbacks(self, origin_station_name=None): for time_change_callback in self.time_change_callbacks: if time_change_callback: - time_change_callback(self.get_time_string(), self.minutes, self.seconds) + time_change_callback(self.get_time_string(), self.minutes, self.seconds, origin_station_name) - def set_countdown_time(self, minutes, seconds): + def set_countdown_time(self, minutes, seconds, origin_station_name=None): self.minutes = minutes self.seconds = seconds - self.fire_time_change_callbacks() \ No newline at end of file + self.fire_time_change_callbacks(origin_station_name) \ No newline at end of file diff --git a/Infrastructure/TipsManager.py b/Infrastructure/TipsManager.py index 50e8093..9908f8f 100644 --- a/Infrastructure/TipsManager.py +++ b/Infrastructure/TipsManager.py @@ -2,19 +2,22 @@ import sys +from Infrastructure.FileUtilities import FileUtilities from Infrastructure.PathUtility import PathUtility class TipsManager(object): - def __init__(self, seed = None, root_directory=sys.argv[0]): - self.root_directory = root_directory + def __init__(self, seed=None, root_directory=sys.argv[0]): + self.file_utility = FileUtilities() + self.root_directory = self.file_utility.go_up_dirs(root_directory, 1) if seed is not None: random.seed(seed) def get_random_tip(self): - tips_folder = "Tips" + tips_folder = self.root_directory + "/Tips" random_file = random.choice(os.listdir("%s" % tips_folder)) - return "{}: {}" .format(random_file, TipsManager.random_line(PathUtility.normalize_path(tips_folder + "\\" + random_file,self.root_directory))) + random_file_path = tips_folder + "\\" + random_file + return "{}: {}" .format(random_file, TipsManager.random_line(random_file_path)) @staticmethod def random_line(file_name): diff --git a/MobTimer.cfg b/MobTimer.cfg index 1a6f4ee..1ba34fe 100644 --- a/MobTimer.cfg +++ b/MobTimer.cfg @@ -3,7 +3,7 @@ mouse wheel seconds delta = 1 click seconds delta = 5 [TRANSPARENT WINDOW SETTINGS] -size percentage = 0.12 +size percentage = 0.14 alpha percentage = 0.4 count down timer font size = 30 driver font size = 15 @@ -22,7 +22,8 @@ randomize next driver = False use logo image = False logo image name = company-logo.png auto theme logo = False - +enable unobtrusive mode = False +enable unobtrusive mode bouncing screen = True [TIMER SETTINGS] minutes = 5 @@ -30,4 +31,15 @@ seconds = 0 extension enabled = False extension minutes = 0 extension seconds = 30 -extension count = 1 \ No newline at end of file +extension count = 1 + +[CODE DOJO] +enable dojo mode = False +broker = localhost +port = 1883 +mob station name = NewName +session id = 1337 +topic root = MobTimer + +[EVENT LOGGING] +enabled = False \ No newline at end of file diff --git a/MobTimer.py b/MobTimer.py index cd9dda5..387c7fa 100644 --- a/MobTimer.py +++ b/MobTimer.py @@ -2,3 +2,9 @@ root = MobTimerController() root.mainloop() + + + + + + diff --git a/README.md b/README.md index 953e6fe..80f8bac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Mob Programming Timer – Python # [Download Now](https://github.com/MobProgramming/MobTimer.Python/releases) +## How to install and basic use video +[![MobTimer Install and Tutorial Video](http://img.youtube.com/vi/GxMP8SI6v0k/0.jpg)](http://www.youtube.com/watch?v=GxMP8SI6v0k) ## What is Mob Programming? All the brilliant people working on the same thing, at the same time, in the same space, and on the same computer. Find out more at the [Mob Programming site]( http://mobprogramming.org/). @@ -13,7 +15,7 @@ When we started mob programming we originally used a phone timer to time the rot - Screen will be blocked when timer runs out - Switch the person at the keyboard to the new driver - Repeat -- To stop timer mid session open the exe again +- To stop timer mid-session, open the exe again ## Random Tips Tips are maintained in the Tips folder. A random file is selected and a random line from the random file is displayed. To add or remove tips simply add or remove files and lines from files. @@ -24,6 +26,21 @@ The Mob timer can be customized by modifying MobTimer.cfg. ## Themes To add a theme simply copy and existing theme. To use that theme, put the file name of the new theme in the theme attribute in the MobTimer.cfg -## Project Links -[Trello Kanban Board]( https://trello.com/b/THISIB9Q/mob-programming-timer-python) +## Build Windows Distributable +Run BuildMobTimer.py +# Kanban +* Make it easy to track time actually working + * Logs should have timestamps + * log when mobber added and removed + * enable/disable time tracking via the config file + * define time types in the config file (Learning Time / Development Time etc...) + * replace continue button with tracking type + * Create plugin infrastructure (to interface with Clockify, etc.) +* Allow typing into the minutes and seconds +* Clicking [Add mobber] should redirect cursor to typing next name +* Flip the order of Next and Driver +* Replace Next with navigator +* Make a new setup for Mac +* Skipping someone on the timer screen should be a button not clicking the name +* Consolidate files into a dir for moving to the './dist' folder \ No newline at end of file diff --git a/Tips/MobTimer b/Tips/MobTimer new file mode 100644 index 0000000..b1e0267 --- /dev/null +++ b/Tips/MobTimer @@ -0,0 +1,19 @@ +You can set the mouse wheel seconds delta in the MobTimer.cfg file. +You can set the mouse click seconds delta delta in the MobTimer.cfg file. +You can set the transparent window size percentage in the MobTimer.cfg file. +You can set the transparent window alpha percentage in the MobTimer.cfg file. +You can set the transparent window count down timer font size in the MobTimer.cfg file. +You can set the transparent window driver text font size in the MobTimer.cfg file. +You can set the transparent window next driver text font size in the MobTimer.cfg file. +You can set the continue screen alpha percentage in the MobTimer.cfg file. +You can set the continue screen to show current time in the MobTimer.cfg file. +You can set the theme to Candy, Dark, Foo, Forest, Lava, Pastel, and Sky in the MobTimer.cfg file. +You can create your own color themes in the Themes folder. +You can disable these tips in the MobTimer.cfg file. +You can set your default team with comma delimited names in the MobTimer.cfg file. +You can enable randomize team in the MobTimer.cfg file. +You can enable randomize next driver in MobTimer.cfg file. +You can able your own logo image in the MobTimer.cfg file. +You can enable unobtrusive mode MobTimer.cfg file. (Instead of blocking the screen, show a small notification that the driver needs to switch) +You can set the default time in the MobTimer.cfg file. +You can enable time extensions in the MobTimer.cfg file. \ No newline at end of file diff --git a/Tips/VsCodeJsRefactorSnippets b/Tips/VsCodeJsRefactorSnippets new file mode 100644 index 0000000..9af9073 --- /dev/null +++ b/Tips/VsCodeJsRefactorSnippets @@ -0,0 +1,10 @@ +anon - anonymous function template +cond - if block template +export - single-line export template +exportObj - object literal export template +fn - function declaration template +iife - immediately invoked function expression template +mfn - object prototype member function template +proto - object template with prototype declaration +require - require statement template +strict - adds 'use strict'; at cursor location diff --git a/Tips/vscodehotkeys b/Tips/vscodehotkeys new file mode 100644 index 0000000..0b0f0e5 --- /dev/null +++ b/Tips/vscodehotkeys @@ -0,0 +1,17 @@ +ctrl+p - open command pallette, search project files +alt+left - return to previous location +alt+shift+f - format code +alt+shift+right - widen selection scope +alt+shift+left - narrow selection scope +alt+up - move selected lines up +alt+down - move selected lines down +alt+shift+up - copy selected lines up +alt+shift+down - copy selected lines down +ctrl+shift+f - search all project files +ctrl+tab - move through recent file list +ctrl+shift+tab - move backwards through recent file list +f12 - rename variable +ctrl+enter - insert line below +ctrl+shift+enter - insert line above +ctrl+shift+l - select all instances of selected text +ctrl+shift+k - delete lines diff --git a/Tips/vscoderefactoring b/Tips/vscoderefactoring new file mode 100644 index 0000000..fd1eb5b --- /dev/null +++ b/Tips/vscoderefactoring @@ -0,0 +1,7 @@ +Convert to Member Function - converts named function to object prototype function +Convert to Named Function - converts variable-assigned anonymous function to standard named function +Export Function - adds export declaration to module for selected function +Wrap in Condition - wraps selected code in an if block +Wrap in Executed Function - wraps selected code in a function which is invoked on the next line +Wrap in Function - wraps selected code in a function declaration +Wrap if IIFE - wraps selected code in an immediately invoked function expression diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1f0e97c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +approvaltests +pyinstaller +pysmb +paho-mqtt +pyreadline +screeninfo +pypiwin32 +dnspython +Pillow +Pillow-PIL +pyasn1 +cffi +pycparser +pysha3 diff --git a/setup.py b/setup.py index 38a337b..a9a7e3e 100644 --- a/setup.py +++ b/setup.py @@ -7,13 +7,16 @@ def create_windows_exe(): # Get theme files and store them in a list def get_file_paths(folder_name): - file_paths = [f for f in listdir(folder_name) if isfile(join(folder_name, f))] - return ['{}/{}'.format(folder_name, i) for i in file_paths] + if os.path.isdir(folder_name): + file_paths = [f for f in listdir(folder_name) if isfile(join(folder_name, f))] + return ['{}/{}'.format(folder_name, i) for i in file_paths] + else: + return [] + [os.remove(x) for x in get_file_paths('dist')] theme_file_paths = get_file_paths("Themes") tips_file_paths = get_file_paths("Tips") image_file_paths = get_file_paths("Images") - tcl__path = '{}\\tcl\\tcl8.6\\init.tcl'.format(os.path.dirname(sys.executable)) setup(windows=[{ "script": 'MobTimer.py', "icon_resources": [(1, "time-bomb.ico")] @@ -22,8 +25,7 @@ def get_file_paths(folder_name): ('', ["MobTimer.cfg", "company-logo.png", "time-bomb.ico"]), ('Themes', theme_file_paths), ('Tips', tips_file_paths), - ('Images', image_file_paths), - tcl__path] + ('Images', image_file_paths)] , requires=['screeninfo']) def create_mac_app(): diff --git a/tests/Infrastructure/CountdownManager/tests.Infrastructure.CountdownManager.TestsCountdownManager.test_subscribe_to_time_changes.approved.txt b/tests/Infrastructure/CountdownManager/TestsCountdownManager.test_subscribe_to_time_changes.approved.txt similarity index 100% rename from tests/Infrastructure/CountdownManager/tests.Infrastructure.CountdownManager.TestsCountdownManager.test_subscribe_to_time_changes.approved.txt rename to tests/Infrastructure/CountdownManager/TestsCountdownManager.test_subscribe_to_time_changes.approved.txt diff --git a/tests/Infrastructure/CountdownManager/TestsCountdownManager.py b/tests/Infrastructure/CountdownManager/test_CountdownManager.py similarity index 88% rename from tests/Infrastructure/CountdownManager/TestsCountdownManager.py rename to tests/Infrastructure/CountdownManager/test_CountdownManager.py index 8b7dbb3..bf7c7df 100644 --- a/tests/Infrastructure/CountdownManager/TestsCountdownManager.py +++ b/tests/Infrastructure/CountdownManager/test_CountdownManager.py @@ -1,9 +1,6 @@ -import datetime -import time import unittest -from approvaltests import Approvals -from approvaltests.TextDiffReporter import TextDiffReporter +from approvaltests.approvals import verify from Infrastructure.CountdownManager import CountdownManager @@ -37,7 +34,7 @@ def time_change_callback(days, minutes, seconds): countdown_manager.set_countdown_duration(853, 32) countdown_manager.set_countdown_duration(3, 62) - Approvals.verify(result["result"], TextDiffReporter()) + verify(result["result"]) if __name__ == '__main__': diff --git a/tests/Infrastructure/EventLoggingManager/__init__.py b/tests/Infrastructure/EventLoggingManager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/Infrastructure/EventLoggingManager/test_EventLoggingManager.py b/tests/Infrastructure/EventLoggingManager/test_EventLoggingManager.py new file mode 100644 index 0000000..9038552 --- /dev/null +++ b/tests/Infrastructure/EventLoggingManager/test_EventLoggingManager.py @@ -0,0 +1,61 @@ +import unittest +from unittest.mock import call, MagicMock, Mock + +from Infrastructure.DateTimeUtility import DateTimeUtility +from Infrastructure.EventLoggingManager import EventLoggingManager +from Infrastructure.FileUtilities import FileUtilities + + +class TestsEventLoggingManager(unittest.TestCase): + + def test_log_file_should_be_created_if_doesnt_exist(self): + file_utility = FileUtilities() + beginning_of_file_path = 'beginning of file path' + file_utility.get_root_path = MagicMock(return_value=beginning_of_file_path + "\\Infrastructure") + file_utility.file_exists = MagicMock(return_value=False) + file_utility.create_file = MagicMock(return_value=True) + date_time_utility = DateTimeUtility() + EventLoggingManager(file_utility,date_time_utility) + + file_utility.create_file.assert_called_with(beginning_of_file_path + '\\MobTimerEvents.log') + + def test_log_file_should_not_create_if_file_exists(self): + file_utility = FileUtilities() + file_utility.get_root_path = MagicMock(return_value='beginning of file path' + '\\Infrastructure') + file_utility.file_exists = MagicMock(return_value=True) + file_utility.create_file = MagicMock(return_value=True) + + date_time_utility = DateTimeUtility() + EventLoggingManager(file_utility, date_time_utility) + + file_utility.create_file.assert_not_called() + + def test_log_file_should_append_to_log(self): + file_utility = FileUtilities() + beginning_of_file_path = 'beginning of file path' + file_utility.get_root_path = MagicMock(return_value=beginning_of_file_path + '\\Infrastructure') + file_utility.file_exists = MagicMock(return_value=True) + file_utility.create_file = MagicMock(return_value=True) + file_utility.append = MagicMock() + date_time_utility = DateTimeUtility() + + timestamps = [1594823426.159446, 1594123426.159447, 1594654426.159448] + date_time_utility.get_timestamp = Mock() + date_time_utility.get_timestamp.side_effect = timestamps + logger = EventLoggingManager(file_utility, date_time_utility) + + test_data = ["Hello world 1", "Hello world 2"] + + for entry in test_data: + logger.log(entry) + calls = [] + index = 0 + for entry in test_data: + calls.append(call(f'{beginning_of_file_path}\\MobTimerEvents.log', f'\n{timestamps[index]} {entry}')) + index += 1 + + file_utility.append.assert_has_calls(calls) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/Infrastructure/MobberManager/TestsMobberManager.test_subscribe_to_mobber_list_changes.approved.txt b/tests/Infrastructure/MobberManager/TestsMobberManager.test_subscribe_to_mobber_list_changes.approved.txt new file mode 100644 index 0000000..e123f40 --- /dev/null +++ b/tests/Infrastructure/MobberManager/TestsMobberManager.test_subscribe_to_mobber_list_changes.approved.txt @@ -0,0 +1,39 @@ +Mobbers in List for Each Change +Action 1: +Action 2:Joe added +Action 3:Joe (Driver) (Navigator), +Action 4:Chris added +Action 5:Joe (Driver), Chris (Navigator), +Action 6:Sam added +Action 7:Joe (Driver), Chris (Navigator), Sam, +Action 8:John added +Action 9:Joe (Driver), Chris (Navigator), Sam, John, +Action 10:Joe, Chris (Driver), Sam (Navigator), John, +Action 11:Bill added +Action 12:Joe, Chris (Driver), Sam (Navigator), John, Bill, +Action 13:Joe, Chris, Sam (Driver), John (Navigator), Bill, +Action 14:Joe, Chris, Sam, John (Driver), Bill (Navigator), +Action 15:Joe (Navigator), Chris, Sam, John, Bill (Driver), +Action 16:Joe (Driver), Chris (Navigator), Sam, John, Bill, +Action 17:Joe, Chris (Driver), Sam (Navigator), John, Bill, +Action 18:Sam removed +Action 19:Joe, Chris (Driver), John (Navigator), Bill, +Action 20:Joe removed +Action 21:Chris, John (Driver), Bill (Navigator), +Action 22:Chris (Navigator), John, Bill (Driver), +Action 23:Chris, John (Driver), Bill (Navigator), +Action 24:Seth added +Action 25:Chris, John (Driver), Bill (Navigator), Seth, +Action 26:Chris (Driver), John (Navigator), Bill, Seth, +Action 27:Chris (Navigator), John, Bill, Seth (Driver), +Action 28:Chris, John, Bill (Driver), Seth (Navigator), +Action 29:John, Chris, Bill (Driver), Seth (Navigator), +Action 30:Fredrick added +Action 31:John, Chris, Bill (Driver), Seth (Navigator), Fredrick, +Action 32:John, Bill, Chris (Driver), Seth (Navigator), Fredrick, +Action 33:Bill removed +Action 34:John, Chris, Seth (Driver), Fredrick (Navigator), +Action 35:John removed +Action 36:Chris (Navigator), Seth, Fredrick (Driver), +Action 37:Chris removed +Action 38:Seth (Driver), Fredrick (Navigator), diff --git a/tests/Infrastructure/MobberManager/TestsMobberManager.test_subscribe_to_mobber_list_changes_random.approved.txt b/tests/Infrastructure/MobberManager/TestsMobberManager.test_subscribe_to_mobber_list_changes_random.approved.txt new file mode 100644 index 0000000..e0e2ce0 --- /dev/null +++ b/tests/Infrastructure/MobberManager/TestsMobberManager.test_subscribe_to_mobber_list_changes_random.approved.txt @@ -0,0 +1,47 @@ +Mobbers in List for Each Change +Action 1: +Action 2:Joe added +Action 3:Joe (Driver) (Next), +Action 4:Chris added +Action 5:Joe (Driver), Chris (Next), +Action 6:Sam added +Action 7:Joe (Driver), Chris, Sam (Next), +Action 8:John added +Action 9:Joe (Driver), Chris, Sam, John (Next), +Action 10:Joe, Chris (Next), Sam, John (Driver), +Action 11:Bill added +Action 12:Joe, Chris (Next), Sam, John (Driver), Bill, +Action 13:Joe, Chris (Driver), Sam (Next), John, Bill, +Action 14:Joe, Chris, Sam (Driver), John (Next), Bill, +Action 15:Joe, Chris (Next), Sam, John (Driver), Bill, +Action 16:Joe removed +Action 17:Chris, Sam (Next), John, Bill (Driver), +Action 18:Chris removed +Action 19:Sam, John (Next), Bill, +Action 20:Sam removed +Action 21:John (Next), Bill (Driver), +Action 22:Hello added +Action 23:John, Bill (Driver), Hello (Next), +Action 24:Eric added +Action 25:John, Bill (Driver), Hello (Next), Eric, +Action 26:Joe added +Action 27:John, Bill (Driver), Hello, Eric (Next), Joe, +Action 28:John, Bill (Next), Hello, Eric (Driver), Joe, +Action 29:John, Bill (Driver), Hello, Eric, Joe (Next), +Action 30:Hello removed +Action 31:John, Bill (Driver), Eric, Joe (Next), +Action 32:John removed +Action 33:Bill, Eric (Driver), Joe (Next), +Action 34:Bill (Next), Eric, Joe (Driver), +Action 35:Seth added +Action 36:Bill, Eric, Joe (Driver), Seth (Next), +Action 37:Eric, Bill (Next), Joe (Driver), Seth, +Action 38:Fredrick added +Action 39:Eric (Next), Bill, Joe (Driver), Seth, Fredrick, +Action 40:Eric, Joe, Bill (Driver), Seth (Next), Fredrick, +Action 41:Joe removed +Action 42:Eric, Bill, Seth (Driver), Fredrick (Next), +Action 43:Eric removed +Action 44:Bill, Seth (Next), Fredrick (Driver), +Action 45:Bill removed +Action 46:Seth (Driver), Fredrick (Next), diff --git a/tests/Infrastructure/MobberManager/TestsMobberManager.py b/tests/Infrastructure/MobberManager/test_MobberManager.py similarity index 86% rename from tests/Infrastructure/MobberManager/TestsMobberManager.py rename to tests/Infrastructure/MobberManager/test_MobberManager.py index 520556d..359b08d 100644 --- a/tests/Infrastructure/MobberManager/TestsMobberManager.py +++ b/tests/Infrastructure/MobberManager/test_MobberManager.py @@ -1,12 +1,8 @@ -import os import random import unittest -from approvaltests import Approvals -from approvaltests.TextDiffReporter import TextDiffReporter +from approvaltests.approvals import verify from Infrastructure.MobberManager import MobberManager -os.environ["APPROVALS_TEXT_DIFF_TOOL"] = "C:\\Program Files\\TortoiseSVN\\bin\\TortoiseMerge.exe" - class TestsMobberManager(unittest.TestCase): def test_empty_mobber_manager_has_no_items(self): @@ -30,7 +26,7 @@ def test_add_mobber_joe_chris_joe__remove_joe_has_joe_chris(self): mobber_manager = MobberManager() mobber_manager.add_mobber("Joe") mobber_manager.add_mobber("Chris") - mobber_manager.add_mobber("Joe") + mobber_manager.add_mobber("John") mobber_manager.remove_mobber(2) result = ["Joe", "Chris"] self.assertEqual(mobber_manager.get_mobbers(), result) @@ -121,6 +117,19 @@ def time_change_callback(mobber_list, driver_index, navigator_index): mobber_manager.subscribe_to_mobber_list_change(time_change_callback) + def on_mobber_add(mobber_name): + result["increment"] += 1 + result["result"] += "Action " + result["increment"].__str__() + ":" + mobber_name + " added\n" + + mobber_manager.subscribe_to_mobber_add(on_mobber_add) + + + def on_mobber_remove(mobber_name): + result["increment"] += 1 + result["result"] += "Action " + result["increment"].__str__() + ":" + mobber_name + " removed\n" + + mobber_manager.subscribe_to_mobber_remove(on_mobber_remove) + mobber_manager.add_mobber("Joe") mobber_manager.add_mobber("Chris") mobber_manager.add_mobber("Sam") @@ -147,7 +156,7 @@ def time_change_callback(mobber_list, driver_index, navigator_index): mobber_manager.remove_mobber(0) mobber_manager.remove_mobber(0) - Approvals.verify(result["result"], TextDiffReporter()) + verify(result["result"]) def test_subscribe_to_mobber_list_changes_random(self): random.seed(0) @@ -167,6 +176,20 @@ def time_change_callback(mobber_list, driver_index, navigator_index): result["result"] += "\n" + def on_mobber_add(mobber_name): + result["increment"] += 1 + result["result"] += "Action " + result["increment"].__str__() + ":" + mobber_name + " added\n" + + mobber_manager.subscribe_to_mobber_add(on_mobber_add) + + + def on_mobber_remove(mobber_name): + result["increment"] += 1 + result["result"] += "Action " + result["increment"].__str__() + ":" + mobber_name + " removed\n" + + mobber_manager.subscribe_to_mobber_remove(on_mobber_remove) + + mobber_manager.subscribe_to_mobber_list_change(time_change_callback) mobber_manager.add_mobber("Joe") @@ -178,6 +201,7 @@ def time_change_callback(mobber_list, driver_index, navigator_index): mobber_manager.switch_next_driver() mobber_manager.switch_next_driver() mobber_manager.switch_next_driver() + mobber_manager.set_mobber_list(["Hello", "Eric", "Joe"]) mobber_manager.switch_next_driver() mobber_manager.switch_next_driver() mobber_manager.remove_mobber(2) @@ -191,7 +215,7 @@ def time_change_callback(mobber_list, driver_index, navigator_index): mobber_manager.remove_mobber(0) mobber_manager.remove_mobber(0) - Approvals.verify(result["result"], TextDiffReporter()) + verify(result["result"]) def test_navigator1_driver0_index(self): mobber_manager = MobberManager() diff --git a/tests/Infrastructure/MobberManager/tests.Infrastructure.MobberManager.TestsMobberManager.test_subscribe_to_mobber_list_changes.approved.txt b/tests/Infrastructure/MobberManager/tests.Infrastructure.MobberManager.TestsMobberManager.test_subscribe_to_mobber_list_changes.approved.txt deleted file mode 100644 index d000eb0..0000000 --- a/tests/Infrastructure/MobberManager/tests.Infrastructure.MobberManager.TestsMobberManager.test_subscribe_to_mobber_list_changes.approved.txt +++ /dev/null @@ -1,27 +0,0 @@ -Mobbers in List for Each Change -Action 1: -Action 2:Joe (Driver) (Navigator), -Action 3:Joe (Driver), Chris (Navigator), -Action 4:Joe (Driver), Chris (Navigator), Sam, -Action 5:Joe (Driver), Chris (Navigator), Sam, John, -Action 6:Joe, Chris (Driver), Sam (Navigator), John, -Action 7:Joe, Chris (Driver), Sam (Navigator), John, Bill, -Action 8:Joe, Chris, Sam (Driver), John (Navigator), Bill, -Action 9:Joe, Chris, Sam, John (Driver), Bill (Navigator), -Action 10:Joe (Navigator), Chris, Sam, John, Bill (Driver), -Action 11:Joe (Driver), Chris (Navigator), Sam, John, Bill, -Action 12:Joe, Chris (Driver), Sam (Navigator), John, Bill, -Action 13:Joe, Chris (Driver), John (Navigator), Bill, -Action 14:Chris, John (Driver), Bill (Navigator), -Action 15:Chris (Navigator), John, Bill (Driver), -Action 16:Chris, John (Driver), Bill (Navigator), -Action 17:Chris, John (Driver), Bill (Navigator), Seth, -Action 18:Chris (Driver), John (Navigator), Bill, Seth, -Action 19:Chris (Navigator), John, Bill, Seth (Driver), -Action 20:Chris, John, Bill (Driver), Seth (Navigator), -Action 21:John, Chris, Bill (Driver), Seth (Navigator), -Action 22:John, Chris, Bill (Driver), Seth (Navigator), Fredrick, -Action 23:John, Bill, Chris (Driver), Seth (Navigator), Fredrick, -Action 24:John, Chris, Seth (Driver), Fredrick (Navigator), -Action 25:Chris (Navigator), Seth, Fredrick (Driver), -Action 26:Seth (Driver), Fredrick (Navigator), diff --git a/tests/Infrastructure/MobberManager/tests.Infrastructure.MobberManager.TestsMobberManager.test_subscribe_to_mobber_list_changes_random.approved.txt b/tests/Infrastructure/MobberManager/tests.Infrastructure.MobberManager.TestsMobberManager.test_subscribe_to_mobber_list_changes_random.approved.txt deleted file mode 100644 index 57355ed..0000000 --- a/tests/Infrastructure/MobberManager/tests.Infrastructure.MobberManager.TestsMobberManager.test_subscribe_to_mobber_list_changes_random.approved.txt +++ /dev/null @@ -1,23 +0,0 @@ -Mobbers in List for Each Change -Action 1: -Action 2:Joe (Driver) (Next), -Action 3:Joe (Driver), Chris (Next), -Action 4:Joe (Driver), Chris, Sam (Next), -Action 5:Joe (Driver), Chris, Sam, John (Next), -Action 6:Joe, Chris (Next), Sam, John (Driver), -Action 7:Joe, Chris (Next), Sam, John (Driver), Bill, -Action 8:Joe, Chris (Driver), Sam (Next), John, Bill, -Action 9:Joe, Chris, Sam (Driver), John (Next), Bill, -Action 10:Joe, Chris (Next), Sam, John (Driver), Bill, -Action 11:Joe, Chris (Driver), Sam (Next), John, Bill, -Action 12:Joe, Chris, Sam (Driver), John, Bill (Next), -Action 13:Joe, Chris (Next), John (Driver), Bill, -Action 14:Chris, John (Next), Bill (Driver), -Action 15:Chris (Next), John (Driver), Bill, -Action 16:Chris, John (Driver), Bill, Seth (Next), -Action 17:John, Chris (Driver), Bill, Seth (Next), -Action 18:John, Chris (Driver), Bill, Seth, Fredrick (Next), -Action 19:John, Bill (Driver), Chris, Seth, Fredrick (Next), -Action 20:John, Chris (Driver), Seth (Next), Fredrick, -Action 21:Chris, Seth (Driver), Fredrick (Next), -Action 22:Seth (Next), Fredrick (Driver), diff --git a/tests/Infrastructure/SessionManager/TestsSessionManager.py b/tests/Infrastructure/SessionManager/test_SessionManager.py similarity index 100% rename from tests/Infrastructure/SessionManager/TestsSessionManager.py rename to tests/Infrastructure/SessionManager/test_SessionManager.py diff --git a/tests/Infrastructure/TimeOptionsManager/tests.Infrastructure.TimeOptionsManager.TestsTimeOptionsManager.test_subscribe_to_time_changes_complex.approved.txt b/tests/Infrastructure/TimeOptionsManager/TestsTimeOptionsManager.test_subscribe_to_time_changes_complex.approved.txt similarity index 94% rename from tests/Infrastructure/TimeOptionsManager/tests.Infrastructure.TimeOptionsManager.TestsTimeOptionsManager.test_subscribe_to_time_changes_complex.approved.txt rename to tests/Infrastructure/TimeOptionsManager/TestsTimeOptionsManager.test_subscribe_to_time_changes_complex.approved.txt index c30ed55..86293ef 100644 --- a/tests/Infrastructure/TimeOptionsManager/tests.Infrastructure.TimeOptionsManager.TestsTimeOptionsManager.test_subscribe_to_time_changes_complex.approved.txt +++ b/tests/Infrastructure/TimeOptionsManager/TestsTimeOptionsManager.test_subscribe_to_time_changes_complex.approved.txt @@ -14,4 +14,4 @@ Time Options after Change: Change 13| 11:45 Change 14| 12:45 Change 15| 13:45 - Change 16| 03:14 \ No newline at end of file + Change 16| 03:14 diff --git a/tests/Infrastructure/TimeOptionsManager/TestsTimeOptionsManager.py b/tests/Infrastructure/TimeOptionsManager/test_TimeOptionsManager.py similarity index 93% rename from tests/Infrastructure/TimeOptionsManager/TestsTimeOptionsManager.py rename to tests/Infrastructure/TimeOptionsManager/test_TimeOptionsManager.py index 75da203..fec0310 100644 --- a/tests/Infrastructure/TimeOptionsManager/TestsTimeOptionsManager.py +++ b/tests/Infrastructure/TimeOptionsManager/test_TimeOptionsManager.py @@ -1,13 +1,13 @@ import os import unittest -from approvaltests import Approvals -from approvaltests.TextDiffReporter import TextDiffReporter +from approvaltests.approvals import verify from Infrastructure.TimeSettingsManager import TimeSettingsManager class TestsTimeOptionsManager(unittest.TestCase): + def test_default_time_10_minutes(self): time_options_manager = TimeSettingsManager() result = time_options_manager.get_time_string() @@ -58,7 +58,7 @@ def test_subscribe_to_time_changes(self): time_options_manager = TimeSettingsManager() result = {"result": "time"} - def time_change_callback(time, minutes, seconds): + def time_change_callback(time, minutes, seconds,origin_station_name): result["result"] += " " + time time_options_manager.subscribe_to_timechange(time_change_callback) @@ -71,7 +71,7 @@ def test_subscribe_to_time_changes_complex(self): time_options_manager = TimeSettingsManager() result = {"result": "Time Options after Change:", "increment" : 0} - def time_change_callback(time, minutes, seconds): + def time_change_callback(time, minutes, seconds,origin_station_name): result["increment"] += 1 result["result"] += "\n Change " + result["increment"].__str__() + "| " + time @@ -93,7 +93,7 @@ def time_change_callback(time, minutes, seconds): time_options_manager.increment_minutes() time_options_manager.set_countdown_time(3, 14) - Approvals.verify(result["result"], TextDiffReporter()) + verify(result["result"]) if __name__ == '__main__': os.environ["APPROVALS_TEXT_DIFF_TOOL"] = "meld" diff --git a/tests/Infrastructure/TipsManager/TestTipsManager.py b/tests/Infrastructure/TipsManager/TestTipsManager.py deleted file mode 100644 index 43230c5..0000000 --- a/tests/Infrastructure/TipsManager/TestTipsManager.py +++ /dev/null @@ -1,23 +0,0 @@ -import unittest - -import sys - -from Infrastructure.TipsManager import TipsManager - - -class TestsTipsManage(unittest.TestCase): - def test_random_tip_from_file(self): - seed = 0 - tips_manager = TipsManager(seed,sys.argv[1]) - result = tips_manager.get_random_tip() - self.assertEqual(result, 'TestTips2.txt: Words\n') - - def test_random_tip_from_file_second(self): - seed = 1 - tips_manager = TipsManager(seed,sys.argv[1]) - result = tips_manager.get_random_tip() - self.assertEqual(result, 'TestTips.txt: Customer collaboration over contract negotiation\n') - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/Infrastructure/TipsManager/test_TipsManager.py b/tests/Infrastructure/TipsManager/test_TipsManager.py new file mode 100644 index 0000000..1fdebf5 --- /dev/null +++ b/tests/Infrastructure/TipsManager/test_TipsManager.py @@ -0,0 +1,46 @@ +import unittest + +import sys + +import os + +from Infrastructure.FileUtilities import FileUtilities +from Infrastructure.TipsManager import TipsManager + + +class TestsTipsManage(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.fileUtilities = FileUtilities() + + def test_random_tip_from_file(self): + seed = 0 + + dirname = os.path.dirname(__file__) + path = self.fileUtilities.go_up_dirs(dirname, 2) + "\\Tips" + tips_manager = TipsManager(seed, path) + result = tips_manager.get_random_tip() + self.assertEqual(result, 'TestTips2.txt: Words\n') + + + def test_random_tip_from_file_second(self): + seed = 1 + dirname = os.path.dirname(__file__) + path = self.fileUtilities.go_up_dirs(dirname, 2) + "\\Tips" + tips_manager = TipsManager(seed, path) + result = tips_manager.get_random_tip() + self.assertEqual(result, 'TestTips.txt: Customer collaboration over contract negotiation\n') + + def test_random_tip_from_file_second_alternate_slashes(self): + seed = 1 + dirname = os.path.dirname(__file__) + path = self.fileUtilities.go_up_dirs(dirname, 2) + "\\Tips" + path = path.replace("\\", "/") + tips_manager = TipsManager(seed, path) + result = tips_manager.get_random_tip() + self.assertEqual(result, 'TestTips.txt: Customer collaboration over contract negotiation\n') + + +if __name__ == '__main__': + unittest.main()