Metadata-Version: 2.2
Name: figlinq
Version: 0.17.0
Summary: Utilities for interfacing with Plotly's Chart Studio and figlinq.com
Home-page: https://github.com/figlinq/figlinq
Author: figlinq.com
Author-email: info@figlinq.com
Maintainer: figlinq.com
Maintainer-email: info@figlinq.com
License: MIT
Project-URL: Github, https://github.com/figlinq/figlinq
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Scientific/Engineering :: Visualization
Description-Content-Type: text/markdown
Requires-Dist: plotly>=5.24.1
Requires-Dist: requests>=2.30.0
Requires-Dist: six
Requires-Dist: cairosvg
Requires-Dist: numpy
Requires-Dist: pandas
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: maintainer
Dynamic: maintainer-email
Dynamic: project-url
Dynamic: requires-dist
Dynamic: summary

# Figlinq Python Library

This package contains utilities for interfacing with Plotly's Chart Studio and Figlinq services. The project has been cloned from [chart-studio 1.1.0](https://pypi.org/project/chart-studio/). The original package has been modified so that credentials and configuration can be set from environment variables (see `figlinq.tools.get_credentials()` and `figlinq.tools.get_config()`). Additional functions have been added to support the Figlinq service. The original package is licensed under the MIT license, and this package is also licensed under the MIT license.

## Installation

```bash
pip install figlinq
```

## Editing figures: figlinq.open_figure

`figlinq.open_figure(fid)` edits a Figlinq figure (a multi-panel page of plots, images and text) from Python. All its methods are coroutines, so use `await` (in a script, `asyncio.run(main())`).

```python
import figlinq

fig = figlinq.open_figure("alice:12")      # the figure's fid
state = await fig.state()                  # read first: page, layers, nodes, selection
await fig.add_content(["alice:12", "alice:15"], layout={"type": "grid", "cols": 2})
await fig.modify_plots(fids=["alice:12"], layout={"title": {"text": "Growth"}}, traceActions=["no_modify"])

async with fig.batch() as b:               # several operations, all or none
    t = b.add_text("A", 10, 10)            # a Ref to the new text's id
    b.align([t], "left")
img = await fig.image()                    # a PNG of the page
```

In a script or a plain Python session every call goes to the server and is stored at once as one revision of the figure. It is guarded by the figure's ETag, so a figure that changed in the meantime raises `VersionConflict`: read `state()` again. Inside the Figlinq web app (Flows, the assistant) with the figure open in the editor, the changes are live in the editor and are not saved until the user saves. `modify_plots` rewrites the plot files at once on both. Errors are subclasses of `figlinq.FigureEditorError` (`figlinq.figure_editor.VersionConflict`, `ReadOnly`, `NotFound`, `PermissionDenied`, ...) with a `.code`. See the docstring of `figlinq.figure_editor` for the full list of operations.

## Reading and editing structures: figlinq.open_structure

`figlinq.open_structure(fid)` reads and edits a Figlinq chemistry file (a molecule or reaction drawn in the structure editor) from Python. A chemistry file is one canvas, so operations have no targets and results have no ids. All methods are coroutines. In a script, set the server and your API key first, for example in the environment (`export FIGLINQ_USERNAME=alice FIGLINQ_API_KEY=... FIGLINQ_API_DOMAIN=https://figlinq.example.org`), or in Python with `figlinq.session.sign_in("alice", "<api key>", plotly_api_domain="https://figlinq.example.org")`.

```python
import asyncio
import figlinq

async def main():
    s = figlinq.open_structure("alice:7")       # the structure file's fid
    state = await s.state()                     # SMILES, InChI, InChIKey, formula, weights, atom and bond counts
    print(state)
    await s.set("c1ccccc1O")                    # SMILES, molfile, rxn or Ketcher JSON (the format is detected)
    await s.add("CC(=O)O")                      # draw a second structure beside it
    img = await s.image(format="png")           # a picture on a white page
    open("structure.png", "wb").write(img.png)  # in the Figlinq app: img.display()

    async with s.batch() as b:                  # several operations, all or none
        b.set("CCO")
        b.layout()
        b.state(["smiles", "ket"])
    print(b.results)

asyncio.run(main())
```

(In a notebook cell or the Figlinq assistant, top-level `await` works and `figlinq.open_structure()` without a fid edits the structure open in the editor.)

