User Menu React Component
The user menu sits at the top right of every web console page. The signed-in user’s profile image opens it, and their email shows when the pointer rests on it. The menu shows the user’s name, email and role (a Superuser or Staff badge), and these items:
Language, which lists the console’s languages, each in its own name, and posts the choice to Django’s
set_languageview. It is shown when the platform’sLANGUAGESsetting has more than one. See Translations.Change Profile Image and Remove Profile Image, which upload and remove the user’s own image. See Dashboard Authentication - Profile Image.
Change Profile, for a user with more than one UserProfile, below.
Welcome Tour, which opens the welcome dialog.
Sign Out. An anonymous request gets Sign In, and the Language item.
The menu is the @smarter/dashboard-user-menu React app, which
templates/dashboard/navbar.html includes on every page, through
templates/react/dashboard-user-menu.html. It renders the Keen theme’s menu
classes itself, rather than through the theme’s JavaScript: the profile image is
a button that opens and closes it, and Escape or a click outside it closes it.
Change Profile
A user can have more than one UserProfile, one per account, e.g. a student who is enrolled in two courses at once. The web console acts as one of them at a time: by default, the newest. For such a user, the menu shows the current profile’s account name below their email, and a Change Profile item, which opens a dialog that lists their profiles, newest first, with each one’s account number, account name and creation date. The current one is marked Current. Switch, on another one, makes the console act as it for the rest of the session, and reloads the page, so that everything on it belongs to the chosen account.
The choice is stored in the Django session, under
SELECTED_USER_PROFILE_SESSION_KEY.
For each request,
SmarterUserProfileSelectionMiddleware
copies it into the context variable
selected_user_profile_id, and the
functions that resolve a profile or an account from the user alone,
UserProfile.get_cached_object(user=...),
get_cached_account_for_user() and
get_cached_avatar_url(), return the chosen one,
through UserProfile.get_selected_object(). A choice applies
only to the user who made it, and a request that authenticates with an API key
ignores it: the key acts as its own user.
API
Views for the user menu’s Change Profile item, in the @smarter/dashboard-user-menu React app.
A user can have more than one UserProfile, one per account, e.g. a student who is enrolled in two
courses at once. The web console acts as one of them at a time: the one that the user chose, for this
session, else their newest. See smarter.apps.account.profile_selection.
user-profiles/lists the user’s profiles, and which one the console acts as.user-profiles/select/chooses the one that it acts as, for the rest of the session.
- class smarter.apps.account.views.dashboard.user_profiles.UserProfileChoice(**data)[source]
Bases:
UserProfilesModelOne of the user’s profiles, as the Change Profile dialog lists it.
- Parameters:
id – The UserProfile’s primary key, which the select view takes.
account_number – The account’s number, e.g.
1234-5678-9012.account_name – The account’s name, its company name.
created_at – When the UserProfile was created: when the user joined the account.
is_current – Whether the web console acts as this profile.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class smarter.apps.account.views.dashboard.user_profiles.UserProfileSelectRequest(**data)[source]
Bases:
UserProfilesModelThe body of a request to the select view.
- Parameters:
user_profile_id – The id of the profile that the web console is to act as.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class smarter.apps.account.views.dashboard.user_profiles.UserProfileSelectView(**kwargs)[source]
Bases:
SmarterAuthenticatedNeverCachedWebViewChooses the profile that the web console acts as, for the rest of the session.
The body is a
UserProfileSelectRequest, as JSON. The profile must be one of the user’s. Its id is stored in the session, underSELECTED_USER_PROFILE_SESSION_KEY. The response is that ofUserProfilesView, with the chosen profile current. The React app reloads the page, so that everything on it belongs to the chosen profile’s account.
- class smarter.apps.account.views.dashboard.user_profiles.UserProfilesApiResponse(**data)[source]
Bases:
UserProfilesModelThe response of both views: the user’s profiles, newest first.
- Parameters:
user_profiles – The user’s profiles.
- classmethod for_user(user, current_id)[source]
The user’s profiles, newest first.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class smarter.apps.account.views.dashboard.user_profiles.UserProfilesModel(**data)[source]
Bases:
BaseModelThe base class of these views’ models: camelCase JSON, and no fields that a model doesn’t define.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class smarter.apps.account.views.dashboard.user_profiles.UserProfilesView(**kwargs)[source]
Bases:
SmarterAuthenticatedNeverCachedWebViewLists the user’s profiles, newest first, and which one the web console acts as.
Response shape:
{ "userProfiles": [ { "id": 12, "accountNumber": "1234-5678-9012", "accountName": "Introduction to Prompt Engineering", "createdAt": "2026-09-01T14:03:00Z", "isCurrent": true } ] }
- smarter.apps.account.views.dashboard.user_profiles.not_signed_in()[source]
The response to a request without a signed-in user.
SmarterAuthenticatedNeverCachedWebViewredirects anonymous requests to the login page, so this is only for a user thatget_resolved_user()can’t resolve.- Returns:
A 401 response.
- Return type:
The UserProfile that a signed-in user has chosen for their web console session.
A user can have more than one UserProfile, one per account, e.g.
a student who is enrolled in two courses at once. Unless they choose otherwise, the platform acts as
their newest one. The user menu’s Change Profile item lists their profiles, and choosing one stores
its id in the Django session, under SELECTED_USER_PROFILE_SESSION_KEY.
SmarterUserProfileSelectionMiddleware copies that id into
selected_user_profile_id for the length of each request, and the functions that resolve a
user’s profile or account from the user alone, UserProfile.get_cached_object(user=...) and
get_cached_account_for_user(), return the chosen profile, through
UserProfile.get_selected_object().
This module imports nothing from Django’s models, so that the models and the middleware can both import it.
- smarter.apps.account.profile_selection.SELECTED_USER_PROFILE_SESSION_KEY = 'smarter_user_profile_id'
The session key of the id of the UserProfile that the user chose in the user menu.
- smarter.apps.account.profile_selection.selected_user_profile_id: ContextVar = <ContextVar name='smarter_selected_user_profile_id' default=None>
The id of the UserProfile that the request’s user chose, or
None.SmarterUserProfileSelectionMiddlewaresets it for each request.It is only a preference:
UserProfile.get_selected_object()ignores an id whose profile doesn’t belong to the user that is being resolved.
Middleware that applies the UserProfile that a signed-in user chose for their web console session.
See smarter.apps.account.profile_selection.
- class smarter.apps.account.middleware.SmarterUserProfileSelectionMiddleware(get_response, *args, **kwargs)[source]
Bases:
SmarterMiddlewareMixinSets
selected_user_profile_idfor each request.The id is the one that the user menu’s Change Profile item stored in the session, under
SELECTED_USER_PROFILE_SESSION_KEY, orNone. A request that authenticates with an api key, in itsAuthorization: Token ...header, getsNone: an api key acts as its own user, whatever a browser session chose.It must come after
SessionMiddleware. It doesn’t readrequest.user: the functions that honor the choice check that the chosen profile belongs to the user that they resolve.- as_profile_id(value)[source]
The session’s value as a UserProfile id.
- property formatted_class_name: str
str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to ‘utf-8’. errors defaults to ‘strict’.
- static has_api_token(request)[source]
Whether the request authenticates with an api key.
- Parameters:
request (
HttpRequest) – The request.- Returns:
True if its
Authorizationheader isToken ....- Return type:
Template Tags
Django template tags for the web console’s user menu, the @smarter/dashboard-user-menu React app.
templates/react/dashboard-user-menu.html uses them, and the console’s navbar,
templates/dashboard/navbar.html, includes it on every page.
- smarter.apps.dashboard.templatetags.react_dashboard_user_menu.dashboard_user_menu(context)[source]
The attributes of the user menu’s root element.
The urls are reversed against
settings.ROOT_URLCONF, because the console’s base template also renders on a deployed LLMClient’s host, whose urlconf has none of the console’s names.- Parameters:
context (
RequestContext) – The template context, whose request’s user is the signed-in user, if any.- Returns:
The root element’s id, the cookie names, who the user is, the image that represents them, how many profiles they have and the account of the one that the console acts as, the languages and the current one, and the urls of the menu’s items. A request without a signed-in user gets only the Sign In item’s url and the languages.
- Return type:
- smarter.apps.dashboard.templatetags.react_dashboard_user_menu.dashboard_user_menu_react_assets()[source]
Load CSS and JS files for the user menu’s React app.
based on its manifest.json.
- Return type:
- smarter.apps.dashboard.templatetags.react_dashboard_user_menu.languages_json()[source]
The languages of
settings.LANGUAGES, each in its own name, as the user menu’s Language item lists them.- Returns:
A JSON list of
{"code": "es", "name": "Español"}. The menu hides the Language item when the list has fewer than two.- Return type:
- smarter.apps.dashboard.templatetags.react_dashboard_user_menu.templatetag_manager = <smarter.lib.django.templatetags.smarter_react_templatetag_manager.SmarterReactTemplateTagManager object>
Manages integration of Vite-built React assets into Django templates.
Expects to find a Vite-generated manifest.json in the file path static/react/@smarter/dashboard-user-menu/.