Implement custom notification settings
This commit is contained in:
parent
4c463e88f2
commit
7ee698f457
|
@ -27,6 +27,7 @@ from core.views import (
|
|||
callbacks,
|
||||
hooks,
|
||||
limits,
|
||||
notifications,
|
||||
positions,
|
||||
profit,
|
||||
signals,
|
||||
|
@ -208,4 +209,9 @@ urlpatterns = [
|
|||
limits.TrendDirectionList.as_view(),
|
||||
name="trenddirections",
|
||||
),
|
||||
path(
|
||||
"notifications/<str:type>/update/",
|
||||
notifications.NotificationsUpdate.as_view(),
|
||||
name="notifications_update",
|
||||
),
|
||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
|
|
@ -3,7 +3,16 @@ from django.contrib.auth.forms import UserCreationForm
|
|||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.forms import ModelForm
|
||||
|
||||
from .models import Account, Hook, Signal, Strategy, Trade, TradingTime, User
|
||||
from .models import (
|
||||
Account,
|
||||
Hook,
|
||||
NotificationSettings,
|
||||
Signal,
|
||||
Strategy,
|
||||
Trade,
|
||||
TradingTime,
|
||||
User,
|
||||
)
|
||||
|
||||
# flake8: noqa: E501
|
||||
|
||||
|
@ -276,3 +285,16 @@ class TradingTimeForm(RestrictedFormMixin, ModelForm):
|
|||
"end_day": "The day of the week to stop trading.",
|
||||
"end_time": "The time of day to stop trading.",
|
||||
}
|
||||
|
||||
|
||||
class NotificationSettingsForm(RestrictedFormMixin, ModelForm):
|
||||
class Meta:
|
||||
model = NotificationSettings
|
||||
fields = (
|
||||
"ntfy_topic",
|
||||
"ntfy_url",
|
||||
)
|
||||
help_texts = {
|
||||
"ntfy_topic": "The topic to send notifications to.",
|
||||
"ntfy_url": "Custom NTFY server. Leave blank to use the default server.",
|
||||
}
|
||||
|
|
|
@ -592,12 +592,12 @@ def execute_strategy(callback, strategy, func):
|
|||
new_trade.save()
|
||||
else:
|
||||
info = new_trade.post()
|
||||
print("INFO", info)
|
||||
log.debug(f"Posted trade: {info}")
|
||||
|
||||
# Send notification with limited number of fields
|
||||
wanted_fields = ["requestID", "type", "symbol", "units", "reason"]
|
||||
sendmsg(
|
||||
user,
|
||||
", ".join([str(v) for k, v in info.items() if k in wanted_fields]),
|
||||
title=f"{direction} {amount_rounded} on {symbol}",
|
||||
)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
from core.util import logs
|
||||
|
||||
|
@ -8,7 +7,8 @@ NTFY_URL = "https://ntfy.sh"
|
|||
log = logs.get_logger(__name__)
|
||||
|
||||
|
||||
def sendmsg(msg, title=None, priority=None, tags=None):
|
||||
# Actual function to send a message to a topic
|
||||
def _sendmsg(msg, title=None, priority=None, tags=None, url=None, topic=None):
|
||||
headers = {"Title": "Fisk"}
|
||||
if title:
|
||||
headers["Title"] = title
|
||||
|
@ -17,7 +17,24 @@ def sendmsg(msg, title=None, priority=None, tags=None):
|
|||
if tags:
|
||||
headers["Tags"] = tags
|
||||
requests.post(
|
||||
f"{NTFY_URL}/{settings.NOTIFY_TOPIC}",
|
||||
f"{url}/{topic}",
|
||||
data=msg,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
|
||||
# Sendmsg helper to send a message to a user's notification settings
|
||||
def sendmsg(user, *args, **kwargs):
|
||||
notification_settings = user.get_notification_settings()
|
||||
if notification_settings.ntfy_url is None:
|
||||
url = NTFY_URL
|
||||
else:
|
||||
url = notification_settings.ntfy_url
|
||||
|
||||
if notification_settings.ntfy_topic is None:
|
||||
# No topic set, so don't send
|
||||
return
|
||||
else:
|
||||
topic = notification_settings.ntfy_topic
|
||||
|
||||
_sendmsg(*args, **kwargs, url=url, topic=topic)
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 4.1.3 on 2022-12-18 17:10
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0046_remove_hook_type_signal_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='NotificationSettings',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ntfy_topic', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('ntfy_url', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -97,6 +97,9 @@ class User(AbstractUser):
|
|||
plan_list = [plan.name for plan in self.plans.all()]
|
||||
return plan in plan_list
|
||||
|
||||
def get_notification_settings(self):
|
||||
return NotificationSettings.objects.get_or_create(user=self)[0]
|
||||
|
||||
|
||||
class Account(models.Model):
|
||||
EXCHANGE_CHOICES = (("alpaca", "Alpaca"), ("oanda", "OANDA"))
|
||||
|
@ -363,6 +366,15 @@ class Strategy(models.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
class NotificationSettings(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
ntfy_topic = models.CharField(max_length=255, null=True, blank=True)
|
||||
ntfy_url = models.CharField(max_length=255, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"Notification settings for {self.user}"
|
||||
|
||||
|
||||
# class Perms(models.Model):
|
||||
# class Meta:
|
||||
# permissions = (
|
||||
|
|
|
@ -272,6 +272,20 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">
|
||||
Account
|
||||
</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<a class="navbar-item" href="{% url 'two_factor:profile' %}">
|
||||
Security
|
||||
</a>
|
||||
<a class="navbar-item" href="{% url 'notifications_update' type='page' %}">
|
||||
Notifications
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if settings.STRIPE_ENABLED %}
|
||||
{% if user.is_authenticated %}
|
||||
|
@ -298,7 +312,6 @@
|
|||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<a class="button" href="{% url 'two_factor:profile' %}">Security</a>
|
||||
<a class="button" href="{% url 'logout' %}">Logout</a>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -343,9 +343,13 @@ class ObjectUpdate(RestrictedViewMixin, ObjectNameMixin, UpdateView):
|
|||
|
||||
model = None
|
||||
submit_url_name = None
|
||||
submit_url_args = ["type", "pk"]
|
||||
|
||||
request = None
|
||||
|
||||
# Whether pk is required in the get request
|
||||
pk_required = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.title = "Update " + self.context_object_name_singular
|
||||
|
@ -376,6 +380,7 @@ class ObjectUpdate(RestrictedViewMixin, ObjectNameMixin, UpdateView):
|
|||
if not type:
|
||||
return HttpResponseBadRequest("No type specified")
|
||||
if not pk:
|
||||
if self.pk_required:
|
||||
return HttpResponseBadRequest("No pk specified")
|
||||
if type not in self.allowed_types:
|
||||
return HttpResponseBadRequest("Invalid type specified")
|
||||
|
@ -385,7 +390,15 @@ class ObjectUpdate(RestrictedViewMixin, ObjectNameMixin, UpdateView):
|
|||
type = "modal"
|
||||
|
||||
self.object = self.get_object()
|
||||
submit_url = reverse(self.submit_url_name, kwargs={"type": type, "pk": pk})
|
||||
|
||||
submit_url_args = {}
|
||||
for arg in self.submit_url_args:
|
||||
if arg in locals():
|
||||
submit_url_args[arg] = locals()[arg]
|
||||
elif arg in kwargs:
|
||||
submit_url_args[arg] = kwargs[arg]
|
||||
submit_url = reverse(self.submit_url_name, kwargs=submit_url_args)
|
||||
|
||||
context = self.get_context_data()
|
||||
form = kwargs.get("form", None)
|
||||
if form:
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
from core.forms import NotificationSettingsForm
|
||||
from core.models import NotificationSettings
|
||||
from core.views import ObjectUpdate
|
||||
|
||||
|
||||
# Notifications - we create a new notification settings object if there isn't one
|
||||
# Hence, there is only an update view, not a create view.
|
||||
class NotificationsUpdate(LoginRequiredMixin, ObjectUpdate):
|
||||
model = NotificationSettings
|
||||
form_class = NotificationSettingsForm
|
||||
|
||||
# list_url_name = "notifications"
|
||||
# list_url_args = ["type"]
|
||||
|
||||
submit_url_name = "notifications_update"
|
||||
submit_url_args = ["type"]
|
||||
|
||||
pk_required = False
|
||||
|
||||
def get_object(self, **kwargs):
|
||||
notification_settings, _ = NotificationSettings.objects.get_or_create(
|
||||
user=self.request.user
|
||||
)
|
||||
return notification_settings
|
Loading…
Reference in New Issue