From 1953fa4fa9510154b26dc861934ad91ff9bba875 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Mon, 19 Mar 2018 17:00:05 -0400 Subject: [PATCH 01/21] Lint production settings file --- tock/tock/settings/production.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tock/tock/settings/production.py b/tock/tock/settings/production.py index 22d76432e..b0ed70952 100644 --- a/tock/tock/settings/production.py +++ b/tock/tock/settings/production.py @@ -11,7 +11,7 @@ ALLOWED_HOSTS = ['*'] # proxied -#FORCE_SCRIPT_NAME = '/tock' +# FORCE_SCRIPT_NAME = '/tock' STATIC_ROOT = '/app/tock/tock/static/' STATIC_URL = '/tock/static/' @@ -41,6 +41,6 @@ try: - from .local_settings import * # noqa + from .local_settings import * # noqa except ImportError: - pass + pass From f6519b736d70de21c3ad6f0ec0247599a0818e0a Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Mon, 19 Mar 2018 17:00:59 -0400 Subject: [PATCH 02/21] Update logging configuration --- tock/tock/remote_user_auth.py | 2 +- tock/tock/settings/production.py | 36 +++++++++++++++++++++++++++++++- tock/tock/utils.py | 2 +- tock/tock/views.py | 2 +- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/tock/tock/remote_user_auth.py b/tock/tock/remote_user_auth.py index f81e34a54..87a3577a7 100644 --- a/tock/tock/remote_user_auth.py +++ b/tock/tock/remote_user_auth.py @@ -8,7 +8,7 @@ from employees.models import UserData -logger = logging.getLogger(__name__) +logger = logging.getLogger('tock-auth') def email_to_username(email): diff --git a/tock/tock/settings/production.py b/tock/tock/settings/production.py index b0ed70952..ac99c46a7 100644 --- a/tock/tock/settings/production.py +++ b/tock/tock/settings/production.py @@ -26,16 +26,50 @@ LOGGING = { 'version': 1, 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] " + "%(message)s", + 'datefmt': "%d/%b/%Y %H:%M:%S", + }, + 'simple': { + 'format': "%(levelname)s %(message)s", + }, + }, 'handlers': { + 'file': { + 'level': 'DEBUG', + 'class': 'logging.FileHandler', + 'filename': os.path.join(BASE_DIR, 'logs/tock.log'), + 'formatter': 'verbose', + }, 'console': { + 'level': 'INFO', 'class': 'logging.StreamHandler', + 'formatter': 'verbose', }, }, 'loggers': { 'django': { - 'handlers': ['console'], + 'handlers': ['console', 'file'], + 'propagate': True, 'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'), }, + 'uaa_client': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, + 'tock-auth': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, + 'tock': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, }, } diff --git a/tock/tock/utils.py b/tock/tock/utils.py index 9c3bd3696..fbcfb8f5c 100644 --- a/tock/tock/utils.py +++ b/tock/tock/utils.py @@ -11,7 +11,7 @@ from tock.settings import base -logger = logging.getLogger(__name__) +logger = logging.getLogger('tock-auth') class PermissionMixin(LoginRequiredMixin, object): diff --git a/tock/tock/views.py b/tock/tock/views.py index c4afef3a7..59f47e8ff 100644 --- a/tock/tock/views.py +++ b/tock/tock/views.py @@ -3,7 +3,7 @@ from django.shortcuts import render import django.contrib.auth -logger = logging.getLogger(__name__) +logger = logging.getLogger('tock') def csrf_failure(request, reason=""): From 194b1546770d3e1328b941f17d914483a5afe96e Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Tue, 20 Mar 2018 13:13:07 -0400 Subject: [PATCH 03/21] Create logs directory for log file handling --- tock/tock/logs/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tock/tock/logs/.gitkeep diff --git a/tock/tock/logs/.gitkeep b/tock/tock/logs/.gitkeep new file mode 100644 index 000000000..e69de29bb From 12c2bd6def739f4560df2a4d6970aec44bda35e6 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Tue, 20 Mar 2018 16:22:51 -0400 Subject: [PATCH 04/21] Record and log deployments --- bin/run.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/bin/run.sh b/bin/run.sh index 03f7ca0a2..3aa8cc7f8 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -17,5 +17,32 @@ else fi fi + +if [[ -f VERSION ]] +then + VERSION=$(cat VERSION) +else + VERSION="Manual Deployment" +fi + +NEW_RELIC_API_KEY=$( + echo "${VCAP_SERVICES}" | \ + jq '.[] | map(select(.name == "tock-credentials-logging")) | .[].credentials.NEW_RELIC_API_KEY' -r +) + +# Append New Relic API key to New Relic INI file for `newrelic-admin` tool +cat <> "${NEW_RELIC_CONFIG_FILE}" + +# Adding license key from script $(basename "${0}") +api_key=${NEW_RELIC_API_KEY} +EOF + +DEPLOYMENT_DESCRIPTION="Recording deployment of ${VERSION}." + +echo "${DEPLOYMENT_DESCRIPTION}" + +# Record deployment using the New Relic Python Admin CLI +newrelic-admin record-deploy "${NEW_RELIC_CONFIG_FILE}" "${DEPLOYMENT_DESCRIPTION}" + python manage.py collectstatic --settings=tock.settings.production --noinput gunicorn -t 120 -k gevent -w 2 tock.wsgi:application From 80c50d23802d1dcd6e5b817cdf7b72d5af7c2115 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Tue, 20 Mar 2018 16:23:09 -0400 Subject: [PATCH 05/21] Have Django templates log on INFO only --- tock/tock/settings/production.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tock/tock/settings/production.py b/tock/tock/settings/production.py index ac99c46a7..c229ee225 100644 --- a/tock/tock/settings/production.py +++ b/tock/tock/settings/production.py @@ -55,6 +55,11 @@ 'propagate': True, 'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'), }, + 'django.template': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, 'uaa_client': { 'handlers': ['console', 'file'], 'propagate': True, From 33ffc5cb918229639b1345fcbe68237cf8c3e796 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Wed, 21 Mar 2018 10:22:45 -0400 Subject: [PATCH 06/21] Add an explicit BASE_DIR rather an implicit --- tock/tock/settings/production.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tock/tock/settings/production.py b/tock/tock/settings/production.py index c229ee225..8314442a2 100644 --- a/tock/tock/settings/production.py +++ b/tock/tock/settings/production.py @@ -6,6 +6,7 @@ # spell out explicit variable dependencies from .base import DATABASES +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) USE_X_FORWARDED_HOST = True From 22040053309a7016efe1d8a8bf75da788611d7f4 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Wed, 21 Mar 2018 14:43:28 -0400 Subject: [PATCH 07/21] Add signals for user_log* events --- tock/tock/signals.py | 22 ++++++++++++++++++++++ tock/tock/views.py | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 tock/tock/signals.py diff --git a/tock/tock/signals.py b/tock/tock/signals.py new file mode 100644 index 000000000..c445f1fdf --- /dev/null +++ b/tock/tock/signals.py @@ -0,0 +1,22 @@ +import logging + +from django.contrib.auth.signals import user_logged_in, user_logged_out, \ + user_login_failed +from django.dispatch import receiver + +logger = logging.getLogger('tock-auth') + + +@receiver(user_logged_in) +def login_logger(sender, request, user, **kwargs): + logger.info(f'Successful login event for {user.username}.') + + +@receiver(user_logged_out) +def logout_logger(sender, request, user, **kwargs): + logger.info(f'Successful logout event for {user.username}.') + + +@receiver(user_logged_fail) +def failed_login_logger(sender, credentials, request, **kwargs): + logger.info(f'Unsuccessful login attempt by {credentials}.') diff --git a/tock/tock/views.py b/tock/tock/views.py index 59f47e8ff..9107bfe77 100644 --- a/tock/tock/views.py +++ b/tock/tock/views.py @@ -3,6 +3,8 @@ from django.shortcuts import render import django.contrib.auth +from . import signals # noqa + logger = logging.getLogger('tock') From 8b4258e446087e4601aea671fa160b6649207b96 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 07:56:49 -0400 Subject: [PATCH 08/21] Fix name of signal --- tock/tock/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tock/tock/signals.py b/tock/tock/signals.py index c445f1fdf..f8683a171 100644 --- a/tock/tock/signals.py +++ b/tock/tock/signals.py @@ -17,6 +17,6 @@ def logout_logger(sender, request, user, **kwargs): logger.info(f'Successful logout event for {user.username}.') -@receiver(user_logged_fail) +@receiver(user_login_failed) def failed_login_logger(sender, credentials, request, **kwargs): logger.info(f'Unsuccessful login attempt by {credentials}.') From b1c1627a059e2c179ebc0ae61bf690e228b94f17 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 09:44:14 -0400 Subject: [PATCH 09/21] Add logging to Employees models --- tock/employees/signals.py | 23 +++++++++++++++++++++++ tock/employees/views.py | 2 +- tock/tock/settings/production.py | 5 +++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tock/employees/signals.py diff --git a/tock/employees/signals.py b/tock/employees/signals.py new file mode 100644 index 000000000..6f39e93b9 --- /dev/null +++ b/tock/employees/signals.py @@ -0,0 +1,23 @@ +import logging + +from django.db.models.signals import pre_save +from django.dispatch import receiver + +from .models import EmployeeGrade, UserData + +logger = logging.getLogger('tock-employees') + + +@receiver(pre_save, sender=EmployeeGrade) +def employee_grade_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating EmployeeGrade for {instance.employee.username}.' + ) + +@receiver(pre_save, sender=UserData) +def user_data_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating UserData for {instance.user.username}.' + ) diff --git a/tock/employees/views.py b/tock/employees/views.py index e30bd8589..305f167c6 100644 --- a/tock/employees/views.py +++ b/tock/employees/views.py @@ -11,7 +11,7 @@ from .forms import UserForm from .models import UserData - +from . import signals def parse_date(date): if date == 'NA': diff --git a/tock/tock/settings/production.py b/tock/tock/settings/production.py index 8314442a2..a1b40b159 100644 --- a/tock/tock/settings/production.py +++ b/tock/tock/settings/production.py @@ -71,6 +71,11 @@ 'propagate': True, 'level': 'INFO', }, + 'tock-employees': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, 'tock': { 'handlers': ['console', 'file'], 'propagate': True, From a696d826462a1204fc626ff84ab706a110710d5e Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 09:44:50 -0400 Subject: [PATCH 10/21] Rename Tock signals logging --- tock/tock/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tock/tock/signals.py b/tock/tock/signals.py index f8683a171..bd03af4f7 100644 --- a/tock/tock/signals.py +++ b/tock/tock/signals.py @@ -4,7 +4,7 @@ user_login_failed from django.dispatch import receiver -logger = logging.getLogger('tock-auth') +logger = logging.getLogger('tock') @receiver(user_logged_in) From 21d6bcbeaa593ccae1b0227d367da55816ae3b35 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 09:45:19 -0400 Subject: [PATCH 11/21] Add logging for Hours Models --- tock/hours/models.py | 3 ++ tock/hours/signals.py | 67 ++++++++++++++++++++++++++++++++ tock/hours/views.py | 2 + tock/tock/settings/production.py | 5 +++ 4 files changed, 77 insertions(+) create mode 100644 tock/hours/signals.py diff --git a/tock/hours/models.py b/tock/hours/models.py index 1e25e9716..6d3b1d9fd 100644 --- a/tock/hours/models.py +++ b/tock/hours/models.py @@ -299,6 +299,9 @@ def hours(self): def notes_list(self): return self.notes.split('\n') + def __str__(self): + return f'{self.timecard} {self.project}' + def save(self, *args, **kwargs): """Custom save() method to append employee grade info and the submitted status of the related timecard.""" diff --git a/tock/hours/signals.py b/tock/hours/signals.py new file mode 100644 index 000000000..38b7e1f47 --- /dev/null +++ b/tock/hours/signals.py @@ -0,0 +1,67 @@ +import logging + +from django.db.models.signals import pre_save, post_save +from django.dispatch import receiver + +from .models import ( + HolidayPrefills, ReportingPeriod, Targets, Timecard, TimecardNote, + TimecardObject, TimecardPrefillData +) + +logger = logging.getLogger('tock-hours') + + +@receiver(pre_save, sender=HolidayPrefills) +def holiday_prefills_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating HolidayPrefills for {instance}.' + ) + + +@receiver(pre_save, sender=ReportingPeriod) +def reporting_period_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating ReportingPeriod for {instance}.' + ) + + +@receiver(pre_save, sender=Targets) +def targets_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating Targets for {instance}.' + ) + + +@receiver(pre_save, sender=Timecard) +def timecard_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating Timecard for {instance}.' + ) + + +@receiver(pre_save, sender=TimecardNote) +def timecard_note_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating TimecardNote for {instance}.' + ) + + +@receiver(pre_save, sender=TimecardObject) +def timecard_object_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating TimecardObject for {instance}.' + ) + + +@receiver(pre_save, sender=TimecardPrefillData) +def timecard_prefill_data_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating TimecardPrefillData for {instance}.' + ) diff --git a/tock/hours/views.py b/tock/hours/views.py index b116418f2..99b062993 100644 --- a/tock/hours/views.py +++ b/tock/hours/views.py @@ -47,6 +47,8 @@ ) from utilization.utils import calculate_utilization, get_fy_first_day +from . import signals + class DashboardReportsList(PermissionMixin, ListView): template_name = 'hours/dashboard_list.html' diff --git a/tock/tock/settings/production.py b/tock/tock/settings/production.py index a1b40b159..54c3df7af 100644 --- a/tock/tock/settings/production.py +++ b/tock/tock/settings/production.py @@ -76,6 +76,11 @@ 'propagate': True, 'level': 'INFO', }, + 'tock-hours': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, 'tock': { 'handlers': ['console', 'file'], 'propagate': True, From 8b917862bb5608d0d41979b69b88c30ee88977af Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 10:23:02 -0400 Subject: [PATCH 12/21] Refactor where signals are initialized --- tock/employees/apps.py | 7 +++++++ tock/employees/models.py | 1 + tock/employees/views.py | 1 - tock/hours/apps.py | 7 +++++++ tock/hours/signals.py | 2 +- tock/hours/views.py | 2 -- tock/tock/apps.py | 7 +++++++ tock/tock/settings/base.py | 6 +++--- tock/tock/views.py | 2 -- 9 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 tock/employees/apps.py create mode 100644 tock/hours/apps.py create mode 100644 tock/tock/apps.py diff --git a/tock/employees/apps.py b/tock/employees/apps.py new file mode 100644 index 000000000..fc53c0664 --- /dev/null +++ b/tock/employees/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class EmployeesAppConfig(AppConfig): + name = "employees" + + def ready(self): + from . import signals # noqa diff --git a/tock/employees/models.py b/tock/employees/models.py index 4c05a3ab1..8770f0430 100644 --- a/tock/employees/models.py +++ b/tock/employees/models.py @@ -7,6 +7,7 @@ from organizations.models import Organization from projects.models import ProfitLossAccount + class EmployeeGrade(models.Model): GRADE_CHOICES = ( (1, '1'), diff --git a/tock/employees/views.py b/tock/employees/views.py index 305f167c6..28637ec9c 100644 --- a/tock/employees/views.py +++ b/tock/employees/views.py @@ -11,7 +11,6 @@ from .forms import UserForm from .models import UserData -from . import signals def parse_date(date): if date == 'NA': diff --git a/tock/hours/apps.py b/tock/hours/apps.py new file mode 100644 index 000000000..3f205cf77 --- /dev/null +++ b/tock/hours/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class HoursAppConfig(AppConfig): + name = "hours" + + def ready(self): + from . import signals # noqa diff --git a/tock/hours/signals.py b/tock/hours/signals.py index 38b7e1f47..aeca4e2e9 100644 --- a/tock/hours/signals.py +++ b/tock/hours/signals.py @@ -1,6 +1,6 @@ import logging -from django.db.models.signals import pre_save, post_save +from django.db.models.signals import pre_save from django.dispatch import receiver from .models import ( diff --git a/tock/hours/views.py b/tock/hours/views.py index 99b062993..b116418f2 100644 --- a/tock/hours/views.py +++ b/tock/hours/views.py @@ -47,8 +47,6 @@ ) from utilization.utils import calculate_utilization, get_fy_first_day -from . import signals - class DashboardReportsList(PermissionMixin, ListView): template_name = 'hours/dashboard_list.html' diff --git a/tock/tock/apps.py b/tock/tock/apps.py new file mode 100644 index 000000000..6a9ee7750 --- /dev/null +++ b/tock/tock/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class TockAppConfig(AppConfig): + name = "tock" + + def ready(self): + from . import signals # noqa diff --git a/tock/tock/settings/base.py b/tock/tock/settings/base.py index 2cd53c217..252ea80bd 100644 --- a/tock/tock/settings/base.py +++ b/tock/tock/settings/base.py @@ -34,10 +34,10 @@ 'django.contrib.sessions', 'django.contrib.messages', 'uaa_client', - 'tock', + 'tock.apps.TockAppConfig', 'projects', - 'hours', - 'employees', + 'hours.apps.HoursAppConfig', + 'employees.apps.EmployeesAppConfig', 'organizations', 'api', 'utilization', diff --git a/tock/tock/views.py b/tock/tock/views.py index 9107bfe77..59f47e8ff 100644 --- a/tock/tock/views.py +++ b/tock/tock/views.py @@ -3,8 +3,6 @@ from django.shortcuts import render import django.contrib.auth -from . import signals # noqa - logger = logging.getLogger('tock') From 39ef95d5a69dda9560630750ff6319e41a79d26e Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 14:26:54 -0400 Subject: [PATCH 13/21] Refactor signal connections to use setup_signals() --- tock/employees/apps.py | 3 ++- tock/employees/signals.py | 19 ++++++++++--- tock/hours/apps.py | 3 ++- tock/hours/signals.py | 56 ++++++++++++++++++++++++++++++--------- tock/tock/apps.py | 3 ++- tock/tock/signals.py | 26 ++++++++++++------ 6 files changed, 82 insertions(+), 28 deletions(-) diff --git a/tock/employees/apps.py b/tock/employees/apps.py index fc53c0664..a36dec821 100644 --- a/tock/employees/apps.py +++ b/tock/employees/apps.py @@ -1,7 +1,8 @@ from django.apps import AppConfig +from .signals import setup_signals class EmployeesAppConfig(AppConfig): name = "employees" def ready(self): - from . import signals # noqa + setup_signals() diff --git a/tock/employees/signals.py b/tock/employees/signals.py index 6f39e93b9..a674b52f5 100644 --- a/tock/employees/signals.py +++ b/tock/employees/signals.py @@ -3,21 +3,32 @@ from django.db.models.signals import pre_save from django.dispatch import receiver -from .models import EmployeeGrade, UserData - logger = logging.getLogger('tock-employees') -@receiver(pre_save, sender=EmployeeGrade) def employee_grade_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( f'Creating EmployeeGrade for {instance.employee.username}.' ) -@receiver(pre_save, sender=UserData) def user_data_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( f'Creating UserData for {instance.user.username}.' ) + + +def setup_signals(): + from .models import EmployeeGrade, UserData + + pre_save.connect( + employee_grade_creation, + sender=EmployeeGrade, + dispatch_uid="employees_employee_grade_creation" + ) + pre_save.connect( + user_data_creation, + sender=UserData, + dispatch_uid="employees_user_data_creation" + ) diff --git a/tock/hours/apps.py b/tock/hours/apps.py index 3f205cf77..2b9bd05aa 100644 --- a/tock/hours/apps.py +++ b/tock/hours/apps.py @@ -1,7 +1,8 @@ from django.apps import AppConfig +from .signals import setup_signals class HoursAppConfig(AppConfig): name = "hours" def ready(self): - from . import signals # noqa + setup_signals() diff --git a/tock/hours/signals.py b/tock/hours/signals.py index aeca4e2e9..96eb14cb3 100644 --- a/tock/hours/signals.py +++ b/tock/hours/signals.py @@ -1,17 +1,10 @@ import logging from django.db.models.signals import pre_save -from django.dispatch import receiver - -from .models import ( - HolidayPrefills, ReportingPeriod, Targets, Timecard, TimecardNote, - TimecardObject, TimecardPrefillData -) logger = logging.getLogger('tock-hours') -@receiver(pre_save, sender=HolidayPrefills) def holiday_prefills_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( @@ -19,7 +12,6 @@ def holiday_prefills_creation(sender, instance=None, **kwargs): ) -@receiver(pre_save, sender=ReportingPeriod) def reporting_period_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( @@ -27,7 +19,6 @@ def reporting_period_creation(sender, instance=None, **kwargs): ) -@receiver(pre_save, sender=Targets) def targets_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( @@ -35,7 +26,6 @@ def targets_creation(sender, instance=None, **kwargs): ) -@receiver(pre_save, sender=Timecard) def timecard_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( @@ -43,7 +33,6 @@ def timecard_creation(sender, instance=None, **kwargs): ) -@receiver(pre_save, sender=TimecardNote) def timecard_note_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( @@ -51,7 +40,6 @@ def timecard_note_creation(sender, instance=None, **kwargs): ) -@receiver(pre_save, sender=TimecardObject) def timecard_object_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( @@ -59,9 +47,51 @@ def timecard_object_creation(sender, instance=None, **kwargs): ) -@receiver(pre_save, sender=TimecardPrefillData) def timecard_prefill_data_creation(sender, instance=None, **kwargs): if instance is not None and instance.pk is None: logger.info( f'Creating TimecardPrefillData for {instance}.' ) + + +def setup_signals(): + from .models import ( + HolidayPrefills, ReportingPeriod, Targets, Timecard, TimecardNote, + TimecardObject, TimecardPrefillData + ) + + pre_save.connect( + holiday_prefills_creation, + sender=HolidayPrefills, + dispatch_uid="hours_holiday_prefills_creation" + ) + pre_save.connect( + reporting_period_creation, + sender=ReportingPeriod, + dispatch_uid="hours_reporting_period_creation" + ) + pre_save.connect( + targets_creation, + sender=Targets, + dispatch_uid="hours_targets_creation" + ) + pre_save.connect( + timecard_creation, + sender=Timecard, + dispatch_uid="hours_timecard_creation" + ) + pre_save.connect( + timecard_note_creation, + sender=TimecardNote, + dispatch_uid="hours_timecard_note_creation" + ) + pre_save.connect( + timecard_object_creation, + sender=TimecardObject, + dispatch_uid="hours_timecard_object_creation" + ) + pre_save.connect( + timecard_prefill_data_creation, + sender=TimecardPrefillData, + dispatch_uid="hours_timecard_prefill_data_creation" + ) diff --git a/tock/tock/apps.py b/tock/tock/apps.py index 6a9ee7750..2f83c7883 100644 --- a/tock/tock/apps.py +++ b/tock/tock/apps.py @@ -1,7 +1,8 @@ from django.apps import AppConfig +from .signals import setup_signals class TockAppConfig(AppConfig): name = "tock" def ready(self): - from . import signals # noqa + setup_signals() diff --git a/tock/tock/signals.py b/tock/tock/signals.py index bd03af4f7..6b951e374 100644 --- a/tock/tock/signals.py +++ b/tock/tock/signals.py @@ -2,21 +2,31 @@ from django.contrib.auth.signals import user_logged_in, user_logged_out, \ user_login_failed -from django.dispatch import receiver logger = logging.getLogger('tock') -@receiver(user_logged_in) -def login_logger(sender, request, user, **kwargs): +def successful_login(sender, request, user, **kwargs): logger.info(f'Successful login event for {user.username}.') -@receiver(user_logged_out) -def logout_logger(sender, request, user, **kwargs): +def successful_logout(sender, request, user, **kwargs): logger.info(f'Successful logout event for {user.username}.') - -@receiver(user_login_failed) -def failed_login_logger(sender, credentials, request, **kwargs): +def failed_login(sender, credentials, request, **kwargs): logger.info(f'Unsuccessful login attempt by {credentials}.') + + +def setup_signals(): + user_logged_in.connect( + successful_login, + dispatch_uid="tock_successful_login" + ) + user_logged_out.connect( + successful_logout, + dispatch_uid="tock_successful_logout" + ) + user_login_failed.connect( + failed_login, + dispatch_uid="tock_failed_login" + ) From 4dbdaf0db4cfae5bbda3a533ef5b989d7ce9055f Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 17:02:02 -0400 Subject: [PATCH 14/21] Add logging to the rest of the Tock-family apps --- tock/api/apps.py | 4 ++ tock/api/signals.py | 1 + tock/organizations/apps.py | 9 +++++ tock/organizations/signals.py | 22 +++++++++++ tock/projects/apps.py | 8 ++++ tock/projects/signals.py | 65 ++++++++++++++++++++++++++++++++ tock/tock/settings/base.py | 8 ++-- tock/tock/settings/production.py | 10 +++++ tock/utilization/apps.py | 4 ++ tock/utilization/signals.py | 1 + 10 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 tock/api/apps.py create mode 100644 tock/api/signals.py create mode 100644 tock/organizations/apps.py create mode 100644 tock/organizations/signals.py create mode 100644 tock/projects/apps.py create mode 100644 tock/projects/signals.py create mode 100644 tock/utilization/apps.py create mode 100644 tock/utilization/signals.py diff --git a/tock/api/apps.py b/tock/api/apps.py new file mode 100644 index 000000000..3204d8c68 --- /dev/null +++ b/tock/api/apps.py @@ -0,0 +1,4 @@ +from django.apps import AppConfig + +class ApiAppConfig(AppConfig): + name = "api" diff --git a/tock/api/signals.py b/tock/api/signals.py new file mode 100644 index 000000000..6fa24b3b0 --- /dev/null +++ b/tock/api/signals.py @@ -0,0 +1 @@ +# Create your signal connections here. diff --git a/tock/organizations/apps.py b/tock/organizations/apps.py new file mode 100644 index 000000000..09f197107 --- /dev/null +++ b/tock/organizations/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig +from .signals import setup_signals + +class OrganizationsAppConfig(AppConfig): + name = "organizations" + + def ready(self): + setup_signals() + diff --git a/tock/organizations/signals.py b/tock/organizations/signals.py new file mode 100644 index 000000000..0770295dc --- /dev/null +++ b/tock/organizations/signals.py @@ -0,0 +1,22 @@ +import logging + +from django.db.models.signals import pre_save + +logger = logging.getLogger('tock-organizations') + + +def organization_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating Organization for {instance}.' + ) + + +def setup_signals(): + from .models import Organization + + pre_save.connect( + organization_creation, + sender=Organization, + dispatch_uid="organizations_organization_creation" + ) diff --git a/tock/projects/apps.py b/tock/projects/apps.py new file mode 100644 index 000000000..ee5f76bcb --- /dev/null +++ b/tock/projects/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from .signals import setup_signals + +class ProjectsAppConfig(AppConfig): + name = "projects" + + def ready(self): + setup_signals() diff --git a/tock/projects/signals.py b/tock/projects/signals.py new file mode 100644 index 000000000..14698ecf9 --- /dev/null +++ b/tock/projects/signals.py @@ -0,0 +1,65 @@ +import logging + +from django.db.models.signals import pre_save + +logger = logging.getLogger('tock-projects') + + +def agency_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating Agency for {instance}.' + ) + +def accounting_code_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating AccountingCode for {instance}.' + ) + + +def project_alert_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating ProjectAlert for {instance}.' + ) + + +def profit_loss_account_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating ProfitLossAccount for {instance}.' + ) + + +def project_creation(sender, instance=None, **kwargs): + if instance is not None and instance.pk is None: + logger.info( + f'Creating Project for {instance}.' + ) + +def setup_signals(): + from .models import ( + Agency, AccountingCode, ProjectAlert, ProfitLossAccount, Project + ) + + pre_save.connect( + agency_creation, + sender=Agency, + dispatch_uid="project_agency_creation" + ) + pre_save.connect( + accounting_code_creation, + sender=AccountingCode, + dispatch_uid="project_accounting_code_creation" + ) + pre_save.connect( + project_alert_creation, + sender=ProjectAlert, + dispatch_uid="project_project_alert_creation" + ) + pre_save.connect( + project_creation, + sender=Project, + dispatch_uid="project_project_creation" + ) diff --git a/tock/tock/settings/base.py b/tock/tock/settings/base.py index 252ea80bd..d53381ef3 100644 --- a/tock/tock/settings/base.py +++ b/tock/tock/settings/base.py @@ -35,12 +35,12 @@ 'django.contrib.messages', 'uaa_client', 'tock.apps.TockAppConfig', - 'projects', + 'projects.apps.ProjectsAppConfig', 'hours.apps.HoursAppConfig', 'employees.apps.EmployeesAppConfig', - 'organizations', - 'api', - 'utilization', + 'organizations.apps.OrganizationsAppConfig', + 'api.apps.ApiAppConfig', + 'utilization.apps.UtilizationAppConfig', 'rest_framework.authtoken', ) diff --git a/tock/tock/settings/production.py b/tock/tock/settings/production.py index 54c3df7af..b483cc85a 100644 --- a/tock/tock/settings/production.py +++ b/tock/tock/settings/production.py @@ -81,6 +81,16 @@ 'propagate': True, 'level': 'INFO', }, + 'tock-organizations': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, + 'tock-projects': { + 'handlers': ['console', 'file'], + 'propagate': True, + 'level': 'INFO', + }, 'tock': { 'handlers': ['console', 'file'], 'propagate': True, diff --git a/tock/utilization/apps.py b/tock/utilization/apps.py new file mode 100644 index 000000000..737b8e949 --- /dev/null +++ b/tock/utilization/apps.py @@ -0,0 +1,4 @@ +from django.apps import AppConfig + +class UtilizationAppConfig(AppConfig): + name = "utilization" diff --git a/tock/utilization/signals.py b/tock/utilization/signals.py new file mode 100644 index 000000000..6fa24b3b0 --- /dev/null +++ b/tock/utilization/signals.py @@ -0,0 +1 @@ +# Create your signal connections here. From 02b10c990e015c3d2dc9ea75ba404d209e43cd9a Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 17:31:38 -0400 Subject: [PATCH 15/21] Add admin logging to main Tock app signals --- tock/tock/signals.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tock/tock/signals.py b/tock/tock/signals.py index 6b951e374..cc5202c93 100644 --- a/tock/tock/signals.py +++ b/tock/tock/signals.py @@ -2,6 +2,7 @@ from django.contrib.auth.signals import user_logged_in, user_logged_out, \ user_login_failed +from django.db.models.signals import pre_save, post_save, m2m_changed logger = logging.getLogger('tock') @@ -13,11 +14,51 @@ def successful_login(sender, request, user, **kwargs): def successful_logout(sender, request, user, **kwargs): logger.info(f'Successful logout event for {user.username}.') + def failed_login(sender, credentials, request, **kwargs): logger.info(f'Unsuccessful login attempt by {credentials}.') +def adminlog_post_save(sender, instance, **kwargs): + from django.contrib.admin.models import ADDITION, CHANGE, DELETION + if instance.action_flag == ADDITION: + logger.info( + f'{instance.user} created {instance.content_type} {instance.object_repr}.' + ) + elif instance.action_flag == CHANGE: + logger.info( + f'{instance.user} changed {instance.content_type} {instance.object_repr}: ' + f'{instance.change_message}.' + ) + elif instance.action_flag == DELETION: + logger.info( + f'{instance.user} deleted {instance.content_type} {instance.object_repr}.' + ) + +def log_m2m_change(sender, instance, action, reverse, model, pk_set, **kwargs): + model_name = model._meta.verbose_name_plural + instance_model = instance._meta.verbose_name + if action == 'post_add': + objects_added = list(model.objects.filter(pk__in=pk_set)) + logger.info( + f'{model_name} given to {instance_model} {instance}: {objects_added}.' + ) + elif action == 'post_remove': + objects_added = list(model.objects.filter(pk__in=pk_set)) + logger.info( + f'{model_name} removed from {instance_model} {instance}: {objects_added}.' + ) + logger.info("%s removed from %s '%s': %s", model_name, instance_model, + instance, objects_added) + elif action == 'post_clear': + logger.info( + f'All {model_name} removed from {instance_model} {instance}.' + ) + def setup_signals(): + from django.contrib.auth.models import User, Group + from django.contrib.admin.models import LogEntry + user_logged_in.connect( successful_login, dispatch_uid="tock_successful_login" @@ -30,3 +71,23 @@ def setup_signals(): failed_login, dispatch_uid="tock_failed_login" ) + post_save.connect( + adminlog_post_save, + sender=LogEntry, + dispatch_uid="tock_adminlog_post_save" + ) + m2m_changed.connect( + log_m2m_change, + sender=User.groups.through, + dispatch_uid="tock_log_m2m_changed_user_groups" + ) + m2m_changed.connect( + log_m2m_change, + sender=User.user_permissions.through, + dispatch_uid="tock_log_m2m_changed_user_permissions" + ) + m2m_changed.connect( + log_m2m_change, + sender=Group.permissions.through, + dispatch_uid="tock_log_m2m_changed_groups_permissions" + ) From 8e7a981fa84b794dc627750b6cb746d69e146c2f Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Thu, 22 Mar 2018 17:54:14 -0400 Subject: [PATCH 16/21] Clear up linting errors --- tock/employees/signals.py | 1 - tock/organizations/apps.py | 1 - tock/projects/signals.py | 5 +++++ tock/tock/signals.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tock/employees/signals.py b/tock/employees/signals.py index a674b52f5..58b543534 100644 --- a/tock/employees/signals.py +++ b/tock/employees/signals.py @@ -1,7 +1,6 @@ import logging from django.db.models.signals import pre_save -from django.dispatch import receiver logger = logging.getLogger('tock-employees') diff --git a/tock/organizations/apps.py b/tock/organizations/apps.py index 09f197107..d72d410a2 100644 --- a/tock/organizations/apps.py +++ b/tock/organizations/apps.py @@ -6,4 +6,3 @@ class OrganizationsAppConfig(AppConfig): def ready(self): setup_signals() - diff --git a/tock/projects/signals.py b/tock/projects/signals.py index 14698ecf9..df74ed73f 100644 --- a/tock/projects/signals.py +++ b/tock/projects/signals.py @@ -58,6 +58,11 @@ def setup_signals(): sender=ProjectAlert, dispatch_uid="project_project_alert_creation" ) + pre_save.connect( + profit_loss_account_creation, + sender=ProfitLossAccount, + dispatch_uid="project_profit_loss_account_creation" + ) pre_save.connect( project_creation, sender=Project, diff --git a/tock/tock/signals.py b/tock/tock/signals.py index cc5202c93..b0fd03786 100644 --- a/tock/tock/signals.py +++ b/tock/tock/signals.py @@ -2,7 +2,7 @@ from django.contrib.auth.signals import user_logged_in, user_logged_out, \ user_login_failed -from django.db.models.signals import pre_save, post_save, m2m_changed +from django.db.models.signals import post_save, m2m_changed logger = logging.getLogger('tock') From 66892ccb9fab129528e85a58ef835b19a43e6925 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Fri, 23 Mar 2018 16:46:11 -0400 Subject: [PATCH 17/21] Clearer logs for RemoteUserAuth --- tock/tock/remote_user_auth.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tock/tock/remote_user_auth.py b/tock/tock/remote_user_auth.py index 87a3577a7..e84913de3 100644 --- a/tock/tock/remote_user_auth.py +++ b/tock/tock/remote_user_auth.py @@ -33,9 +33,8 @@ def verify_userdata(user): try: user = UserData.objects.get(user=user) except UserData.DoesNotExist: - logger.info( - 'Adding UserData for User [%s]' % - user.username + logger.warn( + f'Creating UserData for {user.username}.' ) UserData.objects.create( user=user, @@ -50,8 +49,7 @@ def get_user_by_email(cls, email): user = super().get_user_by_email(email) if user is not None: logger.info( - 'Verifying that User [%s] has UserData' % - user.username + f'Verifying UserData for {user.username}' ) verify_userdata(user) return user @@ -62,14 +60,12 @@ def create_user_with_email(cls, email): try: logger.info( - "Attempting to get user [%s] that exists already." % - username + f'Fetching User for {username}' ) user = User.objects.get(username=username) except User.DoesNotExist: - logger.info( - "Creating a new user [%s]" % - username + logger.warn( + f'Creating User for {username}' ) user = User.objects.create_user(username, email) user.first_name = str(username).split('.')[0].title() From c545b41830d4b08945544c835227a78c060bc3fc Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Fri, 23 Mar 2018 16:49:23 -0400 Subject: [PATCH 18/21] Better logging for PermissionMixin --- tock/tock/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tock/tock/utils.py b/tock/tock/utils.py index fbcfb8f5c..ea3f580db 100644 --- a/tock/tock/utils.py +++ b/tock/tock/utils.py @@ -30,9 +30,11 @@ def wrapped(request, *args, **kwargs): for permission_class in getattr(cls, 'permission_classes', ()): if not permission_class().has_permission(request, self): if isinstance(permission_class(), IsAuthenticated): - logger.info("User isn't logged in, redirecting...") + logger.info('User not authenticated, redirecting to UAA.') return redirect('/auth/login') - logger.info("User isn't allowed, redirecting...") + logger.warn( + f'User not authorized {request.user.username}, redirecting to 404.' + ) raise PermissionDenied return view(request, args, **kwargs) return wrapped From 8ac652f4c3bae594000aee500b2626b2274c361a Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Mon, 26 Mar 2018 14:40:10 -0400 Subject: [PATCH 19/21] Update prefix for admin and account logs --- tock/tock/signals.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tock/tock/signals.py b/tock/tock/signals.py index b0fd03786..4e4ef7ea5 100644 --- a/tock/tock/signals.py +++ b/tock/tock/signals.py @@ -23,16 +23,16 @@ def adminlog_post_save(sender, instance, **kwargs): from django.contrib.admin.models import ADDITION, CHANGE, DELETION if instance.action_flag == ADDITION: logger.info( - f'{instance.user} created {instance.content_type} {instance.object_repr}.' + f'[admin-log] {instance.user} created {instance.content_type} {instance.object_repr} @ {instance.get_admin_url()}.' ) elif instance.action_flag == CHANGE: logger.info( - f'{instance.user} changed {instance.content_type} {instance.object_repr}: ' - f'{instance.change_message}.' + f'[admin-log] {instance.user} changed {instance.content_type} {instance.object_repr}: ' + f'{instance.change_message} @ {instance.get_admin_url()}.' ) elif instance.action_flag == DELETION: logger.info( - f'{instance.user} deleted {instance.content_type} {instance.object_repr}.' + f'[admin-log] {instance.user} deleted {instance.content_type} {instance.object_repr} @ {instance.get_admin_url()}.' ) def log_m2m_change(sender, instance, action, reverse, model, pk_set, **kwargs): @@ -41,18 +41,18 @@ def log_m2m_change(sender, instance, action, reverse, model, pk_set, **kwargs): if action == 'post_add': objects_added = list(model.objects.filter(pk__in=pk_set)) logger.info( - f'{model_name} given to {instance_model} {instance}: {objects_added}.' + f'[account-management] {model_name} given to {instance_model} {instance}: {objects_added}.' ) elif action == 'post_remove': objects_added = list(model.objects.filter(pk__in=pk_set)) logger.info( - f'{model_name} removed from {instance_model} {instance}: {objects_added}.' + f'[account-management] {model_name} removed from {instance_model} {instance}: {objects_added}.' ) logger.info("%s removed from %s '%s': %s", model_name, instance_model, instance, objects_added) elif action == 'post_clear': logger.info( - f'All {model_name} removed from {instance_model} {instance}.' + f'[account-management] All {model_name} removed from {instance_model} {instance}.' ) def setup_signals(): From 2171a029d57a0bda0203734bc12360a72a05048f Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Tue, 27 Mar 2018 17:01:28 -0400 Subject: [PATCH 20/21] Remove about yaml RIP :skull: --- about.yml | 80 ------------------------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 about.yml diff --git a/about.yml b/about.yml deleted file mode 100644 index fc5fa3c80..000000000 --- a/about.yml +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: tock -full_name: Tock - -description: | - 18F uses Tock to track our time, so that we can bill clients accurately and meet our legal and regulatory obligations. - -impact: | - We built a web app that allows 18F employees to log the hours they spend on various projects. - We use this information for client billing and for internal analytics on what our team is working on. - -owner_type: project - -stage: live - -testable: true - -licenses: - tock: - name: Public Domain (CC0) - url: https://github.com/18F/tock/blob/master/LICENSE.md - -partners: -- 18F - -contact: -- url: https://github.com/18F/tock/issues - text: Tock Issue Tracker - -team: -- github: @joshuabailes - role: product owner - -- github: @abrouilette - role: project manager - -- github: @batemapf - role: developer - -- github: @annalee - role: tech lead - -- github: @maya - role: front-end design and development - -- github: @lauraponce - role: research - -type: app - -links: -- url: https://tock.18f.gov/ - text: Tock production site - -# What technologies are used in this project? -stack: -- Cloud.gov -- Cloud.gov authentication -- Docker -- Django - -# What are the services used to supply project status information? -# Items: -# - name: Name of the service -# category: Type of the service -# url: URL for detailed information -# badge: URL for the status badge -services: -- - -# Organizations or individuals who have adopted the project for their own use -# Items: -# - id: The name of the organization or individual -# url: A URL to the user's version of the project -users: -- - -# Tags that describe the project or aspects of the project -tags: -- From 6930ea0875bd6f9285ebf7e7e0a3d416342e0a13 Mon Sep 17 00:00:00 2001 From: Roger Steve Ruiz Date: Wed, 28 Mar 2018 17:19:41 -0400 Subject: [PATCH 21/21] Refactor name to what's used in staging & prod --- bin/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/run.sh b/bin/run.sh index 3aa8cc7f8..d8eb7b3c3 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -27,7 +27,7 @@ fi NEW_RELIC_API_KEY=$( echo "${VCAP_SERVICES}" | \ - jq '.[] | map(select(.name == "tock-credentials-logging")) | .[].credentials.NEW_RELIC_API_KEY' -r + jq '.[] | map(select(.name == "tock-credentials")) | .[].credentials.NEW_RELIC_API_KEY' -r ) # Append New Relic API key to New Relic INI file for `newrelic-admin` tool