`state(include=[...])` can add `ket` (Ketcher JSON, the lossless form), `molfile` (V2000) and `rxn`; a part the engine cannot produce for this structure is `None` and named in `state.unavailable`. In a script every call goes to the server and is stored at once as one revision of the file, guarded by its ETag (`VersionConflict` if it changed in the meantime: read `state()` again). Inside the Figlinq web app with the file open in the editor the changes are live in the editor and not saved until the user saves. A `.mol`, `.rxn` or `.smi` file is never rewritten in another format: a change raises `LossyFormat` unless you pass `save_as="ket"` (server only), which creates a new `.ket` file beside the original (`result.created` has its `fid`). Errors are subclasses of `figlinq.StructureEditorError` (`figlinq.structure_editor.VersionConflict`, `InvalidStructure`, `NoEditor`, `Rejected` with `failed_index` and `results_so_far`, ...) with a `.code`.

## Gating cytometry: figlinq.open_cytometry

`figlinq.open_cytometry(fid)` reads and gates a Figlinq flow-cytometry analysis from Python, in a script with an API key as well as in the app. All its methods are coroutines. In a script, set the server and your API key first, for example in the environment (`export FIGLINQ_USERNAME=alice FIGLINQ_API_KEY=... FIGLINQ_API_DOMAIN=https://figlinq.example.org`), or in Python with `figlinq.session.sign_in("alice", "<api key>", plotly_api_domain="https://figlinq.example.org")`.

```python
import asyncio
import figlinq

async def main():
    c = figlinq.open_cytometry("alice:12")      # the analysis's fid
    state = await c.state()                     # populations with their counts; read first
    print(state)
    img = await c.image(x="FSC-A", y="SSC-A")   # a PNG drawn by the server
    open("plot.png", "wb").write(img.png)       # in the Figlinq app: display(img)
    async with c.batch(rationale="Lymphocytes sit in the dense scatter cloud") as b:
        g = b.rectangle(("FSC-A", "linear"), "SSC-A", 50000, 120000, 5000, 60000, name="Cells")
        b.range("CD3", low=1.5, parent=g, name="T cells")
    print(b.results)

asyncio.run(main())
```

(In a notebook cell or the Figlinq assistant, top-level `await` works and `display(img)` shows the picture; in a plain script `display()` only prints a description, so save `img.png`.)

Every change is one atomic batch, stored at once as one revision of the analysis and guarded by its ETag (a changed analysis raises `Stale`: read `state()` again before the next change). Inside the app, an editor with unsaved edits refuses the change (`UnsavedChanges`) and otherwise reloads after it. Exports (`export_plot`, `export_figure`, `export_stats`) make Figlinq plot, figure and grid files from the analysis; they change nothing in it, need only read access and are not stopped by unsaved edits. Errors are subclasses of `figlinq.CytometryError` with a `.code` (and `.reason`, the server's own code such as `CYTOMETRY_EXPORT_MODIFIED`). See the docstring of `figlinq.cytometry_editor`.

## Reading and editing text: figlinq.open_file

`figlinq.open_file(fid)` reads and changes the text of a Figlinq text file (an HTML text, a stlite app, a plaintext external file such as `.md`, `.py` or `.json`) and the searchable description (`context`) of any file, from Python, in a script with an API key as well as in the app. All its methods are coroutines. In a script, set the server and your API key first, for example in the environment (`export FIGLINQ_USERNAME=alice FIGLINQ_API_KEY=... FIGLINQ_API_DOMAIN=https://figlinq.example.org`), or in Python with `figlinq.session.sign_in("alice", "<api key>", plotly_api_domain="https://figlinq.example.org")`.

```python
import asyncio
import figlinq

async def main():
    f = figlinq.open_file("alice:12")           # the file's fid
    print(await f.state())                      # type, line counts, size
    print(await f.read(start=1, end=40))        # numbered lines; read first, then change
    await f.find_replace("Old title", "New title")
    await f.replace_lines(5, 7, "new text\n")   # lines are 1-indexed and inclusive
    await f.set_context("A short description that search will find")
    async with f.batch(reason="Tidy the heading") as b:   # several operations, all or none
        b.find_replace("a", "b")
        b.insert_lines(0, "# Notes\n")
    print(b.results)

asyncio.run(main())
```

(In a notebook cell or the Figlinq assistant, top-level `await` works.) A change to a file on the server is stored at once as one revision and guarded by the file's ETag (a changed file raises `VersionConflict`: call `state()` or `read()` again before the next change). A `.json` file must stay valid JSON (`InvalidContent`), the text is at most 5 MB, and a read returns at most 20,000 characters (`truncated`, `next_line`). Structured files have their own function; the server names it when you open one here. Inside the app, `figlinq.open_file()` without a fid means the file open in the text editor: changes are live there and not saved until the user saves, and only what that editor can do exists (`find_replace`, `replace_between`, `insert_at_cursor`, `replace_selection`; the others raise `EditorOnly`). Errors are subclasses of `figlinq.FileEditorError` with a `.code`. See the docstring of `figlinq.file_editor`.
