Configuration

The configuration parameters for a USO instance is stored in the settings.py file located in the local directory of your USO instance.

Note

The settings.py file in the local directory is not the same as the main settings.py file for the USO software. You should never change settings directly within the main USO software settings.py if you are just preparing an instance of the software. Only modify the main settings.py if you are making modifications of the USO software. All settings defined within the main settings.py file can be overridden by making changes to local/settings.py. You can edit this file to customize your USO instance configuration.

The default generated local/settings.py file contains all the relevant settings, including database configuration, required for the USO instance to run. You can modify these settings to suit your needs, such as changing the database connection details, configuring email settings, and setting up custom user profile managers and much more.

A few sensitive settings are stored in the .env file, and referenced through environment variables within local/settings.py. To change these settings, you should edit the .env file directly instead.

The .env File

Here are some of the key settings you might want to configure in your .env file:

  • SECRET_KEY: A unique secret key for your USO instance. This is used for cryptographic signing.

  • DATABASE_PASSWORD: The password for the PostgreSQL database user.

  • EMAIL_PASSWORD: The password for the email account used to send notifications.

  • OPEN_WEATHER_API_KEY: The API key for the OpenWeather service (v2.5), used for weather information on the dashboard.

  • GOOGLE_API_KEY: The Google API key used by the publications application for fetching book and patent information.

The local/settings.py File

Other settings can be configured in the local/settings.py file, such as:

DEBUG

Set to True for development, False for production.

ALLOWED_HOSTS

A list of allowed host/domain names allowed to access the USO. Use [‘*’] to allow all hosts.

SITE_URL

The base URL of the USO instance. This is used for generating absolute URLs in emails and links. For example:

SITE_URL = 'https://user-office.example.com'

USO_WEATHER_LOCATION

The latitude and longitude of the location for which to fetch weather information as a list or tuple. For example:

USO_WEATHER_LOCATION = (51.5074, -0.1278)  # London, UK

CACHES

Configuration for caching. By default, a memcache container is deployed along side the USO application and the settings in local/settings.py reflect this default configuration. However, to use a different cache backend, or even an external server for cacheing, you can modify this dictionary. See the Django Documentation for more details.

The default configuration is as follows:

CACHES = {
   'default': {
       'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
       'LOCATION': 'cache:11211',
   }
}

DATABASES

Configuration for the PostgreSQL database. The default configuration uses a local database container deployed along side the USO application. However, you can modify this dictionary to use an external database server or a different database backend. Although Django supports multiple database backends, the USO system is designed to work with PostgreSQL and some specialized database functions may not be available on another backend. See the Django Documentation for more details.

The default configuration is as follows:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'usonline',
        'USER': 'usonline',
        'PASSWORD': os.getenv('DATABASE_PASSWORD'),
        'HOST': 'database',
        'PORT': '',
    }
}

Note

The name of the database and the user are both set to usonline by default. These can be changed if you are using an external database server, but is not recommended to change them if you are using the default local database container. The database will be created automatically when the USO application starts for the first time, and the user will be granted the required privileges on the database.

Email Configuration

To configure email settings for sending notifications, you can set the following parameters in local/settings.py:

  • EMAIL_BACKEND: The email backend to use. The default is django.core.mail.backends.smtp.EmailBackend.

  • EMAIL_HOST: The SMTP server to use for sending emails.

  • EMAIL_PORT: The port to use for the SMTP server. The default is 587 for TLS.

  • EMAIL_HOST_USER: The email address to use for sending emails.

  • EMAIL_HOST_PASSWORD: The password for the email account used to send notifications. Set this in the .env file.

  • EMAIL_USE_TLS: Whether to use TLS for the SMTP connection. Set to True for secure connections.

  • DEFAULT_FROM_EMAIL: The default email address to use for sending emails. This is usually the same as EMAIL_HOST_USER.

  • SERVER_EMAIL: The email address to use for server-related emails, such as error notifications.

In addition, you can set the following parameters to configure users who will receive notifications about system events:

  • ADMINS: A list of tuples containing the names and email addresses of administrators who will receive error notifications.

Note

When DEBUG is set to True, no external emails will be sent, all notifications generated by the USO software will be sent only to ADMINS. This is useful during development and testing to avoid sending test emails to real addresses.

