kopia lustrzana https://github.com/dgtlmoon/changedetection.io
Ability to use restock and price amounts in notifications as tokens (for example {{restock.price}} ) (#2503)
rodzic
bde27c8a8f
commit
321426dea2
14
README.md
14
README.md
|
@ -41,6 +41,20 @@ Using the **Browser Steps** configuration, add basic steps before performing cha
|
||||||
After **Browser Steps** have been run, then visit the **Visual Selector** tab to refine the content you're interested in.
|
After **Browser Steps** have been run, then visit the **Visual Selector** tab to refine the content you're interested in.
|
||||||
Requires Playwright to be enabled.
|
Requires Playwright to be enabled.
|
||||||
|
|
||||||
|
### Awesome restock and price change notifications
|
||||||
|
|
||||||
|
Enable the _"Re-stock & Price detection for single product pages"_ option to activate the best way to monitor product pricing.
|
||||||
|
|
||||||
|
Easily organise and monitor prices for products from the dashboard, get alerts and notifications when the price of a product changes or comes back in stock again!
|
||||||
|
|
||||||
|
[<img src="docs/restock-overview.png" style="max-width:100%;" alt="Easily keep an eye on product price changes directly from the UI" title="Easily keep an eye on product price changes directly from the UI" />](https://changedetection.io?src=github)
|
||||||
|
|
||||||
|
Set price change notification parameters, upper and lower price, price change percentage and more.
|
||||||
|
Always know when a product for sale drops in price.
|
||||||
|
|
||||||
|
[<img src="docs/restock-settings.png" style="max-width:100%;" alt="Set upper lower and percentage price change notification values" title="Set upper lower and percentage price change notification values" />](https://changedetection.io?src=github)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Example use cases
|
### Example use cases
|
||||||
|
|
||||||
|
|
|
@ -106,12 +106,14 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||||
|
|
||||||
form = group_restock_settings_form(formdata=request.form if request.method == 'POST' else None,
|
form = group_restock_settings_form(formdata=request.form if request.method == 'POST' else None,
|
||||||
data=default,
|
data=default,
|
||||||
|
extra_notification_tokens=datastore.get_unique_notification_tokens_available()
|
||||||
)
|
)
|
||||||
|
|
||||||
template_args = {
|
template_args = {
|
||||||
'data': default,
|
'data': default,
|
||||||
'form': form,
|
'form': form,
|
||||||
'watch': default
|
'watch': default,
|
||||||
|
'extra_notification_token_placeholder_info': datastore.get_unique_notification_token_placeholders_available(),
|
||||||
}
|
}
|
||||||
|
|
||||||
included_content = {}
|
included_content = {}
|
||||||
|
@ -161,6 +163,7 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||||
|
|
||||||
form = group_restock_settings_form(formdata=request.form if request.method == 'POST' else None,
|
form = group_restock_settings_form(formdata=request.form if request.method == 'POST' else None,
|
||||||
data=default,
|
data=default,
|
||||||
|
extra_notification_tokens=datastore.get_unique_notification_tokens_available()
|
||||||
)
|
)
|
||||||
# @todo subclass form so validation works
|
# @todo subclass form so validation works
|
||||||
#if not form.validate():
|
#if not form.validate():
|
||||||
|
|
|
@ -128,7 +128,7 @@ nav
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="#notifications" id="notification-setting-reset-to-default" class="pure-button button-xsmall" style="right: 20px; top: 20px; position: absolute; background-color: #5f42dd; border-radius: 4px; font-size: 70%; color: #fff">Use system defaults</a>
|
<a href="#notifications" id="notification-setting-reset-to-default" class="pure-button button-xsmall" style="right: 20px; top: 20px; position: absolute; background-color: #5f42dd; border-radius: 4px; font-size: 70%; color: #fff">Use system defaults</a>
|
||||||
|
|
||||||
{{ render_common_settings_form(form, emailprefix, settings_application) }}
|
{{ render_common_settings_form(form, emailprefix, settings_application, extra_notification_token_placeholder_info) }}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -696,8 +696,9 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
form_class = forms.processor_text_json_diff_form
|
form_class = forms.processor_text_json_diff_form
|
||||||
|
|
||||||
form = form_class(formdata=request.form if request.method == 'POST' else None,
|
form = form_class(formdata=request.form if request.method == 'POST' else None,
|
||||||
data=default
|
data=default,
|
||||||
)
|
extra_notification_tokens=default.extra_notification_token_values()
|
||||||
|
)
|
||||||
|
|
||||||
# For the form widget tag UUID back to "string name" for the field
|
# For the form widget tag UUID back to "string name" for the field
|
||||||
form.tags.datastore = datastore
|
form.tags.datastore = datastore
|
||||||
|
@ -824,6 +825,7 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
'emailprefix': os.getenv('NOTIFICATION_MAIL_BUTTON_PREFIX', False),
|
'emailprefix': os.getenv('NOTIFICATION_MAIL_BUTTON_PREFIX', False),
|
||||||
'extra_title': f" - Edit - {watch.label}",
|
'extra_title': f" - Edit - {watch.label}",
|
||||||
'extra_processor_config': form.extra_tab_content(),
|
'extra_processor_config': form.extra_tab_content(),
|
||||||
|
'extra_notification_token_placeholder_info': datastore.get_unique_notification_token_placeholders_available(),
|
||||||
'form': form,
|
'form': form,
|
||||||
'has_default_notification_urls': True if len(datastore.data['settings']['application']['notification_urls']) else False,
|
'has_default_notification_urls': True if len(datastore.data['settings']['application']['notification_urls']) else False,
|
||||||
'has_extra_headers_file': len(datastore.get_all_headers_in_textfile_for_watch(uuid=uuid)) > 0,
|
'has_extra_headers_file': len(datastore.get_all_headers_in_textfile_for_watch(uuid=uuid)) > 0,
|
||||||
|
@ -878,7 +880,8 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
|
|
||||||
# Don't use form.data on POST so that it doesnt overrid the checkbox status from the POST status
|
# Don't use form.data on POST so that it doesnt overrid the checkbox status from the POST status
|
||||||
form = forms.globalSettingsForm(formdata=request.form if request.method == 'POST' else None,
|
form = forms.globalSettingsForm(formdata=request.form if request.method == 'POST' else None,
|
||||||
data=default
|
data=default,
|
||||||
|
extra_notification_tokens=datastore.get_unique_notification_tokens_available()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Remove the last option 'System default'
|
# Remove the last option 'System default'
|
||||||
|
@ -930,6 +933,7 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
output = render_template("settings.html",
|
output = render_template("settings.html",
|
||||||
api_key=datastore.data['settings']['application'].get('api_access_token'),
|
api_key=datastore.data['settings']['application'].get('api_access_token'),
|
||||||
emailprefix=os.getenv('NOTIFICATION_MAIL_BUTTON_PREFIX', False),
|
emailprefix=os.getenv('NOTIFICATION_MAIL_BUTTON_PREFIX', False),
|
||||||
|
extra_notification_token_placeholder_info=datastore.get_unique_notification_token_placeholders_available(),
|
||||||
form=form,
|
form=form,
|
||||||
hide_remove_pass=os.getenv("SALTED_PASS", False),
|
hide_remove_pass=os.getenv("SALTED_PASS", False),
|
||||||
min_system_recheck_seconds=int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 3)),
|
min_system_recheck_seconds=int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 3)),
|
||||||
|
|
|
@ -231,9 +231,6 @@ class ValidateJinja2Template(object):
|
||||||
"""
|
"""
|
||||||
Validates that a {token} is from a valid set
|
Validates that a {token} is from a valid set
|
||||||
"""
|
"""
|
||||||
def __init__(self, message=None):
|
|
||||||
self.message = message
|
|
||||||
|
|
||||||
def __call__(self, form, field):
|
def __call__(self, form, field):
|
||||||
from changedetectionio import notification
|
from changedetectionio import notification
|
||||||
|
|
||||||
|
@ -248,6 +245,10 @@ class ValidateJinja2Template(object):
|
||||||
try:
|
try:
|
||||||
jinja2_env = ImmutableSandboxedEnvironment(loader=BaseLoader)
|
jinja2_env = ImmutableSandboxedEnvironment(loader=BaseLoader)
|
||||||
jinja2_env.globals.update(notification.valid_tokens)
|
jinja2_env.globals.update(notification.valid_tokens)
|
||||||
|
# Extra validation tokens provided on the form_class(... extra_tokens={}) setup
|
||||||
|
if hasattr(field, 'extra_notification_tokens'):
|
||||||
|
jinja2_env.globals.update(field.extra_notification_tokens)
|
||||||
|
|
||||||
jinja2_env.from_string(joined_data).render()
|
jinja2_env.from_string(joined_data).render()
|
||||||
except TemplateSyntaxError as e:
|
except TemplateSyntaxError as e:
|
||||||
raise ValidationError(f"This is not a valid Jinja2 template: {e}") from e
|
raise ValidationError(f"This is not a valid Jinja2 template: {e}") from e
|
||||||
|
@ -422,6 +423,12 @@ class quickWatchForm(Form):
|
||||||
class commonSettingsForm(Form):
|
class commonSettingsForm(Form):
|
||||||
from . import processors
|
from . import processors
|
||||||
|
|
||||||
|
def __init__(self, formdata=None, obj=None, prefix="", data=None, meta=None, **kwargs):
|
||||||
|
super().__init__(formdata, obj, prefix, data, meta, **kwargs)
|
||||||
|
self.notification_body.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||||
|
self.notification_title.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||||
|
self.notification_urls.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||||
|
|
||||||
extract_title_as_title = BooleanField('Extract <title> from document and use as watch title', default=False)
|
extract_title_as_title = BooleanField('Extract <title> from document and use as watch title', default=False)
|
||||||
fetch_backend = RadioField(u'Fetch Method', choices=content_fetchers.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
fetch_backend = RadioField(u'Fetch Method', choices=content_fetchers.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
||||||
notification_body = TextAreaField('Notification Body', default='{{ watch_url }} had a change.', validators=[validators.Optional(), ValidateJinja2Template()])
|
notification_body = TextAreaField('Notification Body', default='{{ watch_url }} had a change.', validators=[validators.Optional(), ValidateJinja2Template()])
|
||||||
|
@ -429,8 +436,8 @@ class commonSettingsForm(Form):
|
||||||
notification_title = StringField('Notification Title', default='ChangeDetection.io Notification - {{ watch_url }}', validators=[validators.Optional(), ValidateJinja2Template()])
|
notification_title = StringField('Notification Title', default='ChangeDetection.io Notification - {{ watch_url }}', validators=[validators.Optional(), ValidateJinja2Template()])
|
||||||
notification_urls = StringListField('Notification URL List', validators=[validators.Optional(), ValidateAppRiseServers(), ValidateJinja2Template()])
|
notification_urls = StringListField('Notification URL List', validators=[validators.Optional(), ValidateAppRiseServers(), ValidateJinja2Template()])
|
||||||
processor = RadioField( label=u"Processor - What do you want to achieve?", choices=processors.available_processors(), default="text_json_diff")
|
processor = RadioField( label=u"Processor - What do you want to achieve?", choices=processors.available_processors(), default="text_json_diff")
|
||||||
webdriver_delay = IntegerField('Wait seconds before extracting text', validators=[validators.Optional(), validators.NumberRange(min=1,
|
webdriver_delay = IntegerField('Wait seconds before extracting text', validators=[validators.Optional(), validators.NumberRange(min=1, message="Should contain one or more seconds")])
|
||||||
message="Should contain one or more seconds")])
|
|
||||||
|
|
||||||
class importForm(Form):
|
class importForm(Form):
|
||||||
from . import processors
|
from . import processors
|
||||||
|
@ -590,6 +597,11 @@ class globalSettingsForm(Form):
|
||||||
# Define these as FormFields/"sub forms", this way it matches the JSON storage
|
# Define these as FormFields/"sub forms", this way it matches the JSON storage
|
||||||
# datastore.data['settings']['application']..
|
# datastore.data['settings']['application']..
|
||||||
# datastore.data['settings']['requests']..
|
# datastore.data['settings']['requests']..
|
||||||
|
def __init__(self, formdata=None, obj=None, prefix="", data=None, meta=None, **kwargs):
|
||||||
|
super().__init__(formdata, obj, prefix, data, meta, **kwargs)
|
||||||
|
self.application.notification_body.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||||
|
self.application.notification_title.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||||
|
self.application.notification_urls.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||||
|
|
||||||
requests = FormField(globalSettingsRequestForm)
|
requests = FormField(globalSettingsRequestForm)
|
||||||
application = FormField(globalSettingsApplicationForm)
|
application = FormField(globalSettingsApplicationForm)
|
||||||
|
|
|
@ -432,6 +432,17 @@ class model(watch_base):
|
||||||
def toggle_mute(self):
|
def toggle_mute(self):
|
||||||
self['notification_muted'] ^= True
|
self['notification_muted'] ^= True
|
||||||
|
|
||||||
|
def extra_notification_token_values(self):
|
||||||
|
# Used for providing extra tokens
|
||||||
|
# return {'widget': 555}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def extra_notification_token_placeholder_info(self):
|
||||||
|
# Used for providing extra tokens
|
||||||
|
# return [('widget', "Get widget amounts")]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
def extract_regex_from_all_history(self, regex):
|
def extract_regex_from_all_history(self, regex):
|
||||||
import csv
|
import csv
|
||||||
import re
|
import re
|
||||||
|
|
|
@ -272,19 +272,18 @@ def create_notification_parameters(n_object, datastore):
|
||||||
tokens.update(
|
tokens.update(
|
||||||
{
|
{
|
||||||
'base_url': base_url,
|
'base_url': base_url,
|
||||||
'current_snapshot': n_object.get('current_snapshot', ''),
|
|
||||||
'diff': n_object.get('diff', ''), # Null default in the case we use a test
|
|
||||||
'diff_added': n_object.get('diff_added', ''), # Null default in the case we use a test
|
|
||||||
'diff_full': n_object.get('diff_full', ''), # Null default in the case we use a test
|
|
||||||
'diff_patch': n_object.get('diff_patch', ''), # Null default in the case we use a test
|
|
||||||
'diff_removed': n_object.get('diff_removed', ''), # Null default in the case we use a test
|
|
||||||
'diff_url': diff_url,
|
'diff_url': diff_url,
|
||||||
'preview_url': preview_url,
|
'preview_url': preview_url,
|
||||||
'triggered_text': n_object.get('triggered_text', ''),
|
|
||||||
'watch_tag': watch_tag if watch_tag is not None else '',
|
'watch_tag': watch_tag if watch_tag is not None else '',
|
||||||
'watch_title': watch_title if watch_title is not None else '',
|
'watch_title': watch_title if watch_title is not None else '',
|
||||||
'watch_url': watch_url,
|
'watch_url': watch_url,
|
||||||
'watch_uuid': uuid,
|
'watch_uuid': uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# n_object will contain diff, diff_added etc etc
|
||||||
|
tokens.update(n_object)
|
||||||
|
|
||||||
|
if uuid:
|
||||||
|
tokens.update(datastore.data['watching'].get(uuid).extra_notification_token_values())
|
||||||
|
|
||||||
return tokens
|
return tokens
|
||||||
|
|
|
@ -68,3 +68,16 @@ class Watch(BaseWatch):
|
||||||
super().clear_watch()
|
super().clear_watch()
|
||||||
self.update({'restock': Restock()})
|
self.update({'restock': Restock()})
|
||||||
|
|
||||||
|
def extra_notification_token_values(self):
|
||||||
|
values = super().extra_notification_token_values()
|
||||||
|
values['restock'] = self.get('restock', {})
|
||||||
|
return values
|
||||||
|
|
||||||
|
def extra_notification_token_placeholder_info(self):
|
||||||
|
values = super().extra_notification_token_placeholder_info()
|
||||||
|
|
||||||
|
values.append(('restock.price', "Price detected"))
|
||||||
|
values.append(('restock.original_price', "Original price at first check"))
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
|
@ -631,6 +631,33 @@ class ChangeDetectionStore:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_unique_notification_tokens_available(self):
|
||||||
|
# Ask each type of watch if they have any extra notification token to add to the validation
|
||||||
|
extra_notification_tokens = {}
|
||||||
|
watch_processors_checked = set()
|
||||||
|
|
||||||
|
for watch_uuid, watch in self.__data['watching'].items():
|
||||||
|
processor = watch.get('processor')
|
||||||
|
if processor not in watch_processors_checked:
|
||||||
|
extra_notification_tokens.update(watch.extra_notification_token_values())
|
||||||
|
watch_processors_checked.add(processor)
|
||||||
|
|
||||||
|
return extra_notification_tokens
|
||||||
|
|
||||||
|
def get_unique_notification_token_placeholders_available(self):
|
||||||
|
# The actual description of the tokens, could be combined with get_unique_notification_tokens_available instead of doing this twice
|
||||||
|
extra_notification_tokens = []
|
||||||
|
watch_processors_checked = set()
|
||||||
|
|
||||||
|
for watch_uuid, watch in self.__data['watching'].items():
|
||||||
|
processor = watch.get('processor')
|
||||||
|
if processor not in watch_processors_checked:
|
||||||
|
extra_notification_tokens+=watch.extra_notification_token_placeholder_info()
|
||||||
|
watch_processors_checked.add(processor)
|
||||||
|
|
||||||
|
return extra_notification_tokens
|
||||||
|
|
||||||
|
|
||||||
def get_updates_available(self):
|
def get_updates_available(self):
|
||||||
import inspect
|
import inspect
|
||||||
updates_available = []
|
updates_available = []
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
{% from '_helpers.html' import render_field %}
|
{% from '_helpers.html' import render_field %}
|
||||||
|
|
||||||
{% macro render_common_settings_form(form, emailprefix, settings_application) %}
|
{% macro render_common_settings_form(form, emailprefix, settings_application, extra_notification_token_placeholder_info) %}
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
{{ render_field(form.notification_urls, rows=5, placeholder="Examples:
|
{{ render_field(form.notification_urls, rows=5, placeholder="Examples:
|
||||||
Gitter - gitter://token/room
|
Gitter - gitter://token/room
|
||||||
|
@ -107,7 +107,15 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{{ '{{triggered_text}}' }}</code></td>
|
<td><code>{{ '{{triggered_text}}' }}</code></td>
|
||||||
<td>Text that tripped the trigger from filters</td>
|
<td>Text that tripped the trigger from filters</td>
|
||||||
</tr>
|
|
||||||
|
{% if extra_notification_token_placeholder_info %}
|
||||||
|
{% for token in extra_notification_token_placeholder_info %}
|
||||||
|
<tr>
|
||||||
|
<td><code>{{ '{{' }}{{ token[0] }}{{ '}}' }}</code></td>
|
||||||
|
<td>{{ token[1] }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="pure-form-message-inline">
|
<div class="pure-form-message-inline">
|
||||||
|
|
|
@ -246,7 +246,7 @@ User-Agent: wonderbra 1.0") }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="#notifications" id="notification-setting-reset-to-default" class="pure-button button-xsmall" style="right: 20px; top: 20px; position: absolute; background-color: #5f42dd; border-radius: 4px; font-size: 70%; color: #fff">Use system defaults</a>
|
<a href="#notifications" id="notification-setting-reset-to-default" class="pure-button button-xsmall" style="right: 20px; top: 20px; position: absolute; background-color: #5f42dd; border-radius: 4px; font-size: 70%; color: #fff">Use system defaults</a>
|
||||||
|
|
||||||
{{ render_common_settings_form(form, emailprefix, settings_application) }}
|
{{ render_common_settings_form(form, emailprefix, settings_application, extra_notification_token_placeholder_info) }}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
<div class="tab-pane-inner" id="notifications">
|
<div class="tab-pane-inner" id="notifications">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
{{ render_common_settings_form(form.application.form, emailprefix, settings_application) }}
|
{{ render_common_settings_form(form.application.form, emailprefix, settings_application, extra_notification_token_placeholder_info) }}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="pure-control-group" id="notification-base-url">
|
<div class="pure-control-group" id="notification-base-url">
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
from .util import live_server_setup, wait_for_all_checks, extract_UUID_from_client
|
from .util import live_server_setup, wait_for_all_checks, extract_UUID_from_client
|
||||||
|
from ..notification import default_notification_format
|
||||||
|
|
||||||
instock_props = [
|
instock_props = [
|
||||||
# LD+JSON with non-standard list of 'type' https://github.com/dgtlmoon/changedetection.io/issues/1833
|
# LD+JSON with non-standard list of 'type' https://github.com/dgtlmoon/changedetection.io/issues/1833
|
||||||
|
@ -305,6 +307,70 @@ def test_itemprop_percent_threshold(client, live_server):
|
||||||
res = client.get(url_for("form_delete", uuid="all"), follow_redirects=True)
|
res = client.get(url_for("form_delete", uuid="all"), follow_redirects=True)
|
||||||
assert b'Deleted' in res.data
|
assert b'Deleted' in res.data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_with_notification_values(client, live_server):
|
||||||
|
#live_server_setup(live_server)
|
||||||
|
|
||||||
|
if os.path.isfile("test-datastore/notification.txt"):
|
||||||
|
os.unlink("test-datastore/notification.txt")
|
||||||
|
|
||||||
|
test_url = url_for('test_endpoint', _external=True)
|
||||||
|
set_original_response(props_markup=instock_props[0], price='960.45')
|
||||||
|
|
||||||
|
notification_url = url_for('test_notification_endpoint', _external=True).replace('http', 'json')
|
||||||
|
|
||||||
|
######################
|
||||||
|
# You must add a type of 'restock_diff' for its tokens to register as valid in the global settings
|
||||||
|
client.post(
|
||||||
|
url_for("form_quick_watch_add"),
|
||||||
|
data={"url": test_url, "tags": 'restock tests', 'processor': 'restock_diff'},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# A change in price, should trigger a change by default
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
|
||||||
|
# Should see new tokens register
|
||||||
|
res = client.get(url_for("settings_page"))
|
||||||
|
assert b'{{restock.original_price}}' in res.data
|
||||||
|
assert b'Original price at first check' in res.data
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# Set this up for when we remove the notification from the watch, it should fallback with these details
|
||||||
|
res = client.post(
|
||||||
|
url_for("settings_page"),
|
||||||
|
data={"application-notification_urls": notification_url,
|
||||||
|
"application-notification_title": "title new price {{restock.price}}",
|
||||||
|
"application-notification_body": "new price {{restock.price}}",
|
||||||
|
"application-notification_format": default_notification_format,
|
||||||
|
"requests-time_between_check-minutes": 180,
|
||||||
|
'application-fetch_backend': "html_requests"},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# check tag accepts without error
|
||||||
|
|
||||||
|
# Check the watches in these modes add the tokens for validating
|
||||||
|
assert b"A variable or function is not defined" not in res.data
|
||||||
|
|
||||||
|
assert b"Settings updated." in res.data
|
||||||
|
|
||||||
|
|
||||||
|
set_original_response(props_markup=instock_props[0], price='960.45')
|
||||||
|
# A change in price, should trigger a change by default
|
||||||
|
set_original_response(props_markup=instock_props[0], price='1950.45')
|
||||||
|
client.get(url_for("form_watch_checknow"))
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
time.sleep(3)
|
||||||
|
assert os.path.isfile("test-datastore/notification.txt"), "Notification received"
|
||||||
|
with open("test-datastore/notification.txt", 'r') as f:
|
||||||
|
notification = f.read()
|
||||||
|
assert "new price 1950.45" in notification
|
||||||
|
assert "title new price 1950.45" in notification
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_data_sanity(client, live_server):
|
def test_data_sanity(client, live_server):
|
||||||
#live_server_setup(live_server)
|
#live_server_setup(live_server)
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,9 @@ class update_worker(threading.Thread):
|
||||||
'uuid': watch.get('uuid') if watch else None,
|
'uuid': watch.get('uuid') if watch else None,
|
||||||
'watch_url': watch.get('url') if watch else None,
|
'watch_url': watch.get('url') if watch else None,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
n_object.update(watch.extra_notification_token_values())
|
||||||
|
|
||||||
logger.trace(f"Main rendered notification placeholders (diff_added etc) calculated in {time.time()-now:.3f}s")
|
logger.trace(f"Main rendered notification placeholders (diff_added etc) calculated in {time.time()-now:.3f}s")
|
||||||
logger.debug("Queued notification for sending")
|
logger.debug("Queued notification for sending")
|
||||||
notification_q.put(n_object)
|
notification_q.put(n_object)
|
||||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 117 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 77 KiB |
Ładowanie…
Reference in New Issue