mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	| @@ -43,6 +43,16 @@ class NotificationEntryAdmin(admin.ModelAdmin): | ||||
|     list_display = ('key', 'uid', 'updated', ) | ||||
|  | ||||
|  | ||||
| class NotificationMessageAdmin(admin.ModelAdmin): | ||||
|  | ||||
|     list_display = ('age_human', 'user', 'category', 'name', 'read', 'target_object', 'source_object', ) | ||||
|  | ||||
|     list_filter = ('category', 'read', 'user', ) | ||||
|  | ||||
|     search_fields = ('name', 'category', 'message', ) | ||||
|  | ||||
|  | ||||
| admin.site.register(common.models.InvenTreeSetting, SettingsAdmin) | ||||
| admin.site.register(common.models.InvenTreeUserSetting, UserSettingsAdmin) | ||||
| admin.site.register(common.models.NotificationEntry, NotificationEntryAdmin) | ||||
| admin.site.register(common.models.NotificationMessage, NotificationMessageAdmin) | ||||
|   | ||||
| @@ -130,6 +130,57 @@ class UserSettingsDetail(generics.RetrieveUpdateAPIView): | ||||
|     ] | ||||
|  | ||||
|  | ||||
| class NotificationList(generics.ListAPIView): | ||||
|     queryset = common.models.NotificationMessage.objects.all() | ||||
|     serializer_class = common.serializers.NotificationMessageSerializer | ||||
|  | ||||
|     filter_backends = [ | ||||
|         DjangoFilterBackend, | ||||
|         filters.SearchFilter, | ||||
|         filters.OrderingFilter, | ||||
|     ] | ||||
|  | ||||
|     ordering_fields = [ | ||||
|         #'age',  # TODO enable ordering by age | ||||
|         'category', | ||||
|         'name', | ||||
|     ] | ||||
|  | ||||
|     search_fields = [ | ||||
|         'name', | ||||
|         'message', | ||||
|     ] | ||||
|  | ||||
|     def filter_queryset(self, queryset): | ||||
|         """ | ||||
|         Only list notifications which apply to the current user | ||||
|         """ | ||||
|  | ||||
|         try: | ||||
|             user = self.request.user | ||||
|         except AttributeError: | ||||
|             return common.models.NotificationMessage.objects.none() | ||||
|  | ||||
|         queryset = super().filter_queryset(queryset) | ||||
|         queryset = queryset.filter(user=user) | ||||
|         return queryset | ||||
|  | ||||
|  | ||||
| class NotificationDetail(generics.RetrieveDestroyAPIView): | ||||
|     """ | ||||
|     Detail view for an individual notification object | ||||
|  | ||||
|     - User can only view / delete their own notification objects | ||||
|     """ | ||||
|  | ||||
|     queryset = common.models.NotificationMessage.objects.all() | ||||
|     serializer_class = common.serializers.NotificationMessageSerializer | ||||
|  | ||||
|     permission_classes = [ | ||||
|         UserSettingsPermissions, | ||||
|     ] | ||||
|  | ||||
|  | ||||
| common_api_urls = [ | ||||
|  | ||||
|     # User settings | ||||
| @@ -148,6 +199,12 @@ common_api_urls = [ | ||||
|  | ||||
|         # Global Settings List | ||||
|         url(r'^.*$', GlobalSettingsList.as_view(), name='api-global-setting-list'), | ||||
|     ])) | ||||
|     ])), | ||||
|  | ||||
|     # Notifications | ||||
|     url(r'^notifications/', include([ | ||||
|         url(r'^(?P<pk>\d+)/', NotificationDetail.as_view(), name='api-notifications-detail'), | ||||
|         url(r'^.*$', NotificationList.as_view(), name='api-notifications-list'), | ||||
|     ])), | ||||
|  | ||||
| ] | ||||
|   | ||||
							
								
								
									
										33
									
								
								InvenTree/common/migrations/0013_notificationmessage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								InvenTree/common/migrations/0013_notificationmessage.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| # Generated by Django 3.2.5 on 2021-11-27 14:51 | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||
