예제 #1
0
class Transmitter(BaseModel):
    repeater = models.ForeignKey(Repeater,
                                 on_delete=models.CASCADE,
                                 related_name='transmitters')
    active = models.BooleanField(default=True)
    transmit_frequency = models.DecimalField(max_digits=18, decimal_places=6)
    offset = models.DecimalField(max_digits=18, decimal_places=6)
    mode = models.CharField(max_length=16, choices=RF_MODES)
    pep = models.FloatField("PEP",
                            null=True,
                            blank=True,
                            help_text="Peak Envelope Power in W")
    description = models.TextField(blank=True, null=True)
    hardware = models.CharField(max_length=256, blank=True)

    # Analog
    ctcss = models.DecimalField(
        "CTCSS",
        choices=CTCSS,
        decimal_places=1,
        max_digits=5,
        blank=True,
        null=True,
        help_text="Continuous Tone Coded Squelch System")
    echolink = models.IntegerField(blank=True, null=True)

    # Digital
    dmr_id = models.ForeignKey(DMRID,
                               on_delete=models.PROTECT,
                               related_name="transmitters",
                               verbose_name="DMR ID",
                               blank=True,
                               null=True)
    colorcode = models.SmallIntegerField(blank=True, null=True)

    created_by = models.ForeignKey(get_user_model(),
                                   on_delete=models.SET(get_sentinel_user),
                                   related_name="transmitters")
    source = models.CharField(max_length=256, blank=True)

    @property
    def receive_frequency(self) -> Decimal:
        if self.transmit_frequency and self.offset is not None:
            return self.transmit_frequency + self.offset
        else:
            return Decimal(0)

    @property
    def brandmeister_repeater_url(self) -> str:
        if self.dmr_id:
            return f"https://brandmeister.network/index.php?page=repeater&id={ self.dmr_id.name }"
        else:
            return ""

    def __str__(self) -> str:
        return f"{ self.repeater.callsign.name } at { self.transmit_frequency } MHz"
예제 #2
0
class Club(BaseModel):
    callsign = models.OneToOneField(Callsign, on_delete=models.CASCADE)
    website = models.URLField(blank=True)
    description = models.TextField(blank=True)
    members = models.ManyToManyField(Callsign,
                                     related_name="members",
                                     blank=True)
    created_by = models.ForeignKey(get_user_model(),
                                   on_delete=models.SET(get_sentinel_user),
                                   related_name="clubs")

    def get_absolute_url(self) -> str:
        return reverse('callsign:callsign-html-detail',
                       args=[self.callsign.name])

    def __str__(self) -> str:
        return self.callsign.name
예제 #3
0
class Repeater(LocationBaseModel):
    callsign = models.OneToOneField(Callsign, on_delete=models.CASCADE)
    active = models.BooleanField(default=True)
    website = models.URLField(max_length=400, blank=True, null=True)
    altitude = models.FloatField(blank=True, null=True)
    description = models.TextField(blank=True)

    created_by = models.ForeignKey(get_user_model(),
                                   on_delete=models.SET(get_sentinel_user),
                                   related_name="repeaters")
    source = models.CharField(max_length=256, blank=True)

    def __str__(self) -> str:
        return self.callsign.name

    def get_absolute_url(self) -> str:
        return reverse('callsign:callsign-html-detail',
                       args=[self.callsign.name])
예제 #4
0
class Shareditem(models.Model):
    """
	Parent model for all shared items on the page.
	The class is using multiple managers for 
	* inheritance and selecting subclasses (default 'objects'), and
	* for spatial queries ('geo_objects').
	"""

    ITEMTYPES = (
        ("i", "Idea"),
        ("m", "Meeting Note"),
        ("n", "Newspaper Article"),
        ("e", "Photo or Video"),  # legacy: was 'External Media'
        ("d", "Data"),
    )

    # used in template tags, added here for maintenance reasons
    ITEMTYPES_PLURAL = {
        "i": "Ideas",
        "m": "Meeting Notes",
        "n": "Newspaper Articles",
        "e": "Photos & Videos",
        "d": "Data",
    }

    desc = MarkupField(
        "Description",
        help_text=
        "Please see the <a href='#'>Text formatting cheat sheet</a> for help.")
    itemtype = models.CharField(
        max_length=1,
        choices=ITEMTYPES,
    )

    station = models.ForeignKey("Station",
                                verbose_name="Related Station",
                                null=True,
                                blank=True)
    theme = models.ForeignKey("Theme",
                              verbose_name="Related Theme",
                              null=True,
                              blank=True)
    author = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))

    ip = models.IPAddressField(default="127.0.0.1")
    created = models.DateTimeField(auto_now_add=True)
    last_modified = models.DateTimeField(auto_now_add=True, auto_now=True)

    rating = RatingField(range=5, can_change_vote=True)

    objects = InheritanceManager()

    geometry = models.PointField(geography=True, null=True,
                                 blank=True)  # default SRS 4326
    geo_objects = models.GeoManager()

    class Meta:
        ordering = ("-created", "author")
        get_latest_by = "created"

    def __unicode__(self):
        return u"%i" % self.id

    # child model per itemtype
    CHILDMODELS = {
        "i": "idea",
        "m": "meetingnote",
        "n": "newsarticle",
        "e": "media",
        "d": "data",
    }

    @permalink
    def get_absolute_url(self):
        return ("%s_detail" % (Shareditem.CHILDMODELS[self.itemtype]), None, {
            "id": self.id,
        })

    def get_comment_count(self):
        # workaround for problem with for_model method and inheritance
        contenttype = ContentType.objects.get_for_model(self)
        return Comment.objects.filter(content_type=contenttype.id,
                                      object_pk=self.id).count()

    def get_child_contenttype(self):
        return ContentType.objects.get(
            app_label="participation",
            model=Shareditem.CHILDMODELS[self.itemtype])
