sharepointer


Namesharepointer JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
SummaryA tool to manage SharePoint files and folders.
upload_time2025-11-06 21:12:31
maintainerNone
docs_urlNone
authorNone
requires_python>=3.13
licenseMIT
keywords automation file-management office365 python sharepoint
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # SharePoint Manager

A Python library for interacting with SharePoint document libraries using the Microsoft Graph API.

## Features

- 🔐 **Authentication**: MSAL-based authentication with Azure AD
- 📁 **File Operations**: Upload, download, delete, and move files
- 📂 **Folder Operations**: Download, delete, and move folders recursively
- 🔍 **Search Operations**: Search for files and folders by suffix (recursive and non-recursive)
- 🎯 **Unified API**: Single methods that auto-detect file vs folder types

## Installation

### Prerequisites

- Python 3.13+
- Microsoft Azure AD application with SharePoint permissions: `Sites.ReadWrite.All` and `
Files.ReadWrite.All`

### Dependencies

```bash
pip install msal requests python-dotenv
```

## Configuration

### 1. Azure AD App Registration

#### Steps to Register an App in Azure AD and Get Credentials:

**1. Register an App in Azure AD**

- Go to https://portal.azure.com
- Navigate to **Azure Active Directory** → **App registrations** → **New registration**
- Fill in:
    - **Name**: Something like "SharePoint Python App"
    - **Supported account types**: "Accounts in this organizational directory only"
    - **Redirect URI**: Leave blank (not needed for app-only)
- Click **Register**

**2. Get the Client ID**

- After registration, you'll see the Overview page
- Copy the **Application (client) ID** - this is your `CLIENT_ID`

**3. Create a Client Secret**

- In the same app registration, go to **Certificates & secrets** (left menu)
- Click **New client secret**
- Add a description (e.g., "Python SharePoint Access")
- Choose an expiration period (recommended: 12-24 months)
- Click **Add**
- **IMPORTANT**: Copy the **Value** immediately - this is your `CLIENT_SECRET` and you won't be able to see it again!

**4. Grant SharePoint Permissions**

- Go to **API permissions** (left menu)
- Click **Add a permission**
- Choose **SharePoint**
- Select **Application permissions** (not Delegated)
- Add these permissions:
    - `Sites.ReadWrite.All` (for read/write access to all site collections)
    - Or `Sites.FullControl.All` (for full control)
- Click **Add permissions**
- **IMPORTANT**: Click **Grant admin consent for your organization** (requires admin privileges)

**5. Get Your Tenant ID**

- Go back to **Azure Active Directory** → **Overview**
- Copy the **Tenant ID** (also called Directory ID)

### 2. Environment Variables

Create a `.env` file in your project root:

```env
TENANT_ID=your-tenant-id
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret
```

## Quick Start

```python
from sharepointer.sharepoint import SharePointManager
import os
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Initialize SharePoint Manager
sp_manager = SharePointManager(
    tenant_id=os.getenv('TENANT_ID'),
    client_id=os.getenv('CLIENT_ID'),
    client_secret=os.getenv('CLIENT_SECRET'),
    site_name="your-site-name"  # e.g., "contoso" for contoso.sharepoint.com
)

# Get site and drive IDs
sp_manager.get_site_id("/sites/YourSiteName")  # Optional: specify site path
sp_manager.get_drive_id("Documents")  # Document library name
```

## API Reference

### Authentication & Initialization

#### `SharePointManager(tenant_id, client_id, client_secret, site_name)`

Initialize the SharePoint Manager with authentication credentials.

**Parameters:**
- `tenant_id` (str): Azure AD tenant ID
- `client_id` (str): Application (client) ID
- `client_secret` (str): Client secret value
- `site_name` (str): SharePoint site name (without .sharepoint.com)

**Example:**
```python
sp_manager = SharePointManager(
    tenant_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    client_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    client_secret="your-secret",
    site_name="contoso"
)
```

#### `get_site_id(site_path="")`

Retrieve the SharePoint site ID.

**Parameters:**
- `site_path` (str, optional): Site path (e.g., "/sites/YourSite")

**Returns:** Site ID (str)

#### `get_drive_id(drive_name="Documents")`

Retrieve the document library (drive) ID.

**Parameters:**
- `drive_name` (str, optional): Document library name (default: "Documents")

**Returns:** Drive ID (str)

---

### File Operations

#### `download_file(file_path, local_path=None)`

Download a file from SharePoint.

**Parameters:**
- `file_path` (str): Path to file in SharePoint (e.g., "folder/data.csv")
- `local_path` (str, optional): Local path to save file. If None, returns BytesIO object

**Returns:**
- BytesIO object (if local_path is None)
- Local file path (str) (if local_path is provided)

**Example:**
```python
# Download to memory
file_content = sp_manager.download_file("reports/data.csv")

# Download to local file
sp_manager.download_file("reports/data.csv", "/tmp/data.csv")
```

