def test_smooth(): """ Create a step profile with redundant steps, then smooth it. """ profile = [(getTime("01:00:00"), 6.2), (getTime("02:00:00"), 6.2), (getTime("03:00:00"), 6), (getTime("04:00:00"), 6), (getTime("05:00:00"), 6), (getTime("06:00:00"), 5.4), (getTime("07:00:00"), 5.2), (getTime("08:00:00"), 5.2), (getTime("09:00:00"), 5.8), (getTime("10:00:00"), 6), (getTime("11:00:00"), 6.2), (getTime("12:00:00"), 6.2)] expectation = [(getTime("01:00:00"), 6.2), (getTime("03:00:00"), 6), (getTime("06:00:00"), 5.4), (getTime("07:00:00"), 5.2), (getTime("09:00:00"), 5.8), (getTime("10:00:00"), 6), (getTime("11:00:00"), 6.2), (getTime("12:00:00"), 6.2)] # Create profile p = StepProfile() p.T, p.y = lib.unzip(profile) # Smooth it p.smooth() # No redundant steps allowed in smoothed profile assert [p.T, p.y] == lib.unzip(expectation)
def test_cut(): """ Create a profile and cut off some of its data (outside some given time range). """ profile = [(getTime("23:30:00", "1970.01.01"), 6.2), (getTime("00:00:00", "1970.01.02"), 6), (getTime("00:30:00", "1970.01.02"), 5.8), (getTime("00:00:00", "1970.01.03"), 5.6), (getTime("00:30:00", "1970.01.03"), 5.4), (getTime("00:00:00", "1970.01.04"), 5.2)] # Create profile p = Profile() p.T, p.y = lib.unzip(profile) p.start = profile[1][0] p.end = profile[-1][0] # Cut it last = p.cut() # First entry should be cut off assert last == profile[0][1] assert [p.T, p.y] == lib.unzip(profile[1:]) # Rewrite profile p.T, p.y = lib.unzip(profile) # Cut with given datetimes last = p.cut(profile[2][0], profile[-2][0]) # First two entries and last one should be cut off assert last == profile[1][1] assert [p.T, p.y] == lib.unzip(profile[2:-1])
def test_op(): """ Create several step profiles, and do operations on them. """ start, end = getTime("00:00:00"), getTime("02:00:00") profiles = [[(start, 6), (getTime("00:30:00"), 5.8), (getTime("01:00:00"), 5.2), (getTime("01:30:00"), 5.6), (end, 4.8)], [(start, 0.1), (getTime("00:10:00"), 3), (getTime("00:35:00"), 2.8), (getTime("01:05:00"), 6.2), (getTime("01:30:00"), 7.6), (end, 0.8)]] expectationAdd = [(start, 6.1), (getTime("00:10:00"), 9), (getTime("00:30:00"), 8.8), (getTime("00:35:00"), 8.6), (getTime("01:00:00"), 8), (getTime("01:05:00"), 11.4), (getTime("01:30:00"), 13.2), (end, 5.6)] expectationSubtract = [ (start, 5.9), (getTime("00:10:00"), 3), (getTime("00:30:00"), 2.8), (getTime("00:35:00"), 3), (getTime("01:00:00"), 2.4), (getTime("01:05:00"), -1), (getTime("01:30:00"), -2), (end, 4) ] expectationMultiply = [(start, 0.6), (getTime("00:10:00"), 18), (getTime("00:30:00"), 17.4), (getTime("00:35:00"), 16.24), (getTime("01:00:00"), 14.56), (getTime("01:05:00"), 32.24), (getTime("01:30:00"), 42.56), (end, 3.84)] expectationDivide = [(start, 6 / 0.1), (getTime("00:10:00"), 6 / 3), (getTime("00:30:00"), 5.8 / 3), (getTime("00:35:00"), 5.8 / 2.8), (getTime("01:00:00"), 5.2 / 2.8), (getTime("01:05:00"), 5.2 / 6.2), (getTime("01:30:00"), 5.6 / 7.6), (end, 4.8 / 0.8)] # Create profiles p1 = StepProfile() p1.T, p1.y = lib.unzip(profiles[0]) p1.start, p1.end = start, end p1.norm = end p2 = StepProfile() p2.T, p2.y = lib.unzip(profiles[1]) p2.start, p2.end = start, end p2.norm = end # Test operations on them do_test_op("+", p1, p2, expectationAdd) do_test_op("-", p1, p2, expectationSubtract) do_test_op("*", p1, p2, expectationMultiply) do_test_op("/", p1, p2, expectationDivide)
def unpackage(): """ Unpacking of a scene file created by the copyPastePackage.package function. this needs to be looked at. I don't have windows so I have no idea if it works there. """ zipfileLoc = hou.ui.selectFile( title="please select a zipFile created by the package function", pattern="*.zip") if not zipfileLoc: return file_ = zipfile.ZipFile(hou.expandString(zipfileLoc), "r") isOke = False for name in file_.namelist(): if name.endswith(".hip") or name.endswith(".hipnc"): isOke = True break if not isOke: return unpackLoc = hou.expandString( hou.ui.selectFile( title= "please select a directory you wish to use to unpack the files to." )) if not unpackLoc or not os.path.isdir(unpackLoc): return unzip(file_, unpackLoc) unpackageDir = os.path.dirname(file_.namelist()[0]) otlsfiles = glob.glob(os.path.join(unpackLoc, unpackageDir, "otls", "*")) hipfile = glob.glob(os.path.join(unpackLoc, unpackageDir, "*.hip*")) if len(hipfile) != 1: return hou.hipFile.load(hipfile[0]) for otl in otlsfiles: hou.hda.installFile(otl)
def unpackage(): """ Unpacking of a scene file created by the copyPastePackage.package function. this needs to be looked at. I don't have windows so I have no idea if it works there. """ zipfileLoc = hou.ui.selectFile(title="please select a zipFile created by the package function", pattern="*.zip") if not zipfileLoc: return file_ = zipfile.ZipFile(hou.expandString(zipfileLoc), "r") isOke = False for name in file_.namelist(): if name.endswith(".hip") or name.endswith(".hipnc"): isOke = True break if not isOke: return unpackLoc = hou.expandString(hou.ui.selectFile(title="please select a directory you wish to use to unpack the files to.")) if not unpackLoc or not os.path.isdir(unpackLoc): return unzip(file_, unpackLoc) unpackageDir = os.path.dirname(file_.namelist()[0]) otlsfiles = glob.glob(os.path.join(unpackLoc, unpackageDir, "otls", "*")) hipfile = glob.glob(os.path.join(unpackLoc, unpackageDir, "*.hip*")) if len(hipfile) != 1: return hou.hipFile.load(hipfile[0]) for otl in otlsfiles: hou.hda.installFile(otl)
def test_pad(): """ Force start/end limits on profile, using (if available) the value of the step preceding beginning of profile. """ profile = [(getTime(dateString="1970.01.02"), 6.2), (getTime(dateString="1970.01.03"), 6)] start = getTime(dateString="1970.01.01") end = getTime(dateString="1970.01.04") last = 0 zero = 1000 # Create empty profile p = StepProfile() # Pad it p.pad(start, end, last) assert p.T[0] == start and p.T[-1] == end assert p.y[0] == last and p.y[-1] == last # Create profile p = StepProfile() p.T, p.y = lib.unzip(profile) # Pad it p.pad(start, end, last) assert p.T[0] == start and p.T[-1] == end assert p.y[0] == last and p.y[-1] == profile[-1][1] # Create profile with a specific zero value p = StepProfile() p.T, p.y = lib.unzip(profile) p.zero = zero # Pad it without last value p.pad(start, end) assert p.T[0] == start and p.T[-1] == end assert p.y[0] == zero and p.y[-1] == profile[-1][1]
def do_test_fill(profile, fillers, expectations): """ Helper function to test filling of profile. """ # Test expectations for i in range(len(expectations)): # Create step profile with a hole in the middle p = StepProfile() p.T, p.y = lib.unzip(profile) # Create filler f = StepProfile() f.T, f.y = lib.unzip(fillers[i]) # Fill profile p.fill(f) # Every value on the y-axis should be defined # Expectations should be met assert all([y is not None for y in p.y]) assert [p.T, p.y] == lib.unzip(expectations[i])
def test_inject(): """ Inject steps in profile according to given step durations. Tests 4 cases: - Step ends before start of next one (new step injected) - Step ends after start of next one (nothing to do) - Step is a canceling one (value is replaced by profile's zero value) - Step is at the end of profile (new step injected at the end) """ profile = [(getTime("00:00:00"), 6.2), (getTime("01:00:00"), 6), (getTime("01:30:00"), 5.8), (getTime("02:00:00"), 5.6), (getTime("03:00:00"), 5.4)] # Define zero (default y-axis value) for profile zero = 1000 # Define durations for each given step durations = [datetime.timedelta(minutes=d) for d in [5, 60, 20, 0, 30]] # Define expected axes after injection expectations = [(getTime("00:00:00"), 6.2), (getTime("00:05:00"), zero), (getTime("01:00:00"), 6), (getTime("01:30:00"), 5.8), (getTime("01:50:00"), zero), (getTime("02:00:00"), zero), (getTime("03:00:00"), 5.4), (getTime("03:30:00"), zero)] # Create step profile p = StepProfile() # Define it p.T, p.y = lib.unzip(profile) p.durations = durations p.zero = zero # Inject it with zeros p.inject() assert [p.T, p.y] == lib.unzip(expectations)
def test_map(): """ Create a daily profile, give it decoupled data and map it over range of days covered by said profile. """ profile = [(getTime("00:30:00").time(), 1.30), (getTime("01:30:00").time(), 1.45), (getTime("02:30:00").time(), 1.60), (getTime("03:30:00").time(), 1.70), (getTime("04:30:00").time(), 1.80), (getTime("05:30:00").time(), 1.90), (getTime("06:30:00").time(), 2.00)] nDays = 3 days = [ getTime().date() + datetime.timedelta(days=d) for d in range(nDays) ] # Create profile p = DailyProfile() p.T, p.y = lib.unzip(profile) p.days = days # Map its data p.map() assert [p.T, p.y] == lib.unzip( sorted([(datetime.datetime.combine(d, T), y) for d in days for (T, y) in profile])) # Test before beginning of profile with pytest.raises(ValueError): p.f(getTime("00:00:00")) # Test midnight cross assert p.f(getTime("00:00:00") + datetime.timedelta(days=1)) == 2.00
def decouple(self): """ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECOUPLE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Decouple profile data into components: convert string time values to datetime objects and get corresponding y-axis value. """ # Info Logger.debug("Decoupling components of: " + repr(self)) # Decouple data, convert string times to datetimes, and sort them in # chronological order [self.T, self.y] = lib.unzip([(lib.formatTime(T), y) for (T, y) in sorted(self.data.items())])
def map(self): """ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MAP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After decoupling daily profile data, reproduce it on the whole range of days that the profile covers. """ # Info Logger.debug("Mapping time of: " + repr(self)) # Map each time and value to profile's whole set of days mappedEntries = [(datetime.datetime.combine(day, T), y) for (T, y) in zip(self.T, self.y) for day in self.days] # Sort entries in chronological order and store them [self.T, self.y] = lib.unzip(sorted(mappedEntries))
def test_fill_empty(): """ Create a an empty step profile and try filling it with a filler. """ # Create empty profile p = StepProfile() filler = [(getTime("01:00:00"), 6), (getTime("02:00:00"), 5.8), (getTime("04:00:00"), 5.6), (getTime("05:00:00"), 5.4)] # Create filler f = StepProfile() f.T, f.y = lib.unzip(filler) # Fill it p.fill(f) # Empty profile should be left empty after fill assert p.T == [] and p.y == []
def test_decouple(): """ Create a profile, give it data and decouple it into time and value axes. """ # Create an unsorted profile profile = [(getTime("00:30:00", "1970.01.02"), 5.8), (getTime("23:30:00", "1970.01.01"), 6.2), (getTime("00:00:00", "1970.01.02"), 6), (getTime("01:00:00", "1970.01.02"), 5.6)] # Create profile p = Profile() p.data = dict([(lib.formatTime(d), y) for (d, y) in profile]) # Decouple its data p.decouple() # Check profile axes (they should be time ordered) assert [p.T, p.y] == lib.unzip(sorted(profile))
def test_normalize(): """ Create a profile, then normalize its time axis. """ profile = [(getTime("23:30:00", "1970.01.01"), 6.2), (getTime("00:00:00", "1970.01.02"), 6), (getTime("00:30:00", "1970.01.02"), 5.8), (getTime("00:00:00", "1970.01.03"), 5.6), (getTime("00:30:00", "1970.01.03"), 5.4), (getTime("00:00:00", "1970.01.04"), 5.2)] # Create profile and define its norm p = Profile() p.T, p.y = lib.unzip(profile) p.norm = p.T[-1] # Normalize it p.normalize() # Check normalization assert p.t == [lib.normalizeTime(T, p.norm) for T in p.T]
def cut(self, a=None, b=None): """ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CUT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cut off all values that do not fit within [a, b]. Keep track of the last entry before start of profile. """ # Info Logger.debug("Cutting: " + repr(self)) # No start given if a is None: a = self.start # No end given if b is None: b = self.end # Test limit types if type(a) is not datetime.datetime or type(a) is not type(b): raise TypeError("Limit times to use while cutting profile have " + "to be datetime objects.") # Group axes and filter them data = zip(self.T, self.y) olderData = [x for x in data if x[0] < a] filteredData = [x for x in data if a <= x[0] <= b] # Cut-off steps outside of start and end limits [self.T, self.y] = lib.unzip(filteredData) # Find last value before beginning of profile last = olderData[-1][1] if olderData else None # Return step value before beginning of profile return last
def do_test_op(op, base, profile, expectation): """ Helper function to test operations on profiles. """ def isEqual(x, y): return lib.isEqual(x, y, 0.001) wrongStart = base.start + datetime.timedelta(hours=1) wrongEnd = base.end - datetime.timedelta(hours=1) # Create a profile with mismatching limits wrongProfile = [(wrongStart, 0), (wrongEnd, 0)] w = StepProfile() w.T, w.y = lib.unzip(wrongProfile) w.start, w.end = wrongStart, wrongEnd w.norm = wrongEnd # Execute operation on profiles if op == "+": p = base.add(profile) with pytest.raises(errors.MismatchedLimits): base.add(w) with pytest.raises(errors.MismatchedLimits): w.add(base) elif op == "-": p = base.subtract(profile) with pytest.raises(errors.MismatchedLimits): base.subtract(w) with pytest.raises(errors.MismatchedLimits): w.subtract(base) elif op == "*": p = base.multiply(profile) with pytest.raises(errors.MismatchedLimits): base.multiply(w) with pytest.raises(errors.MismatchedLimits): w.multiply(base) elif op == "/": p = base.divide(profile) with pytest.raises(errors.MismatchedLimits): base.divide(w) with pytest.raises(errors.MismatchedLimits): w.divide(base) else: raise TypeError("Bad profile operation type.") assert all([ T1 == T2 and isEqual(y1, y2) for ((T1, y1), (T2, y2)) in zip(expectation, zip(p.T, p.y)) ])