Skip to content

Commit

Permalink
Merge pull request #116 from marcus67/master
Browse files Browse the repository at this point in the history
Release 0.3.11
  • Loading branch information
marcus67 authored Feb 6, 2021
2 parents 76cab84 + cda3c7b commit b5db849
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 31 deletions.
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ jobs:
- store_artifacts:
path: htmlcov
- store_artifacts:
path: debian/little-brother_0.3.10_82.deb
path: debian/little-brother_0.3.11_82.deb
- persist_to_workspace:
root: debian
paths:
- little-brother_0.3.10_82.deb
- little-brother_0.3.11_82.deb
build_pypi:
#working_directory: ~
docker:
Expand All @@ -61,11 +61,11 @@ jobs:

- run: PYTHONPATH=contrib/python_base_app python3 ci_toolbox.py --execute-stage BUILD --use-dev-dir=.
- store_artifacts:
path: "dist/little-brother-0.3.10.tar.gz"
path: "dist/little-brother-0.3.11.tar.gz"
- persist_to_workspace:
root: dist
paths:
- "little-brother-0.3.10.tar.gz"
- "little-brother-0.3.11.tar.gz"
install_pypi:
#working_directory: ~
docker:
Expand Down
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ build_pypi:
artifacts:
when: always
paths:
- dist/little-brother-0.3.10.tar.gz
- dist/little-brother-0.3.11.tar.gz
variables:
# Suppress automatic checkout for all sub modules
GIT_SUBMODULE_STRATEGY: recursive
Expand Down
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

This document lists all changes of `LittleBrother` with the most recent changes at the top.

## Version 0.3.11 Revision 82 (February 6th, 2021)

* Closes #28, see [here](https://github.com/marcus67/little_brother/issues/28)
* Closes #113, see [here](https://github.com/marcus67/little_brother/issues/113)
* Closes #112, see [here](https://github.com/marcus67/little_brother/issues/112) (presumably)
* Closes #58, see [here](https://github.com/marcus67/little_brother/issues/58) (presumably)
* Closes #110, see [here](https://github.com/marcus67/little_brother/issues/110)
* Closes #86, see [here](https://github.com/marcus67/little_brother/issues/86)

## Version 0.3.10 Revision 81 (January 17th, 2021)

* Upgrade to python_base_app 0.2.9
Expand Down
28 changes: 14 additions & 14 deletions bin/generic-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,20 @@ if [ ! "$EUID" == "0" ] ; then
fi

echo "Checking if all Pip packages have been downloaded to $TMP_DIR..."
if [ ! -f $TMP_DIR/little-brother-0.3.10.tar.gz ] ; then
echo "ERROR: package little-brother-0.3.10.tar.gz not found in $TMP_DIR!"
if [ ! -f $TMP_DIR/little-brother-0.3.11.tar.gz ] ; then
echo "ERROR: package little-brother-0.3.11.tar.gz not found in $TMP_DIR!"
echo "Download from test.pypi.org and execute again."
exit 2
else
echo "Package little-brother-0.3.10.tar.gz was found."
echo "Package little-brother-0.3.11.tar.gz was found."
fi

if [ ! -f $TMP_DIR/python-base-app-0.2.9.tar.gz ] ; then
echo "ERROR: package python-base-app-0.2.9.tar.gz not found in $TMP_DIR!"
if [ ! -f $TMP_DIR/python-base-app-0.2.13.tar.gz ] ; then
echo "ERROR: package python-base-app-0.2.13.tar.gz not found in $TMP_DIR!"
echo "Download from test.pypi.org and execute again."
exit 2
else
echo "Package python-base-app-0.2.9.tar.gz was found."
echo "Package python-base-app-0.2.13.tar.gz was found."
fi