#### `upload_file(local_file_path, sharepoint_path, file_name=None)`

Upload a file to SharePoint.

**Parameters:**
- `local_file_path` (str): Path to local file
- `sharepoint_path` (str): Destination folder in SharePoint (empty string for root)
- `file_name` (str, optional): Name for file in SharePoint (defaults to local filename)

**Returns:** Response JSON from upload

**Example:**
```python
sp_manager.upload_file(
    local_file_path="/tmp/report.pdf",
    sharepoint_path="Reports/2024",
    file_name="annual_report.pdf"
)
```

#### `upload_file_from_memory(file_content, sharepoint_path, file_name)`

Upload a file from memory (bytes).

**Parameters:**
- `file_content` (bytes): File content as bytes
- `sharepoint_path` (str): Destination folder in SharePoint
- `file_name` (str): Name for the file in SharePoint

**Returns:** Response JSON from upload

**Example:**
```python
data = b"Hello, World!"
sp_manager.upload_file_from_memory(data, "Documents", "hello.txt")
```

#### `delete_file(file_path)`

Delete a file from SharePoint.

**Parameters:**
- `file_path` (str): Path to file in SharePoint

**Returns:** True if successful

**Example:**
```python
sp_manager.delete_file("old_files/temp.txt")
```

#### `move_file(file_path, destination_folder_path)`

Move a file to a different folder.

**Parameters:**
- `file_path` (str): Current path to file
- `destination_folder_path` (str): Destination folder path

**Returns:** True if successful

**Example:**
```python
sp_manager.move_file("temp/data.csv", "Archive/2024")
```

---

### Folder Operations

#### `download_folder(folder_path, local_directory=None)`

Recursively download an entire folder and its contents.

**Parameters:**
- `folder_path` (str): Path to folder in SharePoint
- `local_directory` (str, optional): Local directory to save folder (defaults to folder name)

**Returns:** Path to downloaded folder (str)

**Example:**
```python
sp_manager.download_folder("Reports/2024", "/tmp/reports")
```

#### `delete_folder(folder_path)`

Delete a folder and all its contents (permanent operation).

**Parameters:**
- `folder_path` (str): Path to folder in SharePoint

**Returns:** True if successful

**Warning:** This operation is permanent and will delete all files and subfolders.

**Example:**
```python
sp_manager.delete_folder("TempFolder")
```

#### `move_folder(folder_path, destination_parent_folder_path)`

Move a folder to a different location.

**Parameters:**
- `folder_path` (str): Current path to folder
- `destination_parent_folder_path` (str): Destination parent folder path

**Returns:** True if successful

**Example:**
```python
sp_manager.move_folder("OldProject", "Archive/Projects")
```

---

### Search Operations

#### File Search

##### `search_files_by_suffix(suffix, folder_path="")`

Search for files with a specific suffix in a folder (non-recursive).

**Parameters:**
- `suffix` (str): File suffix/extension (e.g., ".csv", ".pdf", "txt")
- `folder_path` (str, optional): Folder to search in (empty string for root)

**Returns:** List of `ItemInfo` objects

**Example:**
```python
# Search for CSV files in root
csv_files = sp_manager.search_files_by_suffix(".csv")

# Search for PDFs in specific folder
pdf_files = sp_manager.search_files_by_suffix(".pdf", "Reports/2024")

# Suffix without dot is auto-added
txt_files = sp_manager.search_files_by_suffix("txt")
```

##### `search_files_by_suffix_recursive(suffix, folder_path="")`

Recursively search for files with a specific suffix.

**Parameters:**
- `suffix` (str): File suffix/extension
- `folder_path` (str, optional): Starting folder for search

**Returns:** List of `ItemInfo` objects

**Example:**
```python
# Search all CSV files recursively from root
all_csvs = sp_manager.search_files_by_suffix_recursive(".csv")

# Search recursively from specific folder
project_docs = sp_manager.search_files_by_suffix_recursive(".docx", "Projects")
```

#### Folder Search

##### `search_folders_by_suffix(suffix, folder_path="")`

Search for folders with a specific suffix (non-recursive).

**Parameters:**
- `suffix` (str): Folder suffix (e.g., ".gdb", ".bundle")
- `folder_path` (str, optional): Folder to search in

**Returns:** List of `ItemInfo` objects

**Example:**
```python
# Search for GDB folders in root
gdb_folders = sp_manager.search_folders_by_suffix(".gdb")

# Search in specific location
backup_folders = sp_manager.search_folders_by_suffix(".backup", "Archive")
```

##### `search_folders_by_suffix_recursive(suffix, folder_path="")`

Recursively search for folders with a specific suffix.

**Parameters:**
- `suffix` (str): Folder suffix
- `folder_path` (str, optional): Starting folder for search

