mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-12-18 23:05:07 +01:00
[FIX] Grammar and spelling
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Pre-Commit
|
||||
|
||||
Alliance Auth is a team effort with developers of various skill levels and background. To avoid significant drift or formatting changes between developers we use [pre-commit](https://pre-commit.com/) to apply a very minimal set of formatting checks to code contributed to the project.
|
||||
Alliance Auth is a team effort with developers of various skill levels and background. To avoid significant drift or formatting changes between developers, we use [pre-commit](https://pre-commit.com/) to apply a very minimal set of formatting checks to code contributed to the project.
|
||||
|
||||
Pre-commit is also very popular with our Community Apps and may be significantly more opinionated or looser depending on the project.
|
||||
|
||||
@@ -46,4 +46,4 @@ These can be used to automatically generate our Sphinx documentation in either f
|
||||
|
||||
It is advisable to avoid wide formatting changes on code that is not being modified by an MR. Further to this, automated code formatting should be kept to a minimal when modifying sections of existing files.
|
||||
|
||||
If you are contributing whole modules or rewriting large sections of code you may use any legible code formatting valid under Python.
|
||||
If you are contributing whole modules or rewriting large sections of code, you may use any legible code formatting valid under Python.
|
||||
|
||||
@@ -11,8 +11,8 @@ allows for documentation to be maintained at different versions more easily.
|
||||
|
||||
## Building Documentation
|
||||
|
||||
If you're developing new documentation, its likely you'll want or need to test build it before committing to your
|
||||
branch. To achieve this you can use Sphinx to build the documentation locally as it appears on Read the Docs.
|
||||
If you're developing new documentation, it's likely you'll want or need to test build it before committing to your
|
||||
branch. To achieve this, you can use Sphinx to build the documentation locally as it appears on Read the Docs.
|
||||
|
||||
Activate your virtual environment (if you're using one) and install the documentation requirements found in
|
||||
`docs/requirements.txt` using pip, e.g. `pip install -r docs/requirements.txt`.
|
||||
@@ -35,5 +35,5 @@ reStructuredText here
|
||||
```
|
||||
````
|
||||
|
||||
Markdown is used elsewhere on Github so it provides the most portability of documentation from Issues and Pull Requests
|
||||
as well as providing an easier initial migration path from the Github wiki.
|
||||
Markdown is used elsewhere on GitHub, so it provides the most portability of documentation from Issues and Pull Requests
|
||||
as well as providing an easier initial migration path from the GitHub wiki.
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Integrating Services
|
||||
|
||||
One of the primary roles of Alliance Auth is integrating with external services in order to authenticate and manage users. This is achieved through the use of service modules.
|
||||
One of the primary roles of Alliance Auth is integrating with external services to authenticate and manage users. This is achieved through the use of service modules.
|
||||
|
||||
## The Service Module
|
||||
|
||||
Each service module is its own self contained Django app. It will likely contain views, models, migrations and templates. Anything that is valid in a Django app is valid in a service module.
|
||||
Each service module is its own self-contained Django app. It will likely contain views, models, migrations and templates. Anything that is valid in a Django app is valid in a service module.
|
||||
|
||||
Normally service modules live in `services.modules` though they may also be installed as external packages and installed via `pip` if you wish. A module is installed by including it in the `INSTALLED_APPS` setting.
|
||||
|
||||
### Service Module Structure
|
||||
|
||||
Typically a service will contain 5 key components:
|
||||
Typically, a service will contain 5 key components:
|
||||
|
||||
- [The Hook](#the-hook)
|
||||
- [The Service Manager](#the-service-manager)
|
||||
@@ -36,11 +36,11 @@ The architecture looks something like this:
|
||||
Module --▶ Dependency/Import
|
||||
```
|
||||
|
||||
While this is the typical structure of the existing services modules, there is no enforcement of this structure and you are, effectively, free to create whatever architecture may be necessary. A service module need not even communicate with an external service, for example, if similar triggers such as validate_user, delete_user are required for a module it may be convenient to masquerade as a service. Ideally though, using the common structure improves the maintainability for other developers.
|
||||
While this is the typical structure of the existing services modules, there is no enforcement of this structure, and you are, effectively, free to create whatever architecture may be necessary. A service module need not even communicate with an external service, for example, if similar triggers such as validate_user, delete_user are required for a module it may be convenient to masquerade as a service. Ideally, using the common structure improves the maintainability for other developers.
|
||||
|
||||
### The Hook
|
||||
|
||||
In order to integrate with Alliance Auth service modules must provide a `services_hook`. This hook will be a function that returns an instance of the `services.hooks.ServiceHook` class and decorated with the `@hooks.registerhook` decorator. For example:
|
||||
To integrate with Alliance Auth service modules must provide a `services_hook`. This hook will be a function that returns an instance of the `services.hooks.ServiceHook` class and decorated with the `@hooks.registerhook` decorator. For example:
|
||||
|
||||
```python
|
||||
@hooks.register('services_hook')
|
||||
@@ -61,7 +61,7 @@ class ExampleService(ServicesHook):
|
||||
def __init__(self):
|
||||
ServicesHook.__init__(self)
|
||||
self.urlpatterns = urlpatterns
|
||||
self.service_url = 'http://exampleservice.example.com'
|
||||
self.service_url = 'https://exampleservice.example.com'
|
||||
|
||||
"""
|
||||
Overload base methods here to implement functionality
|
||||
@@ -70,9 +70,9 @@ class ExampleService(ServicesHook):
|
||||
|
||||
### The ServiceHook class
|
||||
|
||||
The base `ServiceHook` class defines function signatures that Alliance Auth will call under certain conditions in order to trigger some action in the service.
|
||||
The base `ServiceHook` class defines function signatures that Alliance Auth will call under certain conditions to trigger some action in the service.
|
||||
|
||||
You will need to subclass `services.hooks.ServiceHook` in order to provide implementation of the functions so that Alliance Auth can interact with the service correctly. All of the functions are optional, so its up to you to define what you need.
|
||||
You will need to subclass `services.hooks.ServiceHook` in order to provide implementation of the functions so that Alliance Auth can interact with the service correctly. All the functions are optional, so its up to you to define what you need.
|
||||
|
||||
Instance Variables:
|
||||
|
||||
@@ -103,7 +103,7 @@ Internal name of the module, should be unique amongst modules.
|
||||
|
||||
#### self.urlpatterns
|
||||
|
||||
You should define all of your service URLs internally, usually in `urls.py`. Then you can import them and set `self.urlpatterns` to your defined urlpatterns.
|
||||
You should usually define all of your service URLs internally, in `urls.py`. Then you can import them and set `self.urlpatterns` to your defined urlpatterns.
|
||||
|
||||
```python
|
||||
from . import urls
|
||||
@@ -122,13 +122,13 @@ This is provided as a courtesy and defines the default template to be used with
|
||||
|
||||
#### title
|
||||
|
||||
This is a property which provides a user friendly display of your service's name. It will usually do a reasonably good job unless your service name has punctuation or odd capitalization. If this is the case you should override this method and return a string.
|
||||
This is a property which provides a user-friendly display of your service's name. It will usually do a reasonably good job unless your service name has punctuation or odd capitalization. If this is the case, you should override this method and return a string.
|
||||
|
||||
#### delete_user
|
||||
|
||||
`def delete_user(self, user, notify_user=False):`
|
||||
|
||||
Delete the users service account, optionally notify them that the service has been disabled. The `user` parameter should be a Django User object. If notify_user is set to `True` a message should be set to the user via the `notifications` module to alert them that their service account has been disabled.
|
||||
Delete the user's service account, optionally notify them that the service has been disabled. The `user` parameter should be a Django User object. If notify_user is set to `True` a message should be set to the user via the `notifications` module to alert them that their service account has been disabled.
|
||||
|
||||
The function should return a boolean, `True` if successfully disabled, `False` otherwise.
|
||||
|
||||
@@ -136,7 +136,7 @@ The function should return a boolean, `True` if successfully disabled, `False` o
|
||||
|
||||
`def validate_user(self, user):`
|
||||
|
||||
Validate the users service account, deleting it if they should no longer have access. The `user` parameter should be a Django User object.
|
||||
Validate the user's service account, deleting it if they should no longer have access. The `user` parameter should be a Django User object.
|
||||
|
||||
An implementation will probably look like the following:
|
||||
|
||||
@@ -155,7 +155,7 @@ This function will be called periodically on all users to validate that the give
|
||||
|
||||
`def sync_nickname(self, user):`
|
||||
|
||||
Very optional. As of writing only one service defines this. The `user` parameter should be a Django User object. When called, the given users nickname for the service should be updated and synchronized with the service.
|
||||
Very optional. As of writing, only one service defines this. The `user` parameter should be a Django User object. When called, the given users nickname for the service should be updated and synchronized with the service.
|
||||
|
||||
If this function is defined, an admin action will be registered on the Django Users view, allowing admins to manually trigger this action for one or many users. The hook will trigger this action user by user, so you won't have to manage a list of users.
|
||||
|
||||
@@ -165,7 +165,7 @@ If this function is defined, an admin action will be registered on the Django Us
|
||||
|
||||
Updates the nickname for a list of users. The `users` parameter must be a list of Django User objects.
|
||||
|
||||
If this method is defined, the admin action for updating service related nicknames for users will call this bulk method instead of sync_nickname. This gives you more control over how mass updates are executed, e.g. ensuring updates do not run in parallel to avoid causing rate limit violations from an external API.
|
||||
If this method is defined, the admin action for updating service related nicknames for users will call this bulk method instead of sync_nickname. This gives you more control over how mass updates are executed, e.g., ensuring updates do not run in parallel to avoid causing rate limit violations from an external API.
|
||||
|
||||
This is an optional method.
|
||||
|
||||
@@ -173,12 +173,12 @@ This is an optional method.
|
||||
|
||||
`def update_groups(self, user):`
|
||||
|
||||
Update the users group membership. The `user` parameter should be a Django User object.
|
||||
When this is called the service should determine the groups the user is a member of and synchronize the group membership with the external service. If you service does not support groups then you are not required to define this.
|
||||
Update the user's group membership. The `user` parameter should be a Django User object.
|
||||
When this is called, the service should determine the groups the user is a member of and synchronize the group membership with the external service. If your service does not support groups, then you are not required to define this.
|
||||
|
||||
If this function is defined, an admin action will be registered on the Django Users view, allowing admins to manually trigger this action for one or many users. The hook will trigger this action user by user, so you won't have to manage a list of users.
|
||||
|
||||
This action is usually called via a signal when a users group membership changes (joins or leaves a group).
|
||||
This action is usually called via a signal when a user's group membership changes (joins or leaves a group).
|
||||
|
||||
#### update_groups_bulk
|
||||
|
||||
@@ -186,7 +186,7 @@ This action is usually called via a signal when a users group membership changes
|
||||
|
||||
Updates the group memberships for a list of users. The `users` parameter must be a list of Django User objects.
|
||||
|
||||
If this method is defined, the admin action for updating service related groups for users will call this bulk method instead of update_groups. This gives you more control over how mass updates are executed, e.g. ensuring updates do not run in parallel to avoid causing rate limit violations from an external API.
|
||||
If this method is defined, the admin action for updating service related groups for users will call this bulk method instead of update_groups. This gives you more control over how mass updates are executed, e.g., ensuring updates do not run in parallel to avoid causing rate limit violations from an external API.
|
||||
|
||||
This is an optional method.
|
||||
|
||||
@@ -204,7 +204,7 @@ I'm really not sure when this is called, it may have been a hold over from befor
|
||||
|
||||
Is this service active for the given user? The `user` parameter should be a Django User object.
|
||||
|
||||
Usually you wont need to override this as it calls `service_enabled_members` or `service_enabled_blues` depending on the users state.
|
||||
Usually you won't need to override this as it calls `service_enabled_members` or `service_enabled_blues` depending on the user's state.
|
||||
|
||||
#### show_service_ctrl
|
||||
|
||||
@@ -212,7 +212,7 @@ Usually you wont need to override this as it calls `service_enabled_members` or
|
||||
|
||||
Should the service be shown for the given `user` with the given `state`? The `user` parameter should be a Django User object, and the `state` parameter should be a valid state from `authentication.states`.
|
||||
|
||||
Usually you wont need to override this function.
|
||||
Usually you won't need to override this function.
|
||||
|
||||
For more information see the [render_service_ctrl](#render_service_ctrl) section.
|
||||
|
||||
@@ -222,7 +222,7 @@ For more information see the [render_service_ctrl](#render_service_ctrl) section
|
||||
|
||||
Render the services control row. This will be called for all active services when a user visits the `/services/` page and [show_service_ctrl](#show_service_ctrl) returns `True` for the given user.
|
||||
|
||||
It should return a string (usually from `render_to_string`) of a table row (`<tr>`) with 4 columns (`<td>`). Column #1 is the service name, column #2 is the users username for this service, column #3 is the services URL, and column #4 is the action buttons.
|
||||
It should return a string (usually from `render_to_string`) of a table row (`<tr>`) with 4 columns (`<td>`). Column #1 is the service name, column #2 is the user's username for this service, column #3 is the services URL, and column #4 is the action buttons.
|
||||
|
||||
You may either define your own service template or use the default one provided. The default can be used like this example:
|
||||
|
||||
@@ -248,61 +248,61 @@ def render_services_ctrl(self, request):
|
||||
}, request=request)
|
||||
```
|
||||
|
||||
the `Urls` class defines the available URL names for the 4 actions available in the default template:
|
||||
the `Urls` class defines the available URL names for the four actions available in the default template:
|
||||
|
||||
- Activate (create service account)
|
||||
- Deactivate (delete service account)
|
||||
- Activate (create a service account)
|
||||
- Deactivate (delete a service account)
|
||||
- Reset Password (random password)
|
||||
- Set Password (custom password)
|
||||
|
||||
If you don't define one or all of these variable the button for the undefined URLs will not be displayed.
|
||||
If you don't define one or all of these variables, the button for the undefined URLs will not be displayed.
|
||||
|
||||
Most services will survive with the default template. If, however, you require extra buttons for whatever reason, you are free to provide your own template as long as you stick within the 4 columns. Multiple rows should be OK, though may be confusing to users.
|
||||
Most services will survive with the default template. If, however, you require extra buttons for whatever reason, you are free to provide your own template as long as you stick within the 4 columns. Multiple rows should be OK, though it may be confusing to users.
|
||||
|
||||
### Menu Item Hook
|
||||
|
||||
If you services needs cannot be satisfied by the Service Control row, you are free to specify extra hooks by subclassing or instantiating the `services.hooks.MenuItemHook` class.
|
||||
If your service needs cannot be satisfied by the Service Control row, you are free to specify extra hooks by subclassing or instantiating the `services.hooks.MenuItemHook` class.
|
||||
|
||||
For more information see the [Menu Hooks](menu-hooks.md) page.
|
||||
For more information, see the [Menu Hooks](menu-hooks.md) page.
|
||||
|
||||
### The Service Manager
|
||||
|
||||
The service manager is what interacts with the external service. Ideally it should be completely agnostic about its environment, meaning that it should avoid calls to Alliance Auth and Django in general (except in special circumstances where the service is managed locally, e.g. Mumble). Data should come in already arranged by the Tasks and data passed back for the tasks to manage or distribute.
|
||||
The service manager is what interacts with the external service. Ideally, it should be completely agnostic about its environment, meaning that it should avoid calls to Alliance Auth and Django in general (except in special circumstances where the service is managed locally, e.g., Mumble). Data should come in already arranged by the Tasks and data passed back for the tasks to manage or distribute.
|
||||
|
||||
The reason for maintaining this separation is that managers may be reused from other sources and there may not even be a need to write a custom manager. Likewise, by maintaining this neutral environment others may reuse the managers that we write. It can also significantly ease the unit testing of services.
|
||||
The reason for maintaining this separation is that managers may be reused from other sources, and there may not even be a need to write a custom manager. Likewise, by maintaining this neutral environment, others may reuse the managers that we write. It can also significantly ease the unit testing of services.
|
||||
|
||||
### The Views
|
||||
|
||||
As mentioned at the start of this page, service modules are fully fledged Django apps. This means you're free to do whatever you wish with your views.
|
||||
|
||||
Typically most traditional username/password services define four views.
|
||||
Typically, most traditional username/password services define four views.
|
||||
|
||||
- Create Account
|
||||
- Delete Account
|
||||
- Reset Password
|
||||
- Set Password
|
||||
|
||||
These views should interact with the service via the Tasks, though in some instances may bypass the Tasks and access the manager directly where necessary, for example OAuth functionality.
|
||||
These views should interact with the service via the Tasks, though in some instances may bypass the Tasks and access the manager directly where necessary, for example, OAuth functionality.
|
||||
|
||||
### The Tasks
|
||||
|
||||
The tasks component is the glue that holds all of the other components of the service module together. It provides the function implementation to handle things like adding and deleting users, updating groups, validating the existence of a users account. Whatever tasks `auth_hooks` and `views` have with interacting with the service will probably live here.
|
||||
The tasks component is the glue that holds all the other components of the service module together. It provides the function implementation to handle things like adding and deleting users, updating groups, and validating the existence of a user's account. Whatever tasks `auth_hooks` and `views` have with interacting with the service will probably live here.
|
||||
|
||||
### The Models
|
||||
|
||||
Its very likely that you'll need to store data about a users remote service account locally. As service modules are fully fledged Django apps you are free to create as many models as necessary for persistent storage. You can create foreign keys to other models in Alliance Auth if necessary, though I _strongly_ recommend you limit this to the User and Groups models from `django.contrib.auth.models` and query any other data manually.
|
||||
It's very likely that you'll need to store data about a users remote service account locally. As service modules are fully fledged Django apps, you are free to create as many models as necessary for persistent storage. You can create foreign keys to other models in Alliance Auth if necessary, though I _strongly_ recommend you limit this to the User and Groups models from `django.contrib.auth.models` and query any other data manually.
|
||||
|
||||
If you create models you should create the migrations that go along with these inside of your module/app.
|
||||
If you create models, you should create the migrations that go along with them inside your module/app.
|
||||
|
||||
## Examples
|
||||
|
||||
There is a bare bones example service included in `services.modules.example`, you may like to use this as the base for your new service.
|
||||
There is a bare-bones example service included in `services.modules.example`, you may like to use this as the base for your new service.
|
||||
|
||||
You should have a look through some of the other service modules before you get started to get an idea of the general structure of one. A lot of them aren't perfect so don't feel like you have to rigidly follow the structure of the existing services if you think its sub-optimal or doesn't suit the external service you're integrating.
|
||||
You should have a look through some of the other service modules before you get started to get an idea of the general structure. A lot of them aren't perfect, so don't feel like you have to rigidly follow the structure of the existing services if you think its suboptimal or doesn't suit the external service you're integrating.
|
||||
|
||||
## Testing
|
||||
|
||||
You will need to add unit tests for all aspects of your service module before it is accepted. Be mindful that you don't actually want to make external calls to the service so you should mock the appropriate components to prevent this behavior.
|
||||
You will need to add unit tests for all aspects of your service module before it is accepted. Be mindful that you don't actually want to make external calls to the service, so you should mock the appropriate components to prevent this behavior.
|
||||
|
||||
```{eval-rst}
|
||||
.. autoclass:: allianceauth.services.hooks.ServicesHook
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Menu Hooks
|
||||
|
||||
The menu hooks allow you to dynamically specify menu items from your plugin app or service. To achieve this you should subclass or instantiate the `services.hooks.MenuItemHook` class and then register the menu item with one of the hooks.
|
||||
The menu hooks allow you to dynamically specify menu items from your plugin app or service. To achieve this, you should subclass or instantiate the `services.hooks.MenuItemHook` class and then register the menu item with one of the hooks.
|
||||
|
||||
To register a MenuItemHook class you would do the following:
|
||||
To register a MenuItemHook class, you would do the following:
|
||||
|
||||
```python
|
||||
@hooks.register('menu_item_hook')
|
||||
@@ -16,7 +16,7 @@ The `MenuItemHook` class specifies some parameters/instance variables required f
|
||||
|
||||
### text
|
||||
|
||||
The text shown as menu item, e.g. usually the name of the app.
|
||||
The text shown as menu item, e.g., usually the name of the app.
|
||||
|
||||
### classes
|
||||
|
||||
@@ -28,7 +28,7 @@ The name of the Django URL to use
|
||||
|
||||
### order
|
||||
|
||||
An integer which specifies the order of the menu item, lowest to highest. Community apps are free ot use an oder above `1000`. Numbers below are served for Auth.
|
||||
An integer which specifies the order of the menu item, lowest to highest. Community apps are free ot use an oder above `1000`. The numbers below are reserved for Auth.
|
||||
|
||||
### navactive
|
||||
|
||||
@@ -38,12 +38,12 @@ A list of views or namespaces the link should be highlighted on. See [django-nav
|
||||
|
||||
`count` is an integer shown next to the menu item as badge when `count` is not `None`.
|
||||
|
||||
This is a great feature to signal the user, that he has some open issues to take care of within an app. For example Auth uses this feature to show the specific number of open group request to the current user.
|
||||
This is a great feature to signal the user that he has some open issues to take care of within an app. For example, Auth uses this feature to show the specific number of open group request to the current user.
|
||||
|
||||
:::{hint}
|
||||
Here is how to stay consistent with the Auth design philosophy for using this feature:
|
||||
|
||||
1. Use it to display open items that the current user can close by himself only. Do not use it for items, that the user has no control over.
|
||||
1. Use it to display open items that the current user can close by himself only. Do not use it for items that the user has no control over.
|
||||
2. If there are currently no open items, do not show a badge at all.
|
||||
:::
|
||||
To use it set count the `render()` function of your subclass in accordance to the current user. Here is an example:
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
## Base functionality
|
||||
|
||||
The URL hooks allow you to dynamically specify URL patterns from your plugin app or service. To achieve this you should subclass or instantiate the `services.hooks.UrlHook` class and then register the URL patterns with the hook.
|
||||
The URL hooks allow you to dynamically specify URL patterns from your plugin app or service. To achieve this, you should subclass or instantiate the `services.hooks.UrlHook` class and then register the URL patterns with the hook.
|
||||
|
||||
To register a UrlHook class you would do the following:
|
||||
To register a UrlHook class, you would do the following:
|
||||
|
||||
```python
|
||||
@hooks.register('url_hook')
|
||||
@@ -14,12 +14,12 @@ def register_urls():
|
||||
|
||||
### Public views
|
||||
|
||||
In addition is it possible to make views public. Normally, all views are automatically decorated with the `main_character_required` decorator. That decorator ensures a user needs to be logged in and have a main before he can access that view. This feature protects against a community app sneaking in a public view without the administrator knowing about it.
|
||||
In addition, is it possible to make views public. Normally, all views are automatically decorated with the `main_character_required` decorator. That decorator ensures a user needs to be logged in and have a main before he can access that view. This feature protects against a community app sneaking in a public view without the administrator knowing about it.
|
||||
|
||||
An app can opt-out of this feature by adding a list of views to be excluded when registering the URLs. See the `excluded_views` parameter for details.
|
||||
An app can opt out of this feature by adding a list of views to be excluded when registering the URLs. See the `excluded_views` parameter for details.
|
||||
|
||||
:::{note}
|
||||
Note that for a public view to work, administrators need to also explicitly allow apps to have public views in their AA installation, by adding the apps label to ``APPS_WITH_PUBLIC_VIEWS`` setting.
|
||||
Note that for a public view to work, administrators need to also explicitly allow apps to have public views in their AA installation, by adding the app label to ``APPS_WITH_PUBLIC_VIEWS`` setting.
|
||||
:::
|
||||
|
||||
## Examples
|
||||
@@ -42,7 +42,7 @@ urlpatterns = [
|
||||
]
|
||||
```
|
||||
|
||||
Subsequently it would implement the UrlHook in a dedicated `auth_hooks.py` file like so:
|
||||
Subsequently, it would implement the UrlHook in a dedicated `auth_hooks.py` file like so:
|
||||
|
||||
```python
|
||||
from alliance_auth import hooks
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
# Development on Windows 10 with WSL and Visual Studio Code
|
||||
|
||||
This document describes step-by-step how to setup a complete development environment for Alliance Auth apps on Windows 10 with Windows Subsystem for Linux (WSL) and Visual Studio Code.
|
||||
This document describes step-by-step how to set up a complete development environment for Alliance Auth apps on Windows 10 with Windows Subsystem for Linux (WSL) and Visual Studio Code.
|
||||
|
||||
The main benefit of this setup is that it runs all services and code in the native Linux environment (WSL) and at the same time can be full controlled from within a comfortable Windows IDE (Visual Studio Code) including code debugging.
|
||||
The main benefit of this setup is that it runs all services and code in the native Linux environment (WSL) and at the same time can be fully controlled from within a comfortable Windows IDE (Visual Studio Code) including code debugging.
|
||||
|
||||
In addition all tools described in this guide are open source or free software.
|
||||
In addition, all tools described in this guide are open source or free software.
|
||||
|
||||
:::{hint}
|
||||
This guide is meant for development purposes only and not for installing AA in a production environment. For production installation please see chapter **Installation**.
|
||||
This guide is meant for development purposes only and not for installing AA in a production environment. For production installation, please see chapter **Installation**.
|
||||
:::
|
||||
|
||||
## Overview
|
||||
|
||||
The development environment consists of the following components:
|
||||
|
||||
- Visual Studio Code with Remote WSL and Python extension
|
||||
- Visual Studio Code with the Remote WSL and Python extension
|
||||
- WSL with Ubuntu (18.04. LTS or higher)
|
||||
- Python environment on WSL (3.8 or higher)
|
||||
- MySQL server on WSL
|
||||
@@ -22,15 +22,15 @@ The development environment consists of the following components:
|
||||
- Alliance Auth on WSL
|
||||
- Celery on WSL
|
||||
|
||||
We will use the build-in Django development web server, so we don't need to setup a WSGI server or a web server.
|
||||
We will use the build-in Django development web server, so we don't need to set up a WSGI server or a web server.
|
||||
|
||||
:::{note}
|
||||
This setup works with both WSL 1 and WSL 2. However, due to the significantly better performance we recommend WSL 2.
|
||||
This setup works with both WSL 1 and WSL 2. However, due to the significantly better performance, we recommend WSL 2.
|
||||
:::
|
||||
|
||||
## Requirement
|
||||
|
||||
The only requirement is a PC with Windows 10 and Internet connection in order to download the additional software components.
|
||||
The only requirement is a PC with Windows 10 and Internet connection to download the additional software components.
|
||||
|
||||
## Installing Windows apps
|
||||
|
||||
@@ -45,7 +45,7 @@ The only requirement is a PC with Windows 10 and Internet connection in order to
|
||||
- Open the app and install the following VSC extensions:
|
||||
- Remote WSL
|
||||
- Connect to WSL. This will automatically install the VSC server on the VSC server for WSL
|
||||
- Once connected to WSL install the Python extension on the WSL side
|
||||
- Once connected to WSL, install the Python extension on the WSL side
|
||||
|
||||
## Setting up WSL / Linux
|
||||
|
||||
@@ -64,13 +64,13 @@ sudo apt-get install gettext
|
||||
|
||||
### Install Python
|
||||
|
||||
Next we need to install Python and related development tools.
|
||||
Next, we need to install Python and related development tools.
|
||||
|
||||
:::{note}
|
||||
Should your Ubuntu come with a newer version of Python we recommend to still setup your dev environment with the oldest Python 3 version currently supported by AA (e.g Python 3.8 at this time of writing) to ensure your apps are compatible with all current AA installations
|
||||
You an check out this `page <https://askubuntu.com/questions/682869/how-do-i-install-a-different-python-version-using-apt-get/1195153>`_ on how to install additional Python versions on Ubuntu.
|
||||
Should your Ubuntu come with a newer version of Python we recommend to still set up your dev environment with the oldest Python 3 version currently supported by AA (e.g., Python 3.8 at this time of writing) to ensure your apps are compatible with all current AA installations
|
||||
You can check out this `page <https://askubuntu.com/questions/682869/how-do-i-install-a-different-python-version-using-apt-get/1195153>`_ on how to install additional Python versions on Ubuntu.
|
||||
|
||||
If you install a different python version from the default you need to adjust some of the commands below to install appopriate versions of those packages for example using Python 3.8 you might need to run the following after using the setup steps for the repository mentioned in the AskUbuntu post above:
|
||||
If you install a different python version from the default, you need to adjust some commands below to install appopriate versions of those packages, for example, using Python 3.8 you might need to run the following after using the setup steps for the repository mentioned in the AskUbuntu post above:
|
||||
|
||||
```shell
|
||||
sudo apt-get install python3.8 python3.8-dev python3.8-venv python3-setuptools python3-pip python-pip
|
||||
@@ -108,7 +108,7 @@ sudo apt-get install mysql-server mysql-client libmysqlclient-dev
|
||||
We chose to use MySQL instead of MariaDB, because the standard version of MariaDB that comes with this Ubuntu distribution will not work with AA.
|
||||
:::
|
||||
|
||||
We need to apply a permission fix to mysql or you will get a warning with every startup:
|
||||
We need to apply a permission fix to mysql, or you will get a warning with every startup:
|
||||
|
||||
```shell
|
||||
sudo usermod -d /var/lib/mysql/ mysql
|
||||
@@ -120,7 +120,7 @@ Start the mysql server
|
||||
sudo service mysql start
|
||||
```
|
||||
|
||||
Create database and user for AA
|
||||
Create a database and user for AA
|
||||
|
||||
```shell
|
||||
sudo mysql -u root
|
||||
@@ -141,7 +141,7 @@ sudo mysql_tzinfo_to_sql /usr/share/zoneinfo | sudo mysql -u root mysql
|
||||
```
|
||||
|
||||
:::{note}
|
||||
If your WSL does not have an init.d service, it will not automatically start your services such as MySQL and Redis when you boot your Windows machine and you have to manually start them. For convenience we recommend putting these commands in a bash script. Here is an example:
|
||||
If your WSL does not have an init.d service, it will not automatically start your services such as MySQL and Redis when you boot your Windows machine, and you have to manually start them. For convenience, we recommend putting these commands in a bash script. Here is an example:
|
||||
|
||||
```shell
|
||||
#/bin/bash
|
||||
@@ -154,7 +154,7 @@ sudo redis-server --daemonize yes
|
||||
|
||||
### Setup dev folder on WSL
|
||||
|
||||
Setup your folders on WSL bash for your dev project. Our approach will setup one AA project with one venv and multiple apps running under the same AA project, but each in their own folder and git.
|
||||
Set up your folders on WSL bash for your dev project. Our approach will set up one AA project with one venv and multiple apps running under the same AA project, but each in their own folder and git.
|
||||
|
||||
A good location for setting up this folder structure is your home folder or a subfolder of your home:
|
||||
|
||||
@@ -167,7 +167,7 @@ A good location for setting up this folder structure is your home folder or a su
|
||||
|- ...
|
||||
```
|
||||
|
||||
Following this approach you can also setup additional AA projects, e.g. aa-dev-2, aa-dev-3 if needed.
|
||||
Following this approach, you can also set up additional AA projects, e.g. aa-dev-2, aa-dev-3 if needed.
|
||||
|
||||
Create the root folder `aa-dev`.
|
||||
|
||||
@@ -203,13 +203,13 @@ pip install -U pip setuptools wheel
|
||||
pip install allianceauth
|
||||
```
|
||||
|
||||
Now we are ready to setup our AA instance. Make sure to run this command in your aa-dev folder:
|
||||
Now we are ready to set up our AA instance. Make sure to run this command in your aa-dev folder:
|
||||
|
||||
```shell
|
||||
allianceauth start myauth
|
||||
```
|
||||
|
||||
Next we will setup our VSC project for aa-dev by starting it directly from the WSL bash:
|
||||
Next, we will set up our VSC project for aa-dev by starting it directly from the WSL bash:
|
||||
|
||||
```shell
|
||||
code .
|
||||
@@ -217,11 +217,11 @@ code .
|
||||
|
||||
First you want to make sure exclude the venv folder from VSC as follows:
|
||||
Open settings and go to Files:Exclude
|
||||
Add pattern: `**/venv`
|
||||
Add the pattern: `**/venv`
|
||||
|
||||
### Create EVE Online SSO App
|
||||
|
||||
For the Eve Online related setup you need to create a SSO app on the developer site:
|
||||
For the Eve Online related setup you need to create an SSO app on the developer site:
|
||||
|
||||
- Create your Eve Online SSO App on the [Eve Online developer site](https://developers.eveonline.com/)
|
||||
- Add all ESI scopes
|
||||
@@ -277,7 +277,7 @@ REGISTRATION_VERIFY_EMAIL = False
|
||||
|
||||
### Migrations and superuser
|
||||
|
||||
Before we can start AA we need to run migrations:
|
||||
Before we can start AA, we need to run migrations:
|
||||
|
||||
```shell
|
||||
cd myauth
|
||||
@@ -300,7 +300,7 @@ We are now ready to run out AA instance with the following command:
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
Once running you can access your auth site on the browser under `http://localhost:8000`. Or the admin site under `http://localhost:8000/admin`
|
||||
Once running, you can access your auth site on the browser under `http://localhost:8000`. Or the admin site under `http://localhost:8000/admin`
|
||||
|
||||
:::{hint}
|
||||
You can start your AA server directly from a terminal window in VSC or with a VSC debug config (see chapter about debugging for details).
|
||||
@@ -308,14 +308,14 @@ You can start your AA server directly from a terminal window in VSC or with a VS
|
||||
|
||||
:::{note}
|
||||
**Debug vs. Non-Debug mode**
|
||||
Usually it is best to run your dev AA instance in debug mode, so you get all the detailed error messages that helps a lot for finding errors. But there might be cases where you want to test features that do not exist in debug mode (e.g. error pages) or just want to see how your app behaves in non-debug / production mode.
|
||||
Usually it is best to run your dev AA instance in debug mode, so you get all the detailed error messages that help a lot for finding errors. But there might be cases where you want to test features that do not exist in debug mode (e.g. error pages) or just want to see how your app behaves in non-debug / production mode.
|
||||
|
||||
When you turn off debug mode you will see a problem though: Your pages will not render correctly. The reason is that Django will stop serving your static files in production mode and expect you to serve them from a real web server. Luckily, there is an option that forces Django to continue serving your static files directly even when not in debug mode. Just start your server with the following option: ``python manage.py runserver --insecure``
|
||||
When you turn off debug mode, you will see a problem though: Your pages will not render correctly. The reason is that Django will stop serving your static files in production mode and expect you to serve them from a real web server. Luckily, there is an option that forces Django to continue serving your static files directly even when not in debug mode. Start your server with the following option: ``python manage.py runserver --insecure``
|
||||
:::
|
||||
|
||||
### Celery
|
||||
|
||||
In addition you can start a celery worker instance for myauth. For development purposed it makes sense to only start one instance and add some additional logging.
|
||||
In addition, you can start a celery worker instance for myauth. For development purposes, it makes sense to only start one instance and add some additional logging.
|
||||
|
||||
This can be done from the command line with the following command in the myauth folder (where manage.py is located):
|
||||
|
||||
@@ -323,21 +323,21 @@ This can be done from the command line with the following command in the myauth
|
||||
celery -A myauth worker -l info -P solo
|
||||
```
|
||||
|
||||
Same as AA itself you can start Celery from any terminal session, from a terminal window within VSC or as a debug config in VSC (see chapter about debugging for details). For convenience we recommend starting Celery as debug config.
|
||||
Same as AA itself, you can start Celery from any terminal session, from a terminal window within VSC or as a debug config in VSC (see chapter about debugging for details). For convenience, we recommend starting Celery as debug config.
|
||||
|
||||
## Debugging setup
|
||||
|
||||
To be able to debug your code you need to add debugging configuration to VSC. At least one for AA and one for celery.
|
||||
To be able to debug your code, you need to add a debugging configuration to VSC. At least one for AA and one for celery.
|
||||
|
||||
### Breakpoints
|
||||
|
||||
By default VSC will break on any uncaught exception. Since every error raised by your tests will cause an uncaught exception we recommend to deactivate this feature.
|
||||
By default, VSC will break on any uncaught exception. Since every error raised by your tests will cause an uncaught exception, we recommend deactivating this feature.
|
||||
|
||||
To deactivate open click on the debug icon to switch to the debug view. Then un-check "Uncaught Exceptions" on the breakpoints window.
|
||||
To deactivate, click on the debug icon to switch to the debug view. Then uncheck "Uncaught Exceptions" in the "Breakpoints" window.
|
||||
|
||||
### AA debug config
|
||||
|
||||
In VSC click on Debug / Add Configuration and choose "Django". Should Django not appear as option make sure to first open a Django file (e.g. the local.py settings) to help VSC detect that you are using Django.
|
||||
In VSC, click on Debug / Add Configuration and choose "Django". Should Django not appear as an option, make sure to first open a Django file (e.g., the local.py settings) to help VSC detect that you are using Django.
|
||||
|
||||
The result should look something like this:
|
||||
|
||||
@@ -359,7 +359,7 @@ The result should look something like this:
|
||||
|
||||
### Debug celery
|
||||
|
||||
For celery we need another debug config, so that we can run it in parallel to our AA instance.
|
||||
For celery, we need another debug config, so that we can run it in parallel to our AA instance.
|
||||
|
||||
Here is an example debug config for Celery:
|
||||
|
||||
@@ -387,7 +387,7 @@ Here is an example debug config for Celery:
|
||||
|
||||
### Debug config for unit tests
|
||||
|
||||
Finally it makes sense to have a dedicated debug config for running unit tests. Here is an example config for running all tests of the app `example`.
|
||||
Finally, it makes sense to have a dedicated debug config for running unit tests. Here is an example config for running all tests of the app `example`.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -407,11 +407,11 @@ Finally it makes sense to have a dedicated debug config for running unit tests.
|
||||
},
|
||||
```
|
||||
|
||||
You can also specify to run just a part of your test suite down to a test method. Just give the full path to the test you want to run, e.g. `example.test.test_models.TestDemoModel.test_this_method`
|
||||
You can also specify to run just a part of your test suite down to a test method. Give the full path to the test you want to run, e.g. `example.test.test_models.TestDemoModel.test_this_method`
|
||||
|
||||
### Debugging normal python scripts
|
||||
|
||||
Finally you may also want to have a debug config to debug a non-Django Python script:
|
||||
Finally, you may also want to have a debug config to debug a non-Django Python script:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -447,39 +447,37 @@ Extension for Visual Studio Code - Markdown linting and style checking for Visua
|
||||
|
||||
#### Live Server
|
||||
|
||||
Live Server allows you to start a mini webserver for any file quickly. This can e.g. be useful for looking at changes to to Sphinx docs.: [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
|
||||
Live Server allows you to start a mini webserver for any file quickly. This can e.g. be useful for looking at changes to Sphinx docs.: [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
|
||||
|
||||
### Django apps
|
||||
|
||||
#### Django Extensions
|
||||
|
||||
[django-extensions](https://django-extensions.readthedocs.io/en/latest/) is a swiss army knife for django developers with adds a lot of very useful features to your Django site. Here are a few highlights:
|
||||
[django-extensions](https://django-extensions.readthedocs.io/en/latest/) is a swiss army knife for django developers with adds a lot of useful features to your Django site. Here are a few highlights:
|
||||
|
||||
- shell_plus - An enhanced version of the Django shell. It will auto-load all your models at startup so you don't have to import anything and can use them right away.
|
||||
- graph_models - Creates a dependency graph of Django models. Visualizing a model dependency structure can be very useful for trying to understand how an existing Django app works, or e.g. how all the AA models work together.
|
||||
- runserver_plus - The standard runserver stuff but with the Werkzeug debugger baked in. This is a must have for any serious debugging.
|
||||
- `shell_plus` - An enhanced version of the Django shell. It will autoload all your models at startup, so you don't have to import anything and can use them right away.
|
||||
- `graph_models` - Creates a dependency graph of Django models. Visualizing a model dependency structure can be useful for trying to understand how an existing Django app works, or e.g., how all the AA models work together.
|
||||
- `runserver_plus` - The standard runserver stuff but with the debugger baked in. This is a must-have for any serious debugging.
|
||||
|
||||
#### Django Debug Toolbar
|
||||
|
||||
The [Django Debug Toolbar](https://github.com/jazzband/django-debug-toolbar) is a configurable set of panels that display various debug information about the current request/response and when clicked, display more details about the panel's content.
|
||||
|
||||
E.g. this tool is invaluable to debug and fix performance issues with Django queries.
|
||||
The [Django Debug Toolbar](https://github.com/jazzband/django-debug-toolbar) is a configurable set of panels that display various debug information about the current request/response and when clicked, display more details about the panel's content. This tool is invaluable to debug and fix performance issues with Django queries.
|
||||
|
||||
### Windows applications
|
||||
|
||||
#### DBeaver
|
||||
|
||||
DBeaver is a free universal database tool and works with many different kinds of databases include MySQL. It can be installed on Windows 10 and will be able to help manage your MySQL databases running on WSL.
|
||||
DBeaver is a free universal database tool and works with many different kinds of databases including MySQL. It can be installed on Windows 10 and will be able to help manage your MySQL databases running on WSL.
|
||||
|
||||
Install from here. [DBeaver](https://dbeaver.io/)
|
||||
|
||||
## Adding apps for development
|
||||
|
||||
The idea behind the particular folder structure of aa-dev is to have each and every app in its own folder and git repo. To integrate them with the AA instance they need to be installed once using the -e option that enabled editing of the package. And then added to the INSTALLED_APPS settings.
|
||||
The idea behind the particular folder structure of aa-dev is to have each and every app in its own folder and git repo. To integrate them with the AA instance, they need to be installed once using the -e option that enabled editing of the package. And then added to the INSTALLED_APPS settings.
|
||||
|
||||
To demonstrate let's add the example plugin to our environment.
|
||||
To demonstrate, let's add the example plugin to our environment.
|
||||
|
||||
Open a WSL bash and navigate to the aa-dev folder. Make sure you have activate your virtual environment. (`source venv/bin/activate`)
|
||||
Open a WSL bash and navigate to the aa-dev folder. Make sure you have activated your virtual environment. (`source venv/bin/activate`)
|
||||
|
||||
Run these commands:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# API
|
||||
|
||||
To reduce redundancy and help speed up development we encourage developers to utilize the following packages when developing apps for Alliance Auth.
|
||||
To reduce redundancy and help speed up development, we encourage developers to utilize the following packages when developing apps for Alliance Auth.
|
||||
|
||||
:::{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
@@ -2,31 +2,31 @@
|
||||
|
||||
**Alliance Auth** uses Celery for asynchronous task management. This page aims to give developers some guidance on how to use Celery when developing apps for Alliance Auth.
|
||||
|
||||
For a complete documentation of Celery please refer to the [official Celery documentation](http://docs.celeryproject.org/en/latest/index.html).
|
||||
For the complete documentation of Celery, please refer to the [official Celery documentation](http://docs.celeryproject.org/en/latest/index.html).
|
||||
|
||||
## When should I use Celery in my app?
|
||||
|
||||
There are two main cases for using celery. Long duration of a process and recurrence of a process.
|
||||
There are two main reasons for using celery. Long duration of a process, and recurrence of a process.
|
||||
|
||||
### Duration
|
||||
|
||||
Alliance Auth is an online web application and as such the user expects fast and immediate responses to any of his clicks or actions. Same as with any other good web site. Good response times are measures in ms and a user will perceive everything that takes longer than 1 sec as an interruption of his flow of thought (see also [Response Times: The 3 Important Limits](https://www.nngroup.com/articles/response-times-3-important-limits/)).
|
||||
Alliance Auth is an online web application, and as such, the user expects fast and immediate responses to any of his clicks or actions. Same as with any other good website. Good response times are measured in ms, and a user will perceive everything that takes longer than 1 sec as an interruption of his flow of thought (see also [Response Times: The 3 Important Limits](https://www.nngroup.com/articles/response-times-3-important-limits/)).
|
||||
|
||||
As a rule of thumb we therefore recommend to use celery tasks for every process that can take longer than 1 sec to complete (also think about how long your process might take with large amounts of data).
|
||||
As a rule of thumb, we therefore recommend using celery tasks for every process that can take longer than 1 sec to complete (also think about how long your process might take with large amounts of data).
|
||||
|
||||
:::{note}
|
||||
Another solution for dealing with long response time in particular when loading pages is to load parts of a page asynchronously, for example with AJAX.
|
||||
Another solution for dealing with long response time in particular when loading pages is to load parts of a page asynchronously, for example, with AJAX.
|
||||
:::
|
||||
|
||||
### Recurrence
|
||||
|
||||
Another case for using celery tasks is when you need recurring execution of tasks. For example you may want to update the list of characters in a corporation from ESI every hour.
|
||||
Another case for using celery tasks is when you need recurring execution of tasks. For example, you may want to update the list of characters in a corporation from ESI every hour.
|
||||
|
||||
These are called periodic tasks and Alliance Auth uses [celery beat](https://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html) to implement them.
|
||||
These are called periodic tasks, and Alliance Auth uses [celery beat](https://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html) to implement them.
|
||||
|
||||
## What is a celery task?
|
||||
|
||||
For the most part a celery task is a Python functions that is configured to be executed asynchronously and controlled by Celery. Celery tasks can be automatically retried, executed periodically, executed in work flows and much more. See the [celery docs](https://docs.celeryproject.org/en/latest/userguide/tasks.html) for a more detailed description.
|
||||
For the most part, a celery task is a Python function configured to be executed asynchronously and controlled by Celery. Celery tasks can be automatically retried, executed periodically, executed in work flows and much more. See the [celery docs](https://docs.celeryproject.org/en/latest/userguide/tasks.html) for a more detailed description.
|
||||
|
||||
## How should I use Celery in my app?
|
||||
|
||||
@@ -61,21 +61,21 @@ example.delay()
|
||||
|
||||
## How should I use celery tasks in the UI?
|
||||
|
||||
There is a well established pattern for integrating asynchronous processes in the UI, for example when the user asks your app to perform a longer running action:
|
||||
There is a well-established pattern for integrating asynchronous processes in the UI, for example, when the user asks your app to perform a longer running action:
|
||||
|
||||
1. Notify the user immediately (with a Django message) that the process for completing the action has been started and that he will receive a report once completed.
|
||||
|
||||
2. Start the celery task
|
||||
|
||||
3. Once the celery task is completed it should send a notification containing the result of the action to the user. It's important to send that notification also in case of errors.
|
||||
3. Once the celery task is completed, it should send a notification containing the result of the action to the user. It's important to send that notification also in case of errors.
|
||||
|
||||
## Can I use long running tasks?
|
||||
## Can I use long-running tasks?
|
||||
|
||||
Long running tasks are possible, but in general Celery works best with short running tasks. Therefore we strongly recommend to try and break down long running tasks into smaller tasks if possible.
|
||||
Long-running tasks are possible, but in general Celery works best with short running tasks. Therefore, we strongly recommend trying to break down long-running tasks into smaller tasks if possible.
|
||||
|
||||
If contextually possible try to break down your long running task in shorter tasks that can run in parallel.
|
||||
If contextually possible, try to break down your long-running task in shorter tasks that can run in parallel.
|
||||
|
||||
However, many long running tasks consist of several smaller processes that need to run one after the other. For example you may have a loop where you perform the same action on hundreds of objects. In those cases you can define each of the smaller processes as it's own task and then link them together, so that they are run one after the other. That is called chaining in Celery and is the preferred approach for implementing long running processes.
|
||||
However, many long-running tasks consist of several smaller processes that need to run one after the other. For example, you may have a loop where you perform the same action on hundreds of objects. In those cases, you can define each of the smaller processes as its own task and then link them together, so that they are run one after the other. That is called chaining in Celery and is the preferred approach for implementing long-running processes.
|
||||
|
||||
Example implementation for a celery chain:
|
||||
|
||||
@@ -101,7 +101,7 @@ def long_runner():
|
||||
chain(my_tasks).delay()
|
||||
```
|
||||
|
||||
In this example we fist add 10 example tasks that need to run one after the other to a list. This can be done by creating a so called signature for a task. Those signature are a kind of wrapper for tasks and can be used in various ways to compose work flow for tasks.
|
||||
In this example, we first add 10 example tasks that need to run one after the other to a list. This can be done by creating a so-called signature for a task. Those signatures are a kind of wrapper for tasks and can be used in various ways to compose work flow for tasks.
|
||||
|
||||
The list of task signatures is then converted to a chain and started asynchronously.
|
||||
|
||||
@@ -110,12 +110,12 @@ In our example we use ``si()``, which is a shortcut for "immutable signatures" a
|
||||
|
||||
For more information on signature and work flows see the official documentation on `Canvas <https://docs.celeryproject.org/en/latest/userguide/canvas.html>`_.
|
||||
|
||||
In this context please note that Alliance Auth currently only supports chaining, because all other variants require a so called results back, which Alliance Auth does not have.
|
||||
In this context, please note that Alliance Auth currently only supports chaining because all other variants require a so-called results back, which Alliance Auth does not have.
|
||||
:::
|
||||
|
||||
## How can I define periodic tasks for my app?
|
||||
|
||||
Periodic tasks are normal celery tasks which are added the scheduler for periodic execution. The convention for defining periodic tasks for an app is to define them in the local settings. So user will need to add those settings manually to his local settings during the installation process.
|
||||
Periodic tasks are normal celery tasks that are added to the scheduler for periodic execution. The convention for defining periodic tasks for an app is to define them in the local settings. So user will need to add those settings manually to his local settings during the installation process.
|
||||
|
||||
Example setting:
|
||||
|
||||
@@ -126,14 +126,14 @@ CELERYBEAT_SCHEDULE['structures_update_all_structures'] = {
|
||||
}
|
||||
```
|
||||
|
||||
- `structures_update_all_structures` is the name of the scheduling entry. You can chose any name, but the convention is name of your app plus name of the task.
|
||||
- `structures_update_all_structures` is the name of the scheduling entry. You can choose any name, but the convention is name of your app plus name of the task.
|
||||
|
||||
- `'task'`: Name of your task (full path)
|
||||
- `'schedule'`: Schedule definition (see Celery documentation on [Periodic Tasks](https://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html) for details)
|
||||
|
||||
## How can I use priorities for tasks?
|
||||
|
||||
In Alliance Auth we have defined task priorities from 0 - 9 as follows:
|
||||
In Alliance Auth we have defined task priorities from 0 to 9 as follows:
|
||||
|
||||
```{eval-rst}
|
||||
====== ========= ===========
|
||||
@@ -151,12 +151,12 @@ In Alliance Auth we have defined task priorities from 0 - 9 as follows:
|
||||
:::{warning}
|
||||
Please make sure to use task priorities with care and especially do not use higher priorities without a good reason. All apps including Alliance Auth share the same task queues, so using higher task priorities excessively can potentially prevent more important tasks (of other apps) from completing on time.
|
||||
|
||||
You also want to make sure to run use lower priorities if you have a large amount of tasks or long running tasks, which are not super urgent. (e.g. the regular update of all Eve characters from ESI runs with priority 7)
|
||||
You also want to make sure to run use lower priorities if you have a large number of tasks or long-running tasks, which are not super urgent. (e.g., the regular update of all Eve characters from ESI runs with priority 7)
|
||||
:::
|
||||
:::{hint}
|
||||
If no priority is specified all tasks will be started with the default priority, which is 5.
|
||||
If no priority is specified, all tasks will be started with the default priority, which is 5.
|
||||
:::
|
||||
To run a task with a different priority you need to specify it when starting it.
|
||||
To run a task with a different priority, you need to specify it when starting it.
|
||||
|
||||
Example for starting a task with priority 3:
|
||||
|
||||
@@ -165,7 +165,7 @@ example.apply_async(priority=3)
|
||||
```
|
||||
|
||||
:::{hint}
|
||||
For defining a priority to tasks you can not use the convenient shortcut ``delay()``, but instead need to start a task with ``apply_async()``, which also requires you to pass parameters to your task function differently. Please check out the `official docs <https://docs.celeryproject.org/en/stable/reference/celery.app.task.html#celery.app.task.Task.apply_async>`_ for details.
|
||||
For defining a priority to tasks, you cannot use the convenient shortcut ``delay()``, but instead need to start a task with ``apply_async()``, which also requires you to pass parameters to your task function differently. Please check out the `official docs <https://docs.celeryproject.org/en/stable/reference/celery.app.task.html#celery.app.task.Task.apply_async>`_ for details.
|
||||
:::
|
||||
|
||||
## What special features should I be aware of?
|
||||
@@ -174,7 +174,7 @@ Every Alliance Auth installation will come with a couple of special celery relat
|
||||
|
||||
### celery-once
|
||||
|
||||
Celery-once is a celery extension "that allows you to prevent multiple execution and queuing of celery tasks". What that means is that you can ensure that only one instance of a celery task runs at any given time. This can be useful for example if you do not want multiple instances of you task to talk to the same external service at the same time.
|
||||
Celery-once is a celery extension "that allows you to prevent multiple execution and queuing of celery tasks". What that means is that you can ensure that only one instance of a celery task runs at any given time. This can be useful, for example, if you do not want multiple instances of your task to talk to the same external service at the same time.
|
||||
|
||||
We use a custom backend for celery_once in Alliance Auth defined [here](https://gitlab.com/allianceauth/allianceauth/-/blob/master/allianceauth/services/tasks.py#L14)
|
||||
You can import it for use like so:
|
||||
@@ -183,7 +183,7 @@ You can import it for use like so:
|
||||
from allianceauth.services.tasks import QueueOnce
|
||||
```
|
||||
|
||||
An example of AllianceAuth's use within the `@sharedtask` decorator, can be seen [here](https://gitlab.com/allianceauth/allianceauth/-/blob/master/allianceauth/services/modules/discord/tasks.py#L62) in the discord module
|
||||
An example of Alliance Auth's use within the `@sharedtask` decorator, can be seen [here](https://gitlab.com/allianceauth/allianceauth/-/blob/master/allianceauth/services/modules/discord/tasks.py#L62) in the discord module
|
||||
You can use it like so:
|
||||
|
||||
```python
|
||||
@@ -194,4 +194,4 @@ Please see the [official documentation](https://pypi.org/project/celery_once/) o
|
||||
|
||||
### task priorities
|
||||
|
||||
Alliance Auth is using task priorities to enable priority based scheduling of task execution. Please see [How can I use priorities for tasks?](#how-can-i-use-priorities-for-tasks) for details.
|
||||
Alliance Auth is using task priorities to enable priority-based scheduling of task execution. Please see [How can I use priorities for tasks?](#how-can-i-use-priorities-for-tasks) for details.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Developing apps
|
||||
|
||||
In this section you find topics useful for app developers.
|
||||
In this section, you find topics useful for app developers.
|
||||
|
||||
:::{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
Reference in New Issue
Block a user