Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.0.2 on 2026-02-13 23:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("market", "0004_rename_address_sublet_street_address_and_more"),
]

operations = [
migrations.AddField(
model_name="sublet",
name="latitude",
field=models.FloatField(blank=True, null=True),
),
migrations.AddField(
model_name="sublet",
name="longitude",
field=models.FloatField(blank=True, null=True),
),
]
34 changes: 33 additions & 1 deletion backend/market/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import hashlib
import math

from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
Expand Down Expand Up @@ -42,7 +45,6 @@ class Meta:
def __str__(self):
return f"Offer for {self.listing} made by {self.user}"


class Category(models.Model):
name = models.CharField(max_length=100, unique=True)

Expand Down Expand Up @@ -130,12 +132,42 @@ class Sublet(Listing):
baths = models.PositiveIntegerField()
start_date = models.DateField()
end_date = models.DateField()
latitude = models.FloatField(null=True, blank=True)
longitude = models.FloatField(null=True, blank=True)

def clean(self):
super().clean()
if self.start_date and self.end_date and self.start_date >= self.end_date:
raise ValidationError({"end_date": "End date must be after start date"})

def _calculate_approximate_location(self, latitude, longitude):
if latitude is None or longitude is None:
return None, None

lat_str = f"{float(latitude):.9f}"
lon_str = f"{float(longitude):.9f}"
seed = hashlib.md5(f"{lat_str}{lon_str}".encode()).hexdigest()

offset_factor = int(seed[:8], 16) / 0xFFFFFFFF

offset_distance = 0.0005 + (offset_factor * 0.0013)
angle = offset_factor * 2 * math.pi

lat_offset = offset_distance * math.sin(angle)
lon_offset = offset_distance * math.cos(angle)

approx_lat = float(latitude) + lat_offset
approx_lon = float(longitude) + lon_offset
return approx_lat, approx_lon

@property
def approximate_location(self):
if self.latitude is not None and self.longitude is not None:
approximate_location = self._calculate_approximate_location(
self.latitude, self.longitude)
return approximate_location
return None, None

Comment thread
alisx255 marked this conversation as resolved.
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
37 changes: 34 additions & 3 deletions backend/market/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError as ModelValidationError
from profanity_check import predict
Expand Down Expand Up @@ -98,10 +99,23 @@ def get_condition(self, obj):


class SubletDataSerializer(ModelSerializer):
latitude = SerializerMethodField()
longitude = SerializerMethodField()

class Meta:
model = Sublet
fields = ["street_address", "beds", "baths", "start_date", "end_date"]
fields = ["street_address", "beds", "baths", "start_date", "end_date",
"latitude", "longitude"]

def get_latitude(self, obj):
if obj.approximate_location is not None:
return float(obj.approximate_location[0])
return None

def get_longitude(self, obj):
if obj.approximate_location is not None:
return float(obj.approximate_location[1])
return None

# Unified serializer for all listing types (Items and Sublets); used for CRUD operations
class ListingSerializer(ListingTypeMixin, ModelSerializer):
Expand Down Expand Up @@ -265,12 +279,23 @@ def _create_item(self, validated_data, additional_data):
def _create_sublet(self, validated_data, additional_data):
tags = validated_data.pop("tags", None)

latitude = additional_data.get("latitude")
longitude = additional_data.get("longitude")


if latitude is not None:
latitude = float(latitude)
if longitude is not None:
longitude = float(longitude)

sublet = Sublet.objects.create(
street_address=additional_data.get("street_address"),
beds=additional_data.get("beds"),
baths=additional_data.get("baths"),
start_date=additional_data.get("start_date"),
end_date=additional_data.get("end_date"),
latitude=latitude,
longitude=longitude,
**validated_data,
)

Expand Down Expand Up @@ -323,10 +348,16 @@ def _update_item(self, instance, additional_data):

def _update_sublet(self, instance, additional_data):
sublet = instance.sublet
sublet_fields = ["street_address", "beds", "baths", "start_date", "end_date"]
for field in sublet_fields:
str_fields = ["street_address", "beds", "baths", "start_date", "end_date"]
float_fields = ["latitude", "longitude"]
for field in str_fields:
if field in additional_data:
setattr(sublet, field, additional_data[field])

for field in float_fields:
if field in additional_data:
value = additional_data[field]
setattr(sublet, field, float(value) if value is not None else None)
sublet.full_clean()
sublet.save()

Expand Down
Loading