Skip to content

Commit

Permalink
Merge branch 'develop' into email_notification_task_template
Browse files Browse the repository at this point in the history
# Conflicts:
#	notification/models.py
  • Loading branch information
nirmeen committed Nov 10, 2023
2 parents 31ba857 + 2b8e497 commit 809fab7
Show file tree
Hide file tree
Showing 46 changed files with 12,953 additions and 5,427 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ fabric.properties
elixir_daisy/settings_local.py
static/css/*
web/static/vendor/node_modules/*

web/static/js/*.bundle.js
web/static/js/*.bundle.js.LICENSE.txt


/log/*.log
Expand Down
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,13 @@ repos:
hooks:
- id: black
language_version: python3
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.15.0
hooks:
- id: eslint
files: web/static/vendor/(components|tests)/.*\.[jt]sx?$
types: [file]
additional_dependencies:
- eslint@8.15.0
- eslint-plugin-react@7.29.4
- eslint-plugin-react-hooks@4.6.0
7 changes: 5 additions & 2 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ sudo /usr/local/bin/pip3.9 install gunicorn
## NPM and Node.js

```bash
curl -sL https://rpm.nodesource.com/setup_16.x | sudo bash -
sudo yum install nodejs
apt-get update && apt-get install -y ca-certificates curl gnupg
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
apt-get update && apt-get install -y nodejs
```

Then you need to compile the static files.
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ You are encouraged to try Daisy for yourself using our [DEMO deployment](https:/
1. Compile and deploy static files

```bash
cd web/static/vendor
npm run build
cd ../../../
docker-compose exec web python manage.py collectstatic
```
1. Create initial data in the database
Expand Down Expand Up @@ -224,7 +227,7 @@ cd web/static/vendor/
npm ci
```

### Compile daisy.scss
### Compile daisy.scss and React
```bash
cd web/static/vendor
npm run-script build
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ services:
# database
db:
image: postgres:10.1
restart: always
restart: on-failure
environment:
POSTGRES_PASSWORD: daisy
POSTGRES_USER: daisy
Expand All @@ -34,7 +34,7 @@ services:
# web werver frontend
nginx:
build: ./docker/nginx
restart: always
restart: on-failure
volumes:
- statics:/public/static:ro
ports:
Expand All @@ -52,7 +52,7 @@ services:
# rabbit mq
mq:
image: rabbitmq:3.9-management-alpine
restart: "always"
restart: on-failure
ports:
- "15672:15672"
- "5672:5672"
Expand Down
1 change: 1 addition & 0 deletions elixir_daisy/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"django.contrib.messages.context_processors.messages",
"web.views.context_processors.daisy_version",
"web.views.context_processors.instance_branding",
"notification.views.context_processors.daisy_notifications_enabled",
],
},
},
Expand Down
2 changes: 2 additions & 0 deletions elixir_daisy/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from core.forms.user import UserAuthForm
from web.urls import web_urls
from notification.urls import notif_urls

urlpatterns = [
url(
Expand All @@ -31,6 +32,7 @@
),
url(r"^logout/$", auth_views.LogoutView.as_view(), name="logout"),
url(r"^admin/", admin.site.urls),
url(r"^notifications/", include(notif_urls)),
url(r"", include(web_urls)),
]

Expand Down
85 changes: 85 additions & 0 deletions notification/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import logging

from django.views.decorators.http import require_http_methods
from django.core.exceptions import PermissionDenied
from django.db.models import QuerySet
from django.http import JsonResponse

from notification.models import Notification


logger = logging.getLogger(__name__)


def jsonify(data):
if isinstance(data, QuerySet) or isinstance(data, list):
data = [o.to_json() for o in data]
else:
data = data.to_json()

return JsonResponse({"data": data}, status=200)


@require_http_methods(["PATCH"])
def api_dismiss_notification(request, pk):
notification = Notification.objects.get(pk=pk)
if request.user != notification.recipient:
raise PermissionDenied(
"You cannot dismiss a notification you are not the recipient of"
)
logger.debug(f"Dismissing user {request.user.pk} notification {notification.pk}")
notification.dismissed = True
notification.save()

notification_list = Notification.objects.filter(
recipient=request.user,
content_type=notification.content_type,
dispatch_in_app=True,
)
return jsonify(notification_list)


@require_http_methods(["PATCH"])
def api_dismiss_all_notifications(request, object_type):
logger.debug(f"Dismissing notifications for object_type {object_type}")
notification_list = Notification.objects.filter(
recipient=request.user,
content_type__model=object_type,
dispatch_in_app=True,
)
for notif in notification_list:
notif.dismissed = True
notif.save()

logger.debug(
f"Successfully dismissed {len(notification_list)} notifications for user {request.user.pk}"
)
return jsonify(notification_list)


@require_http_methods(["GET"])
def api_get_notifications(request):
notifications_list = Notification.objects.filter(
dispatch_in_app=True,
)
if request.GET.get("show_dismissed") != "true":
notifications_list = notifications_list.filter(dismissed=False)
if request.GET.get("as_admin") != "true":
notifications_list = notifications_list.filter(recipient__pk=request.user.pk)
elif request.GET.get("recipient", ""):
notifications_list = notifications_list.filter(
recipient__pk=request.GET.get("recipient")
)

return jsonify(notifications_list.all())


@require_http_methods(["GET"])
def api_get_notifications_number(request):
number = Notification.objects.filter(
recipient=request.user,
dismissed=False,
dispatch_in_app=True,
).count()

return JsonResponse({"success": True, "data": number}, status=200)
28 changes: 28 additions & 0 deletions notification/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Notification class does not have any target at the moment.
"""
from datetime import datetime

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
Expand Down Expand Up @@ -107,5 +109,31 @@ def get_full_url(self):
self.get_absolute_url(),
)

@property
def event_time(self):
return datetime.now()

def to_json(self):
user_json = (
self.recipient.to_json()
if hasattr(self.recipient, "to_json")
else {"id": self.recipient.id, "name": self.recipient.get_full_name()}
)
return {
"id": self.id,
"recipient": user_json,
"verb": self.verb.value,
"on": self.on,
"time": self.time,
"sentInApp": self.dispatch_in_app,
"sentByEmail": self.dispatch_by_email,
"dismissed": self.dismissed,
"message": self.message,
"objectType": self.content_type.model,
"objectDisplayName": self.content_type.name,
"objectName": self.content_object.__str__(),
"objectUrl": self.get_absolute_url() or "",
}

def __str__(self):
return f"N: {self.recipient} {self.verb} {self.object_id} {self.time}"
62 changes: 62 additions & 0 deletions notification/static/notification/css/datatables.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate {
display: flex;
color: #333333;
}

.dataTables_wrapper .dataTables_paginate span {
display: flex;
}

.dataTables_wrapper .dataTables_paginate .paginate_button {
min-width: 2.6em;
margin-right: 0.5em;
text-align: center;
border-radius: 4em;
position: relative;
display: block;
padding: 0.5rem 0.75rem;
margin-left: 0;
line-height: 1.25;
color: #023452 !important;
background: transparent !important;
border: 0 solid #dee2e6 !important;
}

.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
z-index: 2;
text-decoration: none;
color: #000507 !important;
background-color: #e9ecef !important;
border-color: #dee2e6 !important;
}

.dataTables_wrapper .dataTables_paginate .paginate_button:active,
.dataTables_wrapper .dataTables_paginate .paginate_button:not(:disabled):not(.disabled).active,
.dataTables_wrapper .dataTables_paginate .paginate_button:not(:disabled):not(.disabled).active:hover,
.dataTables_wrapper .dataTables_paginate .paginate_button:not(:disabled):not(.disabled).current,
.dataTables_wrapper .dataTables_paginate .paginate_button:not(:disabled):not(.disabled).current:hover {
z-index: 1;
color: #fff !important;
background-color: #023452 !important;
border-color: #023452 !important;
}

.dataTables_wrapper .dataTables_paginate .paginate_button:focus {
z-index: 2;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(2, 52, 82, 0.25);
}

.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
cursor: default;
color: #666 !important;
border: 0 solid transparent !important;
background: transparent !important;
box-shadow: none !important;
}
5 changes: 5 additions & 0 deletions notification/static/notification/css/notification.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.card-badge {
position: absolute;
right: 25px;
bottom: 35%;
}
81 changes: 81 additions & 0 deletions notification/static/notification/js/datatables.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

var csrftoken;

function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});



// Adapting the datatables width when accordions are clicked



$(document).ready(() => {
csrftoken = Cookies.get("csrftoken");

const notificationsDT = document.querySelectorAll(".notifications-dt");

notificationsDT.forEach((notifTable) => {
console.log(notifTable);
// Building the datatable
const colIndexes = {}
for (const index of Array(notifTable.rows[0].cells.length).keys()){
const header = notifTable.rows[0].cells[index];
colIndexes[header.innerText.toString().replace(" ", "-").toLowerCase()] = index;
}

// Building default options
const tableOptions = {
"order": [[colIndexes["received-on"], "desc"]],
};

// Adding options for 'dismiss' columns when it exists
if (colIndexes["dismissed"]){
tableOptions["columnDefs"] = [
{orderable: false, searchable: false, targets: colIndexes["dismiss-all"]},
{visible: false, targets: colIndexes["dismissed"]},
{visible: true, targets: '_all'},
];
}

// Building the table
const individualDataTable = $(`#${notifTable.id}`).DataTable(tableOptions)
individualDataTable.columns.adjust().draw();

if (colIndexes["dismissed"]) {
// Hiding dismissed notifications by default;]
individualDataTable.columns(colIndexes["dismissed"]).search("False").draw();

// Build the checkbox to display/hide dismissed notifications
const showDismissedDOM = `&nbsp;&nbsp;/&nbsp;&nbsp;<label>Show dismissed&nbsp;&nbsp;</label><input id="${notifTable.id}-show-dismissed" type="checkbox" />`;
$(`#${notifTable.id}_length`).append(showDismissedDOM);
const showDismissed = document.querySelector(`#${notifTable.id}-show-dismissed`);
showDismissed.addEventListener("input", () => {
if (showDismissed.checked) {
individualDataTable.columns(colIndexes["dismissed"]).search("").draw();
} else {
individualDataTable.columns(colIndexes["dismissed"]).search("False").draw();
}
});
}
console.log(individualDataTable.data());
})

// Write method to call api to get notifications regarding a user (default is None for admin) and a content_type
document.querySelectorAll(".btn[data-toggle='collapse']").forEach((el) => {
el.addEventListener("shown.bs.collapse", () => {
console.log("Accordion is triggered to show");
$.dataTable.tables({ visible: true }).columns.adjust();
})
});
});
Loading

0 comments on commit 809fab7

Please sign in to comment.