def test_add_archive_records(self):
        # Test adding records using a 'with' statement:
        with weewx.archive.Archive.open_with_create(self.archive_db_dict, archive_schema) as archive:
            archive.addRecord(genRecords())

        # Now test to see what's in there:            
        with weewx.archive.Archive.open(self.archive_db_dict) as archive:
            self.assertEqual(archive.firstGoodStamp(), start_ts)
            self.assertEqual(archive.lastGoodStamp(), stop_ts)
            self.assertEqual(archive.std_unit_system, std_unit_system)
            
            expected_iterator = genRecords()
            for _rec in archive.genBatchRecords():
                try:
                    _expected_rec = expected_iterator.next()
                except StopIteration:
                    break
                # Check that the missing windSpeed is None, then remove it in order to do the compare:
                self.assertEqual(_rec.pop('windSpeed'), None)
                self.assertEqual(_expected_rec, _rec)
                
            # Test adding an existing record. It should just quietly swallow it:
            existing_record = {'dateTime': start_ts, 'interval': interval, 'usUnits' : 1, 'outTemp': 68.0}
            archive.addRecord(existing_record)
            
            # Test changing the unit system. It should raise a ValueError exception:
            metric_record = {'dateTime': stop_ts + interval, 'interval': interval, 'usUnits' : 16, 'outTemp': 20.0}
            self.assertRaises(ValueError, archive.addRecord, metric_record)
def loader(config_dict, engine):

    # This loader uses a bit of a hack to have the simulator resume at a later
    # time. It's not bad, but I'm not enthusiastic about having special
    # knowledge about the database in a driver, albeit just the loader.

    start_ts = resume_ts = None
    if 'start' in config_dict['Simulator']:
        # A start has been specified. Extract the time stamp.
        start_tt = time.strptime(config_dict['Simulator']['start'], "%Y-%m-%d %H:%M")
        start_ts = time.mktime(start_tt)
        # If the 'resume' keyword is present and True, then get the last archive
        # record out of the database and resume with that.
        if int(config_dict['Simulator'].get('resume', 1)):
            import weewx.archive
            # Resume with the last time in the database. If there is no such
            # time, then fall back to the time specified in the configuration
            # dictionary.
            archive_db = config_dict['StdArchive']['archive_database']        
            archive_db_dict = config_dict['Databases'][archive_db]
            try:
                with weewx.archive.Archive.open(archive_db_dict) as archive:
                    resume_ts = archive.lastGoodStamp()
            except weedb.OperationalError:
                pass
        else:
            # The resume keyword is not present. Start with the seed time:
            resume_ts = start_ts
            
    station = Simulator(start_time=start_ts, resume_time=resume_ts, **config_dict['Simulator'])
    
    return station
Exemple #3
0
    def postData(self, archive, time_ts):
        """Post data to CWOP, using the CWOP protocol."""
        
        _last_ts = archive.lastGoodStamp()

        # There are a variety of reasons to skip a post to CWOP.

        # 1. They do not allow backfilling, so there is no reason
        # to post anything other than the latest record:
        if time_ts != _last_ts:
            raise SkippedPost, "CWOP: Record %s is not last record" %\
                    (weeutil.weeutil.timestamp_to_string(time_ts), )

        # 2. No reason to post an old out-of-date record.
        _how_old = time.time() - time_ts
        if self.stale and _how_old > self.stale:
            raise SkippedPost, "CWOP: Record %s is stale (%d > %d)." %\
                    (weeutil.weeutil.timestamp_to_string(time_ts), _how_old, self.stale)
        
        # 3. Finally, we don't want to post more often than the interval
        if self._lastpost and time_ts - self._lastpost < self.interval:
            raise SkippedPost, "CWOP: Wait interval (%d) has not passed." %\
                    (self.interval, )
        
        # Get the data record for this time:
        _record = self.extractRecordFrom(archive, time_ts)

        # Send it to its destination:
        self.sendRecord(_record)

        self._lastpost = time_ts
