From a3d3ca7a7bb92f81fbe70972014e2522fa45364c Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Wed, 16 Feb 2022 11:53:40 +0100 Subject: [PATCH 01/13] allow users to remove documents Applies only to their own collections --- src/mortimer/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mortimer/models.py b/src/mortimer/models.py index 58b71f1..6667ba9 100644 --- a/src/mortimer/models.py +++ b/src/mortimer/models.py @@ -89,7 +89,7 @@ def _prepare_db_role_privileges(self): c_unlinked = {**res, **{"collection": self.alfred_col_unlinked}} c_misc = {**res, **{"collection": self.alfred_col_misc}} - act = ["find", "insert", "update"] + act = ["find", "insert", "update", "remove"] priv = [{"resource": res_dict, "actions": act} for res_dict in [c_exp, c_unlinked, c_misc]] return priv From 601feace442217343aa000a667ff22cce78ad525 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Wed, 16 Feb 2022 11:54:31 +0100 Subject: [PATCH 02/13] Update url-jumping mechanism Brings url-jumping up to date to current alfred --- src/mortimer/web_experiments/alfredo.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mortimer/web_experiments/alfredo.py b/src/mortimer/web_experiments/alfredo.py index a73a8f5..daa5dcc 100644 --- a/src/mortimer/web_experiments/alfredo.py +++ b/src/mortimer/web_experiments/alfredo.py @@ -236,7 +236,10 @@ def start(expid): ) else: abort(500) - + + page = request.args.get("page", None) + if page: + return redirect(url_for("alfredo.experiment", page=page)) return redirect(url_for("alfredo.experiment")) @@ -254,7 +257,7 @@ def experiment(): if request.method == "GET": url_pagename = request.args.get("page", None) # https://basepath.de/experiment?page=name if url_pagename: - experiment.movement_manager.jump_by_name(name=url_pagename) + experiment.movement_manager.move(direction=f"jump>{url_pagename}") token = session["page_tokens"].get(tkey, uuid4().hex) session["page_tokens"][tkey] = token From e92df0596db30bb036dc31b0cda7f10efcacadb9 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Tue, 1 Mar 2022 18:28:09 +0100 Subject: [PATCH 03/13] update for compatibility to pymongo 4 --- src/mortimer/config.py | 2 +- src/mortimer/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mortimer/config.py b/src/mortimer/config.py index cf2af96..7c58459 100644 --- a/src/mortimer/config.py +++ b/src/mortimer/config.py @@ -84,7 +84,7 @@ class BaseConfig: MONGODB_SETTINGS["password"] = None MONGODB_SETTINGS["authentication_source"] = "admin" MONGODB_SETTINGS["ssl"] = False - MONGODB_SETTINGS["ssl_ca_certs"] = None + MONGODB_SETTINGS["tlsCAFile"] = None # Alfred settings ALFRED_DB = "alfred" diff --git a/src/mortimer/models.py b/src/mortimer/models.py index 6667ba9..e8b4f85 100644 --- a/src/mortimer/models.py +++ b/src/mortimer/models.py @@ -163,7 +163,7 @@ def mongo_saving_agent(self) -> dict: "user": self.alfred_user, "password": f.decrypt(self.alfred_pw).decode(), "use_ssl": str(appdb_config.get("ssl")).lower(), - "ca_file_path": str(appdb_config.get("ssl_ca_certs")), + "ca_file_path": str(appdb_config.get("tlsCAFile")), "activation_level": "1", } return {"mongo_saving_agent": mongo_config} From 45d43a3a18349482711232fb342d5735f4a20031 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Tue, 1 Mar 2022 18:28:22 +0100 Subject: [PATCH 04/13] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0ff6387..4246d08 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ dist/ mortimer_venv/ .mortimer/ .venv/ +.python-version From 1bdfb557c56c112145735ecbb011bbf3ee8c81e1 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Mon, 21 Mar 2022 15:15:10 +0100 Subject: [PATCH 05/13] update metadata --- CHANGELOG.md | 11 +++++++++++ src/mortimer/_version.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db890ea..0d4c60a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Mortimer v0.8.13 [unreleased] + +### Changed + +- (New) users are now allowed to remove documents in their own alfred3 + mongoDB collections. + +### Fixed + +- Fixed support for url-jumping support + ## Mortimer v0.8.12 (Released 2021-10-28) ### Added diff --git a/src/mortimer/_version.py b/src/mortimer/_version.py index 0c5e4de..df7f812 100644 --- a/src/mortimer/_version.py +++ b/src/mortimer/_version.py @@ -5,4 +5,4 @@ -__version__ = "0.8.12" +__version__ = "0.8.13a" From 1727c53cc43a147778b5f0b4237433fe269ff48c Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Wed, 27 Apr 2022 19:42:21 +0200 Subject: [PATCH 06/13] add logging, close #78 --- src/mortimer/__init__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mortimer/__init__.py b/src/mortimer/__init__.py index 76c4f68..02f8169 100644 --- a/src/mortimer/__init__.py +++ b/src/mortimer/__init__.py @@ -27,7 +27,15 @@ db = MongoEngine() # mortimer database # application factory -def create_app(instance_path=None): +def create_app(instance_path=None, logfile: str = None): + logger = logging.getLogger("mortimer") + fh = logging.FileHandler(logfile) + formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") + fh.setFormatter(formatter) + + logger.addHandler(fh) + logger.setLevel(logging.INFO) + app = Flask(__name__, instance_path=instance_path) # apply configuration From 07243d6eee615513072a8efd3762eb849d8abb76 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Wed, 27 Apr 2022 19:42:45 +0200 Subject: [PATCH 07/13] remove log call this log call is most likely overly verbose --- src/mortimer/models.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/mortimer/models.py b/src/mortimer/models.py index e8b4f85..a011fd8 100644 --- a/src/mortimer/models.py +++ b/src/mortimer/models.py @@ -273,14 +273,6 @@ def parse_exp_secrets(self) -> ExperimentSecrets: try: secrets_string = f.decrypt(self.exp_secrets).decode() except TypeError: - logger = logging.getLogger(__name__) - logger.info( - ( - "Exception during secrets decryption. " - "To proceed, the value of secrets.conf was set to an empty string." - f" Exp id={str(self.id)}" - ) - ) secrets_string = "" exp_secrets = ExperimentSecrets(expdir=self.path) From ea799b57b079e38d7ddc7a25d03353a49bcf0072 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Wed, 27 Apr 2022 19:57:57 +0200 Subject: [PATCH 08/13] log 412 responses, close #79 --- src/mortimer/web_experiments/alfredo.py | 99 +++++++++++++++---------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/src/mortimer/web_experiments/alfredo.py b/src/mortimer/web_experiments/alfredo.py index daa5dcc..1cfdc03 100644 --- a/src/mortimer/web_experiments/alfredo.py +++ b/src/mortimer/web_experiments/alfredo.py @@ -26,12 +26,16 @@ send_from_directory, session, url_for, - jsonify + jsonify, ) from flask_login import current_user from mortimer.models import WebExperiment, User -from mortimer.utils import create_fernet, is_social_media_preview, render_social_media_preview +from mortimer.utils import ( + create_fernet, + is_social_media_preview, + render_social_media_preview, +) from alfred3 import alfredlog import alfred3.config @@ -45,7 +49,7 @@ def __init__(self): self.config = None def generate_experiment(self, config=None): # pylint: disable=method-hidden - """Hook for the ``generate_experiment`` function extracted from + """Hook for the ``generate_experiment`` function extracted from the user's script.py. It is meant to be replaced in ``run.py``. """ @@ -75,7 +79,9 @@ def number_of_func_params(func): def import_script(experiment_id): - experiment = WebExperiment.objects.get_or_404(id=experiment_id) # pylint: disable=no-member + experiment = WebExperiment.objects.get_or_404( + id=experiment_id + ) # pylint: disable=no-member # Fast path: see if the module has already been imported. try: @@ -127,11 +133,11 @@ def get(self, key): self.lock.release() if rv is None: - try: - print("Tried to access experiment with key %s" % key) - print("Available Keys: %s" % list(self.experiments.keys())) - except Exception: - pass + molog = logging.getLogger("mortimer") + molog.warning( + f"Tried to access experiment with key '{key}'. Available Keys:" + f" {list(self.experiments.keys())}" + ) abort(412) self.save(key, rv) return rv @@ -139,10 +145,11 @@ def get(self, key): def remove_outdated(self): self.lock.acquire() current_time = int(time()) + molog = logging.getLogger("mortimer") for k in list(self.experiments.keys()): v = self.experiments[k] if current_time - v[0] > self.timeout: - print("delete exp with key %s and last access time %s" % (k, v[0])) + molog.warning(f"Delete exp with key {k} and last access time {v[0]}") del self.experiments[k] self.lock.release() @@ -170,17 +177,21 @@ def start(expid): if is_social_media_preview(request.headers.get("User-Agent")): return render_social_media_preview(config) - if not experiment.public and experiment.password != request.form.get("password", None): + if not experiment.public and experiment.password != request.form.get( + "password", None + ): exp_url = url_for("alfredo.start", expid=str(experiment.id)) return ( - '