예제 #5
0
class Walk(models.Model):
    """
    A Walk contains the route of the walk and all location data about the start location
    """

    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    description = models.TextField()
    start = models.PointField(blank=True, null=True)
    route = models.LineStringField()
    submitter = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))
    route_length = models.FloatField(
        help_text="The length of the walk in miles", default=0
    )
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    # Reverse Geocoding from OpenCage
    reverse_geocode_cache_time = models.DateTimeField(blank=True, null=True)
    # annotations
    what3words = models.CharField(max_length=100, blank=True, null=True)
    geohash = models.CharField(max_length=50, blank=True, null=True)
    # components
    continent = models.CharField(max_length=100, blank=True, null=True)
    country = models.CharField(max_length=100, blank=True, null=True)
    state = models.CharField(max_length=100, blank=True, null=True)
    county = models.CharField(max_length=100, blank=True, null=True)
    city = models.CharField(max_length=100, blank=True, null=True)
    suburb = models.CharField(max_length=100, blank=True, null=True)
    road = models.CharField(max_length=100, blank=True, null=True)
    postcode = models.CharField(max_length=100, blank=True, null=True)
    formatted = models.CharField(max_length=500, blank=True, null=True)

    attributes = models.ManyToManyField(Attribute, blank=True)

    def get_absolute_url(self):
        return reverse(
            "walk-detail",
            kwargs={"username": self.submitter.username, "slug": self.slug},
        )

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        initial_start = self.start
        self.slug = slugify(self.name)
        super().save(*args, **kwargs)

        # Check if the start location has changed
        start_location_changed = False

        if not self.start:
            self.start = self.calculate_walk_start()
            start_location_changed = True

        if self.start != initial_start:
            start_location_changed = True

        # check if the cache_has expired
        if self.reverse_geocode_cache_time is None:
            cache_expired = True
        else:
            cache_expired = self.reverse_geocode_cache_time < (
                now() - timedelta(days=180)
            )

        # update the location details if we need to
        if start_location_changed or cache_expired:
            if settings.OPENCAGE_API_KEY:
                geocoder = OpenCageGeocode(settings.OPENCAGE_API_KEY)

                try:
                    results = geocoder.reverse_geocode(
                        round(self.start[1], 6),
                        round(self.start[0], 6),
                        language="en",
                        limit=1,
                    )

                    if results and len(results):
                        self.what3words = (
                            results[0].get("annotations").get("what3words").get("words")
                        )
                        self.geohash = results[0].get("annotations").get("geohash")
                        self.continent = results[0].get("components").get("continent")
                        self.country = results[0].get("components").get("country")
                        self.state = results[0].get("components").get("state")
                        self.county = results[0].get("components").get("county")
                        self.city = results[0].get("components").get("city")
                        self.suburb = results[0].get("components").get("suburb")
                        self.road = results[0].get("components").get("road")
                        self.postcode = results[0].get("components").get("postcode")
                        self.formatted = results[0].get("formatted")

                        self.reverse_geocode_cache_time = now()
                        super().save(*args, **kwargs)

                except RateLimitExceededError as e:
                    print(e)

        self.route.transform(3857)
        self.route_length = D(m=self.route.length).mi
        super().save(*args, **kwargs)

    def calculate_walk_start(self):
        return Point(srid=self.route.srid, x=self.route[0][0], y=self.route[0][1],)
