diff --git a/mslib/mscolab/conf.py b/mslib/mscolab/conf.py index e88bc6d57..d846fcf18 100644 --- a/mslib/mscolab/conf.py +++ b/mslib/mscolab/conf.py @@ -49,6 +49,9 @@ class default_mscolab_settings: # To enable Engine.IO logging set to True or pass a logger object to use. ENGINEIO_LOGGER = False + # To enable flask socketio debugging, this sets in flask_socketio app.debug too + DEBUG = False + # Which origins are allowed to communicate with your server CORS_ORIGINS = ["*"] diff --git a/mslib/mscolab/mscolab.py b/mslib/mscolab/mscolab.py index d802dcbb4..da8f26897 100644 --- a/mslib/mscolab/mscolab.py +++ b/mslib/mscolab/mscolab.py @@ -47,9 +47,10 @@ from mslib.utils import setup_logging -def handle_start(args): +def handle_start(args=None): from mslib.mscolab.server import APP, sockio, cm, fm, start_server - setup_logging(args) + if args is not None: + setup_logging(args) logging.info("MSS Version: %s", __version__) logging.info("Python Version: %s", sys.version) logging.info("Platform: %s (%s)", platform.platform(), platform.architecture()) diff --git a/mslib/mscolab/server.py b/mslib/mscolab/server.py index 19013ea89..f43fdd154 100644 --- a/mslib/mscolab/server.py +++ b/mslib/mscolab/server.py @@ -1015,7 +1015,7 @@ def metadata(idp_identity_name): def start_server(app, sockio, cm, fm, port=8083): create_files() - sockio.run(app, port=port) + sockio.run(app, port=port, debug=mscolab_settings.DEBUG) def main(): diff --git a/tutorials/tutorial_mscolab.py b/tutorials/tutorial_mscolab.py index 68dbf1954..aba7f747b 100644 --- a/tutorials/tutorial_mscolab.py +++ b/tutorials/tutorial_mscolab.py @@ -553,4 +553,4 @@ def _connect_to_mscolab_url(): if __name__ == '__main__': - start(target=automate_mscolab, duration=640) + start(target=automate_mscolab, duration=640, mscolab=True, dry_run=False) diff --git a/tutorials/tutorials.batch b/tutorials/tutorials.batch index e95363dfa..e99eb4ecf 100755 --- a/tutorials/tutorials.batch +++ b/tutorials/tutorials.batch @@ -164,12 +164,6 @@ cd .. #################################################### # tutorial mscolab, 3K, because we need the menu in full screen -# start a mscolab server on standard port after you have it seeded -# we should have a seed for tutorials -$HOME/Miniforge/envs/mssdev/bin/python ../mslib/mscolab/mscolab.py db --seed - -$HOME/Miniforge/envs/mssdev/bin/python ../mslib/mscolab/mscolab.py start & - ~/bin/highlight-pointer -r 10 --key-quit q & $HOME/Miniforge/envs/mssdev/bin/python tutorial_mscolab.py diff --git a/tutorials/utils/__init__.py b/tutorials/utils/__init__.py index 770aef845..d347b70a5 100644 --- a/tutorials/utils/__init__.py +++ b/tutorials/utils/__init__.py @@ -82,6 +82,15 @@ def call_msui(): msui.main(tutorial_mode=True) +def call_mscolab(): + # change of config won't work when it becomes earlier imported + from mslib.mscolab import mscolab + with mscolab.APP.app_context(): + # initialize our seeded example dbase + mscolab.handle_db_seed() + mscolab.handle_start() + + def finish(close_widgets=3): """ Closes all open windows and exits the application. @@ -129,7 +138,7 @@ def finish(close_widgets=3): raise -def start(target=None, duration=120, dry_run=False): +def start(target=None, duration=120, dry_run=False, mscolab=False): """ Starts the automation process. @@ -141,8 +150,31 @@ def start(target=None, duration=120, dry_run=False): Note: Uncomment the line pag.press('q') if recording windows do not close in some cases. """ if platform.system() == 'Linux': + tutdir = "/tmp/msui_tutorials" + if not os.path.isdir(tutdir): + os.mkdir(tutdir) + os.environ["MSUI_CONFIG_PATH"] = tutdir + os.environ["XDG_CACHE_HOME"] = tutdir # makes sure the keyboard is set to US os.system("setxkbmap -layout us") + + # early + if mscolab: + mscdir = "/tmp/mscolab_tutorials" + if not os.path.isdir(mscdir): + os.makedirs(mscdir) + settings_file = os.path.join(mscdir, "mscolab_settings.py") + with open(settings_file, "w") as sf: + sf.write('import os\n') + sf.write('\n\n') + sf.write(f"BASE_DIR = '{mscdir}'\n") + sf.write('DATA_DIR = os.path.join(BASE_DIR, "colabdata")\n') + sf.write('OPERATIONS_DATA = os.path.join(DATA_DIR, "filedata")\n') + sf.write("DEBUG = True\n") + + os.environ["MSCOLAB_SETTINGS"] = settings_file + sys.path.insert(0, mscdir) + if target is None: return p1 = multiprocessing.Process(target=call_msui) @@ -150,6 +182,10 @@ def start(target=None, duration=120, dry_run=False): if not dry_run: p3 = multiprocessing.Process(target=call_recorder, kwargs={"duration": duration}) p3.start() + if mscolab is True: + print("Start and Seed MSColab server") + p4 = multiprocessing.Process(target=call_mscolab, daemon=True) + p4.start() print("\nINFO : Starting Automation.....\n") @@ -158,11 +194,15 @@ def start(target=None, duration=120, dry_run=False): p1.start() p2.start() - p2.join() - p1.join() - if not dry_run: - p3.join() + # unclear for what the join was needed + # p2.join() + # p1.join() + # if not dry_run: + # p3.join() + # if mscolab: + # p4.join() print("\n\nINFO : Automation Completes Successfully!") + # pag.press('q') # In some cases, recording windows does not closes. So it needs to ne there. sys.exit() diff --git a/tutorials/utils/restart_mscolab.py b/tutorials/utils/restart_mscolab.py new file mode 100644 index 000000000..70bc98967 --- /dev/null +++ b/tutorials/utils/restart_mscolab.py @@ -0,0 +1,81 @@ +""" + + mslib.tutorials.utils.restart_mscolab + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + This module has functions related to restarting the mscolab server. + + This file is part of MSS. + + :copyright: Copyright 2024 by Reimar Bauer + :license: APACHE-2.0, see LICENSE for details. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import socketio +import time +from pathlib import Path + +try: + import mscolab_settings +except ImportError: + mscolab_settings = None + + +sio = socketio.Client() + + +@sio.event +def connect(): + print('connection established') + return True + + +@sio.event +def disconnect(): + print('disconnected from server') + return False + + +def restart_mscolab(): + if mscolab_settings is not None: + msc_settings_file = mscolab_settings.__file__ + Path(msc_settings_file).touch() + else: + print("mscolab settings file not found, can't restart, have you set the PYTHONPATH environment variable?") + + +def verify_mscolab_server_alive(url="http://localhost", port="8083"): + try: + sio.connect(f'{url}:{port}') + return True + except socketio.exceptions.ConnectionError: + return False + + +def wait_until_mscolab_server_alive(max_wait=10, interval=0.5): + start_time = time.time() + + while True: + # Check server status + if verify_mscolab_server_alive(): + print("MSColab server is alive!") + break + + # Check if the max wait time has passed + if time.time() - start_time > max_wait: + print(f"Waited for {max_wait} seconds, but MSColab server is still not alive.") + break + + # Wait for short interval before checking server status again + time.sleep(interval)