extensions-website/abuse/models.py
Dalai Felinto 713163df61 Report a Review option
Note:
* Right now I kept the same Reasons and Versions field we use when
  reporting extensions.
* Also I still included the Blender version.

While the Blender Version is relevant (I think), we may want a different
set of reasons for this.

Also, in theory the system was made so users could be reported as well.
They are not at the moment.

Possible future improvements:
* System to report users as well.
* Menu entry Report Queue for moderators.
* Custom Reason set for reviews.
2024-03-01 16:24:44 +01:00

111 lines
4.0 KiB
Python

from django import forms
from django.contrib.auth import get_user_model
from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
from django.core.validators import validate_ipv46_address
from django.db import models
from django.urls import reverse
from extended_choices import Choices
from geoip2.errors import GeoIP2Error
from constants.base import ABUSE_TYPE, ABUSE_TYPE_EXTENSION, ABUSE_TYPE_REVIEW
from common.model_mixins import CreatedModifiedMixin, TrackChangesMixin, SoftDeleteMixin
import extensions.fields
User = get_user_model()
class AbuseReport(CreatedModifiedMixin, TrackChangesMixin, SoftDeleteMixin, models.Model):
TYPE = ABUSE_TYPE
REASONS = Choices(
('OTHER', 127, 'Other'),
('DAMAGE', 1, 'Damages computer and/or data'),
('SPAM', 2, 'Creates spam or advertising'),
('BROKEN', 3, "Doesn't work, breaks Blender, or slows it down"),
('POLICY', 4, 'Hateful, violent, or illegal content'),
('DECEPTIVE', 5, "Pretends to be something it's not"),
)
STATUSES = Choices(
('UNTRIAGED', 1, 'Untriaged'),
('VALID', 2, 'Valid'),
('SUSPICIOUS', 3, 'Suspicious'),
)
# NULL if the reporter is anonymous.
reporter = models.ForeignKey(
User,
null=True,
blank=True,
related_name='abuse_reported',
on_delete=models.SET_NULL,
)
# An abuse report can be for an extension or a user.
# If user is set then extension should be null.
# If extension is null then user should be set.
user = models.ForeignKey(
User, null=True, related_name='abuse_reports', on_delete=models.SET_NULL
)
extension = models.ForeignKey(
'extensions.Extension', blank=True, null=True, on_delete=models.CASCADE
)
extension_version = extensions.fields.VersionStringField(max_length=64, null=True, blank=True)
rating = models.ForeignKey('ratings.Rating', blank=True, null=True, on_delete=models.CASCADE)
message = models.TextField(blank=True)
reason = models.PositiveSmallIntegerField(
default=REASONS[0][1], choices=REASONS.choices, blank=False, null=False
)
version = extensions.fields.VersionStringField(
max_length=64,
null=True,
blank=True,
help_text=('Version of Blender affected by this report, if applicable.'),
)
type = models.PositiveSmallIntegerField(
default=ABUSE_TYPE_EXTENSION, choices=TYPE.choices, blank=False, null=False
)
status = models.PositiveSmallIntegerField(default=STATUSES.UNTRIAGED, choices=STATUSES.choices)
@classmethod
def lookup_country_code_from_ip(cls, ip):
try:
# Early check to avoid initializing GeoIP2 on invalid addresses
if not ip:
raise forms.ValidationError('No IP')
validate_ipv46_address(ip)
geoip = GeoIP2()
value = geoip.country_code(ip)
# Annoyingly, we have to catch both django's GeoIP2Exception (setup
# issue) and geoip2's GeoIP2Error (lookup issue)
except (forms.ValidationError, GeoIP2Exception, GeoIP2Error):
value = ''
return value
@property
def name(self) -> str:
return self.extension.name if self.extension else self.user
def __str__(self) -> str:
return f'Abuse Report for {self.type} {self.name}'
@classmethod
def exists(cls, user_id: int, extension_id: int, rating_id: int = None) -> bool:
if rating_id is None:
return cls.objects.filter(
reporter_id=user_id, extension_id=extension_id, type=ABUSE_TYPE_EXTENSION
).exists()
return cls.objects.filter(
reporter_id=user_id,
extension_id=extension_id,
rating_id=rating_id,
type=ABUSE_TYPE_REVIEW,
).exists()
def get_absolute_url(self):
return reverse('abuse:view-report', args=[self.pk])
def get_admin_url(self):
return reverse('admin:abuse_abusereport_change', args=[self.pk])