From 71ea8d80f3114cb89406e8000106966f117eceb5 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Mon, 17 Mar 2025 17:21:56 +0100 Subject: [PATCH] WIP --- changedetectionio/conditions/__init__.py | 63 ++++++++++--------- changedetectionio/flask_app.py | 4 +- .../processors/text_json_diff/__init__.py | 17 +++-- changedetectionio/static/js/conditions.js | 46 +++++++++----- changedetectionio/templates/edit.html | 3 + 5 files changed, 78 insertions(+), 55 deletions(-) diff --git a/changedetectionio/conditions/__init__.py b/changedetectionio/conditions/__init__.py index 1ced1c93..d66cd55c 100644 --- a/changedetectionio/conditions/__init__.py +++ b/changedetectionio/conditions/__init__.py @@ -144,46 +144,49 @@ def construct_blueprint(datastore): @login_optionally_required def verify_condition_single_rule(watch_uuid): """Verify a single condition rule against the current snapshot""" - + from changedetectionio.processors.text_json_diff import prepare_filter_prevew + from flask import request, jsonify + from copy import deepcopy + + ephemeral_data = {} + # Get the watch data watch = datastore.data['watching'].get(watch_uuid) if not watch: return jsonify({'status': 'error', 'message': 'Watch not found'}), 404 - - # Get the rule data from the request - rule_data = request.json - if not rule_data: - return jsonify({'status': 'error', 'message': 'No rule data provided'}), 400 - - # Create ephemeral data with the current snapshot - ephemeral_data = {} - - # Get the current snapshot if available - if watch.history_n and watch.get_last_fetched_text_before_filters(): - ephemeral_data['text'] = watch.get_last_fetched_text_before_filters() - else: - return jsonify({ - 'status': 'error', - 'message': 'No snapshot available for verification. Please fetch content first.' - }), 400 - - # Test the rule - result = False - try: - # Create a temporary structure with just this rule - temp_watch_data = { - "conditions": [rule_data], - "conditions_match_logic": "ALL" # Single rule, so use ALL - } - # Create a temporary application data structure + # First use prepare_filter_prevew to process the form data + # This will return text_after_filter which is after all current form settings are applied + # Create ephemeral data with the text from the current snapshot + + try: + # Call prepare_filter_prevew to get a processed version of the content with current form settings + # We'll ignore the returned response and just use the datastore which is modified by the function + + # this should apply all filters etc so then we can run the CONDITIONS against the final output text + result = prepare_filter_prevew(datastore=datastore, + form_data=request.form, + watch_uuid=watch_uuid) + + ephemeral_data['text'] = result.get('after_filter', '') + # Create a temporary watch data structure with this single rule + tmp_watch_data = deepcopy(datastore.data['watching'].get(watch_uuid)) + + # Override the conditions in the temporary watch + rule_json = request.args.get("rule") + rule = json.loads(rule_json) if rule_json else None + tmp_watch_data['conditions'] = [rule] + tmp_watch_data['conditions_match_logic'] = "ALL" # Single rule, so use ALL + + + # Create a temporary application data structure for the rule check temp_app_data = { 'watching': { - watch_uuid: temp_watch_data + watch_uuid: tmp_watch_data } } - # Execute the rule against the current snapshot + # Execute the rule against the current snapshot with form data result = execute_ruleset_against_all_plugins( current_watch_uuid=watch_uuid, application_datastruct=temp_app_data, diff --git a/changedetectionio/flask_app.py b/changedetectionio/flask_app.py index 7101e0c0..52bc5ca6 100644 --- a/changedetectionio/flask_app.py +++ b/changedetectionio/flask_app.py @@ -1382,8 +1382,10 @@ def changedetection_app(config=None, datastore_o=None): @login_optionally_required def watch_get_preview_rendered(uuid): '''For when viewing the "preview" of the rendered text from inside of Edit''' + from flask import jsonify from .processors.text_json_diff import prepare_filter_prevew - return prepare_filter_prevew(watch_uuid=uuid, datastore=datastore) + result = prepare_filter_prevew(watch_uuid=uuid, form_data=request.form, datastore=datastore) + return jsonify(result) @app.route("/form/add/quickwatch", methods=['POST']) diff --git a/changedetectionio/processors/text_json_diff/__init__.py b/changedetectionio/processors/text_json_diff/__init__.py index a6e018fd..8e5bdfc8 100644 --- a/changedetectionio/processors/text_json_diff/__init__.py +++ b/changedetectionio/processors/text_json_diff/__init__.py @@ -28,13 +28,13 @@ def _task(watch, update_handler): return text_after_filter -def prepare_filter_prevew(datastore, watch_uuid): +def prepare_filter_prevew(datastore, watch_uuid, form_data): '''Used by @app.route("/edit//preview-rendered", methods=['POST'])''' from changedetectionio import forms, html_tools from changedetectionio.model.Watch import model as watch_model from concurrent.futures import ProcessPoolExecutor from copy import deepcopy - from flask import request, jsonify + from flask import request import brotli import importlib import os @@ -50,12 +50,12 @@ def prepare_filter_prevew(datastore, watch_uuid): if tmp_watch and tmp_watch.history and os.path.isdir(tmp_watch.watch_data_dir): # Splice in the temporary stuff from the form - form = forms.processor_text_json_diff_form(formdata=request.form if request.method == 'POST' else None, - data=request.form + form = forms.processor_text_json_diff_form(formdata=form_data if request.method == 'POST' else None, + data=form_data ) # Only update vars that came in via the AJAX post - p = {k: v for k, v in form.data.items() if k in request.form.keys()} + p = {k: v for k, v in form.data.items() if k in form_data.keys()} tmp_watch.update(p) blank_watch_no_filters = watch_model() blank_watch_no_filters['url'] = tmp_watch.get('url') @@ -103,13 +103,12 @@ def prepare_filter_prevew(datastore, watch_uuid): logger.trace(f"Parsed in {time.time() - now:.3f}s") - return jsonify( - { + return ({ 'after_filter': text_after_filter, 'before_filter': text_before_filter.decode('utf-8') if isinstance(text_before_filter, bytes) else text_before_filter, 'duration': time.time() - now, 'trigger_line_numbers': trigger_line_numbers, 'ignore_line_numbers': ignore_line_numbers, - } - ) + }) + diff --git a/changedetectionio/static/js/conditions.js b/changedetectionio/static/js/conditions.js index 2cb10b84..8c627d63 100644 --- a/changedetectionio/static/js/conditions.js +++ b/changedetectionio/static/js/conditions.js @@ -49,16 +49,7 @@ $(document).ready(function () { alert("Please fill in all fields (Field, Operator, and Value) before verifying."); return; } - - // Extract the watch UUID from the URL - const url = window.location.pathname; - const uuidMatch = url.match(/\/edit\/([^\/]+)/); - if (!uuidMatch || !uuidMatch[1]) { - alert("Could not determine the watch UUID. Please save your changes first."); - return; - } - - const watchUuid = uuidMatch[1]; + // Create a rule object const rule = { @@ -72,13 +63,38 @@ $(document).ready(function () { const originalHTML = $button.html(); $button.html("⌛").prop("disabled", true); + // Collect form data - similar to request_textpreview_update() in watch-settings.js + let formData = new FormData(); + $('#edit-text-filter textarea, #edit-text-filter input').each(function() { + const $element = $(this); + const name = $element.attr('name'); + if (name) { + if ($element.is(':checkbox')) { + formData.append(name, $element.is(':checked') ? $element.val() : false); + } else { + formData.append(name, $element.val()); + } + } + }); + + // Also collect select values + $('#edit-text-filter select').each(function() { + const $element = $(this); + const name = $element.attr('name'); + if (name) { + formData.append(name, $element.val()); + } + }); + + // Send the request to verify the rule $.ajax({ - url: `/conditions/${watchUuid}/verify-condition-single-rule`, + url: verify_condition_rule_url+"?"+ new URLSearchParams({ rule: JSON.stringify(rule) }).toString(), type: "POST", - contentType: "application/json", - data: JSON.stringify(rule), - success: function(response) { + data: formData, + processData: false, // Prevent jQuery from converting FormData to a string + contentType: false, // Let the browser set the correct content type + success: function (response) { if (response.status === "success") { if (response.result) { alert("✅ Condition PASSES verification against current snapshot!"); @@ -90,7 +106,7 @@ $(document).ready(function () { } $button.html(originalHTML).prop("disabled", false); }, - error: function(xhr) { + error: function (xhr) { let errorMsg = "Error verifying condition."; if (xhr.responseJSON && xhr.responseJSON.message) { errorMsg = xhr.responseJSON.message; diff --git a/changedetectionio/templates/edit.html b/changedetectionio/templates/edit.html index 65c71231..4b5e4698 100644 --- a/changedetectionio/templates/edit.html +++ b/changedetectionio/templates/edit.html @@ -286,6 +286,9 @@ Math: {{ 1 + 1 }}") }} {% if watch['processor'] == 'text_json_diff' %}
+