Configuring Roles

To configure roles in the USO system, you can set the following parameters in local/settings.py:

USO_USER_ROLES

A list of strings containing the role names for users of the USO system. These users will have access to the basic features and pages of the USO system, such as submitting proposals, managing their projects, and viewing their sessions. The default setting is:

USO_USER_ROLES = ['user']

USO_ADMIN_ROLES

A list of strings containing the role names for users considered administrators of the USO system. These users will have full access to all features and pages of the USO system, including administrative functions such as managing background tasks, user accounts, and system settings. The default setting is:

USO_ADMIN_ROLES = ['admin:uso']

USO_STAFF_ROLES

A list of strings containing the role names for users considered staff members of the USO system. These users will have access to additional features and pages beyond normal users of the USO system, but will not have access to administrative functions. The default setting is:

USO_STAFF_ROLES = ['staff']

USO_FACILITY_STAFF_ROLES

A string containing the wildcard role template for users considered staff members of a particular facility within USO system. These users will have access to some additional features and pages related to the facility.

Wildcard roles are used to define roles that can be applied to multiple facilities within a hierarchy. Wildcard roles support specifying the Realm as a special character. For example, any of the following

USO_FACILITY_STAFF_ROLES = 'staff:*'
USO_FACILITY_STAFF_ROLES = 'staff:{}'
USO_FACILITY_STAFF_ROLES = 'staff:-'
USO_FACILITY_STAFF_ROLES = 'staff:+'

The meaning of the wildcard is as follows:

  • * or {} matches a specific facility only. Within the context of a specific facility, the * is replaced with the acronym of the target facility before checking of the user has the role. For example, if the facility acronym is MX-ID, then a user with the role staff:mx-id will have access to the facility-specific features and pages.

  • + matches the current facility and all sub-facilities/instruments. For example, if the facility acronym is MX-ID, and a sub-facility has an acronym B123 then a user with the role staff:mx-id will implicitly also have the role staff:b123.

  • - matches the current facility and all parent facilities. For example, if the facility acronym is MX-ID, and a parent facility has an acronym MX, then a user with the role staff:mx-id will implicitly also have the role staff:mx.

USO_FACILITY_ADMIN_ROLES

A string containing the wildcard role template for users considered administrators of a particular facility within USO system. These users will have access to all management features and pages related to the facility/beamline/instrument. As a wildcard role, it supports the same syntax as USO_FACILITY_STAFF_ROLES above.

USO_HSE_ROLES

A list of strings containing the role names for users considered Health & Safety (HSE) staff members of the USO system. These users will have access to features and pages related to health and safety, such as managing health and safety requirements, performing health & safety reviews, and some features of beam time sessions and laboratories. The default setting is:

USO_HSE_ROLES = ['staff:hse']

Additional Roles

Additional roles linked to specific features or applications within the USO system can also be configured within a running instance in several cases. For example:

  • Review Type: Each Review Type can have a specific role associated with it, which is used to determine who can review proposals of that type. This role is configured by the User Office Administrators when creating or editing Review Types.

  • Access Pool: Each Access Pool can have a specific role associated with it, which is used to determine who can submit proposals for that pool. This role is configured by the User Office Administrators when creating or editing Access Pools.

For each of these roles, the role name is a simple string or a wildcard role for roles used in the context of “per-facility” features.

ROLEPERMS_DEBUG

A boolean flag that enables or disables the role permissions debugging mode. When set to True, the USO system will log detailed information about role permissions and access checks. This is useful for debugging and troubleshooting role-based access control issues. The default setting is False.

USO_STYLE_OVERRIDES

To customize the appearance of the USO system, you can create a custom CSS file and set the USO_STYLE_OVERRIDES setting in local/settings.py to the path of your custom CSS file. This file will be loaded after the default USO styles, allowing you to override any styles you want. This setting should contain a list of ‘css’ files, for example:

USO_STYLE_OVERRIDES = [
    'custom.css',
    'extra-styles.css',
]

The custom CSS file should be placed in the local/media/css directory of your USO instance.

Profile Managers

Profile managers can be used to synchronize user profiles with external systems, such as LDAP or other identity providers. The default profile manager does not perform any synchronization. However, you can implement a custom profile for your backend system and configure the USO system to use it.