**Returns:** List of `ItemInfo` objects

**Example:**
```python
# Search all .gdb folders recursively
all_gdb = sp_manager.search_folders_by_suffix_recursive(".gdb")
```

---

### Unified Operations (Auto-detect File/Folder)

#### `download_item(item_path, local_path=None)`

Download a file or folder (auto-detects type).

**Parameters:**
- `item_path` (str): Path to item in SharePoint
- `local_path` (str, optional): Local path to save item

**Returns:**
- For files: BytesIO or local file path
- For folders: Path to downloaded folder

**Example:**
```python
# Downloads file or folder automatically
sp_manager.download_item("SomeItem")
```

#### `delete_item(item_path)`

Delete a file or folder (auto-detects type).

**Parameters:**
- `item_path` (str): Path to item in SharePoint

**Returns:** True if successful

**Example:**
```python
sp_manager.delete_item("OldItem")
```

#### `move_item(item_path, destination_folder_path)`

Move a file or folder (auto-detects type).

**Parameters:**
- `item_path` (str): Current path to item
- `destination_folder_path` (str): Destination folder path

**Returns:** True if successful

**Example:**
```python
sp_manager.move_item("SomeItem", "Archive")
```

---

### Data Classes

#### `ItemInfo`

Data class containing item information from SharePoint.

**Attributes:**
- `name` (str): Item name
- `path` (str): Full path to item
- `size` (int): Size in bytes
- `modified` (str): Last modified datetime (ISO format)
- `id` (str): SharePoint item ID
- `webUrl` (str): Web URL to item

**Aliases:** `FileInfo` and `FolderInfo` are aliases for `ItemInfo` (backward compatibility)

**Example:**
```python
files = sp_manager.search_files_by_suffix(".csv")
for file in files:
    print(f"Name: {file.name}")
    print(f"Path: {file.path}")
    print(f"Size: {file.size} bytes")
    print(f"Modified: {file.modified}")
    print(f"URL: {file.webUrl}")
```

---

## Complete Example

```python
from sharepointer.sharepoint import SharePointManager
import os
from dotenv import load_dotenv

# Load configuration
load_dotenv()

# Initialize
sp = SharePointManager(
    tenant_id=os.getenv('TENANT_ID'),
    client_id=os.getenv('CLIENT_ID'),
    client_secret=os.getenv('CLIENT_SECRET'),
    site_name="contoso"
)

# Setup site and drive
sp.get_site_id("/sites/TeamSite")
sp.get_drive_id("Documents")

# Search for files
csv_files = sp.search_files_by_suffix_recursive(".csv", "Reports")
print(f"Found {len(csv_files)} CSV files")

# Download files
for file in csv_files:
    content = sp.download_file(file.path)
    print(f"Downloaded: {file.name}")

# Upload a file
sp.upload_file("/tmp/report.pdf", "Reports/2024", "annual_report.pdf")

# Search for folders
gdb_folders = sp.search_folders_by_suffix(".gdb")
if gdb_folders:
    # Download first matching folder
    folder = gdb_folders[0]
    sp.download_folder(folder.name, "/tmp/geodatabase")

# Move items
sp.move_item("OldData.csv", "Archive/2023")

# Clean up old files
sp.delete_item("TempFolder")
```

---

## Error Handling

All methods raise exceptions on errors. Common exceptions:

- `Exception`: Generic errors (e.g., "Drive ID not set")
- `requests.exceptions.HTTPError`: HTTP errors from Graph API
  - 404: Item not found
  - 401: Authentication failed
  - 403: Permission denied

**Example:**
```python
try:
    sp.download_file("nonexistent.txt")
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 404:
        print("File not found")
    else:
        print(f"Error: {e.response.status_code}")
except Exception as e:
    print(f"General error: {str(e)}")
```

---

## Testing

The library includes comprehensive pytest tests:

```bash
# Run all tests
pytest tests/

# Run specific test file
pytest tests/test_file_operations.py

# Run with verbose output
pytest tests/ -v

# Run specific test
pytest tests/test_file_operations.py::TestDownloadFile::test_download_file_to_memory
```

### Test Structure

- `tests/test_authentication_and_init.py` - Authentication and initialization
- `tests/test_file_operations.py` - File upload, download, delete, move
- `tests/test_folder_operations.py` - Folder operations and searches
- `tests/test_search_operations.py` - File search operations
- `tests/test_dataclasses.py` - ItemInfo dataclass tests
- `tests/test_url_helpers.py` - URL construction helpers
- `tests/conftest.py` - Pytest fixtures and configuration

---

## Best Practices

### 1. Always Initialize Properly

```python
# ✅ Good
sp.get_site_id()
sp.get_drive_id()
sp.download_file("file.txt")

# ❌ Bad - will raise "Drive ID not set" error
sp.download_file("file.txt")
```

### 2. Use Suffix Auto-Detection

