Compare commits
10 Commits
3eb7dbe984
...
8de5f162eb
Author | SHA1 | Date | |
---|---|---|---|
8de5f162eb | |||
dc3859d589 | |||
b4f52c7876 | |||
885622e581 | |||
a42a3fa177 | |||
27541c6fb6 | |||
14547ad621 | |||
8d2d80c30e | |||
e6f5a42d15 | |||
052e42f76a |
@ -1,7 +1,7 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from .models import AdoptionNotice, Animal, Image, ReportAdoptionNotice, ReportComment, ModerationAction, User, Species, \
|
from .models import AdoptionNotice, Animal, Image, ReportAdoptionNotice, ReportComment, ModerationAction, User, Species, \
|
||||||
Comment
|
Comment, SexChoicesWithAll
|
||||||
from django_registration.forms import RegistrationForm
|
from django_registration.forms import RegistrationForm
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Submit, Layout, Fieldset, HTML, Row, Column, Field, Hidden
|
from crispy_forms.layout import Submit, Layout, Fieldset, HTML, Row, Column, Field, Hidden
|
||||||
@ -66,7 +66,8 @@ class AdoptionNoticeForm(forms.ModelForm):
|
|||||||
class AdoptionNoticeFormWithDateWidget(AdoptionNoticeForm):
|
class AdoptionNoticeFormWithDateWidget(AdoptionNoticeForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AdoptionNotice
|
model = AdoptionNotice
|
||||||
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string", "organization"]
|
fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string",
|
||||||
|
"organization"]
|
||||||
widgets = {
|
widgets = {
|
||||||
'searching_since': DateInput(),
|
'searching_since': DateInput(),
|
||||||
}
|
}
|
||||||
@ -185,5 +186,7 @@ def _get_distances():
|
|||||||
|
|
||||||
|
|
||||||
class AdoptionNoticeSearchForm(forms.Form):
|
class AdoptionNoticeSearchForm(forms.Form):
|
||||||
location = forms.CharField(max_length=20, label=_("Stadt"))
|
location = forms.CharField(max_length=20, label=_("Stadt"), required=False)
|
||||||
max_distance = forms.ChoiceField(choices=_get_distances, label=_("Max. Distanz"))
|
max_distance = forms.ChoiceField(choices=_get_distances, label=_("Max. Distanz"))
|
||||||
|
sex = forms.ChoiceField(choices=SexChoicesWithAll, label=_("Geschlecht"), required=False,
|
||||||
|
initial=SexChoicesWithAll.ALL)
|
||||||
|
18
src/fellchensammlung/migrations/0024_alter_animal_sex.py
Normal file
18
src/fellchensammlung/migrations/0024_alter_animal_sex.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.1 on 2024-11-21 19:34
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fellchensammlung', '0023_user_email_notifications'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='animal',
|
||||||
|
name='sex',
|
||||||
|
field=models.CharField(choices=[('M_N', 'neutered male'), ('M', 'male'), ('F_N', 'neutered female'), ('F', 'female'), ('I', 'intersex')], max_length=20),
|
||||||
|
),
|
||||||
|
]
|
18
src/fellchensammlung/migrations/0025_alter_animal_sex.py
Normal file
18
src/fellchensammlung/migrations/0025_alter_animal_sex.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.1 on 2024-11-21 19:41
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fellchensammlung', '0024_alter_animal_sex'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='animal',
|
||||||
|
name='sex',
|
||||||
|
field=models.CharField(choices=[('F', 'Weiblich'), ('M', 'Männlich'), ('M_N', 'Männlich, kastriert'), ('F_N', 'Weiblich Kastriert'), ('I', 'Intersex')], max_length=20),
|
||||||
|
),
|
||||||
|
]
|
18
src/fellchensammlung/migrations/0026_alter_animal_sex.py
Normal file
18
src/fellchensammlung/migrations/0026_alter_animal_sex.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.1 on 2024-11-21 21:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fellchensammlung', '0025_alter_animal_sex'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='animal',
|
||||||
|
name='sex',
|
||||||
|
field=models.CharField(choices=[('F', 'Weiblich'), ('M', 'Männlich'), ('M_N', 'Männlich, kastriert'), ('F_N', 'Weiblich, kastriert'), ('I', 'Intergeschlechtlich')], max_length=20),
|
||||||
|
),
|
||||||
|
]
|
@ -240,15 +240,18 @@ class AdoptionNotice(models.Model):
|
|||||||
def sexes(self):
|
def sexes(self):
|
||||||
sexes = set()
|
sexes = set()
|
||||||
for animal in self.animals:
|
for animal in self.animals:
|
||||||
sexes.update(animal.sex)
|
sexes.add(animal.sex)
|
||||||
return sexes
|
return sexes
|
||||||
|
|
||||||
def sex_code(self):
|
def sex_code(self):
|
||||||
|
# Treat Intersex as mixed in order to increase their visibility
|
||||||
if len(self.sexes) > 1:
|
if len(self.sexes) > 1:
|
||||||
return "mixed"
|
return "mixed"
|
||||||
elif self.sexes.pop() == Animal.MALE:
|
|
||||||
|
sex = self.sexes.pop()
|
||||||
|
if sex == SexChoices.MALE:
|
||||||
return "male"
|
return "male"
|
||||||
elif self.sexes.pop() == Animal.FEMALE:
|
elif sex == SexChoices.FEMALE:
|
||||||
return "female"
|
return "female"
|
||||||
else:
|
else:
|
||||||
return "mixed"
|
return "mixed"
|
||||||
@ -348,18 +351,26 @@ class AdoptionNotice(models.Model):
|
|||||||
def set_closed(self):
|
def set_closed(self):
|
||||||
self.last_checked = timezone.now()
|
self.last_checked = timezone.now()
|
||||||
self.adoptionnoticestatus.set_closed()
|
self.adoptionnoticestatus.set_closed()
|
||||||
|
self.save()
|
||||||
|
|
||||||
def set_active(self):
|
def set_active(self):
|
||||||
self.last_checked = timezone.now()
|
self.last_checked = timezone.now()
|
||||||
if not hasattr(self, 'adoptionnoticestatus'):
|
if not hasattr(self, 'adoptionnoticestatus'):
|
||||||
AdoptionNoticeStatus.create_other(self)
|
AdoptionNoticeStatus.create_other(self)
|
||||||
self.adoptionnoticestatus.set_active()
|
self.adoptionnoticestatus.set_active()
|
||||||
|
self.save()
|
||||||
|
|
||||||
def set_unchecked(self):
|
def set_unchecked(self):
|
||||||
self.last_checked = timezone.now()
|
self.last_checked = timezone.now()
|
||||||
if not hasattr(self, 'adoptionnoticestatus'):
|
if not hasattr(self, 'adoptionnoticestatus'):
|
||||||
AdoptionNoticeStatus.create_other(self)
|
AdoptionNoticeStatus.create_other(self)
|
||||||
self.adoptionnoticestatus.set_unchecked()
|
self.adoptionnoticestatus.set_unchecked()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
for subscription in self.get_subscriptions():
|
||||||
|
notification_title = _("Vermittlung deaktiviert:") + f" {self}"
|
||||||
|
text = _("Die folgende Vermittlung wurde deaktiviert: ") + f"{self.name}, {self.get_absolute_url()}"
|
||||||
|
BaseNotification.objects.create(user=subscription.owner, text=text, title=notification_title)
|
||||||
|
|
||||||
|
|
||||||
class AdoptionNoticeStatus(models.Model):
|
class AdoptionNoticeStatus(models.Model):
|
||||||
@ -457,24 +468,33 @@ class AdoptionNoticeStatus(models.Model):
|
|||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
class Animal(models.Model):
|
class SexChoices(models.TextChoices):
|
||||||
MALE_NEUTERED = "M_N"
|
FEMALE = "F", _("Weiblich")
|
||||||
MALE = "M"
|
MALE = "M", _("Männlich")
|
||||||
FEMALE_NEUTERED = "F_N"
|
MALE_NEUTERED = "M_N", _("Männlich, kastriert")
|
||||||
FEMALE = "F"
|
FEMALE_NEUTERED = "F_N", _("Weiblich, kastriert")
|
||||||
SEX_CHOICES = {
|
INTER = "I", _("Intergeschlechtlich")
|
||||||
MALE_NEUTERED: "neutered male",
|
|
||||||
MALE: "male",
|
|
||||||
FEMALE_NEUTERED: "neutered female",
|
|
||||||
FEMALE: "female",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
class SexChoicesWithAll(models.TextChoices):
|
||||||
|
FEMALE = "F", _("Weiblich")
|
||||||
|
MALE = "M", _("Männlich")
|
||||||
|
MALE_NEUTERED = "M_N", _("Männlich, kastriert")
|
||||||
|
FEMALE_NEUTERED = "F_N", _("Weiblich Kastriert")
|
||||||
|
INTER = "I", _("Intergeschlechtlich")
|
||||||
|
ALL = "A", _("Alle")
|
||||||
|
|
||||||
|
|
||||||
|
class Animal(models.Model):
|
||||||
date_of_birth = models.DateField(verbose_name=_('Geburtsdatum'))
|
date_of_birth = models.DateField(verbose_name=_('Geburtsdatum'))
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung'))
|
description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung'))
|
||||||
species = models.ForeignKey(Species, on_delete=models.PROTECT)
|
species = models.ForeignKey(Species, on_delete=models.PROTECT)
|
||||||
photos = models.ManyToManyField(Image, blank=True)
|
photos = models.ManyToManyField(Image, blank=True)
|
||||||
sex = models.CharField(max_length=20, choices=SEX_CHOICES, )
|
sex = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
choices=SexChoices.choices,
|
||||||
|
)
|
||||||
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE)
|
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE)
|
||||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
@ -16,7 +16,7 @@ from notfellchen import settings
|
|||||||
from fellchensammlung import logger
|
from fellchensammlung import logger
|
||||||
from .models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \
|
from .models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \
|
||||||
User, Location, AdoptionNoticeStatus, Subscriptions, CommentNotification, BaseNotification, RescueOrganization, \
|
User, Location, AdoptionNoticeStatus, Subscriptions, CommentNotification, BaseNotification, RescueOrganization, \
|
||||||
Species, Log, Timestamp, TrustLevel
|
Species, Log, Timestamp, TrustLevel, SexChoicesWithAll
|
||||||
from .forms import AdoptionNoticeForm, AdoptionNoticeFormWithDateWidget, ImageForm, ReportAdoptionNoticeForm, \
|
from .forms import AdoptionNoticeForm, AdoptionNoticeFormWithDateWidget, ImageForm, ReportAdoptionNoticeForm, \
|
||||||
CommentForm, ReportCommentForm, AnimalForm, \
|
CommentForm, ReportCommentForm, AnimalForm, \
|
||||||
AdoptionNoticeSearchForm, AnimalFormWithDateWidget, AdoptionNoticeFormWithDateWidgetAutoAnimal
|
AdoptionNoticeSearchForm, AnimalFormWithDateWidget, AdoptionNoticeFormWithDateWidgetAutoAnimal
|
||||||
@ -169,27 +169,34 @@ def animal_detail(request, animal_id):
|
|||||||
|
|
||||||
def search(request):
|
def search(request):
|
||||||
place_not_found = None
|
place_not_found = None
|
||||||
|
latest_adoption_list = AdoptionNotice.objects.order_by("-created_at")
|
||||||
|
active_adoptions = [adoption for adoption in latest_adoption_list if adoption.is_active]
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
latest_adoption_list = AdoptionNotice.objects.order_by("-created_at")
|
|
||||||
active_adoptions = [adoption for adoption in latest_adoption_list if adoption.is_active]
|
sex = request.POST.get("sex")
|
||||||
|
if sex != SexChoicesWithAll.ALL:
|
||||||
|
active_adoptions = [adoption for adoption in active_adoptions if sex in adoption.sexes]
|
||||||
|
|
||||||
search_form = AdoptionNoticeSearchForm(request.POST)
|
search_form = AdoptionNoticeSearchForm(request.POST)
|
||||||
max_distance = int(request.POST.get('max_distance'))
|
search_form.is_valid()
|
||||||
if max_distance == "":
|
if search_form.cleaned_data["location"] == "":
|
||||||
max_distance = None
|
|
||||||
geo_api = GeoAPI()
|
|
||||||
search_position = geo_api.get_coordinates_from_query(request.POST['location'])
|
|
||||||
if search_position is None:
|
|
||||||
place_not_found = True
|
|
||||||
adoption_notices_in_distance = active_adoptions
|
adoption_notices_in_distance = active_adoptions
|
||||||
|
place_not_found = False
|
||||||
else:
|
else:
|
||||||
adoption_notices_in_distance = [a for a in active_adoptions if a.in_distance(search_position, max_distance)]
|
max_distance = int(request.POST.get('max_distance'))
|
||||||
|
if max_distance == "":
|
||||||
|
max_distance = None
|
||||||
|
geo_api = GeoAPI()
|
||||||
|
search_position = geo_api.get_coordinates_from_query(request.POST['location'])
|
||||||
|
if search_position is None:
|
||||||
|
place_not_found = True
|
||||||
|
adoption_notices_in_distance = active_adoptions
|
||||||
|
else:
|
||||||
|
adoption_notices_in_distance = [a for a in active_adoptions if a.in_distance(search_position, max_distance)]
|
||||||
|
|
||||||
context = {"adoption_notices": adoption_notices_in_distance, "search_form": search_form,
|
context = {"adoption_notices": adoption_notices_in_distance, "search_form": search_form,
|
||||||
"place_not_found": place_not_found}
|
"place_not_found": place_not_found}
|
||||||
else:
|
else:
|
||||||
latest_adoption_list = AdoptionNotice.objects.order_by("-created_at")
|
|
||||||
active_adoptions = [adoption for adoption in latest_adoption_list if adoption.is_active]
|
|
||||||
search_form = AdoptionNoticeSearchForm()
|
search_form = AdoptionNoticeSearchForm()
|
||||||
context = {"adoption_notices": active_adoptions, "search_form": search_form}
|
context = {"adoption_notices": active_adoptions, "search_form": search_form}
|
||||||
return render(request, 'fellchensammlung/search.html', context=context)
|
return render(request, 'fellchensammlung/search.html', context=context)
|
||||||
|
@ -120,7 +120,7 @@ class SearchTest(TestCase):
|
|||||||
self.assertNotContains(response, "TestAdoption2")
|
self.assertNotContains(response, "TestAdoption2")
|
||||||
|
|
||||||
def test_plz_search(self):
|
def test_plz_search(self):
|
||||||
response = self.client.post(reverse('search'), {"max_distance": 100, "location": "Berlin"})
|
response = self.client.post(reverse('search'), {"max_distance": 100, "location": "Berlin", "sex": "A"})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertContains(response, "TestAdoption1")
|
self.assertContains(response, "TestAdoption1")
|
||||||
self.assertNotContains(response, "TestAdoption3")
|
self.assertNotContains(response, "TestAdoption3")
|
||||||
|
Loading…
Reference in New Issue
Block a user