Exemple #4
0
def loader(config_dict, engine):

    # This loader uses a bit of a hack to have the simulator resume at a later
    # time. It's not bad, but I'm not enthusiastic about having special
    # knowledge about the database in a driver, albeit just the loader.

    start_ts = resume_ts = None
    if 'start' in config_dict['Simulator']:
        # A start has been specified. Extract the time stamp.
        start_tt = time.strptime(config_dict['Simulator']['start'], "%Y-%m-%d %H:%M")
        start_ts = time.mktime(start_tt)
        # If the 'resume' keyword is present and True, then get the last archive
        # record out of the database and resume with that.
        if int(config_dict['Simulator'].get('resume', 1)):
            import weewx.archive
            # Resume with the last time in the database. If there is no such
            # time, then fall back to the time specified in the configuration
            # dictionary.
            archive_db = config_dict['StdArchive']['archive_database']        
            archive_db_dict = config_dict['Databases'][archive_db]
            try:
                with weewx.archive.Archive.open(archive_db_dict) as archive:
                    resume_ts = archive.lastGoodStamp()
            except weedb.OperationalError:
                pass
        else:
            # The resume keyword is not present. Start with the seed time:
            resume_ts = start_ts
            
    station = Simulator(start_time=start_ts, resume_time=resume_ts, **config_dict['Simulator'])
    
    return station
Exemple #5
0
 def main():
     usage_string ="""Usage: 
     
     restful.py config_path upload-site [--today] [--last]
     
     Arguments:
     
       config_path: Path to weewx.conf
       
       upload-site: Either "Wunderground", "PWSweather", or "CWOP" 
       
     Options:
     
         --today: Publish all of today's day
         
         --last: Just do the last archive record. [default]
       """
     parser = OptionParser(usage=usage_string)
     parser.add_option("-t", "--today", action="store_true", dest="do_today", help="Publish today\'s records")
     parser.add_option("-l", "--last", action="store_true", dest="do_last", help="Publish the last archive record only")
     (options, args) = parser.parse_args()
     
     if len(args) < 2:
         sys.stderr.write("Missing argument(s).\n")
         sys.stderr.write(parser.parse_args(["--help"]))
         exit()
         
     if options.do_today and options.do_last:
         sys.stderr.write("Choose --today or --last, not both\n")
         sys.stderr.write(parser.parse_args(["--help"]))
         exit()
 
     if not options.do_today and not options.do_last:
         options.do_last = True
         
     config_path = args[0]
     site        = args[1]
     
     weewx.debug = 1
     
     try :
         config_dict = configobj.ConfigObj(config_path, file_error=True)
     except IOError:
         print "Unable to open configuration file ", config_path
         exit()
         
     # Open up the main database archive
     archiveFilename = os.path.join(config_dict['Station']['WEEWX_ROOT'], 
                                    config_dict['Archive']['archive_file'])
     archive = weewx.archive.Archive(archiveFilename)
     
     stop_ts  = archive.lastGoodStamp()
     start_ts = weeutil.weeutil.startOfDay(stop_ts) if options.do_today else stop_ts
     
     publish(config_dict, site, archive, start_ts, stop_ts )
    def run(self):

        self.setup()

        # Open up the main database archive
        archiveFilename = os.path.join(
            self.config_dict["Station"]["WEEWX_ROOT"], self.config_dict["Archive"]["archive_file"]
        )
        archive = weewx.archive.Archive(archiveFilename)

        stop_ts = archive.lastGoodStamp() if self.gen_ts is None else self.gen_ts

        # Generate any images
        self.genImages(archive, stop_ts)
 def getCurrentRec(self):
     # Open up the main database archive
     archiveFilename = os.path.join(self.config_dict['Station']['WEEWX_ROOT'], 
                                    self.config_dict['Archive']['archive_file'])
     archive = weewx.archive.Archive(archiveFilename)
 
     self.stop_ts  = archive.lastGoodStamp() if self.gen_ts is None else self.gen_ts
     self.start_ts = archive.firstGoodStamp()
     
     # Get a dictionary with the current record:
     current_dict = archive.getRecord(self.stop_ts)
     
     # Wrap it in a ValueDict
     currentRec = weewx.units.ValueDict(current_dict, self.unit_info)
     
     return currentRec
