Esempio n. 1
0
    def __init__(self, config_filename, schedule_filename):
        self.__command = None
        self.__user = None
        self.__days_to_schedule = None
        self.__tz_offset = None
        self.__latitude = None
        self.__longitude = None
        self.__recording_specs = []

        # get a sun calculating object
        self.__sun = Sun()

        # create a config object around the config file
        self.__config = ConfigObj(config_filename)
        self.__config_timestamp = 0
        self.updateConfigFromFile()

        # create a config object around the schedule file
        self.__schedule = ConfigObj(schedule_filename)
        self.__timestamp = -1
        self.updateFromFile()
Esempio n. 2
0
    def parseFile(self, file):
        '''
        Parse the new file and return the values, but don't cache them.
        The argument to this method can be anything that ConfigObj can take as an
        infile parameter to its constructor. This includes filenames, lists of
        strings, a dictionary, or anything with a read method.
        '''
        cache = {}
        cache[K_OBJ] = ConfigObj(file,
                                 list_values=False,
                                 write_empty_values=True)
        self.__updateCacheFromConfigObj(cache)

        return cache[K_VAL]
Esempio n. 3
0
    def __updateCacheFromFile(self, filename, cache):
        if cache and cache.has_key(K_OBJ):
            # if already loaded and file hasn't changed then use cached value
            # if config_obj exists, timestamp should too
            obj_timestamp = self.getTimestamp()
            if cache[K_TIME] < obj_timestamp:
                cache[K_OBJ].reload()
                cache[K_TIME] = obj_timestamp
        else:
            cache[K_OBJ] = ConfigObj(filename,
                                     list_values=False,
                                     write_empty_values=True)
            cache[K_TIME] = self.getTimestamp()

        self.__updateCacheFromConfigObj(cache)
