class RequestRate(models.Model):
    """Implements a rate limit per IP.

	value is increased at every request by the amount the request specifies,
	which is meant to be the average number of seconds until the same
	request could be made again.

	value decreases at a rate of 1 per second until it is a 0.
	"""
    name = models.CharField(max_length=20)
    ipaddr = models.IPAddressField(blank=True, null=True)
    last_update = models.DateTimeField(auto_now_add=True)
    value = models.FloatField()

    objects = RequestRateManager()

    def update(self):
        this_update = datetime.datetime.now()
        td = this_update - self.last_update
        time_delta_sec = (float(td.days) * 3600.0 * 60.0 + float(td.seconds) +
                          float(td.microseconds) / 1000000.0)
        self.value -= time_delta_sec
        if self.value < 0:
            self.value = 0
        self.last_update = this_update

    def request(self, seconds, max_value):
        self.update()
        if self.value + seconds < max_value:
            self.value += seconds
            self.save()
            return True
        else:
            self.save()
            return False
Exemple #2
0
class Checkin(models.Model):
    date = models.DateTimeField(_('Checkin date'), auto_now_add=True)
    place = models.ForeignKey(CheckinPlace, blank=True, null=True)
    user = models.ForeignKey(User)
    is_valid = models.BooleanField(default=False)
    # Checkin infos
    lng = models.FloatField()
    lat = models.FloatField()
    accuracy = models.FloatField(default=20000)
    timestamp = models.DateTimeField(_('Checkin date'), auto_now_add=True)
    useragent = models.CharField(max_length=250, default="Unknown")
    visitor_ip = models.IPAddressField(blank=True, null=True)
    extra_data = models.TextField(blank=True, null=True)

    def distance(self):
        try:
            place = CheckinPlace.objects.filter(pk=self.place.id).distance(
                Point(self.lng, self.lat))[0]
            if place.distance.m > 999:
                return '~%0.3f km' % place.distance.km
            else:
                return '~%0.3f m' % place.distance.m
        except:
            return "Unknown"

    def __unicode__(self):
        title = self.is_valid and 'Valid checkin' or 'Invalid checkin'
        return u"%s at %s" % (title, self.place)

    class Meta:
        ordering = ('-date', )
Exemple #3
0
class Survey(models.Model):
    """
    Survey base questions.
    """
    school = models.ForeignKey(School)
    street = models.CharField(max_length=50, blank=True, null=True)
    cross_st = models.CharField('Cross street',
                                max_length=50,
                                blank=True,
                                null=True)
    nr_vehicles = models.IntegerField('Number of Vehicles',
                                      blank=True,
                                      null=True)
    nr_licenses = models.IntegerField('Number of License',
                                      blank=True,
                                      null=True)
    ip = models.IPAddressField('IP Address', blank=True, null=True)
    # Mile, shortest driving distance, not walk/bike distance
    distance = models.FloatField(null=True)
    created = models.DateTimeField(null=True)
    modified = models.DateTimeField(auto_now=True, null=True)

    user = models.ForeignKey(User, null=True)
    # GeoDjango
    location = models.PointField(geography=True,
                                 blank=True,
                                 null=True,
                                 default='POINT(0 0)')
    # default SRS 4326
    objects = models.GeoManager()

    def __unicode__(self):
        return u'%s' % (self.id)

    def update_location(self):
        if len(self.street) > 0 and len(self.cross_st) > 0:
            school_cross = self.school.get_intersections()
            crosses = school_cross.filter(
                Q(
                    Q(st_name_1__iexact=self.street)
                    & Q(st_name_2__iexact=self.cross_st)) | Q(
                        Q(st_name_2__iexact=self.street)
                        & Q(st_name_1__iexact=self.cross_st)))
            crosses = list(crosses)
            if len(crosses):
                self.location = crosses[0].geometry
                self.location.transform(4326)
            else:
                print 'No Intersection'

    def update_distance(self):
        from maps import get_driving_distance

        try:
            self.distance = get_driving_distance(self.school.geometry,
                                                 self.location)
        except:
            pass
