>> new row <<<')
- output.text(row);
- output.text(row['Name']);
- }
- ```
-
- 1. get the content of the table `Table1` (replace `Table1` with your actual table name).
- 2. get the content of the view `Default View`.
- 3. get the rows displayed in the view `Default View` of the table `Table1`.
- 4. iterate over all rows and print them
-
- This time, we will get content of the `Name` column for each row displayed in the view `Default View` of the table `Table1`.
-
-=== "Write new row"
-
- ``` js
- const table = base.getTableByName('Table1'); // (1)!
-
- const newRow = { // (2)!
- 'Name': 'Hugo',
- 'Age': 3,
- };
-
- try {
- const row = base.addRow(table, newRow);
- output.text(`New row added with _id: ${row._id}`);
- } catch (error) {
- output.text(`Error adding row: ${error}`);
- }
- ```
-
- 1. get the content of the table `Table1` (replace `Table1` with your actual table name).
- 2. create an object containing column names `Name` and `Age` and the values you would like to set.
-
-=== "Update one specific row"
-
- ``` js
- // Get the table
- const table = base.getTableByName('Table1');
-
- // Specify the row_id you want to update
- const rowId = 'KDW9PZMkTOuwtx71pmAMxA'; // (1)!
-
- // Define the updates you want to make
- // Replace 'Name' with the actual column name you want to update
- // and 'NewValue' with the new value you want to set
- // You can define more key:value pairs if you want to update
- // several values of the row at the same time
- const updates = {
- 'Name': 'NewValue'
- };
-
- base.updateRow(table, rowId, updates); // (2)!
- ```
-
- 1. define the id of the row you want to modify. You can also use `base.context.currentRow;` to access the current (selected) row.
- 2. update each values contained in the object `updates` of the row whose id is `rowId` in the table `Table1`.
-
- Do not hesitate to write comments in your code. It will help you (or others) to understand it more easily afterwards.
diff --git a/docs/scripts/javascript/objects/links.md b/docs/scripts/javascript/objects/links.md
deleted file mode 100644
index 6f527a3e..00000000
--- a/docs/scripts/javascript/objects/links.md
+++ /dev/null
@@ -1,223 +0,0 @@
-# Links
-
-!!! warning "link id and column key"
-
- `linkId` should not be mistaken with the column `key`! The `key` value is unique (like an id) whereas the link id will be shared between the two linked columns. Please note that `linkId` is used as argument to add/update/remove links, whereas you'll have to provide `linkColumnKey` (the link column `key`) to get linked records. Both information are available in the column object:
-
- ```json
- {
- "key": "Cp51", /* (1)! */
- "type": "link",
- "name": "Link column",
- "editable": true,
- "width": 200,
- "resizable": true,
- "draggable": true,
- "data": {
- "display_column_key": "0000",
- "is_internal": true,
- "link_id": "UAmR", /* (2)! */
- "table_id": "FJkA", /* (3)! */
- "other_table_id": "nw8k", /* (4)! */
- "is_multiple": true,
- "is_row_form_view": false,
- "view_id": "",
- "array_type": "text",
- "array_data": null,
- "result_type": "array"
- },
- "permission_type": "",
- "permitted_users": [],
- "permitted_group": [],
- "edit_metadata_permission_type": "",
- "edit_metadata_permitted_users": [],
- "edit_metadata_permitted_group": [],
- "description": null,
- "colorbys": {}
- }
- ```
-
- 1. The column `key` (referred as `linkColumnKey` in `base.getLinkedRecords` arguments)
-
- 2. The link id of the column (referred as `linkId` in the add/update/remove link(s) methods arguments)
-
- 3. The table whose id is `table_id` is referred later in this section as the *source* table (the table containing this column)
-
- 4. The table whose id is `other_table_id` is referred later in this section as the *target* table
-
-## Get linkId
-
-!!! abstract "getColumnLinkId"
-
- Get the link id of the column `columnName` from the table `tableName`.
-
- ```js
- base.getColumnLinkId(tableName: String, columnName: String);
- ```
-
- __Output__ String (throws an error if table `tableName` or column `columnName` doesn't exist)
-
- __Example__
-
- ```js
- base.getColumnLinkId('Table1', 'Table2 link');
- ```
-
-## Get linked records
-
-!!! info "Rows and records, source and target"
-
- Rows and records are basically the same things. However, to make the following description easier to understand, we will differentiate them:
-
- - Rows are from the *source* table (the table whose id is `tableId`)
-
- - Records are the rows from the *target* table (the table linked to the *source* table in the column whose `key` is `linkColumnKey` or whose link id is `linkId`)
-
-!!! abstract "getLinkedRecords"
-
- List the records linked (in the column whose `key` is `linkColumnKey`) to one or more rows of the *source* table. The row(s) you want to get the linked records from are defined in the `linkedRows` object (see below).
-
- ```js
- await/* (1)! */ base.getLinkedRecords(tableId: String, linkColumnKey: String, linkedRows: Object) /* (2)! */;
- ```
-
- 1. `await` is used for asynchronous functions. This is **required** to ensure that the following operations (or the variable where you store the results) wait for the query's response to arrive before continuing to execute the script
-
- 2. `tableId`: the id of *source* table
-
- `linkColumnKey`: the column **key** of the link-type column of *source* table (**not** the link id from `base.getColumnLinkId`)
-
- `linkedRows`: an array of objects, each of them containing:
-
- - `row_id`: the id of the row we want to get the linked records from
-
- - `limit`: the maximum number of linked records to get (default is 10)
-
- - `offset`: the number of first linked records not to retrieve (default is 0)
-
- __Output__ A `key`:`value` data structure where each `key` is the id of a row of the *source* table and the corresponding value is an array of link objects (see Output structure example below)
-
- __Example__
-
- === "Function run"
-
- ```js
- await base.getLinkedRecords('0000', '89o4', [
- {'row_id': 'FzNqJxVUT8KrRjewBkPp8Q', 'limit': 2, 'offset': 0},
- {'row_id': 'Jmnrkn6TQdyRg1KmOM4zZg', 'limit': 20}
- ]);
- ```
-
- === "Output structure example"
-
- ```js
- {
- 'FzNqJxVUT8KrRjewBkPp8Q' /* (1)! */: [
- {'row_id': 'LocPgVvsRm6bmnzjFDP9bA', 'display_value': '1'} /* (2)! */,
- {'row_id': 'OA6x7CYoRuyc2pT52Znfmw', 'display_value': '3'},
- ...
- ],
- 'Jmnrkn6TQdyRg1KmOM4zZg': [
- {'row_id': 'LocPgVvsRm6bmnzjFDP9bA', 'display_value': '1'},
- {'row_id': 'OA6x7CYoRuyc2pT52Znfmw', 'display_value': '3'},
- ...
- ]
- }
- ```
-
- 1. id of a row of the *source* table
-
- 2. link object:
-
- - `row_id` is the id of the linked record (row from the *target* table)
- - `display_value` is the value displayed in the column whose `key` is `linkColumnKey`
- (from a column of the *target* table)
-
- __Output__ Object containing the linked records for each row (see Output structure example above)
-
- __Example: Get linked records from current row__
-
- ```js
- const table = base.getTableByName('Table1');
- const linkColumn = base.getColumnByName(table, 'Table2 link');
- const currentRowLinks = await base.getLinkedRecords(table._id, linkColumn.key, [{'row_id': base.context.currentRow._id, 'limit':100 /* (1)! */}]);
- currentRowLinks[base.context.currentRow._id].forEach((link) => {output.text(link)});
- ```
-
- 1. `limit`:100 => the response will return maximum 100 rows
-
-
-## Add link
-
-!!! abstract "addLink"
-
- Add link in a link-type column. You'll need the *source* table's name `tableName`, the *target* table's name `linkedTableName`, the `linkId` from the link-type column and both the ids of the rows you want to link: `rowId` for the row from the *source* table and `linkedRowId` for the record from the *target* table.
-
- ```js
- base.addLink(linkId: String, tableName: String, linkedTableName: String, rowId: String,
- linkedRowId: String);
- ```
-
- __Output__ Nothing
-
- __Example__
-
- ```js
- base.addLink('5WeC', 'Team Members', 'Contacts', 'CGtoJB1oQM60RiKT-c5J-g', 'PALm2wPKTCy-jdJNv_UWaQ');
- ```
-
- __Example: Add link to current row__
-
- ```js
- // Do not hesitate to store the tables' and columns' names at the beginning of your script,
- // it will make it really easier to update if names change
- const table1Name = "Table1";
- const table1LinkColumnName = "Table2 link";
- const table2Name = "Table2";
-
- const linkId = base.getColumnLinkId(table1Name, table1LinkColumnName); /* (1)! */
- const currentRowId = base.context.currentRow._id;
- base.addLink(linkId, table1Name, table2Name, currentRowId, 'J5St2clyTMu_OFf9WD8PbA');
- ```
-
- 1. Remember you can use `base.getColumnLinkId` to get the link id of a specific link-type column.
-
-
-## Update link(s)
-
-!!! abstract "updateLinks"
-
- Update the content of the link-type column whose link id is `linkId` for the row with id `rowId` in the table `tableName`. It will remove all existing row links and add new links to records of table `linkedTableName` with ids listed in the `updatedlinkedRowIds` array.
-
- ```js
- base.updateLinks(linkId, tableName, linkedTableName, rowId, updatedlinkedRowIds: Array of String);
- ```
-
- __Output__ Nothing
-
- __Example__
-
- ```js
- const records = base.getRows('Contacts', 'Default_view');
- // Update links for row from "Team Members" with _id CGtoJB1oQM60RiKT-c5J-g to [records[0]._id, records[1]._id, records[2]._id, records[3]._id]
- // Real-life tip: ensure that the array "records" actually contains at least 4 elements!
- base.updateLinks('5WeC', 'Team Members', 'Contacts', 'CGtoJB1oQM60RiKT-c5J-g', [records[0]._id, records[1]._id, records[2]._id, records[3]._id]);
- ```
-
-## Remove link
-
-!!! abstract "removeLink"
-
- Delete the link to the record from table `linkedTableName` whose id is `linkedRowId` in the row from table `tableName` whose id is `rowId`. Every arguments are `String`.
-
- ```js
- base.removeLink(linkId, tableName, linkedTableName, rowId, linkedRowId);
- ```
-
- __Output__ Nothing
-
- __Example__
-
- ```js
- base.removeLink('5WeC', 'Team Members', 'Contacts', 'CGtoJB1oQM60RiKT-c5J-g', 'PALm2wPKTCy-jdJNv_UWaQ');
- ```
diff --git a/docs/scripts/javascript/objects/output.md b/docs/scripts/javascript/objects/output.md
deleted file mode 100644
index 0a5ad4b4..00000000
--- a/docs/scripts/javascript/objects/output.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Output
-
-Two functions are available to display results in the text editor window, allowing you to output strings in text or Markdown format.
-
-!!! abstract "text"
-
- Prints the content of `anything` as normal text. Code Syntax is ignored and just printed.
-
- ``` js
- output.text(anything: String/Object/Array)
- ```
-
- __Output__ String
-
- __Example__
-
- ``` js
- const table = base.getActiveTable();
- output.text(table.name);
- ```
-
-!!! abstract "markdown"
-
- Prints the content of `anything`, while using Markdown formatting to style the output.
-
- ``` js
- output.markdown(anything: String/Object/Array)
- ```
-
- __Output__ String
-
- __Example__
-
- ``` js
- const table = base.getActiveTable();
- output.markdown(`# This is a headline and prints the name of the table: ${table.name}`);
- ```
diff --git a/docs/scripts/javascript/objects/rows.md b/docs/scripts/javascript/objects/rows.md
deleted file mode 100644
index bd0acc4a..00000000
--- a/docs/scripts/javascript/objects/rows.md
+++ /dev/null
@@ -1,889 +0,0 @@
-# Rows
-
-You'll find below all the available methods to interact with the rows of a SeaTable table. In this section, you'll have to deal with the id of the rows. You can find few tips on how to get it in [the user manual](https://seatable.com/help/was-ist-die-zeilen-id/).
-
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-## Get row(s)
-
-!!! abstract "getRow / getRowById (deprecated)"
-
- Get a `table`'s row via its id `rowId`.
-
- ``` js
- base.getRow(table: Object/String /* (1)! */, rowId: String);
- ```
-
- 1. `table`: either a table object or the table name
-
- __Output__ Single row object (throws an error if `table` doesn't exist or if no row with the specified id `rowId` exists)
-
- __Example__
-
- ``` js
- const table = base.getTableByName('Table1');
- const row = base.getRow(table, "M_lSEOYYTeuKTaHCEOL7nw");
- ```
-
- ``` js
- const row = base.getRow('Table1', "M_lSEOYYTeuKTaHCEOL7nw");
- ```
-
-!!! abstract "getRows"
-
- Get all the rows displayed in the `view` of a `table`.
-
- ``` js
- base.getRows(table: Object/String, view: Object/String /* (1)! */);
- ```
-
- 1. `table`: either a table object or the table name
-
- `view` (required): either a view object or the view name
-
- __Output__ Array of row objects (throws an error if `table` or `view` doesn't exist)
-
- __Example__
-
- ``` js
- const table = base.getTableByName('Table1');
- const view = base.getViewByName(table, 'Default View');
- const rows = base.getRows(table, view);
-
- rows.forEach((row) => {
- output.text(row._id);
- })
- ```
-
- ``` js
- const rows = base.getRows('Table1', 'Default View');
- ```
-
-!!! abstract "query"
-
- Use SQL to query a base. SQL queries are the most powerful way access data stored in a base. If you're not familiar with SQL syntax, we recommend using first the [SQL query plugin](https://seatable.com/help/anleitung-zum-sql-abfrage-plugin/). Most SQL syntax is supported, you can check the [SQL Reference](/scripts/sql/introduction.md) section of this manual for more information.
-
- ``` js
- await/* (1)! */ base.query(sqlStatement: String);
- ```
-
- 1. `await` is used for asynchronous functions. This is **required** to ensure that the following operations (or the variable where you store the results) wait for the query's response to arrive before continuing to execute the script
-
- !!! info "Backticks for table or column names containing special characters or using reserved words"
- For SQL queries, you can use numbers, special characters or spaces in the names of your tables and columns. However, you'll **have to** escape these names with backticks in order for your query to be correctly interpreted, for example `` SELECT * FROM `My Table` ``.
-
- Similarly, if some of your table or column names are the same as [SQL function](/scripts/sql/functions.md) names (for example a date-type column named `date`), you'll also **have to** escape them in order for the query interpreter to understand that it's not a function call missing parameters, but rather a table or column name.
-
- __Output__ Array of row objects (single empty object if no row match the request's conditions)
-
- All the examples below are related to a table **Bill** with the following structure/data:
-
- | name | price | year |
- | ----- | ----- | ----- |
- | Bob | 300 | 2021 |
- | Bob | 300 | 2019 |
- | Tom | 100 | 2019 |
- | Tom | 100 | 2020 |
- | Tom | 200 | 2021 |
- | Jane | 200 | 2020 |
- | Jane | 200 | 2021 |
-
-
- __Example: Get everything with a wildcard__
-
- === "Function call"
-
- ``` js
- const data = await base.query('select * from Bill');/* (1)! */
- output.text(data);
- ```
-
- 1. `*` means that you want to get the whole rows data (columns's values and specific row data such as id, etc.)
-
- === "Output"
-
- ```json
- [
- {
- "name": "Bob",
- "price": 300,
- "year": 2021,
- "_locked": null,
- "_locked_by": null,
- "_archived": false,
- "_creator": "bd26d2b...82ca3fe1178073@auth.local",
- "_ctime": "2025-09-15T10:57:19.106+02:00",
- "_last_modifier": "bd26d2b...82ca3fe1178073@auth.local",
- "_mtime": "2025-09-18T09:52:00+02:00",
- "_id": "W77uzH1cSXu2v2UtqA3xSw"
- },
- {
- "name": "Bob",
- "price": 300,
- "year": 2019,
- "_locked": null,
- "_locked_by": null,
- "_archived": false,
- "_creator": "bd26d2b...82ca3fe1178073@auth.local",
- "_ctime": "2025-09-15T10:57:22.112+02:00",
- "_last_modifier": "bd26d2b...82ca3fe1178073@auth.local",
- "_mtime": "2025-09-18T09:52:00+02:00",
- "_id": "IxONgyDFQxmcDKpZWlQ9XA"
- },
- {
- "name": "Tom",
- "price": 100,
- "year": 2019,
- "_locked": null,
- "_locked_by": null,
- "_archived": false,
- "_creator": "bd26d2b...82ca3fe1178073@auth.local",
- "_ctime": "2025-09-15T10:57:23.4+02:00",
- "_last_modifier": "bd26d2b...82ca3fe1178073@auth.local",
- "_mtime": "2025-09-18T09:52:00+02:00",
- "_id": "K4LBuQ7aSjK9JwN14ITqvA"
- },
- {
- "name": "Tom",
- "price": 100,
- "year": 2020,
- "_locked": null,
- "_locked_by": null,
- "_archived": false,
- "_creator": "bd26d2b...82ca3fe1178073@auth.local",
- "_ctime": "2025-09-18T09:52:00+02:00",
- "_last_modifier": "bd26d2b...82ca3fe1178073@auth.local",
- "_mtime": "2025-09-18T09:52:00+02:00",
- "_id": "EHcQEaxiRzm3Zvq8B33bwQ"
- },
- {
- "name": "Tom",
- "price": 200,
- "year": 2021,
- "_locked": null,
- "_locked_by": null,
- "_archived": false,
- "_creator": "bd26d2b...82ca3fe1178073@auth.local",
- "_ctime": "2025-09-18T09:52:00+02:00",
- "_last_modifier": "bd26d2b...82ca3fe1178073@auth.local",
- "_mtime": "2025-09-18T09:52:00+02:00",
- "_id": "CjaCdBlNRXKkYkm231shqg"
- },
- {
- "name": "Jane",
- "price": 200,
- "year": 2020,
- "_locked": null,
- "_locked_by": null,
- "_archived": false,
- "_creator": "bd26d2b...82ca3fe1178073@auth.local",
- "_ctime": "2025-09-18T09:52:00+02:00",
- "_last_modifier": "bd26d2b...82ca3fe1178073@auth.local",
- "_mtime": "2025-09-18T09:52:00+02:00",
- "_id": "YzmUexIAR7iDWmhKGHgpMw"
- },
- {
- "name": "Jane",
- "price": 200,
- "year": 2021,
- "_locked": null,
- "_locked_by": null,
- "_archived": false,
- "_creator": "bd26d2b...82ca3fe1178073@auth.local",
- "_ctime": "2025-09-18T09:52:00+02:00",
- "_last_modifier": "bd26d2b...82ca3fe1178073@auth.local",
- "_mtime": "2025-09-18T09:52:00+02:00",
- "_id": "HJi7wbUMQIOuIlPaoO9Fbg"
- }
- ]
- ```
-
- __Example with WHERE__
-
- === "Function call 1 (filter by year)"
-
- ``` js
- const data = await base.query('select name, price from Bill where year = 2021');
- output.text(data);
- ```
-
- === "Output #1"
-
- ```json
- [
- {"name":"Bob","price":"300"},
- {"name":"Tom","price":"200"},
- {"name":"Jane","price":"200"}
- ]
- ```
-
- === "Function call 2 (filter by name)"
-
- ```js
- const data = await base.query('select name, price, year from Bill where name = "Bob"');
- output.text(data);
- ```
-
- === "Output #2"
-
- ```json
- [
- {"name":"Bob","price":"300","year":"2021"},
- {"name":"Bob","price":"300","year":"2019"}
- ]
- ```
-
-
- __Example with GROUP BY__
-
- === "Function call"
-
- ``` js
- const data = await base.query('select name, sum(price) from Bill group by name');
- output.text(data);
- ```
-
- === "Output"
-
- ```json
- [
- {'name': 'Bob', 'SUM(price)': 600},
- {'name': 'Tom', 'SUM(price)': 400},
- {'name': 'Jane', 'SUM(price)': 400}
- ]
- ```
-
- __Example with DISTINCT__
-
- === "Function call"
-
- ``` js
- const data = await base.query('select distinct name from Bill');
- output.text(data);
- ```
-
- === "Output"
-
- ```json
- [
- {'name': 'Bob'},
- {'name': 'Tom'},
- {'name': 'Jane'}
- ]
- ```
-
-!!! abstract "getGroupedRows"
-
- Get rows in the grouped `view` of a `table`.
-
- ``` js
- base.getGroupedRows(table: Object/String, view: Object/String /* (1)! */);
- ```
-
- 1. `table`: either a table object or the table name
-
- `view` (required): either a view object or the view name
-
- __Output__ Array of grouped rows object (see Output example below)
-
- __Example__
-
- === "Function call"
-
- ``` js
- const table = base.getTableByName('Table1');
- const view = base.getViewByName(table, 'GroupedView');
- const groupViewRows = base.getGroupedRows(table, view);
- ```
-
- === "Output example"
-
- ```json
- [
- { /* (1)! */
- "column_name": "date",
- "column_key": "tc2B",
- "cell_value": "2025-09",
- "rows": [], /* (2)! */
- "subgroups": [
- {
- "column_name": "Val2",
- "column_key": "7Q0G",
- "cell_value": 462,
- "rows": [
- {
- "bjcM": 12,
- "0000": "John",
- "7Q0G": 462,
- "tc2B": "2025-09-11",
- "Tm99": "520035",
- "_creator": "aa",
- "_last_modifier": "cc7a1d0fce...b65b99@auth.local",
- "_id": "AGO_2SiiTY61uMr-tTVGvQ",
- "_ctime": "2025-09-11T07:38:23.082+00:00",
- "_mtime": "2025-09-11T09:28:32.204+00:00",
- "mpxK": 0
- },
- {
- "bjcM": 12,
- "0000": "John",
- "7Q0G": 462,
- "tc2B": "2025-09-11",
- "Tm99": "520035",
- "_creator": "aa",
- "_last_modifier": "cc7a1d0fce...b65b99@auth.local",
- "_id": "WTu6o6lxS-ChnamkU1wjuA",
- "_ctime": "2025-09-11T07:39:10.297+00:00",
- "_mtime": "2025-09-11T09:28:32.204+00:00",
- "mpxK": 0
- }
- ],
- "subgroups": [] /* (3)! */
- }
- ]
- },
- {
- "column_name": "date",
- "column_key": "tc2B",
- "cell_value": null,
- "rows": [],
- "subgroups": [
- {
- "column_name": "Val2",
- "column_key": "7Q0G",
- "cell_value": 4,
- "rows": [
- {
- "_id": "GIgxrz8VSzm-aHSbJ6_i4w",
- "_participants": [],
- "_creator": "cc7a1d0fce...b65b99@auth.local",
- "_ctime": "2025-09-03T07:03:57.838+00:00",
- "_last_modifier": "cc7a1d0fce...b65b99@auth.local",
- "_mtime": "2025-09-17T15:31:04.150+00:00",
- "bjcM": 1,
- "0000": "name",
- "7Q0G": 4,
- "plxx": 5676,
- "Tm99": "207110",
- "mpxK": ""
- },
- {
- "_id": "PSfpr9dzRPaKUeIn-3va0w",
- "_participants": [],
- "_creator": "cc7a1d0fce...b65b99@auth.local",
- "_ctime": "2025-09-03T07:03:57.838+00:00",
- "_last_modifier": "cc7a1d0fce...b65b99@auth.local",
- "_mtime": "2025-09-11T09:28:32.204+00:00",
- "bjcM": 0,
- "0000": "zu",
- "7Q0G": 4,
- "plxx": 3872,
- "Tm99": "375528",
- "mpxK": 0
- }
- ],
- "subgroups": []
- },
- {
- "column_name": "Val2",
- "column_key": "7Q0G",
- "cell_value": 9,
- "rows": [
- {
- "_id": "H3djeRnkQdWhKBhEG2cGUw",
- "_participants": [],
- "_creator": "cc7a1d0fce...b65b99@auth.local",
- "_ctime": "2025-09-03T07:03:57.838+00:00",
- "_last_modifier": "cc7a1d0fce...b65b99@auth.local",
- "_mtime": "2025-09-11T09:28:32.204+00:00",
- "bjcM": 3,
- "0000": "a",
- "7Q0G": 9,
- "plxx": 1668,
- "Tm99": "520035",
- "mpxK": 0
- },
- {
- "_id": "ARedNyn8R7CZFmRushZmvQ",
- "_participants": [],
- "_creator": "cc7a1d0fce...b65b99@auth.local",
- "_ctime": "2025-09-03T08:23:03.776+00:00",
- "_last_modifier": "cc7a1d0fce...b65b99@auth.local",
- "_mtime": "2025-09-17T15:31:09.842+00:00",
- "0000": "b",
- "bjcM": "",
- "7Q0G": 9,
- "plxx": 610,
- "Tm99": "211464",
- "mpxK": 0
- },
- {
- "_id": "L4IWGz4hT3qb1_u9bBbvFg",
- "_participants": [],
- "_creator": "cc7a1d0fce...b65b99@auth.local",
- "_ctime": "2025-09-03T14:03:51.524+00:00",
- "_last_modifier": "cc7a1d0fce...b65b99@auth.local",
- "_mtime": "2025-09-17T15:31:08.429+00:00",
- "0000": "name",
- "bjcM": 15,
- "7Q0G": 9,
- "plxx": 565,
- "Tm99": "745764",
- "mpxK": 0
- }
- ],
- "subgroups": []
- }
- ]
- }
- ]
- ```
-
- 1. Grouped rows object containing either `rows` or `subgroups` (array of grouped rows objects) in the case of multiple grouping rules
-
- 2. No `rows`: this grouped rows object only contains `subgroups` (member of the first grouping rule)
-
- 3. No `subgroups`: this grouped rows object only contains `rows` (member of the last grouping rule)
-
- ``` js
- const groupViewRows = base.getGroupedRows('Table1', 'GroupedView');
- ```
-
-## Add row
-
-!!! abstract "appendRow / addRow (deprecated)"
-
- Add a row to a `table`. This row contains the data specified in the object `rowData`. The row will be empty if `rowData` is empty or if it contains only keys that don't exist in the `table`.
-
- ``` js
- base.appendRow(table: Object/String, rowData: Object, viewName: String /* (1)! */)
- ```
-
- 1. `table`: either a table object or the table name
-
- `rowData`: object (pairs of `key`:`value`, each `key` being the name of a column), for example:
-
- ```
- {
- 'First Name': 'John',
- 'Last Name': 'Doe',
- 'Invoice amount': 100,
- 'Products': ['Office Supplies', 'Computer']
- }
- ```
-
- __Output__ Single row object (throws an error if `table` doesn't exist)
-
- __Example__
-
- ``` js
- const table = base.getTableByName('Table1');
- base.appendRow(table, {'Name': 'Alex', 'Age': '18'});
- base.appendRow(table, {'Name': 'Alex', 'Age': '18'}, 'Default View');
- ```
-
-## Update row(s)
-
-!!! abstract "updateRow / modifyRow(deprecated)"
-
- Modify a `row` in the `table`. The `updateRowData` object (pairs of `key`:`value`, each `key` being the name of a column) need to contain only the data you want to update. To reset a value, specify the `key`:`value` pair with an empty string `''`.
-
- ``` js
- base.updateRow(table: Object/String, row: Object/String, updateRowData: Object /* (1)! */);
- ```
-
- 1. `table`: either a table object or the table name
-
- `row`: either a row object or the row id
-
- __Output__ Nothing (throws an error if `table` doesn't exist or if no row with the specified id exists)
-
- __Example__
-
- ``` js
- const table = base.getTableByName('Table1');
- const row = base.getRow(table, "M_lSEOYYTeuKTaHCEOL7nw");
- base.updateRow(table, row, {'Name': 'new name', 'number': 100});
- ```
-
- ``` js
- base.updateRow('Table1', 'U_eTV7mDSmSd-K2P535Wzw', {'Name': 'new name', 'number': 100})
- ```
-
-!!! abstract "modifyRows"
-
- Modify multiple `rows` in the `table` at once. `updatedRows` is an array of `updateRowData` objects (see above). Please note that `rows` only accepts an array of row objects (and not of ids).
-
- ``` js
- base.modifyRows(table: Object/String, rows: Array of Object, updatedRows: Array of Object /* (1)! */);
- ```
-
- 1. `table`: either a table object or the table name
-
- `rows`: array of row objects **only** (not row ids)
-
- __Output__ Nothing (throws an error if `table` doesn't exist or if one row in `rows` doesn't exists)
-
- __Example__
-
- ``` js
- const table = base.getTableByName('Table1');
- const rows = base.getRows(table, 'Default View');
- const selectedColumnName = 'Name';
- const selectedRows = [], updatedRows = [];
-
- rows.forEach((row) => {
- if (row[selectedColumnName] === 'name') {
- selectedRows.push(row);
- updatedRows.push({[selectedColumnName]: 'name1'});
- }
- });
- base.modifyRows(table, selectedRows, updatedRows);
- ```
-
- ``` js
- base.modifyRows('Table1', [base.getRow('Table1','GIgxrz8VSzm-aHSbJ6_i4w'),base.getRow('Table1','PSfpr9dzRPaKUeIn-3va0w')], [{'Name': 'name'},{'Name': 'name'}]);
- ```
-
-## Delete row
-
-!!! abstract "deleteRow / deleteRowById (deprecated)"
-
- Delete a row in a `table` by its id `rowId`.
-
- ``` js
- base.deleteRow(table: Object/String, rowId: String /* (1)! */);
- ```
-
- 1. `table`: either a table object or the table name
-
- `rowId`: the id of the row to delete
-
-
- __Output__ Nothing (no error if no row with id `rowId` exists)
-
- __Example__
-
- ``` js
- const table = base.getTableByName('Table1');
- base.deleteRow(table, 'M_lSEOYYTeuKTaHCEOL7nw');
- ```
-
- ``` js
- base.deleteRow('Table1', 'M_lSEOYYTeuKTaHCEOL7nw');
- ```
-
-
-
-## Filter
-
-!!! abstract "filter"
-
- Filters the rows displayed in the view `viewName` of the `table` that meet the conditions of the `filterExpression` (conditional statement), and returns a querySet object. See the `filterExpression` reference below for more details.
-
- ``` js
- base.filter(tableName: String, viewName: String, filterExpression: String);
- ```
-
- __Output__ Single querySet object (see below), the `rows` array being empty if no row meet the `filterExpression` conditions
-
- __Example__
-
- === "Function call"
-
- ``` js
- // Filter out rows whose number column is equal to 5, and return a querySet object
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- ```
-
- === "Output structure"
-
- ```json
- {
- "rows": [ /* (1)! */
- ...
- ],
- "table": { /* (2)! */
- ...
- },
- "parser": {
- ...
- }
- }
- ```
-
- 1. `rows`: array of the rows in the view `viewName` meeting the `filterExpression` conditions
-
- 2. `table`: the whole `table` object
-
-
- ```js
- const querySet = base.filter("Table1", "Default View", "age>18"/* (1)! */)
- ```
-
- 1. `age`: column name
-
- `>`: operator
-
- `18`: parameter
-
-### filterExpression reference
-
-!!! abstract "filterExpression"
-
- The most common operators are available to define the conditional statement of the `filterExpression`:
-
- | Type of operators | Available operators |
- | ------------------ | ------------------- |
- | Greater-Less comparisons | >=, >, <, <= |
- | Equal-Not equal comparisons | =, <> (not equal to) |
- | Arithmetic operators | +, -, *, /, ^ (power), % (modulo) |
- | Logical operators | and, or |
-
- Depending on the data type, there are slight differences in the query method and the format of input statement. Here is a list of the possible operations for each type:
-
-
- | Data structure | Column type | Format for Greater-Less comparisons | Format for Equal-Not equal comparisons | Arithmetic operators |
- | -------------- | ----------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------- | :---------- |
- | String | Text, Long Text, URL, Email, Single Select | Unsupported | String | Unsupported |
- | List | Multiple Select | Unsupported | String | Unsupported |
- | Number | Number | Number | Number and empty String `""`"" | Supported |
- | Date | Date, Created time, Last modified time | Patterns: YYYY-MM-DD, YYYY-MM-DD hh:mm, YYYY-MM-DD hh\:mm:ss | Same patterns as greater-less query | Unsupported |
- | Boolean | Checkbox | Unsupported | true, false and empty String `""`, (case-insensitive) | Unsupported |
-
-
- !!! info "Mind the quotes!"
- For queries involving string-based or date-based columns, you'll have to use double quotes `" "` to define the `filterExpression` as you'll need simple quotes `' '` for the strings/dates... Or the opposite: use either `"column_name='hello world'"` or `'column_name="hello world"'`
-
- Here are more examples of the different filter expressions pending of the column type.
-
- __String-based Column__ (**Text, Long Text, URL, Email, Single Select** columns)
-
-
- ```js
- // Equal-unequal query
- base.filter('Table1', 'Default View', "column_name='hello world'")
- base.filter('Table1', 'Default View', "column_name!=''")
-
- ```
-
-
- __List-based Column__ (**Multiple Select** columns)
-
-
- ```js
- // Equal-unequal query
- base.filter('Table1','Default View', "column_name='A' and column_name='B'") /* (1)! */
- ```
-
- 1. Find the rows which contains both 'A' and 'B'
-
-
- __Number-based Column__ (**Number** columns)
-
- === "Greater-less query"
-
- ```js
- base.filter('Table1', 'Default View', "column_name>18")
- base.filter('Table1', 'Default View', "column_name>-10 and column_name<=0")
- ```
-
- === "Equal-unequal query"
-
- ```js
- base.filter('Table1', 'Default View',"column_name<>20")
- base.filter('Table1', 'Default View', "column_name=0")
- base.filter('Table1', 'Default View',"column_name=''")
- ```
-
- === "Arithmetic query"
-
- ```js
- base.filter('Table1', 'Default View', "column_name+3>18")
- base.filter('Table1', 'Default View', "column_name*2=18")
- base.filter('Table1', 'Default View', "column_name-2=18")
- base.filter('Table1', 'Default View', "column_name/2=18")
- base.filter('Table1', 'Default View', "column_name^2=18")
- base.filter('Table1', 'Default View', "column_name%2=1")
- ```
-
-
- __Date-based Column__ (**Date, Created time, Last modified time** columns)
-
- === "Greater-less query"
-
- ```js
- base.filter('Table1', 'Default View', "column_name>'2020-1-30'")
- base.filter('Table1', 'Default View', "column_name>='2019-1-1 5:30' and column_name<='2019-5-1 6:00'")
- ```
-
- === "Equal-unequal query"
-
- ```js
- base.filter('Table1', 'Default View', "column_name='2020-1-1 10:59:59'")
- base.filter('Table1', 'Default View', "column_name!=''")
- ```
-
-
- __Boolean-based Column__ (**Checkbox** columns)
-
- === "Equal-unequal query"
-
- ```js
- base.filter('Table1', 'Default View','column_name=False')/* (1)! */
- base.filter('Table1', 'Default View', "column_name=True")
- ```
-
- 1. same as `base.filter('Table1', "column_name=''")`
-
-### querySet handling
-
-The output of the `base.filter` function is a `querySet` object. Here are the methods of this object provided to simplify the operations on the filtered data.
-
-!!! abstract "all"
-
- Returns all filtered rows of the `querySet` in the form of a list.
-
- ``` js
- querySet.all();
- ```
-
- __Output__ Array of row objects
-
- __Example__
-
- ``` js
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- const list = querySet.all();
- output.text(list);
- ```
-
-!!! abstract "count"
-
- Returns the number of filtered rows of the `querySet`.
-
- ``` js
- querySet.count();
- ```
-
- __Output__ Number
-
- __Example__
-
- ```js
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- const count = querySet.count();
- output.text(`The querySet contains ${count} rows`);
- ```
-
-!!! abstract "first"
-
- Return the first filtered row of the `querySet`.
-
- ``` js
- querySet.first();
- ```
-
- __Output__ Single row object (`undefined` if the `querySet` contains no row)
-
- __Example__
-
- ```js
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- const row = querySet.first();
- ```
-
-!!! abstract "last"
-
- Return the last filtered row of the `querySet`.
-
- ``` js
- querySet.last();
- ```
-
- __Output__ Single row object (`undefined` if the `querySet` contains no row)
-
- __Example__
-
- ```js
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- const row = querySet.last();
- ```
-
-!!! abstract "delete"
-
- Delete all filtered rows of the `querySet` and return the number of rows successfully deleted.
-
- ``` js
- querySet.delete();
- ```
-
- __Output__ Number
-
- __Example__
-
- ```js
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- const count = querySet.delete();
- output.text(`${count} rows successfully deleted!`);
- ```
-
-!!! abstract "update"
-
- Modify the row data according to the`rowData` Object and return the updated rows.
-
- ``` js
- querySet.update(rowData: Object/* (1)! */);
- ```
-
- 1. `rowData`: object (pairs of `key`:`value`, each `key` being the name of a column)
-
- __Output__ Array of row objects (empty Array if no filtered row)
-
- __Example__
-
- ```js
- // Modify the content of the Name column of all filtered rows to xxxx
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- const rows = querySet.update({Name: 'xxxx'});
- ```
-
-!!! abstract "filter"
-
- Further filtering using the `filterExpression` conditional statement.
-
- ```js
- querySet.filter(filterExpression: String);
- ```
-
- __Output__ Single querySet object
-
- __Example__
-
- ```js
- // Filter out the rows with the value of Tom in the Name column of querySet1
- const querySet1 = base.filter('Table1', 'Default View', 'number = 5');
- const querySet2 = querySet1.filter("Name = 'Tom'");
- ```
-
-!!! abstract "get"
-
- Return the first row of the querySet that meets the conditions of the new `filterExpression`. This is equivalent to `querySet.filter(filterExpression).first()`
-
- ```js
- querySet.get(filterExpression: String);
- ```
-
- __Output__ Single row object (`undefined` if no row meets the conditions of the `filterExpression`, `#ERROR!` if the `filterExpression` is wrong)
-
- __Example__
-
- ```js
- // Get the first data of Tom in the Name column of the querySet
- const querySet = base.filter('Table1', 'Default View', 'number = 5');
- const row = querySet.get("Name = 'Tom'");
- ```
diff --git a/docs/scripts/javascript/objects/tables.md b/docs/scripts/javascript/objects/tables.md
deleted file mode 100644
index af68d7a0..00000000
--- a/docs/scripts/javascript/objects/tables.md
+++ /dev/null
@@ -1,116 +0,0 @@
-# Tables
-
-You'll find below all the available methods to interact with the tables of a SeaTable base.
-
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-You can have a look at the specific [view](./views.md#global-structure), [column](./columns.md#global-structure) or [row](./rows.md#global-structure) structure on the corresponding pages.
-
-## Get Table(s)
-
-!!! abstract "getActiveTable"
-
- Get the currently selected table.
-
- ``` js
- base.getActiveTable();
- ```
- __Output__ Single table object
-
- __Example__
- ``` js
- const table = base.getActiveTable();
- output.text(`The name of the active table is: ${table.name}`);
- ```
-
-!!! abstract "getTables"
-
- Get all tables of the current base.
-
- ```
- base.getTables();
- ```
- __Output__ Array of table objects
-
- __Example__
- ``` js
- const tables = base.getTables();
- output.text(tables);
- ```
-
-!!! abstract "getTableByName"
-
- Get a table object by its name.
-
- ``` js
- base.getTableByName(tableName: String);
- ```
-
- __Output__ Single table object (`undefined` if table doesn't exist)
-
- __Example__
-
- ```js
- const table = base.getTableByName('Table1');
- // Display only table _id
- output.text(`The id of the table is: ${table._id}`);
- // Display whole table structure
- output.text(table);
- ```
-
-## Add Table
-
-!!! abstract "addTable"
-
- Add a new table to this base, given the new table name `tableName`. Please ensure that you choose a `tableName` that doesn't already exist in your base.
-
- ``` js
- base.addTable(tableName: String);
- ```
- __Output__ Nothing
-
- __Example__
- ``` js
- base.addTable('New table');
- output.text("Wow, I just added a new table to this base.")
- ```
-
-## Rename Table
-
-!!! abstract "renameTable"
-
- Rename an existing table named `oldName` to `newName`. Please ensure that you choose a `newName` that doesn't already exist in your base.
-
- ``` js
- base.renameTable(oldName: String, newName: String);
- ```
-
- __Output__ Nothing (throws an error if no table named `oldName` exists)
-
- __Example__
- ``` js
- const old_name = "Table1";
- const new_name = "Projects 2023";
- base.renameTable(old_name, new_name);
- output.text(`This base ${old_name} got a new name: ${new_name}`);
- ```
-
-## Delete Table
-
-!!! abstract "deleteTable"
-
- Delete a table named `tableName` from the base. By the way, the table can be [restored from the logs](https://seatable.com/help/eine-geloeschte-tabelle-wiederherstellen/). Deleting the last table is not possible.
-
- ``` js
- base.deleteTable(tableName: String);
- ```
- __Output__ Nothing (throws an error if no table named `tableName` exists)
-
- __Example__
- ``` js
- base.deleteTable('Old table');
- ```
diff --git a/docs/scripts/javascript/objects/utilities.md b/docs/scripts/javascript/objects/utilities.md
deleted file mode 100644
index ecd6a105..00000000
--- a/docs/scripts/javascript/objects/utilities.md
+++ /dev/null
@@ -1,120 +0,0 @@
-# Utility functions
-
-Utility functions help you to work with data in SeaTable.
-
-## Date and Time
-
-!!! abstract "formatDate"
-
- Format `date` to 'YYYY-MM-DD' to be used in a date-type column.
-
- ``` js
- base.utils.formatDate(date: Date Object)
- ```
-
- __Output__ String
-
- __Example__
- ``` js
- let date = new Date();
- let formatDate = base.utils.formatDate(date);
- output.text(formatDate);
- ```
-
-!!! abstract "formatDateWithMinutes"
-
- Format `date` to 'YYYY-MM-DD HH:mm' to be used in a date-type column.
-
- ``` js
- base.utils.formatDateWithMinutes(date: date object)
- ```
-
- __Output__ String
-
- __Example__
- ``` js
- let date = new Date();
- let formatDate = base.utils.formatDateWithMinutes(date);
- output.text(formatDate);
- ```
-
-## Lookup and Query
-
-!!! abstract "lookupAndCopy"
-
- Similar to the Microsoft Excel VLOOKUP function. Find a matching row in the *source* table for each row of the *target* table, and then copy the data of the specified cell of the matching row to the specified cell of the *target* row. Every arguments are `String`.
-
- ``` js
- base.utils.lookupAndCopy(targetTable, targetColumn, targetColumnToCompare, sourceTableName,
- sourceColumnName, sourceColumnToCompare = null /* (1)! */);
- ```
-
- 1. `targetTable`: the name of the *target* table - i.e. the table you want to copy data **into**
-
- `targetColumn`: the column of `targetTable` you want to copy data into
-
- `targetColumnToCompare`: the column of `targetTable` you want to compare to a column of table `sourceTableName`
-
- `sourceTableName`: the *source* table - i.e. the table you want to copy data **from**
-
- `sourceColumnName`: the column of `sourceTableName` you want to copy data from
-
- `sourceColumnToCompare`: If specified, the column of `sourceTableName` you want to compare with `targetColumnToCompare` to find matching rows. If not specified, the system will look for a column with the name `targetColumn` in the table `sourceTableName`
-
- __Output__ Nothing (throws an error if some tables or columns do not exist)
-
- __Principle example__
-
- Here are two tables, the *source* table containing both names and emails for few Avengers whereas the *target* table only has the user names.
-
- **Source table**
-
- | Name | SourceEmail |
- | --- | --- |
- | Hulk | greenbigboy@stark-industries.movie |
- | Tony | ironman@stark-industries.movie |
-
- **Target table**
-
- | Name | TargetEmail |
- | --- | --- |
- | Hulk | |
- | Tony | |
-
- To copy the email addresses from the *source* table to the *target* table, this function can be used with the following syntax:
-
- ```
- base.utils.lookupAndCopy('Target table', 'TargetEmail', 'Name', 'Source table', 'SourceEmail');
- ```
-
- __Example__
-
- ``` js
- // Match the rows with the same content in the Name column of Table1 and Table2,
- // copy the contents of the Email column of the row in Table1 to the Email column
- // of the corresponding row in Table2
- base.utils.lookupAndCopy('Table2', 'Email', 'Name', 'Table1', 'Email');
-
- // Match the rows with the same content in the Name column in Table1 and the Name1 column
- // in Table2, and copy the contents of the Email column of the row in Table1 to the
- // Email1 column of the corresponding row in Table2
- base.utils.lookupAndCopy('Table2', 'Email1', 'Name1', 'Table1', 'Email', 'Name');
-
- ```
-
-!!! abstract "query"
-
- Filter and summarize the table `tableName` data of the view `viewName` by SQL like `query` statements.
-
- ``` js
- base.utils.query(tableName: String, viewName: String, query: String);
- ```
-
- __Example__
-
- ``` js
- // Filter out the rows where the sum of the three columns 'number', 'number1',
- // and 'number2' is greater than 5 then sum the number and number2 columns in these rows,
- // return {number: 12, number2: 23}
- base.utils.query('Table1', 'Default View', 'select sum(number), sum(number2) where number + number1 + number2 > 5');
- ```
diff --git a/docs/scripts/javascript/objects/views.md b/docs/scripts/javascript/objects/views.md
deleted file mode 100644
index 75bef272..00000000
--- a/docs/scripts/javascript/objects/views.md
+++ /dev/null
@@ -1,143 +0,0 @@
-# Views
-
-You'll find below all the available methods to interact with the views of a SeaTable table.
-
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-## Get View(s)
-
-!!! abstract "getActiveView"
-
- Get the current view of the active table.
-
- ``` js
- base.getActiveView();
- ```
-
- __Output__ Single view object
-
- __Example__
- ``` js
- const view = base.getActiveView();
- output.text(view._id);
- output.text(view);
- ```
-
-!!! abstract "getViewByName"
-
- Get a view of a particular `table`, specified by its name `viewName`.
-
- ``` js
- base.getViewByName(table: Object/String/* (1)! */, viewName: String);
- ```
-
- 1. `table`: either a table object or the table name
-
- __Output__ Single view object (`undefined` if no view called `viewName` exists, throws an error if `table` doesn't exist)
-
- __Example__
- ``` js
- const table = base.getTableByName('Table1');
- const view = base.getViewByName(table, 'Default View');
- output.text(view.name);
- ```
-
- ``` js
- const view = base.getViewByName('Table1', 'Default View');
- output.text(view.name);
- ```
-
-!!! abstract "listViews / getViews (deprecated)"
-
- Get all the views of the `table`.
-
- ``` js
- base.listViews(table: Object/String/* (1)! */);
- ```
-
- 1. `table`: either a table object or the table name
-
- __Output__ Array of view objects (throws an error if `table` doesn't exist)
-
- __Example__
- ``` js
- const table = base.getTableByName('Table1');
- const views = base.listViews(table);
- output.text(views.length);
- ```
-
-## Add View
-
-!!! abstract "addView"
-
- Add a view named `viewName` to a `table`.
-
- ``` js
- base.addView(table: Object/String/* (1)! */, viewName: String);
- ```
-
- 1. `table`: either a table object or the table name
-
- __Output__ Nothing (throws an error if `table` doesn't exist)
-
- __Example__
- ``` js
- const table = base.getTableByName('Table1');
- base.addView(table, 'view 2');
- ```
-
- ``` js
- base.addView('Table1', 'view 2');
- ```
-
-## Rename View
-
-!!! abstract "renameView"
-
- Rename a view in the `table` specified by its current name `currentViewName` and its new name `nextViewName`. Please ensure that you choose a `nextViewName` that doesn't already exists in your `table`.
-
- ``` js
- base.renameView(table: Object/String/* (1)! */, currentViewName: String, nextViewName: String);
- ```
-
- 1. `table`: either a table object or the table name
-
- __Output__ Nothing (throws an error if `table` or `currentViewName` doesn't exist)
-
- __Example__
- ``` js
- const table = base.getTableByName('Table1');
- base.renameView(table, 'Default View', 'view2');
- ```
-
- ``` js
- base.renameView('Table1', 'Default View', 'view2');
- ```
-
-## Delete View
-
-!!! abstract "deleteView"
-
- Delete a view in a particular `table`, specified by its name `viewName`. Deleting the last view is not possible.
-
- ``` js
- base.deleteView(table: Object/String/* (1)! */, viewName: String);
- ```
-
- 1. `table`: either a table object or the table name
-
- __Output__ Nothing (throws an error if `table` doesn't exist or no view called `viewName` exists)
-
- __Example__
- ``` js
- const table = base.getTableByName('Table1');
- base.deleteView(table, 'view2');
- ```
-
- ``` js
- base.deleteView('Table1', 'view2');
- ```
diff --git a/docs/scripts/python/common_questions.md b/docs/scripts/python/common_questions.md
deleted file mode 100644
index 3d9bdbae..00000000
--- a/docs/scripts/python/common_questions.md
+++ /dev/null
@@ -1,139 +0,0 @@
-# Common questions (Python)
-
-??? question "How to make the script support both local and cloud run"
-
- ## How to make the script support both local and cloud run
-
- __Flexible authorization__
-
- When the script runs in the cloud, it will provide a context object, which contains the server URL auto generated by the system and the API token of base. If you run the script in local, you need to manually specify these two variables; the API token can be generated in the drop-down menu "Advanced -> API Token" of the table.
-
- Use the following method to make the script support both local and cloud run
-
- ```
- from seatable_api import Base, context
-
- server_url = context.server_url or 'https://cloud.seatable.io'
- api_token = context.api_token or 'c3c75dca2c369848455a39f4436147639cf02b2d'
-
-
- base = Base(api_token, server_url)
- base.auth()
- ```
-
- __Dependencies that need to be installed to run the script local__
-
- The script need to install `seatable-api` when run in local.
-
- ```
- pip3 install seatable-api
- ```
-
- Additional requirements are:
-
- - Python >= 3.5
- - requests
- - socketIO-client-nexus
-
-??? question "List of libraries supported in the cloud environment"
-
- ## List of libraries supported in the cloud environment
-
- In the cloud environment, Python scripts run within a Docker container. This container comes pre-configured with a set of Python libraries that can be imported and used in your scripts. If you require libraries not included in this set, please contact our support team. Otherwise, scripts using unsupported libraries can only be executed locally.
-
- __Python Standard Library__
-
- The cloud environment currently utilizes **Python 3.12**. This version supports all modules in the Python 3.12 standard library. Common built-in libraries such as `os`, `sys`, `datetime`, and others are readily available for use in your scripts.
-
- __Third-Party Libraries__
-
- In addition to the standard library, we've included several popular third-party packages to enhance your scripting capabilities:
-
- - [seatable-api](https://pypi.org/project/seatable-api/): Official SeaTable Python API
- - [dateutils](https://pypi.org/project/dateutils/): Extensions to Python's datetime module
- - [requests](https://pypi.org/project/requests/): HTTP library for Python
- - [pyOpenSSL](https://pypi.org/project/pyOpenSSL/): Python wrapper for OpenSSL
- - [Pillow](https://pypi.org/project/Pillow/): Python Imaging Library (Fork) with support for [HEIF images](https://pypi.org/project/pillow-heif/)
- - [python-barcode](https://pypi.org/project/python-barcode/): Barcode generator
- - [qrcode](https://pypi.org/project/qrcode/): QR Code generator
- - [pandas](https://pypi.org/project/pandas/): Data manipulation and analysis library
- - [numpy](https://pypi.org/project/numpy/): Fundamental package for scientific computing
- - [openai](https://pypi.org/project/openai/): OpenAI API client library
- - [ldap3](https://pypi.org/project/ldap3/): LDAP v3 client library
- - [pydantic](https://pypi.org/project/pydantic/): Data validation and settings management using Python type annotations
- - [httpx](https://pypi.org/project/httpx/): A next-generation HTTP client for Python
- - [PyJWT](https://pypi.org/project/PyJWT/): JSON Web Token implementation in Python
- - [python-socketio](https://pypi.org/project/python-socketio/): Python implementation of the Socket.IO realtime server
- - [scipy](https://pypi.org/project/scipy/): Fundamental algorithms for scientific computing in Python
- - [PyPDF](https://pypi.org/project/pypdf/): PDF toolkit for Python
- - [pdfmerge](https://pypi.org/project/pdfmerge/): Merge PDF files
- - [Markdown](https://pypi.org/project/Markdown/): Convert Markdown to HTML
- - [RapidFuzz](https://pypi.org/project/RapidFuzz/): A fast string matching library using string similarity calculations
-
- This list is not exhaustive. For a complete, up-to-date list of available third-party packages, you can run the following Python script in your SeaTable environment:
-
- ```python
- import importlib.metadata
-
- # List all installed packages
- installed_packages = importlib.metadata.distributions()
-
- # Print package names
- for package in installed_packages:
- print(package.metadata['Name'])
- ```
-
-??? question "Install and use custom python libraries"
-
- ## Install and use custom python libraries
-
- - The python libraries in SeaTable Cloud can not be changed.
- - If you run your own SeaTable Server it is possible to install your own libraries.
-
-??? question "Printing complex elements (dicts, tables, arrays of rows) is sometimes difficult to read"
-
- ## Printing complex elements is sometimes difficult to read
-
- Do not hesitate to run your code in a Python IDE which could have specific features for data visualization (don't forget you won't be able to rely on context to provide `api_token` and `server_url`, see first question for dual run syntax). You could also use the `json` library to make the output of complex objects easier to read:
-
- ```python
- import json # (1)!
- from seatable_api import Base, context
- base = Base(context.api_token, context.server_url)
- base.auth()
-
- print(json.dumps(base.get_metadata(), indent=' ')) # (2)!
- ```
-
- 1. Import the json library
-
- 2. Print `json.dumps(object, indent=' ')` instead of just printing object. You have to explicitly specify the indent character (which is not a classic space character) as the output window of SeaTable's script editor actually trims indent spaces.
-
-??? question "How to deal with more than 1000 rows at once with batch operations?"
-
- ## Dealing with more than 1000 rows at once with batch operations
-
- As presented in the [API Reference](https://api.seatable.com/reference/limits), batch operations such as `base.batch_append_rows`, `base.batch_update_rows`, `base.batch_delete_rows` or `base.batch_update_links` have a maximum number of 1000 rows. To deal with a higher number of rows, you could:
-
- - Use an `INSERT`, `UPDATE` or `DELETE` [SQL query](/scripts/sql/introduction.md#supported-sql-syntax) that can operate on an unlimited number of rows
-
- - Use a `while` loop to split your operation into 1000-rows chunks for example (however this won't exactly be a single operation anymore):
-
- ```python
- from seatable_api import Base, context
-
- base = Base(context.api_token, context.server_url)
- base.auth()
-
- # You want to batch append new_rows which is more than 1000-rows long
- while len(new_rows)>0 :
- end = min(1000, len(new_rows))
- rows_chunk = new_rows[:end]
- print(f"{rows_chunk[0]['Name']} > {rows_chunk[-1]['Name']}")
- base.batch_append_rows("Table1", rows_chunk)
- new_rows = new_rows[end:len(new_rows)]
- ```
-
- To [batch update links](./objects/links.md#update-links), the loop will be slightly more complex as you'll have to deal with `other_rows_ids_map` as well
-
-
diff --git a/docs/scripts/python/examples/auto-add-rows.md b/docs/scripts/python/examples/auto-add-rows.md
deleted file mode 100644
index b71b1250..00000000
--- a/docs/scripts/python/examples/auto-add-rows.md
+++ /dev/null
@@ -1,62 +0,0 @@
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-Unlike JavaScript, Python scripts allow you to handle single- or multiple-select options, which make you capable of checking if the needed options exist and of creating them if necessary directly inside the script.
-
-```python
-from seatable_api import Base, context
-from seatable_api.date_utils import dateutils
-"""
-This script add two expenses rows in a ledger. Before adding them,
-it checks if they have already been added for the current month.
-"""
-
-base = Base(context.api_token, context.server_url)
-base.auth()
-
-# Get date objects on the 10th and 20th of the current month
-date = dateutils.today()
-date10 = dateutils.date(dateutils.year(date), dateutils.month(date), 10)
-date20 = dateutils.date(dateutils.year(date), dateutils.month(date), 20)
-
-# Check if the options you will need already exist, and create them if necessary
-options_to_add = []
-current_options = base.get_column_by_name('Daily expenses', 'Type (single select)')['data']['options']
-cloud_service_option = [o for o in current_options if o['name'] == 'Cloud service']
-if not cloud_service_option :
- options_to_add.append({"name": "Cloud service", "color": "#aaa", "textColor": "#000000"})
-daily_office_option = [o for o in current_options if o['name'] == 'Daily office']
-if not daily_office_option :
- options_to_add.append({"name": "daily office", "color": "#aaa", "textColor": "#000000"})
-if options_to_add :
- base.add_column_options('Daily expenses', 'Type (single select)', options_to_add)
-
-# Check if the monthly expense items have already been created and eventually create them
-feeAWS = {}
-feeAWSCurrentMonth = base.query('select * from `Daily expenses` where Name="Amazon Cloud Service" and Date="' + date10 + '"')
-if not feeAWSCurrentMonth :
- feeAWS = {'Name': 'Amazon Cloud Service',
- 'Date': date10,
- 'Type': 'Cloud service',
- 'Type (single select)': 'Cloud service',
- }
-
-feeClean = {}
-feeCleanCurrentMonth = base.query('select * from `Daily expenses` where Name="Clean" and Date ="' + date20 + '"')
-if not feeCleanCurrentMonth :
- feeClean = {'Name': 'Clean',
- 'Date': date20,
- 'Type': 'Daily office',
- 'Type (single select)': 'Daily office',
- 'Fee': 260
- }
-
-# Create the monthly expense items (if needed)
-if (feeAWS) :
- base.append_row('Daily expenses', feeAWS);
-if (feeClean) :
- base.append_row('Daily expenses', feeClean);
-```
\ No newline at end of file
diff --git a/docs/scripts/python/examples/calculate-accumulated-value.md b/docs/scripts/python/examples/calculate-accumulated-value.md
deleted file mode 100644
index 2ac20a0e..00000000
--- a/docs/scripts/python/examples/calculate-accumulated-value.md
+++ /dev/null
@@ -1,65 +0,0 @@
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-```python
-from seatable_api import Base, context
-from seatable_api.date_utils import dateutils
-"""
-This script accumulates the values of the current row and the previous rows,
-and records the result to the current row (as the *Calculate accumulated value*
-operation from the data processing menu).
-"""
-
-base = Base(context.api_token, context.server_url)
-base.auth()
-
-table_name = 'Accumulated value'
-view_name = 'Default View'
-
-# Name of the column that records total number at a specific time
-value_column_name = 'Value to add'
-# Name of the column that need to calculate incremental value
-incremental_column_name = 'Incremental total'
-
-view = base.get_view_by_name(table_name, view_name)
-rows = base.list_rows(table_name, view_name)
-
-# If current view is a grouped view
-if 'groupbys' in view and len(view['groupbys']) > 0 :
-# # Get group view rows
- grouping_column = [c for c in base.list_columns(table_name) if 'column_key' in view['groupbys'][0] and c['key'] == view['groupbys'][0]['column_key']]
- if grouping_column and len(grouping_column) == 1 :
- grouping_column_name = grouping_column[0]['name']
- group_values = []
- for row in rows :
- if row[grouping_column_name] not in group_values :
- group_values.append(row[grouping_column_name])
- for value in group_values :
- group_rows = [r for r in rows if r[grouping_column_name] == value]
- incremental_total = 0
- for row_index, row in enumerate(group_rows) :
- current_number = row[value_column_name];
- if current_number :
- # Calculate increment
- # If there is no previous row, set increase_count to 0
- previous_number = 0 if row_index == 0 else incremental_total
- increase_count = current_number + previous_number
- incremental_total = increase_count
- # Set calculated increment to row
- base.update_row(table_name, row['_id'], {incremental_column_name: increase_count})
-else :
- incremental_total = 0
- for row_index, row in enumerate(rows) :
- current_number = row[value_column_name];
- if current_number :
- # Calculate increment
- # If there is no previous row, set increase_count to 0
- previous_number = 0 if row_index == 0 else incremental_total
- increase_count = current_number + previous_number
- incremental_total = increase_count
- # Set calculated increment to row
- base.update_row(table_name, row['_id'], {incremental_column_name: increase_count})
-```
\ No newline at end of file
diff --git a/docs/scripts/python/examples/compute-attendance-statistics.md b/docs/scripts/python/examples/compute-attendance-statistics.md
deleted file mode 100644
index c3d20073..00000000
--- a/docs/scripts/python/examples/compute-attendance-statistics.md
+++ /dev/null
@@ -1,83 +0,0 @@
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-```python
-from seatable_api import Base, context
-"""
-This script computes, from a list of clocking times,
-daily clock in (earliest clocking) and clock out
-(latest clocking) times for each day and staff member.
-"""
-
-base = Base(context.api_token, context.server_url)
-base.auth()
-
-origin_table_name = 'Clocking table'
-origin_view_name = 'Default View'
-origin_name_column_name = 'Name'
-origin_department_column_name = 'Department'
-origin_date_column_name = 'Date'
-origin_time_column_name = 'Clocking time'
-
-target_table_name = 'Attendance statistics'
-target_name_column_name = 'Name'
-target_department_column_name = 'Department'
-target_date_column_name = 'Date'
-target_start_time_column_name = 'Clock-in'
-target_end_time_column_name = 'Clock-out'
-
-def get_date(e):
- return e[origin_date_column_name]
-
-#table = base.getTableByName(origin_table_name)
-#view = base.getViewByName(table, origin_view_name)
-rows = base.list_rows(origin_table_name, origin_view_name)
-
-# Sort the rows in the Clocking table according to the date column
-rows.sort(key=get_date)
-
-# Group all rows via date and save them to groupedRows, the format
-# of the object is {'2020-09-01': [row, ...], '2020-09-02': [row, ...]}
-grouped_rows = {}
-date_stat_items = []
-for row in rows :
- date = row[origin_date_column_name]
- if date not in grouped_rows :
- grouped_rows[date] = []
- grouped_rows[date].append(row)
-
-# Traverse all the groups in grouped_rows
-for date_key in grouped_rows :
- # Get all clocking data of all members for the current date
- date_rows = grouped_rows[date_key]
- staff_date_stat_item = {}
- # Traverse these rows and group by the name of the employee, get the clock-in and clock-out time of each employee that day, and save it to staffDateStatItem
- # the format is { EmployeeName: {Name: 'EmployeeName', Date: '2020-09-01', Clock-in: '08:00', Clock-out: '18:00'},... }
- for row in date_rows :
- name = row[origin_name_column_name]
- if name not in staff_date_stat_item :
- # Generate a new row based on the original row data, and add Clock-in and Clock-out columns in the newly generated row
- staff_date_stat_item[name] = {
- target_name_column_name: name,
- target_date_column_name: row[origin_date_column_name],
- target_department_column_name: row[origin_department_column_name],
- target_end_time_column_name: row[origin_time_column_name],
- target_start_time_column_name: row[origin_time_column_name]
- }
- else :
- # When another record (same employee and same date) is found, compare the time, choose the latest one as the Clock-out time, and the earliest one as the Clock-in time
- time = row[origin_time_column_name]
- staff_item = staff_date_stat_item[name]
- if staff_item[target_start_time_column_name] > time :
- staff_item[target_start_time_column_name] = time
- elif staff_item[target_end_time_column_name] < time :
- staff_item[target_end_time_column_name] = time
- for staff in staff_date_stat_item :
- date_stat_items.append(staff_date_stat_item[staff])
-
-# Write the attendance data of all employees on the current date into the table
-base.batch_append_rows(target_table_name, date_stat_items)
-```
\ No newline at end of file
diff --git a/docs/scripts/python/examples/generate_barcode.md b/docs/scripts/python/examples/generate_barcode.md
deleted file mode 100644
index f95e988a..00000000
--- a/docs/scripts/python/examples/generate_barcode.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Generate Barcode
-
-This Python script demonstrates the process of converting text slices into barcode images using the `barcode` module and storing them in an image column within SeaTable. It offers an automated way to generate barcode images from text data in a SeaTable table, enhancing data visualization and association within the SeaTable platform.
-
-Here is the structure of the table named `Generate 1 or 2D barcodes` you need so that this script could run (variables are present at the beginning of the script to easily adapt the names):
-
-| Column name | Message | Barcode image |
-| ----------- |:------:|:------:|
-| **Column type** | text | image |
-
-This table can be shared with the [Generate QR code example](./generate_qrcode.md) by adding it an extra *QRcode image* image-type column.
-
-## Process Overview
-
-1. **Iterates through rows** in a SeaTable table whose name is specified in the `TABLE_NAME` variable and check if a barcode already exists for each row (operates only on rows without barcode). Includes exception handling to manage errors encountered during the barcode image generation process.
-2. **Converts text data** from a designated column (`TEXT_COL`) into barcode images using the specified barcode type (`BARCODE_TYPE`).
-3. **Saves the generated barcode images** temporarily.
-4. **Uploads the generated barcode images** to SeaTable and associates them with corresponding records (in the `BARCODE_IMAGE_COL` column).
-5. **Removes temporary barcode image files** after successful upload.
-
-## Code
-
-```python
-import os
-import time
-import barcode
-from barcode.writer import ImageWriter
-from seatable_api import Base, context
-"""
-The python script shows how to transfer a slice of text into a barcode image and save it into
-the image column
-"""
-
-api_token = context.api_token or "859ad340d9a2b...8992e14853af5"
-server_url = context.server_url or "https://cloud.seatable.io"
-
-TABLE_NAME = 'Generate 1 or 2D barcodes'
-TEXT_COL = "Message" # column which is expected to be transferred into barcode
-BARCODE_IMAGE_COL = "Barcode image"
-BARCODE_TYPE = 'code128'
-
-CUSTOM_OPTIONS = {
- "module_width": 0.2, # width of single stripe of barcode, mm
- "module_height": 30.0, # height of barcode, mm
- "quiet_zone": 6.5, # padding size of first and last stripe to the image, mm
- "font_size": 10, # font size of the text below the barcode, pt
- "text_distance": 5.0, # distance between the text and the barcode, mm
-}
-
-
-CODE = barcode.get_barcode_class(BARCODE_TYPE)
-base = Base(api_token, server_url)
-base.auth()
-
-def get_time_stamp():
- return str(int(time.time()*100000))
-
-updated_rows = 0
-# 1. Iterate through rows
-for row in base.list_rows(TABLE_NAME):
- # 1.b Continue if the image is already shown up here
- if row.get(BARCODE_IMAGE_COL):
- continue
- # 1.c Error handling
- try:
- row_id = row.get('_id')
- msg = str(row.get(TEXT_COL))
-
- # 2. Create a barcode object
- code_img = CODE(msg, writer=ImageWriter())
-
- # 3. Temporarily save the image
- save_name = "%s_%s" % (row_id, get_time_stamp())
- file_name = code_img.save("/tmp/%s" % save_name, options=CUSTOM_OPTIONS)
-
- # 4. Upload the barcode image to the base and associate it to the row
- info_dict = base.upload_local_file(file_name, name=None, file_type='image', replace=True)
- img_url = info_dict.get('url')
- base.update_row(TABLE_NAME, row_id, {BARCODE_IMAGE_COL: [img_url]})
-
- # 5. Remove the image file which was saved temporarily
- os.remove(file_name)
- updated_rows += 1
- except Exception as error:
- print("error occurred during barcode generate", error)
- continue
-
-# Summary
-print("I created %s barcodes" % updated_rows)
-```
diff --git a/docs/scripts/python/examples/generate_qrcode.md b/docs/scripts/python/examples/generate_qrcode.md
deleted file mode 100644
index c3dddafb..00000000
--- a/docs/scripts/python/examples/generate_qrcode.md
+++ /dev/null
@@ -1,97 +0,0 @@
-# Generate QR code
-
-This Python script is designed to generate QR codes and associate them with corresponding records in a SeaTable base. In addition to `seatable_api` library, it uses the `qrcode` module to accomplish this task. In comparison to the [Generate barcode example](./generate_barcode.md), this example adds an `OVERWRITE` parameter to choose if existing QRcodes should be recreated or not.
-
-Here is the structure of the table named `Generate 1 or 2D barcodes` you need so that this script could run (variables are present at the beginning of the script to easily adapt the names):
-
-| Column name | Message | QRcode image |
-| ----------- |:------:|:------:|
-| **Column type** | text | image |
-
-This table can be shared with the [Generate barcode example](./generate_barcode.md) by adding it an extra *Barcode image* image-type column.
-
-## Process Overview
-
-1. **Iterates through rows** in a SeaTable table whose name is specified in the `TABLE_NAME` variable and check if a QRcode already exists for each row (operates either on all rows or only on rows without QRcodes depending on the `OVERWRITE` parameter). Includes exception handling to manage errors encountered during the barcode image generation process.
-2. **Generates QR codes** based on the text content in the designated column (`TEXT_COL`).
-3. **Saves the QR code images** temporarily.
-4. **Uploads the generated images** to SeaTable and associates them with corresponding records (in the `QRCODE_IMAGE_COL` column).
-5. **Removes temporary image files** after successful upload.
-
-## Code
-
-```python
-import os
-import time
-import qrcode
-from seatable_api import Base, context
-"""
-The python script shows how to transfer a slice of text into a QR code image and save it into
-the image column
-"""
-
-api_token = context.api_token or "859ad340d9a2b...8992e14853af5"
-server_url = context.server_url or "https://cloud.seatable.io"
-
-TABLE_NAME = "Generate 1 or 2D barcodes"
-TEXT_COL = "Message" # text column which is expected to be transferred into QR code
-QRCODE_IMAGE_COL = "QR code image column"
-
-OVERWRITE = True # set to True to overwrite existing barcode images
-
-base = Base(api_token, server_url)
-base.auth()
-
-qr = qrcode.QRCode(
- version=2,
- error_correction=qrcode.constants.ERROR_CORRECT_L,
- box_size=40,
- border=8
-)
-
-def get_time_stamp():
- return str(int(time.time() * 100000))
-
-def main():
- # 1. Iterate through rows
- for row in base.list_rows(TABLE_NAME):
- # 1.b Continue if the image is already shown up here
- # and OVERWRITE parameter is not True
- if not OVERWRITE and row.get(QRCODE_IMAGE_COL):
- print("Skipping row. Image already exists.")
- continue
- # 1.c Error handling
- try:
- row_id = row.get('_id')
- message = row.get(TEXT_COL)
-
- # Check if message isn't empty before processing
- if not message:
- print("Skipping row. Empty message.")
- continue
-
- # 2. Clear, add data and make a QRCode object
- qr.clear()
- qr.add_data(str(message))
- qr.make()
-
- img = qr.make_image(fill_color="black", back_color="white")
-
- # 3. Temporarily save the image
- save_name = f"{row_id}_{get_time_stamp()}"
- img.save(f"/tmp/{save_name}.png")
-
- # 4. Upload the QR code image to the base and associate it to the row
- info_dict = base.upload_local_file(f"/tmp/{save_name}.png", name=None, file_type='image', replace=True)
- img_url = info_dict.get('url')
- base.update_row(TABLE_NAME, row_id, {QRCODE_IMAGE_COL: [img_url]})
-
- # 4. Remove the image file which was saved temporarily
- os.remove(f"/tmp/{save_name}.png")
- except Exception as exception:
- print("Error occurred during Image generation:", exception)
- continue
-
-if __name__ == "__main__":
- main()
-```
diff --git a/docs/scripts/python/examples/heic_to_png.md b/docs/scripts/python/examples/heic_to_png.md
deleted file mode 100644
index dd7dad1d..00000000
--- a/docs/scripts/python/examples/heic_to_png.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# Convert HEIC to PNG
-
-!!! warning "Requires Python Runner v4.1.1"
-
- The library `pillow_heif` was added with the Python Runner version 4.1.1. If you're using SeaTable Cloud, this was added with v5.1.
-
-This Python script demonstrates how to convert HEIC image files to PNG format and save the converted file into a new row in a SeaTable base. It uses the `pillow_heif` library to handle HEIC files, `Pillow` for image processing, and the `seatable_api` library to interact with SeaTable. The script processes **one HEIC file per row**; if you need to handle multiple HEIC files per row, you'll need to modify the script accordingly.
-
-Here is the structure of the table named `Convert images` you need so that this script could run (variables are present at the beginning of the script to easily adapt the names):
-
-| Column name | HEIC | PNG |
-| ----------- |:------:|:------:|
-| **Column type** | image | image |
-
-## Script Overview
-
-The script performs the following steps:
-
-1. **Authenticate with SeaTable:** Uses the API token and server URL to authenticate.
-2. **Download HEIC Files:** Retrieves HEIC files from the `HEIC` column in SeaTable.
-3. **Convert HEIC to PNG:** Transforms the downloaded HEIC file to PNG format with 90% quality using `Pillow`.
-4. **Upload Converted PNG:** (a) Uploads the PNG file back to SeaTable and (b) updates the row with the new file URL in the `PNG` column.
-
-## Example Script
-
-```python
-import requests
-from PIL import Image
-from pillow_heif import register_heif_opener
-from seatable_api import Base, context
-"""
-This Python script demonstrates how to convert HEIC image files
- to PNG format and save the converted file into a new row in a SeaTable base.
-"""
-
-# Activate heif/heic support
-register_heif_opener() # (1)!
-
-TABLE_NAME = "Convert images"
-FILE_COLUMN = "HEIC"
-RESULT_COLUMN = "PNG"
-
-# 1. Authentication
-base = Base(context.api_token, context.server_url)
-base.auth()
-
-for row in base.list_rows(TABLE_NAME):
- if row.get(FILE_COLUMN) is None:
- continue
-
- # 2. Download heic image
- url = row.get(FILE_COLUMN)[0]
- filename_heic = url.split('/')[-1]
- base.download_file(url, filename_heic)
-
- # 3. Transform image to png
- im = Image.open(filename_heic)
- filename_png = f'image-{row["_id"]}.png'
- im.save(filename_png, quality=90)
- print('Saved image')
-
- # 4.a) Upload
- info_dict = base.upload_local_file(filename_png, name=None, file_type='image', replace=True)
- print('Uploaded file')
-
- # 4.b) Save back to SeaTable Base
- img_url = info_dict.get('url')
- base.update_row(TABLE_NAME, row['_id'], {RESULT_COLUMN: [img_url]})
- print('Stored image info in base')
-```
-
-1. Note the `register_heif_opener()` call to enable HEIC file support.
-
diff --git a/docs/scripts/python/examples/index.md b/docs/scripts/python/examples/index.md
deleted file mode 100644
index 0eabe34b..00000000
--- a/docs/scripts/python/examples/index.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Examples
-
-This section contains some examples of Python Scripts. The **first three scripts** are the same as in the JavaScript section.
-
-Even if Python scripts are capable of checking if the base structure (tables and columns) needed exist and of creating it if necessary, we didn't implement this feature in the scripts so you can focus on the actual goal of each script.
-
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-# Add rows
-
-This script demonstrates how to add rows to record monthly repetitive expenses in a ledger.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/auto-add-rows/)
-
-## Calculate accumulated value
-
-This script computes an accumulated value (adds the value of the current row and the previous rows), similar to the *Calculate accumulated value* operation from the data processing menu.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/calculate-accumulated-value/)
-
-## Statistics
-
-This script computes, from a list of clocking times, daily clock in (earliest clocking) and clock out (latest clocking) times for each day and staff member.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/compute-attendance-statistics/)
-
-## Email sender
-
-This Python script demonstrates sending emails via SMTP using the smtplib module, constructing MIME objects to compose rich content emails within SeaTable and creating HTML content from a "long text"-type column using the markdown module.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/send_email/)
-
-## Barcode generator
-
-This Python script demonstrates the process of converting text slices into barcode images and storing them in an image column within SeaTable.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/generate_barcode/)
-
-## QR code generator
-
-This Python script is designed to generate QR codes and associate them with corresponding records in a SeaTable base. It uses the seatable_api library and qrcode library to accomplish this task.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/generate_qrcode/)
-
-## MySQL synchronization
-
-This Python script facilitates the synchronization of data from a MySQL database to a SeaTable table.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/sync_mysql/)
-
-## Watch stock price
-
-Integrating data from the Twelve Data API with SeaTable facilitates the updating and maintenance of current stock prices within a designated table in the SeaTable environment.
-
-[read more :material-arrow-right-thin:](/scripts/python/examples/update_stock_price/)
-
-## Merge PDF
-
-Merge PDF files and save the merged file into a new row in a SeaTable base.
-
-[read more: :material-arrow-right-thin:](/scripts/python/examples/merge_pdf/)
-
-## Convert HEIC to PNG
-
-Convert HEIC image files to PNG format and save the converted file into a new row in a SeaTable base.
-
-[read more: :material-arrow-right-thin:](/scripts/python/examples/heic_to_png/)
diff --git a/docs/scripts/python/examples/merge_pdf.md b/docs/scripts/python/examples/merge_pdf.md
deleted file mode 100644
index 2d12312a..00000000
--- a/docs/scripts/python/examples/merge_pdf.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# Merge PDF
-
-!!! warning "Requires Python Runner v4.1.1"
-
- The library `pdfmerge` was added with the Python Runner version 4.1.1. If you're using SeaTable Cloud, this was added with v5.1.
-
-This Python script demonstrates how to merge several PDF files and save the merged file into a new column in a SeaTable base. It utilizes the `pdfmerge` library to handle the PDF merging process and the `seatable_api` library to interact with SeaTable.
-
-Here is the structure of the table named `Merge PDF` you need so that this script could run (variables are present at the beginning of the script to easily adapt the names):
-
-| Column name | PDF files | Merged file |
-| ----------- |:------:|:------:|
-| **Column type** | file | file |
-
-## Script Overview
-
-The script performs the following steps:
-
-1. **Authenticate with SeaTable:** Uses the API token and server URL to authenticate.
-2. **Retrieve the files:** For each row, the script gets the name and URL of every file in the `PDF files` column.
-3. **Download PDF Files**
-4. **Merge PDFs:** Combines the downloaded PDF files using `pdfmerge` into a single PDF named with the pattern `output-{row_id}.pdf`.
-5. **Upload Merged PDF:** Uploads the merged PDF back to SeaTable and updates the row with the new file in the `Merged file` column.
-
-## Example Script
-
-```python
-import os
-import requests
-import sys
-import shutil
-from pdfmerge import pdfmerge
-from seatable_api import Base, context
-"""
-This Python script demonstrates how to merge PDF
-files and save the merged file into a new column.
-"""
-
-TABLE_NAME = "Merge PDF"
-FILE_COLUMN = "PDF files"
-RESULT_COLUMN = "Merged file"
-
-# 1. Authentication
-base = Base(context.api_token, context.server_url)
-base.auth()
-
-# Get rows
-for row in base.list_rows(TABLE_NAME):
- if row.get(FILE_COLUMN) is None:
- continue
-
- # 2. Retrieve all files from the row
- files = [{'name': file['name'], 'URL': file['url']} for file in row[FILE_COLUMN]]
- file_names = []
-
- # 3. Download PDFs
- for f in files :
- base.download_file(f['URL'],f['name'])
- file_names.append(f['name'])
- assert len(file_names) == len(files)
- print(f"Downloaded {len(files)} files")
-
- # 4. Merge
- output_filename = f'output-{row["_id"]}.pdf'
- pdfmerge(file_names, output_filename)
- print('Merged PDF files')
-
- # 5. Upload file + store URL in the base
- info_dict = base.upload_local_file(output_filename, name=None, file_type='file', replace=True)
- print(info_dict)
- base.update_row(TABLE_NAME, row['_id'], {RESULT_COLUMN: [info_dict]})
- print('Uploaded PDF file')
-```
diff --git a/docs/scripts/python/examples/send_email.md b/docs/scripts/python/examples/send_email.md
deleted file mode 100644
index d30af524..00000000
--- a/docs/scripts/python/examples/send_email.md
+++ /dev/null
@@ -1,194 +0,0 @@
-# Send emails
-
-This Python script demonstrates sending emails via SMTP using the [smtplib module](https://docs.python.org/3/library/smtplib.html), constructing MIME objects to compose rich content emails within SeaTable and creating HTML content from a "long text"-type column using the markdown module. It also retrieves configuration parameters from the database. This example uses two tables:
-
-- The `Contacts` table storing the contacts you want to send email to:
-
-| Column name | Name | Email |
-| ----------- |:------:|:------:|
-| **Column type** | text | email |
-
-- The `Send email config` table storing the email sending parameters:
-
-| Column name | Subject | Recipient email | Subject source| Email format | Attach file | File |
-| ----------- |:-----:|:-------------:|:-----------:|:----------:|:---------:|:--:|
-| **Column type** | text | single select | single select | single select | checkbox | file |
-
-- Recipient email can be `hard-coded` (recipients are defined l.48 of the script as a list of email addresses) or `database` (recipients are retrieved from the `Email` column of the `Contacts` table).
-- `Subject source` can be `hard-coded` (define manually the subject of the mail l.57 of the script) or `database` (the subject is retrieved from the `Subject` column of the `Send email config` table).
-- Email format can be `text` (plain text defined l.71), `html` (HTML-formatted message, defined l.77) or `database` (the email body retrieved from the `Email body` column of the `Send email config` table).
-- If `Attach file` is checked, the first file from the `File` column will be enclosed (don't forget to adapt the `_subtype` l.115 of the script if your file is not a pdf).
-- You can eventually add a `Send email` column, configured to launch the script. The script itself is written to use either the `context.current_row` data, or the first row of the table if no `context` is defined (script launched from outside SeaTable).
-
-## Process overview
-
-1. **Retrieves email configuration** from the `Send email config` table.
-2. Eventually **retrieves recipient email addresses** from a designated SeaTable table column (`Email` column in `Contact` table).
-3. Eventually **retrieves email subject**.
-3. Eventually **retrieves email body**.
-4. **Composes an email** using plain text or HTML content to create a rich-text message body.
-5. **Attaches a file** from SeaTable to the email by fetching its download link using the SeaTable API and attaching it to the email.
-6. **Sends the email** after authenticating using SMTP parameters.
-
-## Code
-
-```python linenums="1"
-import markdown
-import smtplib, ssl
-from email.mime.application import MIMEApplication
-from email.mime.multipart import MIMEMultipart
-from email.mime.text import MIMEText
-from email.header import Header
-from urllib import parse
-import requests
-from seatable_api import Base, context
-"""
-This Python script demonstrates sending emails via SMTP
-using the smtplib module and constructing MIME objects
-to compose rich content emails within SeaTable.
-"""
-
-# SeaTable API authentication
-base = Base(context.api_token, context.server_url)
-base.auth()
-
-CONFIG_TABLE = 'Send email config'
-CONTACTS_TABLE = 'Contacts'
-
-# SMTP server configurations for sending emails
-SMTP_SERVER = 'my.smtpserver.com'
-SMTP_PORT = 465
-USERNAME = 'my.em@il.com'
-PASSWORD = 'topsecret'
-SENDER = 'My name'
-
-# 1. Get email configuration from the 'Send email config' table
-current_row = context.current_row or base.list_rows(CONFIG_TABLE)[0]
-# Choose RECIPIENT_EMAIL between "hard-coded" (addresses l.48 of this script)
-# or "database" (get emails from 'Email' column in the 'Contacts' table)
-RECIPIENT_EMAIL = current_row.get('Recipient email')
-# Choose SUBJECT between "hard-coded" (subject l.57 of this script)
-# or "database" (get subject from 'Subject' column in the 'Send email config' table)
-SUBJECT_SOURCE = current_row.get('Subject source')
-# Choose EMAIL_FORMAT between "text" (hard-coded plain text, defined l.71),
-# "html" (hard-coded HTML, defined l.77)
-# and "database" (content of the 'Email body' column in the 'Send email config' table)
-EMAIL_FORMAT = current_row.get('Email format')
-# If Attach file, the script retrieves the first file from the 'File' column of the 'Sending email config'
-ATTACH_FILE = current_row.get('Attach file')
-
-# 2. Set recipient email addresses
-if RECIPIENT_EMAIL == "hard-coded" :
- # Option a) Define the recipient email address in this script
- receivers = ['johndoe@email.com']
-elif RECIPIENT_EMAIL == "database" :
- # Option b) Retrieve recipient email addresses from the 'Contacts' table in SeaTable
- receiver_rows = base.list_rows(CONTACTS_TABLE)
- receivers = [row['Email'] for row in receiver_rows if row.get('Email')]
-
-# 3. Set email subject
-if SUBJECT_SOURCE == "hard-coded" :
- # Option a) Define the subject in this script
- subject = 'SeaTable Send email'
-elif SUBJECT_SOURCE == "database" :
- # Option b) Retrieve the subject from the 'Send email config' table
- current_row = context.current_row or base.list_rows(CONFIG_TABLE)[0]
- subject = current_row.get('Subject')
-
-# 4. Construct the email message
-msg = MIMEMultipart()
-msg['Subject'] = subject
-msg['From'] = SENDER + '<' + USERNAME + '>'
-msg['To'] = ", ".join(receivers)
-
-if EMAIL_FORMAT == "text" :
- # Option a) plain text message
- text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.seatable.com"
- text_plain = MIMEText(text,'plain', 'utf-8')
- msg.attach(text_plain)
-
-elif EMAIL_FORMAT == "html" :
- # Option b) HTML content for the email body
- html = """
-
-
-
- Hi!
- This is a sample message from SeaTable
-
-
-
- """
- text_html = MIMEText(html, 'html', 'utf-8')
- msg.attach(text_html)
-
-elif EMAIL_FORMAT == "database" :
- # Option c) HTML content for the email body from the Email body column
- current_row = context.current_row or base.list_rows(CONFIG_TABLE)[0]
- text_html = MIMEText(markdown.markdown(current_row['Email body']),'html', 'utf-8')
- msg.attach(text_html)
-
-# 5. Attach a file from SeaTable to the email
-if ATTACH_FILE :
- # Get the file from the 'send email config' table
- current_row = context.current_row or base.list_rows(CONFIG_TABLE)[0]
- file_name = current_row['File'][0]['name']
- file_url = current_row['File'][0]['url']
- path = file_url[file_url.find('/files/'):]
- download_link = base.get_file_download_link(parse.unquote(path))
-
- try:
- response = requests.get(download_link)
- if response.status_code != 200:
- print('Failed to download file, status code: ', response.status_code)
- exit(1)
- except Exception as e:
- print(e)
- exit(1)
-
- # Attach the file to the email (adapt _subtype to the type of your file)
- attached_file = MIMEApplication(response.content, _subtype = "pdf")
- attached_file.add_header('content-disposition', 'attachment', filename = file_name)
- msg.attach(attached_file)
-
-# 6. Send the email
-
-# option a) Sending the email using SMTP
-try:
- with smtplib.SMTP() as email_server:
- email_server.connect(SMTP_SERVER)
- email_server.login(USERNAME, PASSWORD)
- email_server.send_message(msg)
- email_server.quit()
-except smtplib.SMTPAuthenticationError:
- print("SMTP User authentication error, Email not sent!")
-except Exception as e:
- print(f"SMTP exception {e}")
-
-'''
-# option b) Sending the email using SMTP / SSL
-ssl_context = ssl.create_default_context()
-try:
- with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT,
- context=ssl_context) as email_server:
- email_server.login(USERNAME, PASSWORD)
- email_server.send_message(msg)
- email_server.quit()
-except smtplib.SMTPAuthenticationError:
- print("SMTP User authentication error, Email not sent!")
-except Exception as e:
- print(f"SMTP exception {e}")
-
-# option c) Sending the email using SMTP with STARTTLS
-try:
- with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as email_server:
- email_server.starttls()
- email_server.login(USERNAME, PASSWORD)
- email_server.send_message(msg)
- email_server.quit()
-except smtplib.SMTPAuthenticationError:
- print("SMTP User authentication error, Email not sent!")
-except Exception as e:
- print(f"SMTP exception {e}")
-'''
-```
diff --git a/docs/scripts/python/examples/sync_mysql.md b/docs/scripts/python/examples/sync_mysql.md
deleted file mode 100644
index e394c1c6..00000000
--- a/docs/scripts/python/examples/sync_mysql.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# SeaTable MySQL Synchronization
-
-This Python script facilitates the synchronization of data from a MySQL database to a SeaTable table, ensuring consistency and updating records seamlessly. Variables are present at the beginning of the script to easily adapt the names of both SeaTable and MySQL tables and columns. The `Sync MySQL` table requires a single `Name` text-type column for the script to be able to run.
-
-## Process Overview
-
-1. **Initializes connections** to both (a) SeaTable and (b) MySQL databases.
-2. **Fetches existing data** from the `Name` column of the `Sync MySQL` SeaTable table.
-3. **Retrieves data** from the MySQL `order` table.
-4. **Compares MySQL data with SeaTable data** to identify new records by matching the `name` field.
-5. **Adds new records** from MySQL to SeaTable (`Sync MySQL`) for synchronization.
-
-
-## Code
-
-```python
-import pymysql
-from seatable_api import Base, context
-"""
-This Python script facilitates the synchronization of data
-from a MySQL database to a SeaTable table, ensuring consistency
-and updating records seamlessly.
-"""
-
-# SeaTable base config
-SERVER_URL = context.server_url or 'http://127.0.0.1:8000'
-API_TOKEN = context.api_token or '...'
-
-# SeaTable table config
-ST_TABLE_NAME = 'Sync MySQL'
-ST_NAME_COLUMN = 'Name'
-
-# MySQL config
-HOST = 'localhost'
-USER = 'username'
-PASSWORD = 'topsecret'
-MYSQL_DB = 'seatable'
-MYSQL_TABLE = 'order'
-MYSQL_NAME_COLUMN = 'name'
-
-def sync_mysql():
- # 1. Initialize connection
- # 1. a) SeaTable authentication
- base = Base(API_TOKEN, SERVER_URL)
- base.auth()
-
- # 1. b) MySQL connection
- connection = pymysql.connect(host=HOST, user=USER, password=PASSWORD, db=MYSQL_DB)
-
- # 2. Fetch existing rows from seaTable
- rows = base.list_rows(ST_TABLE_NAME)
- row_keys = [row.get(ST_NAME_COLUMN) for row in rows]
-
- # 3. Retrieving data from MySQL
- with connection.cursor(pymysql.cursors.DictCursor) as cursor:
- sql = "SELECT * FROM " + MYSQL_TABLE
- cursor.execute(sql)
- mysql_data = cursor.fetchall()
-
- # Synchronization
- rows_data = []
- for item in mysql_data:
- # 4. Look for data from MySQL not present in SeaTable
- if item.get(MYSQL_NAME_COLUMN) not in row_keys:
- row_data = {
- ST_NAME_COLUMN: item.get(MYSQL_NAME_COLUMN),
- }
- rows_data.append(row_data)
- # 5. Eventually add missing records
- if rows_data :
- base.batch_append_rows(ST_TABLE_NAME, rows_data)
-
-
-if __name__ == '__main__':
- sync_mysql()
-```
diff --git a/docs/scripts/python/examples/update_stock_price.md b/docs/scripts/python/examples/update_stock_price.md
deleted file mode 100644
index f0e9a60d..00000000
--- a/docs/scripts/python/examples/update_stock_price.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Watch stock price by querying an API
-
-This Python script demonstrates how to retrieve data from an external source by making a `GET` request to an external API. The [Twelve Data](https://twelvedata.com) API is indeed used to update and maintain current stock prices within a designated SeaTable table.
-
-!!! info "Free subscription and fake/mock APIs"
- A free subscription is available for Twelve Data if you just want to test the script (up to 800 calls per days are free).
-
- If you're interested in querying external APIs, you can find free playground APIs for such purpose such as the very specific [cat API](https://thecatapi.com/), [JSONPlaceholder](https://jsonplaceholder.typicode.com/) or more complex mock API such as [MockFast.io](https://mockfast.io/) allowing you to define the structure of the response for more complex and heavy testing.
-
-Here is the structure of the table named `Watch stock` you need so that this script could run (variables are present at the beginning of the script to easily adapt the names):
-
-| Column name | Symbol | Current stock price |
-| ----------- |:------:|:------:|
-| **Column type** | text | number (dollar) |
-
-You can create several lines to watch current stock price, for example by specifying *AAPL* or *AMZN* for the `Symbol` column.
-
-## Process Overview
-
-1. **Initializes configurations** for the Twelve Data API and SeaTable server.
-2. **Fetches current stock prices** using the Twelve Data API based on stock symbols from a SeaTable table (from the `Symbol` column in the `Watch stock` table).
-3. **Updates the SeaTable table** with the fetched current stock prices in the designated column (`Current stock price`).
-4. **Displays the updated stock prices** for each symbol in the console.
-
-This script enables the automated update of current stock prices within a SeaTable table by leveraging data from the Twelve Data API, ensuring that stock information remains up-to-date within the SeaTable environment.
-
-## Code
-
-```python
-from seatable_api import Base, context
-import requests
-"""
-This Python script integrates data from the Twelve Data API with SeaTable
-to update and maintain current stock prices.
-"""
-
-# 1. Configuration variables for both SeaTable and Twelve Data
-TWELVE_DATA_API_KEY = "dfb122bbca6a4..." # Replace this with your actual API key from Twelve Data
-
-SERVER_URL = context.server_url or "https://cloud.seatable.io/"
-API_TOKEN = context.api_token or "..."
-
-TABLE_WITH_STOCK_SYMBOLS = "Stock watch"
-COLUMN_WITH_STOCK_SYMBOLS = "Symbol"
-COLUMN_WITH_STOCK_PRICE = "Current stock price"
-
-def get_stock_price(SYMBOL):
- # Endpoint to fetch current stock price
- url = f"https://api.twelvedata.com/price?symbol={SYMBOL}&apikey={TWELVE_DATA_API_KEY}"
-
- # Make the GET request to fetch the data
- response = requests.get(url)
-
- if response.status_code == 200:
- output = response.json()
- return output['price']
- else:
- return False
-
-# Get symbols from SeaTable base and update the current stock prices
-def update_stock_price():
- for row in base.list_rows(TABLE_WITH_STOCK_SYMBOLS):
- # 2. Fetches the current stock price from Twelve Data API
- current_price = get_stock_price(row['Symbol'])
- # 3. Update the stock price in the table
- base.update_row(TABLE_WITH_STOCK_SYMBOLS, row.get('_id'), {COLUMN_WITH_STOCK_PRICE: current_price})
- # 4. Display the fetched value in the console
- print(f"The current price of {row['Symbol']} is: {current_price}")
-
-if __name__ == '__main__':
- base = Base(API_TOKEN, SERVER_URL)
- base.auth()
- update_stock_price()
- print("Update complete.")
-```
diff --git a/docs/scripts/python/introduction.md b/docs/scripts/python/introduction.md
deleted file mode 100644
index b89d92ff..00000000
--- a/docs/scripts/python/introduction.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# Introduction
-
-Python scripts connect to SeaTable databases with the python library [seatable-api](https://pypi.org/project/seatable-api/). You can find the source code on [GitHub](https://github.com/seatable/seatable-api-python). Python scripts can be created and executed directly in a base using a SeaTable component called Python Pipeline. You can also choose to run scripts locally. Where you run your Python script has consequences on the available libraries and authentication.
-
-!!! warning "Indents are important"
-
- Please take care of indentations! Indentation is mandatory in Python to define the blocks of statements. The number of spaces must be uniform in a block of code. It is preferred to use whitespaces instead of tabs to indent in Python. If the indentations are wrong, the scripts will throw errors or not work as expected!
-
-## Libraries
-
-The current Python Pipeline ships with Python 3.12 and a bundle of [third party libraries](/scripts/python/common_questions/#list-of-libraries-supported-in-the-cloud-environment). One of the bundled libraries and the main library to interact with SeaTable bases is [seatable-api](https://github.com/seatable/seatable-api-python).
-
-At a minimum, the Base and context function from the seatable-api library must be imported. Additionally, you can import functions from the bundled libraries.
-
-```python
-from seatable_api import Base, context
-from datetime import datetime
-```
-
-When running Python scripts locally, you can take advantages of the uncountable number of Python libraries.
-
-## Authentication
-
-Python (in comparison to JavaScript) scripts need an authentication. SeaTable provides multiple tokens to obtain authorization to read and write a base. But let's keep things simple! If you develop Python scripts in SeaTable, just use the context object `context.api_token` or provide a so called `API token` of a base (see [Authorization with API token below](#authorization-with-api-token)). If you want to learn more about authentication, all details can be found in the [SeaTable API Reference](https://api.seatable.com/reference/authentication).
-
-!!! warning "Protect your credentials"
-
- Please be aware that a python script is readable for all users, who have access to this base. Therefore try to avoid exposing your credentials directly in the code! Use environment variables or `.env` files instead.
-
-### Authorization with API token
-
-Using this method, you will use the API token of the base. Within SeaTable's integrated Python editor, authentication can be done very simply thanks to the [context object](https://developer.seatable.com/scripts/python/objects/context/). In local environment, the context object is not available. You'll have to provide directly the `api_token` and the `server_url` variables. The API token can be directly [generated in the web interface](https://seatable.com/help/erzeugen-eines-api-tokens/).
-
-=== "SeaTable's integrated Python editor"
-
- ```python
- from seatable_api import Base, context # (1)!
- base = Base(context.api_token, context.server_url)
- base.auth()
- ```
-
- 1. Don't forget to import `context`. Thanks to this, you won't have to manually provide any credential.
-
-=== "Local execution"
-
- ```python
- from seatable_api import Base # (1)!
-
- API_TOKEN = 'c3c75dca2c369848455a39f4436147639cf02b2d' # (2)!
- SERVER_URL = 'https://cloud.seatable.io'
-
- base = Base(API_TOKEN, SERVER_URL)
- base.auth()
- ```
-
- 1. No need to import `context` here as it won't actually be available.
-
- 2. This is for demonstration purpose only: try to avoid exposing your credentials directly in the code! Use environment variables or `.env` files instead.
-
-It is even possible to develop a Python script in the way that it could be [executed both in the cloud and locally](/scripts/python/common_questions/#how-to-make-the-script-support-both-local-and-cloud-run) without changing the code.
-
-### Authorization with account object
-
-Instead of using an API token, you can also authenticate using the `account` object. Doing so, you'll have to provide both your `username` and `password` (in addition to the `server_url` variable).
-
-Whereas the API token is specific to a base, the `account` object is general and gives you access to all your bases (as when you log on SeaTable). To get a specific base, you'll have to use the `get_base` function, given the workspace ID `workspace_id` and the name of the base `base_name`. To get the workspace ID:
-
-1. Go to the SeaTable home page.
-
-2. Click the base whose workspace ID you want to determine.
-
-3. When the selected base has opened, you can read the Workspace ID at the top of the page URL, which actually looks like *https://cloud.seatable.io/workspace/`84254`/dtable/MyBase* (or any `server_url` instead of *https://cloud.seatable.io*).
-
-
-```python
-from seatable_api import Account
-account = Account(username, password, server_url)
-account.auth()
-base = account.get_base(workspace_id, base_name)
-```
-
-### Authorization expiration handling
-
-!!! info "This feature works with SeaTable version 3.1+"
-
-In some cases, the program needs to run for a (very) long time, the code of base operations usually being located in a `while` or `for` loop. In this case, authorization may expire during execution and cause the program to break. We provide an exception called `AuthExpiredError` that can be caught for reauthorization.
-
-```python
-from seatable_api import Base, context
-from seatable_api.exception import AuthExpiredError
-
-server_url = context.server_url or 'https://cloud.seatable.io'
-api_token = context.api_token or 'c3c75dca2c369849455a39f4436147639cf02b2d'
-
-base = Base(api_token, server_url)
-base.auth()
-
-while True: # (1)!
- try:
- base.append_row('Table1', {"xxx":"xxx"})
- ...
- except AuthExpiredError:
- base.auth()
-```
-
-1. Always be careful with infinite loops!
-
-## Base operations limits
-
-As Python scripts are tailored for huge base manipulations and because they actually rely on the [SeaTable API](https://api.seatable.com), you might encounter [Rate](https://api.seatable.com/reference/limits#general-rate-limits) or [Size](https://api.seatable.com/reference/limits#size-limits) limits if you are not vigilant. Here are a few tips to avoid reaching the limits:
-
-- Be always careful with operations in `for` or `while` loops (ensure the ending conditions will be reached)
-
-- Use *batch* operations as often as possible. Replace for example several `base.append_row` calls with a single `base.batch_append_rows` call. Here are the main batch functions:
-
- - `base.batch_append_rows`
- - `base.batch_update_rows`
- - `base.batch_delete_rows`
- - `base.batch_update_links`
-
-- Learn more about [lowering your calls](https://seatable.com/api-optimization/)
diff --git a/docs/scripts/python/objects/big_data.md b/docs/scripts/python/objects/big_data.md
deleted file mode 100644
index 8fe9d54a..00000000
--- a/docs/scripts/python/objects/big_data.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Big data storage
-
-## Insert rows into big data storage
-
-!!! abstract "big_data_insert_rows"
-
- Batch insert rows into big data storage.
-
- ``` python
- base.big_data_insert_rows(table_name, rows_data)
- ```
-
- __Output__ Dict containing a single `inserted_row_count` key with the number of rows actually inserted in the big data storage.
-
- __Example__
-
- ``` python
- rows = [
- {'Name': "A"},
- {'Name': "B"}
- ]
- base.big_data_insert_rows('Table1', rows_data=rows)
- ```
diff --git a/docs/scripts/python/objects/index.md b/docs/scripts/python/objects/index.md
deleted file mode 100644
index 52f34a72..00000000
--- a/docs/scripts/python/objects/index.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Predefined objects and methods (Python)
-
-This manual list all available objects and methods (also called functions) that are available within Python scripts in SeaTable. When running directly in SeaTable, Python scripts have the ability to access the [base context](context.md). [Date utilities](date-utils.md) are also available.
-
-If you compare JavaScript and Python, you will notice that Python has no specific output methods. This is not necessary, because the output is either written into the base or directly returned by the methods. Besides, you'll see that **Python methods never accept objects** for table, view or row selection arguments, but only their names/`_ids` as strings. Unless otherwise stated, **all method arguments are required**.
-
-## Data model
-
-{%
- include-markdown "includes.md"
- start=""
- end=""
-%}
-
-!!! info "Need a specific function?"
-
- The Python library `seatable_api` does not yet cover all available functions of the SeaTable API. If you are missing a special function, please contact us at [support@seatable.io](mailto:support@seatable.io) and we will try to add the missing functions.
-
-## Getting started
-
-Let's make this concrete and let us look at some basic examples.
-
-1. Jump to your SeaTable web interface
-2. Create a new Python script
-3. Copy the following code
-4. Run the script
-
-You will learn from these examples, that it is quite easy to read, output and even manipulate the data of a base inside SeaTable with the predefined objects and the corresponding methods.
-
-
-
-=== "1. Add a table to a base"
-
- This examples shows how to add a table to an existing base.
-
- ``` python
- from seatable_api import Base, context
- base = Base(context.api_token, context.server_url)
- base.auth() # (1)!
-
- columns=[
- {
- "column_type" : "text",
- "column_name": "name"
- },
- {
- "column_type": "number",
- "column_name": "age"
- }
- ]
-
- base.add_table("ScriptTest", lang='en', columns=columns)
- ```
-
- 1. These three lines are always required to authorize against the base in SeaTable.
-
-=== "2. Add a row to this new table"
- This examples shows how to add a record to a table. The example script assumes that a table "ScriptTest" table with two columns "name" and "age" exists in the base.
-
- ``` python
- from seatable_api import Base, context
- base = Base(context.api_token, context.server_url)
- base.auth()
-
- row_data = {
- 'name': 'Tom',
- 'age': 18
- }
-
- base.append_row('ScriptTest', row_data)
- ```
diff --git a/docs/scripts/python/objects/users.md b/docs/scripts/python/objects/users.md
deleted file mode 100644
index 5753b226..00000000
--- a/docs/scripts/python/objects/users.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Users
-
-## Get user info
-
-!!! abstract "get_user_info"
-
- Returns the name of the user and his ID (the one you can see in your [profile](https://seatable.com/help/persoenliche-einstellungen/)). The username you have to provide is a unique identifier ending by `@auth.local`. This is **neither** the email address of the user **nor** its name.
-
- ``` python
- base.get_user_info(username)
- ```
-
- __Output__ Dict containing `id_in_org` and `name` keys
-
-
- __Example__
-
- ``` python
- from seatable_api import Base, context
-
- base = Base(context.api_token, context.server_url)
- base.auth()
- user_info = base.get_user_info("aea9e807bcfd4f3481d60294df74f6ee@auth.local")
- print(user_info)
- ```
diff --git a/docs/scripts/sql/introduction.md b/docs/scripts/sql/introduction.md
deleted file mode 100644
index 691bf370..00000000
--- a/docs/scripts/sql/introduction.md
+++ /dev/null
@@ -1,298 +0,0 @@
-# SQL in SeaTable
-
-SQL queries are the most powerful way to access data stored in a base. If you're not familiar with SQL syntax, we recommend using first the [SQL query plugin](https://seatable.com/help/anleitung-zum-sql-abfrage-plugin/). If some tables in a base are archived, archived rows are also queried, as well as rows that are not archived yet.
-
-!!! info "Backticks for table or column names containing special characters or using reserved words"
- For SQL queries, you can use numbers, special characters or spaces in the names of your tables and columns. However, you'll **have to** escape these names with backticks in order for your query to be correctly interpreted, for example `` SELECT * FROM `My Table` ``.
-
- Similarly, if some of your table or column names are the same as [SQL function](./functions.md) names (for example a date-type column named `date`), you'll also **have to** escape them in order for the query interpreter to understand that it's not a function call missing parameters, but rather a table or column name.
-
-## Supported SQL Syntax
-
-Currently only `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements are supported (the last three require version 2.7 or later). You'll find below the syntax for these statements.
-
-Please note that the SQL syntax is case insensitive: we use only upper-cased instructions here for ease of reading (differentiating SQL instructions from table or column names).
-
-### Retrieving row(s)
-
-!!! abstract "SELECT"
- The `SELECT` statement allows you to retrieve an eventually filtered, sorted and/or grouped list of the rows from a specific table. Each returned row is a JSON object. The keys of the object are the column **keys, NOT the column names**. To use column names as keys, the `convert_keys` parameter (available since version 2.4) in query request should be `true` (which is the default value when using `base.query` for both JavaScript and Python scripts).
- The syntax of `SELECT` statement is:
-
- ```
- SELECT [Column List] FROM tableName [Where Clause] [Group By Clause] [Having Clause] [Order By Clause] [Limit Option]
- ```
-
- `[Column List]` is the list of columns you want to retrieve, separated by commas. If you want to retrieve all the columns, you can use a wildcard (`*`).
- You can consult specific sections for [Where, Group By, Having or Order By clauses](#where-group-by-having-and-order-by-clauses)
- `Limit Option` uses MySQL format. The general syntax is `LIMIT ... OFFSET ...`. This parameters are optional. Unless you specify a higher limit, the method returns **a maximum of 100 rows**. The maximum number of rows returned is **10000** no matter the limit specified in the SQL statement. The `OFFSET` will help you retrieve the following rows in other queries
-
- __Example__ `SELECT * FROM Table1 LIMIT 10000` returns the first 10000 rows, `SELECT * FROM Table1 LIMIT 10000 OFFSET 10000` returns the next 10000 rows
-
-
- Since version 4.3, basic **implicit** *join* query is supported, for example:
-
- ```
- SELECT ... FROM Table1, Table2 WHERE Table1.column1 = Table2.column2 AND ...
- ```
-
- The *join* queries have the following restrictions:
-
- - You **must not** explicitly write JOIN keyword
- - Only *inner join* is supported; *left join*, *right join*, and *full join* are not supported.
- - Tables in the `FROM` clause should be unique (no duplicate tables).
- - Each table in the `FROM` clause should be associated with at least one join condition.
- - Join conditions should be placed in the `WHERE` clause, and eventually connected with one or more `AND` operators.
- - Join conditions can only use **equality operator** on columns, e.g. `Table1.column1 = Table2.column2`.
- - Columns in join conditions must be indexed, unless the table is not archived.
-
-!!! info "Field aliases"
- Field alias with `AS` syntax is supported. For example, `SELECT table.a as a FROM table` returns rows whose first column is keyed by "a". There are two important points to note however:
-
- - Field alias can be referred in `GROUP BY`, `HAVING` and `ORDER BY` clauses. For example, `SELECT i.amount AS a, COUNT(*) FROM Invoices AS i GROUP BY a HAVING a > 100` is valid.
- - Field alias cannot be referred in `where` clause. E.g., `select t.registration as r, count(*) from t group by r where r > "2020-01-01"` will report syntax error.
-
-!!! info "Aggregation functions"
- While retrieving rows, you can add aggregation functions to the list of columns if you specify a [GroupByClause](#where-group-by-having-and-order-by-clauses). The available functions are:
-
- - `COUNT` returns the number of non-empty values in a specific column or for all columns with `COUNT(*)`
- - `SUM` computes the sum of values in a specific column, for example `SUM(Invoices.Amount)`
- - `MAX` retrieves the greatest value in a specific column, for example `MAX(Invoices.Amount)`
- - `MIN` retrieves the smallest value in a specific column, for example `MIN(Invoices.Amount)`
- - `AVG` computes the average of non-empty values in a specific column, for example `AVG(Invoices.Amount)`
-
- __Example__
-
- ```
- SELECT Customer, SUM(Amount) from Invoices GROUP BY Customer
- ```
-
-### Modifying database content
-
-!!! abstract "INSERT"
-
- !!! warning "Enterprise subscription needed"
-
- `INSERT` requires [Big Data](https://seatable.com/help/big-data-capabilities/) storage support, which is available only with an [Enterprise subscription](https://seatable.com/help/subscription-plans/#seatable-cloud-enterprise-search).
-
- `INSERT` allows you to append a new row to a table. `INSERT` statement **only** supports bases that have been [archived](https://seatable.com/help/aktivieren-des-big-data-backends-in-einer-base/#designation-as-archivebackend-search). The rows will be inserted into big-data storage. It'll return error if the base is not archived yet.
-
-
- If you want to insert rows in a non-archived base, please use the API dedicated functions (e.g. the [Python API](../python/objects/rows.md#add-rows)).
-
- ```
- INSERT INTO table_name [column_list] VALUES value_list [, ...]
- ```
-
- - `column_list` is a list of column names surrounded by parentheses. If omitted, it defaults to all updatable columns.
- - `value_list` is a list of values surrounded by parentheses. Values must be in the same order as the column list, for example: `(1, "2", 3.0)`.
- - Columns with multiple values, such as "multiple select"-type column , requires values to be surrounded by parentheses, for example: `(1, "2", 3.0, ("foo", "bar"))`.
- - Values of "single select" and "multiple select"-type columns must be option names, not option keys.
- - Few column types are **not allowed** to insert:
-
- - built-in columns, such as `_id`, `_ctime`.
- - image, file, formula, link, link-formula, geolocation, auto-number, button
-
- __Example__
-
- ```
- INSERT INTO Table1 (Name, Age) values ('Erika', 38)
- ```
-
-!!! abstract "UPDATE"
- `UPDATE` allows you to update one or multiple existing rows of a table. Unlike the `INSERT` statement, `UPDATE` allows you to update rows in both normal and big-data storage. `WhereClause` is optional. However, keep in mind that if omitted, **all rows** will be updated!
-
- ```
- UPDATE table_name SET column_name = value [, ...] [WhereClause]
- ```
-
- !!! warning "Only constant values in SET clause"
- The `value` in the SET clause must be a **constant** (string, number, or boolean). Functions (e.g. `upper()`, `round()`, `now()`, `if()`), arithmetic expressions (e.g. `Amount + 10`), and column references are **not supported** in the SET clause. Functions and expressions can only be used in `SELECT` and `WHERE` clauses.
-
- - Columns with multiple values, such as "multiple select"-type column, requires values to be surrounded by parentheses, for example: `("foo", "bar")`.
- - Values of "single select" and "multiple select"-type columns must be option names, not option keys.
- - Few column types are **not allowed** to update:
-
- - built-in columns, such as `_id`, `_ctime`.
- - image, file, formula, link, link-formula, geolocation, auto-number, button
-
- __Example__
-
- ```
- UPDATE Contacts SET Adult=true WHERE Age>=18
- ```
-
- __Example__
-
- ```
- UPDATE Contacts SET Adult=true, `Age group`="18+" WHERE Age>=18
- ```
-
- If `Age group` is a "single select"-type column, the option you want to select (here "18+") has to exist already.
-
-!!! abstract "DELETE"
- `DELETE` allows you to delete one or multiple existing rows of a table. Unlike the `INSERT` statement, `DELETE` allows you to delete rows in both normal and big-data storage. `WhereClause` is optional. However, keep in mind that if omitted, **all rows** will be deleted!
-
- ```
- DELETE FROM table_name [WhereClause]
- ```
-
- __Example__
-
- ```
- DELETE FROM Contacts WHERE Age<18
- ```
-
-
-### WHERE, GROUP BY, HAVING and ORDER BY clauses
-
-!!! abstract "WHERE clause"
- Most SQL syntax can be used in the `WHERE` clause, including arithmetic expressions, comparison operators, `[NOT] LIKE`, `IN`, `BETWEEN ... AND ...`, `AND`, `OR`, `NOT`, `IS [NOT] TRUE`, `IS [NOT] NULL`.
-
- - Arithmetic expressions only support numbers.
- - Time constants should be strings in ISO format (e.g. "2020-09-08 00:11:23"). Since 2.8 version, strings in RFC 3339 format are supported (such as "2020-12-31T23:59:60Z").
-
-!!! abstract "GROUP BY clause"
- `GROUP BY` uses strict syntax. The selected fields must appear in the clause list, except for aggregation functions (`COUNT`, `SUM`, `MAX`, `MIN`, `AVG`) and formulas (see extended syntax section below).
-
-!!! abstract "HAVING clause"
- `HAVING` filters rows resulting from the `GROUP BY` clause. Only fields referred in the `GROUP BY` clause or aggregation functions (such as "SUM") can be used in `HAVING` clause. Other syntax is the same as specified for the `WHERE` clause.
-
-!!! abstract "ORDER BY clause"
- Fields in `ORDER BY` list must be a column or an expression in the selected fields. For example, `select a from table order by b` is invalid; while `select a, b from table order by b` and `select abs(a), b from table order by abs(a)` are valid.
-
-### LIKE and BETWEEN operators
-
-!!! abstract "LIKE operator"
- `LIKE` only supports strings. The key word `ILIKE` can be used instead of `LIKE` to make the match case insensitive. The percent sign `%` you will use in the `LIKE` expression represents zero, one, or multiple characters.
-
- __Example__
- ```
- SELECT `Full Name` from Contacts WHERE `Full Name` LIKE "% M%"
- ```
- returns every record with a last name starting with M (considering that the `Full Name` fields is actually composed like "`First Name` `Last Name`")
-
-!!! abstract "BETWEEN operator"
- `BETWEEN lowerLimit AND upperLimit` only supports numbers and time. `lowerLimit` and `upperLimit` are included in the search. They have to be in the right order (if `upperLimit`<`lowerLimit`, no records will be found).
-
- __Example__
- ```
- SELECT * from Contacts WHERE Age BETWEEN 18 AND 25
- ```
- returns every record with an age between 18 and 25 (both included)
-
-
-## Data types
-
-### SeaTable <> SQL mapping
-
-Below is the mapping of SeaTable column types to SQL data types.
-
-| SeaTable column type | SQL data type | Query result format | Use in WHERE clause | Use in GROUP BY / ORDER BY clause |
-| :-------------------- | :--------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------- |
-| text | String | | Supported | Supported |
-| long-text | String | Raw text in Markdown format | Supported | Supported |
-| number | Float | | Supported | Supported |
-| single-select | String | Returned rows contain the option name. | Supported. Refer an option by its name. E.g. `WHERE single_select = "New York"`. | Order by the definition order of the options |
-| multiple-select | List of strings | Returned rows contain the option names. | Supported. Refer an option by its name. E.g. `WHERE multi_select = "New York"`. More details in the "List types" section below. | More details in the "List types" section below. |
-| checkbox | Boolean | | Supported | Supported |
-| date | Datetime | Time strings in RFC 3339 format | Supported. Constants are expressed in strings in ISO format. e.g. "2006-1-2" or "2006-1-2 15:04:05". Since 2.8 version, strings in RFC 3339 format are supported (such as "2020-12-31T23:59:60Z"). | Supported |
-| image | List of URL for images | A JSON array with image URLs as elements | Supported. More details in the "List types" section below. | Supported. More details in the "List types" section below. |
-| file | Will be returned as JSON format string when queried. | Not supported | Not Supported | Not Supported |
-| collaborator | List of user IDs | Format is like 5758ec...6d3388@auth.local. If you need user names, you have to convert with SeaTable APIs. | Supported. More details in the "List types" section below. | Supported. More details in the "List types" section below. |
-| link to other records | List of linked rows | Supported. More details in the "List types" section below. | Supported. More details in the "List types" section below. | Supported. More details in the "List types" section below. |
-| formula | The type depends on the return value of the formula. | Depends on the type of the return value | Depends on the type of the return value | Depends on the type of the return value |
-| \_creator | User ID as string | Format is like 5758ec...6d3388@auth.local. If you need user names, you have to convert with SeaTable APIs. | Supported | Supported |
-| \_ctime | Datetime | Time strings in RFC 3339 format | Supported. Constants are expressed in strings in ISO format. e.g. "2006-1-2" or "2006-1-2 15:04:05". Since 2.8 version, strings in RFC 3339 format are supported (such as "2020-12-31T23:59:60Z"). | Supported |
-| \_last_modifier | User ID as string | Format is like 5758ec...6d3388@auth.local. If you need user names, you have to convert with SeaTable APIs. | Supported | Supported |
-| \_mtime | Datetime | Time strings in RFC 3339 format | Supported. Constants are expressed in strings in ISO format. e.g. "2006-1-2" or "2006-1-2 15:04:05". Since 2.8 version, strings in RFC 3339 format are supported (such as "2020-12-31T23:59:60Z"). | Supported |
-| auto number | String | | Supported | Supported |
-| url | String | | Supported | Supported |
-| email | String | | Supported | Supported |
-| duration | Float | Returned in seconds | Supported | Supported |
-
-### List types
-
-In SeaTable, two categories of column types are list types (columns with multiple values):
-
-- Built-in list types: including multiple selection, image, file, collaborator and link to other records.
-- Formula columns dealing with linked records (using either `{link.column}` or `lookup`) and link formula columns whose formula is `lookup`, `findmin` or `findmax`.
-
-When referring to a list-type column in a `WHERE` clause, the following rules apply, depending on the type for the list elements. If an operator is not listed below, it's unsupported.
-
-| Element Type | Operator | Rule |
-| :------------ | :---------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
-| string | `IN`, extended list operators (e.g. `HAS ANY OF`) | Follow the rules of the operator. |
-| string | `LIKE`, `ILIKE` | Always take the first element for comparison; if there is no element, use an empty string ("").
- |
-| string | `IS NULL` | Return `true` when the list is empty or when there is no data in the cell. |
-| string | =, != | Always take the first element for comparison; if there is no element, use an empty string (""). |
-| float | `IN`, extended list operators (e.g. `HAS ANY OF`) | Follow the rules of the operator. |
-| float | =, !=, <, <=, >, >=, between | If there is only 1 element, use that element; otherwise only return `true` for `!=` operator. |
-| float | `IS NULL` | Return `true` when the list is empty or when there is no data in the cell. |
-| float | Arithmetics operations such as +, -, * or / | Use the first element for calculation. |
-| Datetime | `IN`, extended list operators (e.g. `HAS ANY OF`) | Follow the rules of the operator. |
-| Datetime | =, !=, <, <=, >, >=, between | If there is only 1 element, use that element; otherwise only return `true` for `!=` operator. |
-| Datetime | `IS NULL` | Return `true` when the list is empty or when there is no data in the cell. |
-| bool | `IS TRUE` | Always take the first element for comparison; return false if there are no elements.
-| linked record | | Follow the rules for the type of the display column. |
-
-When a list column is returned in a selected field, only the ten first elements are returned.
-
-When used in `GROUP BY` or `ORDER BY` clauses, the elements for each list will first be sorted in ascending order, then the lists will be sorted by the rules below:
-
-- Compare the elements one by one, list with smaller element is sorted before list with larger element.
-- If all elements compared in step 1 are equal, shorter list is sorted before longer list.
-- Otherwise the two lists are equal.
-
-If a list column is passed as parameter to a formula, and the parameter expects a scalar value, the first element will be used. And if the element is a linked record, the value of its display column will be used.
-
-When applying aggregate functions (min, max, sum, avg) to a list column, if there is only 1 element in the list, use that element; otherwise this row will not be aggregated.
-
-### NULL values
-
-NULL value is distinct from 0. It represents a missing value. The following values are treated as NULL:
-
-- Empty cells in a table.
-- Values which cannot be converted to the column type.
-- Empty strings (""). This is different from standard SQL.
-- Lists are treated as NULL based on the rules described in the "List Types" section.
-- Functions or formula columns that return error.
-
-In the `WHERE` clause:
-
-- Arithmetics operations such as +, -, * or / on NULL values will return NULL.
-- `!=`, `NOT LIKE`, `NOT IN`, `NOT BETWEEN`, `HAS NONE OF`, `IS NOT TRUE`, and `IS NULL` operations will return `true` when the value is NULL.
-- `AND`, `OR`, `NOT` treat NULL values as `false`.
-- Aggregate functions (min, max, sum, avg) will ignore NULL values.
-
-In formulas, NULL values will be converted to 0 or an empty string.
-
-## Extended syntax
-
-### Using formulas in SQL query
-
-You may use a formula syntax that's almost the same as SeaTable's formulas in SQL queries. There are a few special notes:
-
-- Link formulas are not supported. e.g. {link.age} is invalid.
-- Reference to columns should not be enclosed by curly brackets ("{}"). Don't write `SELECT abs({column}) FROM table`. Write `SELECT abs(column) FROM table`. This is consistent with standard SQL syntax.
-- You have to use backticks ("\`\`") to enclose column references, when column name contains space or "-". E.g. ```SELECT abs(`column-a`) FROM table```.
-- You cannot use column alias in formulas. E.g. `SELECT abs(t.column) FROM table AS t;` is invalid.
-- Formulas can be used in `GROUP BY` and `ORDER BY` clauses.
-
-For an exhaustive list of available functions, please refer to the complete [function reference](./functions.md).
-
-### Extended list operators
-
-Some column types in SeaTable have list values. The SeaTable UI supports a few special filters for such types, which are `HAS ANY OF`, `HAS ALL OF`, `HAS NONE OF` and `IS EXACTLY`. You can use the same syntax to filter such columns with SQL. For all these operators, the list of string constant are enclosed with brackets, just like the syntax for `IN`. Please note that the order of values in the list is not taken into account.
-
-__Example__ `SELECT * FROM table WHERE city HAS ANY OF ("New York", "Paris")` will retrieve all rows that contain either "New York" or "Paris" in the "multiple select"-type column `city`
-
-## Big Data storage indexes
-
-To improve query performance, SeaTable will automatically create indexes for the rows stored in big data storage engine. Currently, text, number, date, single select, multiple select, collaborators, creator, create date, modifier and modification date columns are indexed.
-
-When you add or delete a column in a table, the index for this column is not added/removed immediately. Indexes creation and deletion are triggered in two cases:
-
-1. When you archive the table for the next time, indexes are created for new columns and indexes for removed columns are removed.
-2. Users may manage indexes from "index management" UI. You can open it from the "Big data management" menu in the base.
diff --git a/docs/sql/clauses.md b/docs/sql/clauses.md
new file mode 100644
index 00000000..4df718bc
--- /dev/null
+++ b/docs/sql/clauses.md
@@ -0,0 +1,56 @@
+# Clauses
+
+!!! info "Backticks for special names"
+ Escape table or column names that contain spaces, special characters, or match [SQL function](./functions.md) names with backticks: `` SELECT * FROM `My Table` ``.
+
+## WHERE
+
+Most SQL syntax can be used in the `WHERE` clause: arithmetic expressions, comparison operators, `[NOT] LIKE`, `IN`, `BETWEEN ... AND ...`, `AND`, `OR`, `NOT`, `IS [NOT] TRUE`, `IS [NOT] NULL`.
+
+- Arithmetic expressions only support numbers
+- Time constants must be strings in ISO format (e.g. `"2020-09-08 00:11:23"`). Since version 2.8, RFC 3339 format is also supported (e.g. `"2020-12-31T23:59:60Z"`)
+
+## GROUP BY
+
+`GROUP BY` uses strict syntax. Selected fields must appear in the GROUP BY list, except for aggregation functions (`COUNT`, `SUM`, `MAX`, `MIN`, `AVG`) and formulas.
+
+## HAVING
+
+`HAVING` filters rows resulting from `GROUP BY`. Only fields in the GROUP BY list or aggregation functions can be used. Other syntax is the same as WHERE.
+
+## ORDER BY
+
+Fields in the `ORDER BY` list must be a column or expression that appears in the selected fields.
+
+__Valid:__
+```
+SELECT a, b FROM table ORDER BY b
+SELECT abs(a), b FROM table ORDER BY abs(a)
+```
+
+__Invalid:__
+```
+SELECT a FROM table ORDER BY b
+```
+
+## LIKE
+
+`LIKE` only supports strings. Use `ILIKE` for case-insensitive matching. The percent sign `%` represents zero, one, or multiple characters.
+
+__Example__
+
+```
+SELECT `Full Name` FROM Contacts WHERE `Full Name` LIKE "% M%"
+```
+
+Returns every record with a last name starting with M.
+
+## BETWEEN
+
+`BETWEEN lowerLimit AND upperLimit` supports numbers and time. Both limits are included. They must be in the correct order.
+
+__Example__
+
+```
+SELECT * FROM Contacts WHERE Age BETWEEN 18 AND 25
+```
diff --git a/docs/sql/data-types.md b/docs/sql/data-types.md
new file mode 100644
index 00000000..3132f90e
--- /dev/null
+++ b/docs/sql/data-types.md
@@ -0,0 +1,78 @@
+# Data Types
+
+## SeaTable to SQL mapping
+
+| SeaTable column type | SQL data type | Query result format | WHERE | GROUP BY / ORDER BY |
+|:---|:---|:---|:---|:---|
+| text | String | | Supported | Supported |
+| long-text | String | Raw Markdown | Supported | Supported |
+| number | Float | | Supported | Supported |
+| single-select | String | Option name | By option name | By definition order |
+| multiple-select | List of strings | Option names | By option name | See list types |
+| checkbox | Boolean | | Supported | Supported |
+| date | Datetime | RFC 3339 format | ISO or RFC 3339 strings | Supported |
+| image | List of URLs | JSON array | See list types | See list types |
+| file | JSON string | | Not supported | Not supported |
+| collaborator | List of user IDs | `xxx@auth.local` | See list types | See list types |
+| link to other records | List of linked rows | | See list types | See list types |
+| formula | Depends on return value | | Depends on type | Depends on type |
+| \_creator | String (user ID) | `xxx@auth.local` | Supported | Supported |
+| \_ctime | Datetime | RFC 3339 format | ISO or RFC 3339 strings | Supported |
+| \_last\_modifier | String (user ID) | `xxx@auth.local` | Supported | Supported |
+| \_mtime | Datetime | RFC 3339 format | ISO or RFC 3339 strings | Supported |
+| auto number | String | | Supported | Supported |
+| url | String | | Supported | Supported |
+| email | String | | Supported | Supported |
+| duration | Float | In seconds | Supported | Supported |
+
+## List types
+
+Two categories of columns have list values:
+
+- **Built-in**: multiple select, image, file, collaborator, link to other records
+- **Formula-based**: link formulas using `lookup`, `findmin`, or `findmax`
+
+### WHERE clause rules for list types
+
+| Element type | Operator | Rule |
+|:---|:---|:---|
+| string | `IN`, `HAS ANY OF`, etc. | Follow operator rules |
+| string | `LIKE`, `ILIKE` | Uses first element; empty string if no element |
+| string | `IS NULL` | True when list is empty |
+| string | `=`, `!=` | Uses first element |
+| float | `IN`, `HAS ANY OF`, etc. | Follow operator rules |
+| float | `=`, `!=`, `<`, `<=`, `>`, `>=`, `BETWEEN` | Uses single element; only `!=` returns true for multiple |
+| float | `IS NULL` | True when list is empty |
+| float | `+`, `-`, `*`, `/` | Uses first element |
+| datetime | Same rules as float | |
+| bool | `IS TRUE` | Uses first element; false if empty |
+| linked record | | Follows rules for the display column type |
+
+Only the first ten elements are returned in query results.
+
+### Sorting list types
+
+In GROUP BY / ORDER BY, elements are first sorted ascending within each list, then lists are compared element by element. Shorter lists sort before longer lists when all compared elements are equal.
+
+### Aggregation on list types
+
+For `MIN`, `MAX`, `SUM`, `AVG`: if the list has exactly one element, that element is used. Otherwise the row is not aggregated.
+
+## NULL values
+
+NULL represents a missing value (distinct from 0). These are treated as NULL:
+
+- Empty cells
+- Values that cannot be converted to the column type
+- Empty strings (`""`)
+- Empty lists (see list types rules)
+- Formulas that return an error
+
+### NULL in WHERE
+
+- Arithmetic on NULL returns NULL
+- `!=`, `NOT LIKE`, `NOT IN`, `NOT BETWEEN`, `HAS NONE OF`, `IS NOT TRUE`, `IS NULL` return `true` for NULL
+- `AND`, `OR`, `NOT` treat NULL as `false`
+- Aggregate functions ignore NULL values
+
+In formulas, NULL is converted to 0 or empty string.
diff --git a/docs/sql/extended-syntax.md b/docs/sql/extended-syntax.md
new file mode 100644
index 00000000..78b7a058
--- /dev/null
+++ b/docs/sql/extended-syntax.md
@@ -0,0 +1,41 @@
+# Extended Syntax
+
+## Formulas in SQL queries
+
+You can use SeaTable formula syntax directly in SQL queries. A few differences from SeaTable's built-in formulas:
+
+- Link formulas (e.g. `{link.age}`) are **not** supported
+- Column references are **not** enclosed in curly brackets: use `abs(column)`, not `abs({column})`
+- Use backticks for column names with spaces or hyphens: `` abs(`column-a`) ``
+- Column aliases cannot be used in formulas: `abs(t.column)` is invalid
+- Formulas can be used in `GROUP BY` and `ORDER BY` clauses
+
+For the complete list of available functions, see the [function reference](./functions.md).
+
+## Extended list operators
+
+SeaTable supports special operators for list-type columns (multiple select, collaborator, etc.):
+
+| Operator | Description |
+|---|---|
+| `HAS ANY OF` | Row contains at least one of the values |
+| `HAS ALL OF` | Row contains all of the values |
+| `HAS NONE OF` | Row contains none of the values |
+| `IS EXACTLY` | Row contains exactly these values (order-independent) |
+
+Values are enclosed in parentheses, like the `IN` operator.
+
+__Example__
+
+```
+SELECT * FROM table WHERE city HAS ANY OF ("New York", "Paris")
+```
+
+## Big Data storage indexes
+
+SeaTable automatically creates indexes for rows in big data storage to improve query performance. Indexed column types: text, number, date, single select, multiple select, collaborators, creator, create date, modifier, modification date.
+
+Indexes are updated when:
+
+1. The table is archived the next time
+2. A user triggers index management from the "Big data management" menu in the base
diff --git a/docs/scripts/sql/functions.md b/docs/sql/functions.md
similarity index 100%
rename from docs/scripts/sql/functions.md
rename to docs/sql/functions.md
diff --git a/docs/sql/index.md b/docs/sql/index.md
new file mode 100644
index 00000000..be60774a
--- /dev/null
+++ b/docs/sql/index.md
@@ -0,0 +1,21 @@
+# SQL
+
+SQL queries are the most powerful way to access data stored in a base. SeaTable supports `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements. SQL is not a standalone interface but is used through the [Python](../python/) or [JavaScript](../javascript/) API via `base.query()`:
+
+=== "Python"
+
+ ```python
+ results = base.query("SELECT * FROM Table1 LIMIT 100")
+ ```
+
+=== "JavaScript"
+
+ ```js
+ const results = await base.query("SELECT * FROM Table1 LIMIT 100");
+ ```
+
+SQL syntax is case insensitive. We use upper-cased keywords for readability.
+
+!!! tip "New to SQL?"
+
+ Try the [SQL query plugin](https://seatable.com/help/anleitung-zum-sql-abfrage-plugin/) in SeaTable to experiment with queries interactively.
diff --git a/docs/sql/insert-update-delete.md b/docs/sql/insert-update-delete.md
new file mode 100644
index 00000000..3a81b884
--- /dev/null
+++ b/docs/sql/insert-update-delete.md
@@ -0,0 +1,84 @@
+# INSERT, UPDATE, DELETE
+
+These statements modify data in a base. Available since SeaTable version 2.7.
+
+## INSERT
+
+Appends a new row to a table. `INSERT` **only** works with bases that have [Big Data storage](https://seatable.com/help/big-data-capabilities/) enabled. Rows are inserted into big data storage.
+
+!!! warning "Enterprise subscription needed"
+
+ `INSERT` requires Big Data storage support, which is available only with an [Enterprise subscription](https://seatable.com/help/subscription-plans/#seatable-cloud-enterprise-search).
+
+For non-archived bases, use the API functions instead (e.g. [Python `append_row`](/python/objects/rows/#add-rows) or [JavaScript `appendRow`](/javascript/rows/)).
+
+### Syntax
+
+```
+INSERT INTO table_name [column_list] VALUES value_list [, ...]
+```
+
+- `column_list`: column names in parentheses. If omitted, defaults to all updatable columns.
+- `value_list`: values in parentheses, matching the column order: `(1, "text", 3.0)`
+- Multi-value columns (e.g. multiple select): use nested parentheses: `(1, "text", ("foo", "bar"))`
+- Single/multiple select values must be option **names**, not keys
+
+### Column restrictions
+
+These column types cannot be inserted: `_id`, `_ctime` (built-in), image, file, formula, link, link-formula, geolocation, auto-number, button.
+
+__Example__
+
+```
+INSERT INTO Table1 (Name, Age) VALUES ('Erika', 38)
+```
+
+## UPDATE
+
+Updates one or multiple rows. Works with both normal and big data storage.
+
+### Syntax
+
+```
+UPDATE table_name SET column_name = value [, ...] [WHERE ...]
+```
+
+!!! warning "No WHERE = update all rows"
+
+ If you omit the WHERE clause, **all rows** will be updated.
+
+!!! warning "Only constant values in SET"
+
+ The `value` in SET must be a constant (string, number, or boolean). Functions, arithmetic expressions, and column references are **not supported** in SET. They can only be used in SELECT and WHERE.
+
+The same column restrictions and multi-value rules as INSERT apply.
+
+__Example__
+
+```
+UPDATE Contacts SET Adult=true WHERE Age>=18
+```
+
+```
+UPDATE Contacts SET Adult=true, `Age group`="18+" WHERE Age>=18
+```
+
+## DELETE
+
+Deletes one or multiple rows. Works with both normal and big data storage.
+
+### Syntax
+
+```
+DELETE FROM table_name [WHERE ...]
+```
+
+!!! warning "No WHERE = delete all rows"
+
+ If you omit the WHERE clause, **all rows** will be deleted.
+
+__Example__
+
+```
+DELETE FROM Contacts WHERE Age<18
+```
diff --git a/docs/sql/select.md b/docs/sql/select.md
new file mode 100644
index 00000000..a57bad6e
--- /dev/null
+++ b/docs/sql/select.md
@@ -0,0 +1,82 @@
+# SELECT
+
+The `SELECT` statement retrieves an optionally filtered, sorted, and grouped list of rows from a table. Each returned row is a JSON object.
+
+## Syntax
+
+```
+SELECT [Column List] FROM tableName [WHERE ...] [GROUP BY ...] [HAVING ...] [ORDER BY ...] [LIMIT ... OFFSET ...]
+```
+
+`[Column List]` is a comma-separated list of columns. Use `*` to retrieve all columns.
+
+See [Clauses](clauses.md) for details on WHERE, GROUP BY, HAVING, and ORDER BY.
+
+## Limits
+
+Unless you specify a higher limit, the method returns a maximum of **100 rows**. The absolute maximum is **10,000 rows**.
+
+__Example__
+
+```
+SELECT * FROM Table1 LIMIT 10000
+```
+
+Returns the first 10,000 rows.
+
+```
+SELECT * FROM Table1 LIMIT 10000 OFFSET 10000
+```
+
+Returns the next 10,000 rows.
+
+## Column keys vs. column names
+
+By default, returned rows use column **names** as keys (when using `base.query` in Python or JavaScript). The raw API returns column **keys**. This can be controlled with the `convert_keys` parameter.
+
+## JOIN
+
+Since version 4.3, basic implicit join queries are supported:
+
+```
+SELECT ... FROM Table1, Table2 WHERE Table1.column1 = Table2.column2 AND ...
+```
+
+Restrictions:
+
+- Do **not** use the `JOIN` keyword explicitly
+- Only inner join is supported (no left, right, or full join)
+- Tables in the `FROM` clause must be unique
+- Each table must have at least one join condition
+- Join conditions use equality only: `Table1.column1 = Table2.column2`
+- Join conditions must be placed in the `WHERE` clause, connected with `AND`
+- Columns in join conditions must be indexed (unless the table is not archived)
+
+## Field aliases
+
+Field aliases with `AS` are supported:
+
+```
+SELECT table.amount AS a, COUNT(*) FROM Invoices AS i GROUP BY a HAVING a > 100
+```
+
+- Aliases **can** be used in `GROUP BY`, `HAVING`, and `ORDER BY`
+- Aliases **cannot** be used in `WHERE`
+
+## Aggregation functions
+
+When using `GROUP BY`, these aggregation functions are available:
+
+| Function | Description | Example |
+|---|---|---|
+| `COUNT(*)` | Number of rows | `COUNT(*)` |
+| `SUM(col)` | Sum of values | `SUM(Amount)` |
+| `MAX(col)` | Maximum value | `MAX(Amount)` |
+| `MIN(col)` | Minimum value | `MIN(Amount)` |
+| `AVG(col)` | Average of non-empty values | `AVG(Amount)` |
+
+__Example__
+
+```
+SELECT Customer, SUM(Amount) FROM Invoices GROUP BY Customer
+```
diff --git a/docs/tests.md b/docs/tests.md
deleted file mode 100644
index eec349ba..00000000
--- a/docs/tests.md
+++ /dev/null
@@ -1,54 +0,0 @@
-## just to test ...
-
-``` mermaid
-graph LR
- A[Start] --> B{Error?};
- B -->|Yes| C[Hmm...];
- C --> D[Debug];
- D --> B;
- B ---->|No| E[Yay!];
-```
-
-``` mermaid
-gitGraph
- commit
- commit
- branch develop
- checkout develop
- commit
- commit
- checkout main
- merge develop
- commit
- commit
-```
-
-``` mermaid
-graph TD;
- A-->B;
- A-->C;
- B-->D;
- C-->D;
- E-->C;
- F-->E;
- G-->F;
-```
-
-``` mermaid
-pie title Pets adopted by volunteers
- "Dogs" : 386
- "Cats" : 85
- "Rats" : 15
-```
-
-``` mermaid
-gantt
- title A Gantt Diagram
- dateFormat YYYY-MM-DD
- section Section
- A task :a1, 2014-01-01, 4d
- Another task :after a1, 10d
- section Another
- Task in Another :2014-01-12, 2d
- another task :4d
-```
diff --git a/mkdocs.yml b/mkdocs.yml
index be25f27f..f6befdcc 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -56,10 +56,72 @@ plugins:
- git-revision-date-localized
- include-markdown:
rewrite_relative_urls: false
- #- redirects:
- # redirect_maps:
- # 'changelog/server-changelog.md': 'https://seatable.com/changelog/'
- # 'changelog/changelog-for-seatable-professional-server.md': 'https://seatable.com/changelog/'
+ - redirects:
+ redirect_maps:
+ # Old "Scripting" section → new language sections
+ 'scripts/index.md': 'index.md'
+ 'scripts/python/introduction.md': 'python/index.md'
+ 'scripts/python/objects/index.md': 'python/objects/metadata.md'
+ 'scripts/python/objects/metadata.md': 'python/objects/metadata.md'
+ 'scripts/python/objects/context.md': 'python/objects/context.md'
+ 'scripts/python/objects/tables.md': 'python/objects/tables.md'
+ 'scripts/python/objects/views.md': 'python/objects/views.md'
+ 'scripts/python/objects/columns.md': 'python/objects/columns.md'
+ 'scripts/python/objects/rows.md': 'python/objects/rows.md'
+ 'scripts/python/objects/links.md': 'python/objects/links.md'
+ 'scripts/python/objects/files.md': 'python/objects/files.md'
+ 'scripts/python/objects/big_data.md': 'python/objects/rows.md'
+ 'scripts/python/objects/accounts.md': 'python/objects/accounts.md'
+ 'scripts/python/objects/users.md': 'python/objects/users.md'
+ 'scripts/python/objects/date-utils.md': 'python/objects/date-utils.md'
+ 'scripts/python/objects/communication-utils.md': 'python/objects/communication-utils.md'
+ 'scripts/python/common_questions.md': 'python/index.md'
+ 'scripts/python/examples/index.md': 'python/index.md'
+ 'scripts/python/examples/auto-add-rows.md': 'python/index.md'
+ 'scripts/python/examples/calculate-accumulated-value.md': 'python/index.md'
+ 'scripts/python/examples/compute-attendance-statistics.md': 'python/index.md'
+ 'scripts/python/examples/send_email.md': 'python/index.md'
+ 'scripts/python/examples/generate_barcode.md': 'python/index.md'
+ 'scripts/python/examples/generate_qrcode.md': 'python/index.md'
+ 'scripts/python/examples/sync_mysql.md': 'python/index.md'
+ 'scripts/python/examples/update_stock_price.md': 'python/index.md'
+ 'scripts/python/examples/merge_pdf.md': 'python/index.md'
+ 'scripts/python/examples/heic_to_png.md': 'python/index.md'
+ 'scripts/javascript/objects/index.md': 'javascript/index.md'
+ 'scripts/javascript/objects/tables.md': 'javascript/tables.md'
+ 'scripts/javascript/objects/views.md': 'javascript/views.md'
+ 'scripts/javascript/objects/columns.md': 'javascript/columns.md'
+ 'scripts/javascript/objects/rows.md': 'javascript/rows.md'
+ 'scripts/javascript/objects/links.md': 'javascript/links.md'
+ 'scripts/javascript/objects/context.md': 'javascript/scripting-features.md'
+ 'scripts/javascript/objects/utilities.md': 'javascript/scripting-features.md'
+ 'scripts/javascript/objects/output.md': 'javascript/scripting-features.md'
+ 'scripts/javascript/common_questions.md': 'javascript/index.md'
+ 'scripts/javascript/examples/index.md': 'javascript/index.md'
+ 'scripts/javascript/examples/auto-add-rows.md': 'javascript/index.md'
+ 'scripts/javascript/examples/calculate-accumulated-value.md': 'javascript/index.md'
+ 'scripts/javascript/examples/compute-attendance-statistics.md': 'javascript/index.md'
+ 'scripts/sql/introduction.md': 'sql/index.md'
+ 'scripts/sql/functions.md': 'sql/functions.md'
+
+ # Old "Client APIs" section → new language sections
+ 'clients/index.md': 'index.md'
+ 'clients/python_api.md': 'python/index.md'
+ 'clients/javascript/javascript_api.md': 'javascript/index.md'
+ 'clients/javascript/metadata.md': 'javascript/metadata.md'
+ 'clients/javascript/tables.md': 'javascript/tables.md'
+ 'clients/javascript/views.md': 'javascript/views.md'
+ 'clients/javascript/columns.md': 'javascript/columns.md'
+ 'clients/javascript/rows.md': 'javascript/rows.md'
+ 'clients/javascript/links.md': 'javascript/links.md'
+ 'clients/javascript/sql_query.md': 'javascript/sql.md'
+ 'clients/javascript/constants.md': 'javascript/constants.md'
+ 'clients/javascript/examples/file-upload.md': 'javascript/files.md'
+ 'clients/php_api.md': 'php/index.md'
+ 'clients/ruby_api.md': 'ruby/index.md'
+
+ # Old introduction pages
+ 'introduction/coding_for_beginners.md': 'index.md'
# Customization
extra:
@@ -120,82 +182,47 @@ markdown_extensions:
nav:
- Introduction:
- index.md
- - Coding for beginners: introduction/coding_for_beginners.md
- Get support: introduction/get_support.md
- - Scripting:
- - scripts/index.md
- - JavaScript:
- - Objects & Methods:
- - scripts/javascript/objects/index.md
- - Tables: scripts/javascript/objects/tables.md
- - Views: scripts/javascript/objects/views.md
- - Columns: scripts/javascript/objects/columns.md
- - Rows: scripts/javascript/objects/rows.md
- - Links: scripts/javascript/objects/links.md
- - Context: scripts/javascript/objects/context.md
- - Utilities: scripts/javascript/objects/utilities.md
- - Output: scripts/javascript/objects/output.md
- - Examples:
- - scripts/javascript/examples/index.md
- - 1. Add rows: scripts/javascript/examples/auto-add-rows.md
- - 2. Calculate accumulated value: scripts/javascript/examples/calculate-accumulated-value.md
- - 3. Compute attendance statistics: scripts/javascript/examples/compute-attendance-statistics.md
- - Common questions: scripts/javascript/common_questions.md
- - Python:
- - Introduction: scripts/python/introduction.md
- - Objects & Methods:
- - scripts/python/objects/index.md
- - Metadata: scripts/python/objects/metadata.md
- - Context: scripts/python/objects/context.md
- - Tables: scripts/python/objects/tables.md
- - Views: scripts/python/objects/views.md
- - Columns: scripts/python/objects/columns.md
- - Rows: scripts/python/objects/rows.md
- - Links: scripts/python/objects/links.md
- - Files: scripts/python/objects/files.md
- - Big data: scripts/python/objects/big_data.md
- - Accounts: scripts/python/objects/accounts.md
- - Users: scripts/python/objects/users.md
- - Date utilities: scripts/python/objects/date-utils.md
- - Communication utilities: scripts/python/objects/communication-utils.md
- - Examples:
- - scripts/python/examples/index.md
- - 1. Add rows: scripts/python/examples/auto-add-rows.md
- - 2. Calculate accumulated value: scripts/python/examples/calculate-accumulated-value.md
- - 3. Compute attendance statistics: scripts/python/examples/compute-attendance-statistics.md
- - 4. Send emails: scripts/python/examples/send_email.md
- - 5. Generate barcode: scripts/python/examples/generate_barcode.md
- - 6. Generate QR code: scripts/python/examples/generate_qrcode.md
- - 7. Sync MySQL: scripts/python/examples/sync_mysql.md
- - 8. Watch stock price: scripts/python/examples/update_stock_price.md
- - 9. Merge PDF: scripts/python/examples/merge_pdf.md
- - 10. Convert HEIC to PNG: scripts/python/examples/heic_to_png.md
- - Common questions: scripts/python/common_questions.md
- - Query with SQL:
- - Introduction: scripts/sql/introduction.md
- - SQL function reference: scripts/sql/functions.md
+ - Python:
+ - python/index.md
+ - Metadata: python/objects/metadata.md
+ - Context: python/objects/context.md
+ - Tables: python/objects/tables.md
+ - Views: python/objects/views.md
+ - Columns: python/objects/columns.md
+ - Rows: python/objects/rows.md
+ - Links: python/objects/links.md
+ - Files: python/objects/files.md
+ - Accounts: python/objects/accounts.md
+ - Users: python/objects/users.md
+ - Date utilities: python/objects/date-utils.md
+ - Communication utilities: python/objects/communication-utils.md
+ - JavaScript:
+ - javascript/index.md
+ - Tables: javascript/tables.md
+ - Views: javascript/views.md
+ - Columns: javascript/columns.md
+ - Rows: javascript/rows.md
+ - Links: javascript/links.md
+ - SQL Queries: javascript/sql.md
+ - Metadata: javascript/metadata.md
+ - Files: javascript/files.md
+ - Constants: javascript/constants.md
+ - Scripting Features: javascript/scripting-features.md
+ - PHP:
+ - php/index.md
+ - Examples: php/examples.md
+ - API Reference: php/api-reference.md
+ - Ruby: ruby/index.md
+ - SQL:
+ - sql/index.md
+ - SELECT: sql/select.md
+ - INSERT, UPDATE, DELETE: sql/insert-update-delete.md
+ - Clauses: sql/clauses.md
+ - Data Types: sql/data-types.md
+ - Extended Syntax: sql/extended-syntax.md
+ - Functions: sql/functions.md
- Plugin Development:
- plugins/index.md
- Environments: plugins/environments.md
- Available methods: plugins/methods.md
- - Client APIs:
- - clients/index.md
- - JavaScript:
- - Introduction: clients/javascript/javascript_api.md
- - Objects & Methods:
- - Metadata: clients/javascript/metadata.md
- - Tables: clients/javascript/tables.md
- - Views: clients/javascript/views.md
- - Columns: clients/javascript/columns.md
- - Rows: clients/javascript/rows.md
- - Links: clients/javascript/links.md
- - SQL: clients/javascript/sql_query.md
- - Constants: clients/javascript/constants.md
- - Examples:
- - File Upload: clients/javascript/examples/file-upload.md
- - Python: clients/python_api.md
- - PHP: clients/php_api.md
- - Ruby: clients/ruby_api.md
-## open topics:
-# social cards https://squidfunk.github.io/mkdocs-material/setup/setting-up-social-cards/
-# feedback integration: ...