mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2025-12-20 23:16:44 +00:00
Compare commits
19 Commits
export-dat
...
darkmode
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6f8b44c27 | ||
|
|
49bed43cff | ||
|
|
4218f25cb8 | ||
|
|
dfdd69de23 | ||
|
|
f612df1b1b | ||
|
|
c82bf98f13 | ||
|
|
855605c434 | ||
|
|
3b2b1ea2f7 | ||
|
|
5219698b07 | ||
|
|
00be226210 | ||
|
|
3078ddb261 | ||
|
|
cdf691d5c4 | ||
|
|
50e50f1caf | ||
|
|
f96340c45e | ||
|
|
0696dfa30d | ||
|
|
b65f08eadb | ||
|
|
e3a1a4275a | ||
|
|
b2da26b2f1 | ||
|
|
427cf105a3 |
@@ -810,12 +810,10 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
|
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
@app.route("/diff/<string:uuid>", methods=['GET', 'POST'])
|
@app.route("/diff/<string:uuid>", methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def diff_history_page(uuid):
|
def diff_history_page(uuid):
|
||||||
|
|
||||||
from changedetectionio import forms
|
|
||||||
|
|
||||||
# More for testing, possible to return the first/only
|
# More for testing, possible to return the first/only
|
||||||
if uuid == 'first':
|
if uuid == 'first':
|
||||||
uuid = list(datastore.data['watching'].keys()).pop()
|
uuid = list(datastore.data['watching'].keys()).pop()
|
||||||
@@ -827,28 +825,6 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
flash("No history found for the specified link, bad link?", "error")
|
flash("No history found for the specified link, bad link?", "error")
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
# For submission of requesting an extract
|
|
||||||
extract_form = forms.extractDataForm(request.form)
|
|
||||||
if request.method == 'POST':
|
|
||||||
if not extract_form.validate():
|
|
||||||
flash("An error occurred, please see below.", "error")
|
|
||||||
|
|
||||||
else:
|
|
||||||
extract_regex = request.form.get('extract_regex').strip()
|
|
||||||
output = watch.extract_regex_from_all_history(extract_regex)
|
|
||||||
if output:
|
|
||||||
watch_dir = os.path.join(datastore_o.datastore_path, uuid)
|
|
||||||
response = make_response(send_from_directory(directory=watch_dir, path=output, as_attachment=True))
|
|
||||||
response.headers['Content-type'] = 'text/csv'
|
|
||||||
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
|
||||||
response.headers['Pragma'] = 'no-cache'
|
|
||||||
response.headers['Expires'] = 0
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
flash('Nothing matches that RegEx', 'error')
|
|
||||||
redirect(url_for('diff_history_page', uuid=uuid)+'#extract')
|
|
||||||
|
|
||||||
history = watch.history
|
history = watch.history
|
||||||
dates = list(history.keys())
|
dates = list(history.keys())
|
||||||
|
|
||||||
@@ -891,24 +867,23 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
watch.get('fetch_backend', None) is None and system_uses_webdriver) else False
|
watch.get('fetch_backend', None) is None and system_uses_webdriver) else False
|
||||||
|
|
||||||
output = render_template("diff.html",
|
output = render_template("diff.html",
|
||||||
current_diff_url=watch['url'],
|
watch_a=watch,
|
||||||
current_previous_version=str(previous_version),
|
newest=newest_version_file_contents,
|
||||||
dark_mode=getDarkModeSetting(),
|
previous=previous_version_file_contents,
|
||||||
extra_stylesheets=extra_stylesheets,
|
extra_stylesheets=extra_stylesheets,
|
||||||
|
dark_mode=getDarkModeSetting(),
|
||||||
|
versions=dates[:-1], # All except current/last
|
||||||
|
uuid=uuid,
|
||||||
|
newest_version_timestamp=dates[-1],
|
||||||
|
current_previous_version=str(previous_version),
|
||||||
|
current_diff_url=watch['url'],
|
||||||
extra_title=" - Diff - {}".format(watch['title'] if watch['title'] else watch['url']),
|
extra_title=" - Diff - {}".format(watch['title'] if watch['title'] else watch['url']),
|
||||||
extract_form=extract_form,
|
left_sticky=True,
|
||||||
|
screenshot=screenshot_url,
|
||||||
is_html_webdriver=is_html_webdriver,
|
is_html_webdriver=is_html_webdriver,
|
||||||
last_error=watch['last_error'],
|
last_error=watch['last_error'],
|
||||||
last_error_screenshot=watch.get_error_snapshot(),
|
|
||||||
last_error_text=watch.get_error_text(),
|
last_error_text=watch.get_error_text(),
|
||||||
left_sticky=True,
|
last_error_screenshot=watch.get_error_snapshot()
|
||||||
newest=newest_version_file_contents,
|
|
||||||
newest_version_timestamp=dates[-1],
|
|
||||||
previous=previous_version_file_contents,
|
|
||||||
screenshot=screenshot_url,
|
|
||||||
uuid=uuid,
|
|
||||||
versions=dates[:-1], # All except current/last
|
|
||||||
watch_a=watch
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|||||||
@@ -448,8 +448,3 @@ class globalSettingsForm(Form):
|
|||||||
requests = FormField(globalSettingsRequestForm)
|
requests = FormField(globalSettingsRequestForm)
|
||||||
application = FormField(globalSettingsApplicationForm)
|
application = FormField(globalSettingsApplicationForm)
|
||||||
save_button = SubmitField('Save', render_kw={"class": "pure-button pure-button-primary"})
|
save_button = SubmitField('Save', render_kw={"class": "pure-button pure-button-primary"})
|
||||||
|
|
||||||
|
|
||||||
class extractDataForm(Form):
|
|
||||||
extract_regex = StringField('RegEx to extract', validators=[validators.Length(min=1, message="Needs a RegEx")])
|
|
||||||
extract_submit_button = SubmitField('Extract as CSV', render_kw={"class": "pure-button pure-button-primary"})
|
|
||||||
|
|||||||
@@ -318,47 +318,3 @@ class model(dict):
|
|||||||
if os.path.isfile(fname):
|
if os.path.isfile(fname):
|
||||||
return fname
|
return fname
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def extract_regex_from_all_history(self, regex):
|
|
||||||
import csv
|
|
||||||
import re
|
|
||||||
import datetime
|
|
||||||
csv_output_filename = False
|
|
||||||
csv_writer = False
|
|
||||||
f = None
|
|
||||||
|
|
||||||
# self.history will be keyed with the full path
|
|
||||||
for k, fname in self.history.items():
|
|
||||||
if os.path.isfile(fname):
|
|
||||||
with open(fname, "r") as f:
|
|
||||||
contents = f.read()
|
|
||||||
res = re.findall(regex, contents, re.MULTILINE)
|
|
||||||
if res:
|
|
||||||
if not csv_writer:
|
|
||||||
# A file on the disk can be transferred much faster via flask than a string reply
|
|
||||||
csv_output_filename = 'report.csv'
|
|
||||||
f = open(os.path.join(self.watch_data_dir, csv_output_filename), 'w')
|
|
||||||
# @todo some headers in the future
|
|
||||||
#fieldnames = ['Epoch seconds', 'Date']
|
|
||||||
csv_writer = csv.writer(f,
|
|
||||||
delimiter=',',
|
|
||||||
quotechar='"',
|
|
||||||
quoting=csv.QUOTE_MINIMAL,
|
|
||||||
#fieldnames=fieldnames
|
|
||||||
)
|
|
||||||
csv_writer.writerow(['Epoch seconds', 'Date'])
|
|
||||||
# csv_writer.writeheader()
|
|
||||||
|
|
||||||
date_str = datetime.datetime.fromtimestamp(int(k)).strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
for r in res:
|
|
||||||
row = [k, date_str]
|
|
||||||
if isinstance(r, str):
|
|
||||||
row.append(r)
|
|
||||||
else:
|
|
||||||
row+=r
|
|
||||||
csv_writer.writerow(row)
|
|
||||||
|
|
||||||
if f:
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
return csv_output_filename
|
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ $(document).ready(function () {
|
|||||||
} else if (hash_name === '#error-screenshot') {
|
} else if (hash_name === '#error-screenshot') {
|
||||||
$("img#error-screenshot-img").attr('src', error_screenshot_url);
|
$("img#error-screenshot-img").attr('src', error_screenshot_url);
|
||||||
$("#settings").hide();
|
$("#settings").hide();
|
||||||
} else if (hash_name === '#extract') {
|
|
||||||
$("#settings").hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setCookieValue = (value) => {
|
const setCookieValue = (value) => {
|
||||||
document.cookie = `css_dark_mode=${value};max-age=31536000;path=/`
|
document.cookie = `css_dark_mode=${value};max-age=31536000`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
--color-grey-850: #eee;
|
--color-grey-850: #eee;
|
||||||
--color-grey-900: #f2f2f2;
|
--color-grey-900: #f2f2f2;
|
||||||
--color-black: #000;
|
--color-black: #000;
|
||||||
--color-dark-red: #a00;
|
|
||||||
--color-light-red: #dd0000;
|
|
||||||
--color-background-page: var(--color-grey-100);
|
--color-background-page: var(--color-grey-100);
|
||||||
--color-background-gradient-first: #5ad8f7;
|
--color-background-gradient-first: #5ad8f7;
|
||||||
--color-background-gradient-second: #2f50af;
|
--color-background-gradient-second: #2f50af;
|
||||||
@@ -29,9 +27,9 @@
|
|||||||
--color-link: #1b98f8;
|
--color-link: #1b98f8;
|
||||||
--color-menu-accent: #ed5900;
|
--color-menu-accent: #ed5900;
|
||||||
--color-background-code: var(--color-grey-850);
|
--color-background-code: var(--color-grey-850);
|
||||||
--color-error: var(--color-dark-red);
|
--color-error: #a00;
|
||||||
--color-error-input: #ffebeb;
|
--color-error-input: #ffebeb;
|
||||||
--color-error-list: var(--color-light-red);
|
--color-error-list: #dd0000;
|
||||||
--color-table-background: var(--color-background);
|
--color-table-background: var(--color-background);
|
||||||
--color-table-stripe: var(--color-grey-900);
|
--color-table-stripe: var(--color-grey-900);
|
||||||
--color-text-tab: var(--color-white);
|
--color-text-tab: var(--color-white);
|
||||||
@@ -86,9 +84,7 @@
|
|||||||
--color-text-menu-link-hover: var(--color-grey-300);
|
--color-text-menu-link-hover: var(--color-grey-300);
|
||||||
--color-shadow-jump: var(--color-grey-500);
|
--color-shadow-jump: var(--color-grey-500);
|
||||||
--color-icon-github: var(--color-black);
|
--color-icon-github: var(--color-black);
|
||||||
--color-icon-github-hover: var(--color-grey-300);
|
--color-icon-github-hover: var(--color-grey-300); }
|
||||||
--color-watch-table-error: var(--color-dark-red);
|
|
||||||
--color-watch-table-row-text: var(--color-grey-100); }
|
|
||||||
|
|
||||||
html[data-darkmode="true"] {
|
html[data-darkmode="true"] {
|
||||||
--color-link: #59bdfb;
|
--color-link: #59bdfb;
|
||||||
@@ -118,13 +114,9 @@ html[data-darkmode="true"] {
|
|||||||
--color-background-snapshot-age: var(--color-grey-200);
|
--color-background-snapshot-age: var(--color-grey-200);
|
||||||
--color-shadow-jump: var(--color-grey-200);
|
--color-shadow-jump: var(--color-grey-200);
|
||||||
--color-icon-github: var(--color-white);
|
--color-icon-github: var(--color-white);
|
||||||
--color-icon-github-hover: var(--color-grey-700);
|
--color-icon-github-hover: var(--color-grey-700); }
|
||||||
--color-watch-table-error: var(--color-light-red);
|
|
||||||
--color-watch-table-row-text: var(--color-grey-800); }
|
|
||||||
html[data-darkmode="true"] .watch-controls img {
|
html[data-darkmode="true"] .watch-controls img {
|
||||||
opacity: 0.4; }
|
opacity: 0.4; }
|
||||||
html[data-darkmode="true"] .watch-table .unviewed {
|
|
||||||
color: #fff; }
|
|
||||||
html[data-darkmode="true"] .icon-spread {
|
html[data-darkmode="true"] .icon-spread {
|
||||||
filter: hue-rotate(-10deg) brightness(1.5); }
|
filter: hue-rotate(-10deg) brightness(1.5); }
|
||||||
html[data-darkmode="true"] .watch-table .title-col a[target="_blank"]::after,
|
html[data-darkmode="true"] .watch-table .title-col a[target="_blank"]::after,
|
||||||
@@ -136,9 +128,8 @@ html[data-darkmode="true"] {
|
|||||||
padding: 2em;
|
padding: 2em;
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
border-radius: 5px; }
|
border-radius: 5px;
|
||||||
#diff-ui #text {
|
font-size: 11px; }
|
||||||
font-size: 11px; }
|
|
||||||
#diff-ui table {
|
#diff-ui table {
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
width: 100%; }
|
width: 100%; }
|
||||||
|
|||||||
@@ -7,11 +7,7 @@
|
|||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
font-size: 11px;
|
||||||
// The first tab 'text' diff
|
|
||||||
#text {
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
table {
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
--color-grey-850: #eee;
|
--color-grey-850: #eee;
|
||||||
--color-grey-900: #f2f2f2;
|
--color-grey-900: #f2f2f2;
|
||||||
--color-black: #000;
|
--color-black: #000;
|
||||||
--color-dark-red: #a00;
|
|
||||||
--color-light-red: #dd0000;
|
|
||||||
|
|
||||||
--color-background-page: var(--color-grey-100);
|
--color-background-page: var(--color-grey-100);
|
||||||
--color-background-gradient-first: #5ad8f7;
|
--color-background-gradient-first: #5ad8f7;
|
||||||
@@ -31,9 +29,9 @@
|
|||||||
--color-link: #1b98f8;
|
--color-link: #1b98f8;
|
||||||
--color-menu-accent: #ed5900;
|
--color-menu-accent: #ed5900;
|
||||||
--color-background-code: var(--color-grey-850);
|
--color-background-code: var(--color-grey-850);
|
||||||
--color-error: var(--color-dark-red);
|
--color-error: #a00;
|
||||||
--color-error-input: #ffebeb;
|
--color-error-input: #ffebeb;
|
||||||
--color-error-list: var(--color-light-red);
|
--color-error-list: #dd0000;
|
||||||
--color-table-background: var(--color-background);
|
--color-table-background: var(--color-background);
|
||||||
--color-table-stripe: var(--color-grey-900);
|
--color-table-stripe: var(--color-grey-900);
|
||||||
--color-text-tab: var(--color-white);
|
--color-text-tab: var(--color-white);
|
||||||
@@ -98,9 +96,6 @@
|
|||||||
--color-shadow-jump: var(--color-grey-500);
|
--color-shadow-jump: var(--color-grey-500);
|
||||||
--color-icon-github: var(--color-black);
|
--color-icon-github: var(--color-black);
|
||||||
--color-icon-github-hover: var(--color-grey-300);
|
--color-icon-github-hover: var(--color-grey-300);
|
||||||
|
|
||||||
--color-watch-table-error: var(--color-dark-red);
|
|
||||||
--color-watch-table-row-text: var(--color-grey-100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-darkmode="true"] {
|
html[data-darkmode="true"] {
|
||||||
@@ -128,6 +123,7 @@ html[data-darkmode="true"] {
|
|||||||
--color-text-input-description: var(--color-grey-600);
|
--color-text-input-description: var(--color-grey-600);
|
||||||
--color-text-input-placeholder: var(--color-grey-600);
|
--color-text-input-placeholder: var(--color-grey-600);
|
||||||
--color-text-watch-tag-list: #fa3e92;
|
--color-text-watch-tag-list: #fa3e92;
|
||||||
|
|
||||||
--color-background-code: var(--color-grey-200);
|
--color-background-code: var(--color-grey-200);
|
||||||
|
|
||||||
--color-background-tab: rgba(0, 0, 0, 0.2);
|
--color-background-tab: rgba(0, 0, 0, 0.2);
|
||||||
@@ -137,8 +133,6 @@ html[data-darkmode="true"] {
|
|||||||
--color-shadow-jump: var(--color-grey-200);
|
--color-shadow-jump: var(--color-grey-200);
|
||||||
--color-icon-github: var(--color-white);
|
--color-icon-github: var(--color-white);
|
||||||
--color-icon-github-hover: var(--color-grey-700);
|
--color-icon-github-hover: var(--color-grey-700);
|
||||||
--color-watch-table-error: var(--color-light-red);
|
|
||||||
--color-watch-table-row-text: var(--color-grey-800);
|
|
||||||
|
|
||||||
// Anything that can't be manipulated through variables follows.
|
// Anything that can't be manipulated through variables follows.
|
||||||
.watch-controls {
|
.watch-controls {
|
||||||
@@ -146,9 +140,7 @@ html[data-darkmode="true"] {
|
|||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.watch-table .unviewed {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.icon-spread {
|
.icon-spread {
|
||||||
filter: hue-rotate(-10deg) brightness(1.5);
|
filter: hue-rotate(-10deg) brightness(1.5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,16 +121,12 @@ code {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
|
|
||||||
tr {
|
tr.unviewed {
|
||||||
&.unviewed {
|
font-weight: bold;
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
color: var(--color-watch-table-row-text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: var(--color-watch-table-error);
|
color: var(--color-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
@@ -326,12 +322,6 @@ a.pure-button-selected {
|
|||||||
padding: 0.5rem 0 1rem 0;
|
padding: 0.5rem 0 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#notification-customisation {
|
#notification-customisation {
|
||||||
border: 1px solid var(--color-border-notification);
|
border: 1px solid var(--color-border-notification);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
|||||||
@@ -21,8 +21,6 @@
|
|||||||
--color-grey-850: #eee;
|
--color-grey-850: #eee;
|
||||||
--color-grey-900: #f2f2f2;
|
--color-grey-900: #f2f2f2;
|
||||||
--color-black: #000;
|
--color-black: #000;
|
||||||
--color-dark-red: #a00;
|
|
||||||
--color-light-red: #dd0000;
|
|
||||||
--color-background-page: var(--color-grey-100);
|
--color-background-page: var(--color-grey-100);
|
||||||
--color-background-gradient-first: #5ad8f7;
|
--color-background-gradient-first: #5ad8f7;
|
||||||
--color-background-gradient-second: #2f50af;
|
--color-background-gradient-second: #2f50af;
|
||||||
@@ -32,9 +30,9 @@
|
|||||||
--color-link: #1b98f8;
|
--color-link: #1b98f8;
|
||||||
--color-menu-accent: #ed5900;
|
--color-menu-accent: #ed5900;
|
||||||
--color-background-code: var(--color-grey-850);
|
--color-background-code: var(--color-grey-850);
|
||||||
--color-error: var(--color-dark-red);
|
--color-error: #a00;
|
||||||
--color-error-input: #ffebeb;
|
--color-error-input: #ffebeb;
|
||||||
--color-error-list: var(--color-light-red);
|
--color-error-list: #dd0000;
|
||||||
--color-table-background: var(--color-background);
|
--color-table-background: var(--color-background);
|
||||||
--color-table-stripe: var(--color-grey-900);
|
--color-table-stripe: var(--color-grey-900);
|
||||||
--color-text-tab: var(--color-white);
|
--color-text-tab: var(--color-white);
|
||||||
@@ -89,9 +87,7 @@
|
|||||||
--color-text-menu-link-hover: var(--color-grey-300);
|
--color-text-menu-link-hover: var(--color-grey-300);
|
||||||
--color-shadow-jump: var(--color-grey-500);
|
--color-shadow-jump: var(--color-grey-500);
|
||||||
--color-icon-github: var(--color-black);
|
--color-icon-github: var(--color-black);
|
||||||
--color-icon-github-hover: var(--color-grey-300);
|
--color-icon-github-hover: var(--color-grey-300); }
|
||||||
--color-watch-table-error: var(--color-dark-red);
|
|
||||||
--color-watch-table-row-text: var(--color-grey-100); }
|
|
||||||
|
|
||||||
html[data-darkmode="true"] {
|
html[data-darkmode="true"] {
|
||||||
--color-link: #59bdfb;
|
--color-link: #59bdfb;
|
||||||
@@ -121,13 +117,9 @@ html[data-darkmode="true"] {
|
|||||||
--color-background-snapshot-age: var(--color-grey-200);
|
--color-background-snapshot-age: var(--color-grey-200);
|
||||||
--color-shadow-jump: var(--color-grey-200);
|
--color-shadow-jump: var(--color-grey-200);
|
||||||
--color-icon-github: var(--color-white);
|
--color-icon-github: var(--color-white);
|
||||||
--color-icon-github-hover: var(--color-grey-700);
|
--color-icon-github-hover: var(--color-grey-700); }
|
||||||
--color-watch-table-error: var(--color-light-red);
|
|
||||||
--color-watch-table-row-text: var(--color-grey-800); }
|
|
||||||
html[data-darkmode="true"] .watch-controls img {
|
html[data-darkmode="true"] .watch-controls img {
|
||||||
opacity: 0.4; }
|
opacity: 0.4; }
|
||||||
html[data-darkmode="true"] .watch-table .unviewed {
|
|
||||||
color: #fff; }
|
|
||||||
html[data-darkmode="true"] .icon-spread {
|
html[data-darkmode="true"] .icon-spread {
|
||||||
filter: hue-rotate(-10deg) brightness(1.5); }
|
filter: hue-rotate(-10deg) brightness(1.5); }
|
||||||
html[data-darkmode="true"] .watch-table .title-col a[target="_blank"]::after,
|
html[data-darkmode="true"] .watch-table .title-col a[target="_blank"]::after,
|
||||||
@@ -339,12 +331,10 @@ code {
|
|||||||
.watch-table {
|
.watch-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 80%; }
|
font-size: 80%; }
|
||||||
.watch-table tr {
|
.watch-table tr.unviewed {
|
||||||
color: var(--color-watch-table-row-text); }
|
font-weight: bold; }
|
||||||
.watch-table tr.unviewed {
|
|
||||||
font-weight: bold; }
|
|
||||||
.watch-table .error {
|
.watch-table .error {
|
||||||
color: var(--color-watch-table-error); }
|
color: var(--color-error); }
|
||||||
.watch-table td {
|
.watch-table td {
|
||||||
white-space: nowrap; }
|
white-space: nowrap; }
|
||||||
.watch-table td.title-col {
|
.watch-table td.title-col {
|
||||||
@@ -480,9 +470,6 @@ a.pure-button-selected {
|
|||||||
.notifications-wrapper {
|
.notifications-wrapper {
|
||||||
padding: 0.5rem 0 1rem 0; }
|
padding: 0.5rem 0 1rem 0; }
|
||||||
|
|
||||||
label:hover {
|
|
||||||
cursor: pointer; }
|
|
||||||
|
|
||||||
#notification-customisation {
|
#notification-customisation {
|
||||||
border: 1px solid var(--color-border-notification);
|
border: 1px solid var(--color-border-notification);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
{% if dark_mode %}
|
{% if dark_mode %}
|
||||||
{% set darkClass = 'dark' %}
|
{% set darkClass = 'dark' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="toggle-theme {{darkClass}}" type="button" title="Toggle Light/Dark Mode">
|
<button class="toggle-theme {{darkClass}}" type="button">
|
||||||
<span class="visually-hidden">Toggle light/dark mode</span>
|
<span class="visually-hidden">Toggle light/dark mode</span>
|
||||||
<span class="icon-light">
|
<span class="icon-light">
|
||||||
{% include "svgs/light-mode-toggle-icon.svg" %}
|
{% include "svgs/light-mode-toggle-icon.svg" %}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% from '_helpers.jinja' import render_field, render_checkbox_field, render_button %}
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<script>
|
<script>
|
||||||
const screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid)}}";
|
const screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid)}}";
|
||||||
@@ -58,7 +58,6 @@
|
|||||||
{% if last_error_screenshot %}<li class="tab" id="error-screenshot-tab"><a href="#error-screenshot">Error Screenshot</a></li> {% endif %}
|
{% if last_error_screenshot %}<li class="tab" id="error-screenshot-tab"><a href="#error-screenshot">Error Screenshot</a></li> {% endif %}
|
||||||
<li class="tab" id=""><a href="#text">Text</a></li>
|
<li class="tab" id=""><a href="#text">Text</a></li>
|
||||||
<li class="tab" id="screenshot-tab"><a href="#screenshot">Screenshot</a></li>
|
<li class="tab" id="screenshot-tab"><a href="#screenshot">Screenshot</a></li>
|
||||||
<li class="tab" id="extract-tab"><a href="#extract">Extract Data</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -109,37 +108,6 @@
|
|||||||
<strong>Screenshot requires Playwright/WebDriver enabled</strong>
|
<strong>Screenshot requires Playwright/WebDriver enabled</strong>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane-inner" id="extract">
|
|
||||||
<form id="extract-data-form" class="pure-form pure-form-stacked edit-form"
|
|
||||||
action="{{ url_for('diff_history_page', uuid=uuid) }}#extract"
|
|
||||||
method="POST">
|
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
|
||||||
|
|
||||||
<p>This tool will extract text data from all of the watch history.</p>
|
|
||||||
|
|
||||||
<div class="pure-control-group">
|
|
||||||
{{ render_field(extract_form.extract_regex) }}
|
|
||||||
<span class="pure-form-message-inline">
|
|
||||||
A <strong>RegEx</strong> is a pattern that identifies exactly which part inside of the text that you want to extract.<br/>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
For example, to extract only the numbers from text ‐</br>
|
|
||||||
<strong>Raw text</strong>: <code>Temperature <span style="color: red">5.5</span>°C in Sydney</code></br>
|
|
||||||
<strong>RegEx to extract:</strong> <code>Temperature ([0-9\.]+)</code><br/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://RegExr.com/">Be sure to test your RegEx here.</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Each RegEx group bracket <code>()</code> will be in its own column, the first column value is always the date.
|
|
||||||
</p>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="pure-control-group">
|
|
||||||
{{ render_button(extract_form.extract_submit_button) }}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,186 +1,290 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %} {% block content %} {% from '_helpers.jinja' import
|
||||||
|
render_field, render_checkbox_field, render_button %} {% from
|
||||||
{% block content %}
|
'_common_fields.jinja' import render_common_settings_form %}
|
||||||
{% from '_helpers.jinja' import render_field, render_checkbox_field, render_button %}
|
|
||||||
{% from '_common_fields.jinja' import render_common_settings_form %}
|
|
||||||
<script>
|
<script>
|
||||||
const notification_base_url="{{url_for('ajax_callback_send_notification_test')}}";
|
const notification_base_url="{{url_for('ajax_callback_send_notification_test')}}";
|
||||||
{% if emailprefix %}
|
{% if emailprefix %}
|
||||||
const email_notification_prefix=JSON.parse('{{emailprefix|tojson}}');
|
const email_notification_prefix=JSON.parse('{{emailprefix|tojson}}');
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
|
<script
|
||||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='notifications.js')}}" defer></script>
|
type="text/javascript"
|
||||||
|
src="{{url_for('static_content', group='js', filename='tabs.js')}}"
|
||||||
|
defer
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="{{url_for('static_content', group='js', filename='notifications.js')}}"
|
||||||
|
defer
|
||||||
|
></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='global-settings.js')}}" defer></script>
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="{{url_for('static_content', group='js', filename='global-settings.js')}}"
|
||||||
|
defer
|
||||||
|
></script>
|
||||||
<div class="edit-form">
|
<div class="edit-form">
|
||||||
<div class="tabs collapsable">
|
<div class="tabs collapsable">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="tab" id=""><a href="#general">General</a></li>
|
<li class="tab" id=""><a href="#general">General</a></li>
|
||||||
<li class="tab"><a href="#notifications">Notifications</a></li>
|
<li class="tab"><a href="#notifications">Notifications</a></li>
|
||||||
<li class="tab"><a href="#fetching">Fetching</a></li>
|
<li class="tab"><a href="#fetching">Fetching</a></li>
|
||||||
<li class="tab"><a href="#filters">Global Filters</a></li>
|
<li class="tab"><a href="#filters">Global Filters</a></li>
|
||||||
<li class="tab"><a href="#api">API</a></li>
|
<li class="tab"><a href="#api">API</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-wrap inner">
|
<div class="box-wrap inner">
|
||||||
<form class="pure-form pure-form-stacked settings" action="{{url_for('settings_page')}}" method="POST">
|
<form
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
class="pure-form pure-form-stacked settings"
|
||||||
<div class="tab-pane-inner" id="general">
|
action="{{url_for('settings_page')}}"
|
||||||
<fieldset>
|
method="POST"
|
||||||
<div class="pure-control-group">
|
>
|
||||||
{{ render_field(form.requests.form.time_between_check, class="time-check-widget") }}
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||||
<span class="pure-form-message-inline">Default time for all watches, when the watch does not have a specific time setting.</span>
|
<div class="tab-pane-inner" id="general">
|
||||||
</div>
|
<fieldset>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
{{ render_field(form.requests.form.jitter_seconds, class="jitter_seconds") }}
|
{{ render_field(form.requests.form.time_between_check,
|
||||||
<span class="pure-form-message-inline">Example - 3 seconds random jitter could trigger up to 3 seconds earlier or up to 3 seconds later</span>
|
class="time-check-widget") }}
|
||||||
</div>
|
<span class="pure-form-message-inline"
|
||||||
<div class="pure-control-group">
|
>Default time for all watches, when the watch does not have a
|
||||||
{{ render_field(form.application.form.filter_failure_notification_threshold_attempts, class="filter_failure_notification_threshold_attempts") }}
|
specific time setting.</span
|
||||||
<span class="pure-form-message-inline">After this many consecutive times that the CSS/xPath filter is missing, send a notification
|
>
|
||||||
<br/>
|
</div>
|
||||||
Set to <strong>0</strong> to disable
|
<div class="pure-control-group">
|
||||||
</span>
|
{{ render_field(form.requests.form.jitter_seconds,
|
||||||
</div>
|
class="jitter_seconds") }}
|
||||||
<div class="pure-control-group">
|
<span class="pure-form-message-inline"
|
||||||
{% if not hide_remove_pass %}
|
>Example - 3 seconds random jitter could trigger up to 3 seconds
|
||||||
{% if current_user.is_authenticated %}
|
earlier or up to 3 seconds later</span
|
||||||
{{ render_button(form.application.form.removepassword_button) }}
|
>
|
||||||
{% else %}
|
</div>
|
||||||
{{ render_field(form.application.form.password) }}
|
<div class="pure-control-group">
|
||||||
<span class="pure-form-message-inline">Password protection for your changedetection.io application.</span>
|
{{
|
||||||
{% endif %}
|
render_field(form.application.form.filter_failure_notification_threshold_attempts,
|
||||||
{% else %}
|
class="filter_failure_notification_threshold_attempts") }}
|
||||||
<span class="pure-form-message-inline">Password is locked.</span>
|
<span class="pure-form-message-inline"
|
||||||
{% endif %}
|
>After this many consecutive times that the CSS/xPath filter is
|
||||||
</div>
|
missing, send a notification
|
||||||
|
<br />
|
||||||
|
Set to <strong>0</strong> to disable
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
{% if not hide_remove_pass %} {% if current_user.is_authenticated %}
|
||||||
|
{{ render_button(form.application.form.removepassword_button) }} {%
|
||||||
|
else %} {{ render_field(form.application.form.password) }}
|
||||||
|
<span class="pure-form-message-inline"
|
||||||
|
>Password protection for your changedetection.io
|
||||||
|
application.</span
|
||||||
|
>
|
||||||
|
{% endif %} {% else %}
|
||||||
|
<span class="pure-form-message-inline">Password is locked.</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
{{ render_field(form.application.form.base_url, placeholder="http://yoursite.com:5000/",
|
{{ render_field(form.application.form.base_url,
|
||||||
class="m-d") }}
|
placeholder="http://yoursite.com:5000/", class="m-d") }}
|
||||||
<span class="pure-form-message-inline">
|
<span class="pure-form-message-inline">
|
||||||
Base URL used for the <code>{base_url}</code> token in notifications and RSS links.<br/>Default value is the ENV var 'BASE_URL' (Currently "{{settings_application['current_base_url']}}"),
|
Base URL used for the <code>{base_url}</code> token in
|
||||||
<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Configurable-BASE_URL-setting">read more here</a>.
|
notifications and RSS links.<br />Default value is the ENV var
|
||||||
</span>
|
'BASE_URL' (Currently
|
||||||
</div>
|
"{{settings_application['current_base_url']}}"),
|
||||||
|
<a
|
||||||
|
href="https://github.com/dgtlmoon/changedetection.io/wiki/Configurable-BASE_URL-setting"
|
||||||
|
>read more here</a
|
||||||
|
>.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
{{ render_checkbox_field(form.application.form.extract_title_as_title) }}
|
{{
|
||||||
<span class="pure-form-message-inline">Note: This will automatically apply to all existing watches.</span>
|
render_checkbox_field(form.application.form.extract_title_as_title)
|
||||||
</div>
|
}}
|
||||||
<div class="pure-control-group">
|
<span class="pure-form-message-inline"
|
||||||
{{ render_checkbox_field(form.application.form.empty_pages_are_a_change) }}
|
>Note: This will automatically apply to all existing
|
||||||
<span class="pure-form-message-inline">When a page contains HTML, but no renderable text appears (empty page), is this considered a change?</span>
|
watches.</span
|
||||||
</div>
|
>
|
||||||
{% if form.requests.proxy %}
|
</div>
|
||||||
<div class="pure-control-group inline-radio">
|
<div class="pure-control-group">
|
||||||
{{ render_field(form.requests.form.proxy, class="fetch-backend-proxy") }}
|
{{
|
||||||
<span class="pure-form-message-inline">
|
render_checkbox_field(form.application.form.empty_pages_are_a_change)
|
||||||
Choose a default proxy for all watches
|
}}
|
||||||
</span>
|
<span class="pure-form-message-inline"
|
||||||
</div>
|
>When a page contains HTML, but no renderable text appears (empty
|
||||||
{% endif %}
|
page), is this considered a change?</span
|
||||||
</fieldset>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
{% if form.requests.proxy %}
|
||||||
|
<div class="pure-control-group inline-radio">
|
||||||
|
{{ render_field(form.requests.form.proxy,
|
||||||
|
class="fetch-backend-proxy") }}
|
||||||
|
<span class="pure-form-message-inline">
|
||||||
|
Choose a default proxy for all watches
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<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,
|
||||||
</div>
|
settings_application) }}
|
||||||
</fieldset>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="fetching">
|
<div class="tab-pane-inner" id="fetching">
|
||||||
<div class="pure-control-group inline-radio">
|
<div class="pure-control-group inline-radio">
|
||||||
{{ render_field(form.application.form.fetch_backend, class="fetch-backend") }}
|
{{ render_field(form.application.form.fetch_backend,
|
||||||
<span class="pure-form-message-inline">
|
class="fetch-backend") }}
|
||||||
<p>Use the <strong>Basic</strong> method (default) where your watched sites don't need Javascript to render.</p>
|
<span class="pure-form-message-inline">
|
||||||
<p>The <strong>Chrome/Javascript</strong> method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'. </p>
|
<p>
|
||||||
</span>
|
Use the <strong>Basic</strong> method (default) where your watched
|
||||||
<br/>
|
sites don't need Javascript to render.
|
||||||
Tip: <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Proxy-configuration#brightdata-proxy-support">Connect using BrightData Proxies, find out more here.</a>
|
</p>
|
||||||
</div>
|
<p>
|
||||||
<fieldset class="pure-group" id="webdriver-override-options">
|
The <strong>Chrome/Javascript</strong> method requires a network
|
||||||
<div class="pure-form-message-inline">
|
connection to a running WebDriver+Chrome server, set by the ENV
|
||||||
<strong>If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here.</strong>
|
var 'WEBDRIVER_URL'.
|
||||||
<br/>
|
</p>
|
||||||
This will wait <i>n</i> seconds before extracting the text.
|
</span>
|
||||||
</div>
|
<br />
|
||||||
<div class="pure-control-group">
|
Tip:
|
||||||
{{ render_field(form.application.form.webdriver_delay) }}
|
<a
|
||||||
</div>
|
href="https://github.com/dgtlmoon/changedetection.io/wiki/Proxy-configuration#brightdata-proxy-support"
|
||||||
</fieldset>
|
>Connect using BrightData Proxies, find out more here.</a
|
||||||
</div>
|
>
|
||||||
|
</div>
|
||||||
|
<fieldset class="pure-group" id="webdriver-override-options">
|
||||||
|
<div class="pure-form-message-inline">
|
||||||
|
<strong
|
||||||
|
>If you're having trouble waiting for the page to be fully
|
||||||
|
rendered (text missing etc), try increasing the 'wait' time
|
||||||
|
here.</strong
|
||||||
|
>
|
||||||
|
<br />
|
||||||
|
This will wait <i>n</i> seconds before extracting the text.
|
||||||
|
</div>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
{{ render_field(form.application.form.webdriver_delay) }}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="filters">
|
<div class="tab-pane-inner" id="filters">
|
||||||
|
<fieldset class="pure-group">
|
||||||
<fieldset class="pure-group">
|
{{ render_checkbox_field(form.application.form.ignore_whitespace) }}
|
||||||
{{ render_checkbox_field(form.application.form.ignore_whitespace) }}
|
<span class="pure-form-message-inline"
|
||||||
<span class="pure-form-message-inline">Ignore whitespace, tabs and new-lines/line-feeds when considering if a change was detected.<br/>
|
>Ignore whitespace, tabs and new-lines/line-feeds when considering
|
||||||
<i>Note:</i> Changing this will change the status of your existing watches, possibly trigger alerts etc.
|
if a change was detected.<br />
|
||||||
</span>
|
<i>Note:</i> Changing this will change the status of your existing
|
||||||
</fieldset>
|
watches, possibly trigger alerts etc.
|
||||||
<fieldset class="pure-group">
|
</span>
|
||||||
{{ render_checkbox_field(form.application.form.render_anchor_tag_content) }}
|
</fieldset>
|
||||||
<span class="pure-form-message-inline">Render anchor tag content, default disabled, when enabled renders links as <code>(link text)[https://somesite.com]</code>
|
<fieldset class="pure-group">
|
||||||
<br/>
|
{{
|
||||||
<i>Note:</i> Changing this could affect the content of your existing watches, possibly trigger alerts etc.
|
render_checkbox_field(form.application.form.render_anchor_tag_content)
|
||||||
</span>
|
}}
|
||||||
</fieldset>
|
<span class="pure-form-message-inline"
|
||||||
<fieldset class="pure-group">
|
>Render anchor tag content, default disabled, when enabled renders
|
||||||
{{ render_field(form.application.form.global_subtractive_selectors, rows=5, placeholder="header
|
links as <code>(link text)[https://somesite.com]</code>
|
||||||
|
<br />
|
||||||
|
<i>Note:</i> Changing this could affect the content of your existing
|
||||||
|
watches, possibly trigger alerts etc.
|
||||||
|
</span>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="pure-group">
|
||||||
|
{{ render_field(form.application.form.global_subtractive_selectors,
|
||||||
|
rows=5, placeholder="header
|
||||||
footer
|
footer
|
||||||
nav
|
nav
|
||||||
.stockticker") }}
|
.stockticker") }}
|
||||||
<span class="pure-form-message-inline">
|
<span class="pure-form-message-inline">
|
||||||
<ul>
|
<ul>
|
||||||
<li> Remove HTML element(s) by CSS selector before text conversion. </li>
|
<li>
|
||||||
<li> Add multiple elements or CSS selectors per line to ignore multiple parts of the HTML. </li>
|
Remove HTML element(s) by CSS selector before text conversion.
|
||||||
</ul>
|
</li>
|
||||||
</span>
|
<li>
|
||||||
</fieldset>
|
Add multiple elements or CSS selectors per line to ignore
|
||||||
<fieldset class="pure-group">
|
multiple parts of the HTML.
|
||||||
{{ render_field(form.application.form.global_ignore_text, rows=5, placeholder="Some text to ignore in a line
|
</li>
|
||||||
/some.regex\d{2}/ for case-INsensitive regex
|
</ul>
|
||||||
") }}
|
</span>
|
||||||
<span class="pure-form-message-inline">Note: This is applied globally in addition to the per-watch rules.</span><br/>
|
</fieldset>
|
||||||
<span class="pure-form-message-inline">
|
<fieldset class="pure-group">
|
||||||
<ul>
|
{{ render_field(form.application.form.global_ignore_text, rows=5,
|
||||||
<li>Note: This is applied globally in addition to the per-watch rules.</li>
|
placeholder="Some text to ignore in a line /some.regex\d{2}/ for
|
||||||
<li>Each line processed separately, any line matching will be ignored (removed before creating the checksum)</li>
|
case-INsensitive regex ") }}
|
||||||
<li>Regular Expression support, wrap the entire line in forward slash <code>/regex/</code></li>
|
<span class="pure-form-message-inline"
|
||||||
<li>Changing this will affect the comparison checksum which may trigger an alert</li>
|
>Note: This is applied globally in addition to the per-watch
|
||||||
<li>Use the preview/show current tab to see ignores</li>
|
rules.</span
|
||||||
</ul>
|
><br />
|
||||||
</span>
|
<span class="pure-form-message-inline">
|
||||||
</fieldset>
|
<ul>
|
||||||
</div>
|
<li>
|
||||||
|
Note: This is applied globally in addition to the per-watch
|
||||||
|
rules.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Each line processed separately, any line matching will be
|
||||||
|
ignored (removed before creating the checksum)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Regular Expression support, wrap the entire line in forward
|
||||||
|
slash <code>/regex/</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Changing this will affect the comparison checksum which may
|
||||||
|
trigger an alert
|
||||||
|
</li>
|
||||||
|
<li>Use the preview/show current tab to see ignores</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="api">
|
<div class="tab-pane-inner" id="api">
|
||||||
|
<p>
|
||||||
|
Drive your changedetection.io via API, More about
|
||||||
|
<a
|
||||||
|
href="https://github.com/dgtlmoon/changedetection.io/wiki/API-Reference"
|
||||||
|
>API access here</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>Drive your changedetection.io via API, More about <a href="https://github.com/dgtlmoon/changedetection.io/wiki/API-Reference">API access here</a></p>
|
<div class="pure-control-group">
|
||||||
|
{{
|
||||||
|
render_checkbox_field(form.application.form.api_access_token_enabled)
|
||||||
|
}}
|
||||||
|
<div class="pure-form-message-inline">
|
||||||
|
Restrict API access limit by using <code>x-api-key</code> header
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="pure-form-message-inline">
|
||||||
|
<br />API Key <span id="api-key">{{api_key}}</span>
|
||||||
|
<span style="display: none" id="api-key-copy">copy</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div id="actions">
|
||||||
{{ render_checkbox_field(form.application.form.api_access_token_enabled) }}
|
<div class="pure-control-group">
|
||||||
<div class="pure-form-message-inline">Restrict API access limit by using <code>x-api-key</code> header</div><br/>
|
{{ render_button(form.save_button) }}
|
||||||
<div class="pure-form-message-inline"><br/>API Key <span id="api-key">{{api_key}}</span>
|
<a href="{{url_for('index')}}" class="pure-button button-cancel"
|
||||||
<span style="display:none;" id="api-key-copy" >copy</span>
|
>Back</a
|
||||||
</div>
|
>
|
||||||
</div>
|
<a
|
||||||
</div>
|
href="{{url_for('clear_all_history')}}"
|
||||||
|
class="pure-button button-cancel"
|
||||||
<div id="actions">
|
>Clear Snapshot History</a
|
||||||
<div class="pure-control-group">
|
>
|
||||||
{{ render_button(form.save_button) }}
|
</div>
|
||||||
<a href="{{url_for('index')}}" class="pure-button button-small button-cancel">Back</a>
|
</div>
|
||||||
<a href="{{url_for('clear_all_history')}}" class="pure-button button-small button-cancel">Clear Snapshot History</a>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import time
|
|
||||||
from flask import url_for
|
|
||||||
from urllib.request import urlopen
|
|
||||||
from .util import set_original_response, set_modified_response, live_server_setup, wait_for_all_checks
|
|
||||||
|
|
||||||
sleep_time_for_fetch_thread = 3
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_check_extract_text_from_diff(client, live_server):
|
|
||||||
import time
|
|
||||||
with open("test-datastore/endpoint-content.txt", "w") as f:
|
|
||||||
f.write("Now it's {} seconds since epoch, time flies!".format(str(time.time())))
|
|
||||||
|
|
||||||
live_server_setup(live_server)
|
|
||||||
|
|
||||||
# Add our URL to the import page
|
|
||||||
res = client.post(
|
|
||||||
url_for("import_page"),
|
|
||||||
data={"urls": url_for('test_endpoint', _external=True)},
|
|
||||||
follow_redirects=True
|
|
||||||
)
|
|
||||||
|
|
||||||
assert b"1 Imported" in res.data
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
# Load in 5 different numbers/changes
|
|
||||||
last_date=""
|
|
||||||
for n in range(5):
|
|
||||||
# Give the thread time to pick it up
|
|
||||||
print("Bumping snapshot and checking.. ", n)
|
|
||||||
last_date = str(time.time())
|
|
||||||
with open("test-datastore/endpoint-content.txt", "w") as f:
|
|
||||||
f.write("Now it's {} seconds since epoch, time flies!".format(last_date))
|
|
||||||
|
|
||||||
client.get(url_for("form_watch_checknow"), follow_redirects=True)
|
|
||||||
wait_for_all_checks(client)
|
|
||||||
|
|
||||||
res = client.post(
|
|
||||||
url_for("diff_history_page", uuid="first"),
|
|
||||||
data={"extract_regex": "Now it's ([0-9\.]+)",
|
|
||||||
"extract_submit_button": "Extract as CSV"},
|
|
||||||
follow_redirects=False
|
|
||||||
)
|
|
||||||
|
|
||||||
assert b'Nothing matches that RegEx' not in res.data
|
|
||||||
assert res.content_type == 'text/csv'
|
|
||||||
|
|
||||||
# Read the csv reply as stringio
|
|
||||||
from io import StringIO
|
|
||||||
import csv
|
|
||||||
|
|
||||||
f = StringIO(res.data.decode('utf-8'))
|
|
||||||
reader = csv.reader(f, delimiter=',')
|
|
||||||
output=[]
|
|
||||||
|
|
||||||
for row in reader:
|
|
||||||
output.append(row)
|
|
||||||
|
|
||||||
assert output[0][0] == 'Epoch seconds'
|
|
||||||
|
|
||||||
# Header line + 1 origin/first + 5 changes
|
|
||||||
assert(len(output) == 7)
|
|
||||||
|
|
||||||
# We expect to find the last bumped date in the changes in the last field of the spreadsheet
|
|
||||||
assert(output[6][2] == last_date)
|
|
||||||
# And nothing else, only that group () of the decimal and .
|
|
||||||
assert "time flies" not in output[6][2]
|
|
||||||
Reference in New Issue
Block a user