Exemple #4
0
class Author(models.Model):
    objects = AuthorManager()

    # Data
    user = models.ForeignKey(User, **OPT)

    ip_address = models.IPAddressField(**OPT)
    user_agent = models.TextField(**OPT)
    referer = models.TextField(**OPT)

    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return "{} ({})".format(self.user, self.ip_address)
Exemple #5
0
class Visit(AuditableModel):

    visitor = models.ForeignKey(User,
                                verbose_name=strings.VISIT_VISITOR,
                                related_name='visits',
                                null=True,
                                blank=True)

    visitor_ip = models.IPAddressField(verbose_name=strings.VISIT_IP_ADDRESS,
                                       null=True,
                                       blank=True)

    country_code = models.CharField(verbose_name=strings.VISIT_COUNTRY_CODE,
                                    max_length=2,
                                    null=True,
                                    blank=True)

    user_agent = models.CharField(verbose_name=strings.VISIT_USER_AGENT,
                                  max_length=256,
                                  null=True,
                                  blank=True)

    # Generic FK to any other model, Circuit, Place, etc...
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

    def __unicode__(self):
        return self.visitor_ip

    def update_data(self, request, commit=True):
        self.user_agent = request.META.get('HTTP_USER_AGENT', None)
        geo = GeoIP(settings.GEOIP_DATABASE)
        self.country_code = geo.country_code_by_addr(
            request.META.get('REMOTE_ADDR', None))
        self.visitor_ip = request.META.get('REMOTE_ADDR', None)
        if hasattr(request, 'user') and request.user.is_authenticated():
            self.visitor = request.user
        if commit:
            self.save()

    class Meta:
        verbose_name = strings.VISIT_VERBOSE_NAME
        verbose_name_plural = strings.VISIT_VERBOSE_NAME_PLURAL

    @staticmethod
    def create_visit(request, content_object):
        visit = Visit(content_object=content_object)
        visit.update_data(request)
Exemple #6
0
class Submission(models.Model):
    optional = dict(null=True, default=None, blank=True)

    # Meta
    submission_id = models.CharField(max_length=64, default=generate_id)
    user = models.ForeignKey(User, related_name='submitted_route', **optional)
    ip_address = models.IPAddressField(**optional)
    user_agent = models.CharField(max_length=1024, **optional)
    transportation = models.ForeignKey(Transportation,
                                       related_name='history',
                                       **optional)

    # Submitted data
    parent = models.ForeignKey('self', **optional)
    data_version = models.IntegerField(default=1)
    raw_geojson = models.TextField()
    raw_source = models.TextField(**optional)

    # Parsed data
    province = models.CharField(max_length=5, choices=PROVINCES, **optional)
    city = models.CharField(max_length=256, **optional)
    company = models.CharField(max_length=256, **optional)
    number = models.CharField(max_length=64, **optional)
    origin = models.CharField(max_length=256, **optional)
    destination = models.CharField(max_length=256, **optional)
    route = models.MultiLineStringField(srid=4326, **optional)

    parsed_ok = models.NullBooleanField(**optional)
    parsed_date = models.DateTimeField(**optional)
    parsed_error = models.CharField(max_length=1024, **optional)

    source = models.CharField(max_length=100, **optional)

    # Internal
    active = models.BooleanField(default=True)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return '{} {} - {} - {}'.format(self.company, self.number, self.city,
                                        self.user)

    class Meta:
        ordering = ('-updated', )

    def to_geojson(self):
        from . import utils
        return utils.to_geojson(self)
