models.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import logging
  2. from typing import Dict
  3. from uuid import uuid4
  4. from django.contrib.auth import get_user_model
  5. from django.conf import settings
  6. from django.db import models
  7. from django.urls import reverse
  8. from django_extensions.db.models import TimeStampedModel
  9. from scrobbles.mixins import ScrobblableMixin
  10. logger = logging.getLogger(__name__)
  11. BNULL = {"blank": True, "null": True}
  12. User = get_user_model()
  13. ACCURACY = getattr(settings, "GEOLOC_ACCURACY", 3)
  14. class GeoLocation(ScrobblableMixin):
  15. COMPLETION_PERCENT = getattr(settings, "LOCATION_COMPLETION_PERCENT", 100)
  16. uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
  17. lat = models.FloatField()
  18. lon = models.FloatField()
  19. truncated_lat = models.FloatField(**BNULL)
  20. truncated_lon = models.FloatField(**BNULL)
  21. altitude = models.FloatField(**BNULL)
  22. class Meta:
  23. unique_together = [["lat", "lon", "altitude"]]
  24. def __str__(self):
  25. if self.title:
  26. return self.title
  27. return f"{self.lat} x {self.lon}"
  28. def get_absolute_url(self):
  29. return reverse(
  30. "locations:geo_location_detail", kwargs={"slug": self.uuid}
  31. )
  32. @classmethod
  33. def find_or_create(cls, data_dict: Dict) -> "GeoLocation":
  34. """Given a data dict from GPSLogger, does the heavy lifting of looking up
  35. the location, creating if if doesn't exist yet.
  36. """
  37. # TODO Add constants for all these data keys
  38. if "lat" not in data_dict.keys() or "lon" not in data_dict.keys():
  39. logger.error("No lat or lon keys in data dict")
  40. return
  41. int_lat, r_lat = str(data_dict.get("lat", "")).split(".")
  42. int_lon, r_lon = str(data_dict.get("lon", "")).split(".")
  43. try:
  44. trunc_lat = r_lat[0:GEOLOC_ACCURACY]
  45. except IndexError:
  46. trunc_lat = r_lat
  47. try:
  48. trunc_lon = r_lon[0:GEOLOC_ACCURACY]
  49. except IndexError:
  50. trunc_lon = r_lon
  51. data_dict["lat"] = float(f"{int_lat}.{trunc_lat}")
  52. data_dict["lon"] = float(f"{int_lon}.{trunc_lon}")
  53. int_alt, r_alt = str(data_dict.get("alt", "")).split(".")
  54. data_dict["altitude"] = float(int_alt)
  55. location = cls.objects.filter(
  56. lat=data_dict.get("lat"),
  57. lon=data_dict.get("lon"),
  58. altitude=data_dict.get("altitude"),
  59. ).first()
  60. if not location:
  61. location = cls.objects.create(
  62. lat=data_dict.get("lat"),
  63. lon=data_dict.get("lon"),
  64. altitude=data_dict.get("altitude"),
  65. )
  66. return location
  67. class RawGeoLocation(TimeStampedModel):
  68. user = models.ForeignKey(User, on_delete=models.CASCADE)
  69. lat = models.FloatField()
  70. lon = models.FloatField()
  71. altitude = models.FloatField(**BNULL)
  72. speed = models.FloatField(**BNULL)
  73. timestamp = models.DateTimeField(**BNULL)