2020-07-27 18:57:36 +00:00
|
|
|
import actions
|
|
|
|
import logger
|
|
|
|
|
|
|
|
import testutil
|
|
|
|
import test_engine
|
|
|
|
|
|
|
|
log = logger.Logger(__name__, logger.INFO)
|
|
|
|
|
|
|
|
def _bulk_update(table_name, col_names, row_data):
|
|
|
|
return actions.BulkUpdateRecord(
|
|
|
|
*testutil.table_data_from_rows(table_name, col_names, row_data))
|
|
|
|
|
|
|
|
class TestDerived(test_engine.EngineTestCase):
|
|
|
|
sample = testutil.parse_test_sample({
|
|
|
|
"SCHEMA": [
|
|
|
|
[1, "Customers", [
|
|
|
|
[1, "firstName", "Text", False, "", "", ""],
|
|
|
|
[2, "lastName", "Text", False, "", "", ""],
|
|
|
|
[3, "state", "Text", False, "", "", ""],
|
|
|
|
]],
|
|
|
|
[2, "Orders", [
|
|
|
|
[10, "year", "Int", False, "", "", ""],
|
|
|
|
[11, "customer", "Ref:Customers", False, "", "", ""],
|
|
|
|
[12, "product", "Text", False, "", "", ""],
|
|
|
|
[13, "amount", "Numeric", False, "", "", ""],
|
|
|
|
]],
|
|
|
|
],
|
|
|
|
"DATA": {
|
|
|
|
"Customers": [
|
|
|
|
["id", "firstName", "lastName", "state"],
|
|
|
|
[1, "Lois", "Long", "NY"],
|
|
|
|
[2, "Felix", "Myers", "NY"],
|
|
|
|
[3, "Grace", "Hawkins", "CT"],
|
|
|
|
[4, "Bessie", "Green", "NJ"],
|
|
|
|
[5, "Jerome", "Daniel", "CT"],
|
|
|
|
],
|
|
|
|
"Orders": [
|
|
|
|
["id", "year", "customer", "product", "amount" ],
|
|
|
|
[1, 2012, 3, "A", 15 ],
|
|
|
|
[2, 2013, 2, "A", 15 ],
|
|
|
|
[3, 2013, 3, "A", 15 ],
|
|
|
|
[4, 2014, 1, "B", 35 ],
|
|
|
|
[5, 2014, 5, "B", 35 ],
|
|
|
|
[6, 2014, 3, "A", 16 ],
|
|
|
|
[7, 2015, 1, "A", 17 ],
|
|
|
|
[8, 2015, 2, "B", 36 ],
|
|
|
|
[9, 2015, 3, "B", 36 ],
|
|
|
|
[10, 2015, 5, "A", 17 ],
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
def test_group_by_one(self):
|
|
|
|
"""
|
|
|
|
Test basic summary table operation, for a table grouped by one columns.
|
|
|
|
"""
|
|
|
|
self.load_sample(self.sample)
|
|
|
|
|
|
|
|
# Create a derived table summarizing count and total of orders by year.
|
|
|
|
self.apply_user_action(["CreateViewSection", 2, 0, 'record', [10]])
|
|
|
|
|
|
|
|
# Check the results.
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", ["id", "year", "count", "amount", "group" ], [
|
|
|
|
[1, 2012, 1, 15, [1]],
|
|
|
|
[2, 2013, 2, 30, [2,3]],
|
|
|
|
[3, 2014, 3, 86, [4,5,6]],
|
|
|
|
[4, 2015, 4, 106, [7,8,9,10]],
|
|
|
|
])
|
|
|
|
|
|
|
|
# Updating amounts should cause totals to be updated in the summary.
|
|
|
|
out_actions = self.update_records("Orders", ["id", "amount"], [
|
|
|
|
[1, 14],
|
|
|
|
[2, 14]
|
|
|
|
])
|
|
|
|
self.assertPartialOutActions(out_actions, {
|
2020-11-02 15:48:47 +00:00
|
|
|
"stored": [
|
|
|
|
actions.BulkUpdateRecord("Orders", [1,2], {'amount': [14, 14]}),
|
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [1,2], {'amount': [14, 29]})
|
|
|
|
],
|
2020-07-27 18:57:36 +00:00
|
|
|
"calls": {"GristSummary_6_Orders": {"amount": 2}}
|
|
|
|
})
|
|
|
|
|
|
|
|
# Changing a record from one product to another should cause the two affected lines to change.
|
|
|
|
out_actions = self.update_record("Orders", 10, year=2012)
|
|
|
|
self.assertPartialOutActions(out_actions, {
|
2020-11-02 15:48:47 +00:00
|
|
|
"stored": [
|
|
|
|
actions.UpdateRecord("Orders", 10, {"year": 2012}),
|
2020-07-27 18:57:36 +00:00
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [1,4], {"amount": [31.0, 89.0]}),
|
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [1,4], {"count": [2,3]}),
|
2020-11-02 15:48:47 +00:00
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [1,4], {"group": [[1,10], [7,8,9]]}),
|
2020-07-27 18:57:36 +00:00
|
|
|
],
|
|
|
|
"calls": {"GristSummary_6_Orders": {"group": 2, "amount": 2, "count": 2},
|
|
|
|
"Orders": {"#lookup##summary#GristSummary_6_Orders": 1,
|
|
|
|
"#summary#GristSummary_6_Orders": 1}}
|
|
|
|
})
|
|
|
|
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", ["id", "year", "count", "amount", "group" ], [
|
|
|
|
[1, 2012, 2, 31.0, [1,10]],
|
|
|
|
[2, 2013, 2, 29.0, [2,3]],
|
|
|
|
[3, 2014, 3, 86.0, [4,5,6]],
|
|
|
|
[4, 2015, 3, 89.0, [7,8,9]],
|
|
|
|
])
|
|
|
|
|
|
|
|
# Changing a record to a new year that wasn't in the summary should cause an add-record.
|
|
|
|
out_actions = self.update_record("Orders", 10, year=1999)
|
|
|
|
self.assertPartialOutActions(out_actions, {
|
|
|
|
"stored": [
|
|
|
|
actions.UpdateRecord("Orders", 10, {"year": 1999}),
|
|
|
|
actions.AddRecord("GristSummary_6_Orders", 5, {'year': 1999}),
|
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [1,5], {"amount": [14.0, 17.0]}),
|
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [1,5], {"count": [1,1]}),
|
2020-11-02 15:48:47 +00:00
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [1,5], {"group": [[1], [10]]}),
|
2020-07-27 18:57:36 +00:00
|
|
|
],
|
|
|
|
"calls": {
|
|
|
|
"GristSummary_6_Orders": {'#lookup#year': 1, "group": 2, "amount": 2, "count": 2},
|
2021-12-15 14:50:55 +00:00
|
|
|
"Orders": {"#lookup##summary#GristSummary_6_Orders": 1,
|
|
|
|
"#summary#GristSummary_6_Orders": 1}}
|
2020-07-27 18:57:36 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", ["id", "year", "count", "amount", "group" ], [
|
|
|
|
[1, 2012, 1, 14.0, [1]],
|
|
|
|
[2, 2013, 2, 29.0, [2,3]],
|
|
|
|
[3, 2014, 3, 86.0, [4,5,6]],
|
|
|
|
[4, 2015, 3, 89.0, [7,8,9]],
|
|
|
|
[5, 1999, 1, 17.0, [10]],
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
def test_group_by_two(self):
|
|
|
|
"""
|
|
|
|
Test a summary table created by grouping on two columns.
|
|
|
|
"""
|
|
|
|
self.load_sample(self.sample)
|
|
|
|
|
|
|
|
self.apply_user_action(["CreateViewSection", 2, 0, 'record', [10, 12]])
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", [
|
|
|
|
"id", "year", "product", "count", "amount", "group"
|
|
|
|
], [
|
|
|
|
[1, 2012, "A", 1, 15.0, [1]],
|
|
|
|
[2, 2013, "A", 2, 30.0, [2,3]],
|
|
|
|
[3, 2014, "B", 2, 70.0, [4,5]],
|
|
|
|
[4, 2014, "A", 1, 16.0, [6]],
|
|
|
|
[5, 2015, "A", 2, 34.0, [7,10]],
|
|
|
|
[6, 2015, "B", 2, 72.0, [8,9]],
|
|
|
|
])
|
|
|
|
|
|
|
|
# Changing a record from one product to another should cause the two affected lines to change,
|
|
|
|
# or new lines to be created as needed.
|
|
|
|
out_actions = self.update_records("Orders", ["id", "product"], [
|
|
|
|
[2, "B"],
|
|
|
|
[6, "B"],
|
|
|
|
[7, "C"],
|
|
|
|
])
|
|
|
|
self.assertPartialOutActions(out_actions, {
|
|
|
|
"stored": [
|
|
|
|
actions.BulkUpdateRecord("Orders", [2, 6, 7], {"product": ["B", "B", "C"]}),
|
|
|
|
actions.AddRecord("GristSummary_6_Orders", 7, {'year': 2013, 'product': 'B'}),
|
|
|
|
actions.AddRecord("GristSummary_6_Orders", 8, {'year': 2015, 'product': 'C'}),
|
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [2,3,4,5,7,8], {
|
|
|
|
"amount": [15.0, 86.0, 0, 17.0, 15.0, 17.0]
|
|
|
|
}),
|
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [2,3,4,5,7,8], {
|
|
|
|
"count": [1, 3, 0, 1, 1, 1]
|
|
|
|
}),
|
2020-11-02 15:48:47 +00:00
|
|
|
actions.BulkUpdateRecord("GristSummary_6_Orders", [2,3,4,5,7,8], {
|
|
|
|
"group": [[3], [4,5,6], [], [10], [2], [7]]
|
|
|
|
}),
|
2020-07-27 18:57:36 +00:00
|
|
|
],
|
|
|
|
})
|
|
|
|
|
|
|
|
# Verify the results.
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", [
|
|
|
|
"id", "year", "product", "count", "amount", "group"
|
|
|
|
], [
|
|
|
|
[1, 2012, "A", 1, 15.0, [1]],
|
|
|
|
[2, 2013, "A", 1, 15.0, [3]],
|
|
|
|
[3, 2014, "B", 3, 86.0, [4,5,6]],
|
|
|
|
[4, 2014, "A", 0, 0.0, []],
|
|
|
|
[5, 2015, "A", 1, 17.0, [10]],
|
|
|
|
[6, 2015, "B", 2, 72.0, [8,9]],
|
|
|
|
[7, 2013, "B", 1, 15.0, [2]],
|
|
|
|
[8, 2015, "C", 1, 17.0, [7]],
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_group_with_references(self):
|
|
|
|
"""
|
|
|
|
Test summary tables grouped on indirect values. In this example we want for each
|
|
|
|
customer.state, the number of customers and the total of their orders, which we can do either
|
|
|
|
as a summary on the Customers table, or a summary on the Orders table.
|
|
|
|
"""
|
|
|
|
self.load_sample(self.sample)
|
|
|
|
|
|
|
|
# Create a summary on the Customers table. Adding orders involves a lookup for each customer.
|
|
|
|
self.apply_user_action(["CreateViewSection", 1, 0, 'record', [3]])
|
|
|
|
self.add_column("GristSummary_9_Customers", "totalAmount",
|
|
|
|
formula="sum(sum(Orders.lookupRecords(customer=c).amount) for c in $group)")
|
|
|
|
|
|
|
|
self.assertPartialData("GristSummary_9_Customers", ["id", "state", "count", "totalAmount"], [
|
|
|
|
[1, "NY", 2, 103.0 ],
|
|
|
|
[2, "CT", 2, 134.0 ],
|
|
|
|
[3, "NJ", 1, 0.0 ],
|
|
|
|
])
|
|
|
|
|
|
|
|
# # Create the same summary on the Orders table, looking up 'state' via the Customer reference.
|
|
|
|
# self.apply_user_action(["AddDerivedTableSource", "Summary4", "Orders",
|
|
|
|
# {"state": "$customer.state"}])
|
|
|
|
# self.add_column("Summary4", "numCustomers", formula="len(set($source_Orders.customer))")
|
|
|
|
# self.add_column("Summary4", "totalAmount", formula="sum($source_Orders.amount)")
|
|
|
|
|
|
|
|
# self.assertPartialData("Summary4", ["id", "state", "numCustomers", "totalAmount"], [
|
|
|
|
# [1, "CT", 2, 134.0 ],
|
|
|
|
# [2, "NY", 2, 103.0 ],
|
|
|
|
# ])
|
|
|
|
|
|
|
|
# In either case, changing an amount (from 36->37 for a CT customer) should update summaries.
|
|
|
|
out_actions = self.update_record('Orders', 9, amount=37)
|
|
|
|
self.assertPartialOutActions(out_actions, {
|
2020-11-02 15:48:47 +00:00
|
|
|
"stored": [
|
|
|
|
actions.UpdateRecord("Orders", 9, {"amount": 37}),
|
|
|
|
actions.UpdateRecord("GristSummary_9_Customers", 2, {"totalAmount": 135.0}),
|
|
|
|
]
|
2020-07-27 18:57:36 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
# In either case, changing a customer's state should trigger recomputation too.
|
|
|
|
# We are changing a NY customer with $51 in orders to MA.
|
|
|
|
self.update_record('Customers', 2, state="MA")
|
|
|
|
self.assertPartialData("GristSummary_9_Customers", ["id", "state", "count", "totalAmount"], [
|
|
|
|
[1, "NY", 1, 52.0 ],
|
|
|
|
[2, "CT", 2, 135.0 ],
|
|
|
|
[3, "NJ", 1, 0.0 ],
|
|
|
|
[4, "MA", 1, 51.0 ],
|
|
|
|
])
|
|
|
|
# self.assertPartialData("Summary4", ["id", "state", "numCustomers", "totalAmount"], [
|
|
|
|
# [1, "CT", 2, 135.0 ],
|
|
|
|
# [2, "NY", 1, 52.0 ],
|
|
|
|
# [3, "MA", 1, 51.0 ],
|
|
|
|
# ])
|
|
|
|
|
|
|
|
# Similarly, changing an Order to refer to a different customer should update both tables.
|
|
|
|
# Here we are changing a $17 order (#7) for a NY customer (#1) to a NJ customer (#4).
|
|
|
|
out_actions = self.update_record("Orders", 7, customer=4)
|
|
|
|
# self.assertPartialOutActions(out_actions, {
|
|
|
|
# "stored": [actions.UpdateRecord("Orders", 7, {"customer": 4}),
|
|
|
|
# actions.AddRecord("Summary4", 4, {"state": "NJ"}),
|
|
|
|
# actions.UpdateRecord("Summary4", 4, {"manualSort": 4.0})]
|
|
|
|
# })
|
|
|
|
self.assertPartialData("GristSummary_9_Customers", ["id", "state", "count", "totalAmount"], [
|
|
|
|
[1, "NY", 1, 35.0 ],
|
|
|
|
[2, "CT", 2, 135.0 ],
|
|
|
|
[3, "NJ", 1, 17.0 ],
|
|
|
|
[4, "MA", 1, 51.0 ],
|
|
|
|
])
|
|
|
|
# self.assertPartialData("Summary4", ["id", "state", "numCustomers", "totalAmount"], [
|
|
|
|
# [1, "CT", 2, 135.0 ],
|
|
|
|
# [2, "NY", 1, 35.0 ],
|
|
|
|
# [3, "MA", 1, 51.0 ],
|
|
|
|
# [4, "NJ", 1, 17.0 ],
|
|
|
|
# ])
|
|
|
|
|
|
|
|
def test_deletions(self):
|
|
|
|
self.load_sample(self.sample)
|
|
|
|
|
|
|
|
# Create a summary table summarizing count and total of orders by year.
|
|
|
|
self.apply_user_action(["CreateViewSection", 2, 0, 'record', [10]])
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", ["id", "year", "count", "amount", "group" ], [
|
|
|
|
[1, 2012, 1, 15.0, [1]],
|
|
|
|
[2, 2013, 2, 30.0, [2,3]],
|
|
|
|
[3, 2014, 3, 86.0, [4,5,6]],
|
|
|
|
[4, 2015, 4, 106.0, [7,8,9,10]],
|
|
|
|
])
|
|
|
|
|
|
|
|
# Update a record so that a new line appears in the summary table.
|
|
|
|
out_actions_update = self.update_record("Orders", 1, year=2007)
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", ["id", "year", "count", "amount", "group" ], [
|
|
|
|
[1, 2012, 0, 0.0, []],
|
|
|
|
[2, 2013, 2, 30.0, [2,3]],
|
|
|
|
[3, 2014, 3, 86.0, [4,5,6]],
|
|
|
|
[4, 2015, 4, 106.0, [7,8,9,10]],
|
|
|
|
[5, 2007, 1, 15.0, [1]],
|
|
|
|
])
|
|
|
|
|
|
|
|
# Undo and ensure that the new line is gone from the summary table.
|
|
|
|
out_actions_undo = self.apply_undo_actions(out_actions_update.undo)
|
|
|
|
self.assertPartialData("GristSummary_6_Orders", ["id", "year", "count", "amount", "group" ], [
|
|
|
|
[1, 2012, 1, 15.0, [1]],
|
|
|
|
[2, 2013, 2, 30.0, [2,3]],
|
|
|
|
[3, 2014, 3, 86.0, [4,5,6]],
|
|
|
|
[4, 2015, 4, 106.0, [7,8,9,10]],
|
|
|
|
])
|
|
|
|
self.assertPartialOutActions(out_actions_undo, {
|
2020-11-02 15:48:47 +00:00
|
|
|
"stored": [
|
|
|
|
actions.UpdateRecord("GristSummary_6_Orders", 1, {"group": [1]}),
|
|
|
|
actions.UpdateRecord("GristSummary_6_Orders", 1, {"count": 1}),
|
|
|
|
actions.UpdateRecord("GristSummary_6_Orders", 1, {"amount": 15.0}),
|
|
|
|
actions.RemoveRecord("GristSummary_6_Orders", 5),
|
|
|
|
actions.UpdateRecord("Orders", 1, {"year": 2012}),
|
|
|
|
],
|
2020-07-27 18:57:36 +00:00
|
|
|
"calls": {"GristSummary_6_Orders": {"group": 1, "amount": 1, "count": 1},
|
2021-12-15 14:50:55 +00:00
|
|
|
"Orders": {"#lookup##summary#GristSummary_6_Orders": 1,
|
|
|
|
"#summary#GristSummary_6_Orders": 1}}
|
2020-07-27 18:57:36 +00:00
|
|
|
})
|