|         ('contenttypes', '0002_remove_content_type_name'), | ||||
|         ('common', '0012_notificationentry'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='NotificationMessage', | ||||
|             fields=[ | ||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||
|                 ('target_object_id', models.PositiveIntegerField()), | ||||
|                 ('source_object_id', models.PositiveIntegerField(blank=True, null=True)), | ||||
|                 ('category', models.CharField(max_length=250)), | ||||
|                 ('name', models.CharField(max_length=250)), | ||||
|                 ('message', models.CharField(blank=True, max_length=250, null=True)), | ||||
|                 ('creation', models.DateTimeField(auto_now=True)), | ||||
|                 ('read', models.BooleanField(default=False)), | ||||
|                 ('source_content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notification_source', to='contenttypes.contenttype')), | ||||
|                 ('target_content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_target', to='contenttypes.contenttype')), | ||||
|                 ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), | ||||
|             ], | ||||
|         ), | ||||
|     ] | ||||
| @@ -13,8 +13,13 @@ from datetime import datetime, timedelta | ||||
|  | ||||
| from django.db import models, transaction | ||||
| from django.contrib.auth.models import User, Group | ||||
| from django.contrib.contenttypes.fields import GenericForeignKey | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.db.utils import IntegrityError, OperationalError | ||||
| from django.conf import settings | ||||
| from django.urls import reverse | ||||
| from django.utils.timezone import now | ||||
| from django.contrib.humanize.templatetags.humanize import naturaltime | ||||
|  | ||||
| from djmoney.settings import CURRENCY_CHOICES | ||||
| from djmoney.contrib.exchange.models import convert_money | ||||
| @@ -1419,3 +1424,89 @@ class NotificationEntry(models.Model): | ||||
|         ) | ||||
|  | ||||
|         entry.save() | ||||
|  | ||||
|  | ||||
| class NotificationMessage(models.Model): | ||||
|     """ | ||||
|     A NotificationEntry records the last time a particular notifaction was sent out. | ||||
|  | ||||
|     It is recorded to ensure that notifications are not sent out "too often" to users. | ||||
|  | ||||
|     Attributes: | ||||
|     - key: A text entry describing the notification e.g. 'part.notify_low_stock' | ||||
|     - uid: An (optional) numerical ID for a particular instance | ||||
|     - date: The last time this notification was sent | ||||
|     """ | ||||
|  | ||||
|     # generic link to target | ||||
|     target_content_type = models.ForeignKey( | ||||
|         ContentType, | ||||
|         on_delete=models.CASCADE, | ||||
|         related_name='notification_target', | ||||
|     ) | ||||
|  | ||||
|     target_object_id = models.PositiveIntegerField() | ||||
|  | ||||
|     target_object = GenericForeignKey('target_content_type', 'target_object_id') | ||||
|  | ||||
|     # generic link to source | ||||
|     source_content_type = models.ForeignKey( | ||||
|         ContentType, | ||||
|         on_delete=models.SET_NULL, | ||||
|         related_name='notification_source', | ||||
|         null=True, | ||||
|         blank=True, | ||||
|     ) | ||||
|  | ||||
|     source_object_id = models.PositiveIntegerField( | ||||
|         null=True, | ||||
|         blank=True, | ||||
|     ) | ||||
|  | ||||
|     source_object = GenericForeignKey('source_content_type', 'source_object_id') | ||||
|  | ||||
|     # user that receives the notification | ||||
|     user = models.ForeignKey( | ||||
|         User, | ||||
|         on_delete=models.CASCADE, | ||||
|         verbose_name=_('User'), | ||||
|         help_text=_('User'), | ||||
|     ) | ||||
|  | ||||
|     category = models.CharField( | ||||
|         max_length=250, | ||||
|         blank=False, | ||||
|     ) | ||||
|  | ||||
|     name = models.CharField( | ||||
|         max_length=250, | ||||
|         blank=False, | ||||
|     ) | ||||
|  | ||||
|     message = models.CharField( | ||||
|         max_length=250, | ||||
|         blank=True, | ||||
|         null=True, | ||||
|     ) | ||||
|  | ||||
|     creation = models.DateTimeField( | ||||
|         auto_now=True, | ||||
|         null=False, | ||||
|     ) | ||||
|  | ||||
|     read = models.BooleanField( | ||||
|         default=False, | ||||
|     ) | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_api_url(): | ||||
|         return reverse('api-notifications-list') | ||||
|  | ||||
|     def age(self): | ||||
|         """age of the message in seconds""" | ||||
|         delta = now() - self.creation | ||||
|         return delta.seconds | ||||
|  | ||||
|     def age_human(self): | ||||
|         """humanized age""" | ||||
|         return naturaltime(self.creation) | ||||
|   | ||||
| @@ -9,7 +9,7 @@ from InvenTree.serializers import InvenTreeModelSerializer | ||||
|  | ||||
| from rest_framework import serializers | ||||
|  | ||||
| from common.models import InvenTreeSetting, InvenTreeUserSetting | ||||
| from common.models import InvenTreeSetting, InvenTreeUserSetting, NotificationMessage | ||||
|  | ||||
|  | ||||
| class SettingsSerializer(InvenTreeModelSerializer): | ||||
| @@ -95,3 +95,39 @@ class UserSettingsSerializer(SettingsSerializer): | ||||
|             'type', | ||||
|             'choices', | ||||
|         ] | ||||
|  | ||||
|  | ||||
| class NotificationMessageSerializer(SettingsSerializer): | ||||
|     """ | ||||
|     Serializer for the InvenTreeUserSetting model | ||||
|     """ | ||||
|  | ||||
|     #content_object = serializers.PrimaryKeyRelatedField(read_only=True) | ||||
|  | ||||
|     user = serializers.PrimaryKeyRelatedField(read_only=True) | ||||
|  | ||||
|     category = serializers.CharField(read_only=True) | ||||
|  | ||||
|     name = serializers.CharField(read_only=True) | ||||
|  | ||||
|     message = serializers.CharField(read_only=True) | ||||
|  | ||||
|     creation = serializers.CharField(read_only=True) | ||||
|  | ||||
|     age = serializers.IntegerField() | ||||
|  | ||||
|     age_human = serializers.CharField() | ||||
|  | ||||
|     class Meta: | ||||
|         model = NotificationMessage | ||||
|         fields = [ | ||||
|             'pk', | ||||
|             #'content_object', | ||||
|             'user', | ||||
|             'category', | ||||
|             'name', | ||||
|             'message', | ||||
|             'creation', | ||||
|             'age', | ||||
|             'age_human', | ||||
|         ] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user