Esempio n. 4
0
class ScheduleParser(object):
    def __init__(self, config_filename, schedule_filename):
        self.__command = None
        self.__user = None
        self.__days_to_schedule = None
        self.__tz_offset = None
        self.__latitude = None
        self.__longitude = None
        self.__recording_specs = []

        # get a sun calculating object
        self.__sun = Sun()

        # create a config object around the config file
        self.__config = ConfigObj(config_filename)
        self.__config_timestamp = 0
        self.updateConfigFromFile()

        # create a config object around the schedule file
        self.__schedule = ConfigObj(schedule_filename)
        self.__timestamp = -1
        self.updateFromFile()


    @property
    def command(self):
        self.updateConfigFromFile()
        return self.__command
    @command.setter
    def command(self, command):
        self.updateConfigFromFile()
        self.__command = command
    def assertCommandIsDefined(self):
        if not self.__command:
            sys.stderr.write('%s must be specified in the %s section of "%s"\n'
                    % (CONFIG_COMMAND, CONFIG_SECTION, self.__config.filename))
            return False
        return True

    @property
    def user(self):
        self.updateConfigFromFile()
        return self.__user
    @user.setter
    def user(self, user):
        self.updateConfigFromFile()
        self.__user = user

    @property
    def days_to_schedule(self):
        self.updateConfigFromFile()
        return self.__days_to_schedule
    @days_to_schedule.setter
    def days_to_schedule(self, days_to_schedule):
        self.updateConfigFromFile()
        self.__days_to_schedule = days_to_schedule

    @property
    def tz_offset(self):
        self.updateConfigFromFile()
        return self.__tz_offset
    @tz_offset.setter
    def setTzOffset(self, tz_offset):
        self.updateConfigFromFile()
        self.__tz_offset = tz_offset

    @property
    def latitude(self):
        self.updateConfigFromFile()
        return self.__latitude
    @latitude.setter
    def latitude(self, latitude):
        self.updateConfigFromFile()
        self.__latitude = latitude

    @property
    def longitude(self):
        self.updateConfigFromFile()
        return self.__longitude
    @longitude.setter
    def longitude(self, longitude):
        self.updateConfigFromFile()
        self.__longitude = longitude

    @property
    def filename(self):
        return self.__schedule.filename


    def updateConfigFromFile(self):
        if self.__config_timestamp >= self.getConfigTimestamp():
            return

        # read station information
        station_section = self.__config[STATION_SECTION]
        for key in station_section:
            if key == STATION_TZ_KEY:
                # get timezone from config
                self.__tz_offset = int(station_section[key])
            elif key == STATION_LAT_KEY:
                # get latitude from config
                self.__latitude = float(station_section[key])
            elif key == STATION_LONG_KEY:
                # get longitude from config
                self.__longitude = float(station_section[key])

        # this information is required for now, (eventually ask GPS)
        if not self.__latitude or not self.__longitude:
            raise MissingConfigKeyError('%s and %s must be specified in the %s '
                    'section of "%s" (at least until we can talk to the GPS)\n'
                    % (CONFIG_LAT_KEY, CONFIG_LONG_KEY, CONFIG_SECTION,
                    self.__config.filename))

        # if timezone is not specified in config, then get it from python
        if not self.__tz_offset:
            # python tz offsets are negative and in seconds
            self.__tz_offset = -time.timezone / 3600

        # read sound capture info
        config_section = self.__config[CONFIG_SECTION]
        for key in config_section:
            if key == CONFIG_COMMAND:
                self.__command = config_section[key]
            elif key == CONFIG_USER:
                self.__user = config_section[key]
            elif key == CONFIG_DAYS_KEY:
                self.__days_to_schedule = int(config_section[key])


    def updateFromFile(self):
        file_timestamp = self.getTimestamp()
        if self.__timestamp >= file_timestamp:
            return

        self.__schedule.reload()
        # if this is a new file (or has been modified, put header back)
        if not self.__schedule.initial_comment:
            self.__schedule.initial_comment = SCHEDULE_HEADER.split('\n')
        self.__timestamp = file_timestamp
        self.updateFromConfigObj()


    def updateFromConfigObj(self):
        # parse the schedule to get the recording times
        self.__recording_specs = []
        for key in self.__schedule:
            self.__recording_specs.append(self.parseRecordingSpec(key,
                    self.__schedule[key]))


    # convert given string into a TimeSpec
    @staticmethod
    def parseTimeSpec(spec):
        assert type(spec) == str or type(spec) == unicode, (
                'time spec should be string, not %s' % type(spec))

        match = TIME_RE.match(spec)
        if match:
            start_type, start_hour, start_minute = match.groups()
            return TimeSpec(TimeSpec.Type(start_type), int(start_hour),
                    int(start_minute))
        else:
            raise ValueError('"%s" is not a valid time spec' % spec)


    # convert given string into a RecordingSpec
    @classmethod
    def parseRecordingSpec(cls, title, spec_part1, spec_part2=None):
        if spec_part2 == None:
            # used when spec is a single string
            assert type(spec_part1) == str or type(spec_part1) == unicode, (
                    'recording spec should be string, not %s' % (spec_part1))

            match = SCHEDULE_RE.match(spec_part1)
            if match:
                (start_type, start_hour, start_minute, finish_type, finish_hour,
                        finish_minute) = match.groups()
                return RecordingSpec(title, TimeSpec(TimeSpec.Type(start_type),
                                int(start_hour), int(start_minute)),
                        TimeSpec(TimeSpec.Type(finish_type),
                                int(finish_hour), int(finish_minute)))
            else:
                raise ValueError('"%s" is not a valid recording spec (%s->%s)'
                        % (spec, SCHEDULE_SECTION, title, spec))
        else:
            # when the spec is in two parts (a start and a finish)
            return RecordingSpec(title, cls.parseTimeSpec(spec_part1),
                    cls.parseTimeSpec(spec_part2))


    def getSchedules(self, schedule_day=None, days_to_schedule=None):
        # update specs if file has been modified
        self.updateFromFile()

        if type(schedule_day) == datetime.datetime:
            schedule_day = schedule_day.replace(hour=0, minute=0, second=0,
                    microsecond=0)
        elif type(schedule_day) == datetime.date:
            schedule_day = datetime.datetime(schedule_day.year,
                    schedule_day.month, schedule_day.day)
        elif not schedule_day:
            schedule_day = datetime.datetime.today().replace(hour=0, minute=0,
                    second=0, microsecond=0)
        else:
            raise TypeError('schedule_day must be datetime or date, not "%s"'
                    % type(schedule_day))

        if not days_to_schedule:
            days_to_schedule = self.__days_to_schedule

        assert type(days_to_schedule) == int, ('invalid type for '
                'days_to_schedule "%s"' % type(days_to_schedule))

        recording_times = []
        for i in range(days_to_schedule):
            day = schedule_day + datetime.timedelta(days=i)
            #TODO optimisation: skip sunrise/set calc if all times are absolute
            rise, set = [datetime.timedelta(hours=(t + self.tz_offset)) for t in
                    self.__sun.sunRiseSet(day.year, day.month, day.day,
                            self.longitude, self.latitude)]
            for recording_spec in self.__recording_specs:
                times = []
                for time_spec in (recording_spec.start, recording_spec.finish):
                    if time_spec.type.isSunriseRelative():
                        times.append(day + rise + time_spec.offset)
                    elif time_spec.type.isSunsetRelative():
                        times.append(day + set + time_spec.offset)
                    else:
                        times.append(day + time_spec.offset)
                recording_times.append(RecordingTime(recording_spec.title,
                        times[0], times[1]))
        # sort times by start time
        recording_times.sort(key=operator.attrgetter('start'))

        return recording_times


    def findSchedulesCovering(self, start, finish):
        '''Find all schedules that cover the given period. It may be that there
            are more than one, due to overlapping schedules, or one recording
            may overlap the boundary between two'''
        schedules = []
        # (search 1 day either side)
        for schedule in self.getSchedules(start
                - datetime.timedelta(1), (finish - start).days + 2):
            # if either start or finish of recording file is in the schedule
            if (start in schedule or finish in schedule):
                schedules.append(schedule)

        return schedules


    def getScheduleSpecs(self):
        # update if file has been modified
        self.updateFromFile()

        # return a copy
        return deepcopy(self.__recording_specs)


    def getScheduleSpec(self, title):
        # update if file has been modified
        self.updateFromFile()

        return next((spec for spec in self.__recording_specs
                if spec.title == title), None)


    def setScheduleSpec(self, spec):
        assert isinstance(spec, RecordingSpec)

        self.__schedule[spec.title] = '%s - %s' % (spec.start, spec.finish)
        self.updateFromConfigObj()


    def deleteScheduleSpec(self, title):
        del(self.__schedule[title])
        self.updateFromConfigObj()


    def clearScheduleSpecs(self):
        self.__schedule.clear()
        self.updateFromConfigObj()


    def getScheduleTitles(self):
        # update if file has been modified
        self.updateFromFile()

        return [deepcopy(spec.title) for spec in self.__recording_specs]


    def getTimestamp(self):
        if (self.__schedule.filename
                and os.path.exists(self.__schedule.filename)):
            return int(os.path.getmtime(self.__schedule.filename))
        return 0


    def getConfigTimestamp(self):
        if self.__config.filename and os.path.exists(self.__config.filename):
            return int(os.path.getmtime(self.__config.filename))
        return 0


    def saveToFile(self):
        try:
            # update file
            self.__schedule.write()
            self.__timestamp = self.getTimestamp();
        except:
            # if this fails we should re-read the file to keep them synced
            self.__schedule.reload()
            self.__timestamp = self.getTimestamp();
            raise


    def export(self, export_filename):
        with open(export_filename, 'w') as save_file:
            self.__schedule.write(save_file)
Esempio n. 5
0

# default location of configuration file
DEFAULT_CONFIG_FILE = '../bowerbird_config'
DEFAULT_OUTPUT_FILE = "/dev/stdout"


if __name__ == '__main__':
    # parse commandline options
    parser = OptionParser()
    parser.add_option("-c", "--config", dest="config_file",
            help="configuration file to convert", default=DEFAULT_CONFIG_FILE)
    parser.add_option("-o", "--output", dest="output_file",
            help="file to write shell variables",
            default=DEFAULT_OUTPUT_FILE)
    parser.add_option("-s", "--shell-separator", dest="shell_separator",
            help="separator to use in output", default=DEFAULT_SHELL_SEPARATOR)
    (options, args) = parser.parse_args()

    # open configuration file
    config = ConfigObj(options.config_file)
    # open output file
    output = file(options.output_file, "w")

    # write converted config out
    convertConfig(config, output, options.shell_separator)

    # close file handles
    output.close()
    #unsupported: config.close()
Esempio n. 6
0
 def __initCache(self, cache):
     cache[K_OBJ] = ConfigObj()
     cache[K_TIME] = 0
     cache[K_VAL] = OrderedDict()
     cache[K_DICT] = OrderedDict()