def test_facts_stored_with_context(self): # Test we can store 2 facts of the same concept but with different # contexts, and pull them both back out. doc = Entrypoint("CutSheet", self.taxonomy) concept = "solar:InverterCutSheetNotes" ctx_jan = Context( duration={ "start": datetime(year=2018, month=1, day=1), "end": datetime(year=2018, month=2, day=1) }, entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember") ctx_feb = Context( duration={ "start": datetime(year=2018, month=2, day=1), "end": datetime(year=2018, month=3, day=1) }, entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember") doc.set(concept, "Jan Value", context=ctx_jan) doc.set(concept, "Feb Value", context=ctx_feb) jan_fact = doc.get(concept, context=ctx_jan) feb_fact = doc.get(concept, context=ctx_feb) self.assertEqual(jan_fact.value, "Jan Value") self.assertEqual(feb_fact.value, "Feb Value")
def test_set_context_arg(self): # Tests the case where .set() is called correctly, using # the way of calling .set() where we pass in a Context # object. Verify the data is stored and can be retrieved # using .get(). doc = Entrypoint("CutSheet", self.taxonomy) ctx = Context(duration="forever", entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember") doc.set("solar:TypeOfDevice", "ModuleMember", context=ctx) now = datetime.now(), ctx = Context(instant=now, entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember") doc.set("solar:DeviceCost", 100, context=ctx, unit_name="USD") # Get the data bacK: typeFact = doc.get( "solar:TypeOfDevice", Context(duration="forever", entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember")) self.assertEqual(typeFact.value, "ModuleMember") costFact = doc.get( "solar:DeviceCost", Context(instant=now, entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember")) self.assertEqual(costFact.value, 100)
def test_hypercube_store_context(self): doc = Entrypoint("CutSheet", self.taxonomy) table = doc.get_table("solar:InverterPowerLevelTable") c1 = table.store_context( Context(duration="forever", entity="JUPITER", ProductIdentifierAxis="ABCD", InverterPowerLevelPercentAxis= 'solar:InverterPowerLevel50PercentMember')) self.assertEqual(c1.get_id(), "solar:InverterPowerLevelTable_0") c2 = table.store_context( Context(duration="forever", entity="JUPITER", ProductIdentifierAxis="ABCD", InverterPowerLevelPercentAxis= 'solar:InverterPowerLevel50PercentMember')) # Same self.assertIs(c1, c2) c3 = table.store_context( Context(duration="forever", entity="JUPITER", ProductIdentifierAxis="ABCD", InverterPowerLevelPercentAxis= 'solar:InverterPowerLevel75PercentMember')) # Different self.assertIsNot(c1, c3)
def test_set_separate_dimension_args(self): # Tests the case where .set() is called correctly. Use the # way of calling .set() where we pass in every dimension # separately. Verify the data is stored and can be retrieved # using .get(). doc = Entrypoint("CutSheet", self.taxonomy) # Write a TypeOfDevice and a DeviceCost: doc.set("solar:TypeOfDevice", "ModuleMember", duration="forever", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember") now = datetime.now() doc.set("solar:DeviceCost", 100, instant=now, ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", unit_name="USD") typeFact = doc.get( "solar:TypeOfDevice", Context(duration="forever", ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember")) self.assertEqual(typeFact.value, "ModuleMember") costFact = doc.get( "solar:DeviceCost", Context(instant=now, ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember")) self.assertEqual(costFact.value, 100) self.assertEqual(costFact.unit, "USD")
def test_hypercube_rejects_out_of_domain_axis_values(self): # Try passing in something as a value for TestConditionAxis that is not # one of the enumerated Members; it should be rejected: doc = Entrypoint("CutSheet", self.taxonomy) table = doc.get_table("solar:CutSheetDetailsTable") self.assertTrue( table.axis_value_within_domain( "solar:TestConditionAxis", "solar:StandardTestConditionMember")) self.assertFalse( table.axis_value_within_domain( "solar:TestConditionAxis", "solar:InverterPowerLevel100PercentMember")) concept = 'solar:InverterOutputRatedPowerAC' context = Context( duration="forever", ProductIdentifierAxis="placeholder", InverterPowerLevelPercentAxis='solar:StandardTestConditionMember') # not a valid value for InverterPowerLevelPercentAxis with self.assertRaises(Exception): doc.sufficient_context(concept, context)
def test_set_default_multiple_times(self): # set default for some fields, then set default again for different # fields, assert the non-replaced fields keep old values. doc = OBInstance("CutSheet", self.taxonomy) now = datetime.now() doc.set_default_context({ "entity": "JUPITER", "solar:TestConditionAxis": "solar:StandardTestConditionMember", }) doc.set_default_context({ PeriodType.instant: now, PeriodType.duration: "forever" }) # The second set_default_context should not erase defaults for entity or # TestConditionAxis doc.set("solar:DeviceCost", "100", unit_name="USD", ProductIdentifierAxis="placeholder") fact = doc.get( "solar:DeviceCost", Context(ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", entity="JUPITER", instant=now)) self.assertEqual(fact.value, "100") self.assertEqual(fact.unit, "USD") self.assertEqual(fact.context.entity, "JUPITER") self.assertEqual(fact.context.instant, now)
def test_set_context_arg(self): # Tests the case where .set() is called correctly, using # the way of calling .set() where we pass in a Context # object. Verify the data is stored and can be retrieved # using .get(). doc = Entrypoint("CutSheet", self.taxonomy) ctx = Context(duration="forever", entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="placeholder") doc.set("solar:TypeOfDevice", "Module", context=ctx) ctx = Context(instant=datetime.now(), entity="JUPITER", ProductIdentifierAxis="placeholder", TestConditionAxis="placeholder") doc.set("solar:DeviceCost", 100, context=ctx, unit="dollars")
def test_sufficient_context_instant_vs_duration(self): doc = Entrypoint("CutSheet", self.taxonomy) # in order to set a concept value, sufficient context must be # provided. what is sufficient context varies by concept. # in general the context must provide the correct time information # (either duration or instant) # We shouldn't even be able to instantiate a context with no time info: with self.assertRaises(Exception): noTimeContext = Context( ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember") # solar:DeviceCost has period_type instant # so it requires a context with an instant. A context without an instant # should be insufficient: instantContext = Context( ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", instant=datetime.now()) durationContext = Context( ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", duration="forever") self.assertTrue( doc.sufficient_context("solar:DeviceCost", instantContext)) # A context with a duration instead of an instant should also be # rejected: with self.assertRaises(Exception): doc.sufficient_context("solar:DeviceCost", durationContext) # solar:ModuleNameplateCapacity has period_type duration. # A context with an instant instead of a duration should also be # rejected: with self.assertRaises(Exception): doc.sufficient_context("solar:ModuleNameplateCapacity", instantContext) self.assertTrue( doc.sufficient_context("solar:ModuleNameplateCapacity", durationContext))
def test_hypercube_rejects_context_with_unwanted_axes(self): # Test that giving a context an *extra* axis that is invalid for the table # causes it to be rejected as well. doc = Entrypoint("CutSheet", self.taxonomy) twoAxisContext = Context( ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", instant=datetime.now()) self.assertTrue( doc.sufficient_context("solar:DeviceCost", twoAxisContext)) threeAxisContext = Context( ProductIdentifierAxis="placeholder", InverterPowerLevelPercentAxis= 'solar:InverterPowerLevel50PercentMember', TestConditionAxis="solar:StandardTestConditionMember", instant=datetime.now()) # InverterPowerLevelPercentAxis is a valid axis and this is a valid value for it, # but the table that holds DeviceCost doesn't want this axis: with self.assertRaises(Exception): doc.sufficient_context("solar:DeviceCost", threeAxisContext)
def test_tableless_facts(self): # Some entry points, like MonthlyOperatingReport, seem to have concepts # in them that are not part of any table: doc = Entrypoint("MonthlyOperatingReport", self.taxonomy) doc.set("solar:MonthlyOperatingReportEffectiveDate", date(year=2018, month=6, day=1), entity="JUPITER", duration="forever") fact = doc.get("solar:MonthlyOperatingReportEffectiveDate", Context(entity="JUPITER", duration="forever")) self.assertEqual(fact.value, date(year=2018, month=6, day=1))
def test_sufficient_context_axes(self): doc = Entrypoint("CutSheet", self.taxonomy) # The context must also provide all of the axes needed to place the # fact within the right table. # DeviceCost is on the CutSheetDetailsTable so it needs a value # for ProductIdentifierAxis and TestConditionAxis. with self.assertRaises(Exception): doc.sufficient_context("solar:DeviceCost", {}) context = Context( instant=datetime.now(), ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember") self.assertTrue(doc.sufficient_context("solar:DeviceCost", context)) badContext = Context( instant=datetime.now(), TestConditionAxis="solar:StandardTestConditionMember") with self.assertRaises(Exception): doc.sufficient_context("solar:DeviceCost", badContext) badContext = Context(instant=datetime.now(), ProductIdentifierAxis="placeholder") with self.assertRaises(Exception): doc.sufficient_context("solar:DeviceCost", badContext) # How do we know what are valid values for ProductIdentifierAxis and # TestConditionAxis? (I think they are meant to be UUIDs.) # Note: TestConditionAxis is part of the following relationships: # solar:TestConditionAxis -> dimension-domain -> solar:TestConditionDomain # solar:TestConditionAxis -> dimension-default -> solar:TestConditionDomain # i wonder what that "dimension-default" means #'solar:InverterOutputRatedPowerAC' is on the 'solar:InverterPowerLevelTable' which requires axes: [u'solar:ProductIdentifierAxis', u'solar:InverterPowerLevelPercentAxis']. it's a duration. concept = 'solar:InverterOutputRatedPowerAC' context = Context(duration="forever", ProductIdentifierAxis="placeholder", InverterPowerLevelPercentAxis= 'solar:InverterPowerLevel100PercentMember') self.assertTrue(doc.sufficient_context(concept, context)) badContext = Context(instant=datetime.now(), InverterPowerLevelPercentAxis= 'solar:InverterPowerLevel100PercentMember') with self.assertRaises(Exception): doc.sufficient_context(concept, badContext) badContext = Context(instant=datetime.now(), ProductIdentifierAxis="placeholder") with self.assertRaises(Exception): doc.sufficient_context(concept, badContext)
def test_ids_in_xml_and_json(self): # facts should have IDs in both exported JSON and exported XML, and they # should be the same ID either way. doc = OBInstance("CutSheet", self.taxonomy) now = datetime.now() doc.set_default_context({ "entity": "JUPITER", "solar:TestConditionAxis": "solar:StandardTestConditionMember", PeriodType.instant: now, PeriodType.duration: "forever" }) doc.set("solar:ModuleNameplateCapacity", "6.25", unit_name="W", ProductIdentifierAxis=1) fact = doc.get( "solar:ModuleNameplateCapacity", Context(ProductIdentifierAxis=1, TestConditionAxis="solar:StandardTestConditionMember", entity="JUPITER", duration="forever")) # Read the fact ID that was automatically assigned when we set the fact: fact_id = fact.id # Look for fact ID in JSON: jsonstring = doc.to_JSON_string() facts = json.loads(jsonstring)["facts"] self.assertEqual(len(list(facts.keys())), 1) self.assertEqual(list(facts.keys())[0], fact_id) # Look for fact ID in XML: xml = doc.to_XML_string() root = etree.fromstring(xml) fact = root.find( "{http://xbrl.us/Solar/v1.2/2018-03-31/solar}ModuleNameplateCapacity" ) self.assertEqual(fact.attrib["id"], fact_id)
def test_set_default_context_values(self): # Test setting default values, for example something like: doc = Entrypoint("CutSheet", self.taxonomy) now = datetime.now() doc.set_default_context({ "entity": "JUPITER", "solar:TestConditionAxis": "solar:StandardTestConditionMember", PeriodType.instant: now, PeriodType.duration: "forever" }) # Could also support setting default unit, even though that's not part of context: # If we set a fact that wants an instant context, it should use 'now': doc.set("solar:DeviceCost", "100", unit_name="USD", ProductIdentifierAxis="placeholder") # This would normally raise an exception because it's missing instant, entity, and # TestConditionAxis. But we set defaults for those, so they should be filled in: fact = doc.get( "solar:DeviceCost", Context(ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", entity="JUPITER", instant=now)) self.assertEqual(fact.value, "100") self.assertEqual(fact.unit, "USD") self.assertEqual(fact.context.entity, "JUPITER") self.assertEqual(fact.context.instant, now) # TODO test method of calling set() where we pass in Context object. # If we set a fact that wants a duration context, it should use jan 1 - jan 31: doc.set("solar:ModuleNameplateCapacity", "0.3", unit_name="W", ProductIdentifierAxis="placeholder") # Would normally raise an exception because missing duration, entity, and # TestConditionAxis. But we set defaults for those, so they should be filled in: fact = doc.get( "solar:ModuleNameplateCapacity", Context(ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", entity="JUPITER", duration="forever")) self.assertEqual(fact.value, "0.3") self.assertEqual(fact.unit, "W") self.assertEqual(fact.context.entity, "JUPITER") self.assertEqual(fact.context.duration, "forever") # Try setting ALL the fields in set_default_context and then pass in NO context fields, # that should work too: doc.set_default_context({ "entity": "JUPITER", "solar:TestConditionAxis": "solar:StandardTestConditionMember", PeriodType.instant: now, "solar:ProductIdentifierAxis": "placeholder" }) doc.set("solar:DeviceCost", "99", unit_name="USD") fact = doc.get( "solar:DeviceCost", Context(ProductIdentifierAxis="placeholder", TestConditionAxis="solar:StandardTestConditionMember", entity="JUPITER", instant=now)) self.assertEqual(fact.value, "99")