Helper Script to create base documentation

Hi everyone,

our base designers are constantly challenged to keep track of their bases: Which columns are used for what, which are just temporary, can some be cleaned up, should any not be renamed because some API calls rely on them?

Using the “Column Description” is not only (in my opinion) reserved for the end user, but also does not provide a good overview. Therefore, I have created the following script, which compiles some relevant meta data into an extra table in the same base. You can use additional columns to annotate, mark, sort etc. Re-run to merge changes in the base design. More important info in the source code.

Forgive my not-so-good Javascript skills, and the fact that this hasn’t been tested much. If you like it, use it. If you improve or correct it (and / or create a Python version), post your version here.


const tables = base.getTables();
// IMPORTANT preparations BEFORE you run this Script
// 1. Create am empty table to hold the data, name it like this constant OR change the constant
const dname = 'Tabledoc';
const dtable = base.getTableByName(dname);

// 2. Create the following columns in any order in that table, name them like these constants OR change the constants
const column_name = 'Column Name' // Text - visible Column name
const table_name = 'Table Name' // Text - visible Column name
const tid = 'tid' // Text - internal Table ID 
const cid = 'cid' // Text - internal Column ID
const type = 'Type' // Single select - internal column type as given by Seatable
const column_description = 'Column Description' // Text - visible column description
const valid = 'Valid' // Checkbox - Marker, unchecked = columns has been deleted since last run
const table_order = 'Table Order' // Number - Tab / Table order for nice sorting
const column_order = 'Column Order' // Number - Column order in tables for nice sorting

// 3. Create a view, name it like this constant OR change the constant
const view_name = 'DO NOT CHANGE VIEW DISPLAY';
const drows = base.getRows(dname, view_name);

// 4. Protect all those columns from above so nobody edits them - only the script will fill them
// 5. Make sure that view %view_name% alsways shows all records and all the columns above
// 6. Add columns and views to help you stay organized
// 7. Re-Run the script from time to time - your additional columns should stay untouched

const dmap = new Map();
drows.forEach(function(row){
  if( row[tid] && row[cid] ){
    dmap.set(row[tid] + "-" + row[cid], row._id);
  }
});

let tcounter = 1;
tables.forEach(function(table){
  const tname = table.name;
  const table_id = table._id;
  let counter = 1;
  if(dname != tname){ // Do not include this table
    output.text('Analyzing Table / ID: ' + tname + ' / ' + table_id);
    table.columns.forEach(function(column){
      const description = (column.description) ? column.description : "";
      if(dmap.get(table_id + '-' + column.key)){ // Existing - Update
        base.modifyRow(dtable, dmap.get(table_id + "-" + column.key),
            {[table_name]: tname,
             [column_name]: column.name, 
             [type]: column.type,
             [column_description]: description,
             [valid]: true,
             [table_order]: tcounter,
             [column_order]: counter});
        dmap.delete(table_id + '-' + column.key);
      } else { // New - Create
        base.addRow(dtable,
            {[tid]: table_id,
             [cid]: column.key,
             [table_name]: tname,
             [column_name]: column.name, 
             [type]: column.type,
             [column_description]: description,
             [valid]: true,
             [table_order]: tcounter,
             [column_order]: counter});
      }
      counter = counter + 1;
    });    
  }
  tcounter = tcounter + 1;
});

// Deleted - Mark
dmap.forEach(function(row){
  base.modifyRow(dtable, row, {[valid]: false});
});

3 Likes

Improved Version

  • For formulae, extracts the formula in a long text field named “Extra”
    • Javascript formatting fits the best IMO
    • Generic “Extra” name: More to come for other field types
  • For formulae, creates references (as links) to affected / referenced other columns
    • Thanks to Seatable for providing lots of metadata
    • “Linked columns” might hold other references for other field types

Again, sorry for the lack of coding skills and documentation.


const tables = base.getTables();
// IMPORTANT preparations BEFORE you run this Script
// 1. Create am empty table to hold the data, name it like this constant OR change the constant
const dname = 'Tabledoc';
const dtable = base.getTableByName(dname);

