sea-serpent


Namesea-serpent JSON
Version 0.4.1 PyPI version JSON
download
home_pagehttps://github.com/schlegelp/sea-serpent
SummaryDataframe-like wrapper for SeaTable API.
upload_time2024-12-10 16:51:06
maintainerNone
docs_urlNone
authorPhilipp Schlegel
requires_python>=3.6
licenseGNU GPL V3
keywords seatable api interface dataframe
VCS
bugtrack_url
requirements pandas seatable-api numpy tqdm requests PyJWT
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # sea-serpent
A dataframe-like wrapper around the [SeaTable](https://seatable.io/en/) API.

This library tries to make interacting with SeaTables as easy as if you were
working with a local pandas DataFrame.

## Install

From PyPI:

```bash
pip3 install sea-serpent
```

Bleeding edge from Github:

```bash
pip3 install git+https://github.com/schlegelp/sea-serpent@main
```

## Examples

### Getting your API (auth) token

```python
>>> import seaserpent as ss
>>> ss.get_auth_token(username='USER',
...                   password='PASSWORD',
...                   server='https://cloud.seatable.io')
{'token': 'somelongassstring1234567@£$^@£$^£'}
```

For future use, set your default server and auth token as `SEATABLE_SERVER` and
`SEATABLE_TOKEN` environment variable, respectively.

### Initializing a table

`Table` works as connection to a single SeaTable table. If its name is unique,
you can initialize the connection with just the name:

```python
>>> import seaserpent as ss
>>> # Initialize the table
>>> # (if there are multiple tables with this name you need to provide the base too)
>>> table = ss.Table(table='MyTable')
>>> table
SeaTable <"MyTable", 10 rows, 2 columns>
>>> # Inspect the first couple rows
>>> table.head()
    column1     labels
0         1          A
1         2          B
2         3          C
```

### Fetching data

The `Table` itself doesn't download any of the data. Reading the data works
via an interface similar to `pandas.DataFrames`:

```python
>>> # Fetching a column returns a promise
>>> c = table['column1']  # this works too: c = table.column1
>>> c
Column <column="column1", table="LH_bodies", datatype=number>
>>> # To get the values
>>> c.values
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> # Filters are automatically translated into SQL query
>>> table.loc[table.column1 >= 7]
    column1     labels
0         7          H
1         8          I
2         9          J
>>> table.loc[table.labels.isin(['D', 'E']) ]
    column1     labels
0         4          D
1         5          E
>>> # Download the whole table as pandas DataFrame
>>> df = table.to_frame()
```

### Adding a column

```python
>>> # First we need to re-initialize the table with write access
>>> table = ss.Table(table='MyTable', read_only=False)
>>> table.add_column(col_name='checked', col_type=bool)
>>> # The column will be empty
>>> table.head()
    column1     labels   checked
0         1          A      None
1         2          B      None
2         3          C      None
```

### Pushing data to table

```python
>>> # Overwrite the whole column
>>> table['checked'] = False
>>> table.head()
    column1     labels   checked
0         1          A     False
1         2          B     False
2         3          C     False
>>> # Alternatively pass a list of values
>>> table['checked'] = [False, True, False]
>>> table.head()
    column1     labels   checked
0         1          A     False
1         2          B      True
2         3          C     False
>>> # Write to a subset of the column
>>> table.loc[:2, 'checked'] = False
>>> table.loc[table.labels == 'C', 'checked'] = True
>>> table.head()
    column1     labels   checked
0         1          A     False
1         2          B     False
2         3          C      True
>>> # To write only changed values to the table
>>> # (faster & better for logs)
>>> values = table.checked.values
>>> values[0:2] = True  # Change only two values
>>> table.checked.update(values)
```

### Deleting a column

```python
>>> table['checked'].delete()
>>> table.head()
    column1     labels
0         1          A
1         2          B
2         3          C
>>> # Alternatively you can also clear an entire column
>>> table.checked.clear()
>>> table.head()
    column1     labels   checked
0         1          A      None
1         2          B      None
2         3          C      None
```

### Creating a new table

Empty table:

```python
>>> table = ss.Table.new(table_name='MyNewTable', base='MyBase')
```

From pandas DataFrame:

```python
>>> table = ss.Table.from_frame(df, table_name='MyNewTable', base='MyBase')
```

### Linking tables

Create links:

```python
>>> table.link(other_table='OtherTable',    # name of the other table (must be same base)
...            link_on='Column1',           # column in this table to link on
...            link_on_other='ColumnA',     # column in other table to link on
...            link_col='OtherTableLinks')  # name of column to store links in
```

Create column that pulls data from linked table:

```python
>>> table.add_linked_column(col_name='LinkedData',      # name of new column
...                         link_col='OtherTableLinks', # column with link(s) to other table
...                         link_on='some_value',       # which column in other table to link to
...                         formula='lookup')           # how to aggregate data (lookup, mean, max, etc)
```

## Random notes, limitations & oddities

1. For convenience and ease of access we're using names to identify tables,
   columns and bases. Hence you should avoid duplicate names if at all possible.
2. 64 bit integers/floats are truncated when writing to a table. I suspect this
   happens on the server side when decoding the JSON payload because manually
   entering large numbers through the web interface works perfectly well
   (copy-pasting still fails though). Hence, `seaserpent` quietly downcasts 64
   bit to 32 bit if possible and failing that converts to strings before uploading.
3. The web interface appears to only show floats up to the 8th decimal. In the
   database the precision must be higher though because I have successfully
   written 1e-128 floats.
4. Infinite values (i.e. `np.inf`) raise an error when trying to write.
5. Cells manually cleared through the UI return empty strings (``''``). By
   default, ``sea-serpent`` will convert these to ``None`` where possible.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/schlegelp/sea-serpent",
    "name": "sea-serpent",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": null,
    "keywords": "SeaTable API interface dataframe",
    "author": "Philipp Schlegel",
    "author_email": "pms70@cam.ac.uk",
    "download_url": "https://files.pythonhosted.org/packages/86/69/096c4eb5884fb81e82d0876d9bca9d816e92da13b6c2cf8b72577ace6656/sea_serpent-0.4.1.tar.gz",
    "platform": null,
    "description": "# sea-serpent\nA dataframe-like wrapper around the [SeaTable](https://seatable.io/en/) API.\n\nThis library tries to make interacting with SeaTables as easy as if you were\nworking with a local pandas DataFrame.\n\n## Install\n\nFrom PyPI:\n\n```bash\npip3 install sea-serpent\n```\n\nBleeding edge from Github:\n\n```bash\npip3 install git+https://github.com/schlegelp/sea-serpent@main\n```\n\n## Examples\n\n### Getting your API (auth) token\n\n```python\n>>> import seaserpent as ss\n>>> ss.get_auth_token(username='USER',\n...                   password='PASSWORD',\n...                   server='https://cloud.seatable.io')\n{'token': 'somelongassstring1234567@\u00a3$^@\u00a3$^\u00a3'}\n```\n\nFor future use, set your default server and auth token as `SEATABLE_SERVER` and\n`SEATABLE_TOKEN` environment variable, respectively.\n\n### Initializing a table\n\n`Table` works as connection to a single SeaTable table. If its name is unique,\nyou can initialize the connection with just the name:\n\n```python\n>>> import seaserpent as ss\n>>> # Initialize the table\n>>> # (if there are multiple tables with this name you need to provide the base too)\n>>> table = ss.Table(table='MyTable')\n>>> table\nSeaTable <\"MyTable\", 10 rows, 2 columns>\n>>> # Inspect the first couple rows\n>>> table.head()\n    column1     labels\n0         1          A\n1         2          B\n2         3          C\n```\n\n### Fetching data\n\nThe `Table` itself doesn't download any of the data. Reading the data works\nvia an interface similar to `pandas.DataFrames`:\n\n```python\n>>> # Fetching a column returns a promise\n>>> c = table['column1']  # this works too: c = table.column1\n>>> c\nColumn <column=\"column1\", table=\"LH_bodies\", datatype=number>\n>>> # To get the values\n>>> c.values\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n>>> # Filters are automatically translated into SQL query\n>>> table.loc[table.column1 >= 7]\n    column1     labels\n0         7          H\n1         8          I\n2         9          J\n>>> table.loc[table.labels.isin(['D', 'E']) ]\n    column1     labels\n0         4          D\n1         5          E\n>>> # Download the whole table as pandas DataFrame\n>>> df = table.to_frame()\n```\n\n### Adding a column\n\n```python\n>>> # First we need to re-initialize the table with write access\n>>> table = ss.Table(table='MyTable', read_only=False)\n>>> table.add_column(col_name='checked', col_type=bool)\n>>> # The column will be empty\n>>> table.head()\n    column1     labels   checked\n0         1          A      None\n1         2          B      None\n2         3          C      None\n```\n\n### Pushing data to table\n\n```python\n>>> # Overwrite the whole column\n>>> table['checked'] = False\n>>> table.head()\n    column1     labels   checked\n0         1          A     False\n1         2          B     False\n2         3          C     False\n>>> # Alternatively pass a list of values\n>>> table['checked'] = [False, True, False]\n>>> table.head()\n    column1     labels   checked\n0         1          A     False\n1         2          B      True\n2         3          C     False\n>>> # Write to a subset of the column\n>>> table.loc[:2, 'checked'] = False\n>>> table.loc[table.labels == 'C', 'checked'] = True\n>>> table.head()\n    column1     labels   checked\n0         1          A     False\n1         2          B     False\n2         3          C      True\n>>> # To write only changed values to the table\n>>> # (faster & better for logs)\n>>> values = table.checked.values\n>>> values[0:2] = True  # Change only two values\n>>> table.checked.update(values)\n```\n\n### Deleting a column\n\n```python\n>>> table['checked'].delete()\n>>> table.head()\n    column1     labels\n0         1          A\n1         2          B\n2         3          C\n>>> # Alternatively you can also clear an entire column\n>>> table.checked.clear()\n>>> table.head()\n    column1     labels   checked\n0         1          A      None\n1         2          B      None\n2         3          C      None\n```\n\n### Creating a new table\n\nEmpty table:\n\n```python\n>>> table = ss.Table.new(table_name='MyNewTable', base='MyBase')\n```\n\nFrom pandas DataFrame:\n\n```python\n>>> table = ss.Table.from_frame(df, table_name='MyNewTable', base='MyBase')\n```\n\n### Linking tables\n\nCreate links:\n\n```python\n>>> table.link(other_table='OtherTable',    # name of the other table (must be same base)\n...            link_on='Column1',           # column in this table to link on\n...            link_on_other='ColumnA',     # column in other table to link on\n...            link_col='OtherTableLinks')  # name of column to store links in\n```\n\nCreate column that pulls data from linked table:\n\n```python\n>>> table.add_linked_column(col_name='LinkedData',      # name of new column\n...                         link_col='OtherTableLinks', # column with link(s) to other table\n...                         link_on='some_value',       # which column in other table to link to\n...                         formula='lookup')           # how to aggregate data (lookup, mean, max, etc)\n```\n\n## Random notes, limitations & oddities\n\n1. For convenience and ease of access we're using names to identify tables,\n   columns and bases. Hence you should avoid duplicate names if at all possible.\n2. 64 bit integers/floats are truncated when writing to a table. I suspect this\n   happens on the server side when decoding the JSON payload because manually\n   entering large numbers through the web interface works perfectly well\n   (copy-pasting still fails though). Hence, `seaserpent` quietly downcasts 64\n   bit to 32 bit if possible and failing that converts to strings before uploading.\n3. The web interface appears to only show floats up to the 8th decimal. In the\n   database the precision must be higher though because I have successfully\n   written 1e-128 floats.\n4. Infinite values (i.e. `np.inf`) raise an error when trying to write.\n5. Cells manually cleared through the UI return empty strings (``''``). By\n   default, ``sea-serpent`` will convert these to ``None`` where possible.\n",
    "bugtrack_url": null,
    "license": "GNU GPL V3",
    "summary": "Dataframe-like wrapper for SeaTable API.",
    "version": "0.4.1",
    "project_urls": {
        "Homepage": "https://github.com/schlegelp/sea-serpent",
        "Source": "https://github.com/schlegelp/sea-serpent"
    },
    "split_keywords": [
        "seatable",
        "api",
        "interface",
        "dataframe"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2ffaea6261598296a7d744f48f1dea2e4b1ce5a6239fea3c5f48c8a4b3f4f6c2",
                "md5": "2dea8663a7cb9eaa894aa9fa8aef3aa8",
                "sha256": "ecab929392af4003d792e3999d8097ca61f28adae34ed8890893f1a1a5b89920"
            },
            "downloads": -1,
            "filename": "sea_serpent-0.4.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2dea8663a7cb9eaa894aa9fa8aef3aa8",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6",
            "size": 45918,
            "upload_time": "2024-12-10T16:51:03",
            "upload_time_iso_8601": "2024-12-10T16:51:03.916138Z",
            "url": "https://files.pythonhosted.org/packages/2f/fa/ea6261598296a7d744f48f1dea2e4b1ce5a6239fea3c5f48c8a4b3f4f6c2/sea_serpent-0.4.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8669096c4eb5884fb81e82d0876d9bca9d816e92da13b6c2cf8b72577ace6656",
                "md5": "a10261a44a4434c5b8f79e8d832b0fd3",
                "sha256": "5979f9121cc42d1ca75040540a8a9644ad5ac26f07f056e637ea45d1a0d26b19"
            },
            "downloads": -1,
            "filename": "sea_serpent-0.4.1.tar.gz",
            "has_sig": false,
            "md5_digest": "a10261a44a4434c5b8f79e8d832b0fd3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 44440,
            "upload_time": "2024-12-10T16:51:06",
            "upload_time_iso_8601": "2024-12-10T16:51:06.329215Z",
            "url": "https://files.pythonhosted.org/packages/86/69/096c4eb5884fb81e82d0876d9bca9d816e92da13b6c2cf8b72577ace6656/sea_serpent-0.4.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-10 16:51:06",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "schlegelp",
    "github_project": "sea-serpent",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "pandas",
            "specs": []
        },
        {
            "name": "seatable-api",
            "specs": []
        },
        {
            "name": "numpy",
            "specs": []
        },
        {
            "name": "tqdm",
            "specs": []
        },
        {
            "name": "requests",
            "specs": []
        },
        {
            "name": "PyJWT",
            "specs": []
        }
    ],
    "lcname": "sea-serpent"
}
        
Elapsed time: 0.38378s