if [ ! -f $TMP_DIR/some-flask-helpers-0.1.tar.gz ] ; then
Expand Down Expand Up @@ -182,19 +182,19 @@ chmod og-rwx /etc/little-brother/little-brother.config
${PIP3} --version
${PIP3} install wheel setuptools
echo "Installing PIP packages..."
echo " * little-brother-0.3.10.tar.gz"
echo " * python-base-app-0.2.9.tar.gz"
echo " * little-brother-0.3.11.tar.gz"
echo " * python-base-app-0.2.13.tar.gz"
echo " * some-flask-helpers-0.1.tar.gz"
# see https://stackoverflow.com/questions/19548957/can-i-force-pip-to-reinstall-the-current-version
${PIP3} install --upgrade --force-reinstall \
${TMP_DIR}/little-brother-0.3.10.tar.gz\
${TMP_DIR}/python-base-app-0.2.9.tar.gz\
${TMP_DIR}/little-brother-0.3.11.tar.gz\
${TMP_DIR}/python-base-app-0.2.13.tar.gz\
${TMP_DIR}/some-flask-helpers-0.1.tar.gz


echo "Removing installation file ${TMP_DIR}/little-brother-0.3.10.tar.gz..."
rm ${TMP_DIR}/little-brother-0.3.10.tar.gz
echo "Removing installation file ${TMP_DIR}/python-base-app-0.2.9.tar.gz..."
rm ${TMP_DIR}/python-base-app-0.2.9.tar.gz
echo "Removing installation file ${TMP_DIR}/little-brother-0.3.11.tar.gz..."
rm ${TMP_DIR}/little-brother-0.3.11.tar.gz
echo "Removing installation file ${TMP_DIR}/python-base-app-0.2.13.tar.gz..."
rm ${TMP_DIR}/python-base-app-0.2.13.tar.gz
echo "Removing installation file ${TMP_DIR}/some-flask-helpers-0.1.tar.gz..."
rm ${TMP_DIR}/some-flask-helpers-0.1.tar.gz
5 changes: 5 additions & 0 deletions etc/master.config
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ scan_active=true
# Default: 7
#process_lookback_in_days = 14

# Sets the number of days that entries of entities 'process_info', 'admin_event' and 'rule_override' will be kept
# in the database. This number should always be at least one day larger than 'process_lookback_in_days'!
# Default: 180
#history_length_in_days = 30

# Sets the number of future days that user play time configuration will be available in the frontend.
# Default: 7
#admin_lookahead_in_days = 14
Expand Down
10 changes: 10 additions & 0 deletions little_brother/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
PACKAGE_NAME = 'little_brother'

DEFAULT_USER_HANDLER = unix_user_handler.HANDLER_NAME
DEFAULT_CLEAN_HISTORY_INTERVAL = 24 * 60 * 60 # seconds


class AppConfigModel(base_app.BaseAppConfigModel):
Expand All @@ -54,6 +55,7 @@ def __init__(self):
super(AppConfigModel, self).__init__(APP_NAME)

self.check_interval = base_app.DEFAULT_TASK_INTERVAL
self.clean_history_interval = DEFAULT_CLEAN_HISTORY_INTERVAL


def get_argument_parser(p_app_name):
Expand Down Expand Up @@ -303,6 +305,14 @@ def prepare_services(self, p_full_startup=True):
p_interval=self._client_device_handler.check_interval)
self.add_recurring_task(p_recurring_task=task)

if self.is_master():
task = base_app.RecurringTask(
p_name="app_control.clean_history",
p_handler_method=lambda: self._app_control.clean_history(),
p_interval=self._app_config.clean_history_interval)
self.add_recurring_task(p_recurring_task=task)