Please enter the password

\ -
' - % exp_url + '

Please enter the password

Submit
' % exp_url ) - + args = request.args.to_dict() test_mode = args.get("test") in ["true", "True", "TRUE"] - debug_mode = args.get("debug") in ["true", "True", "TRUE"] or config.getboolean("general", "debug") + debug_mode = args.get("debug") in ["true", "True", "TRUE"] or config.getboolean( + "general", "debug" + ) if not experiment.active and not test_mode and not debug_mode: return render_template("exp_inactive.html") @@ -199,11 +210,8 @@ def start(expid): try: user_script = import_script(experiment.id) exp_session = user_script.exp.create_session( - session_id=sid, - config=config, - secrets=secrets, - **request.args - ) + session_id=sid, config=config, secrets=secrets, **request.args + ) except Exception: msg = "Error during creation of experiment session." @@ -215,10 +223,11 @@ def start(expid): "web_experiments.experiment", username=current_user.username, exp_title=experiment.title, - )) + ) + ) else: abort(500) - + try: exp_session._start() experiment_manager.save(sid, exp_session) @@ -236,7 +245,7 @@ def start(expid): ) else: abort(500) - + page = request.args.get("page", None) if page: return redirect(url_for("alfredo.experiment", page=page)) @@ -245,9 +254,14 @@ def start(expid): @alfredo.route("/experiment", methods=["GET", "POST"]) def experiment(): + molog = logging.getLogger("mortimer") try: sid = session["sid"] except KeyError: + molog.warning( + "Experiment route called, but there was no session id in the session" + " cookie." + ) abort(412) experiment = experiment_manager.get(sid) @@ -255,13 +269,15 @@ def experiment(): try: if request.method == "GET": - url_pagename = request.args.get("page", None) # https://basepath.de/experiment?page=name + url_pagename = request.args.get( + "page", None + ) # https://basepath.de/experiment?page=name if url_pagename: experiment.movement_manager.move(direction=f"jump>{url_pagename}") token = session["page_tokens"].get(tkey, uuid4().hex) session["page_tokens"][tkey] = token - session.modified = True # because the dict is mutable + session.modified = True # because the dict is mutable current_page_html = make_response(experiment.ui.render_html(token)) current_page_html.cache_control.no_cache = True @@ -272,11 +288,10 @@ def experiment(): submitted_token = request.values.get("page_token", None) token = session["page_tokens"].pop(tkey, None) - session.modified = True # because the dict is mutable + session.modified = True # because the dict is mutable if not token or not token == submitted_token: return redirect(url_for("alfredo.experiment")) - data = request.values.to_dict() data.pop("move", None) data.pop("directjump", None) @@ -290,9 +305,11 @@ def experiment(): else: abort(400) return redirect(url_for("alfredo.experiment")) - + except Exception: - log = alfredlog.QueuedLoggingInterface("alfred3", f"exp.{str(experiment.exp_id)}") + log = alfredlog.QueuedLoggingInterface( + "alfred3", f"exp.{str(experiment.exp_id)}" + ) log.session_id = sid msg = "Exception during experiment execution." log.exception(msg) @@ -314,9 +331,13 @@ def staticfile(identifier): try: sid = session["sid"] experiment = experiment_manager.get(sid) - path, content_type = experiment.user_interface_controller.get_static_file(identifier) + path, content_type = experiment.user_interface_controller.get_static_file( + identifier + ) dirname, filename = os.path.split(path) - resp = make_response(send_from_directory(dirname, filename, mimetype=content_type)) + resp = make_response( + send_from_directory(dirname, filename, mimetype=content_type) + ) # resp.cache_control.no_cache = True return resp @@ -329,13 +350,16 @@ def dynamicfile(identifier): try: sid = session["sid"] experiment = experiment_manager.get(sid) - strIO, content_type = experiment.user_interface_controller.get_dynamic_file(identifier) + strIO, content_type = experiment.user_interface_controller.get_dynamic_file( + identifier + ) except KeyError: abort(404) resp = make_response(send_file(strIO, mimetype=content_type)) resp.cache_control.no_cache = True return resp + @alfredo.route("/callable/", methods=["GET", "POST"]) def callable(identifier): try: @@ -344,14 +368,14 @@ def callable(identifier): f = experiment.user_interface_controller.get_callable(identifier) except KeyError: abort(404) - + if request.content_type == "application/json": values = request.get_json() else: values = request.values.to_dict() - - values.pop("_", None) # remove argument with name "_" - + + values.pop("_", None) # remove argument with name "_" + rv = f(**values) if rv is not None: resp = jsonify(rv) @@ -359,4 +383,3 @@ def callable(identifier): return resp else: return (" ", 204) - From 79ff8bfff927ed6c44f81349b3c9dace17c2e2fe Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Wed, 27 Apr 2022 20:03:55 +0200 Subject: [PATCH 09/13] update meta --- CHANGELOG.md | 5 +++++ src/mortimer/_version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4c60a..27f5642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed support for url-jumping support +### Added + +- Added logging capability (#78) +- Added log calls for 412 server responses (#79) + ## Mortimer v0.8.12 (Released 2021-10-28) ### Added diff --git a/src/mortimer/_version.py b/src/mortimer/_version.py index df7f812..ad19d6f 100644 --- a/src/mortimer/_version.py +++ b/src/mortimer/_version.py @@ -5,4 +5,4 @@ -__version__ = "0.8.13a" +__version__ = "0.8.13b" From e0b164924c3a5c0e3911f868c1447fc410f3e0e6 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Wed, 27 Apr 2022 20:22:36 +0200 Subject: [PATCH 10/13] improve log messages (#79) --- src/mortimer/web_experiments/alfredo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mortimer/web_experiments/alfredo.py b/src/mortimer/web_experiments/alfredo.py index 1cfdc03..688bd86 100644 --- a/src/mortimer/web_experiments/alfredo.py +++ b/src/mortimer/web_experiments/alfredo.py @@ -135,7 +135,7 @@ def get(self, key): if rv is None: molog = logging.getLogger("mortimer") molog.warning( - f"Tried to access experiment with key '{key}'. Available Keys:" + f"Tried to access experiment with session id '{key}'. Available Keys:" f" {list(self.experiments.keys())}" ) abort(412) @@ -149,7 +149,7 @@ def remove_outdated(self): for k in list(self.experiments.keys()): v = self.experiments[k] if current_time - v[0] > self.timeout: - molog.warning(f"Delete exp with key {k} and last access time {v[0]}") + molog.warning(f"Delete exp with session id '{k}' and last access time {v[0]}") del self.experiments[k] self.lock.release() From 0e5cab89694506e4b994fae912345d896daf808d Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Sat, 21 May 2022 17:31:29 +0200 Subject: [PATCH 11/13] fixes #82 --- src/mortimer/templates/experiment.html | 8 ++++---- src/mortimer/web_experiments/routes.py | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/mortimer/templates/experiment.html b/src/mortimer/templates/experiment.html index d2d9852..9664674 100644 --- a/src/mortimer/templates/experiment.html +++ b/src/mortimer/templates/experiment.html @@ -146,15 +146,15 @@