Custom profile managers can override the the following methods:

from users.profiles import ExternalProfileManager

class CustomProfileManager(ExternalProfileManager):

    PROFILE_FIELDS = ['first_name', 'last_name', 'email']  # list of fields to sync with the User model

    @classmethod
    def create_username(cls, profile: dict) -> str:
        """
        Create a username from the profile. This is used to create a username for a new user.
        :param profile: dictionary of profile parameters containing first_name and last_name
        :return: unique username string
        """
        ...

    @classmethod
    def fetch_profile(cls, username: str) -> dict:
        """
        Called to fetch a user profile from the remote source. This is used to sync the specified user's
        profile from the remote source to the local database. Only fields specified in PROFILE_FIELDS will be changed in
        the User model.
        :param username: username of the user to fetch
        :return: dictionary of profile parameters
        """
        ...

    @classmethod
    def create_profile(cls, profile: dict) -> dict:
        """
        Called to create a new profile in the remote source. User is expected to not exist in the remote source.
        :param profile: Dictionary of profile parameters.
        """
        ...

    @classmethod
    def update_profile(cls, username, profile: dict, photo=None) -> bool:
        """
        Called to update the profile in the remote source. User is expected to exist in the remote source.

        :param profile: Dictionary of profile parameters.
        :param photo: File-like object of the user's photo
        :param username: username of the user to update
        :return: True if successful, False otherwise.
        """
        ...

    @classmethod
    def fetch_new_users(cls) -> list[dict]:
        """
        Fetch new users from the remote source. This is used to sync new users from the remote source to the local database.
        :return: list of dicts, one per user.
        """
        ...

    @classmethod
    def get_user_photo_url(cls, username: str) -> str:
        """
        Get the URL of the user's profile photo. This is used to display the user's photo in the USO system.
        :param username: username of the user
        :return: URL of the user's profile photo
        """
        ...

To configure profile managers, you can set the following parameters in local/settings.py as follows:

from .custom import CustomProfileManager
# where custom.py is a module within the `local` directory

USO_PROFILE_MANAGER = CustomProfileManager

Code Generators

Within the USO system, some objects such as Proposals, Projects, Submissions and Materials use natural keys available through the code attribute. These are unique identifiers that are automatically generated when the object is created and saved. The algorithm for generating these codes is defined within various code generator functions that can be overridden in the local/settings.py file.

The default code generators are defined as follows:

USO_CODE_GENERATORS = {
    'PROPOSAL': 'proposals.utils.generate_proposal_code',
    'PROJECT': 'projects.utils.generate_project_code',
    'SUBMISSION': 'proposals.utils.generate_submission_code',
    'MATERIAL': 'projects.utils.generate_material_code',
}

You can override these code generators by implementing your own functions to generate codes and replacing the entry in USO_CODE_GENERATORS with a string representing the path to the module. Modules placed in the local directory are also available. For example:

USO_CODE_GENERATORS = {
    'PROPOSAL': 'local.custom.generate_proposal_code',
}

The above code will replace the default proposal code generator with a custom function defined in the local/custom.py module.

Code generator functions should follow a signature similar to the following:

def generate_proposal_code(proposal: Proposal) -> str:
    """
    Generate a unique code for the proposal.
    :param proposal: The proposal object for which to generate the code.
    :return: A unique string code for the proposal.
    """
    ...

Objects like Proposals, Projects, Submissions and Materials that support this interface, also provide some helper functions and attributes that may be useful when generating codes. These include:

  • year_index(): Returns the year index of the object, which is useful for generating codes that increment within the year and reset at the start of each year.

  • month_index(): Returns the index of the object within the month. Resets each month.

  • created: DateTime object representing the creation time of the object

  • modified: DateTime object representing the last modification time of the object

The year_index() and month_index() methods are involve database queries and may be slow for very large datasets.

Note

The code generator functions should return a string that is unique within the scope of the object type. Only provide a custom code generator for the object types you want to change. The default code generators will be used for the other object types.

Warning

Changing code generators will not change the codes of existing objects. It will only affect new objects created after the change. If you want to change the codes of existing objects, you will need to manually update them in the database.