if status_server_config.is_active():
self._status_server = status_server.StatusServer(
p_config=self._config[status_server.SECTION_NAME],
Expand Down
38 changes: 30 additions & 8 deletions little_brother/app_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
DEFAULT_SCAN_ACTIVE = True
DEFAULT_ADMIN_LOOKAHEAD_IN_DAYS = 7 # days
DEFAULT_PROCESS_LOOKUP_IN_DAYS = 7 # days
DEFAULT_HISTORY_LENGTH_IN_DAYS = 180 # days
DEFAULT_MIN_ACTIVITY_DURATION = 60 # seconds
DEFAULT_CHECK_INTERVAL = 5 # seconds
DEFAULT_INDEX_REFRESH_INTERVAL = 60 # seconds
Expand Down Expand Up @@ -74,6 +75,7 @@ def __init__(self):
super(AppControlConfigModel, self).__init__(p_section_name=SECTION_NAME)

self.process_lookback_in_days = DEFAULT_PROCESS_LOOKUP_IN_DAYS
self.history_length_in_days = DEFAULT_HISTORY_LENGTH_IN_DAYS
self.admin_lookahead_in_days = DEFAULT_ADMIN_LOOKAHEAD_IN_DAYS
self.server_group = login_mapping.DEFAULT_SERVER_GROUP
self.hostname = configuration.NONE_STRING
Expand Down Expand Up @@ -102,12 +104,21 @@ def node_type(self):
return _("Master") if self.is_master else _("Slave")

@property
def last_message_string(self):
def seconds_without_ping(self):
if self.last_message is None:
return None

return (tools.get_current_time() - self.last_message).seconds

@property
def last_message_string(self):

some_seconds_without_ping = self.seconds_without_ping

if some_seconds_without_ping is None:
return _("n/a")

seconds_without_ping = (tools.get_current_time() - self.last_message).seconds
return tools.get_duration_as_string(seconds_without_ping)
return tools.get_duration_as_string(some_seconds_without_ping)


@property
Expand Down Expand Up @@ -465,6 +476,10 @@ def stop(self):
if not self.is_master():
self.send_events()

def clean_history(self):
self._persistence.delete_historic_entries(p_history_length_in_days=self._config.history_length_in_days)


def queue_event(self, p_event, p_to_master=False, p_is_action=False):

if p_is_action:
Expand Down Expand Up @@ -533,7 +548,14 @@ def handle_event_process_downtime(self, p_event):

def handle_event_process_start(self, p_event):

pinfo, updated = self.get_process_handler(p_id=p_event.processhandler).handle_event_process_start(p_event)
process_handler = self.get_process_handler(p_id=p_event.processhandler)

if process_handler is None:
msg = "Received event for process handler of type id '{id}' which is not registered -> discarding event"
self._logger.warning(msg.format(id=p_event.processhandler))
return

pinfo, updated = process_handler.handle_event_process_start(p_event)

if updated:
if self._persistence is not None:
Expand Down Expand Up @@ -877,14 +899,14 @@ def pick_text_for_approaching_logout(self, p_rule_result_info):
t = gettext.translation('messages', localedir=self._locale_dir,
languages=[p_rule_result_info.locale], fallback=True)

if p_rule_result_info.approaching_logout_rules & rule_handler.RULE_TIME_PER_DAY:
return t.gettext(self.text_no_time_left_approaching).format(**p_rule_result_info.args)
if p_rule_result_info.approaching_logout_rules & rule_handler.RULE_ACTIVITY_DURATION:
return t.gettext(self.text_need_break_approaching).format(**p_rule_result_info.args)

elif p_rule_result_info.approaching_logout_rules & rule_handler.RULE_TOO_LATE:
return t.gettext(self.text_too_late_approaching).format(**p_rule_result_info.args)

elif p_rule_result_info.approaching_logout_rules & rule_handler.RULE_ACTIVITY_DURATION:
return t.gettext(self.text_need_break_approaching).format(**p_rule_result_info.args)
elif p_rule_result_info.approaching_logout_rules & rule_handler.RULE_TIME_PER_DAY:
return t.gettext(self.text_no_time_left_approaching).format(**p_rule_result_info.args)

else:
fmt = "pick_text_for_approaching_logout(): cannot derive text for rule result %d" % p_rule_result_info.approaching_logout_rules
Expand Down
40 changes: 39 additions & 1 deletion little_brother/persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ def update_rule_override(self, p_rule_override):
def load_process_infos(self, p_lookback_in_days):

with SessionContext(self) as session_context:
session_context = SessionContext(self)
# session_context = SessionContext(self)
session = session_context.get_session()
reference_time = datetime.datetime.now() + datetime.timedelta(days=-p_lookback_in_days)

Expand All @@ -846,6 +846,44 @@ def load_process_infos(self, p_lookback_in_days):

return result

def delete_historic_entries(self, p_history_length_in_days):

msg = "Deleting historic entries older than {days} days..."
self._logger.info(msg.format(days=p_history_length_in_days))

with SessionContext(self) as session_context:
session = session_context.get_session()
reference_time = datetime.datetime.now() + datetime.timedelta(days=-p_history_length_in_days)
reference_date = reference_time.date()

result = session.query(RuleOverride).filter(RuleOverride.reference_date < reference_date).all()

msg = "Deleting {count} rule override entries..."
self._logger.info(msg.format(count=len(result)))

for override in result:
session.delete(override)

result = session.query(AdminEvent).filter(AdminEvent.event_time < reference_time).all()

msg = "Deleting {count} admin events..."
self._logger.info(msg.format(count=len(result)))

for event in result:
session.delete(event)

result = session.query(ProcessInfo).filter(ProcessInfo.start_time < reference_time).all()

msg = "Deleting {count} process infos..."
self._logger.info(msg.format(count=len(result)))

for pinfo in result:
session.delete(pinfo)

session.commit()



def load_rule_overrides(self, p_lookback_in_days):

session = self.get_session()
Expand Down
2 changes: 1 addition & 1 deletion little_brother/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
settings = {
"name": "little-brother",
"url": "https://github.com/marcus67/little_brother",
"version": "0.3.10",
"version": "0.3.11",
"description": "Simple parental control application monitoring specific processes on Linux hosts "
"to monitor and limit the play time of (young) children.",
"author": "Marcus Rickert",
Expand Down
18 changes: 18 additions & 0 deletions little_brother/status_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import datetime
import gettext
import os

Expand All @@ -23,6 +24,7 @@
import flask_babel
import flask_login
import flask_wtf
import humanize

import little_brother
from some_flask_helpers import blueprint_adapter
Expand Down Expand Up @@ -170,6 +172,7 @@ def __init__(self,
self._app.jinja_env.filters['format_babel_date'] = self.format_babel_date
self._app.jinja_env.filters['format_text_array'] = self.format_text_array
self._app.jinja_env.filters['invert'] = self.invert
self._app.jinja_env.filters['seconds_as_humanized_duration'] = self.format_seconds_as_humanized_duration
self._app.jinja_env.filters['_base'] = self._base_gettext

self._babel = flask_babel.Babel(self._app)
Expand Down Expand Up @@ -236,6 +239,21 @@ def format_time(self, value):
else:
return value.strftime(self._config.time_format)

def format_seconds_as_humanized_duration(self, seconds):

if seconds is None:
return _("n/a")

try:
# trying to activate the localization for a non-existing locale (including 'en'!) triggers an exception
if self._locale_helper.locale is not None:
humanize.i18n.activate(self._locale_helper.locale)

except Exception:
humanize.i18n.deactivate()

return humanize.naturaldelta(datetime.timedelta(seconds=seconds))

@staticmethod
def format_seconds(value):

Expand Down
2 changes: 1 addition & 1 deletion little_brother/templates/topology.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
<DIV CLASS="col-xs-2 localetext {{ info.version_class }}">{{ info.version_string }}</DIV>
<DIV CLASS="col-xs-2 localetext">{{ info.client_stats.revision }}</DIV>
<DIV CLASS="col-xs-2 localetext">{{ info.client_stats.python_version }}</DIV>
<DIV CLASS="col-xs-2 localetext {{ info.last_message_class}}">{{ info.last_message_string }}</DIV>
<DIV CLASS="col-xs-2 localetext {{ info.last_message_class}}">{{ info.seconds_without_ping | seconds_as_humanized_duration }}</DIV>
</DIV>
{% endfor %}
</DIV>
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Flask-WTF>=0.14.3
selenium>=3.141.0
urllib3>=1.26.2
prometheus_client>=0.9.0
humanize>=3.2.0

0 comments on commit b5db849

Please sign in to comment.