// 2. Create the following columns in any order in that table, name them like these constants OR change the constants
const column_name = 'Column Name' // Text - visible Column name
const table_name = 'Table Name' // Text - visible Column name
const tid = 'tid' // Text - internal Table ID 
const cid = 'cid' // Text - internal Column ID
const type = 'Type' // Single select - internal column type as given by Seatable
const column_description = 'Column Description' // Text - visible column description
const valid = 'Valid' // Checkbox - Marker, unchecked = columns has been deleted since last run
const table_order = 'Table Order' // Number - Tab / Table order for nice sorting
const column_order = 'Column Order' // Number - Column order in tables for nice sorting
const extra = 'Extra' // Long Text - Extra Info like formulae, filter data etc. etc.
const column_link = 'Linked columns' // Link to this very same table
const column_link_id = base.getColumnLinkId(dname, column_link)

// 3. Create a view, name it like this constant OR change the constant
const view_name = 'DO NOT CHANGE VIEW DISPLAY';
const drows = base.getRows(dname, view_name);

// 4. Protect all those columns from above so nobody edits them - only the script will fill them
// 5. Make sure that view %view_name% alsways shows all records and all the columns above
// 6. Add columns and views to help you stay organized
// 7. Re-Run the script from time to time - your additional columns should stay untouched

const idmap = new Map();
const dmap = new Map();
drows.forEach(function(row){
  if( row[tid] && row[cid] ){
    dmap.set(row[tid] + "-" + row[cid], row._id);
  }
});
const linkmap = new Map();

let tcounter = 1;
tables.forEach(function(table){
  const tname = table.name;
  const table_id = table._id;
  let counter = 1;
  if(dname != tname){ // Do not include this table
    output.text('Analyzing Table / ID: ' + tname + ' / ' + table_id);
    table.columns.forEach(function(column){
      const description = (column.description) ? column.description : "";
      const extradata = ('formula' === column.type && (column.data && (column.data.formula))) ? 'Formula source\n```javascript\n' + column.data.formula + '\n```' : '';
      const table_column_key = table_id + '-' + column.key;
      if(currentrow_id = dmap.get(table_column_key)){ // Existing - Update
        base.modifyRow(dtable, currentrow_id,
            {[table_name]: tname,
             [column_name]: column.name, 
             [type]: column.type,
             [extra]: extradata,
             [column_description]: description,
             [valid]: true,
             [table_order]: tcounter,
             [column_order]: counter});
        dmap.delete(table_id + '-' + column.key);
      } else { // New - Create
        addedrow = base.addRow(dtable,
            {[tid]: table_id,
             [cid]: column.key,
             [table_name]: tname,
             [column_name]: column.name, 
             [type]: column.type,
             [extra]: extradata,
             [column_description]: description,
             [valid]: true,
             [table_order]: tcounter,
             [column_order]: counter});
        currentrow_id = addedrow._id;
      }
      idmap.set(table_column_key,currentrow_id);
      if ('formula' === column.type && (column.data && (column.data.operated_columns))){
        const cols = [];
        column.data.operated_columns.forEach(function(colid){
          cols.push(table_id + '-' + colid);
        });
        linkmap.set(currentrow_id,cols);
      }
      counter = counter + 1;
    });    
  }
  tcounter = tcounter + 1;
});

// Deleted - Mark
dmap.forEach(function(row){
  base.modifyRow(dtable, row, {[valid]: false});
});

// Link fields
linkmap.forEach(function(row, idx){
  const cols = [];
  row.forEach(function(colid){
    cols.push(idmap.get(colid));
  });
  base.updateLinks(column_link_id, dname, dname, idx, cols);
});

Improved version, which follows all the column links in formulae and links. Even worse code than before, even less dcumentation, and largely untested :grin:

Complements the new “table structure” plugin nicely as a development documentation.

const tables = base.getTables();
// IMPORTANT preparations BEFORE you run this Script
// 1. Create am empty table to hold the data, name it like this constant OR change the constant
const dname = 'Tabledoc';
const dtable = base.getTableByName(dname);

// 2. Create the following columns in any order in that table, name them like these constants OR change the constants
const column_name = 'Column Name' // Text - visible Column name
const table_name = 'Table Name' // Text - visible Column name
const tid = 'tid' // Text - internal Table ID 
const cid = 'cid' // Text - internal Column ID
const type = 'Type' // Single select - internal column type as given by Seatable
const column_description = 'Column Description' // Text - visible column description
const valid = 'Valid' // Single select - Marker for "Column OK", "Column missing", "Link invalid"
const table_order = 'Table Order' // Number - Tab / Table order for nice sorting
const column_order = 'Column Order' // Number - Column order in tables for nice sorting
const extra = 'Extra' // Long Text - Extra Info like formulae, filter data etc. etc.
const column_link = 'Linked columns' // Link to this very same table
const column_link_id = base.getColumnLinkId(dname, column_link)