Exemple #7
0
class Hit(models.Model):
    session = models.ForeignKey(Session)
    location = models.ForeignKey('accounts.Location', null=True)
    path = models.CharField(max_length=255)
    referrer = models.TextField(blank=True)
    verb = models.CharField(max_length=6)
    response_code = models.PositiveIntegerField()
    timestamp = models.DateTimeField(default=datetime.now)
    ip_address = models.IPAddressField()
    coords = models.PointField(null=True)
    hit_number = models.PositiveIntegerField()
    objects = HitManager()

    def save(self, *args, **kwargs):
        g = GeoIP()
        self.coords = g.geos(self.ip_address)
        super(Hit, self).save(*args, **kwargs)
Exemple #8
0
class DonePlace(AuditableModel):
    """
    Relation between places and users, where users
    explicitly mark a place as visited or done
    """

    place = models.ForeignKey(Place,
        verbose_name=strings.DONE_PLACE_PLACE,
        related_name='done_place_records'
    )

    user = models.ForeignKey(User,
        verbose_name=strings.DONE_PLACE_USER,
        related_name='done_place_records'
    )

    ipaddr = models.IPAddressField(
        verbose_name=strings.DONE_PLACE_IPADDR,
        null=True,
        blank=True
    )

    coordinates = models.PointField(
        verbose_name=strings.DONE_PLACE_COORDINATES,
        null=True,
        blank=True
    )

    created_inplace = models.NullBooleanField(
        verbose_name=strings.DONE_PLACE_CREATED_INPLACE,
        null=True,
        blank=True
    )

    def __unicode__(self):
        return strings.DONE_PLACE_UNICODE % {
            'user':self.user.get_full_name(), 
            'place':self.place.name,
        }
        

    class Meta:
        verbose_name = strings.DONE_PLACE_VERBOSE_NAME
        verbose_name_plural = strings.DONE_PLACE_VERBOSE_NAME_PLURAL
        unique_together = (('user', 'place'), )
Exemple #9
0
class Asset(models.Model):
    title = models.CharField(
        'Asset Title',
        max_length=100,
    )
    category = models.CharField('Asset Category',
                                max_length=1,
                                default='p',
                                choices=ASSET_CATEGORIES)
    ip = models.IPAddressField('IP Address', blank=True, null=True)
    # GeoDjango
    location = models.PointField(geography=True,
                                 blank=True,
                                 null=True,
                                 default='POINT(0 0)')  # default SRS 4326
    objects = models.GeoManager()

    def __unicode__(self):
        return u'%s' % (self.id)

    def get_absolute_url(self):
        return "/asset/%i/" % self.id
Exemple #10
0
class Comment(models.Model):
    """
    Comment left on the map for a particular location.
    """

    description = models.TextField()
    user_name = models.CharField(max_length=50, blank=True, null=True)
    user_email = models.EmailField(max_length=75, blank=True, null=True)

    admin_key = models.CharField(max_length=12, blank=True, null=True)

    followup = models.CharField(max_length=1,
                                default='n',
                                choices=FOLLOWUP_CHOICES)

    ip = models.IPAddressField('IP Address', blank=True, null=True)
    last_modified = models.DateTimeField(editable=False, auto_now=True)

    location = models.PointField(geography=True,
                                 blank=True,
                                 null=True,
                                 default='POINT (0 0)')  # default SRS 4326
    objects = models.GeoManager()

    class Meta:
        verbose_name = _('Comment')
        verbose_name_plural = _('Comments')

    def __unicode__(self):
        return "%i" % (self.pk)

    def save(self, *args, **kwargs):
        # self.description += self.user_email
        # no geometry, potential spam
        if self.location == GEOSGeometry('POINT(0 0)'):
            self.followup = 'm'
        super(Comment, self).save(*args, **kwargs)
Exemple #11
0
class Hit(models.Model):
    article = models.ForeignKey(Article)
    date = models.DateTimeField(default=datetime.datetime.now)
    ip_address = models.IPAddressField()
    user = models.ForeignKey(User, blank=True, null=True)
