123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- import os
- import time
- from datetime import datetime, timedelta
- import dateutil
- from django.db import models
- from django.utils.translation import ugettext_lazy as _
- from django.core.urlresolvers import reverse
- from django.contrib.auth.models import User
- #from template_utils.markup import formatter
- import csv
- from directory.models import Point, Place, Town
- from utils.moon_phase import moon_phase
- class StandardMetadata(models.Model):
- """
- A basic (abstract) model for metadata.
-
- Included in each model file to maintain application separation.
-
- Subclass new models from 'StandardMetadata' instead of 'models.Model'.
- """
- created = models.DateTimeField(default=datetime.now, editable=False)
- updated = models.DateTimeField(default=datetime.now, editable=False)
-
- class Meta:
- abstract = True
-
- def save(self, *args, **kwargs):
- self.updated = datetime.now()
- super(StandardMetadata, self).save(*args, **kwargs)
-
- class WeatherAlert(models.Model):
- ''' WeatherAlert model.
- Based on the NOAA weather alert feed. '''
- zone_id=models.CharField(_("NOAA zone id"), max_length=6)
- zone=models.CharField(_("NOAA zone"), max_length=255)
- updated=models.DateTimeField(_("updated"))
- summary=models.TextField(_('summary'), blank=True, null=True)
- effective=models.DateTimeField(_('effective time'))
- expires=models.DateTimeField(_("expiration time"))
- status=models.CharField(_("status"), max_length=30, blank=True, null=True)
- msg_type=models.CharField(_("message type"), max_length=30, blank=True, null=True)
- urgency=models.CharField(_("urgency"), max_length=30, blank=True, null=True)
- severity=models.CharField(_("severity"), max_length=30, blank=True, null=True)
- certainty=models.CharField(_("certainty"), max_length=30, blank=True, null=True)
- area=models.TextField(_("affected area"), blank=True, null=True)
-
- class Meta:
- verbose_name=_('weather alert')
- verbose_name_plural=_('weather alerts')
- ordering=('effective', 'expires',)
- get_latest_by='effective'
-
- def __unicode__(self):
- return u'Weather alert for %s until %s' % (self.zone, self.expires)
-
- class WeatherConditions(models.Model):
- ''' WeatherConditions model.'''
- town=models.ForeignKey(Town)
- observation_time=models.DateTimeField(_("observation time"))
- conditions=models.CharField(_("conditions"), max_length=140)
- temperature=models.FloatField(_("temperature (F)"))
- wind_dir=models.CharField(_("wind direction"), max_length=5)
- wind_speed=models.FloatField(_("wind speed (MPH)"))
- pressure=models.CharField(_("pressure (in)"), max_length=30)
- humidity=models.CharField(_("humitidy"), max_length=4)
-
- class Meta:
- verbose_name=_('weather condition')
- verbose_name_plural=_('weather conditions')
- ordering=('observation_time',)
- get_latest_by='observation_time'
-
- def __unicode__(self):
- return u'%s %s' % (self.town, self.observation_time)
- class WeatherForecast(models.Model):
- town=models.ForeignKey(Town)
- observation_time=models.DateTimeField(_("observation time (RFC822)"))
- period=models.CharField(_("period"), max_length=15)
- conditions=models.CharField(_("conditions"), max_length=255)
- high_temperature_f=models.IntegerField(_("high temperature (F)"), max_length=3, blank=True, null=True)
- low_temperature_f=models.IntegerField(_("low temperature (F)"), max_length=3, blank=True, null=True)
- wind_conditions=models.CharField(_("wind conditions"), max_length=100, blank=True, null=True)
-
- class Meta:
- verbose_name=_('weather condition')
- verbose_name_plural=_('weather conditions')
- ordering=('observation_time',)
- get_latest_by='observation_time'
-
- def __unicode__(self):
- return u'%s %s' % (self.town, self.observation_time)
- HEMISPHERES=(
- ('N', _('North')),
- ('S', _('South')),
- )
- MOON_PHASES = (
- (0, _('New')),
- (1, _('1st Q.')),
- (2, _('Full')),
- (3, _('3rd Q.')),
- )
- class AstroChart(models.Model):
- year=models.IntegerField(_('year'), max_length=4)
- dst_start=models.DateField(_('daylight savings start'))
- dst_end=models.DateField(_('daylight savings end'))
- hemisphere=models.CharField(max_length=1, choices=HEMISPHERES, default='N')
-
- class Meta:
- verbose_name=_('astronomical chart')
- verbose_name_plural=_('astronomical charts ')
- ordering=('-year',)
- get_latest_by='year'
-
- def __unicode__(self):
- return u'%s Astronomical Chart for the %s Hemisphere' % (self.year, self.hemisphere)
-
- class Day(models.Model):
- date=models.DateField(_('date'))
- chart=models.ForeignKey(AstroChart)
- sunrise=models.TimeField(_('sunrise'), null=True, blank=True)
- sunset=models.TimeField(_('sunset'), null=True, blank=True)
- moonrise=models.TimeField(_('moonrise'), null=True, blank=True)
- moonset=models.TimeField(_('moonset'), null=True, blank=True)
- moonphase=models.IntegerField(_('moon phase'), choices=MOON_PHASES, null=True, blank=True)
- moonphase_time=models.TimeField(_('moon phase time'), null=True, blank=True)
- moonphase_fraction=models.FloatField(_('moon phase fraction'), null=True, blank=True)
- civil_twilight_am=models.TimeField(_('morning civil twilight'), null=True, blank=True)
- civil_twilight_pm=models.TimeField(_('evening civil twilight'), null=True, blank=True)
- nautical_twilight_am=models.TimeField(_('morning nautical twilight'), null=True, blank=True)
- nautical_twilight_pm=models.TimeField(_('evening nautical twilight'), null=True, blank=True)
- daily_avg_temp=models.DecimalField(_('daily average temperature'), null=True, blank=True, decimal_places=2, max_digits=5)
- daily_high_temp=models.DecimalField(_('daily high temperature'), null=True, blank=True, decimal_places=2, max_digits=5)
- daily_low_temp=models.DecimalField(_('daily low temperature'), null=True, blank=True, decimal_places=2, max_digits=5)
- monthly_avg_temp=models.DecimalField(_('monthly average temperature'), null=True, blank=True, decimal_places=2, max_digits=5)
- yearly_avg_temp=models.DecimalField(_('yearly average temperature'), null=True, blank=True, decimal_places=2, max_digits=5)
-
- class Meta:
- verbose_name=_('day')
- verbose_name_plural=_('days')
- ordering=('date',)
- get_latest_by='date'
-
- def __unicode__(self):
- return u'%s' % (self.date)
-
- TIDE_STATES = (
- ('H', _('High')),
- ('L', _('Low')),
- )
- class TideChart(StandardMetadata):
- place=models.ForeignKey(Place)
- year=models.CharField(max_length=4)
- mean_range=models.FloatField()
- spring_range=models.FloatField()
- mean_tide_level=models.FloatField()
- public=models.BooleanField(_('public'), default=False)
- reference=models.ForeignKey('self', blank=True, null=True, related_name='ref_chart')
- high_tide_offset=models.FloatField(_('high tide offset'), blank=True, null=True)
- low_tide_offest=models.FloatField(_('low tide offset'), blank=True, null=True)
- high_time_offset=models.IntegerField(_('high time offset'), blank=True, null=True, max_length=2)
- low_time_offset=models.IntegerField(_('low time offset'), blank=True, null=True, max_length=2)
-
- class Meta:
- verbose_name = _('tide chart')
- verbose_name_plural = _('tide charts')
- ordering = ('place',)
-
- def __unicode__(self):
- return u'%s %s %s' % (self.place.title, "Tide Chart", self.year)
- def get_absolute_url(self):
- args = self.place.slug+'-'+self.year
- return reverse('al-tide-chart-detail', args=args)
-
- def save(self, *args, **kwargs):
- super(TideChart, self).save(*args, **kwargs)
- if self.reference:
- if not TideDay.objects.filter(tide_chart=self):
- # There are no days for this chart and it's referenced from another one
- # Let's generate the days
- self.generate_tidedays(self.reference.tideday_set.all())
-
-
- def generate_tidedays(self, tidedays):
- fill_tide=Tide.objects.create(time="12:00", level=0.0, state='L')
- overflow=False
- for ref_day in tidedays:
- new_day=TideDay.objects.create(day=ref_day.day, tide_chart=self)
- if overflow: # We overflowed from the previous day, so offset tides
- new_day.tide_1=self.generate_tide(fill_tide)
- new_day.tide_2=self.generate_tide(ref_day.tide_1)
- new_day.tide_3=self.generate_tide(ref_day.tide_2)
- new_day.tide_4=self.generate_tide(ref_day.tide_3)
- overflow=False # Reset the overflow flag
- else:
- new_day.tide_1=self.generate_tide(ref_day.tide_1)
- new_day.tide_2=self.generate_tide(ref_day.tide_2)
- new_day.tide_3=self.generate_tide(ref_day.tide_3)
- try: # Does ref_day.tide_4 exist?
- if ref_day.tide_4.time.hour==23:
- if ref_day.tide_4.state=='H':
- if (ref_day.tide_4.time.minute + self.high_time_offset) > 59:
- # Adding the offest would overflow the day, so save
- # the tide to add to the start of the next day.
- fill_tide=ref_day.tide_4
- overflow=True
- else:
- # Otherwise just save the high tide
- new_day.tide_4=self.generate_tide(ref_day.tide_4)
- else:
- # Do the same as the overflow scenario above, but for low tides
- if (ref_day.tide_4.time.minute + self.low_time_offset) > 59:
- fill_tide=ref_day.tide_4
- overflow=True
- else:
- new_day.tide_4=self.generate_tide(ref_day.tide_4)
- else: # Tide 4 is not even at 11 and will not trigger an overflow
- new_day.tide_4=self.generate_tide(ref_day.tide_4)
- except:
- pass
-
- new_day.save()
- return True
-
- def generate_tide(self, ref_tide):
- if ref_tide.state=='H':
- d=datetime(1900,1,1,ref_tide.time.hour, ref_tide.time.minute)+timedelta(minutes=self.high_time_offset)
- new_time=d.strftime("%H:%M")
- new_level=ref_tide.level*self.high_tide_offset
- new_state='H'
- else:
- d=datetime(1908,1,1,ref_tide.time.hour, ref_tide.time.minute)+timedelta(minutes=self.low_time_offset)
- new_time=d.strftime("%H:%M")
- new_level=ref_tide.level*self.low_tide_offest
- new_state='L'
- return Tide.objects.create(time=(new_time), level=(new_level), state=new_state)
-
- class Tide(models.Model):
- time=models.TimeField(_('time'))
- level=models.FloatField(_('level'), blank=True, null=True)
- state=models.CharField(max_length=1, choices=TIDE_STATES, blank=True)
-
- def __unicode__(self):
- return u'%s' % (self.time)
-
- def get_absolute_url(self):
- args = self.state + "-at-" + self.time
- return reverse('al-tide-detail', args=args)
-
- class TideDay(models.Model):
- day=models.ForeignKey(Day)
- tide_chart=models.ForeignKey(TideChart)
- tide_1=models.ForeignKey(Tide, related_name='tide_1', blank=True, null=True)
- tide_2=models.ForeignKey(Tide, related_name='tide_2', blank=True, null=True)
- tide_3=models.ForeignKey(Tide, related_name='tide_3', blank=True, null=True)
- tide_4=models.ForeignKey(Tide, related_name='tide_4', blank=True, null=True)
-
- class Meta:
- verbose_name=_('tide day')
- verbose_name_plural=_('tide days')
- ordering=('day',)
- get_latest_by='day'
-
- def __unicode__(self):
- return u'%s' % (self.day)
- class AstroChartUpload(models.Model):
- data_file = models.FileField(_('data file (.csv)'), upload_to="temp",
- help_text=_('A comma separeted list of tide data for your particular location.'))
- chart=models.ForeignKey(AstroChart, help_text=_('Select a chart to add the astronomical data to.'))
- dst=models.BooleanField(_('correct for DST'), default=False)
-
- class Meta:
- verbose_name = _('astronomical chart upload')
- verbose_name_plural = _('astronomical chart uploads')
-
-
- def save(self, *args, **kwargs):
- super(AstroChartUpload, self).save(*args, **kwargs)
- self.process_datafile()
- super(AstroChartUpload, self).delete()
-
-
- def process_datafile(self):
- try:
- data=csv.reader(open(self.data_file.path))
- except:
- raise Exception('There is a problem with your csv file.')
- for row in data:
- print row[0]
- day=Day.objects.create(date=row[0], chart=self.chart)
- if self.dst:
- if self.chart.dst_start < datetime.strptime(day.date, "%Y-%m-%d").date() < self.chart.dst_end:
- delta=timedelta(hours=1)
- else:
- delta=timedelta(hours=0)
- else:
- delta=timedelta(hours=0)
- # B/c python datetime blows:
- # Take string in csv file, convert to time, convert to datetime, add delta,
- # convert to timetuple and convert to string for insertion to db.
- day.sunrise=time.strftime("%H:%M", (datetime(*time.strptime(row[1], "%H%M")[0:5])+delta).timetuple())
- day.sunset=time.strftime("%H:%M", (datetime(*time.strptime(row[2], "%H%M")[0:5])+delta).timetuple())
- if row[3]:
- day.moonrise=time.strftime("%H:%M", (datetime(*time.strptime(row[3], "%H%M")[0:5])+delta).timetuple())
- if row[4]:
- day.moonset=time.strftime("%H:%M", (datetime(*time.strptime(row[4], "%H%M")[0:5])+delta).timetuple())
- day.civil_twilight_am=time.strftime("%H:%M", (datetime(*time.strptime(row[5], "%H%M")[0:5])+delta).timetuple())
- day.civil_twilight_pm=time.strftime("%H:%M", (datetime(*time.strptime(row[6], "%H%M")[0:5])+delta).timetuple())
- day.nautical_twilight_am=time.strftime("%H:%M", (datetime(*time.strptime(row[7], "%H%M")[0:5])+delta).timetuple())
- day.nautical_twilight_pm=time.strftime("%H:%M", (datetime(*time.strptime(row[8], "%H%M")[0:5])+delta).timetuple())
- if row[9]:
- day.moonphase=row[9]
- day.moonphase_time=time.strftime("%H:%M", (datetime(*time.strptime(row[10], "%H%M")[0:5])+delta).timetuple())
- day.save()
-
- class TideChartUpload(models.Model):
- data_file = models.FileField(_('data file (.csv)'), upload_to="temp",
- help_text=_('A comma separeted list of tide data for your particular location.'))
- chart = models.ForeignKey(TideChart, help_text=_('Select a chart to add the tide data to.'))
-
- class Meta:
- verbose_name = _('tide chartupload')
- verbose_name_plural = _('tide chart uploads')
-
- def save(self, *args, **kwargs):
- super(TideChartUpload, self).save(*args, **kwargs)
- self.process_datafile()
- super(TideChartUpload, self).delete()
-
-
- def process_datafile(self):
- try:
- tides=BeautifulStoneSoup(open(self.data_file.path))
- except:
- raise Exception('There is a problem with your XML file or you do not have BeautifulSoup installed.')
- try:
- days = Day.objects.filter(date__year=chart.year)
- for day in days:
- '''
- XML for tide data from tidesandcurrents.noaa.gov:
- <item>
- <date>YYYY-MM-DD</date>
- <time>HH:MM A/P</time>
- <level_std>11.2 ft</level>
- <level_mtr>2.1 m</leve>
- <state>H/L</state>
- </item>
-
- '''
-
- days_tides = tides.findAll(text=day.date.replace('/','-'))
- tideday=TideDay.objects.create(day=day, tide_chart=self.chart)
- tide_1=days_tides[0].parent.parent
- tide_2=days_tides[1].parent.parent
- tide_3=days_tides[2].parent.parent
- try:
- tide_4=days_tides[3].parent.parent
- except:
- pass
- tideday.tide_1 = Tide.objects.create(time=tide_1.time.contents[0], level=tide_1.predictions_in_ft.contents[0], state=tide_1.highlow.conetnts[0])
- tideday.tide_2 = Tide.objects.create(time=tide_2.time.contents[0], level=tide_2.predictions_in_ft.contents[0], state=tide_2.highlow.conetnts[0])
- tideday.tide_3 = Tide.objects.create(time=tide_3.time.contents[0], level=tide_3.predictions_in_ft.contents[0], state=tide_3.highlow.conetnts[0])
- if tide_4:
- tideday.tide_4 = Tide.objects.create(time=tide_4.time.contents[0], level=tide_4.predictions_in_ft.contents[0], state=tide_4.highlow.conetnts[0])
- tideday.save()
- except:
- raise Exception('Tide data must be loaded after astronomical data for any given date.')
-
|