feat: Auto-add location to adoption notice
This commit is contained in:
		@@ -40,6 +40,7 @@ class AdoptionNoticeForm(forms.ModelForm):
 | 
			
		||||
                'name',
 | 
			
		||||
                'group_only',
 | 
			
		||||
                'searching_since',
 | 
			
		||||
                'location_string',
 | 
			
		||||
                'description',
 | 
			
		||||
                'further_information',
 | 
			
		||||
            ),
 | 
			
		||||
@@ -47,7 +48,7 @@ class AdoptionNoticeForm(forms.ModelForm):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = AdoptionNotice
 | 
			
		||||
        fields = ['name', "group_only", "further_information", "description", "searching_since"]
 | 
			
		||||
        fields = ['name', "group_only", "further_information", "description", "searching_since", "location_string"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AnimalForm(forms.ModelForm):
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,81 @@
 | 
			
		||||
# Generated by Django 5.0.6 on 2024-06-05 13:19
 | 
			
		||||
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("fellchensammlung", "0006_announcement_type"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="country",
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="description",
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="postcode",
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="adoptionnotice",
 | 
			
		||||
            name="location",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                to="fellchensammlung.location",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="adoptionnotice",
 | 
			
		||||
            name="location_string",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                default="72072", max_length=200, verbose_name="Ortsangabe"
 | 
			
		||||
            ),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="latitude",
 | 
			
		||||
            field=models.FloatField(default=47),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="longitude",
 | 
			
		||||
            field=models.FloatField(default=9),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="osm_id",
 | 
			
		||||
            field=models.IntegerField(default=1),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="place_id",
 | 
			
		||||
            field=models.IntegerField(default=1),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="rescueorganization",
 | 
			
		||||
            name="location_string",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                default="72072", max_length=200, verbose_name="Ort der Organisation"
 | 
			
		||||
            ),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="location",
 | 
			
		||||
            name="name",
 | 
			
		||||
            field=models.CharField(max_length=2000),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -66,19 +66,11 @@ class Location(models.Model):
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"{self.name}"
 | 
			
		||||
 | 
			
		||||
    GERMANY = "DE"
 | 
			
		||||
    AUSTRIA = "AT"
 | 
			
		||||
    SWITZERLAND = "CH"
 | 
			
		||||
    COUNTRIES_CHOICES = {
 | 
			
		||||
        GERMANY: "Germany",
 | 
			
		||||
        AUSTRIA: "Austria",
 | 
			
		||||
        SWITZERLAND: "Switzerland"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    name = models.CharField(max_length=200)
 | 
			
		||||
    postcode = models.CharField(max_length=200)
 | 
			
		||||
    country = models.CharField(max_length=20, choices=COUNTRIES_CHOICES)
 | 
			
		||||
    description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung'))
 | 
			
		||||
    place_id = models.IntegerField()
 | 
			
		||||
    osm_id = models.IntegerField()
 | 
			
		||||
    latitude = models.FloatField()
 | 
			
		||||
    longitude = models.FloatField()
 | 
			
		||||
    name = models.CharField(max_length=2000)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RescueOrganization(models.Model):
 | 
			
		||||
@@ -87,6 +79,7 @@ class RescueOrganization(models.Model):
 | 
			
		||||
 | 
			
		||||
    name = models.CharField(max_length=200)
 | 
			
		||||
    trusted = models.BooleanField(default=False, verbose_name=_('Vertrauenswürdig'))
 | 
			
		||||
    location_string = models.CharField(max_length=200, verbose_name=_("Ort der Organisation"))
 | 
			
		||||
    location = models.ForeignKey(Location, on_delete=models.PROTECT)
 | 
			
		||||
    instagram = models.URLField(null=True, blank=True, verbose_name=_('Instagram Profil'))
 | 
			
		||||
    facebook = models.URLField(null=True, blank=True, verbose_name=_('Facebook Profil'))
 | 
			
		||||
@@ -112,6 +105,8 @@ class AdoptionNotice(models.Model):
 | 
			
		||||
    further_information = models.URLField(null=True, blank=True, verbose_name=_('Link zu mehr Informationen'))
 | 
			
		||||
    group_only = models.BooleanField(default=False, verbose_name=_('Ausschließlich Gruppenadoption'))
 | 
			
		||||
    photos = models.ManyToManyField(Image, blank=True)
 | 
			
		||||
    location_string = models.CharField(max_length=200, verbose_name=_("Ortsangabe"))
 | 
			
		||||
    location = models.ForeignKey(Location, blank=True, null=True, on_delete=models.SET_NULL,)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def animals(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import requests
 | 
			
		||||
import json
 | 
			
		||||
from notfellchen import __version__ as nf_version
 | 
			
		||||
from fellchensammlung.models import Location
 | 
			
		||||
 | 
			
		||||
from math import radians, sqrt, sin, cos, atan2
 | 
			
		||||
 | 
			
		||||
@@ -21,13 +22,15 @@ def calculate_distance_between_coordinates(position1, position2):
 | 
			
		||||
    distance_lat = radians(latitude2 - latitude1)
 | 
			
		||||
    distance_long = radians(longitude2 - longitude1)
 | 
			
		||||
 | 
			
		||||
    a = pow(sin(distance_lat / 2), 2) + cos(radians(latitude1)) * cos(radians(latitude2)) * pow(sin(distance_long / 2), 2)
 | 
			
		||||
    a = pow(sin(distance_lat / 2), 2) + cos(radians(latitude1)) * cos(radians(latitude2)) * pow(sin(distance_long / 2),
 | 
			
		||||
                                                                                                2)
 | 
			
		||||
    c = 2 * atan2(sqrt(a), sqrt(1 - a))
 | 
			
		||||
 | 
			
		||||
    distance_in_km = earth_radius_km * c
 | 
			
		||||
 | 
			
		||||
    return distance_in_km
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResponseMock:
 | 
			
		||||
    content = b'[{"place_id":138181499,"licence":"Data \xc2\xa9 OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright","osm_type":"relation","osm_id":1247237,"lat":"48.4949904","lon":"9.040330235970146","category":"boundary","type":"postal_code","place_rank":21, "importance":0.12006895017929346,"addresstype":"postcode","name":"72072","display_name":"72072, Derendingen, T\xc3\xbcbingen, Landkreis T\xc3\xbcbingen, Baden-W\xc3\xbcrttemberg, Deutschland", "boundingbox":["48.4949404","48.4950404","9.0402802","9.0403802"]}]'
 | 
			
		||||
    status_code = 200
 | 
			
		||||
@@ -43,21 +46,33 @@ class RequestMock:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GeoAPI:
 | 
			
		||||
    api_url = "https://nominatim.openstreetmap.org/search"
 | 
			
		||||
    headers = {
 | 
			
		||||
        'User-Agent': f"Notfellchen {nf_version}",
 | 
			
		||||
        'From': 'info@notfellchen.org'  # This is another valid field
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, debug=True):
 | 
			
		||||
        self.api_url = "https://nominatim.openstreetmap.org/search"
 | 
			
		||||
        if debug:
 | 
			
		||||
            self.requests = RequestMock
 | 
			
		||||
        else:
 | 
			
		||||
            self.requests = requests
 | 
			
		||||
 | 
			
		||||
    def get_coordinates_from_postcode(self, postcode):
 | 
			
		||||
        headers = {
 | 
			
		||||
            'User-Agent': f"Notfellchen {nf_version}",
 | 
			
		||||
            'From': 'info@notfellchen.org'  # This is another valid field
 | 
			
		||||
        }
 | 
			
		||||
        result = self.requests.get(self.api_url, {"q": postcode, "format": "jsonv2"}, headers=headers).json()[0]
 | 
			
		||||
        result = self.requests.get(self.api_url, {"q": postcode, "format": "jsonv2"}, headers=self.headers).json()[0]
 | 
			
		||||
        return result["lat"], result["lon"]
 | 
			
		||||
 | 
			
		||||
    def get_location_from_string(self, location_string):
 | 
			
		||||
        result = self.requests.get(self.api_url, {"q": location_string, "format": "jsonv2"}, headers=self.headers).json()[0]
 | 
			
		||||
        location = Location.objects.create(
 | 
			
		||||
            place_id=result["place_id"],
 | 
			
		||||
            osm_id=result["osm_id"],
 | 
			
		||||
            latitude=result["lat"],
 | 
			
		||||
            longitude=result["lon"],
 | 
			
		||||
            name=result["name"],
 | 
			
		||||
        )
 | 
			
		||||
        return location
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    geo = GeoAPI(debug=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ urlpatterns = [
 | 
			
		||||
    # ex: /search/
 | 
			
		||||
    path("suchen/", views.search, name="search"),
 | 
			
		||||
    # ex: /vermitteln/
 | 
			
		||||
    path("vermitteln/", views.add_adoption, name="add-adoption"),
 | 
			
		||||
    path("vermitteln/", views.add_adoption_notice, name="add-adoption"),
 | 
			
		||||
 | 
			
		||||
    path("ueber-uns/", views.about, name="about"),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ from fellchensammlung.models import AdoptionNotice, Text, Animal, Rule, Image, R
 | 
			
		||||
    Member
 | 
			
		||||
from .forms import AdoptionNoticeForm, ImageForm, ReportAdoptionNoticeForm, CommentForm, ReportCommentForm, AnimalForm
 | 
			
		||||
from .models import Language, Announcement
 | 
			
		||||
from .tools.geo import GeoAPI
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def index(request):
 | 
			
		||||
@@ -93,12 +94,19 @@ def search(request):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def add_adoption(request):
 | 
			
		||||
def add_adoption_notice(request):
 | 
			
		||||
    if request.method == 'POST':
 | 
			
		||||
        form = AdoptionNoticeForm(request.POST, request.FILES, in_adoption_notice_creation_flow=True)
 | 
			
		||||
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            instance = form.save()
 | 
			
		||||
 | 
			
		||||
            """Search the location given in the location string and add it to the adoption notice"""
 | 
			
		||||
            geo_api = GeoAPI(debug=True)
 | 
			
		||||
            location = geo_api.get_location_from_string(instance.location_string)
 | 
			
		||||
            instance.location = location
 | 
			
		||||
            instance.save()
 | 
			
		||||
 | 
			
		||||
            return redirect(reverse("adoption-notice-add-animal", args=[instance.pk]))
 | 
			
		||||
    else:
 | 
			
		||||
        form = AdoptionNoticeForm(in_adoption_notice_creation_flow=True)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user