User and Group Management#

This vignette provides a comprehensive guide to managing users and groups in a Teamworks AMS instance using teamworksams, covering get_user(), get_group(), edit_user(), and create_user(). It equips you to fetch user and group data, update user accounts, and create new users withpractical workflows, detailed examples, best practices, and troubleshooting tips. See Functions and Classes for detailed function documentation and Exporting Data and Importing Data for additional data-related workflows.

Overview#

teamworksams simplifies user and group management through the Teamworks AMS API, offering functions to:

  • Fetch Users: Retrieve user data (e.g., IDs, names, account info, groups), optionally filtered by attributes like group or email with get_user().

  • Fetch Groups: List groups accessible to the user with get_group().

  • Update Users: Modify user fields (e.g., email, name) for existing accounts with edit_user().

  • Create Users: Add new user accounts with required and optional attributes with create_user()

All examples use the placeholder AMS instance URL https://example.smartabase.com/site, username username, and password password, with credentials managed via a .env file for security and ease of use.

Prerequisites#

Ensure teamworksams is installed and credentials are configured, as described in Getting Started with teamworksams. The recommended setup is a .env file:

.env#
AMS_URL = https://example.smartabase.com/site
AMS_USERNAME = username
AMS_PASSWORD = password

Load credentials with python-dotenv:

from dotenv import load_dotenv
load_dotenv()

Alternatively, use os, direct arguments, or keyring (see Managing Credentials, Authentication, and Caching). Required dependencies (installed with teamworksams): pandas, requests, python-dotenv, tqdm.

Fetching User Data with get_user()#

The get_user() function retrieves user data as a pandas.DataFrame, with optional filtering using a UserFilter object to narrow results by attributes like username, email, group, or about field. It’s ideal for auditing user accounts, generating reports, or identifying specific users.

Basic Usage

Fetch all accessible users:

from teamworksams import get_user, UserOption

df = get_user(
    url = "https://example.smartabase.com/site",
    option = UserOption(
        columns = ["user_id", "first_name", "last_name", "email", "groups"],
        interactive_mode = True
    )
)

print(df)

Output:

ℹ Fetching user data...
✔ Retrieved 5 users.
   user_id first_name last_name                email      groups
0    12345       John       Doe   john.doe@example.com     [TeamA]
1    12346       Jane     Smith jane.smith@example.com     [TeamB]
...

Filtering Users

Use UserFilter to retrieve users in a specific group:

from teamworksams import UserFilter

df = get_user(
    url = "https://example.smartabase.com/site",
    filter = UserFilter(user_key = "group", user_value = "TeamA"),
    option = UserOption(interactive_mode = True)
)

print(df[['user_id', 'first_name', 'groups']])

Output:

ℹ Fetching user data...
✔ Retrieved 2 users.
   user_id first_name  groups
0    12345       John  [TeamA]
1    12347        Bob  [TeamA]

Customizing Output

Select specific columns:

df = get_user(
    url = "https://example.smartabase.com/site",
    option = UserOption(columns = ["user_id", "email"], interactive_mode = True)
)
print(df)

Fetch users by email:

df = get_user(
    url = "https://example.smartabase.com/site",
    filter = UserFilter(user_key = "email", user_value = ["john.doe@example.com"])
)
print(df[['user_id', 'email']])

Best Practices

  • Use UserFilter to target specific users for efficiency.

  • Limit columns with option.columns to optimize DataFrame size.

  • Enable interactive_mode for real-time feedback during long operations.

Fetching Group Data with get_group()#

The get_group() function retrieves a list of groups (e.g., teams, departments) as a pandas.DataFrame. It’s useful for auditing group structures or preparing data for user assignments.

Basic Usage

Fetch all accessible groups:

from teamworksams import get_group, GroupOption

group_df = get_group(
    url = "https://example.smartabase.com/site",
    option = GroupOption(interactive_mode = True)
)

print(group_df)

Output:

ℹ Fetching group data...
✔ Retrieved 3 groups.
      name
0  TeamA
1  TeamB
2  TeamC