{{ n["fin"] }} / {{ n["tot
- {% if activity["last"] != 'none' %} - {{ activity["last"] }} + {% if activity["last_time"] != 'none' %} + {{ activity["last_time"] }} {% else %} - none - {% endif %}

- {% if activity["last"] != 'none' %} - {{ activity["last"] }} + {% if activity["last_date"] != 'none' %} + {{ activity["last_date"] }} {% else %} - none - {% endif %} diff --git a/src/mortimer/web_experiments/routes.py b/src/mortimer/web_experiments/routes.py index f646a4b..349372c 100644 --- a/src/mortimer/web_experiments/routes.py +++ b/src/mortimer/web_experiments/routes.py @@ -192,13 +192,16 @@ def experiment(username, exp_title): if times: try: activity["first"] = datetime.fromtimestamp(times[0]["first"]).strftime('%Y-%m-%d, %H:%M') - activity["last"] = datetime.fromtimestamp(times[0]["last"]).strftime('%H:%M') + activity["last_time"] = datetime.fromtimestamp(times[0]["last"]).strftime('%H:%M') + activity["last_date"] = datetime.fromtimestamp(times[0]["last"]).strftime('%Y-%m-%d') except TypeError: activity["first"] = times[0]["first"] - activity["last"] = times[0]["last"] + activity["last_time"] = times[0]["last"] + activity["last_date"] = "none" else: activity["first"] = "none" - activity["last"] = "none" + activity["last_time"] = "none" + activity["last_date"] = "none" # Form for script.py upload form = NewScriptForm() From 0ada1c5b14688c147db520aea7a3fd24571e3dff Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Sat, 21 May 2022 17:32:58 +0200 Subject: [PATCH 12/13] Update _version.py --- src/mortimer/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mortimer/_version.py b/src/mortimer/_version.py index ad19d6f..5b8d99b 100644 --- a/src/mortimer/_version.py +++ b/src/mortimer/_version.py @@ -5,4 +5,4 @@ -__version__ = "0.8.13b" +__version__ = "0.8.13" From c5680c4ad1b982cd0aaa20bd284d0041c9ac7029 Mon Sep 17 00:00:00 2001 From: Johannes Brachem <37882800+jobrachem@users.noreply.github.com> Date: Sat, 21 May 2022 17:33:43 +0200 Subject: [PATCH 13/13] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27f5642..45e8b90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Mortimer v0.8.13 [unreleased] +## Mortimer v0.8.13 (Released 2022-05-21) ### Changed @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed support for url-jumping support +- Fixed #82 ### Added