// 3. Create a view, name it like this constant OR change the constant
const view_name = 'DO NOT CHANGE VIEW DISPLAY';
const drows = base.getRows(dname, view_name);

// 4. Protect all those columns from above so nobody edits them - only the script will fill them
// 5. Make sure that view %view_name% alsways shows all records and all the columns above
// 6. Add columns and views to help you stay organized
// 7. Re-Run the script from time to time - your additional columns should stay untouched

const idmap = new Map();
const dmap = new Map();
drows.forEach(function(row){
  if( row[tid] && row[cid] ){
    dmap.set(row[tid] + "-" + row[cid], row._id);
  }
});
const linkmap = new Map();

let tcounter = 1;
tables.forEach(function(table){
  const tname = table.name;
  const table_id = table._id;
  let counter = 1;
  if(dname != tname){ // Do not include this table
    output.text('Analyzing Table / ID: ' + tname + ' / ' + table_id);
    table.columns.forEach(function(column){
      const description = (column.description) ? column.description : "";
      const extradata =
        ( 'formula' === column.type && (column.data && (column.data.formula)))
        ? 'Formula source\n```javascript\n' + column.data.formula + '\n```' : 
        ( ('link' === column.type || 'link-formula' === column.type) && (column.data))
        ? 'JSON config\n```json\n' + JSON.stringify(column.data,null,4) + '\n```' :
        '';
      const table_column_key = table_id + '-' + column.key;
      if(currentrow_id = dmap.get(table_column_key)){ // Existing - Update
        base.modifyRow(dtable, currentrow_id,
            {[table_name]: tname,
             [column_name]: column.name, 
             [type]: column.type,
             [extra]: extradata,
             [column_description]: description,
             [valid]: 'Column OK',
             [table_order]: tcounter,
             [column_order]: counter});
        dmap.delete(table_id + '-' + column.key);
      } else { // New - Create
        addedrow = base.addRow(dtable,
            {[tid]: table_id,
             [cid]: column.key,
             [table_name]: tname,
             [column_name]: column.name, 
             [type]: column.type,
             [extra]: extradata,
             [column_description]: description,
             [valid]: 'Column OK',
             [table_order]: tcounter,
             [column_order]: counter});
        currentrow_id = addedrow._id;
      }
      idmap.set(table_column_key,new Array());
      idmap.get(table_column_key).push(currentrow_id)
      if ('link' === column.type && (column.data)){
        linkmap.set(currentrow_id,[[column.data.other_table_id + '-' + column.data.display_column_key],tname]);
        idmap.get(table_column_key).push((table_id != column.data.other_table_id) ? column.data.other_table_id : column.data.table_id );
      }
      if (('formula' == column.type|| 'link-formula' == column.type) && (column.data && (column.data.operated_columns))){
        const cols = [];
        column.data.operated_columns.forEach(function(colid){
          cols.push(table_id + '-' + colid);
        });
        if(column.data.link_column_key && !cols.includes(table_id + '-' + column.data.link_column_key)){
          cols.push(table_id + '-' + column.data.link_column_key);
        };
        if (column.data.level1_linked_table_column_key && 'link-formula' == column.type){
          cols.push('-' + column.data.level1_linked_table_column_key);
        };
        if (column.data.level1_linked_table_column_key && 'formula' == column.type){
          cols.push('-' + column.data.level1_linked_table_column_key);
        };
        linkmap.set(currentrow_id,Array(cols,tname));
      }
      counter = counter + 1;
    });    
  }
  tcounter = tcounter + 1;
});

// Deleted - Mark
dmap.forEach(function(row){
  base.modifyRow(dtable, row, {[valid]: 'Column missing'});
});

// Link fields
linkmap.forEach(function(row, idx){
  const cols = [];
  prefix = '';
  row[0].forEach(function(colid){
    if (colid.startsWith('-') && prefix != ''){
      if(idmap.get(prefix + colid)){
        cols.push(idmap.get(prefix + colid)[0]);
        if(idmap.get(prefix + colid)[1]) prefix = idmap.get(prefix + colid)[1];
      } else {
        base.modifyRow(dtable, idx, {[valid]: 'Link invalid'});
      }
    } else {
      if(idmap.get(colid)){
        cols.push(idmap.get(colid)[0]);
        prefix = idmap.get(colid)[1];
      }
    }
  });
  base.updateLinks(column_link_id, dname, dname, idx, cols);
});

1 Like