123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- 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."
- )
|