Skip to content

Commit ad1caad

Browse files
authored
Merge pull request #150 from uhd-urz/dev
Merge dev into main
2 parents 909a742 + f5c8f21 commit ad1caad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2963
-63
lines changed

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.3.1] - 2025-05-23
9+
10+
This release brings some minor bug fixes and improvements.
11+
12+
### Added
13+
14+
- Added `--include-team-ids` to `bill-teams generate-table`
15+
- Long-awaited [examples/](https://github.com/uhd-urz/elAPI/tree/main/examples)
16+
17+
### Fixed
18+
19+
- elAPI re-installations (or new installation) would break `--help` due to breaking click update (
20+
GH https://github.com/fastapi/typer/discussions/1215)
21+
22+
### Changes
23+
24+
- Migrated from Poetry from uv (GH #148)
25+
26+
827
## [2.3.0] - 2024-12-21
928

1029
This release mainly revamps and completes the `bill-teams` plugin that is used for billing eLabFTW usage at the

CITATION.cff

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
cff-version: 1.2.0
2+
message: "If you use elAPI in your work, please cite it using the following metadata."
3+
title: "elAPI"
4+
version: "2.3.1"
5+
doi: 10.11588/DATA/E4XHXZ
6+
date-released: 2023-11-15
7+
authors:
8+
- family-names: Xion
9+
given-names: Mahadi
10+
- family-names: Haller
11+
given-names: Alexander
12+
license: AGPL-3.0
13+
conference: E-Science-Tage 2025
14+
repository-code: https://github.com/uhd-urz/elAPI
15+
url: https://doi.org/10.11588/DATA/E4XHXZ
16+
type: software

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,18 @@ local `~/Downloads/` folder.
3939

4040
## Installation
4141

42-
We recommend [`pipx`](https://pipx.pypa.io/stable/) for installing elAPI for use of its CLI functionalities. Pipx
43-
installs packages in isolated virtual environments, so Pipx-installed elAPI should not conflict with elAPI installed
44-
inside other virtual environments.
42+
We recommend either [`uv`](https://docs.astral.sh/uv/concepts/tools/) or [`pipx`](https://pipx.pypa.io/stable/) for
43+
installing elAPI for use of its CLI functionalities. Both uv and Pipx
44+
install packages in isolated virtual environments, so elAPI installed as a CLI tool should not conflict with elAPI
45+
installed inside other virtual environments.
4546

4647
```shell
47-
$ pipx install elapi
48+
$ uv tool install elapi
49+
# elAPI can be updated to the latest version with
50+
# uv tool upgrade elapi
4851
```
4952

50-
After installation with Pipx is complete, you would also be able to run elAPI just by entering the `elapi` command on
53+
After installation with `uv` is complete, you would also be able to run elAPI just by entering the `elapi` command on
5154
the terminal. You can move on to ["Getting started"](#getting-started).
5255

5356
### Advanced installation
@@ -160,17 +163,19 @@ as detected by `elapi show-config`. All plugins will also automatically listen t
160163
be useful to set certain configurations temporarily. E.g., `elapi --OC '{"timeout": 300"}' get info` will override
161164
the `timeout` from the configuration files.
162165

163-
## Usage
166+
## Examples and usage
164167

165-
elAPI can be invoked from the command-line.
168+
Check out the [examples directory](https://github.com/uhd-urz/elAPI/tree/main/examples) for some comprehensive code
169+
examples with elAPI. elAPI CLI usage details can be displayed with:
166170

167171
```shell
168-
$ elapi --help
172+
$ elapi --help
173+
169174
```
170175

171176
### `GET` requests
172177

173-
Request an overview of running eLabFTW server:
178+
Request an overview of a running eLabFTW server:
174179

175180
```shell
176181
$ elapi get info -F yml

examples/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# eLabFTW automation examples with elAPI
2+
3+
We list some notable examples that showcase elAPI's capabilities, its APIs and use-cases. Many examples here have been
4+
inspired by [elabapi-python](https://github.com/elabftw/elabapi-python/tree/master/examples)'s. The examples were
5+
originally created for [FDM-Werkstatt 2024](https://fdm-nrw.coscine.de/#/FDM-Werkstatt).
6+
7+
## How the examples are structured
8+
9+
The examples are first categorized into `python` and `bash` directories. Each example is placed inside a group
10+
sub-directory of its topic. Each script is heavily commented with explanations.
11+
12+
```bash
13+
examples/
14+
python/
15+
- topic-A/
16+
- Example 1 script of topic A
17+
- Example 2 script of topic A
18+
- topic-B/
19+
- Example 1 script of topic B
20+
- Example 2 script of topic B
21+
```
22+
23+
## How to use the examples
24+
25+
elAPI can be used as a CLI tool with Bash for simple tasks, and for more advanced tasks, as a Python library. Install
26+
`elapi` [inside a virtual environment](https://github.com/uhd-urz/elAPI?tab=readme-ov-file#installing-elapi-as-a-library)
27+
if you intend to write Python scripts with elAPI. Make sure elAPI is
28+
correctly [configured](https://github.com/uhd-urz/elAPI?tab=readme-ov-file#getting-started) first. You can just clone
29+
this entire repository, or copy/paste individual scripts. E.g., with Python examples, the script
30+
[
31+
`download_bulk_experiments.py`](https://github.com/uhd-urz/elAPI/blob/main/examples/download_experiments/download_bulk_experiments.py)
32+
can be run without any modification to the script:
33+
34+
```bash
35+
python examples/python/download_experiments/download_bulk_experiments.py
36+
```
37+
38+
> [!TIP]
39+
> Some entities such as "experiments" and "resources" share similar endpoints. So, if there's an example script about
40+
> experiments, it's very likely the same script will work with resources (or items) as well.
41+
42+
## How to test an example plugin
43+
44+
Copy and paste the `awesome` folder from `create_plugins/awesome` into your local elAPI third-party plugins folder (
45+
typically `~/.local/share/elapi/plugins`). You can find the exact local third-party plugin directory location by running
46+
`elapi show-config`
47+
48+
```shell
49+
$ cp -v examples/python/create_plugins/awesome ~/.local/share/elapi/plugins/
50+
```
51+
52+
Once done, run `elapi`, and it should show the plugin `awesome` under third-party plugins.
53+
54+
55+
> [!IMPORTANT]
56+
> The `obfuscate` plugin should not be run in production, unless you absolutely intend to.
57+
> The plugin will obfuscate/modify all resource/experiment titles.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#! /bin/bash
2+
# ╔─────────────────────────────────────────╗
3+
# │ │
4+
# │ Create a new user account in Bash │
5+
# │ │
6+
# ╚─────────────────────────────────────────╝
7+
#--------------------------------------------
8+
# ┌─────────────┐
9+
# │ Objective │
10+
# └─────────────┘
11+
# We would like to create a new eLabFTW user account. We will also pass some
12+
# optional parameter to the API call.
13+
#
14+
# Ideally, this can be done with a single line (see comment line below starting with *)
15+
# of elapi command via the terminal. Here, we show the example in Bash to demonstrate
16+
# how simple automation for eLabFTW can be done, and to show how creating a single user account
17+
# in the simplest way looks like. In the next example, we will show
18+
# how to create a new user account in Python.
19+
# ┌────────────────┐
20+
# │ Requirements │
21+
# └────────────────┘
22+
# - We need elAPI installed (preferably via pipx for this example).
23+
# - In case, we don't have bash in our system, we can follow an equivalent solution
24+
# that just uses elAPI directly on the CLI (comment line below starting with *).
25+
# - We need the following minimum user information to create an account:
26+
# 1. First name, 2. Last name, 3. Email, 4. Team ID
27+
# A user always belongs to a team, hence the team ID. If no team ID is provided,
28+
# eLabFTW will assign the user to the "Default Team".
29+
30+
RANDOM_NUM="$RANDOM" # Random number for creating unique user name/email.
31+
FIRSTNAME="Max" # User first name
32+
LASTNAME="Mustermann $RANDOM_NUM" # User lastname name
33+
EMAIL="max.mustermann$RANDOM_NUM@localhost.example"
34+
TEAM_ID=2 # Which team the user should belong to. Team name "team alpha" in elabftw-dev
35+
VALID_UNTIL="2024-12-31" # When the user account should expire
36+
# * The following is equivalent of:
37+
# elapi post users -d '{"firstname": <name>, "lastname": <name>, "email": <email>, "team": <team ID>, "valid_until": <date>}'
38+
elapi post users -d "{\"firstname\": \"$FIRSTNAME\", \"lastname\": \"$LASTNAME\", \"email\": \"$EMAIL\",
39+
\"team\": \"$TEAM_ID\", \"valid_until\": \"$VALID_UNTIL\"}"
40+
# Note: No password is assigned yet.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#! /bin/bash
2+
# ╔──────────────────────────────────────╗
3+
# │ │
4+
# │ Create and modify new resource │
5+
# │ │
6+
# ╚──────────────────────────────────────╝
7+
# ----------------------------------------
8+
# ┌─────────────┐
9+
# │ Objective │
10+
# └─────────────┘
11+
# We want to create a new resource/item. Note: the endpoint name is "items".
12+
# Then we will modify its title, body and status.
13+
# ┌────────────────┐
14+
# │ Requirements │
15+
# └────────────────┘
16+
# - We need elAPI installed (preferably via pipx for this example).
17+
# - We definitely need an API token with write access.
18+
# - We need an existing resource category (ID) and an existing status (ID)
19+
20+
RANDOM_NUM="$RANDOM" # random number for unique title
21+
resource_id=$(elapi post items --data '{"category_id": 77}' --get-loc | cut -d "," -f 1)
22+
# 1. elapi post items --data '{"category_id": 77}': This will create the resource
23+
# assigned to given category ID. But we also need to know the ID of the newly created
24+
# resource.
25+
# 2. For the new resource ID, we pass the --get-loc. When --get-loc is passed,
26+
# elAPI will print the ID and the URL of the ID to terminal separated by a comma.
27+
# Try running said command with and without --get-loc yourself to see the difference in output!
28+
# 3. We just need the resource ID. We capture the ID with "cut".
29+
# We could do it in many other ways in bash.
30+
echo "New resource/item with ID $resource_id created."
31+
32+
# 4. We now use the new resource ID to make a PATCH request to modify the title,
33+
# body, and status.
34+
elapi patch items --id "$resource_id" --data "{\"title\": \"Resource Material ${RANDOM_NUM}\",
35+
\"body\": \"This resource is created via elAPI.\", \"status\": 6}" 1>/dev/null
36+
# eLabFTW sends the resource data in JSON when the PATCH request goes through.
37+
# elAPI will print that data to STDOUT. We actually don't need that data.
38+
# So, we redirect it to /dev/null/.
39+
# We will check on the browser if our resource creation and modification was successful.
40+
echo "Resource title, body and status successfully changed."
41+
42+
# We could also delete the resource
43+
#elapi delete items --id "$resource_id" 1>/dev/null
44+
#echo "Resource successfully deleted."
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#! /bin/bash
2+
3+
# ╔───────────────────────────────────────╗
4+
# │ │
5+
# │ Get list of all resources/items │
6+
# │ and export them as CSV │
7+
# │ │
8+
# ╚───────────────────────────────────────╝
9+
# -----------------------------------------
10+
# ┌─────────────┐
11+
# │ Objective │
12+
# └─────────────┘
13+
# We mainly would like to get familiar with elAPI's raw get, post, patch and delete commands.
14+
# We will start simple by getting a list of all resources/items (previously called "Databases"),
15+
# and export it to a CSV file.
16+
17+
# eLabFTW by default sends resources data in JSON. elAPI attempts to convert the JSON to CSV
18+
# appropriately. CSV can be way more explorable and readable than structured data like JSON.
19+
# ┌────────────────┐
20+
# │ Requirements │
21+
# └────────────────┘
22+
# - We need elAPI installed (preferably via pipx for this example).
23+
24+
elapi get items --format csv --export ./Downloads
25+
# 1. elAPI get items makes a GET request to endpoint "items".
26+
# 2. --format defines the data format we want to have the resources as: CSV
27+
# 3. --export defines the location. The location can be a file path or a directory.
28+
# Here, we pass a directory "<current directory>/Downloads".
29+
# --export will automatically set an appropriate file name for us.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# ╔─────────────────────────────────────────────╗
2+
# │ │
3+
# │ Attach file to an existing experiment │
4+
# │ by Unique eLabID and with validation │
5+
# │ │
6+
# ╚─────────────────────────────────────────────╝
7+
# -----------------------------------------------
8+
# ┌─────────────┐
9+
# │ Objective │
10+
# └─────────────┘
11+
# We attach a local file to an experiment same as before as we did in "add_attachment_simple.py".
12+
# We will do some additional validations before uploading the attachment.
13+
# Furthermore, instead of using the experiment ID, we will use the "Unique eLabID,"
14+
# which is more conspicuous on an experiment page.
15+
# ┌────────────────┐
16+
# │ Requirements │
17+
# └────────────────┘
18+
# - We only attach to an experiment that already exists in eLabFTW.
19+
# - We get the path of the local file for attachment. Here, we already have two
20+
# example files stored in ./attachments directory.
21+
# - The minimum Python version required is 3.9. It's recommended that we create a
22+
# Python virtual environment, and we run/edit the script from inside the environment.
23+
# - We need to install elAPI from inside the activated virtual environment.
24+
# Simple `pip install elapi` should work.
25+
# Note: The elAPI we installed using `uv tool` or pipx remains isolated and is meant to work as
26+
# a user-friendly CLI tool. Here, we want to use elAPI as a library.
27+
# ┌─────────────────┐
28+
# │ Code overview │
29+
# └─────────────────┘
30+
# We explain in comments around each significant line of code what the code does.
31+
# Here we will give a short overview:
32+
# 1. elAPI already offers a plugin for working with eLabFTW experiments.
33+
# We use "attach_to_experiment" method provided by the "experiments" plugin.
34+
# 2. We define the ID for the existing experiment we want to attach our file to.
35+
# 3. We define the path to our attachment file.
36+
# 4. We call "attach_to_experiment".
37+
# 5. We print a minimal success message.
38+
from pathlib import Path
39+
40+
from httpx import HTTPError
41+
42+
from elapi.loggers import Logger
43+
from elapi.plugins.experiments import attach_to_experiment, ExperimentIDValidator
44+
# For making sure we have the appropriate permission
45+
from elapi.validators import (
46+
Validate,
47+
HostIdentityValidator,
48+
APITokenRWValidator,
49+
ValidationError,
50+
Exit,
51+
)
52+
53+
# We use Logger instance for logging errors and other messages
54+
# Logs are printed to the terminal and to a local file.
55+
# See the log file location from `elapi show-config`.
56+
logger = Logger()
57+
58+
# We need an API key/token with write permission to be able to upload an attachment.
59+
# We also need to make sure we have the right permission(s) to be able to create users in the first place.
60+
# These validations can be immensely helpful as they can help us understand any
61+
# permission or network related error early on without having to analyze the error response sent by eLabFTW.
62+
validate = Validate(HostIdentityValidator(), APITokenRWValidator(can_write=True))
63+
# HostIdentityValidator mainly ensures if the configured host and api_token are valid.
64+
# APITokenRWValidator makes sure our API key/token has write permission.
65+
# If something is found to be wrong (or invalid) by the validator, elAPI will show the appropriate error message
66+
# and quit.
67+
# +------------------+
68+
# | Run validation |
69+
# +------------------+
70+
validate()
71+
72+
UNIQUE_ELAB_ID = "20240309-b629ba4a6789fac1662505d057a3c57459ccc646"
73+
# Unique eLabID instead of experiment ID of our experiment
74+
ATTACHMENT_FILE = Path(__file__).parent / "attachments/elapi_get_architecture_old.pdf"
75+
# +-------------------------------+
76+
# | Run validation for |
77+
# | experiment ID/unique eLabID |
78+
# +-------------------------------+
79+
# We validate the unique eLabID, i.e., we want to check if the experiment exists first.
80+
try:
81+
experiment_id = Validate(ExperimentIDValidator(UNIQUE_ELAB_ID)).get()
82+
except ValidationError as e:
83+
logger.error(e)
84+
# If the ID/experiment does not exist, we abort/quit.
85+
raise Exit(1)
86+
try:
87+
# If ID does exist, we continue with uploading the attachment.
88+
attach_to_experiment(
89+
experiment_id=experiment_id,
90+
file_path=ATTACHMENT_FILE,
91+
comment="Uploaded via elAPI",
92+
)
93+
# We catch any network error during upload.
94+
except HTTPError as e:
95+
logger.error(
96+
f"There was a network error in attaching the file '{ATTACHMENT_FILE}'. "
97+
f"Exception details: {e}"
98+
)
99+
raise Exit(1)
100+
else:
101+
# If everything goes well (no error caught above),
102+
# we print a success message.
103+
logger.info(f"Successfully attached the file '{ATTACHMENT_FILE}' to experiment.")

0 commit comments

Comments
 (0)