```python
# Both work the same
sp.search_files_by_suffix(".csv")
sp.search_files_by_suffix("csv")  # Dot is added automatically
```

### 3. Use Unified Methods When Type is Unknown

```python
# Auto-detects if item is file or folder
sp.download_item("UnknownItem")
sp.delete_item("UnknownItem")
sp.move_item("UnknownItem", "Archive")
```

### 4. Handle Paths Consistently

```python
# Root folder
sp.search_files_by_suffix(".csv", "")  # Root
sp.search_files_by_suffix(".csv")      # Also root (default)

# Nested folders (no leading slash)
sp.search_files_by_suffix(".csv", "Reports/2024")
```

---

## Limitations

1. **Large File Upload**: Files larger than 4MB should use the upload session API (not currently implemented)
2. **Rate Limiting**: The Microsoft Graph API has rate limits. Consider implementing retry logic for production use
3. **Concurrent Operations**: No built-in concurrency support. Implement your own if needed
4. **Permissions**: Requires appropriate SharePoint permissions in Azure AD

---

## Troubleshooting

### Authentication Errors

**Problem:** "Authentication failed" error

**Solutions:**
- Verify Azure AD app credentials
- Check API permissions are granted and admin consented
- Ensure client secret hasn't expired

### Drive ID Not Set

**Problem:** "Drive ID not set" error

**Solution:**
```python
sp.get_drive_id()  # Call this before any file/folder operations
```

### 404 Not Found

**Problem:** Item not found errors

**Solutions:**
- Verify item path is correct (no leading slash for items)
- Check item exists in SharePoint
- Ensure you have permissions to access the item

### Site Name Issues

**Problem:** Can't connect to site

**Solutions:**
```python
# If full URL: contoso.sharepoint.com
site_name = "contoso"

# If full URL: contoso.sharepoint.com/sites/TeamSite
site_name = "contoso"
site_path = "/sites/TeamSite"
```

---

## Changelog

### Version 1.0.0 (Current)
- Initial release
- File operations (upload, download, delete, move)
- Folder operations (download, delete, move)
- Search operations (files and folders, recursive and non-recursive)
- Unified operations (auto-detect file/folder type)
- ItemInfo dataclass with backward compatible aliases
- Helper method to eliminate code duplication
- Comprehensive test suite (78 tests)

---

## License

[Add your license here]

---

## Support

