Initial commit
This commit is contained in:
262
skills/labarchive-integration/SKILL.md
Normal file
262
skills/labarchive-integration/SKILL.md
Normal file
@@ -0,0 +1,262 @@
|
||||
---
|
||||
name: labarchive-integration
|
||||
description: "Electronic lab notebook API integration. Access notebooks, manage entries/attachments, backup notebooks, integrate with Protocols.io/Jupyter/REDCap, for programmatic ELN workflows."
|
||||
---
|
||||
|
||||
# LabArchives Integration
|
||||
|
||||
## Overview
|
||||
|
||||
LabArchives is an electronic lab notebook platform for research documentation and data management. Access notebooks, manage entries and attachments, generate reports, and integrate with third-party tools programmatically via REST API.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
This skill should be used when:
|
||||
- Working with LabArchives REST API for notebook automation
|
||||
- Backing up notebooks programmatically
|
||||
- Creating or managing notebook entries and attachments
|
||||
- Generating site reports and analytics
|
||||
- Integrating LabArchives with third-party tools (Protocols.io, Jupyter, REDCap)
|
||||
- Automating data upload to electronic lab notebooks
|
||||
- Managing user access and permissions programmatically
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. Authentication and Configuration
|
||||
|
||||
Set up API access credentials and regional endpoints for LabArchives API integration.
|
||||
|
||||
**Prerequisites:**
|
||||
- Enterprise LabArchives license with API access enabled
|
||||
- API access key ID and password from LabArchives administrator
|
||||
- User authentication credentials (email and external applications password)
|
||||
|
||||
**Configuration setup:**
|
||||
|
||||
Use the `scripts/setup_config.py` script to create a configuration file:
|
||||
|
||||
```bash
|
||||
python3 scripts/setup_config.py
|
||||
```
|
||||
|
||||
This creates a `config.yaml` file with the following structure:
|
||||
|
||||
```yaml
|
||||
api_url: https://api.labarchives.com/api # or regional endpoint
|
||||
access_key_id: YOUR_ACCESS_KEY_ID
|
||||
access_password: YOUR_ACCESS_PASSWORD
|
||||
```
|
||||
|
||||
**Regional API endpoints:**
|
||||
- US/International: `https://api.labarchives.com/api`
|
||||
- Australia: `https://auapi.labarchives.com/api`
|
||||
- UK: `https://ukapi.labarchives.com/api`
|
||||
|
||||
For detailed authentication instructions and troubleshooting, refer to `references/authentication_guide.md`.
|
||||
|
||||
### 2. User Information Retrieval
|
||||
|
||||
Obtain user ID (UID) and access information required for subsequent API operations.
|
||||
|
||||
**Workflow:**
|
||||
|
||||
1. Call the `users/user_access_info` API method with login credentials
|
||||
2. Parse the XML/JSON response to extract the user ID (UID)
|
||||
3. Use the UID to retrieve detailed user information via `users/user_info_via_id`
|
||||
|
||||
**Example using Python wrapper:**
|
||||
|
||||
```python
|
||||
from labarchivespy.client import Client
|
||||
|
||||
# Initialize client
|
||||
client = Client(api_url, access_key_id, access_password)
|
||||
|
||||
# Get user access info
|
||||
login_params = {'login_or_email': user_email, 'password': auth_token}
|
||||
response = client.make_call('users', 'user_access_info', params=login_params)
|
||||
|
||||
# Extract UID from response
|
||||
import xml.etree.ElementTree as ET
|
||||
uid = ET.fromstring(response.content)[0].text
|
||||
|
||||
# Get detailed user info
|
||||
params = {'uid': uid}
|
||||
user_info = client.make_call('users', 'user_info_via_id', params=params)
|
||||
```
|
||||
|
||||
### 3. Notebook Operations
|
||||
|
||||
Manage notebook access, backup, and metadata retrieval.
|
||||
|
||||
**Key operations:**
|
||||
|
||||
- **List notebooks:** Retrieve all notebooks accessible to a user
|
||||
- **Backup notebooks:** Download complete notebook data with optional attachment inclusion
|
||||
- **Get notebook IDs:** Retrieve institution-defined notebook identifiers for integration with grants/project management systems
|
||||
- **Get notebook members:** List all users with access to a specific notebook
|
||||
- **Get notebook settings:** Retrieve configuration and permissions for notebooks
|
||||
|
||||
**Notebook backup example:**
|
||||
|
||||
Use the `scripts/notebook_operations.py` script:
|
||||
|
||||
```bash
|
||||
# Backup with attachments (default, creates 7z archive)
|
||||
python3 scripts/notebook_operations.py backup --uid USER_ID --nbid NOTEBOOK_ID
|
||||
|
||||
# Backup without attachments, JSON format
|
||||
python3 scripts/notebook_operations.py backup --uid USER_ID --nbid NOTEBOOK_ID --json --no-attachments
|
||||
```
|
||||
|
||||
**API endpoint format:**
|
||||
```
|
||||
https://<api_url>/notebooks/notebook_backup?uid=<UID>&nbid=<NOTEBOOK_ID>&json=true&no_attachments=false
|
||||
```
|
||||
|
||||
For comprehensive API method documentation, refer to `references/api_reference.md`.
|
||||
|
||||
### 4. Entry and Attachment Management
|
||||
|
||||
Create, modify, and manage notebook entries and file attachments.
|
||||
|
||||
**Entry operations:**
|
||||
- Create new entries in notebooks
|
||||
- Add comments to existing entries
|
||||
- Create entry parts/components
|
||||
- Upload file attachments to entries
|
||||
|
||||
**Attachment workflow:**
|
||||
|
||||
Use the `scripts/entry_operations.py` script:
|
||||
|
||||
```bash
|
||||
# Upload attachment to an entry
|
||||
python3 scripts/entry_operations.py upload --uid USER_ID --nbid NOTEBOOK_ID --entry-id ENTRY_ID --file /path/to/file.pdf
|
||||
|
||||
# Create a new entry with text content
|
||||
python3 scripts/entry_operations.py create --uid USER_ID --nbid NOTEBOOK_ID --title "Experiment Results" --content "Results from today's experiment..."
|
||||
```
|
||||
|
||||
**Supported file types:**
|
||||
- Documents (PDF, DOCX, TXT)
|
||||
- Images (PNG, JPG, TIFF)
|
||||
- Data files (CSV, XLSX, HDF5)
|
||||
- Scientific formats (CIF, MOL, PDB)
|
||||
- Archives (ZIP, 7Z)
|
||||
|
||||
### 5. Site Reports and Analytics
|
||||
|
||||
Generate institutional reports on notebook usage, activity, and compliance (Enterprise feature).
|
||||
|
||||
**Available reports:**
|
||||
- Detailed Usage Report: User activity metrics and engagement statistics
|
||||
- Detailed Notebook Report: Notebook metadata, member lists, and settings
|
||||
- PDF/Offline Notebook Generation Report: Export tracking for compliance
|
||||
- Notebook Members Report: Access control and collaboration analytics
|
||||
- Notebook Settings Report: Configuration and permission auditing
|
||||
|
||||
**Report generation:**
|
||||
|
||||
```python
|
||||
# Generate detailed usage report
|
||||
response = client.make_call('site_reports', 'detailed_usage_report',
|
||||
params={'start_date': '2025-01-01', 'end_date': '2025-10-20'})
|
||||
```
|
||||
|
||||
### 6. Third-Party Integrations
|
||||
|
||||
LabArchives integrates with numerous scientific software platforms. This skill provides guidance on leveraging these integrations programmatically.
|
||||
|
||||
**Supported integrations:**
|
||||
- **Protocols.io:** Export protocols directly to LabArchives notebooks
|
||||
- **GraphPad Prism:** Export analyses and figures (Version 8+)
|
||||
- **SnapGene:** Direct molecular biology workflow integration
|
||||
- **Geneious:** Bioinformatics analysis export
|
||||
- **Jupyter:** Embed Jupyter notebooks as entries
|
||||
- **REDCap:** Clinical data capture integration
|
||||
- **Qeios:** Research publishing platform
|
||||
- **SciSpace:** Literature management
|
||||
|
||||
**OAuth authentication:**
|
||||
LabArchives now uses OAuth for all new integrations. Legacy integrations may use API key authentication.
|
||||
|
||||
For detailed integration setup instructions and use cases, refer to `references/integrations.md`.
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Complete notebook backup workflow
|
||||
|
||||
1. Authenticate and obtain user ID
|
||||
2. List all accessible notebooks
|
||||
3. Iterate through notebooks and backup each one
|
||||
4. Store backups with timestamp metadata
|
||||
|
||||
```bash
|
||||
# Complete backup script
|
||||
python3 scripts/notebook_operations.py backup-all --email user@example.edu --password AUTH_TOKEN
|
||||
```
|
||||
|
||||
### Automated data upload workflow
|
||||
|
||||
1. Authenticate with LabArchives API
|
||||
2. Identify target notebook and entry
|
||||
3. Upload experimental data files
|
||||
4. Add metadata comments to entries
|
||||
5. Generate activity report
|
||||
|
||||
### Integration workflow example (Jupyter → LabArchives)
|
||||
|
||||
1. Export Jupyter notebook to HTML or PDF
|
||||
2. Use entry_operations.py to upload to LabArchives
|
||||
3. Add comment with execution timestamp and environment info
|
||||
4. Tag entry for easy retrieval
|
||||
|
||||
## Python Package Installation
|
||||
|
||||
Install the `labarchives-py` wrapper for simplified API access:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/mcmero/labarchives-py
|
||||
cd labarchives-py
|
||||
uv pip install .
|
||||
```
|
||||
|
||||
Alternatively, use direct HTTP requests via Python's `requests` library for custom implementations.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Rate limiting:** Implement appropriate delays between API calls to avoid throttling
|
||||
2. **Error handling:** Always wrap API calls in try-except blocks with appropriate logging
|
||||
3. **Authentication security:** Store credentials in environment variables or secure config files (never in code)
|
||||
4. **Backup verification:** After notebook backup, verify file integrity and completeness
|
||||
5. **Incremental operations:** For large notebooks, use pagination and batch processing
|
||||
6. **Regional endpoints:** Use the correct regional API endpoint for optimal performance
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Common issues:**
|
||||
|
||||
- **401 Unauthorized:** Verify access key ID and password are correct; check API access is enabled for your account
|
||||
- **404 Not Found:** Confirm notebook ID (nbid) exists and user has access permissions
|
||||
- **403 Forbidden:** Check user permissions for the requested operation
|
||||
- **Empty response:** Ensure required parameters (uid, nbid) are provided correctly
|
||||
- **Attachment upload failures:** Verify file size limits and format compatibility
|
||||
|
||||
For additional support, contact LabArchives at support@labarchives.com.
|
||||
|
||||
## Resources
|
||||
|
||||
This skill includes bundled resources to support LabArchives API integration:
|
||||
|
||||
### scripts/
|
||||
|
||||
- `setup_config.py`: Interactive configuration file generator for API credentials
|
||||
- `notebook_operations.py`: Utilities for listing, backing up, and managing notebooks
|
||||
- `entry_operations.py`: Tools for creating entries and uploading attachments
|
||||
|
||||
### references/
|
||||
|
||||
- `api_reference.md`: Comprehensive API endpoint documentation with parameters and examples
|
||||
- `authentication_guide.md`: Detailed authentication setup and configuration instructions
|
||||
- `integrations.md`: Third-party integration setup guides and use cases
|
||||
342
skills/labarchive-integration/references/api_reference.md
Normal file
342
skills/labarchive-integration/references/api_reference.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# LabArchives API Reference
|
||||
|
||||
## API Structure
|
||||
|
||||
All LabArchives API calls follow this URL pattern:
|
||||
|
||||
```
|
||||
https://<base_url>/api/<api_class>/<api_method>?<authentication_parameters>&<method_parameters>
|
||||
```
|
||||
|
||||
## Regional API Endpoints
|
||||
|
||||
| Region | Base URL |
|
||||
|--------|----------|
|
||||
| US/International | `https://api.labarchives.com/api` |
|
||||
| Australia | `https://auapi.labarchives.com/api` |
|
||||
| UK | `https://ukapi.labarchives.com/api` |
|
||||
|
||||
## Authentication
|
||||
|
||||
All API calls require authentication parameters:
|
||||
|
||||
- `access_key_id`: Provided by LabArchives administrator
|
||||
- `access_password`: Provided by LabArchives administrator
|
||||
- Additional user-specific credentials may be required for certain operations
|
||||
|
||||
## API Classes and Methods
|
||||
|
||||
### Users API Class
|
||||
|
||||
#### `users/user_access_info`
|
||||
|
||||
Retrieve user ID and notebook access information.
|
||||
|
||||
**Parameters:**
|
||||
- `login_or_email` (required): User's email address or login username
|
||||
- `password` (required): User's external applications password (not regular login password)
|
||||
|
||||
**Returns:** XML or JSON response containing:
|
||||
- User ID (uid)
|
||||
- List of accessible notebooks with IDs (nbid)
|
||||
- Account status and permissions
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
params = {
|
||||
'login_or_email': 'researcher@university.edu',
|
||||
'password': 'external_app_password'
|
||||
}
|
||||
response = client.make_call('users', 'user_access_info', params=params)
|
||||
```
|
||||
|
||||
#### `users/user_info_via_id`
|
||||
|
||||
Retrieve detailed user information by user ID.
|
||||
|
||||
**Parameters:**
|
||||
- `uid` (required): User ID obtained from user_access_info
|
||||
|
||||
**Returns:** User profile information including:
|
||||
- Name and email
|
||||
- Account creation date
|
||||
- Institution affiliation
|
||||
- Role and permissions
|
||||
- Storage quota and usage
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
params = {'uid': '12345'}
|
||||
response = client.make_call('users', 'user_info_via_id', params=params)
|
||||
```
|
||||
|
||||
### Notebooks API Class
|
||||
|
||||
#### `notebooks/notebook_backup`
|
||||
|
||||
Download complete notebook data including entries, attachments, and metadata.
|
||||
|
||||
**Parameters:**
|
||||
- `uid` (required): User ID
|
||||
- `nbid` (required): Notebook ID
|
||||
- `json` (optional, default: false): Return data in JSON format instead of XML
|
||||
- `no_attachments` (optional, default: false): Exclude attachments from backup
|
||||
|
||||
**Returns:**
|
||||
- When `no_attachments=false`: 7z compressed archive containing all notebook data
|
||||
- When `no_attachments=true`: XML or JSON structured data with entry content
|
||||
|
||||
**File format:**
|
||||
The returned archive includes:
|
||||
- Entry text content in HTML format
|
||||
- File attachments in original formats
|
||||
- Metadata XML files with timestamps, authors, and version history
|
||||
- Comment threads and annotations
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
# Full backup with attachments
|
||||
params = {
|
||||
'uid': '12345',
|
||||
'nbid': '67890',
|
||||
'json': 'false',
|
||||
'no_attachments': 'false'
|
||||
}
|
||||
response = client.make_call('notebooks', 'notebook_backup', params=params)
|
||||
|
||||
# Write to file
|
||||
with open('notebook_backup.7z', 'wb') as f:
|
||||
f.write(response.content)
|
||||
```
|
||||
|
||||
```python
|
||||
# Metadata only backup (JSON format, no attachments)
|
||||
params = {
|
||||
'uid': '12345',
|
||||
'nbid': '67890',
|
||||
'json': 'true',
|
||||
'no_attachments': 'true'
|
||||
}
|
||||
response = client.make_call('notebooks', 'notebook_backup', params=params)
|
||||
import json
|
||||
notebook_data = json.loads(response.content)
|
||||
```
|
||||
|
||||
#### `notebooks/list_notebooks`
|
||||
|
||||
Retrieve all notebooks accessible to a user (method name may vary by API version).
|
||||
|
||||
**Parameters:**
|
||||
- `uid` (required): User ID
|
||||
|
||||
**Returns:** List of notebooks with:
|
||||
- Notebook ID (nbid)
|
||||
- Notebook name
|
||||
- Creation and modification dates
|
||||
- Access level (owner, editor, viewer)
|
||||
- Member count
|
||||
|
||||
### Entries API Class
|
||||
|
||||
#### `entries/create_entry`
|
||||
|
||||
Create a new entry in a notebook.
|
||||
|
||||
**Parameters:**
|
||||
- `uid` (required): User ID
|
||||
- `nbid` (required): Notebook ID
|
||||
- `title` (required): Entry title
|
||||
- `content` (optional): HTML-formatted entry content
|
||||
- `date` (optional): Entry date (defaults to current date)
|
||||
|
||||
**Returns:** Entry ID and creation confirmation
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
params = {
|
||||
'uid': '12345',
|
||||
'nbid': '67890',
|
||||
'title': 'Experiment 2025-10-20',
|
||||
'content': '<p>Conducted PCR amplification of target gene...</p>',
|
||||
'date': '2025-10-20'
|
||||
}
|
||||
response = client.make_call('entries', 'create_entry', params=params)
|
||||
```
|
||||
|
||||
#### `entries/create_comment`
|
||||
|
||||
Add a comment to an existing entry.
|
||||
|
||||
**Parameters:**
|
||||
- `uid` (required): User ID
|
||||
- `nbid` (required): Notebook ID
|
||||
- `entry_id` (required): Target entry ID
|
||||
- `comment` (required): Comment text (HTML supported)
|
||||
|
||||
**Returns:** Comment ID and timestamp
|
||||
|
||||
#### `entries/create_part`
|
||||
|
||||
Add a component/part to an entry (e.g., text section, table, image).
|
||||
|
||||
**Parameters:**
|
||||
- `uid` (required): User ID
|
||||
- `nbid` (required): Notebook ID
|
||||
- `entry_id` (required): Target entry ID
|
||||
- `part_type` (required): Type of part (text, table, image, etc.)
|
||||
- `content` (required): Part content in appropriate format
|
||||
|
||||
**Returns:** Part ID and creation confirmation
|
||||
|
||||
#### `entries/upload_attachment`
|
||||
|
||||
Upload a file attachment to an entry.
|
||||
|
||||
**Parameters:**
|
||||
- `uid` (required): User ID
|
||||
- `nbid` (required): Notebook ID
|
||||
- `entry_id` (required): Target entry ID
|
||||
- `file` (required): File data (multipart/form-data)
|
||||
- `filename` (required): Original filename
|
||||
|
||||
**Returns:** Attachment ID and upload confirmation
|
||||
|
||||
**Example using requests library:**
|
||||
```python
|
||||
import requests
|
||||
|
||||
url = f'{api_url}/entries/upload_attachment'
|
||||
files = {'file': open('/path/to/data.csv', 'rb')}
|
||||
params = {
|
||||
'uid': '12345',
|
||||
'nbid': '67890',
|
||||
'entry_id': '11111',
|
||||
'filename': 'data.csv',
|
||||
'access_key_id': access_key_id,
|
||||
'access_password': access_password
|
||||
}
|
||||
response = requests.post(url, files=files, data=params)
|
||||
```
|
||||
|
||||
### Site Reports API Class
|
||||
|
||||
Enterprise-only features for institutional reporting and analytics.
|
||||
|
||||
#### `site_reports/detailed_usage_report`
|
||||
|
||||
Generate comprehensive usage statistics for the institution.
|
||||
|
||||
**Parameters:**
|
||||
- `start_date` (required): Report start date (YYYY-MM-DD)
|
||||
- `end_date` (required): Report end date (YYYY-MM-DD)
|
||||
- `format` (optional): Output format (csv, json, xml)
|
||||
|
||||
**Returns:** Usage metrics including:
|
||||
- User login frequency
|
||||
- Entry creation counts
|
||||
- Storage utilization
|
||||
- Collaboration statistics
|
||||
- Time-based activity patterns
|
||||
|
||||
#### `site_reports/detailed_notebook_report`
|
||||
|
||||
Generate detailed report on all notebooks in the institution.
|
||||
|
||||
**Parameters:**
|
||||
- `include_settings` (optional, default: false): Include notebook settings
|
||||
- `include_members` (optional, default: false): Include member lists
|
||||
|
||||
**Returns:** Notebook inventory with:
|
||||
- Notebook names and IDs
|
||||
- Owner information
|
||||
- Creation and last modified dates
|
||||
- Member count and access levels
|
||||
- Storage size
|
||||
- Settings (if requested)
|
||||
|
||||
#### `site_reports/pdf_offline_generation_report`
|
||||
|
||||
Track PDF exports for compliance and auditing purposes.
|
||||
|
||||
**Parameters:**
|
||||
- `start_date` (required): Report start date
|
||||
- `end_date` (required): Report end date
|
||||
|
||||
**Returns:** Export activity log with:
|
||||
- User who generated PDF
|
||||
- Notebook and entry exported
|
||||
- Export timestamp
|
||||
- IP address
|
||||
|
||||
### Utilities API Class
|
||||
|
||||
#### `utilities/institutional_login_urls`
|
||||
|
||||
Retrieve institutional login URLs for SSO integration.
|
||||
|
||||
**Parameters:** None required (uses access key authentication)
|
||||
|
||||
**Returns:** List of institutional login endpoints
|
||||
|
||||
## Response Formats
|
||||
|
||||
### XML Response Example
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<response>
|
||||
<uid>12345</uid>
|
||||
<email>researcher@university.edu</email>
|
||||
<notebooks>
|
||||
<notebook>
|
||||
<nbid>67890</nbid>
|
||||
<name>Lab Notebook 2025</name>
|
||||
<role>owner</role>
|
||||
</notebook>
|
||||
</notebooks>
|
||||
</response>
|
||||
```
|
||||
|
||||
### JSON Response Example
|
||||
|
||||
```json
|
||||
{
|
||||
"uid": "12345",
|
||||
"email": "researcher@university.edu",
|
||||
"notebooks": [
|
||||
{
|
||||
"nbid": "67890",
|
||||
"name": "Lab Notebook 2025",
|
||||
"role": "owner"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Code | Message | Meaning | Solution |
|
||||
|------|---------|---------|----------|
|
||||
| 401 | Unauthorized | Invalid credentials | Verify access_key_id and access_password |
|
||||
| 403 | Forbidden | Insufficient permissions | Check user role and notebook access |
|
||||
| 404 | Not Found | Resource doesn't exist | Verify uid, nbid, or entry_id are correct |
|
||||
| 429 | Too Many Requests | Rate limit exceeded | Implement exponential backoff |
|
||||
| 500 | Internal Server Error | Server-side issue | Retry request or contact support |
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
LabArchives implements rate limiting to ensure service stability:
|
||||
|
||||
- **Recommended:** Maximum 60 requests per minute per API key
|
||||
- **Burst allowance:** Short bursts up to 100 requests may be tolerated
|
||||
- **Best practice:** Implement 1-2 second delays between requests for batch operations
|
||||
|
||||
## API Versioning
|
||||
|
||||
LabArchives API is backward compatible. New methods are added without breaking existing implementations. Monitor LabArchives announcements for new capabilities.
|
||||
|
||||
## Support and Documentation
|
||||
|
||||
For API access requests, technical questions, or feature requests:
|
||||
- Email: support@labarchives.com
|
||||
- Include your institution name and specific use case for faster assistance
|
||||
357
skills/labarchive-integration/references/authentication_guide.md
Normal file
357
skills/labarchive-integration/references/authentication_guide.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# LabArchives Authentication Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1. Enterprise License
|
||||
|
||||
API access requires an Enterprise LabArchives license. Contact your LabArchives administrator or sales@labarchives.com to:
|
||||
- Verify your institution has Enterprise access
|
||||
- Request API access enablement for your account
|
||||
- Obtain institutional API credentials
|
||||
|
||||
### 2. API Credentials
|
||||
|
||||
You need two sets of credentials:
|
||||
|
||||
#### Institutional API Credentials (from LabArchives administrator)
|
||||
- **Access Key ID**: Institution-level identifier
|
||||
- **Access Password**: Institution-level secret
|
||||
|
||||
#### User Authentication Credentials (self-configured)
|
||||
- **Email**: Your LabArchives account email (e.g., researcher@university.edu)
|
||||
- **External Applications Password**: Set in your LabArchives account settings
|
||||
|
||||
## Setting Up External Applications Password
|
||||
|
||||
The external applications password is different from your regular LabArchives login password. It provides API access without exposing your primary credentials.
|
||||
|
||||
**Steps to create external applications password:**
|
||||
|
||||
1. Log into your LabArchives account at mynotebook.labarchives.com (or your institutional URL)
|
||||
2. Navigate to **Account Settings** (click your name in top-right corner)
|
||||
3. Select **Security & Privacy** tab
|
||||
4. Find **External Applications** section
|
||||
5. Click **Generate New Password** or **Reset Password**
|
||||
6. Copy and securely store this password (you won't see it again)
|
||||
7. Use this password for all API authentication
|
||||
|
||||
**Security note:** Treat this password like an API token. If compromised, regenerate it immediately from account settings.
|
||||
|
||||
## Configuration File Setup
|
||||
|
||||
Create a `config.yaml` file to store your credentials securely:
|
||||
|
||||
```yaml
|
||||
# Regional API endpoint
|
||||
api_url: https://api.labarchives.com/api
|
||||
|
||||
# Institutional credentials (from administrator)
|
||||
access_key_id: YOUR_ACCESS_KEY_ID_HERE
|
||||
access_password: YOUR_ACCESS_PASSWORD_HERE
|
||||
|
||||
# User credentials (for user-specific operations)
|
||||
user_email: researcher@university.edu
|
||||
user_external_password: YOUR_EXTERNAL_APP_PASSWORD_HERE
|
||||
```
|
||||
|
||||
**Alternative: Environment variables**
|
||||
|
||||
For enhanced security, use environment variables instead of config file:
|
||||
|
||||
```bash
|
||||
export LABARCHIVES_API_URL="https://api.labarchives.com/api"
|
||||
export LABARCHIVES_ACCESS_KEY_ID="your_key_id"
|
||||
export LABARCHIVES_ACCESS_PASSWORD="your_access_password"
|
||||
export LABARCHIVES_USER_EMAIL="researcher@university.edu"
|
||||
export LABARCHIVES_USER_PASSWORD="your_external_app_password"
|
||||
```
|
||||
|
||||
## Regional Endpoints
|
||||
|
||||
Select the correct regional API endpoint for your institution:
|
||||
|
||||
| Region | Endpoint | Use if your LabArchives URL is |
|
||||
|--------|----------|--------------------------------|
|
||||
| US/International | `https://api.labarchives.com/api` | `mynotebook.labarchives.com` |
|
||||
| Australia | `https://auapi.labarchives.com/api` | `aunotebook.labarchives.com` |
|
||||
| UK | `https://ukapi.labarchives.com/api` | `uknotebook.labarchives.com` |
|
||||
|
||||
Using the wrong regional endpoint will result in authentication failures even with correct credentials.
|
||||
|
||||
## Authentication Flow
|
||||
|
||||
### Option 1: Using labarchives-py Python Wrapper
|
||||
|
||||
```python
|
||||
from labarchivespy.client import Client
|
||||
import yaml
|
||||
|
||||
# Load configuration
|
||||
with open('config.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
# Initialize client with institutional credentials
|
||||
client = Client(
|
||||
config['api_url'],
|
||||
config['access_key_id'],
|
||||
config['access_password']
|
||||
)
|
||||
|
||||
# Authenticate as specific user to get UID
|
||||
login_params = {
|
||||
'login_or_email': config['user_email'],
|
||||
'password': config['user_external_password']
|
||||
}
|
||||
response = client.make_call('users', 'user_access_info', params=login_params)
|
||||
|
||||
# Parse response to extract UID
|
||||
import xml.etree.ElementTree as ET
|
||||
uid = ET.fromstring(response.content)[0].text
|
||||
print(f"Authenticated as user ID: {uid}")
|
||||
```
|
||||
|
||||
### Option 2: Direct HTTP Requests with Python requests
|
||||
|
||||
```python
|
||||
import requests
|
||||
import yaml
|
||||
|
||||
# Load configuration
|
||||
with open('config.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
# Construct API call
|
||||
url = f"{config['api_url']}/users/user_access_info"
|
||||
params = {
|
||||
'access_key_id': config['access_key_id'],
|
||||
'access_password': config['access_password'],
|
||||
'login_or_email': config['user_email'],
|
||||
'password': config['user_external_password']
|
||||
}
|
||||
|
||||
# Make authenticated request
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
print("Authentication successful!")
|
||||
print(response.content.decode('utf-8'))
|
||||
else:
|
||||
print(f"Authentication failed: {response.status_code}")
|
||||
print(response.content.decode('utf-8'))
|
||||
```
|
||||
|
||||
### Option 3: Using R
|
||||
|
||||
```r
|
||||
library(httr)
|
||||
library(xml2)
|
||||
|
||||
# Configuration
|
||||
api_url <- "https://api.labarchives.com/api"
|
||||
access_key_id <- "YOUR_ACCESS_KEY_ID"
|
||||
access_password <- "YOUR_ACCESS_PASSWORD"
|
||||
user_email <- "researcher@university.edu"
|
||||
user_external_password <- "YOUR_EXTERNAL_APP_PASSWORD"
|
||||
|
||||
# Make authenticated request
|
||||
response <- GET(
|
||||
paste0(api_url, "/users/user_access_info"),
|
||||
query = list(
|
||||
access_key_id = access_key_id,
|
||||
access_password = access_password,
|
||||
login_or_email = user_email,
|
||||
password = user_external_password
|
||||
)
|
||||
)
|
||||
|
||||
# Parse response
|
||||
if (status_code(response) == 200) {
|
||||
content <- content(response, as = "text", encoding = "UTF-8")
|
||||
xml_data <- read_xml(content)
|
||||
uid <- xml_text(xml_find_first(xml_data, "//uid"))
|
||||
print(paste("Authenticated as user ID:", uid))
|
||||
} else {
|
||||
print(paste("Authentication failed:", status_code(response)))
|
||||
}
|
||||
```
|
||||
|
||||
## OAuth Authentication (New Integrations)
|
||||
|
||||
LabArchives now uses OAuth 2.0 for new third-party integrations. Legacy API key authentication (described above) continues to work for direct API access.
|
||||
|
||||
**OAuth flow (for app developers):**
|
||||
|
||||
1. Register your application with LabArchives
|
||||
2. Obtain client ID and client secret
|
||||
3. Implement OAuth 2.0 authorization code flow
|
||||
4. Exchange authorization code for access token
|
||||
5. Use access token for API requests
|
||||
|
||||
Contact LabArchives developer support for OAuth integration documentation.
|
||||
|
||||
## Troubleshooting Authentication Issues
|
||||
|
||||
### 401 Unauthorized Error
|
||||
|
||||
**Possible causes and solutions:**
|
||||
|
||||
1. **Incorrect access_key_id or access_password**
|
||||
- Verify credentials with your LabArchives administrator
|
||||
- Check for typos or extra whitespace in config file
|
||||
|
||||
2. **Wrong external applications password**
|
||||
- Confirm you're using the external applications password, not your regular login password
|
||||
- Regenerate external applications password in account settings
|
||||
|
||||
3. **API access not enabled**
|
||||
- Contact your LabArchives administrator to enable API access for your account
|
||||
- Verify your institution has Enterprise license
|
||||
|
||||
4. **Wrong regional endpoint**
|
||||
- Confirm your api_url matches your institution's LabArchives instance
|
||||
- Check if you're using .com, .auapi, or .ukapi domain
|
||||
|
||||
### 403 Forbidden Error
|
||||
|
||||
**Possible causes and solutions:**
|
||||
|
||||
1. **Insufficient permissions**
|
||||
- Verify your account role has necessary permissions
|
||||
- Check if you have access to the specific notebook (nbid)
|
||||
|
||||
2. **Account suspended or expired**
|
||||
- Contact your LabArchives administrator to check account status
|
||||
|
||||
### Network and Connection Issues
|
||||
|
||||
**Firewall/proxy configuration:**
|
||||
|
||||
If your institution uses a firewall or proxy:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# Configure proxy
|
||||
proxies = {
|
||||
'http': 'http://proxy.university.edu:8080',
|
||||
'https': 'http://proxy.university.edu:8080'
|
||||
}
|
||||
|
||||
# Make request with proxy
|
||||
response = requests.get(url, params=params, proxies=proxies)
|
||||
```
|
||||
|
||||
**SSL certificate verification:**
|
||||
|
||||
For self-signed certificates (not recommended for production):
|
||||
|
||||
```python
|
||||
# Disable SSL verification (use only for testing)
|
||||
response = requests.get(url, params=params, verify=False)
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Never commit credentials to version control**
|
||||
- Add `config.yaml` to `.gitignore`
|
||||
- Use environment variables or secret management systems
|
||||
|
||||
2. **Rotate credentials regularly**
|
||||
- Change external applications password every 90 days
|
||||
- Regenerate API keys annually
|
||||
|
||||
3. **Use least privilege principle**
|
||||
- Request only necessary API permissions
|
||||
- Create separate API credentials for different applications
|
||||
|
||||
4. **Monitor API usage**
|
||||
- Regularly review API access logs
|
||||
- Set up alerts for unusual activity
|
||||
|
||||
5. **Secure storage**
|
||||
- Encrypt configuration files at rest
|
||||
- Use system keychain or secret management tools (e.g., AWS Secrets Manager, Azure Key Vault)
|
||||
|
||||
## Testing Authentication
|
||||
|
||||
Use this script to verify your authentication setup:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Test LabArchives API authentication"""
|
||||
|
||||
from labarchivespy.client import Client
|
||||
import yaml
|
||||
import sys
|
||||
|
||||
def test_authentication():
|
||||
try:
|
||||
# Load config
|
||||
with open('config.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
print("Configuration loaded successfully")
|
||||
print(f"API URL: {config['api_url']}")
|
||||
|
||||
# Initialize client
|
||||
client = Client(
|
||||
config['api_url'],
|
||||
config['access_key_id'],
|
||||
config['access_password']
|
||||
)
|
||||
print("Client initialized")
|
||||
|
||||
# Test authentication
|
||||
login_params = {
|
||||
'login_or_email': config['user_email'],
|
||||
'password': config['user_external_password']
|
||||
}
|
||||
response = client.make_call('users', 'user_access_info', params=login_params)
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ Authentication successful!")
|
||||
|
||||
# Extract UID
|
||||
import xml.etree.ElementTree as ET
|
||||
uid = ET.fromstring(response.content)[0].text
|
||||
print(f"User ID: {uid}")
|
||||
|
||||
# Get user info
|
||||
user_response = client.make_call('users', 'user_info_via_id', params={'uid': uid})
|
||||
print("✅ User information retrieved successfully")
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Authentication failed: {response.status_code}")
|
||||
print(response.content.decode('utf-8'))
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
if __name__ == '__main__':
|
||||
success = test_authentication()
|
||||
sys.exit(0 if success else 1)
|
||||
```
|
||||
|
||||
Run this script to confirm everything is configured correctly:
|
||||
|
||||
```bash
|
||||
python3 test_auth.py
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
If authentication continues to fail after troubleshooting:
|
||||
|
||||
1. Contact your institutional LabArchives administrator
|
||||
2. Email LabArchives support: support@labarchives.com
|
||||
3. Include:
|
||||
- Your institution name
|
||||
- Your LabArchives account email
|
||||
- Error messages and response codes
|
||||
- Regional endpoint you're using
|
||||
- Programming language and library versions
|
||||
425
skills/labarchive-integration/references/integrations.md
Normal file
425
skills/labarchive-integration/references/integrations.md
Normal file
@@ -0,0 +1,425 @@
|
||||
# LabArchives Third-Party Integrations
|
||||
|
||||
## Overview
|
||||
|
||||
LabArchives integrates with numerous scientific software platforms to streamline research workflows. This document covers programmatic integration approaches, automation strategies, and best practices for each supported platform.
|
||||
|
||||
## Integration Categories
|
||||
|
||||
### 1. Protocol Management
|
||||
|
||||
#### Protocols.io Integration
|
||||
|
||||
Export protocols directly from Protocols.io to LabArchives notebooks.
|
||||
|
||||
**Use cases:**
|
||||
- Standardize experimental procedures across lab notebooks
|
||||
- Maintain version control for protocols
|
||||
- Link protocols to experimental results
|
||||
|
||||
**Setup:**
|
||||
1. Enable Protocols.io integration in LabArchives settings
|
||||
2. Authenticate with Protocols.io account
|
||||
3. Browse and select protocols to export
|
||||
|
||||
**Programmatic approach:**
|
||||
```python
|
||||
# Export Protocols.io protocol as HTML/PDF
|
||||
# Then upload to LabArchives via API
|
||||
|
||||
def import_protocol_to_labarchives(client, uid, nbid, protocol_id):
|
||||
"""Import Protocols.io protocol to LabArchives entry"""
|
||||
# 1. Fetch protocol from Protocols.io API
|
||||
protocol_data = fetch_protocol_from_protocolsio(protocol_id)
|
||||
|
||||
# 2. Create new entry in LabArchives
|
||||
entry_params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'title': f"Protocol: {protocol_data['title']}",
|
||||
'content': protocol_data['html_content']
|
||||
}
|
||||
response = client.make_call('entries', 'create_entry', params=entry_params)
|
||||
|
||||
# 3. Add protocol metadata as comment
|
||||
entry_id = extract_entry_id(response)
|
||||
comment_params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'entry_id': entry_id,
|
||||
'comment': f"Protocols.io ID: {protocol_id}<br>Version: {protocol_data['version']}"
|
||||
}
|
||||
client.make_call('entries', 'create_comment', params=comment_params)
|
||||
|
||||
return entry_id
|
||||
```
|
||||
|
||||
**Updated:** September 22, 2025
|
||||
|
||||
### 2. Data Analysis Tools
|
||||
|
||||
#### GraphPad Prism Integration (Version 8+)
|
||||
|
||||
Export analyses, graphs, and figures directly from Prism to LabArchives.
|
||||
|
||||
**Use cases:**
|
||||
- Archive statistical analyses with raw data
|
||||
- Document figure generation for publications
|
||||
- Maintain analysis audit trail for compliance
|
||||
|
||||
**Setup:**
|
||||
1. Install GraphPad Prism 8 or higher
|
||||
2. Configure LabArchives connection in Prism preferences
|
||||
3. Use "Export to LabArchives" option from File menu
|
||||
|
||||
**Programmatic approach:**
|
||||
```python
|
||||
# Upload Prism files to LabArchives via API
|
||||
|
||||
def upload_prism_analysis(client, uid, nbid, entry_id, prism_file_path):
|
||||
"""Upload GraphPad Prism file to LabArchives entry"""
|
||||
import requests
|
||||
|
||||
url = f'{client.api_url}/entries/upload_attachment'
|
||||
files = {'file': open(prism_file_path, 'rb')}
|
||||
params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'entry_id': entry_id,
|
||||
'filename': os.path.basename(prism_file_path),
|
||||
'access_key_id': client.access_key_id,
|
||||
'access_password': client.access_password
|
||||
}
|
||||
|
||||
response = requests.post(url, files=files, data=params)
|
||||
return response
|
||||
```
|
||||
|
||||
**Supported file types:**
|
||||
- .pzfx (Prism project files)
|
||||
- .png, .jpg, .pdf (exported graphs)
|
||||
- .xlsx (exported data tables)
|
||||
|
||||
**Updated:** September 8, 2025
|
||||
|
||||
### 3. Molecular Biology & Bioinformatics
|
||||
|
||||
#### SnapGene Integration
|
||||
|
||||
Direct integration for molecular biology workflows, plasmid maps, and sequence analysis.
|
||||
|
||||
**Use cases:**
|
||||
- Document cloning strategies
|
||||
- Archive plasmid maps with experimental records
|
||||
- Link sequences to experimental results
|
||||
|
||||
**Setup:**
|
||||
1. Install SnapGene software
|
||||
2. Enable LabArchives export in SnapGene preferences
|
||||
3. Use "Send to LabArchives" feature
|
||||
|
||||
**File format support:**
|
||||
- .dna (SnapGene files)
|
||||
- .gb, .gbk (GenBank format)
|
||||
- .fasta (sequence files)
|
||||
- .png, .pdf (plasmid map exports)
|
||||
|
||||
**Programmatic workflow:**
|
||||
```python
|
||||
def upload_snapgene_file(client, uid, nbid, entry_id, snapgene_file):
|
||||
"""Upload SnapGene file with preview image"""
|
||||
# Upload main SnapGene file
|
||||
upload_attachment(client, uid, nbid, entry_id, snapgene_file)
|
||||
|
||||
# Generate and upload preview image (requires SnapGene CLI)
|
||||
preview_png = generate_snapgene_preview(snapgene_file)
|
||||
upload_attachment(client, uid, nbid, entry_id, preview_png)
|
||||
```
|
||||
|
||||
#### Geneious Integration
|
||||
|
||||
Bioinformatics analysis export from Geneious to LabArchives.
|
||||
|
||||
**Use cases:**
|
||||
- Archive sequence alignments and phylogenetic trees
|
||||
- Document NGS analysis pipelines
|
||||
- Link bioinformatics workflows to wet-lab experiments
|
||||
|
||||
**Supported exports:**
|
||||
- Sequence alignments
|
||||
- Phylogenetic trees
|
||||
- Assembly reports
|
||||
- Variant calling results
|
||||
|
||||
**File formats:**
|
||||
- .geneious (Geneious documents)
|
||||
- .fasta, .fastq (sequence data)
|
||||
- .bam, .sam (alignment files)
|
||||
- .vcf (variant files)
|
||||
|
||||
### 4. Computational Notebooks
|
||||
|
||||
#### Jupyter Integration
|
||||
|
||||
Embed Jupyter notebooks as LabArchives entries for reproducible computational research.
|
||||
|
||||
**Use cases:**
|
||||
- Document data analysis workflows
|
||||
- Archive computational experiments
|
||||
- Link code, results, and narrative
|
||||
|
||||
**Workflow:**
|
||||
|
||||
```python
|
||||
def export_jupyter_to_labarchives(notebook_path, client, uid, nbid):
|
||||
"""Export Jupyter notebook to LabArchives"""
|
||||
import nbformat
|
||||
from nbconvert import HTMLExporter
|
||||
|
||||
# Load notebook
|
||||
with open(notebook_path, 'r') as f:
|
||||
nb = nbformat.read(f, as_version=4)
|
||||
|
||||
# Convert to HTML
|
||||
html_exporter = HTMLExporter()
|
||||
html_exporter.template_name = 'classic'
|
||||
(body, resources) = html_exporter.from_notebook_node(nb)
|
||||
|
||||
# Create entry in LabArchives
|
||||
entry_params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'title': f"Jupyter Notebook: {os.path.basename(notebook_path)}",
|
||||
'content': body
|
||||
}
|
||||
response = client.make_call('entries', 'create_entry', params=entry_params)
|
||||
|
||||
# Upload original .ipynb file as attachment
|
||||
entry_id = extract_entry_id(response)
|
||||
upload_attachment(client, uid, nbid, entry_id, notebook_path)
|
||||
|
||||
return entry_id
|
||||
```
|
||||
|
||||
**Best practices:**
|
||||
- Export with outputs included (Run All Cells before export)
|
||||
- Include environment.yml or requirements.txt as attachment
|
||||
- Add execution timestamp and system info in comments
|
||||
|
||||
### 5. Clinical Research
|
||||
|
||||
#### REDCap Integration
|
||||
|
||||
Clinical data capture integration with LabArchives for research compliance and audit trails.
|
||||
|
||||
**Use cases:**
|
||||
- Link clinical data collection to research notebooks
|
||||
- Maintain audit trails for regulatory compliance
|
||||
- Document clinical trial protocols and amendments
|
||||
|
||||
**Integration approach:**
|
||||
- REDCap API exports data to LabArchives entries
|
||||
- Automated data synchronization for longitudinal studies
|
||||
- HIPAA-compliant data handling
|
||||
|
||||
**Example workflow:**
|
||||
```python
|
||||
def sync_redcap_to_labarchives(redcap_api_token, client, uid, nbid):
|
||||
"""Sync REDCap data to LabArchives"""
|
||||
# Fetch REDCap data
|
||||
redcap_data = fetch_redcap_data(redcap_api_token)
|
||||
|
||||
# Create LabArchives entry
|
||||
entry_params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'title': f"REDCap Data Export {datetime.now().strftime('%Y-%m-%d')}",
|
||||
'content': format_redcap_data_html(redcap_data)
|
||||
}
|
||||
response = client.make_call('entries', 'create_entry', params=entry_params)
|
||||
|
||||
return response
|
||||
```
|
||||
|
||||
**Compliance features:**
|
||||
- 21 CFR Part 11 compliance
|
||||
- Audit trail maintenance
|
||||
- Data integrity verification
|
||||
|
||||
### 6. Research Publishing
|
||||
|
||||
#### Qeios Integration
|
||||
|
||||
Research publishing platform integration for preprints and peer review.
|
||||
|
||||
**Use cases:**
|
||||
- Export research findings to preprint servers
|
||||
- Document publication workflows
|
||||
- Link published articles to lab notebooks
|
||||
|
||||
**Workflow:**
|
||||
- Export formatted entries from LabArchives
|
||||
- Submit to Qeios platform
|
||||
- Maintain bidirectional links between notebook and publication
|
||||
|
||||
#### SciSpace Integration
|
||||
|
||||
Literature management and citation integration.
|
||||
|
||||
**Use cases:**
|
||||
- Link references to experimental procedures
|
||||
- Maintain literature review in notebooks
|
||||
- Generate bibliographies for reports
|
||||
|
||||
**Features:**
|
||||
- Citation import from SciSpace to LabArchives
|
||||
- PDF annotation synchronization
|
||||
- Reference management
|
||||
|
||||
## OAuth Authentication for Integrations
|
||||
|
||||
LabArchives now uses OAuth 2.0 for new third-party integrations.
|
||||
|
||||
**OAuth flow for app developers:**
|
||||
|
||||
```python
|
||||
def labarchives_oauth_flow(client_id, client_secret, redirect_uri):
|
||||
"""Implement OAuth 2.0 flow for LabArchives integration"""
|
||||
import requests
|
||||
|
||||
# Step 1: Get authorization code
|
||||
auth_url = "https://mynotebook.labarchives.com/oauth/authorize"
|
||||
auth_params = {
|
||||
'client_id': client_id,
|
||||
'redirect_uri': redirect_uri,
|
||||
'response_type': 'code',
|
||||
'scope': 'read write'
|
||||
}
|
||||
# User visits auth_url and grants permission
|
||||
|
||||
# Step 2: Exchange code for access token
|
||||
token_url = "https://mynotebook.labarchives.com/oauth/token"
|
||||
token_params = {
|
||||
'client_id': client_id,
|
||||
'client_secret': client_secret,
|
||||
'redirect_uri': redirect_uri,
|
||||
'grant_type': 'authorization_code',
|
||||
'code': authorization_code # From redirect
|
||||
}
|
||||
|
||||
response = requests.post(token_url, data=token_params)
|
||||
tokens = response.json()
|
||||
|
||||
return tokens['access_token'], tokens['refresh_token']
|
||||
```
|
||||
|
||||
**OAuth advantages:**
|
||||
- More secure than API keys
|
||||
- Fine-grained permission control
|
||||
- Token refresh for long-running integrations
|
||||
- Revocable access
|
||||
|
||||
## Custom Integration Development
|
||||
|
||||
### General Workflow
|
||||
|
||||
For tools not officially supported, develop custom integrations:
|
||||
|
||||
1. **Export data** from source application (API or file export)
|
||||
2. **Transform format** to HTML or supported file type
|
||||
3. **Authenticate** with LabArchives API
|
||||
4. **Create entry** or upload attachment
|
||||
5. **Add metadata** via comments for traceability
|
||||
|
||||
### Example: Custom Integration Template
|
||||
|
||||
```python
|
||||
class LabArchivesIntegration:
|
||||
"""Template for custom LabArchives integrations"""
|
||||
|
||||
def __init__(self, config_path):
|
||||
self.client = self._init_client(config_path)
|
||||
self.uid = self._authenticate()
|
||||
|
||||
def _init_client(self, config_path):
|
||||
"""Initialize LabArchives client"""
|
||||
with open(config_path) as f:
|
||||
config = yaml.safe_load(f)
|
||||
return Client(config['api_url'],
|
||||
config['access_key_id'],
|
||||
config['access_password'])
|
||||
|
||||
def _authenticate(self):
|
||||
"""Get user ID"""
|
||||
# Implementation from authentication_guide.md
|
||||
pass
|
||||
|
||||
def export_data(self, source_data, nbid, title):
|
||||
"""Export data to LabArchives"""
|
||||
# Transform data to HTML
|
||||
html_content = self._transform_to_html(source_data)
|
||||
|
||||
# Create entry
|
||||
params = {
|
||||
'uid': self.uid,
|
||||
'nbid': nbid,
|
||||
'title': title,
|
||||
'content': html_content
|
||||
}
|
||||
response = self.client.make_call('entries', 'create_entry', params=params)
|
||||
|
||||
return extract_entry_id(response)
|
||||
|
||||
def _transform_to_html(self, data):
|
||||
"""Transform data to HTML format"""
|
||||
# Custom transformation logic
|
||||
pass
|
||||
```
|
||||
|
||||
## Integration Best Practices
|
||||
|
||||
1. **Version control:** Track which software version generated the data
|
||||
2. **Metadata preservation:** Include timestamps, user info, and processing parameters
|
||||
3. **File format standards:** Use open formats when possible (CSV, JSON, HTML)
|
||||
4. **Batch operations:** Implement rate limiting for bulk uploads
|
||||
5. **Error handling:** Implement retry logic with exponential backoff
|
||||
6. **Audit trails:** Log all API operations for compliance
|
||||
7. **Testing:** Validate integrations in test notebooks before production use
|
||||
|
||||
## Troubleshooting Integrations
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Integration not appearing in LabArchives:**
|
||||
- Verify integration is enabled by administrator
|
||||
- Check OAuth permissions if using OAuth
|
||||
- Ensure compatible software version
|
||||
|
||||
**File upload failures:**
|
||||
- Verify file size limits (typically 2GB per file)
|
||||
- Check file format compatibility
|
||||
- Ensure sufficient storage quota
|
||||
|
||||
**Authentication errors:**
|
||||
- Verify API credentials are current
|
||||
- Check if integration-specific tokens have expired
|
||||
- Confirm user has necessary permissions
|
||||
|
||||
### Integration Support
|
||||
|
||||
For integration-specific issues:
|
||||
- Check software vendor documentation (e.g., GraphPad, Protocols.io)
|
||||
- Contact LabArchives support: support@labarchives.com
|
||||
- Review LabArchives knowledge base: help.labarchives.com
|
||||
|
||||
## Future Integration Opportunities
|
||||
|
||||
Potential integrations for custom development:
|
||||
- Electronic data capture (EDC) systems
|
||||
- Laboratory information management systems (LIMS)
|
||||
- Instrument data systems (chromatography, spectroscopy)
|
||||
- Cloud storage platforms (Box, Dropbox, Google Drive)
|
||||
- Project management tools (Asana, Monday.com)
|
||||
- Grant management systems
|
||||
|
||||
For custom integration development, contact LabArchives for API partnership opportunities.
|
||||
334
skills/labarchive-integration/scripts/entry_operations.py
Executable file
334
skills/labarchive-integration/scripts/entry_operations.py
Executable file
@@ -0,0 +1,334 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
LabArchives Entry Operations
|
||||
|
||||
Utilities for creating entries, uploading attachments, and managing notebook content.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import yaml
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def load_config(config_path='config.yaml'):
|
||||
"""Load configuration from YAML file"""
|
||||
try:
|
||||
with open(config_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Configuration file not found: {config_path}")
|
||||
print(" Run setup_config.py first to create configuration")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ Error loading configuration: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def init_client(config):
|
||||
"""Initialize LabArchives API client"""
|
||||
try:
|
||||
from labarchivespy.client import Client
|
||||
return Client(
|
||||
config['api_url'],
|
||||
config['access_key_id'],
|
||||
config['access_password']
|
||||
)
|
||||
except ImportError:
|
||||
print("❌ labarchives-py package not installed")
|
||||
print(" Install with: pip install git+https://github.com/mcmero/labarchives-py")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_user_id(client, config):
|
||||
"""Get user ID via authentication"""
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
login_params = {
|
||||
'login_or_email': config['user_email'],
|
||||
'password': config['user_external_password']
|
||||
}
|
||||
|
||||
try:
|
||||
response = client.make_call('users', 'user_access_info', params=login_params)
|
||||
|
||||
if response.status_code == 200:
|
||||
uid = ET.fromstring(response.content)[0].text
|
||||
return uid
|
||||
else:
|
||||
print(f"❌ Authentication failed: HTTP {response.status_code}")
|
||||
print(f" Response: {response.content.decode('utf-8')[:200]}")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error during authentication: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def create_entry(client, uid, nbid, title, content=None, date=None):
|
||||
"""Create a new entry in a notebook"""
|
||||
print(f"\n📝 Creating entry: {title}")
|
||||
|
||||
# Prepare parameters
|
||||
params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'title': title
|
||||
}
|
||||
|
||||
if content:
|
||||
# Ensure content is HTML formatted
|
||||
if not content.startswith('<'):
|
||||
content = f'<p>{content}</p>'
|
||||
params['content'] = content
|
||||
|
||||
if date:
|
||||
params['date'] = date
|
||||
|
||||
try:
|
||||
response = client.make_call('entries', 'create_entry', params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ Entry created successfully")
|
||||
|
||||
# Try to extract entry ID from response
|
||||
try:
|
||||
import xml.etree.ElementTree as ET
|
||||
root = ET.fromstring(response.content)
|
||||
entry_id = root.find('.//entry_id')
|
||||
if entry_id is not None:
|
||||
print(f" Entry ID: {entry_id.text}")
|
||||
return entry_id.text
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
print(f"❌ Entry creation failed: HTTP {response.status_code}")
|
||||
print(f" Response: {response.content.decode('utf-8')[:200]}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error creating entry: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def create_comment(client, uid, nbid, entry_id, comment):
|
||||
"""Add a comment to an existing entry"""
|
||||
print(f"\n💬 Adding comment to entry {entry_id}")
|
||||
|
||||
params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'entry_id': entry_id,
|
||||
'comment': comment
|
||||
}
|
||||
|
||||
try:
|
||||
response = client.make_call('entries', 'create_comment', params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ Comment added successfully")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Comment creation failed: HTTP {response.status_code}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error creating comment: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def upload_attachment(client, config, uid, nbid, entry_id, file_path):
|
||||
"""Upload a file attachment to an entry"""
|
||||
import requests
|
||||
|
||||
file_path = Path(file_path)
|
||||
|
||||
if not file_path.exists():
|
||||
print(f"❌ File not found: {file_path}")
|
||||
return False
|
||||
|
||||
print(f"\n📎 Uploading attachment: {file_path.name}")
|
||||
print(f" Size: {file_path.stat().st_size / 1024:.2f} KB")
|
||||
|
||||
url = f"{config['api_url']}/entries/upload_attachment"
|
||||
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
files = {'file': f}
|
||||
data = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'entry_id': entry_id,
|
||||
'filename': file_path.name,
|
||||
'access_key_id': config['access_key_id'],
|
||||
'access_password': config['access_password']
|
||||
}
|
||||
|
||||
response = requests.post(url, files=files, data=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ Attachment uploaded successfully")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Upload failed: HTTP {response.status_code}")
|
||||
print(f" Response: {response.content.decode('utf-8')[:200]}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error uploading attachment: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def batch_upload(client, config, uid, nbid, entry_id, directory):
|
||||
"""Upload all files from a directory as attachments"""
|
||||
directory = Path(directory)
|
||||
|
||||
if not directory.is_dir():
|
||||
print(f"❌ Directory not found: {directory}")
|
||||
return
|
||||
|
||||
files = list(directory.glob('*'))
|
||||
files = [f for f in files if f.is_file()]
|
||||
|
||||
if not files:
|
||||
print(f"❌ No files found in {directory}")
|
||||
return
|
||||
|
||||
print(f"\n📦 Batch uploading {len(files)} files from {directory}")
|
||||
|
||||
successful = 0
|
||||
failed = 0
|
||||
|
||||
for file_path in files:
|
||||
if upload_attachment(client, config, uid, nbid, entry_id, file_path):
|
||||
successful += 1
|
||||
else:
|
||||
failed += 1
|
||||
|
||||
print("\n" + "="*60)
|
||||
print(f"Batch upload complete: {successful} successful, {failed} failed")
|
||||
print("="*60)
|
||||
|
||||
|
||||
def create_entry_with_attachments(client, config, uid, nbid, title, content,
|
||||
attachments):
|
||||
"""Create entry and upload multiple attachments"""
|
||||
# Create entry
|
||||
entry_id = create_entry(client, uid, nbid, title, content)
|
||||
|
||||
if not entry_id:
|
||||
print("❌ Cannot upload attachments without entry ID")
|
||||
return False
|
||||
|
||||
# Upload attachments
|
||||
for attachment_path in attachments:
|
||||
upload_attachment(client, config, uid, nbid, entry_id, attachment_path)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""Main command-line interface"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='LabArchives Entry Operations',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Create simple entry
|
||||
python3 entry_operations.py create --nbid 12345 --title "Experiment Results"
|
||||
|
||||
# Create entry with content
|
||||
python3 entry_operations.py create --nbid 12345 --title "Results" \\
|
||||
--content "PCR amplification successful"
|
||||
|
||||
# Create entry with HTML content
|
||||
python3 entry_operations.py create --nbid 12345 --title "Results" \\
|
||||
--content "<p>Results:</p><ul><li>Sample A: Positive</li></ul>"
|
||||
|
||||
# Upload attachment to existing entry
|
||||
python3 entry_operations.py upload --nbid 12345 --entry-id 67890 \\
|
||||
--file data.csv
|
||||
|
||||
# Batch upload multiple files
|
||||
python3 entry_operations.py batch-upload --nbid 12345 --entry-id 67890 \\
|
||||
--directory ./experiment_data/
|
||||
|
||||
# Add comment to entry
|
||||
python3 entry_operations.py comment --nbid 12345 --entry-id 67890 \\
|
||||
--text "Follow-up analysis needed"
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('--config', default='config.yaml',
|
||||
help='Path to configuration file (default: config.yaml)')
|
||||
parser.add_argument('--nbid', required=True,
|
||||
help='Notebook ID')
|
||||
|
||||
subparsers = parser.add_subparsers(dest='command', help='Command to execute')
|
||||
|
||||
# Create entry command
|
||||
create_parser = subparsers.add_parser('create', help='Create new entry')
|
||||
create_parser.add_argument('--title', required=True, help='Entry title')
|
||||
create_parser.add_argument('--content', help='Entry content (HTML supported)')
|
||||
create_parser.add_argument('--date', help='Entry date (YYYY-MM-DD)')
|
||||
create_parser.add_argument('--attachments', nargs='+',
|
||||
help='Files to attach to the new entry')
|
||||
|
||||
# Upload attachment command
|
||||
upload_parser = subparsers.add_parser('upload', help='Upload attachment to entry')
|
||||
upload_parser.add_argument('--entry-id', required=True, help='Entry ID')
|
||||
upload_parser.add_argument('--file', required=True, help='File to upload')
|
||||
|
||||
# Batch upload command
|
||||
batch_parser = subparsers.add_parser('batch-upload',
|
||||
help='Upload all files from directory')
|
||||
batch_parser.add_argument('--entry-id', required=True, help='Entry ID')
|
||||
batch_parser.add_argument('--directory', required=True,
|
||||
help='Directory containing files to upload')
|
||||
|
||||
# Comment command
|
||||
comment_parser = subparsers.add_parser('comment', help='Add comment to entry')
|
||||
comment_parser.add_argument('--entry-id', required=True, help='Entry ID')
|
||||
comment_parser.add_argument('--text', required=True, help='Comment text')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# Load configuration and initialize
|
||||
config = load_config(args.config)
|
||||
client = init_client(config)
|
||||
uid = get_user_id(client, config)
|
||||
|
||||
# Execute command
|
||||
if args.command == 'create':
|
||||
if args.attachments:
|
||||
create_entry_with_attachments(
|
||||
client, config, uid, args.nbid, args.title,
|
||||
args.content, args.attachments
|
||||
)
|
||||
else:
|
||||
create_entry(client, uid, args.nbid, args.title,
|
||||
args.content, args.date)
|
||||
|
||||
elif args.command == 'upload':
|
||||
upload_attachment(client, config, uid, args.nbid,
|
||||
args.entry_id, args.file)
|
||||
|
||||
elif args.command == 'batch-upload':
|
||||
batch_upload(client, config, uid, args.nbid,
|
||||
args.entry_id, args.directory)
|
||||
|
||||
elif args.command == 'comment':
|
||||
create_comment(client, uid, args.nbid, args.entry_id, args.text)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
269
skills/labarchive-integration/scripts/notebook_operations.py
Executable file
269
skills/labarchive-integration/scripts/notebook_operations.py
Executable file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
LabArchives Notebook Operations
|
||||
|
||||
Utilities for listing, backing up, and managing LabArchives notebooks.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_config(config_path='config.yaml'):
|
||||
"""Load configuration from YAML file"""
|
||||
try:
|
||||
with open(config_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Configuration file not found: {config_path}")
|
||||
print(" Run setup_config.py first to create configuration")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ Error loading configuration: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def init_client(config):
|
||||
"""Initialize LabArchives API client"""
|
||||
try:
|
||||
from labarchivespy.client import Client
|
||||
return Client(
|
||||
config['api_url'],
|
||||
config['access_key_id'],
|
||||
config['access_password']
|
||||
)
|
||||
except ImportError:
|
||||
print("❌ labarchives-py package not installed")
|
||||
print(" Install with: pip install git+https://github.com/mcmero/labarchives-py")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_user_id(client, config):
|
||||
"""Get user ID via authentication"""
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
login_params = {
|
||||
'login_or_email': config['user_email'],
|
||||
'password': config['user_external_password']
|
||||
}
|
||||
|
||||
try:
|
||||
response = client.make_call('users', 'user_access_info', params=login_params)
|
||||
|
||||
if response.status_code == 200:
|
||||
uid = ET.fromstring(response.content)[0].text
|
||||
return uid
|
||||
else:
|
||||
print(f"❌ Authentication failed: HTTP {response.status_code}")
|
||||
print(f" Response: {response.content.decode('utf-8')[:200]}")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error during authentication: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def list_notebooks(client, uid):
|
||||
"""List all accessible notebooks for a user"""
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
print(f"\n📚 Listing notebooks for user ID: {uid}\n")
|
||||
|
||||
# Get user access info which includes notebook list
|
||||
login_params = {'uid': uid}
|
||||
|
||||
try:
|
||||
response = client.make_call('users', 'user_access_info', params=login_params)
|
||||
|
||||
if response.status_code == 200:
|
||||
root = ET.fromstring(response.content)
|
||||
notebooks = root.findall('.//notebook')
|
||||
|
||||
if not notebooks:
|
||||
print("No notebooks found")
|
||||
return []
|
||||
|
||||
notebook_list = []
|
||||
print(f"{'Notebook ID':<15} {'Name':<40} {'Role':<10}")
|
||||
print("-" * 70)
|
||||
|
||||
for nb in notebooks:
|
||||
nbid = nb.find('nbid').text if nb.find('nbid') is not None else 'N/A'
|
||||
name = nb.find('name').text if nb.find('name') is not None else 'Unnamed'
|
||||
role = nb.find('role').text if nb.find('role') is not None else 'N/A'
|
||||
|
||||
notebook_list.append({'nbid': nbid, 'name': name, 'role': role})
|
||||
print(f"{nbid:<15} {name:<40} {role:<10}")
|
||||
|
||||
print(f"\nTotal notebooks: {len(notebooks)}")
|
||||
return notebook_list
|
||||
|
||||
else:
|
||||
print(f"❌ Failed to list notebooks: HTTP {response.status_code}")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error listing notebooks: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def backup_notebook(client, uid, nbid, output_dir='backups', json_format=False,
|
||||
no_attachments=False):
|
||||
"""Backup a notebook"""
|
||||
print(f"\n💾 Backing up notebook {nbid}...")
|
||||
|
||||
# Create output directory
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(exist_ok=True)
|
||||
|
||||
# Prepare parameters
|
||||
params = {
|
||||
'uid': uid,
|
||||
'nbid': nbid,
|
||||
'json': 'true' if json_format else 'false',
|
||||
'no_attachments': 'true' if no_attachments else 'false'
|
||||
}
|
||||
|
||||
try:
|
||||
response = client.make_call('notebooks', 'notebook_backup', params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
# Determine file extension
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
|
||||
if no_attachments:
|
||||
ext = 'json' if json_format else 'xml'
|
||||
filename = f"notebook_{nbid}_{timestamp}.{ext}"
|
||||
else:
|
||||
filename = f"notebook_{nbid}_{timestamp}.7z"
|
||||
|
||||
output_file = output_path / filename
|
||||
|
||||
# Write to file
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(response.content)
|
||||
|
||||
file_size = output_file.stat().st_size / (1024 * 1024) # MB
|
||||
print(f"✅ Backup saved: {output_file}")
|
||||
print(f" File size: {file_size:.2f} MB")
|
||||
|
||||
return str(output_file)
|
||||
|
||||
else:
|
||||
print(f"❌ Backup failed: HTTP {response.status_code}")
|
||||
print(f" Response: {response.content.decode('utf-8')[:200]}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error during backup: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def backup_all_notebooks(client, uid, output_dir='backups', json_format=False,
|
||||
no_attachments=False):
|
||||
"""Backup all accessible notebooks"""
|
||||
print("\n📦 Backing up all notebooks...\n")
|
||||
|
||||
notebooks = list_notebooks(client, uid)
|
||||
|
||||
if not notebooks:
|
||||
print("No notebooks to backup")
|
||||
return
|
||||
|
||||
successful = 0
|
||||
failed = 0
|
||||
|
||||
for nb in notebooks:
|
||||
nbid = nb['nbid']
|
||||
name = nb['name']
|
||||
|
||||
print(f"\n--- Backing up: {name} (ID: {nbid}) ---")
|
||||
|
||||
result = backup_notebook(client, uid, nbid, output_dir, json_format, no_attachments)
|
||||
|
||||
if result:
|
||||
successful += 1
|
||||
else:
|
||||
failed += 1
|
||||
|
||||
print("\n" + "="*60)
|
||||
print(f"Backup complete: {successful} successful, {failed} failed")
|
||||
print("="*60)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main command-line interface"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='LabArchives Notebook Operations',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# List all notebooks
|
||||
python3 notebook_operations.py list
|
||||
|
||||
# Backup specific notebook
|
||||
python3 notebook_operations.py backup --nbid 12345
|
||||
|
||||
# Backup all notebooks (JSON format, no attachments)
|
||||
python3 notebook_operations.py backup-all --json --no-attachments
|
||||
|
||||
# Backup to custom directory
|
||||
python3 notebook_operations.py backup --nbid 12345 --output my_backups/
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('--config', default='config.yaml',
|
||||
help='Path to configuration file (default: config.yaml)')
|
||||
|
||||
subparsers = parser.add_subparsers(dest='command', help='Command to execute')
|
||||
|
||||
# List command
|
||||
subparsers.add_parser('list', help='List all accessible notebooks')
|
||||
|
||||
# Backup command
|
||||
backup_parser = subparsers.add_parser('backup', help='Backup a specific notebook')
|
||||
backup_parser.add_argument('--nbid', required=True, help='Notebook ID to backup')
|
||||
backup_parser.add_argument('--output', default='backups',
|
||||
help='Output directory (default: backups)')
|
||||
backup_parser.add_argument('--json', action='store_true',
|
||||
help='Return data in JSON format instead of XML')
|
||||
backup_parser.add_argument('--no-attachments', action='store_true',
|
||||
help='Exclude attachments from backup')
|
||||
|
||||
# Backup all command
|
||||
backup_all_parser = subparsers.add_parser('backup-all',
|
||||
help='Backup all accessible notebooks')
|
||||
backup_all_parser.add_argument('--output', default='backups',
|
||||
help='Output directory (default: backups)')
|
||||
backup_all_parser.add_argument('--json', action='store_true',
|
||||
help='Return data in JSON format instead of XML')
|
||||
backup_all_parser.add_argument('--no-attachments', action='store_true',
|
||||
help='Exclude attachments from backup')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# Load configuration and initialize
|
||||
config = load_config(args.config)
|
||||
client = init_client(config)
|
||||
uid = get_user_id(client, config)
|
||||
|
||||
# Execute command
|
||||
if args.command == 'list':
|
||||
list_notebooks(client, uid)
|
||||
|
||||
elif args.command == 'backup':
|
||||
backup_notebook(client, uid, args.nbid, args.output, args.json, args.no_attachments)
|
||||
|
||||
elif args.command == 'backup-all':
|
||||
backup_all_notebooks(client, uid, args.output, args.json, args.no_attachments)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
205
skills/labarchive-integration/scripts/setup_config.py
Executable file
205
skills/labarchive-integration/scripts/setup_config.py
Executable file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
LabArchives Configuration Setup Script
|
||||
|
||||
This script helps create a config.yaml file with necessary credentials
|
||||
for LabArchives API access.
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_regional_endpoint():
|
||||
"""Prompt user to select regional API endpoint"""
|
||||
print("\nSelect your regional API endpoint:")
|
||||
print("1. US/International (mynotebook.labarchives.com)")
|
||||
print("2. Australia (aunotebook.labarchives.com)")
|
||||
print("3. UK (uknotebook.labarchives.com)")
|
||||
print("4. Custom endpoint")
|
||||
|
||||
choice = input("\nEnter choice (1-4): ").strip()
|
||||
|
||||
endpoints = {
|
||||
'1': 'https://api.labarchives.com/api',
|
||||
'2': 'https://auapi.labarchives.com/api',
|
||||
'3': 'https://ukapi.labarchives.com/api'
|
||||
}
|
||||
|
||||
if choice in endpoints:
|
||||
return endpoints[choice]
|
||||
elif choice == '4':
|
||||
return input("Enter custom API endpoint URL: ").strip()
|
||||
else:
|
||||
print("Invalid choice, defaulting to US/International")
|
||||
return endpoints['1']
|
||||
|
||||
|
||||
def get_credentials():
|
||||
"""Prompt user for API credentials"""
|
||||
print("\n" + "="*60)
|
||||
print("LabArchives API Credentials")
|
||||
print("="*60)
|
||||
print("\nYou need two sets of credentials:")
|
||||
print("1. Institutional API credentials (from LabArchives administrator)")
|
||||
print("2. User authentication credentials (from your account settings)")
|
||||
print()
|
||||
|
||||
# Institutional credentials
|
||||
print("Institutional Credentials:")
|
||||
access_key_id = input(" Access Key ID: ").strip()
|
||||
access_password = input(" Access Password: ").strip()
|
||||
|
||||
# User credentials
|
||||
print("\nUser Credentials:")
|
||||
user_email = input(" Your LabArchives email: ").strip()
|
||||
|
||||
print("\nExternal Applications Password:")
|
||||
print("(Set this in your LabArchives Account Settings → Security & Privacy)")
|
||||
user_password = input(" External Applications Password: ").strip()
|
||||
|
||||
return {
|
||||
'access_key_id': access_key_id,
|
||||
'access_password': access_password,
|
||||
'user_email': user_email,
|
||||
'user_external_password': user_password
|
||||
}
|
||||
|
||||
|
||||
def create_config_file(config_data, output_path='config.yaml'):
|
||||
"""Create YAML configuration file"""
|
||||
with open(output_path, 'w') as f:
|
||||
yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
# Set file permissions to user read/write only for security
|
||||
os.chmod(output_path, 0o600)
|
||||
|
||||
print(f"\n✅ Configuration saved to: {os.path.abspath(output_path)}")
|
||||
print(" File permissions set to 600 (user read/write only)")
|
||||
|
||||
|
||||
def verify_config(config_path='config.yaml'):
|
||||
"""Verify configuration file can be loaded"""
|
||||
try:
|
||||
with open(config_path, 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
required_keys = ['api_url', 'access_key_id', 'access_password',
|
||||
'user_email', 'user_external_password']
|
||||
|
||||
missing = [key for key in required_keys if key not in config or not config[key]]
|
||||
|
||||
if missing:
|
||||
print(f"\n⚠️ Warning: Missing required fields: {', '.join(missing)}")
|
||||
return False
|
||||
|
||||
print("\n✅ Configuration file verified successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error verifying configuration: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def test_authentication(config_path='config.yaml'):
|
||||
"""Test authentication with LabArchives API"""
|
||||
print("\nWould you like to test the connection? (requires labarchives-py package)")
|
||||
test = input("Test connection? (y/n): ").strip().lower()
|
||||
|
||||
if test != 'y':
|
||||
return
|
||||
|
||||
try:
|
||||
# Try to import labarchives-py
|
||||
from labarchivespy.client import Client
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# Load config
|
||||
with open(config_path, 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
# Initialize client
|
||||
print("\nInitializing client...")
|
||||
client = Client(
|
||||
config['api_url'],
|
||||
config['access_key_id'],
|
||||
config['access_password']
|
||||
)
|
||||
|
||||
# Test authentication
|
||||
print("Testing authentication...")
|
||||
login_params = {
|
||||
'login_or_email': config['user_email'],
|
||||
'password': config['user_external_password']
|
||||
}
|
||||
response = client.make_call('users', 'user_access_info', params=login_params)
|
||||
|
||||
if response.status_code == 200:
|
||||
# Extract UID
|
||||
uid = ET.fromstring(response.content)[0].text
|
||||
print(f"\n✅ Authentication successful!")
|
||||
print(f" User ID: {uid}")
|
||||
|
||||
# Get notebook count
|
||||
root = ET.fromstring(response.content)
|
||||
notebooks = root.findall('.//notebook')
|
||||
print(f" Accessible notebooks: {len(notebooks)}")
|
||||
|
||||
else:
|
||||
print(f"\n❌ Authentication failed: HTTP {response.status_code}")
|
||||
print(f" Response: {response.content.decode('utf-8')[:200]}")
|
||||
|
||||
except ImportError:
|
||||
print("\n⚠️ labarchives-py package not installed")
|
||||
print(" Install with: pip install git+https://github.com/mcmero/labarchives-py")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Connection test failed: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main setup workflow"""
|
||||
print("="*60)
|
||||
print("LabArchives API Configuration Setup")
|
||||
print("="*60)
|
||||
|
||||
# Check if config already exists
|
||||
if os.path.exists('config.yaml'):
|
||||
print("\n⚠️ config.yaml already exists")
|
||||
overwrite = input("Overwrite existing configuration? (y/n): ").strip().lower()
|
||||
if overwrite != 'y':
|
||||
print("Setup cancelled")
|
||||
return
|
||||
|
||||
# Get configuration
|
||||
api_url = get_regional_endpoint()
|
||||
credentials = get_credentials()
|
||||
|
||||
# Combine configuration
|
||||
config_data = {
|
||||
'api_url': api_url,
|
||||
**credentials
|
||||
}
|
||||
|
||||
# Create config file
|
||||
create_config_file(config_data)
|
||||
|
||||
# Verify
|
||||
verify_config()
|
||||
|
||||
# Test connection
|
||||
test_authentication()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("Setup complete!")
|
||||
print("="*60)
|
||||
print("\nNext steps:")
|
||||
print("1. Add config.yaml to .gitignore if using version control")
|
||||
print("2. Use notebook_operations.py to list and backup notebooks")
|
||||
print("3. Use entry_operations.py to create entries and upload files")
|
||||
print("\nFor more information, see references/authentication_guide.md")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user