def configDatabases(archive_db_dict, stats_db_dict):
    """Configures the main and stats databases."""

    # Check to see if it already exists and is configured correctly.
    try:
        with weewx.archive.Archive.open(archive_db_dict) as archive:
            if archive.firstGoodStamp() == start_ts and archive.lastGoodStamp() == stop_ts:
                # Database already exists. We're done.
                return
    except:
        pass
        
    # Delete anything that might already be there.
    try:
        weedb.drop(archive_db_dict)
    except:
        pass
    
    # Now build a new one:
    with weewx.archive.Archive.open_with_create(archive_db_dict, user.schemas.defaultArchiveSchema) as archive:
    
        # Because this can generate voluminous log information,
        # suppress all but the essentials:
        syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_ERR))
        
        # Now generate and add the fake records to populate the database:
        t1= time.time()
        archive.addRecord(genFakeRecords())
        t2 = time.time()
        print "Time to create synthetic archive database = %6.2fs" % (t2-t1,)
        # Now go back to regular logging:
        syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_DEBUG))
    
        # Delete any old stats database:
        try:
            weedb.drop(stats_db_dict)
        except weedb.NoDatabase:
            pass
        # Now create and configure a new one:
        with weewx.stats.StatsDb.open_with_create(stats_db_dict, user.schemas.defaultStatsSchema) as stats:
            t1 = time.time()
            # Now backfill the stats database from the main archive database.
            nrecs = stats.backfillFrom(archive)
            t2 = time.time()
            print "Time to backfill stats database with %d records: %6.2fs" % (nrecs, t2-t1)
 def test_empty_archive(self):
     archive = weewx.archive.Archive.open_with_create(self.archive_db_dict, archive_schema)
     self.assertEqual(archive.firstGoodStamp(), None)
     self.assertEqual(archive.lastGoodStamp(), None)
     self.assertEqual(archive.getRecord(123456789), None)
     self.assertEqual(archive.getRecord(123456789, max_delta=1800), None)
#       python /home/weewx/bin/examples/thisfile.py  /home/weewx/weewx.conf
#

import string
import weeutil.weeutil
import weewx.stats

import sys