Best Practices

  • Use guess_col_type=True in GroupOption() to ensure consistent data types.

  • Cache the client for efficiency when combining with get_user().

Updating User Profiles with edit_user()#

The edit_user() function updates existing user fields (e.g., email, name) based on a mapping DataFrame, using a user key (e.g., username, email) to identify users. It returns a pandas.DataFrame of results, making it easy to track updates and issues. This is a powerful function as it will only update the fields based on columns provided in the mapping_df - meaning that it’s possible to just update things like username, email, e.g. alone.

Basic Usage

Update user emails:

import pandas as pd
from teamworksams import edit_user, UserOption
mapping_df = pd.DataFrame({
    "username": ["john.doe", "jane.smith"],
    "email": ["john.doe@new.com", "jane.smith@new.com"]
})

results_df = edit_user(
    mapping_df = mapping_df,
    user_key = "username",
    url = "https://example.smartabase.com/site",
    option = UserOption(interactive_mode = True)
)

print(results_df)

Output:

ℹ Fetching all user data...
✔ Retrieved 30 users.
ℹ Attempting to map 2 users using username from provided dataframe...
ℹ Successfully mapped 2 users.
ℹ Updating 2 users...
Processing users: 100%|██████████| 2/2 [00:04<00:00, 3.46it/s]
✔ Successfully updated 2 users.
Empty DataFrame
Columns: [user_id, user_key, status, reason]
Index: []

Updating Multiple Fields

Update names and emails using about as the key:

mapping_df = pd.DataFrame({
    "about": ["John Doe", "Jane Smith"],
    "first_name": ["Jonathan", "Janet"],
    "email": ["jonathan.doe@new.com", "janet.smith@new.com"]
})

results_df = edit_user(
    mapping_df = mapping_df,
    user_key = "about",
    url = "https://example.smartabase.com/site"
)

print(results_df)

Best Practices

  • Validate mapping_df columns against supported fields (e.g., first_name, email, dob).

  • Use interactive_mode for progress tracking and confirmation prompts.

  • Check results_df for successful updates and errors.

Creating New Users with create_user()#

The create_user() function adds new user accounts, requiring fields like first_name, username, and password. It returns a pandas.DataFrame of failed creations.

Basic Usage

Create a new user:

user_df = pd.DataFrame({
    "first_name": ["Alice"],
    "last_name": ["Yu"],
    "username": ["alice.yu"],
    "email": ["alice.yu@example.com"],
    "dob": ["1990-01-01"],
    "password": ["secure789"],
    "active": [True]
})

failed_df = create_user(
    user_df = user_df,
    url = "https://example.smartabase.com/site",
    option = UserOption(interactive_mode = True)
)

print(failed_df)

Output:

ℹ Creating 1 users...
✔ Successfully created 1 users.
Empty DataFrame
Columns: [user_key, reason]
Index: []

Creating Multiple Users

Create users with optional fields:

user_df = pd.DataFrame({
    "first_name": ["Bob", "Carol"],
    "last_name": ["Lee", "Wong"],
    "username": ["bob.lee", "carol.wong"],
    "email": ["bob.lee@example.com", "carol.wong@example.com"],
    "dob": ["1985-06-15", "1992-03-22"],
    "password": ["pass123", "pass456"],
    "active": [True, False],
    "sex": ["Male", "Female"],
    "uuid": ["025380", "024854"]
})
failed_df = create_user(user_df = user_df, url = "https://example.smartabase.com/site")

print(failed_df)

Best Practices

  • Ensure required fields (first_name, last_name, username, email, dob, password, active) are present in user_df.

  • Use strong passwords compliant with AMS policies.

  • Verify failed_df for creation issues.

Options and Usage Notes#