예제 #6
0
class Callsign(LocationBaseModel):
    name = CallsignField(unique=True, db_index=True)
    prefix = models.ForeignKey(CallsignPrefix,
                               on_delete=models.PROTECT,
                               null=True,
                               blank=True)
    country = models.ForeignKey(Country,
                                on_delete=models.PROTECT,
                                null=True,
                                blank=True)
    cq_zone = CQZoneField("CQ zone", null=True, blank=True)
    itu_zone = ITUZoneField("ITU zone", null=True, blank=True)
    itu_region = ITURegionField("ITU region", null=True, blank=True)
    type = models.CharField(choices=CALLSIGN_TYPES, max_length=32, blank=True)
    owner = models.ForeignKey(get_user_model(),
                              on_delete=models.SET_NULL,
                              null=True,
                              blank=True)
    active = models.BooleanField(default=True)
    issued = models.DateField(null=True, blank=True)
    expired = models.DateField(null=True, blank=True)
    license_type = models.CharField(max_length=64, blank=True)
    dstar = models.BooleanField("D-STAR", default=False)
    identifier = models.CharField(_("Optional identifier"),
                                  max_length=128,
                                  unique=True,
                                  blank=True,
                                  null=True)
    website = models.URLField(max_length=128, blank=True, null=True)
    comment = models.TextField(blank=True)
    _official_validated = models.BooleanField(
        default=False,
        help_text="Callsign is validated by a government agency")
    _location_source = models.CharField(max_length=32,
                                        choices=LOCATION_SOURCE_CHOICES,
                                        blank=True)

    lotw_last_activity = models.DateTimeField("LOTW last activity",
                                              null=True,
                                              blank=True)
    eqsl = models.BooleanField(default=False)

    created_by = models.ForeignKey(get_user_model(),
                                   on_delete=models.SET(get_sentinel_user),
                                   related_name="callsigns")
    internal_comment = models.TextField(blank=True)
    source = models.CharField(max_length=256, blank=True)
    objects = CallsignManager()

    # TODO(elnappo) make sure a user can not change his name after validation

    def __str__(self) -> str:
        return self.name

    def set_default_meta_data(self):
        prefix = CallsignPrefix.objects.extra(
            where=["%s LIKE name||'%%'"],
            params=[self.name]).order_by("-name").first()

        # Add changed fields to ImportCommand._callsign_bulk_create()
        if prefix:
            self.prefix = prefix
            self.country = prefix.country
            self.cq_zone = prefix.cq_zone
            self.itu_zone = prefix.itu_zone
            self.location = prefix.location
            self._location_source = "prefix"

    def get_absolute_url(self) -> str:
        return reverse('callsign:callsign-html-detail', args=[self.name])

    def update_location(self, location: Point, source: str) -> bool:
        # Update location only if new location source has higher priority than current location source.
        # Does no update if new and current location source are equal.
        if LOCATION_SOURCE_PRIORITY.index(
                source) > LOCATION_SOURCE_PRIORITY.index(self.location_source):
            self.location = location
            self._location_source = source
            return True
        else:
            return False

    @property
    def aprs_passcode(self) -> int:
        if self.official_validated == "false" or self.type == "shortwave_listener":
            # Not a good idea to raise an exception here?
            raise PermissionDenied(
                "callsign is not official assigned or is shortwave listener")
        return generate_aprs_passcode(self.name)

    @property
    def official_validated(self) -> str:
        if self.country and self.country.telecommunicationagency.used_for_official_callsign_import and self._official_validated:
            return "true"
        elif self.country and self.country.telecommunicationagency.used_for_official_callsign_import and not self._official_validated:
            return "false"
        else:
            return "unknown"

    @property
    def location_source(self) -> str:
        if self._location_source in ("official", "unofficial"):
            return "address"
        else:
            return self._location_source

    @property
    def grid(self):
        if self.location_source == "prefix":
            return self._grid(high_accuracy=False)
        else:
            return self._grid(high_accuracy=True)

    @property
    def lotw(self) -> bool:
        return bool(self.lotw_last_activity)

    @property
    def eqsl_profile_url(self) -> str:
        return f"https://www.eqsl.cc/Member.cfm?{ self.name }"

    @property
    def clublog_profile_url(self) -> str:
        return f"https://secure.clublog.org/logsearch/{ self.name }"

    @property
    def dxheat_profile_url(self) -> str:
        return f"https://dxheat.com/db/{ self.name }"

    @property
    def aprsfi_profile_url(self) -> str:
        return f"https://aprs.fi/info/?call={ self.name }"

    @property
    def pskreporter_profile_url(self) -> str:
        return f"http://www.pskreporter.de/table?call={ self.name }"

    @property
    def qrzcq_profile_url(self) -> str:
        return f"https://www.qrzcq.com/call/{ self.name }"

    @property
    def qrz_profile_url(self) -> str:
        return f"https://www.qrz.com/db/{ self.name }"

    @property
    def hamqth_profile_url(self) -> str:
        return f"https://www.hamqth.com/{ self.name }"

    @property
    def hamcall_profile_url(self) -> str:
        return f"https://hamcall.net/call?callsign={ self.name }"

    @property
    def dxwatch_profile_url(self) -> str:
        return f"https://dxwatch.com/qrz/{self.name}"