コード例 #1
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        station_id: WindGuru station identifier

        password: WindGuru password
        """
        super(WindGuru, self).__init__(engine, config_dict)
        log.info("service version is %s", VERSION)
        try:
            site_dict = config_dict['StdRESTful']['WindGuru']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['station_id']
            site_dict['password']
        except KeyError as e:
            log.error("Data will not be posted: Missing option %s", e)
            return
        site_dict['manager_dict'] = weewx.manager.get_manager_dict(
            config_dict['DataBindings'], config_dict['Databases'],
            'wx_binding')

        self.archive_queue = queue.Queue()
        self.archive_thread = WindGuruThread(self.archive_queue, **site_dict)
        self.archive_thread.start()
        self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
        log.info("Data will be uploaded for %s", site_dict['station_id'])
コード例 #2
0
    def __init__(self, engine, config_dict):
        """This service recognizes standard restful options plus the following:

        stationid = INSERT_STATIONID_HERE
        password  = INSERT_PASSWORD_HERE

        latitude: Station latitude in decimal degrees
        Default is station latitude

        longitude: Station longitude in decimal degrees
        Default is station longitude
        """
        super(Weather365, self).__init__(engine, config_dict)
        log.info("service version is %s", VERSION)
        try:
            site_dict = config_dict['StdRESTful']['Weather365']
            site_dict = accumulateLeaves(site_dict, max_level=1)
            site_dict['stationid']
            site_dict['password']
        except KeyError as e:
            log.error("Data will not be posted: Missing option %s", e)
            return
        site_dict.setdefault('latitude', engine.stn_info.latitude_f)
        site_dict.setdefault('longitude', engine.stn_info.longitude_f)
        site_dict.setdefault('altitude', engine.stn_info.altitude_vt[0])
        site_dict['manager_dict'] = weewx.manager.get_manager_dict(
            config_dict['DataBindings'], config_dict['Databases'],
            'wx_binding')

        self.archive_queue = queue.Queue()
        self.archive_thread = Weather365Thread(self.archive_queue, **site_dict)
        self.archive_thread.start()
        self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
        log.info("Data will be uploaded for station id %s",
                 site_dict['stationid'])
コード例 #3
0
def check_enable(cfg_dict, service, *args):

    try:
        wdsupp_dict = accumulateLeaves(cfg_dict[service], max_level=1)
    except KeyError:
        log.debug("weewxwd3 check_enable: '%s' No config info. Skipped.",
                  service)
        return None

    # Check to see whether all the needed options exist, and none of them have
    # been set to 'replace_me':
    try:
        for option in args:
            if wdsupp_dict[option] == 'replace_me':
                raise KeyError(option)
    except KeyError as e:
        log.debug("weewxwd3 check_enable: '%s' Missing option '%s'", service,
                  e)
        return None

    return wdsupp_dict
コード例 #4
0
    def genImages(self, gen_ts):
        """Generate the images.

        The time scales will be chosen to include the given timestamp, with
        nice beginning and ending times.

        gen_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 database.]
        """
        t1 = time.time()
        ngen = 0

        # determine how much logging is desired
        log_success = to_bool(search_up(self.image_dict, 'log_success', True))

        # 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 = accumulateLeaves(
                    self.image_dict[timespan][plotname])

                plotgen_ts = gen_ts
                if not plotgen_ts:
                    binding = plot_options['data_binding']
                    db_manager = self.db_binder.get_manager(binding)
                    plotgen_ts = db_manager.lastGoodStamp()
                    if not plotgen_ts:
                        plotgen_ts = time.time()

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

                ai = to_int(plot_options.get('aggregate_interval'))
                # Check whether this plot needs to be done at all:
                if skipThisPlot(plotgen_ts, ai, img_file):
                    continue

                # skip image files that are fresh, but only if staleness is defined
                stale = to_int(plot_options.get('stale_age'))
                if stale is not None:
                    t_now = time.time()
                    try:
                        last_mod = os.path.getmtime(img_file)
                        if t_now - last_mod < stale:
                            log.debug("Skip '%s': last_mod=%s age=%s stale=%s",
                                      img_file, last_mod, t_now - last_mod,
                                      stale)
                            continue
                    except os.error:
                        pass

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

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

                # Calculate a suitable min, max time for the requested time.
                (minstamp, maxstamp, timeinc) = weeplot.utilities.scaletime(
                    plotgen_ts - int(plot_options.get('time_length', 86400)),
                    plotgen_ts)
                # Override the x interval if the user has given an explicit interval:
                timeinc_user = to_int(plot_options.get('x_interval'))
                if timeinc_user is not None:
                    timeinc = timeinc_user
                plot.setXScaling((minstamp, maxstamp, timeinc))

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

                # 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(plotgen_ts))
                plot.setBottomLabel(bottom_label)

                # Set day/night display
                plot.setLocation(self.stn_info.latitude_f,
                                 self.stn_info.longitude_f)
                plot.setDayNight(
                    to_bool(plot_options.get('show_daynight', False)),
                    weeplot.utilities.tobgr(
                        plot_options.get('daynight_day_color', '0xffffff')),
                    weeplot.utilities.tobgr(
                        plot_options.get('daynight_night_color', '0xf0f0f0')),
                    weeplot.utilities.tobgr(
                        plot_options.get('daynight_edge_color', '0xefefef')))

                # 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 = 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)

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

                    # Now its time to find and hit the database:
                    binding = line_options['data_binding']
                    db_manager = self.db_binder.get_manager(binding)
                    start_vec_t, stop_vec_t, data_vec_t = weewx.xtypes.get_series(
                        var_type,
                        TimeSpan(minstamp, maxstamp),
                        db_manager,
                        aggregate_type=aggregate_type,
                        aggregate_interval=aggregate_interval)

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

                    if aggregate_type and aggregate_type.lower() in (
                            'avg', 'max', 'min') and plot_type != 'bar':
                        # Put the point in the middle of the aggregate_interval for these aggregation types
                        start_vec_t = ValueTuple([
                            x - aggregate_interval / 2.0
                            for x in start_vec_t[0]
                        ], start_vec_t[1], start_vec_t[2])
                        stop_vec_t = ValueTuple([
                            x - aggregate_interval / 2.0 for x in stop_vec_t[0]
                        ], stop_vec_t[1], stop_vec_t[2])

                    # Do any necessary unit conversions:
                    new_start_vec_t = self.converter.convert(start_vec_t)
                    new_stop_vec_t = self.converter.convert(stop_vec_t)
                    new_data_vec_t = self.converter.convert(data_vec_t)

                    # Add a unit label. NB: all will get overwritten except the
                    # last. Get the label from the configuration dictionary.
                    unit_label = line_options.get(
                        'y_label',
                        weewx.units.get_label_string(self.formatter,
                                                     self.converter, var_type))
                    # Strip off any leading and trailing whitespace so it's
                    # easy to center
                    plot.setUnitLabel(unit_label.strip())

                    # See if a line label has been explicitly requested:
                    label = line_options.get('label')
                    if not label:
                        # No explicit label. Look up a generic one. NB: title_dict is a KeyDict which
                        # will substitute the key if the value is not in the dictionary.
                        label = self.title_dict[var_type]

                    # See if a color has been explicitly requested.
                    color = line_options.get('color')
                    if color is not None:
                        color = weeplot.utilities.tobgr(color)
                    fill_color = line_options.get('fill_color')
                    if fill_color is not None:
                        fill_color = weeplot.utilities.tobgr(fill_color)

                    # Get the line width, if explicitly requested.
                    width = to_int(line_options.get('width'))

                    interval_vec = None
                    gap_fraction = None

                    # Some plot types require special treatments:
                    if plot_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

                        if plot_type == 'bar':
                            interval_vec = [
                                x[1] - x[0]
                                for x in zip(new_start_vec_t.value,
                                             new_stop_vec_t.value)
                            ]
                        elif plot_type == 'line':
                            gap_fraction = to_float(
                                line_options.get('line_gap_fraction'))
                        if gap_fraction is not None:
                            if not 0 < gap_fraction < 1:
                                log.error(
                                    "Gap fraction %5.3f outside range 0 to 1. Ignored.",
                                    gap_fraction)
                                gap_fraction = None

                    # Get the type of line (only 'solid' or 'none' for now)
                    line_type = line_options.get('line_type', 'solid')
                    if line_type.strip().lower() in ['', 'none']:
                        line_type = None

                    marker_type = line_options.get('marker_type')
                    marker_size = to_int(line_options.get('marker_size', 8))

                    # Add the line to the emerging plot:
                    plot.addLine(
                        weeplot.genplot.PlotLine(new_stop_vec_t[0],
                                                 new_data_vec_t[0],
                                                 label=label,
                                                 color=color,
                                                 fill_color=fill_color,
                                                 width=width,
                                                 plot_type=plot_type,
                                                 line_type=line_type,
                                                 marker_type=marker_type,
                                                 marker_size=marker_size,
                                                 bar_width=interval_vec,
                                                 vector_rotate=vector_rotate,
                                                 gap_fraction=gap_fraction))

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

                try:
                    # Now save the image
                    image.save(img_file, optimize=True)
                    ngen += 1
                except IOError as e:
                    log.error("Unable to save to file '%s' %s:", img_file, e)
        t2 = time.time()

        if log_success:
            log.info("Generated %d images for report %s in %.2f seconds", ngen,
                     self.skin_dict['REPORT_NAME'], t2 - t1)
コード例 #5
0
    def generate(self, section, gen_ts):
        """Generate one or more reports for the indicated section.  Each
        section in a period is a report.  A report has one or more templates.

        section: A ConfigObj dictionary, holding the templates to be
        generated.  Any subsections in the dictionary will be recursively
        processed as well.
        
        gen_ts: The report will be current to this time.
        """

        ngen = 0
        # Go through each subsection (if any) of this section,
        # generating from any templates they may contain
        for subsection in section.sections:
            # Sections 'SummaryByMonth' and 'SummaryByYear' imply summarize_by
            # certain time spans
            if 'summarize_by' not in section[subsection]:
                if subsection in CheetahGenerator.generator_dict:
                    section[subsection]['summarize_by'] = subsection
            # Call recursively, to generate any templates in this subsection
            ngen += self.generate(section[subsection], gen_ts)

        # We have finished recursively processing any subsections in this
        # section. Time to do the section itself. If there is no option
        # 'template', then there isn't anything to do. Return.
        if 'template' not in section:
            return ngen

        # Change directory to the skin subdirectory.  We use absolute paths
        # for cheetah, so the directory change is not necessary for generating
        # files.  However, changing to the skin directory provides a known
        # location so that calls to os.getcwd() in any templates will return
        # a predictable result.
        os.chdir(
            os.path.join(self.config_dict['WEEWX_ROOT'],
                         self.skin_dict['SKIN_ROOT'],
                         self.skin_dict.get('skin', '')))

        report_dict = accumulateLeaves(section)

        (template, dest_dir, encoding,
         default_binding) = self._prepGen(report_dict)

        # Get start and stop times
        default_archive = self.db_binder.get_manager(default_binding)
        start_ts = default_archive.firstGoodStamp()
        if not start_ts:
            log.info('Skipping template %s: cannot find start time',
                     section['template'])
            return ngen

        if gen_ts:
            record = default_archive.getRecord(
                gen_ts, max_delta=to_int(report_dict.get('max_delta')))
            if record:
                stop_ts = record['dateTime']
            else:
                log.info(
                    'Skipping template %s: generate time %s not in database',
                    section['template'], timestamp_to_string(gen_ts))
                return ngen
        else:
            stop_ts = default_archive.lastGoodStamp()

        # Get an appropriate generator function
        summarize_by = report_dict['summarize_by']
        if summarize_by in CheetahGenerator.generator_dict:
            _spangen = CheetahGenerator.generator_dict[summarize_by]
        else:
            # Just a single timespan to generate. Use a lambda expression.
            _spangen = lambda start_ts, stop_ts: [
                weeutil.weeutil.TimeSpan(start_ts, stop_ts)
            ]

        # Use the generator function
        for timespan in _spangen(start_ts, stop_ts):
            start_tt = time.localtime(timespan.start)
            stop_tt = time.localtime(timespan.stop)

            if summarize_by in CheetahGenerator.format_dict:
                # This is a "SummaryBy" type generation. If it hasn't been done already, save the
                # date as a string, to be used inside the document
                date_str = time.strftime(
                    CheetahGenerator.format_dict[summarize_by], start_tt)
                if date_str not in self.outputted_dict[summarize_by]:
                    self.outputted_dict[summarize_by].append(date_str)
                # For these "SummaryBy" generations, the file name comes from the start of the timespan:
                _filename = self._getFileName(template, start_tt)
            else:
                # This is a "ToDate" generation. File name comes
                # from the stop (i.e., present) time:
                _filename = self._getFileName(template, stop_tt)

            # Get the absolute path for the target of this template
            _fullname = os.path.join(dest_dir, _filename)

            # Skip summary files outside the timespan
            if report_dict['summarize_by'] in CheetahGenerator.generator_dict \
                    and os.path.exists(_fullname) \
                    and not timespan.includesArchiveTime(stop_ts):
                continue

            # skip files that are fresh, but only if staleness is defined
            stale = to_int(report_dict.get('stale_age'))
            if stale is not None:
                t_now = time.time()
                try:
                    last_mod = os.path.getmtime(_fullname)
                    if t_now - last_mod < stale:
                        log.debug("Skip '%s': last_mod=%s age=%s stale=%s",
                                  _filename, last_mod, t_now - last_mod, stale)
                        continue
                except os.error:
                    pass

            searchList = self._getSearchList(encoding, timespan,
                                             default_binding)
            tmpname = _fullname + '.tmp'

            try:
                # Cheetah V2 will crash if given a template file name in Unicode. So,
                # be prepared to catch the exception and convert to ascii:
                try:
                    # TODO: Look into cacheing the compiled template.
                    compiled_template = Cheetah.Template.Template(
                        file=template,
                        searchList=searchList,
                        filter='assure_unicode',
                        filtersLib=weewx.cheetahgenerator)
                except TypeError:
                    compiled_template = Cheetah.Template.Template(
                        file=template.encode('ascii', 'ignore'),
                        searchList=searchList,
                        filter='assure_unicode',
                        filtersLib=weewx.cheetahgenerator)

                unicode_string = compiled_template.respond()

                if encoding == 'html_entities':
                    byte_string = unicode_string.encode(
                        'ascii', 'xmlcharrefreplace')
                elif encoding == 'strict_ascii':
                    byte_string = unicode_string.encode('ascii', 'ignore')
                else:
                    byte_string = unicode_string.encode('utf8')

                # Open in binary mode. We are writing a byte-string, not a string
                with open(tmpname, mode='wb') as fd:
                    fd.write(byte_string)
                os.rename(tmpname, _fullname)

            except Exception as e:
                # We would like to get better feedback when there are cheetah
                # compiler failures, but there seem to be no hooks for this.
                # For example, if we could get cheetah to emit the source
                # on which the compiler is working, one could compare that with
                # the template to figure out exactly where the problem is.
                # In Cheetah.Compile.ModuleCompiler the source is manipulated
                # a bit then handed off to parserClass.  Unfortunately there
                # are no hooks to intercept the source and spit it out.  So
                # the best we can do is indicate the template that was being
                # processed when the failure ocurred.
                log.error("Generate failed with exception '%s'", type(e))
                log.error("**** Ignoring template %s", template)
                log.error("**** Reason: %s", e)
                weeutil.logger.log_traceback(log.error, "****  ")
            else:
                ngen += 1
            finally:
                try:
                    os.unlink(tmpname)
                except OSError:
                    pass

        return ngen
コード例 #6
0
ファイル: cheetahgenerator.py プロジェクト: hoetzgit/hesweewx
    def generate(self, section, section_name, gen_ts):
        """Generate one or more reports for the indicated section.  Each
        section in a period is a report.  A report has one or more templates.

        section: A ConfigObj dictionary, holding the templates to be
        generated.  Any subsections in the dictionary will be recursively
        processed as well.
        
        gen_ts: The report will be current to this time.
        """

        ngen = 0
        # Go through each subsection (if any) of this section,
        # generating from any templates they may contain
        for subsection in section.sections:
            # Sections 'SummaryByMonth' and 'SummaryByYear' imply summarize_by
            # certain time spans
            if 'summarize_by' not in section[subsection]:
                if subsection in CheetahGenerator.generator_dict:
                    section[subsection]['summarize_by'] = subsection
            # Call recursively, to generate any templates in this subsection
            ngen += self.generate(section[subsection], subsection, gen_ts)

        # We have finished recursively processing any subsections in this
        # section. Time to do the section itself. If there is no option
        # 'template', then there isn't anything to do. Return.
        if 'template' not in section:
            return ngen

        # Change directory to the skin subdirectory.  We use absolute paths
        # for cheetah, so the directory change is not necessary for generating
        # files.  However, changing to the skin directory provides a known
        # location so that calls to os.getcwd() in any templates will return
        # a predictable result.
        os.chdir(
            os.path.join(self.config_dict['WEEWX_ROOT'],
                         self.skin_dict['SKIN_ROOT'],
                         self.skin_dict.get('skin', '')))

        report_dict = accumulateLeaves(section)

        (template, dest_dir, encoding,
         default_binding) = self._prepGen(report_dict)

        # Get start and stop times
        default_archive = self.db_binder.get_manager(default_binding)
        start_ts = default_archive.firstGoodStamp()
        if not start_ts:
            log.info('Skipping template %s: cannot find start time',
                     section['template'])
            return ngen

        if gen_ts:
            record = default_archive.getRecord(
                gen_ts, max_delta=to_int(report_dict.get('max_delta')))
            if record:
                stop_ts = record['dateTime']
            else:
                log.info(
                    'Skipping template %s: generate time %s not in database',
                    section['template'], timestamp_to_string(gen_ts))
                return ngen
        else:
            stop_ts = default_archive.lastGoodStamp()

        # Get an appropriate generator function
        summarize_by = report_dict['summarize_by']
        if summarize_by in CheetahGenerator.generator_dict:
            _spangen = CheetahGenerator.generator_dict[summarize_by]
        else:
            # Just a single timespan to generate. Use a lambda expression.
            _spangen = lambda start_ts, stop_ts: [
                weeutil.weeutil.TimeSpan(start_ts, stop_ts)
            ]

        # Use the generator function
        for timespan in _spangen(start_ts, stop_ts):
            start_tt = time.localtime(timespan.start)
            stop_tt = time.localtime(timespan.stop)

            if summarize_by in CheetahGenerator.format_dict:
                # This is a "SummaryBy" type generation. If it hasn't been done already, save the
                # date as a string, to be used inside the document
                date_str = time.strftime(
                    CheetahGenerator.format_dict[summarize_by], start_tt)
                if date_str not in self.outputted_dict[summarize_by]:
                    self.outputted_dict[summarize_by].append(date_str)
                # For these "SummaryBy" generations, the file name comes from the start of the timespan:
                _filename = self._getFileName(template, start_tt)
            else:
                # This is a "ToDate" generation. File name comes
                # from the stop (i.e., present) time:
                _filename = self._getFileName(template, stop_tt)

            # Get the absolute path for the target of this template
            _fullname = os.path.join(dest_dir, _filename)

            # Skip summary files outside the timespan
            if report_dict['summarize_by'] in CheetahGenerator.generator_dict \
                    and os.path.exists(_fullname) \
                    and not timespan.includesArchiveTime(stop_ts):
                continue

            # skip files that are fresh, but only if staleness is defined
            stale = to_int(report_dict.get('stale_age'))
            if stale is not None:
                t_now = time.time()
                try:
                    last_mod = os.path.getmtime(_fullname)
                    if t_now - last_mod < stale:
                        log.debug("Skip '%s': last_mod=%s age=%s stale=%s",
                                  _filename, last_mod, t_now - last_mod, stale)
                        continue
                except os.error:
                    pass

            searchList = self._getSearchList(
                encoding, timespan, default_binding, section_name,
                os.path.join(os.path.dirname(report_dict['template']),
                             _filename))

            # First, compile the template
            try:
                # TODO: Look into caching the compiled template.
                # Under Python 2, Cheetah V2 will crash if given a template file name in Unicode,
                # so make sure it's a string first, using six.ensure_str().
                compiled_template = Cheetah.Template.Template(
                    file=six.ensure_str(template),
                    searchList=searchList,
                    filter='AssureUnicode',
                    filtersLib=weewx.cheetahgenerator)
            except Exception as e:
                log.error(
                    "Compilation of template %s failed with exception '%s'",
                    template, type(e))
                log.error("**** Ignoring template %s", template)
                log.error("**** Reason: %s", e)
                weeutil.logger.log_traceback(log.error, "****  ")
                continue

            # Second, evaluate the compiled template
            try:
                # We have a compiled template in hand. Evaluate it. The result will be a long
                # Unicode string.
                unicode_string = compiled_template.respond()
            except Cheetah.Parser.ParseError as e:
                log.error("Parse error while evaluating file %s", template)
                log.error("**** Ignoring template %s", template)
                log.error("**** Reason: %s", e)
                continue
            except Cheetah.NameMapper.NotFound as e:
                log.error("Evaluation of template %s failed.", template)
                log.error("**** Ignoring template %s", template)
                log.error("**** Reason: %s", e)
                log.error(
                    "**** To debug, try inserting '#errorCatcher Echo' at top of template"
                )
                continue
            except Exception as e:
                log.error(
                    "Evaluation of template %s failed with exception '%s'",
                    template, type(e))
                log.error("**** Ignoring template %s", template)
                log.error("**** Reason: %s", e)
                weeutil.logger.log_traceback(log.error, "****  ")
                continue

            # Third, convert the results to a byte string, using the strategy chosen by the user.
            if encoding == 'html_entities':
                byte_string = unicode_string.encode('ascii',
                                                    'xmlcharrefreplace')
            elif encoding == 'strict_ascii':
                byte_string = unicode_string.encode('ascii', 'ignore')
            elif encoding == 'normalized_ascii':
                # Normalize the string, replacing accented characters with non-accented
                # equivalents
                normalized = unicodedata.normalize('NFD', unicode_string)
                byte_string = normalized.encode('ascii', 'ignore')
            else:
                byte_string = unicode_string.encode(encoding)

            # Finally, write the byte string to the target file
            try:
                # Write to a temporary file first
                tmpname = _fullname + '.tmp'
                # Open it in binary mode. We are writing a byte-string, not a string
                with open(tmpname, mode='wb') as fd:
                    fd.write(byte_string)
                # Now move the temporary file into place
                os.rename(tmpname, _fullname)
                ngen += 1
            finally:
                try:
                    os.unlink(tmpname)
                except OSError:
                    pass

        return ngen
コード例 #7
0
ファイル: imagegenerator.py プロジェクト: mrbalky/weewx
    def gen_images(self, gen_ts):
        """Generate the images.

        The time scales will be chosen to include the given timestamp, with nice beginning and
        ending times.

        Args:
            gen_ts (int): 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 database.]
        """
        t1 = time.time()
        ngen = 0

        # determine how much logging is desired
        log_success = to_bool(search_up(self.image_dict, 'log_success', True))

        # 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 = accumulateLeaves(
                    self.image_dict[timespan][plotname])

                plotgen_ts = gen_ts
                if not plotgen_ts:
                    binding = plot_options['data_binding']
                    db_manager = self.db_binder.get_manager(binding)
                    plotgen_ts = db_manager.lastGoodStamp()
                    if not plotgen_ts:
                        plotgen_ts = time.time()

                image_root = os.path.join(self.config_dict['WEEWX_ROOT'],
                                          plot_options['HTML_ROOT'])
                # Get the path 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:
                if _skip_this_plot(plotgen_ts, plot_options, img_file):
                    continue

                # Generate the plot.
                plot = self.gen_plot(plotgen_ts, plot_options,
                                     self.image_dict[timespan][plotname])

                # 'plot' will be None if skip_if_empty was truthy, and the plot contains no data
                if plot:
                    # We have a valid plot. Render it onto an image
                    image = plot.render()

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

                    try:
                        # Now save the image
                        image.save(img_file)
                        ngen += 1
                    except IOError as e:
                        log.error("Unable to save to file '%s' %s:", img_file,
                                  e)

        t2 = time.time()

        if log_success:
            log.info("Generated %d images for report %s in %.2f seconds", ngen,
                     self.skin_dict['REPORT_NAME'], t2 - t1)
コード例 #8
0
ファイル: imagegenerator.py プロジェクト: mrbalky/weewx
    def gen_plot(self, plotgen_ts, plot_options, plot_dict):
        """Generate a single plot image.

        Args:
            plotgen_ts: A timestamp for which the plot will be valid. This is generally the last
            datum to be plotted.

            plot_options: A dictionary of plot options.

            plot_dict: A section in a ConfigObj. Each subsection will contain data about plots
            to be generated

        Returns:
            An instance of weeplot.genplot.TimePlot or None. If the former, it will be ready
            to render. If None, then skip_if_empty was truthy and no valid data were found.
        """

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

        # Calculate a suitable min, max time for the requested time.
        minstamp, maxstamp, timeinc = weeplot.utilities.scaletime(
            plotgen_ts - int(plot_options.get('time_length', 86400)),
            plotgen_ts)
        x_domain = weeutil.weeutil.TimeSpan(minstamp, maxstamp)

        # Override the x interval if the user has given an explicit interval:
        timeinc_user = to_int(plot_options.get('x_interval'))
        if timeinc_user is not None:
            timeinc = timeinc_user
        plot.setXScaling((x_domain.start, x_domain.stop, timeinc))

        # Set the y-scaling, using any user-supplied hints:
        yscale = plot_options.get('yscale', ['None', 'None', 'None'])
        plot.setYScaling(weeutil.weeutil.convertToFloat(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(plotgen_ts))
        plot.setBottomLabel(bottom_label)

        # Set day/night display
        plot.setLocation(self.stn_info.latitude_f, self.stn_info.longitude_f)
        plot.setDayNight(
            to_bool(plot_options.get('show_daynight', False)),
            weeplot.utilities.tobgr(
                plot_options.get('daynight_day_color', '0xffffff')),
            weeplot.utilities.tobgr(
                plot_options.get('daynight_night_color', '0xf0f0f0')),
            weeplot.utilities.tobgr(
                plot_options.get('daynight_edge_color', '0xefefef')))

        # Calculate the domain over which we should check for non-null data. It will be
        # 'None' if we are not to do the check at all.
        check_domain = _get_check_domain(
            plot_options.get('skip_if_empty', False), x_domain)

        # Set to True if we have _any_ data for the plot
        have_data = False

        # Loop over each line to be added to the plot.
        for line_name in plot_dict.sections:

            # Accumulate options from parent nodes.
            line_options = accumulateLeaves(plot_dict[line_name])

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

            # Find the database
            binding = line_options['data_binding']
            db_manager = self.db_binder.get_manager(binding)

            # If we were asked, see if there is any non-null data in the plot
            skip = _skip_if_empty(db_manager, var_type, check_domain)
            if skip:
                # Nothing but null data. Skip this line and keep going
                continue
            # Either we found some non-null data, or skip_if_empty was false, and we don't care.
            have_data = True

            # Look for aggregation type:
            aggregate_type = line_options.get('aggregate_type')
            if aggregate_type in (None, '', 'None', 'none'):
                # No aggregation specified.
                aggregate_type = aggregate_interval = None
            else:
                try:
                    # Aggregation specified. Get the interval.
                    aggregate_interval = weeutil.weeutil.nominal_spans(
                        line_options['aggregate_interval'])
                except KeyError:
                    log.error(
                        "Aggregate interval required for aggregate type %s",
                        aggregate_type)
                    log.error("Line type %s skipped", var_type)
                    continue

            # we need to pass the line options and plotgen_ts to our xtype
            # first get a copy of line_options
            option_dict = dict(line_options)
            # but we need to pop off aggregate_type and
            # aggregate_interval as they are used as explicit arguments
            # in our xtypes call
            option_dict.pop('aggregate_type', None)
            option_dict.pop('aggregate_interval', None)
            # then add plotgen_ts
            option_dict['plotgen_ts'] = plotgen_ts
            try:
                start_vec_t, stop_vec_t, data_vec_t = weewx.xtypes.get_series(
                    var_type,
                    x_domain,
                    db_manager,
                    aggregate_type=aggregate_type,
                    aggregate_interval=aggregate_interval,
                    **option_dict)
            except weewx.UnknownType:
                # If skip_if_empty is set, it's OK if a type is unknown.
                if not skip:
                    raise

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

            if aggregate_type and plot_type != 'bar':
                # If aggregating, put the point in the middle of the interval
                start_vec_t = ValueTuple(
                    [x - aggregate_interval / 2.0
                     for x in start_vec_t[0]],  # Value
                    start_vec_t[1],  # Unit
                    start_vec_t[2])  # Unit group
                stop_vec_t = ValueTuple(
                    [x - aggregate_interval / 2.0
                     for x in stop_vec_t[0]],  # Velue
                    stop_vec_t[1],  # Unit
                    stop_vec_t[2])  # Unit group

            # Convert the data to the requested units
            new_data_vec_t = self.converter.convert(data_vec_t)

            # Add a unit label. NB: all will get overwritten except the last. Get the label
            # from the configuration dictionary.
            unit_label = line_options.get(
                'y_label', self.formatter.get_label_string(new_data_vec_t[1]))
            # Strip off any leading and trailing whitespace so it's easy to center
            plot.setUnitLabel(unit_label.strip())

            # See if a line label has been explicitly requested:
            label = line_options.get('label')
            if label:
                # Yes. Get the text translation. Use the untranslated version if no translation
                # is available.
                label = self.text_dict.get(label, label)
            else:
                # No explicit label. Look up a generic one. Use the variable type itself if
                # there is no generic label.
                label = self.generic_dict.get(var_type, var_type)

            # See if a color has been explicitly requested.
            color = line_options.get('color')
            if color is not None: color = weeplot.utilities.tobgr(color)
            fill_color = line_options.get('fill_color')
            if fill_color is not None:
                fill_color = weeplot.utilities.tobgr(fill_color)

            # Get the line width, if explicitly requested.
            width = to_int(line_options.get('width'))

            interval_vec = None
            gap_fraction = None
            vector_rotate = None

            # Some plot types require special treatments:
            if plot_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
            elif plot_type == 'bar':
                interval_vec = [
                    x[1] - x[0]
                    for x in zip(start_vec_t.value, stop_vec_t.value)
                ]
            elif plot_type == 'line':
                gap_fraction = to_float(line_options.get('line_gap_fraction'))
                if gap_fraction is not None and not 0 < gap_fraction < 1:
                    log.error(
                        "Gap fraction %5.3f outside range 0 to 1. Ignored.",
                        gap_fraction)
                    gap_fraction = None
            else:
                log.error("Unknown plot type '%s'. Ignored", plot_type)
                continue

            # Get the type of line (only 'solid' or 'none' for now)
            line_type = line_options.get('line_type', 'solid')
            if line_type.strip().lower() in ['', 'none']:
                line_type = None

            marker_type = line_options.get('marker_type')
            marker_size = to_int(line_options.get('marker_size', 8))

            # Add the line to the emerging plot:
            plot.addLine(
                weeplot.genplot.PlotLine(stop_vec_t[0],
                                         new_data_vec_t[0],
                                         label=label,
                                         color=color,
                                         fill_color=fill_color,
                                         width=width,
                                         plot_type=plot_type,
                                         line_type=line_type,
                                         marker_type=marker_type,
                                         marker_size=marker_size,
                                         bar_width=interval_vec,
                                         vector_rotate=vector_rotate,
                                         gap_fraction=gap_fraction))

        # Return the constructed plot if it has any non-null data, otherwise return None
        return plot if have_data else None