This section provides detailed guidance on using option classes (UserOption(), GroupOption(), and the filter class UserFilter() to customize user and group operations, along with key usage notes for caching, column selection, data validation, and interactive mode.

Option Classes

Each user and group function supports a specific option class to configure its behavior. These classes must be instantiated with parameters like interactive_mode, cache, and others. For example, to select specific columns in get_user():

from teamworksams import get_user, UserOption

df = get_user(
    url = "https://example.smartabase.com/site",
    option = UserOption(columns = ["user_id", "email"])
)

The option classes and their associated functions are:

Available parameters for UserFilter():

  • columns (Optional[List[str]]): Only for get_user(). List of column names to include in the output DataFrame (e.g., [‘user_id’, ‘first_name’, ‘email’]). If None, includes all available columns (e.g., ‘user_id’, ‘first_name’, ‘last_name’, ‘email’, ‘groups’, ‘about’, ‘active’, ‘dob’, ‘sex’, ‘uuid’). Defaults to None. Example:

    option = UserOption(columns = ["user_id", "email", "groups"])
    df = get_user(url = "...", option = option)
    
  • interactive_mode (bool): If True, displays status messages (e.g., “Retrieved 5 users” for get_user(), “Successfully updated 2 users” for edit_user()) and tqdm progress bars for edit_user() and create_user(). Set to False for silent execution in automated scripts. Defaults to True. Example:

    option = UserOption(interactive_mode = False)
    failed_df = create_user(..., option = option)  # No output
    
  • cache (bool): If True, reuses an existing AMSClient() via get_client(), reducing API calls for authentication or data retrieval. Set to False for fresh data, increasing API overhead. Defaults to True. See “Caching” below.

Available parameters for GroupOption():

  • guess_col_type (bool): If True, infers column data types in the output DataFrame (e.g., string for ‘name’), ensuring compatibility with operations like merging with get_user() results. Set to False to use default pandas types (e.g., object). Defaults to True. Example:

    option = GroupOption(guess_col_type = False)
    group_df = get_group(url = "...", option = option)
    print(group_df.dtypes)  # name: object
    
  • interactive_mode (bool): If True, displays status messages (e.g., “Retrieved 3 groups”). Set to False for silent execution. Defaults to True.

  • cache (bool): If True, reuses an existing AMSClient, reducing API calls. Defaults to True.

Filter Class

The UserFilter() class filters users in get_user() by attributes. For example, to filter by email:

from teamworksams import UserFilter
df = get_user(
    url = "https://example.smartabase.com/site",
    filter = UserFilter(user_key = "email", user_value = "john.doe@example.com")
)

Available parameters for UserFilter:

  • user_key (Optional[str]): Attribute to filter by. Must be one of ‘username’, ‘email’, ‘group’, or ‘about’. For example, ‘group’ filters by group membership (e.g., ‘TeamA’). If None, no filtering is applied. Defaults to None.

  • user_value (Optional[Union[str, List[str]]]): Value(s) to match for user_key. For example, ‘TeamA’ or [‘TeamA’, ‘TeamB’] for user_key="group". Case-sensitive. If None, no filtering is applied. Defaults to None. Example:

    filter = UserFilter(user_key = "username", user_value = ["john.doe", "jane.smith"])
    df = get_user(url = "...", filter = filter)
    

Valid user_key values and their user_value requirements:

  • username: AMS usernames (e.g., [“john.doe”]).

  • email: User emails (e.g., [”john.doe@example.com”]).

  • about: Full names (e.g., [“John Doe”]).

  • group: Group name (e.g., “TeamA”). Use get_group() to list groups.

Caching

When option.cache=True (default), functions reuse an existing AMSClient() created by get_client(), maintaining an authenticated session and reducing API calls for login or data retrieval. For example:

option = UserOption(cache = True)
user_df = get_user(url = "...", option = option)
group_df = get_group(url = "...", option = GroupOption(cache = True))  # Reuses client

Set cache=False for independent sessions, ensuring fresh data but increasing API overhead.

Column Selection

For get_user(), the columns parameter in UserOption() allows selecting specific columns from the API response. Supported columns include:

  • user_id: Unique AMS-generated user ID.

  • first_name, last_name: User’s name.

  • email: User’s email address.

  • groups: List of group memberships (e.g., [‘TeamA’]).

  • about: Full name (concatenation of first and last names).

  • active: Boolean indicating account status.

  • dob: Date of birth (format: YYYY-MM-DD or DD/MM/YYYY).

  • sex: Gender (e.g., “Male”, “Female”).

  • uuid: Optional unique identifier.

  • Others: Additional fields like known_as, language, sidebar_width (if available).

Example:

option = UserOption(columns = ["user_id", "email", "active"])
df = get_user(url = "...", option = option)
print(df.dtypes)  # user_id: int64, email: object, active: bool

Data Validation

For edit_user(), the mapping_df DataFrame must include a user_key column (one of ‘username’, ‘email’, ‘about’, ‘uuid’) and updatable columns from the supported list (e.g., first_name, email, dob, sex, active, uuid). Empty values are sent as empty strings. Example:

mapping_df = pd.DataFrame({
    "uuid": ["025380"],
    "email": ["new.email@example.com"],
    "active": [False]
})
results_df = edit_user(mapping_df = mapping_df, user_key = "uuid", url = "...")

For create_user(), the user_df DataFrame must include required columns: first_name, last_name, username, email, dob (format: YYYY-MM-DD or DD/MM/YYYY), password, active (boolean). Optional columns include uuid, known_as, sex, middle_names, language, sidebar_width. Example:

user_df = pd.DataFrame({
    "first_name": ["Alice"],
    "last_name": ["Yu"],
    "username": ["alice.yu"],
    "email": ["alice.yu@example.com"],
    "dob": ["1990-01-01"],
    "password": ["secure789"],
    "active": [True],
    "sex": ["Female"]
})
failed_df = create_user(user_df = user_df, url = "...")

Validate data before submission to avoid AMSError:

required = ["first_name", "last_name", "username", "email", "dob", "password", "active"]
if not all(col in user_df.columns for col in required):
    print("Missing required columns:", set(required) - set(user_df.columns))
else:
    failed_df = create_user(user_df = user_df, url = "...")

Interactive Mode

When interactive_mode=True (default), functions display progress messages (e.g., “ℹ Creating 1 users”) and tqdm progress bars for edit_user() and create_user(), enhancing feedback in interactive environments. For edit_user(), it includes detailed success/failure summaries. Set interactive_mode=False for silent execution in automated pipelines:

option = UserOption(interactive_mode = False)
results_df = edit_user(..., option = option)  # No output

Error Handling#

teamworksams functions provide descriptive error messages via AMSError, with interactive feedback when interactive_mode is enabled. For simple scripts or interactive use (e.g., Jupyter notebooks), rely on these messages:

df = get_user(
    url = "https://example.smartabase.com/invalid",
    option = UserOption(interactive_mode = True)
)

Output:

✖ Failed to log username into https://example.smartabase.com/invalid: Invalid URL or login credentials...
AMSError: Invalid URL or login credentials...

Best Practices#

  • Security: Store credentials in .env or keyring to prevent exposure. Keep .env files in secure, private directories.

  • Data Validation: Verify DataFrame inputs before calling edit_user() or create_user():

    if mapping_df.empty:
        print("Error: Mapping DataFrame is empty")
    else:
        failed_df = edit_user(mapping_df, user_key = "username", url = "https://example.smartabase.com/site")
    
  • Interactive Mode: Enable for user-friendly feedback during operations.

  • Caching: Use a cached client for multi-call efficiency:

    from teamworksams import get_client
    client = get_client(url = "https://example.smartabase.com/site", cache = True)
    

Troubleshooting#

  • No Users Found:

    AMSError: No users returned from server...
    

    Solution: Check credentials and filter settings. Test without filters:

    df = get_user(url = "https://example.smartabase.com/site")
    
  • Invalid DataFrame:

    ValueError: mapping_df is empty...
    

    Solution: Ensure DataFrame is non-empty and includes required columns:

    print(mapping_df.columns)  # Verify columns
    failed_df = edit_user(mapping_df, user_key = "username", url = "https://example.smartabase.com/site")
    
  • API Errors:

    AMSError: Failed to fetch data... (status 503)...
    

    Solution: Check AMS server status. Retry with delay:

    from time import sleep
    for i in range(3):
        try:
            df = get_group(url = "https://example.smartabase.com/site")
            break
        except AMSError as e:
            print(f"Retry {i+1}: {e}")
            sleep(2)
    

Next Steps#