def make_module(self, schema): """Regenerates the code text and usercode module from upated document schema.""" # Collect summary tables to group them by source table. summary_tables = {} for table_info in schema.itervalues(): source_table_id = summary.decode_summary_table_name(table_info.tableId) if source_table_id: summary_tables.setdefault(source_table_id, []).append(table_info) fullparts = ["import grist\n" + "from functions import * # global uppercase functions\n" + "import datetime, math, re # modules commonly needed in formulas\n"] userparts = fullparts[:] for table_info in schema.itervalues(): fullparts.append("\n\n") fullparts.append(self._make_table_model(table_info, summary_tables.get(table_info.tableId))) if not _is_special_table(table_info.tableId): userparts.append("\n\n") userparts.append(self._make_table_model(table_info, summary_tables.get(table_info.tableId), filter_for_user=True)) # Once all formulas are generated, replace the formula cache with the newly-populated version. self._formula_cache = self._new_formula_cache self._new_formula_cache = {} self._full_builder = textbuilder.Combiner(fullparts) self._user_builder = textbuilder.Combiner(userparts) self._usercode = exec_module_text(self._full_builder.get_text())
def _make_table_model(self, table_info, summary_tables, filter_for_user=False): """ Returns the code for a table model. If filter_for_user is True, includes only user-visible columns. """ table_id = table_info.tableId source_table_id = summary.decode_summary_table_name(table_id) # Sort columns by "isFormula" to output all data columns before all formula columns. columns = sorted(table_info.columns.itervalues(), key=lambda c: c.isFormula) if filter_for_user: columns = [c for c in columns if is_visible_column(c.colId)] parts = ["@grist.UserTable\nclass %s:\n" % table_id] if source_table_id: parts.append(indent(textbuilder.Text("_summarySourceTable = %r\n" % source_table_id))) for col_info in columns: parts.append(indent(self._make_field(col_info, table_id))) if summary_tables: # Include summary formulas, for the user's information. formulas = OrderedDict((c.colId, c) for s in summary_tables for c in s.columns.itervalues() if c.isFormula) parts.append(indent(textbuilder.Text("\nclass _Summary:\n"))) for col_info in formulas.itervalues(): parts.append(indent(self._make_field(col_info, table_id), levels=2)) return textbuilder.Combiner(parts)
def test_encode_summary_table_name(self): self.assertEqual(summary.encode_summary_table_name("Foo"), "GristSummary_3_Foo") self.assertEqual(summary.encode_summary_table_name("Foo2"), "GristSummary_4_Foo2") self.assertEqual(summary.decode_summary_table_name("GristSummary_3_Foo"), "Foo") self.assertEqual(summary.decode_summary_table_name("GristSummary_4_Foo2"), "Foo2") self.assertEqual(summary.decode_summary_table_name("GristSummary_3_Foo2"), "Foo") self.assertEqual(summary.decode_summary_table_name("GristSummary_4_Foo2_2"), "Foo2") # Test that underscore in the name is OK. self.assertEqual(summary.decode_summary_table_name("GristSummary_5_Foo_234"), "Foo_2") self.assertEqual(summary.decode_summary_table_name("GristSummary_4_Foo_234"), "Foo_") self.assertEqual(summary.decode_summary_table_name("GristSummary_6__Foo_234"), "_Foo_2") # Test that we return None for invalid values. self.assertEqual(summary.decode_summary_table_name("Foo2"), None) self.assertEqual(summary.decode_summary_table_name("GristSummary_3Foo"), None) self.assertEqual(summary.decode_summary_table_name("GristSummary_4_Foo"), None) self.assertEqual(summary.decode_summary_table_name("GristSummary_3X_Foo"), None) self.assertEqual(summary.decode_summary_table_name("_5_Foo_234"), None) self.assertEqual(summary.decode_summary_table_name("_GristSummary_3_Foo"), None) self.assertEqual(summary.decode_summary_table_name("gristsummary_3_Foo"), None) self.assertEqual(summary.decode_summary_table_name("GristSummary3_Foo"), None)
def _is_special_table(table_id): return table_id.startswith("_grist_") or bool(summary.decode_summary_table_name(table_id))