Working with Entries#
A LabArchives page contains entries for rich text, plain text, headers, file attachments, and widgets.
Accessing Entries#
A NotebookPage exposes entries, an Entries sequence.
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]
Searching Entries#
Use search() to search entries in a
notebook. It returns an EntrySearch, a lazy,
iterable sequence of result pages — no API request is made until you iterate
it or fetch a specific page. Each page contains the same entry classes used by
page.entries.
from labapi import TextEntry
for result_page in notebook.search('"brown rat"', page_size=100):
for entry in result_page.entries:
if isinstance(entry, TextEntry):
entry.content = entry.content.replace(
"brown rat",
"Rattus norvegicus",
)
Iteration fetches consecutive pages until every match has been returned, and yields nothing when there are no matches.
Each result page is an EntrySearchPage that
carries the matched entries along with result metadata. You can also fetch a
single page on demand with page()
instead of iterating:
search = notebook.search("Rattus norvegicus") # page_size defaults to 25
first_page = search.page(0) # page numbers are zero-based
print(f"{first_page.total_found} matching entries in the notebook")
print(f"{first_page.total_returned} entries on this page")
page() raises IndexError for
negative or out-of-range page numbers, and
search() raises ValueError if
page_size is not positive.
The search request itself is read-only. Returned entries use the same
content accessors and setters as entries loaded from page.entries.
Entry Types#
LabArchives supports several entry types. In labapi, each type is represented by a 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 HTML syntax.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.
If LabArchives returns an entry type that labapi does not model,
entries wraps unrecognized part types as
UnknownEntry and
recognized-but-unimplemented types as
UnimplementedEntry, each with a warning,
instead of silently dropping them.
Creating New Entries#
Call create() to add an entry to a page.
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#
Create an Attachment object, then pass it to create().
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
attachment = Attachment.from_file("data.csv")
page.entries.create(AttachmentEntry, attachment)
Attachment.from_file() accepts a filesystem path (string or Path) or a
random-access binary file object.
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. create_json_entry() creates both in one call.
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#
Assigning to entry.content sends an update request to LabArchives. Unknown, unimplemented, and widget entries do not support updates and raise NotImplementedError.
entry = page.entries[0]
entry.content = "New content for the entry"
For attachments, assign a new Attachment to attachment_entry.content to replace the uploaded file.
new_attachment = Attachment.from_file("updated_data.csv")
attachment_entry.content = new_attachment