For issues, questions, or contributions, please [add contact information or repository link].

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "sharepointer",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.13",
    "maintainer_email": null,
    "keywords": "automation, file-management, office365, python, sharepoint",
    "author": null,
    "author_email": "Wouter van Riel <wouter.van.riel@infralytics.org>",
    "download_url": "https://files.pythonhosted.org/packages/23/cc/6fc6da5fd781fc5094b9759ccb3e78fe8df8de33c41ad6ef7f068875f577/sharepointer-0.1.1.tar.gz",
    "platform": null,
    "description": "# SharePoint Manager\n\nA Python library for interacting with SharePoint document libraries using the Microsoft Graph API.\n\n## Features\n\n- \ud83d\udd10 **Authentication**: MSAL-based authentication with Azure AD\n- \ud83d\udcc1 **File Operations**: Upload, download, delete, and move files\n- \ud83d\udcc2 **Folder Operations**: Download, delete, and move folders recursively\n- \ud83d\udd0d **Search Operations**: Search for files and folders by suffix (recursive and non-recursive)\n- \ud83c\udfaf **Unified API**: Single methods that auto-detect file vs folder types\n\n## Installation\n\n### Prerequisites\n\n- Python 3.13+\n- Microsoft Azure AD application with SharePoint permissions: `Sites.ReadWrite.All` and `\nFiles.ReadWrite.All`\n\n### Dependencies\n\n```bash\npip install msal requests python-dotenv\n```\n\n## Configuration\n\n### 1. Azure AD App Registration\n\n#### Steps to Register an App in Azure AD and Get Credentials:\n\n**1. Register an App in Azure AD**\n\n- Go to https://portal.azure.com\n- Navigate to **Azure Active Directory** \u2192 **App registrations** \u2192 **New registration**\n- Fill in:\n    - **Name**: Something like \"SharePoint Python App\"\n    - **Supported account types**: \"Accounts in this organizational directory only\"\n    - **Redirect URI**: Leave blank (not needed for app-only)\n- Click **Register**\n\n**2. Get the Client ID**\n\n- After registration, you'll see the Overview page\n- Copy the **Application (client) ID** - this is your `CLIENT_ID`\n\n**3. Create a Client Secret**\n\n- In the same app registration, go to **Certificates & secrets** (left menu)\n- Click **New client secret**\n- Add a description (e.g., \"Python SharePoint Access\")\n- Choose an expiration period (recommended: 12-24 months)\n- Click **Add**\n- **IMPORTANT**: Copy the **Value** immediately - this is your `CLIENT_SECRET` and you won't be able to see it again!\n\n**4. Grant SharePoint Permissions**\n\n- Go to **API permissions** (left menu)\n- Click **Add a permission**\n- Choose **SharePoint**\n- Select **Application permissions** (not Delegated)\n- Add these permissions:\n    - `Sites.ReadWrite.All` (for read/write access to all site collections)\n    - Or `Sites.FullControl.All` (for full control)\n- Click **Add permissions**\n- **IMPORTANT**: Click **Grant admin consent for your organization** (requires admin privileges)\n\n**5. Get Your Tenant ID**\n\n- Go back to **Azure Active Directory** \u2192 **Overview**\n- Copy the **Tenant ID** (also called Directory ID)\n\n### 2. Environment Variables\n\nCreate a `.env` file in your project root:\n\n```env\nTENANT_ID=your-tenant-id\nCLIENT_ID=your-client-id\nCLIENT_SECRET=your-client-secret\n```\n\n## Quick Start\n\n```python\nfrom sharepointer.sharepoint import SharePointManager\nimport os\nfrom dotenv import load_dotenv\n\n# Load environment variables\nload_dotenv()\n\n# Initialize SharePoint Manager\nsp_manager = SharePointManager(\n    tenant_id=os.getenv('TENANT_ID'),\n    client_id=os.getenv('CLIENT_ID'),\n    client_secret=os.getenv('CLIENT_SECRET'),\n    site_name=\"your-site-name\"  # e.g., \"contoso\" for contoso.sharepoint.com\n)\n\n# Get site and drive IDs\nsp_manager.get_site_id(\"/sites/YourSiteName\")  # Optional: specify site path\nsp_manager.get_drive_id(\"Documents\")  # Document library name\n```\n\n## API Reference\n\n### Authentication & Initialization\n\n#### `SharePointManager(tenant_id, client_id, client_secret, site_name)`\n\nInitialize the SharePoint Manager with authentication credentials.\n\n**Parameters:**\n- `tenant_id` (str): Azure AD tenant ID\n- `client_id` (str): Application (client) ID\n- `client_secret` (str): Client secret value\n- `site_name` (str): SharePoint site name (without .sharepoint.com)\n\n**Example:**\n```python\nsp_manager = SharePointManager(\n    tenant_id=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n    client_id=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n    client_secret=\"your-secret\",\n    site_name=\"contoso\"\n)\n```\n\n#### `get_site_id(site_path=\"\")`\n\nRetrieve the SharePoint site ID.\n\n**Parameters:**\n- `site_path` (str, optional): Site path (e.g., \"/sites/YourSite\")\n\n**Returns:** Site ID (str)\n\n#### `get_drive_id(drive_name=\"Documents\")`\n\nRetrieve the document library (drive) ID.\n\n**Parameters:**\n- `drive_name` (str, optional): Document library name (default: \"Documents\")\n\n**Returns:** Drive ID (str)\n\n---\n\n### File Operations\n\n#### `download_file(file_path, local_path=None)`\n\nDownload a file from SharePoint.\n\n**Parameters:**\n- `file_path` (str): Path to file in SharePoint (e.g., \"folder/data.csv\")\n- `local_path` (str, optional): Local path to save file. If None, returns BytesIO object\n\n**Returns:**\n- BytesIO object (if local_path is None)\n- Local file path (str) (if local_path is provided)\n\n**Example:**\n```python\n# Download to memory\nfile_content = sp_manager.download_file(\"reports/data.csv\")\n\n# Download to local file\nsp_manager.download_file(\"reports/data.csv\", \"/tmp/data.csv\")\n```\n\n#### `upload_file(local_file_path, sharepoint_path, file_name=None)`\n\nUpload a file to SharePoint.\n\n**Parameters:**\n- `local_file_path` (str): Path to local file\n- `sharepoint_path` (str): Destination folder in SharePoint (empty string for root)\n- `file_name` (str, optional): Name for file in SharePoint (defaults to local filename)\n\n**Returns:** Response JSON from upload\n\n**Example:**\n```python\nsp_manager.upload_file(\n    local_file_path=\"/tmp/report.pdf\",\n    sharepoint_path=\"Reports/2024\",\n    file_name=\"annual_report.pdf\"\n)\n```\n\n#### `upload_file_from_memory(file_content, sharepoint_path, file_name)`\n\nUpload a file from memory (bytes).\n\n**Parameters:**\n- `file_content` (bytes): File content as bytes\n- `sharepoint_path` (str): Destination folder in SharePoint\n- `file_name` (str): Name for the file in SharePoint\n\n**Returns:** Response JSON from upload\n\n**Example:**\n```python\ndata = b\"Hello, World!\"\nsp_manager.upload_file_from_memory(data, \"Documents\", \"hello.txt\")\n```\n\n#### `delete_file(file_path)`\n\nDelete a file from SharePoint.\n\n**Parameters:**\n- `file_path` (str): Path to file in SharePoint\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.delete_file(\"old_files/temp.txt\")\n```\n\n#### `move_file(file_path, destination_folder_path)`\n\nMove a file to a different folder.\n\n**Parameters:**\n- `file_path` (str): Current path to file\n- `destination_folder_path` (str): Destination folder path\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.move_file(\"temp/data.csv\", \"Archive/2024\")\n```\n\n---\n\n### Folder Operations\n\n#### `download_folder(folder_path, local_directory=None)`\n\nRecursively download an entire folder and its contents.\n\n**Parameters:**\n- `folder_path` (str): Path to folder in SharePoint\n- `local_directory` (str, optional): Local directory to save folder (defaults to folder name)\n\n**Returns:** Path to downloaded folder (str)\n\n**Example:**\n```python\nsp_manager.download_folder(\"Reports/2024\", \"/tmp/reports\")\n```\n\n#### `delete_folder(folder_path)`\n\nDelete a folder and all its contents (permanent operation).\n\n**Parameters:**\n- `folder_path` (str): Path to folder in SharePoint\n\n**Returns:** True if successful\n\n**Warning:** This operation is permanent and will delete all files and subfolders.\n\n**Example:**\n```python\nsp_manager.delete_folder(\"TempFolder\")\n```\n\n#### `move_folder(folder_path, destination_parent_folder_path)`\n\nMove a folder to a different location.\n\n**Parameters:**\n- `folder_path` (str): Current path to folder\n- `destination_parent_folder_path` (str): Destination parent folder path\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.move_folder(\"OldProject\", \"Archive/Projects\")\n```\n\n---\n\n### Search Operations\n\n#### File Search\n\n##### `search_files_by_suffix(suffix, folder_path=\"\")`\n\nSearch for files with a specific suffix in a folder (non-recursive).\n\n**Parameters:**\n- `suffix` (str): File suffix/extension (e.g., \".csv\", \".pdf\", \"txt\")\n- `folder_path` (str, optional): Folder to search in (empty string for root)\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search for CSV files in root\ncsv_files = sp_manager.search_files_by_suffix(\".csv\")\n\n# Search for PDFs in specific folder\npdf_files = sp_manager.search_files_by_suffix(\".pdf\", \"Reports/2024\")\n\n# Suffix without dot is auto-added\ntxt_files = sp_manager.search_files_by_suffix(\"txt\")\n```\n\n##### `search_files_by_suffix_recursive(suffix, folder_path=\"\")`\n\nRecursively search for files with a specific suffix.\n\n**Parameters:**\n- `suffix` (str): File suffix/extension\n- `folder_path` (str, optional): Starting folder for search\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search all CSV files recursively from root\nall_csvs = sp_manager.search_files_by_suffix_recursive(\".csv\")\n\n# Search recursively from specific folder\nproject_docs = sp_manager.search_files_by_suffix_recursive(\".docx\", \"Projects\")\n```\n\n#### Folder Search\n\n##### `search_folders_by_suffix(suffix, folder_path=\"\")`\n\nSearch for folders with a specific suffix (non-recursive).\n\n**Parameters:**\n- `suffix` (str): Folder suffix (e.g., \".gdb\", \".bundle\")\n- `folder_path` (str, optional): Folder to search in\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search for GDB folders in root\ngdb_folders = sp_manager.search_folders_by_suffix(\".gdb\")\n\n# Search in specific location\nbackup_folders = sp_manager.search_folders_by_suffix(\".backup\", \"Archive\")\n```\n\n##### `search_folders_by_suffix_recursive(suffix, folder_path=\"\")`\n\nRecursively search for folders with a specific suffix.\n\n**Parameters:**\n- `suffix` (str): Folder suffix\n- `folder_path` (str, optional): Starting folder for search\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search all .gdb folders recursively\nall_gdb = sp_manager.search_folders_by_suffix_recursive(\".gdb\")\n```\n\n---\n\n### Unified Operations (Auto-detect File/Folder)\n\n#### `download_item(item_path, local_path=None)`\n\nDownload a file or folder (auto-detects type).\n\n**Parameters:**\n- `item_path` (str): Path to item in SharePoint\n- `local_path` (str, optional): Local path to save item\n\n**Returns:**\n- For files: BytesIO or local file path\n- For folders: Path to downloaded folder\n\n**Example:**\n```python\n# Downloads file or folder automatically\nsp_manager.download_item(\"SomeItem\")\n```\n\n#### `delete_item(item_path)`\n\nDelete a file or folder (auto-detects type).\n\n**Parameters:**\n- `item_path` (str): Path to item in SharePoint\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.delete_item(\"OldItem\")\n```\n\n#### `move_item(item_path, destination_folder_path)`\n\nMove a file or folder (auto-detects type).\n\n**Parameters:**\n- `item_path` (str): Current path to item\n- `destination_folder_path` (str): Destination folder path\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.move_item(\"SomeItem\", \"Archive\")\n```\n\n---\n\n### Data Classes\n\n#### `ItemInfo`\n\nData class containing item information from SharePoint.\n\n**Attributes:**\n- `name` (str): Item name\n- `path` (str): Full path to item\n- `size` (int): Size in bytes\n- `modified` (str): Last modified datetime (ISO format)\n- `id` (str): SharePoint item ID\n- `webUrl` (str): Web URL to item\n\n**Aliases:** `FileInfo` and `FolderInfo` are aliases for `ItemInfo` (backward compatibility)\n\n**Example:**\n```python\nfiles = sp_manager.search_files_by_suffix(\".csv\")\nfor file in files:\n    print(f\"Name: {file.name}\")\n    print(f\"Path: {file.path}\")\n    print(f\"Size: {file.size} bytes\")\n    print(f\"Modified: {file.modified}\")\n    print(f\"URL: {file.webUrl}\")\n```\n\n---\n\n## Complete Example\n\n```python\nfrom sharepointer.sharepoint import SharePointManager\nimport os\nfrom dotenv import load_dotenv\n\n# Load configuration\nload_dotenv()\n\n# Initialize\nsp = SharePointManager(\n    tenant_id=os.getenv('TENANT_ID'),\n    client_id=os.getenv('CLIENT_ID'),\n    client_secret=os.getenv('CLIENT_SECRET'),\n    site_name=\"contoso\"\n)\n\n# Setup site and drive\nsp.get_site_id(\"/sites/TeamSite\")\nsp.get_drive_id(\"Documents\")\n\n# Search for files\ncsv_files = sp.search_files_by_suffix_recursive(\".csv\", \"Reports\")\nprint(f\"Found {len(csv_files)} CSV files\")\n\n# Download files\nfor file in csv_files:\n    content = sp.download_file(file.path)\n    print(f\"Downloaded: {file.name}\")\n\n# Upload a file\nsp.upload_file(\"/tmp/report.pdf\", \"Reports/2024\", \"annual_report.pdf\")\n\n# Search for folders\ngdb_folders = sp.search_folders_by_suffix(\".gdb\")\nif gdb_folders:\n    # Download first matching folder\n    folder = gdb_folders[0]\n    sp.download_folder(folder.name, \"/tmp/geodatabase\")\n\n# Move items\nsp.move_item(\"OldData.csv\", \"Archive/2023\")\n\n# Clean up old files\nsp.delete_item(\"TempFolder\")\n```\n\n---\n\n## Error Handling\n\nAll methods raise exceptions on errors. Common exceptions:\n\n- `Exception`: Generic errors (e.g., \"Drive ID not set\")\n- `requests.exceptions.HTTPError`: HTTP errors from Graph API\n  - 404: Item not found\n  - 401: Authentication failed\n  - 403: Permission denied\n\n**Example:**\n```python\ntry:\n    sp.download_file(\"nonexistent.txt\")\nexcept requests.exceptions.HTTPError as e:\n    if e.response.status_code == 404:\n        print(\"File not found\")\n    else:\n        print(f\"Error: {e.response.status_code}\")\nexcept Exception as e:\n    print(f\"General error: {str(e)}\")\n```\n\n---\n\n## Testing\n\nThe library includes comprehensive pytest tests:\n\n```bash\n# Run all tests\npytest tests/\n\n# Run specific test file\npytest tests/test_file_operations.py\n\n# Run with verbose output\npytest tests/ -v\n\n# Run specific test\npytest tests/test_file_operations.py::TestDownloadFile::test_download_file_to_memory\n```\n\n### Test Structure\n\n- `tests/test_authentication_and_init.py` - Authentication and initialization\n- `tests/test_file_operations.py` - File upload, download, delete, move\n- `tests/test_folder_operations.py` - Folder operations and searches\n- `tests/test_search_operations.py` - File search operations\n- `tests/test_dataclasses.py` - ItemInfo dataclass tests\n- `tests/test_url_helpers.py` - URL construction helpers\n- `tests/conftest.py` - Pytest fixtures and configuration\n\n---\n\n## Best Practices\n\n### 1. Always Initialize Properly\n\n```python\n# \u2705 Good\nsp.get_site_id()\nsp.get_drive_id()\nsp.download_file(\"file.txt\")\n\n# \u274c Bad - will raise \"Drive ID not set\" error\nsp.download_file(\"file.txt\")\n```\n\n### 2. Use Suffix Auto-Detection\n\n```python\n# Both work the same\nsp.search_files_by_suffix(\".csv\")\nsp.search_files_by_suffix(\"csv\")  # Dot is added automatically\n```\n\n### 3. Use Unified Methods When Type is Unknown\n\n```python\n# Auto-detects if item is file or folder\nsp.download_item(\"UnknownItem\")\nsp.delete_item(\"UnknownItem\")\nsp.move_item(\"UnknownItem\", \"Archive\")\n```\n\n### 4. Handle Paths Consistently\n\n```python\n# Root folder\nsp.search_files_by_suffix(\".csv\", \"\")  # Root\nsp.search_files_by_suffix(\".csv\")      # Also root (default)\n\n# Nested folders (no leading slash)\nsp.search_files_by_suffix(\".csv\", \"Reports/2024\")\n```\n\n---\n\n## Limitations\n\n1. **Large File Upload**: Files larger than 4MB should use the upload session API (not currently implemented)\n2. **Rate Limiting**: The Microsoft Graph API has rate limits. Consider implementing retry logic for production use\n3. **Concurrent Operations**: No built-in concurrency support. Implement your own if needed\n4. **Permissions**: Requires appropriate SharePoint permissions in Azure AD\n\n---\n\n## Troubleshooting\n\n### Authentication Errors\n\n**Problem:** \"Authentication failed\" error\n\n**Solutions:**\n- Verify Azure AD app credentials\n- Check API permissions are granted and admin consented\n- Ensure client secret hasn't expired\n\n### Drive ID Not Set\n\n**Problem:** \"Drive ID not set\" error\n\n**Solution:**\n```python\nsp.get_drive_id()  # Call this before any file/folder operations\n```\n\n### 404 Not Found\n\n**Problem:** Item not found errors\n\n**Solutions:**\n- Verify item path is correct (no leading slash for items)\n- Check item exists in SharePoint\n- Ensure you have permissions to access the item\n\n### Site Name Issues\n\n**Problem:** Can't connect to site\n\n**Solutions:**\n```python\n# If full URL: contoso.sharepoint.com\nsite_name = \"contoso\"\n\n# If full URL: contoso.sharepoint.com/sites/TeamSite\nsite_name = \"contoso\"\nsite_path = \"/sites/TeamSite\"\n```\n\n---\n\n## Changelog\n\n### Version 1.0.0 (Current)\n- Initial release\n- File operations (upload, download, delete, move)\n- Folder operations (download, delete, move)\n- Search operations (files and folders, recursive and non-recursive)\n- Unified operations (auto-detect file/folder type)\n- ItemInfo dataclass with backward compatible aliases\n- Helper method to eliminate code duplication\n- Comprehensive test suite (78 tests)\n\n---\n\n## License\n\n[Add your license here]\n\n---\n\n## Support\n\nFor issues, questions, or contributions, please [add contact information or repository link].\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A tool to manage SharePoint files and folders.",
    "version": "0.1.1",
    "project_urls": {
        "Bug Tracker": "https://github.com/woutervriel/sharepointmanager/issues",
        "Homepage": "https://github.com/woutervriel/sharepointmanager",
        "Repository": "https://github.com/woutervriel/sharepointmanager.git"
    },
    "split_keywords": [
        "automation",
        " file-management",
        " office365",
        " python",
        " sharepoint"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ae63dc5e3c088d056c34da2990470da880fbb21c684895ee08bcd27003781fe3",
                "md5": "2c1cbc1428e45a40ca23a3de5a2250c2",
                "sha256": "7fa893b32dbc2fee181abd8f925297e406674065f95282e3cf6777d27f1e2dbe"
            },
            "downloads": -1,
            "filename": "sharepointer-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2c1cbc1428e45a40ca23a3de5a2250c2",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.13",
            "size": 6072,
            "upload_time": "2025-11-06T21:12:29",
            "upload_time_iso_8601": "2025-11-06T21:12:29.890457Z",
            "url": "https://files.pythonhosted.org/packages/ae/63/dc5e3c088d056c34da2990470da880fbb21c684895ee08bcd27003781fe3/sharepointer-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "23cc6fc6da5fd781fc5094b9759ccb3e78fe8df8de33c41ad6ef7f068875f577",
                "md5": "7ed148a5031cdcc3b362ac766e9fe73f",
                "sha256": "2bcd0b8a5222ce5b3f2aac4f131470a118b295c27b852900e0cdf326cb0741f5"
            },
            "downloads": -1,
            "filename": "sharepointer-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "7ed148a5031cdcc3b362ac766e9fe73f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.13",
            "size": 64174,
            "upload_time": "2025-11-06T21:12:31",
            "upload_time_iso_8601": "2025-11-06T21:12:31.191294Z",
            "url": "https://files.pythonhosted.org/packages/23/cc/6fc6da5fd781fc5094b9759ccb3e78fe8df8de33c41ad6ef7f068875f577/sharepointer-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-06 21:12:31",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "woutervriel",
    "github_project": "sharepointmanager",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "sharepointer"
}
        
Elapsed time: 2.38163s