This commit is contained in:
dgtlmoon
2025-03-17 17:21:56 +01:00
parent 4f48958187
commit 71ea8d80f3
5 changed files with 78 additions and 55 deletions
+33 -30
View File
@@ -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,
+3 -1
View File
@@ -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'])
@@ -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/<string:uuid>/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,
}
)
})
+31 -15
View File
@@ -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;
+3
View File
@@ -286,6 +286,9 @@ Math: {{ 1 + 1 }}") }}
{% if watch['processor'] == 'text_json_diff' %}
<div class="tab-pane-inner" id="conditions">
<script>
const verify_condition_rule_url="{{url_for('conditions.verify_condition_single_rule', watch_uuid=uuid)}}";
</script>
<style>
.verifyRuleRow {
background-color: #4caf50;