if __name__ == '__main__':

    # get the min/max timestamp from the archive
    import weewx.archive
    archive = weewx.archive.Archive('/home/weewx/archive/weewx.sdb')
    myMinStamp = archive.firstGoodStamp()
    myMaxStamp = archive.lastGoodStamp()

    timeSpan = weeutil.weeutil.TimeSpan(myMinStamp, myMaxStamp)

    statsdb = weewx.stats.StatsReadonlyDb('/tmp/stats.sdb')
    spanStats = weewx.stats.TimeSpanStats(statsdb, timeSpan)

    # Print maximum temperature for each month in the year:
    print "========================="
    print "outTemp max/min per month"
    print "========================="
    for monthStats in spanStats.months:
        m = monthStats.outTemp.maxtime.format("%Y-%m")
        print "%s : %s %s" % (m, monthStats.outTemp.max,
                              monthStats.outTemp.min)
    def genImages(self, archive, time_ts):
        """Generate the images.
        
        The time scales will be chosen to include the given timestamp, with nice beginning
        and ending times.
    
        time_ts: The time around which plots are to be generated. This will also be used as
        the bottom label in the plots. [optional. Default is to use the time of the last record
        in the archive database.]
        """
        t1 = time.time()
        ngen = 0

        if not time_ts:
            time_ts = archive.lastGoodStamp()
            if not time_ts:
                time_ts = time.time()

        # Loop over each time span class (day, week, month, etc.):
        for timespan in self.image_dict.sections:

            # Now, loop over all plot names in this time span class:
            for plotname in self.image_dict[timespan].sections:

                # Accumulate all options from parent nodes:
                plot_options = weeutil.weeutil.accumulateLeaves(self.image_dict[timespan][plotname])

                image_root = os.path.join(self.weewx_root, plot_options["HTML_ROOT"])
                # Get the path of the file that the image is going to be saved to:
                img_file = os.path.join(image_root, "%s.png" % plotname)

                # Check whether this plot needs to be done at all:
                ai = plot_options.as_int("aggregate_interval") if plot_options.has_key("aggregate_interval") else None
                if skipThisPlot(time_ts, ai, img_file):
                    continue

                # Create the subdirectory that the image is to be put in.
                # Wrap in a try block in case it already does.
                try:
                    os.makedirs(os.path.dirname(img_file))
                except:
                    pass

                # Calculate a suitable min, max time for the requested time span
                (minstamp, maxstamp, timeinc) = weeplot.utilities.scaletime(
                    time_ts - plot_options.as_int("time_length"), time_ts
                )

                # Create a new instance of a time plot and start adding to it
                plot = weeplot.genplot.TimePlot(plot_options)

                # Set the min, max time axis
                plot.setXScaling((minstamp, maxstamp, timeinc))

                # Set the y-scaling, using any user-supplied hints:
                plot.setYScaling(weeutil.weeutil.convertToFloat(plot_options.get("yscale")))

                # Get a suitable bottom label:
                bottom_label_format = plot_options.get("bottom_label_format", "%m/%d/%y %H:%M")
                bottom_label = time.strftime(bottom_label_format, time.localtime(time_ts))
                plot.setBottomLabel(bottom_label)

                # Loop over each line to be added to the plot.
                for line_name in self.image_dict[timespan][plotname].sections:

                    # Accumulate options from parent nodes.
                    line_options = weeutil.weeutil.accumulateLeaves(self.image_dict[timespan][plotname][line_name])

                    # See what SQL variable type to use for this line. By default,
                    # use the section name.
                    var_type = line_options.get("data_type", line_name)

                    # Add a unit label. NB: all will get overwritten except the last.
                    # Get the label from the configuration dictionary.
                    # TODO: Allow multiple unit labels, one for each plot line?
                    unit_label = line_options.get("y_label", self.unit_label_dict.get(var_type, ""))
                    # PIL cannot handle UTF-8. So, convert to Latin1. Also, strip off
                    # any leading and trailing whitespace so it's easy to center
                    unit_label = weeutil.weeutil.utf8_to_latin1(unit_label).strip()
                    plot.setUnitLabel(unit_label)

                    # See if a line label has been explicitly requested:
                    label = line_options.get("label")
                    if not label:
                        # No explicit label. Is there a generic one?
                        # If not, then the SQL type will be used instead
                        label = self.title_dict.get(var_type, var_type)
                    # Convert to Latin-1
                    label = weeutil.weeutil.utf8_to_latin1(label)

                    # See if a color has been explicitly requested.
                    color_str = line_options.get("color")
                    color = int(color_str, 0) if color_str is not None else None

                    # Get the line width, if explicitly requested.
                    width_str = line_options.get("width")
                    width = int(width_str) if width_str is not None else None

                    # Get the type of line ("bar', 'line', or 'vector')
                    line_type = line_options.get("plot_type", "line")

                    if line_type == "vector":
                        vector_rotate_str = line_options.get("vector_rotate")
                        vector_rotate = -float(vector_rotate_str) if vector_rotate_str is not None else None
                    else:
                        vector_rotate = None

                    # Look for aggregation type:
                    aggregate_type = line_options.get("aggregate_type")
                    if aggregate_type in (None, "", "None", "none"):
                        # No aggregation specified.
                        aggregate_type = None
                        aggregate_interval = None
                    else:
                        try:
                            # Aggregation specified. Get the interval.
                            aggregate_interval = line_options.as_int("aggregate_interval")
                        except KeyError:
                            syslog.syslog(
                                syslog.LOG_ERR,
                                "genimages: aggregate interval required for aggregate type %s" % aggregate_type,
                            )
                            syslog.syslog(syslog.LOG_ERR, "genimages: line type %s skipped" % var_type)
                            continue

                    # Get the time and data vectors from the database:
                    (time_vec_t, data_vec_t) = archive.getSqlVectorsExtended(
                        var_type, minstamp, maxstamp, aggregate_interval, aggregate_type
                    )

                    new_time_vec_t = self.unit_info.convert(time_vec_t)
                    new_data_vec_t = self.unit_info.convert(data_vec_t)
                    # Add the line to the emerging plot:
                    plot.addLine(
                        weeplot.genplot.PlotLine(
                            new_time_vec_t[0],
                            new_data_vec_t[0],
                            label=label,
                            color=color,
                            width=width,
                            line_type=line_type,
                            interval=aggregate_interval,
                            vector_rotate=vector_rotate,
                        )
                    )

                # OK, the plot is ready. Render it onto an image
                image = plot.render()

                # Now save the image
                image.save(img_file)
                ngen += 1
        t2 = time.time()

        syslog.syslog(syslog.LOG_INFO, "genimages: Generated %d images in %.2f seconds" % (ngen, t2 - t1))