Exemple #12
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])
Exemple #13
0
class Bookmark(Feature):
    description = models.TextField(default="", null=True, blank=True)
    latitude = models.FloatField()
    longitude = models.FloatField()
    altitude = models.FloatField()
    heading = models.FloatField(default=0)
    tilt = models.FloatField(default=0)
    roll = models.FloatField(default=0)
    altitudeMode = models.FloatField(default=1)
    ip = models.IPAddressField(default="0.0.0.0", null=True, blank=True)
    publicstate = models.TextField(default="{}")

    @property
    def kml(self):
        camera = "<Camera>\n"
        camera_params = [
            "latitude", "longitude", "altitude", "heading", "tilt", "roll",
            "altitudeMode"
        ]
        for p in camera_params:
            val = self.__dict__[p]
            if val is not None:
                camera += "                <%s>%s</%s>\n" % (p, val, p)
        camera += "            </Camera>\n"

        return """
        <Placemark id="%s">
            <visibility>1</visibility>
            <name>%s</name>
            <styleUrl>#%s-default</styleUrl>
            <ExtendedData>
                <Data name="descr"><value>%s</value></Data>
            </ExtendedData>
            %s
        </Placemark>
        """ % (self.uid, escape(
            self.name), self.model_uid(), escape(self.description), camera)

    @property
    def kml_style(self):
        return """
        <Style id="%s-default"> 
            <!-- invisible -->
            <BalloonStyle>
                <bgColor>ffeeeeee</bgColor>
                <text> <![CDATA[ 
                    <h3>$[name]</h3>
                    <p>$[descr]</p>
                ]]> </text>
            </BalloonStyle>
            <IconStyle>
                <scale>0.0</scale>
            </IconStyle>
            <LabelStyle>
                <scale>0.0</scale>
            </LabelStyle>
        </Style>
        """ % (self.model_uid())

    class Options:
        manipulators = []
        optional_manipulators = []
        verbose_name = 'Bookmark'
        form = 'madrona.bookmarks.forms.BookmarkForm'
        icon_url = 'bookmarks/images/bookmark.png'
        form_template = 'bookmarks/form.html'
        show_template = 'bookmarks/show.html'
