Initial commit
This commit is contained in:
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