Working with Entries#

Entries are the fundamental building blocks of a LabArchives page. They contain the actual data, such as rich text, plain text, headers, file attachments, and widgets.

Accessing Entries#

You can access the entries of a NotebookPage through its entries property. This property returns an Entries object, which behaves like a sequence of entries.

page = notebook.traverse("Project A/Experiment 1/Results")

# Get the number of entries
print(len(page.entries))

# Iterate over entries
for entry in page.entries:
    print(f"ID: {entry.id}, Type: {entry.content_type}")

# Access an entry by index
first_entry = page.entries[0]

Entry Types#

LabArchives supports several entry types. In labapi, each type is represented by a specific class inheriting from Entry.

Text-based Entries#

Text-based entries store their content as strings.

  • Rich Text Entry (TextEntry): Used for formatted text, typically HTML. Content type: "text entry". See MDN HTML documentation for supported markup patterns.

  • Plain Text Entry (PlainTextEntry): Used for unformatted, raw text. Content type: "plain text entry".

  • Header Entry (HeaderEntry): Used for headings or titles within a page. Content type: "heading".

# Accessing text content
text_entry = page.entries[0]
print(text_entry.content)

# Updating rich text content
text_entry.content = "<p>Updated <strong>rich text</strong> content</p>"

Attachment Entries#

Attachment entries (AttachmentEntry) represent file attachments. Their content is an Attachment object.

attachment_entry = page.entries[1]
attachment = attachment_entry.content

print(f"Filename: {attachment.filename}")
print(f"MIME Type: {attachment.mime_type}")
print(f"Caption: {attachment.caption}")

# Read the file data
data = attachment.read()

Widget Entries#

Widget entries (WidgetEntry) embed interactive content or external applications. Like text entries, their content is represented as a string (often JSON or HTML).

Note

Widget entries are currently read-only in labapi. Their content property returns the widget’s internal data as a JSON-formatted string.

If LabArchives returns an entry type that labapi does not model yet, entries wraps it as UnknownEntry and emits a warning instead of silently dropping it.

Creating New Entries#

You can create new entries using the create() method.

from labapi import TextEntry, HeaderEntry, PlainTextEntry

# Create a rich text entry (HTML is rendered in LabArchives)
page.entries.create(TextEntry, "<h2>New Section</h2><p>Some <em>formatted</em> content...</p>")

# Create a heading (displayed as a section label/divider)
page.entries.create(HeaderEntry, "Experiment Notes")

# Create a plain text entry (displayed literally)
page.entries.create(PlainTextEntry, "<h2>Raw instrument output</h2>")

Creating Attachments#

To create an attachment entry, you first need to create an Attachment object.

from io import BytesIO
from labapi import Attachment, AttachmentEntry

# Create attachment from in-memory data
data = BytesIO(b"Hello, LabArchives!")
attachment = Attachment(data, "text/plain", "hello.txt", "A simple text file")

# Upload as a new entry
page.entries.create(AttachmentEntry, attachment)

You can also create an attachment directly from a file on disk:

from labapi import Attachment, AttachmentEntry

with open("data.csv", "rb") as f:
    attachment = Attachment.from_file(f)
    page.entries.create(AttachmentEntry, attachment)

Working with JSON Data#

A common pattern is to store structured data as a JSON attachment with a formatted preview in a companion text entry. The create_json_entry() method simplifies this.

data = {
    "experiment_id": "EXP-123",
    "results": [1.2, 3.4, 5.6],
    "status": "complete"
}

# Creates both an attachment entry and a text entry preview
file_entry, text_entry = page.entries.create_json_entry(data)

Modifying Entries#

To modify an entry, simply set its content property. The change is automatically synchronized with the LabArchives server.

entry = page.entries[0]
entry.content = "New content for the entry"

For attachments, setting the content property with a new Attachment object will update the file.

with open("updated_data.csv", "rb") as f:
    new_attachment = Attachment.from_file(f)
    attachment_entry.content = new_attachment