Exemple #14
0
class Message(models.Model):
    """ Message data """
    class Meta():
        ordering = ['-date_add']
        get_latest_by = 'date_add'
        verbose_name = _('Message')
        verbose_name_plural = _('Messages')

    # Message types
    REQUEST = 1
    OFFER = 2
    INFO = 3

    TYPES_CHOICE = (
        (REQUEST, _("Help requests")),
        (OFFER, _("Help offers")),
        # (INFO, _("Informatial message"))
    )

    # Message status
    NEW = 1
    UNVERIFIED = 2
    VERIFIED = 3
    PENDING = 4
    CLOSED = 6

    MESSAGE_STATUS = ((NEW, _('New')), (UNVERIFIED, _('Unverified')),
                      (VERIFIED, _('Verified')), (PENDING, _('Pending')),
                      (CLOSED, _('Closed')))

    # Managers
    objects = PassThroughGeoManager.for_queryset_class(MessageQueryset)()

    # Main message fields
    title = models.CharField(max_length=200,
                             verbose_name=_('title'),
                             blank=True)
    message = models.TextField(verbose_name=_('Message'))
    # Additional message information. This is important for message, but
    # useless for engine.
    additional_info = JSONField(blank=True,
                                default='',
                                verbose_name=_("Additional info"))
    # Message type. Only moderator can change this type.
    messageType = models.IntegerField(
        choices=TYPES_CHOICE,
        db_column='message_type',
        verbose_name=_('Message type'),
    )
    # Link to message author. It can be anonymous for engine, so this will be
    # link to settings.ANONYMOUS_USER_ID
    user = models.ForeignKey(
        User,
        verbose_name=_("User"),
        editable=False,
        db_column='user_id',
    )

    # Optional fields
    # Message original source
    source = models.CharField(max_length=255,
                              verbose_name=_("source"),
                              blank=True)

    is_virtual = models.BooleanField(default=False,
                                     verbose_name=_('Is virtual'))

    # Moderator's fields
    # Message can be inactive, i.e. not 'closed' and no more information can be
    # added.
    is_active = models.BooleanField(default=False, verbose_name=_('active'))
    # Is it urgent message?
    is_important = models.BooleanField(default=False,
                                       verbose_name=_('important'))
    is_anonymous = models.BooleanField(default=True,
                                       verbose_name=_('hide contacts'))
    is_removed = models.BooleanField(default=False, verbose_name=_('removed'))
    allow_feedback = models.BooleanField(default=True,
                                         verbose_name=_('allow feedback'))

    status = models.SmallIntegerField(choices=MESSAGE_STATUS,
                                      verbose_name=_('status'),
                                      default=NEW,
                                      blank=True,
                                      null=True)

    #Internal fields
    date_add = models.DateTimeField(auto_now_add=True,
                                    db_column='date_add',
                                    editable=False)
    last_edit = models.DateTimeField(auto_now=True,
                                     db_column='date_modify',
                                     editable=False)
    expired_date = models.DateTimeField(verbose_name=_("expired at"),
                                        blank=True,
                                        null=True)
    edit_key = models.CharField(max_length=40, blank=True)
    sender_ip = models.IPAddressField(blank=True,
                                      null=True,
                                      editable=False,
                                      verbose_name=_("sender IP"))

    address = models.CharField(verbose_name=_('Address'),
                               max_length=250,
                               blank=True)

    location = models.MultiPointField(verbose_name=_('On map'),
                                      blank=True,
                                      null=True)

    # Links to core models
    category = models.ManyToManyField(Category,
                                      symmetrical=False,
                                      verbose_name=_("message categories"),
                                      null=True,
                                      blank=True)

    def __unicode__(self):
        return self.title or "Untitled"

    def save(self, *args, **kwargs):
        self.full_clean()
        super(Message, self).save(*args, **kwargs)

    def get_sender_name(self):
        """ Format sender name based on is_anonymous flag """

        first_name = self.additional_info['first_name'].capitalize()
        last_name = self.additional_info['last_name'].capitalize()
        if self.is_anonymous and last_name:
            last_name = last_name[0]
        return "%s %s" % (first_name, last_name)

    def get_absolute_url(self):
        return reverse_lazy("message-details", args=[str(self.pk)])
