Skip to content

Commit

Permalink
Notification feature (#312)
Browse files Browse the repository at this point in the history
* Create notifications folder in zubhub_backend/zubhub, begin implementing test functions

* Move send_whatsapp function to adapters.py, create task in tasks.py, create send-notification path for testing, test get method in views

* Create notifications folder in zubhub_backend/zubhub, begin implementing test functions

* Move send_whatsapp function to adapters.py, create task in tasks.py, create send-notification path for testing, test get method in views

* Use whatsapp phone in adapter

* Fix whatsapp message sending

* Fix bug in send_whatsapp function in adapters.py

* Fix settings name

* Notification Model and Endpoints (#286)

* initial commit

* fix namings

* Add update and delete requests

* Add boilerplate stuff

* finish all endpoints

* Add source and preferred contact

* modifications to notification endpoints

* fix notification list view

* hyphens

* address merge conflict

* Update start

Co-authored-by: Zora Zhang <[email protected]>
Co-authored-by: Grace Zhang <[email protected]>
Co-authored-by: Aditya Jain <[email protected]>
Co-authored-by: Zora Zhang <[email protected]>

* Add notification button components (#302)

* Add notification button components

* Fix popper positioning

* Add popper arrow and inner content

* Give dropdown box shadow

* Add mobile view

* Fix box shadow on arrow for dropdown panel

* Fix coloring of unread notifications text

* Add push notification util function and use CreatorSerializer (#316)

* Add push notification util function and use CreatorSerializer for notification serializer

* Add notification type field to notification model

* Add type to notification serializer and add migration

* Remove references to message property of notification

* Add viewed to notification serializer

* Use creator minimal serializer

* Add new link property to notification model

* Notification Helper Function (#306)

* initial setup

* add setting

* fix the way to get setting

* commit

* finished and tested function

* add original code back in

* modify function to send mass text/email

* Add channel verification

Co-authored-by: Zora Zhang <[email protected]>
Co-authored-by: Aditya Jain <[email protected]>

* Fix migrations

* Add several functions and maps for notifications logic (#324)

* Add several functions and maps for notifications logic, not working

* Finish notification sending function

* Remove redundant check for valid notification channel for notification type

* Send notifications when notification events occur

* Recommend unused function

* Add throttle classes back

* Fix file extensions

* Fix notification template prefix

* Add message field to notification model to support notifications that mention project names

* Properly create migrations and use char field

* Use name instead of label for django integer choice class

* Change notification link attribute to be a char field

* Fix email templates for notifications

* Notifications panel loading notifications complete (#341)

* Notifications panel loading notifications complete

* Use artifical loading for pages after 1

* Change padding

* Add comment for recent notification threshold

* Remove console log

* Format

* Remove unncessary space

* Reset scroll on notification tab change

* Add notification grouping code (#351)

* Add notification grouping code

* Use set to determine valid grouped notification types

* Actually make the grouping work

* Persist context for pushing notifications

* Remove prints

* Fix django db warning'

* Return notification in all branch paths

* No need to return notification

* Notification Component for Notification Panel (#311)

* Outline for Notification components and stylings

* Testing notification

* Complete majority of stylings for notification component, add dummy component and props to PageWrapper.jsx

* Complete stylings for notification component, fix hovers!

* Make small style changes to notification component

* Begin connecting to backend using realistic parameters, not clean or very functional yet

* Begin adapting notification panel to hold notification components

* Finish stylings for notification component and message rendering

* Finish up API methods to updated viewed notification, add token constant to component

* Add logic for rendering notification timestamp, redirect link, and notification message

* Clean up and reduce code, remove redunant or unused sections

* Move renderTimeAgo and getNotification to new NotificationScripts.js file, simplify logic in scripts, remove viewComment from userActions.js, call API.viewNotification directly in component

* Add proper date formatting using dFormatter, string truncation for notification message

* Add double image design to nofications, add project titles to views, fix small format issue in comment template

* Finish up issue, update notification templates for consitency, fix bug in project details useEffect

* Update clapped  web_many message to not include strong tags

* Update web_many message templates to include project name

* Fix url

* Minor fixes, add mobile width dependency, update getMessage() to get consistent character limits, remove mock data from PageWrapper

Co-authored-by: Aditya Jain <[email protected]>

* Notification feature fix (#429)

* Fix most notification feature bugs

* Fix app bar on top of other elements

* Fix panel zindex

* Fix panel loading

* Fix comment notif

Co-authored-by: Aditya Jain <[email protected]>

* Add quotes to template

* linearize migrations

* Fix import

* Add project to template

* Styling fixes

* Delete .gitignore

Co-authored-by: saxytony <[email protected]>
Co-authored-by: AndrewLester <[email protected]>
Co-authored-by: Zora Zhang <[email protected]>
Co-authored-by: Zora Zhang <[email protected]>
Co-authored-by: Grace Zhang <[email protected]>
Co-authored-by: Zora Zhang <[email protected]>
Co-authored-by: saxytony <[email protected]>
  • Loading branch information
8 people authored and NdibeRaymond committed Jun 5, 2022
1 parent 0e72590 commit eaafc5c
Show file tree
Hide file tree
Showing 68 changed files with 1,507 additions and 120 deletions.
2 changes: 1 addition & 1 deletion zubhub_backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,4 @@ destroy: ## Remove stopped containers, networks not currently in use, images no
.PHONY: destroy

.env: ## Generate a .env file for local development
bash ./compose/make_backend_env.sh ./.env
bash ./compose/make_backend_env.sh ./.env
1 change: 1 addition & 0 deletions zubhub_backend/compose/celery/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,4 @@ vine>=1.3.0
watchdog>=0.10.2
wcwidth>=0.2.5
whitenoise>=4.1.4
django-extensions>=1.0.0
11 changes: 6 additions & 5 deletions zubhub_backend/compose/web/dev/start
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ set -o nounset


python /zubhub_backend/zubhub/manage.py collectstatic --noinput
# python /zubhub_backend/zubhub/manage.py makemigrations zubhub
# python /zubhub_backend/zubhub/manage.py makemigrations creators
# python /zubhub_backend/zubhub/manage.py makemigrations projects
# python /zubhub_backend/zubhub/manage.py migrate
#python /zubhub_backend/zubhub/manage.py makemigrations zubhub
#python /zubhub_backend/zubhub/manage.py makemigrations creators
#python /zubhub_backend/zubhub/manage.py makemigrations projects
#python /zubhub_backend/zubhub/manage.py makemigrations notifications
#python /zubhub_backend/zubhub/manage.py migrate

python /zubhub_backend/zubhub/manage.py createcachetable

Expand All @@ -20,4 +21,4 @@ python /zubhub_backend/zubhub/manage.py populate_categories
python /zubhub_backend/zubhub/manage.py populate_initial_creator_tags

exec /usr/local/bin/gunicorn zubhub.wsgi --reload --threads=3 --timeout 155 --bind 0.0.0.0:8000 \
--access-logfile - --error-logfile - --chdir /zubhub_backend/zubhub
--access-logfile - --error-logfile - --chdir /zubhub_backend/zubhub
1 change: 1 addition & 0 deletions zubhub_backend/zubhub/APIS/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
path('rest-auth/registration/', include('rest_auth.registration.urls')),
path('creators/', include('creators.urls', namespace="creators")),
path('projects/', include('projects.urls', namespace="projects")),
path('notifications/', include('notifications.urls', namespace="notifications")),
path('upload-file/', UploadFileAPIView, name="upload_file"),
path('delete-file/', DeleteFileAPIView, name="delete_file"),
path('upload-file-to-local/', UploadFileToLocalAPIView,
Expand Down
64 changes: 38 additions & 26 deletions zubhub_backend/zubhub/creators/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,23 @@ def confirm_group_invite(self, request, creator, creatorgroup):
creatorgroup.members.add(creator)
creatorgroup.save()

def get_whatsapp_from_phone(self):
return settings.DEFAULT_WHATSAPP_FROM_PHONE

def get_from_phone(self):
"""
This is a hook that can be overridden to programatically
set the 'from' phone number for sending phone texts messages
"""
return settings.DEFAULT_FROM_PHONE

def render_text(self, template_name, phone, context):
def render_text(self, template_name, phone, context, from_phone=None):
"""
Renders a text to `text`. `template_prefix` identifies the
text that is to be sent, e.g. "account/phone/phone_confirmation"
"""

from_phone = self.get_from_phone()
if from_phone is None:
from_phone = self.get_from_phone()

try:
body = render_to_string(
Expand All @@ -97,6 +100,14 @@ def send_text(self, template_name, phone, context):
text = self.render_text(template_name, phone, context)
client.messages.create(**text)

def send_whatsapp(self, template_name, phone, context):
whatsapp_phone = "whatsapp:" + phone
client = Client(settings.TWILIO_ACCOUNT_SID,
settings.TWILIO_AUTH_TOKEN)
text = self.render_text(template_name, whatsapp_phone, context,
from_phone=self.get_whatsapp_from_phone())
client.messages.create(**text)

def send_confirmation_text(self, request, phoneconfirmation, signup):

ctx = {
Expand Down Expand Up @@ -140,31 +151,32 @@ def send_group_invite_mail(self, group_invite_confirmation):
self.send_mail(
email_template, group_invite_confirmation.creator.email, ctx)

def send_mass_email(self, template_prefix, contexts):
if settings.ENVIRONMENT == "production" and not settings.DEBUG:
def send_mass_email(self, template_prefix, contexts, full_template=False):
if not full_template:
template_prefix = "projects/email/" + template_prefix
connection = get_connection(
username=None, password=None, fail_silently=False)
messages = []
for ctx in contexts:
msg = self.render_mail(template_prefix, ctx["email"], ctx)
messages.append(msg)

return connection.send_messages(messages)

def send_mass_text(self, template_prefix, contexts):
if settings.ENVIRONMENT == "production" and not settings.DEBUG:
connection = get_connection(
username=None, password=None, fail_silently=False)
messages = []
for ctx in contexts:
msg = self.render_mail(template_prefix, ctx["email"], ctx)
messages.append(msg)

return connection.send_messages(messages)

def send_mass_text(self, template_prefix, contexts, full_template=False):
template_name = template_prefix
if not full_template:
template_name = "projects/phone/" + template_prefix + "_message.txt"
client = Client(settings.TWILIO_ACCOUNT_SID,
settings.TWILIO_AUTH_TOKEN)
client = Client(settings.TWILIO_ACCOUNT_SID,
settings.TWILIO_AUTH_TOKEN)

rendered_text = self.render_text(
template_name, contexts[0]["phone"], contexts[0])
rendered_text = self.render_text(
template_name, contexts[0]["phone"], contexts[0])

bindings = list(map(lambda context: json.dumps(
{'binding_type': 'sms', 'address': context["phone"]}), contexts))
bindings = list(map(lambda context: json.dumps(
{'binding_type': 'sms', 'address': context["phone"]}), contexts))

client.notify.services(settings.TWILIO_NOTIFY_SERVICE_SID).notifications.create(
to_binding=bindings,
body=rendered_text["body"]
)
client.notify.services(settings.TWILIO_NOTIFY_SERVICE_SID).notifications.create(
to_binding=bindings,
body=rendered_text["body"]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1 on 2022-02-22 02:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('creators', '0007_auto_20220308_1140'),
]

operations = [
migrations.AddField(
model_name='setting',
name='contact',
field=models.PositiveSmallIntegerField(blank=True, choices=[(1, 'WHATSAPP'), (2, 'EMAIL'), (3, 'SMS'), (4, 'WEB')], default=4, null=True),
),
]
32 changes: 32 additions & 0 deletions zubhub_backend/zubhub/creators/model_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from django.core.exceptions import FieldDoesNotExist

from django.contrib.auth import get_user_model


def user_field(user, field, *args):
"""
Gets or sets (optional) user model fields. No-op if fields do not exist.
"""
if not field:
return
User = get_user_model()
try:
field_meta = User._meta.get_field(field)
max_length = field_meta.max_length
except FieldDoesNotExist:
if not hasattr(user, field):
return
max_length = None
if args:
# Setter
v = args[0]
if v:
v = v[0:max_length]
setattr(user, field, v)
else:
# Getter
return getattr(user, field)


def user_phone(user, *args):
return user_field(user, "phone", *args)
17 changes: 16 additions & 1 deletion zubhub_backend/zubhub/creators/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from django.contrib.postgres.indexes import GinIndex

from .managers import PhoneNumberManager
from .utils import user_phone
from .model_utils import user_phone

try:
from allauth.account import app_settings as allauth_settings
Expand Down Expand Up @@ -80,9 +80,24 @@ def save(self, *args, **kwargs):


class Setting(models.Model):
WHATSAPP = 1
EMAIL = 2
SMS = 3
WEB = 4

CONTACT_CHOICES = (
(WHATSAPP, 'WHATSAPP'),
(EMAIL, 'EMAIL'),
(SMS, 'SMS'),
(WEB, 'WEB')
)

creator = models.OneToOneField(
Creator, on_delete=models.CASCADE, primary_key=True)
subscribe = models.BooleanField(blank=True, default=False)
contact = models.PositiveSmallIntegerField(
choices=CONTACT_CHOICES, blank=True, null=True, default=SMS
)

def __str__(self):
return self.creator.username
Expand Down
20 changes: 16 additions & 4 deletions zubhub_backend/zubhub/creators/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from celery import shared_task
import requests
from zubhub.utils import upload_file_to_media_server
from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

try:
from allauth.account.adapter import get_adapter
Expand All @@ -21,19 +24,28 @@ def send_text(self, phone, template_name, ctx):
uniform(2, 4) ** self.request.retries))


@shared_task(name="creators.tasks.send_whatsapp", bind=True, acks_late=True, max_retries=10)
def send_whatsapp(self, phone, template_name, ctx):
try:
get_adapter().send_whatsapp(template_name, phone, ctx)
except Exception as e:
raise self.retry(exc=e, countdown=int(
uniform(2, 4) ** self.request.retries))


@shared_task(name="creators.tasks.send_mass_email", bind=True, acks_late=True, max_retries=10)
def send_mass_email(self, template_name, ctxs):
def send_mass_email(self, template_name, ctxs, full_template=False):
try:
get_adapter().send_mass_email(template_name, ctxs)
get_adapter().send_mass_email(template_name, ctxs, full_template)
except Exception as e:
raise self.retry(exc=e, countdown=int(
uniform(2, 4) ** self.request.retries))


@shared_task(name="creators.tasks.send_mass_text", bind=True, acks_late=True, max_retries=10)
def send_mass_text(self, template_name, ctxs):
def send_mass_text(self, template_name, ctxs, full_template=False):
try:
get_adapter().send_mass_text(template_name, ctxs)
get_adapter().send_mass_text(template_name, ctxs, full_template)
except Exception as e:
raise self.retry(exc=e, countdown=int(
uniform(2, 4) ** self.request.retries))
Expand Down
Loading

0 comments on commit eaafc5c

Please sign in to comment.