Exemple #15
0
class WeatherStation(models.Model):
    name = models.CharField(max_length=100)
    location = models.ForeignKey(Location, null=True, blank=True)
    abbreviation = models.CharField(max_length=20)
    bom_abbreviation = models.CharField(max_length=4)
    ip_address = models.IPAddressField()
    port = models.PositiveIntegerField(default=43000)
    last_scheduled = models.DateTimeField()
    last_reading = models.DateTimeField()
    battery_voltage = models.DecimalField(max_digits=3, decimal_places=1)
    connect_every = models.PositiveSmallIntegerField(default=15)
    active = models.BooleanField(default=False)
    stay_connected = models.BooleanField(default=False,verbose_name="Persistant connection")
    class Meta:
        ordering=['-last_reading']

    def last_reading_local(self):
        # TODO: use pytz
        reading = (timezone.localtime(self.last_reading) + timedelta(hours=8)).isoformat().rsplit(":", 2)[0]
        return reading

    def last_reading_time(self):
        # 24hr format time
        return self.last_reading_local().split("T")[1].replace(":", "")

    def reload(self):
        new_self = self.__class__.objects.get(pk=self.pk)
        #return new_self
        # You may want to clear out the old dict first or perform a selective merge
        self.__dict__.update(new_self.__dict__)


    def rain_since_nine_am(self):
        import datetime
        import pytz
        tz = pytz.timezone('Australia/Perth')

        now = timezone.make_aware(datetime.datetime.now(),tz)

        if now.time() > datetime.time(9):
            last_9am = now.replace(hour=9,minute=0,second=0,microsecond=0)
        else:
            yesterday = now - datetime.timedelta(hours=24)
            last_9am = yesterday.replace(hour=9,minute=0,second=0,microsecond=0)
        try:
            rainfall_stats = self.readings.filter(rainfall__gt=0,date__gte=last_9am).aggregate(Min('rainfall'),Max('rainfall'))      #.earliest('date')
            return rainfall_stats['rainfall__max'] - rainfall_stats['rainfall__min']
        except:
            return 0


    def save_weather_data(self, raw_data, last_reading=None):
        """
        Convert NVP format to django record
        """
        if last_reading is None:
            last_reading = timezone.now()

        # Data is stored in NVP format separated by the pipe symbol '|'.
        #   |<NAME>=<VALUE>|
        items = raw_data.split('|')
        data = {}
        for item in items:
            if (item != ''):
                try:
                    key, value = item.split('=')
                    data[key] = value
                except:
                    pass

        EMPTY = Decimal('0.00')

        # Create a weather reading.from the retrieved data.
        reading = WeatherObservation()
        reading.temperature_min = data.get('TN') or EMPTY
        reading.temperature_max = data.get('TX') or EMPTY
        reading.temperature = data.get('T') or EMPTY
        reading.temperature_deviation = data.get('TS') or EMPTY
        reading.temperature_outliers = data.get('TO') or 0

        reading.pressure_min = data.get('QFEN') or EMPTY
        reading.pressure_max = data.get('QFEX') or EMPTY
        reading.pressure = data.get('QFE') or EMPTY
        reading.pressure_deviation = data.get('QFES') or EMPTY
        reading.pressure_outliers = data.get('QFEO') or 0

        reading.humidity_min = data.get('HN') or EMPTY
        reading.humidity_max = data.get('HX') or EMPTY
        reading.humidity = data.get('H') or EMPTY
        reading.humidity_deviation = data.get('HS') or EMPTY
        reading.humidity_outliers = data.get('HO') or 0

        reading.wind_direction_min = data.get('DN') or EMPTY
        reading.wind_direction_max = data.get('DX') or EMPTY
        reading.wind_direction = data.get('D') or EMPTY
        reading.wind_direction_deviation = data.get('DS') or EMPTY
        reading.wind_direction_outliers = data.get('DO') or 0
#10.3.11.100
#10.159.8.67

        if (data.get('SN')):
            reading.wind_speed_min = Decimal(data.get('SN')) * KNOTS_TO_MS or 0
            reading.wind_speed_min_kn = Decimal(data.get('SN')) or 0
            reading.wind_speed_deviation = Decimal(data.get('SS')) * KNOTS_TO_MS or 0
            reading.wind_speed_outliers = data.get('SO') or 0
            reading.wind_speed_deviation_kn = Decimal(data.get('SS')) or 0
        if (data.get('SX')):
            reading.wind_speed_max = Decimal(data.get('SX')) * KNOTS_TO_MS or 0
            reading.wind_speed_max_kn = Decimal(data.get('SX')) or 0

        if (data.get('S')):
            reading.wind_speed = Decimal(data.get('S'))      * KNOTS_TO_MS or 0
            reading.wind_speed_kn = Decimal(data.get('S'))      or 0

        reading.rainfall = data.get('R') or EMPTY

        try:
            reading.dew_point = dew_point(float(reading.temperature),
                                          float(reading.humidity))
            reading.actual_rainfall = actual_rainfall(Decimal(reading.rainfall),
                                                      self, last_reading)
            reading.actual_pressure = actual_pressure(float(reading.temperature),
                                                      float(reading.pressure),
                                                      float(self.location.height))
        except Exception, e:
            logger.error("We didnt get enough data to do these calculations because %s"%e)

        reading.raw_data = raw_data
        reading.station = self
        reading.save()

        self.last_reading = last_reading
        self.battery_voltage = data.get('BV', EMPTY) or EMPTY
        self.save()

        return reading