Commit ab0b6caf authored by torradeflot's avatar torradeflot
Browse files

Add notebook to generate advanced Mollweide plots with matplotlib and cartopy

parent 9624adf8
Loading
Loading
Loading
Loading
+437 −0

File added.

Preview size limit exceeded, changes collapsed.

+0 −0
Original line number Diff line number Diff line
+105 −42
Original line number Diff line number Diff line
%% Cell type:code id:297d3556-9bef-4337-9880-aff634a5d48c tags:
%% Cell type:code id:7456abed-c9eb-4e1e-8add-ab1894a8bba2 tags:

``` python
import os
import sys
import stat
from datetime import datetime
from collections import Counter

import gfal2
```

%% Cell type:markdown id:3e903939-85d6-494e-ae15-c4f47fcdf2c5 tags:

# Setup

%% Cell type:markdown id:ab72b5e6-e7f4-467a-ada9-f8c983c86f88 tags:

## Environment

Need to have an environment with these packages in order to have the CLI scripts and the python bindings to manage the transfers.

* python=3.12
* gfal2-util
* python-gfal2
* fts3
* ipykernel


%% Cell type:markdown id:6fc89d0a-8a96-43c0-87d5-0b07a22b1427 tags:

## OIDC token

Steps to generate the token to be used for authentication
```
eval `oidc-agent`
oidc-gen -m --flow=device --no-webserver --iss=https://indigoiam-test.pic.es/ --client-secret=XXX --client-id=6679edd3-c74a-4e75-8774-0fc0cc72799d  --scope="profile openid offline_access storage.modify:/ storage.create:/ storage.read:/" fts_client_incaem001
export BEARER_TOKEN=$(oidc-token fts_client_incaem001)
```

Copy paste the BEARER_TOKEN in the following cell.

%% Cell type:code id:275ec614-a285-435c-a8e4-d18bf6612ca5 tags:

``` python
TOKEN = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJ3bGNnLnZlciI6IjEuMCIsInN1YiI6IjNjNDljOTEyLWEyNWYtYTc5NS02NTBmLTg1ODNkOWU4YTBlOCIsImF1ZCI6Imh0dHBzOlwvXC93bGNnLmNlcm4uY2hcL2p3dFwvdjFcL2FueSIsIm5iZiI6MTcyNTM2NTg1MSwic2NvcGUiOiJzdG9yYWdlLmNyZWF0ZTpcLyBvcGVuaWQgb2ZmbGluZV9hY2Nlc3MgcHJvZmlsZSBzdG9yYWdlLnJlYWQ6XC8gc3RvcmFnZS5tb2RpZnk6XC8iLCJpc3MiOiJodHRwczpcL1wvaW5kaWdvaWFtLXRlc3QucGljLmVzXC8iLCJleHAiOjE3MjUzNjk0NTEsImlhdCI6MTcyNTM2NTg1MSwianRpIjoiMzI3OGI5ODAtYTM0MC00OTJmLTk4MTUtYjU4M2FjM2ExMWE3IiwiY2xpZW50X2lkIjoiNjY3OWVkZDMtYzc0YS00ZTc1LTg3NzQtMGZjMGNjNzI3OTlkIn0.WgH2rff3vBaE45GADjCOh4TxZL0NyVDID7U4VYKej5A1ZnmFCbcVY6EUMcMsCDAE-NpgEPWw_F7cKwYf6CX6IzGk73SZ6VAQxTimDlF4BqVe7QIMtDkBkW4ZVyVsb_Yq1HlD8C5ORDTA2nU4a4Z0weJVCd_rotS_nWmwBAwhcjc"
TOKEN = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJ3bGNnLnZlciI6IjEuMCIsInN1YiI6IjNjNDljOTEyLWEyNWYtYTc5NS02NTBmLTg1ODNkOWU4YTBlOCIsImF1ZCI6Imh0dHBzOlwvXC93bGNnLmNlcm4uY2hcL2p3dFwvdjFcL2FueSIsIm5iZiI6MTcyNTYwOTIxNiwic2NvcGUiOiJzdG9yYWdlLnJlYWQ6XC8gc3RvcmFnZS5jcmVhdGU6XC8gc3RvcmFnZS5tb2RpZnk6XC8gb3BlbmlkIG9mZmxpbmVfYWNjZXNzIHByb2ZpbGUiLCJpc3MiOiJodHRwczpcL1wvaW5kaWdvaWFtLXRlc3QucGljLmVzXC8iLCJleHAiOjE3MjU2OTU2MTYsImlhdCI6MTcyNTYwOTIxNiwianRpIjoiOTZkYjkwZWItYWJhOC00ODc2LTg3OGMtMTAzMWIyZmFjZTM5IiwiY2xpZW50X2lkIjoiNjY3OWVkZDMtYzc0YS00ZTc1LTg3NzQtMGZjMGNjNzI3OTlkIn0.O5J26M3vSHn6W_UnY-9raDJvtEidttfGiVaP6O_1XcvL6AW-Jlx3TDvCAwYrF9lyGww6Wzv0dlVhTsvv5chwmL232rFhenZb9WXRmNEPabl5V7lbD6j8tahwEQu2CQsANTFTU4DuLtp_kcHC09SmMUaqOc-SXg9H1QTVefyFtfg"
```

%% Cell type:code id:c9a9084c-e243-4ff3-bc4e-ea53f71f984b tags:

``` python
# Origin endpoint / folder
ORIGIN_RSE = "https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um"
#ORIGIN_RSE = "https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um"
ORIGIN_RSE = "https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um"

# Destination endpoint / folder
DES_RSE = "https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um"
#DEST_RSE = "https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um"
#DEST_RSE = "https://incaem-ops01.pic.es:1094/20201125_multislice_largeBandwidth_zRange10um"
DEST_RSE = "https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um_copy"

# FTS server to submit the transfer to
FTS_SERVER = 'https://fts02.pic.es:8446'
```

%% Cell type:code id:e0f2d3c4-419e-4963-9fac-82d2cafc3244 tags:

``` python
os.environ['BEARER_TOKEN'] = TOKEN
```

%% Cell type:markdown id:d39e771e-5612-4bf5-a19d-e819688c8004 tags:

# Build the list of files to be transferred

%% Cell type:code id:424340b3-2e9a-42f7-9219-d3c40707cdd7 tags:

``` python
ctx = gfal2.creat_context()
```

%% Cell type:markdown id:04fbd022-47ad-4362-9326-e372caf5033c tags:

Code in the following cell was picked from [here](https://github.com/cern-fts/gfal2-python/blob/develop/example/python/gfal2_recursive_ls.py) and modified to enable returning a list containing all origin file urls.

%% Cell type:code id:dd6960de-cd22-44a4-9c1b-7973d8d85353 tags:

``` python
class Crawler:

    def __init__(self, output='long', recursive=False,
                 max_levels=2, context=gfal2.creat_context()):
        self.context = context
        self.output = output # one of long, short, list
        self.recursive = recursive
        self.max_levels = max_levels

        # initialize empty file list filled by the crawl method
        self.file_list = []

    def _short_format(self, fname):
        return fname

    def _mode2str(self, mode):
        s = ['-'] * 10
        if stat.S_ISDIR(mode):
            s[0] = 'd'
        elif stat.S_ISLNK(mode):
            s[0] = 'l'
        elif not stat.S_ISREG(mode):
            s[0] = '?'

        for i in range(3):
            if mode & stat.S_IRUSR:
                s[1 + i * 3] = 'r'
            if mode & stat.S_IWUSR:
                s[2 + i * 3] = 'w'
            if mode & stat.S_IXUSR:
                s[3 + i * 3] = 'x'
            mode = mode << 3
        return ''.join(s)

    def _long_format(self, fname, fstat):
        return "%s %3d %5d %5d %10d %s %s" % \
            (self._mode2str(fstat.st_mode),
             fstat.st_nlink, fstat.st_uid, fstat.st_gid,
             fstat.st_size,
             datetime.fromtimestamp(fstat.st_mtime).strftime('%b %d %H:%M'),
             fname)

    def _crawl(self, url, out, level=0):
        if level > self.max_levels:
            return

        tabbing = '\n' + '  ' * level
        try:
            entries = [f for f in self.context.listdir(url)
                       if f != '.' and f != '..']
        except gfal2.GError:
            out.write(tabbing + '!')
            return

        for f in entries:
            full = url + '/' + f
            # Do the stat only if we need to
            if self.recursive or self.long:
                try:
                    fstat = self.context.stat(full)
                except gfal2.GError:
                    fstat = self.context.st_stat()

            # Print entry
            if self.output == 'long':
                out.write(tabbing + self._long_format(f, fstat))
            elif self.output == 'short':
                out.write(tabbing + self._short_format(f))
            elif self.output == 'list' and not stat.S_ISDIR(fstat.st_mode):
                self.file_list.append(full)

            # Descend
            if self.recursive and stat.S_ISDIR(fstat.st_mode):
                self._crawl(full, out, level + 1)

    def crawl(self, url, out=sys.stdout):
        self.file_list = []
        self._crawl(url, out)
        return self.file_list
```

%% Cell type:code id:253faade-2678-49f3-863b-1163956023be tags:

``` python
crawler = Crawler(output="list", recursive=True, context=ctx, max_levels=float("inf"))
```

%% Cell type:code id:eae48c45-4c1e-4f77-aa9e-839d8465110d tags:

``` python
origin_file_list = crawler.crawl(ORIGIN_RSE)
```

%% Cell type:code id:96927935-165c-4595-b508-17040e4aec30 tags:

``` python
len(origin_file_list)
```

%% Output

    737

%% Cell type:code id:ee98d603-3080-447a-8568-7b85fda10bf4 tags:

``` python
origin_file_list[0]
```

%% Output

    'https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um/imgs2ppt/dofmap_sirius_bandwidth116.67.png'
    'https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um/imgs2ppt/dofmap_sirius_bandwidth116.67.png'

%% Cell type:markdown id:625c24b8-681a-44ce-8c75-1d2c4e265d89 tags:

# Submit the file transfers to FTS

Following the instructions in the FTS3 [easy bindings documentation](https://github.com/cern-fts/fts-rest/tree/develop/docs/easy)

%% Cell type:code id:9dc83208-bba0-43e8-ac7e-b3c57f3194ce tags:

``` python
import fts3.rest.client.easy as fts3
```

%% Cell type:markdown id:950c849b-4389-46ca-bf89-9cb8ce4ca2c7 tags:

FTS only allows file transfers (not folders), so we have to build a list of transfers, file to file.

%% Cell type:code id:8676c4e0-c394-46e9-997d-06ac3a262002 tags:

``` python
transfer_list = [ fts3.new_transfer(
    source=f,
    destination=f.replace(ORIGIN_RSE, DES_RSE),
    checksum=None
    destination=f.replace(ORIGIN_RSE, DEST_RSE)
) for f in origin_file_list ]
```

%% Cell type:code id:d1b00a46-49a9-4da7-aefc-045a35bdbc5c tags:

``` python
transfer_list[0]
```

%% Output

    {'sources': ['https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um/imgs2ppt/dofmap_sirius_bandwidth116.67.png'],
     'destinations': ['https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um_copy/imgs2ppt/dofmap_sirius_bandwidth116.67.png'],
     'checksum': 'ADLER32',
     'selection_strategy': 'auto'}

%% Cell type:code id:2400db17-d7f2-47a9-b570-0e91d972a6b3 tags:

``` python
fts_job = fts3.new_job(transfer_list)
fts_job = fts3.new_job(transfer_list, retry=3,
                       verify_checksum="none",
                       overwrite=True)
```

%% Cell type:code id:5873deff-834a-45d4-af6a-858afba21cea tags:

``` python
fts_context = fts3.Context(
    FTS_SERVER,
    fts_access_token=os.environ['BEARER_TOKEN'],
    fts_access_token=TOKEN,
    verify=False
)
```

%% Cell type:markdown id:6788d226-f022-4287-b12f-bf5ac2960822 tags:

Check that authentication with FTS is working

%% Cell type:code id:930e8f15-e7e5-4ee1-b9be-842d312d5e41 tags:

``` python
fts3.whoami(fts_context)
```

%% Output

    {'user_dn': '3c49c912-a25f-a795-650f-8583d9e8a0e8',
     'dn': ['3c49c912-a25f-a795-650f-8583d9e8a0e8'],
     'base_id': '01874efb-4735-4595-bc9c-591aef8240c9',
     'voms_cred': ['offline_access',
      'openid',
      'profile',
      'storage.create:/',
      'storage.modify:/',
      'storage.read:/'],
     'vos': ['indigoiam-test.pic.es'],
     'vos_id': ['f4656e4b-8f00-537c-8744-78a0d32b2dd4'],
     'roles': [],
     'level': {'transfer': 'vo', 'datamanagement': 'all'},
     'delegation_id': 'adf087ff3b34b961',
     'method': 'oauth2',
     'is_root': False,
     'oauth2_scope': 'offline_access openid profile storage.create:/ storage.modify:/ storage.read:/',
     'wlcg_profile': True,
     'get_granted_level_for_overriden': {},
     'get_granted_level_for': {}}

%% Cell type:markdown id:ee767e97-b718-4d6e-a894-1c8846818957 tags:

Submit the transfer to FTS

%% Cell type:code id:4829c6fb-20e4-4752-92ea-0541a43e1e5b tags:

``` python
job_id = fts3.submit(fts_context, fts_job)
```

%% Cell type:code id:fed364ba-a706-4099-b102-f282ee1d1744 tags:

``` python
job_id
```

%% Output

    '106e8c30-69f1-11ef-9268-001a4aae003f'
    '39058554-6b77-11ef-b6af-001a4aae003f'

%% Cell type:markdown id:795302d9-3186-418e-830d-4e511f2b1e46 tags:

Old job ids

* 2nd job where all transfers crashed: 'd150702e-69fd-11ef-907f-001a4aae003f'
* job from dcache to pic's xrootd that succeeded: c552a874-6ac9-11ef-8fbd-001a4aae003f
* 2024-09-04 17:28 from ALBA's xrootd to pic's dcache: 304520d2-6ad2-11ef-b6f3-001a4aae003f
* 2024-09-05 07:40 from ALBA's xrootd to pic's dcache after enabling token daemons: 4ee171a4-6b49-11ef-b94b-001a4aae003f
* 2024-09-05 09:07 same with 24h token: 42b7249c-6b66-11ef-8b4d-001a4aae003f. Refresh tokens expired in 1h, same result
*

%% Cell type:code id:73ffbeba-742c-4b32-9372-fcfa665ec989 tags:

``` python
fts3.get_job_status(fts_context, job_id)
status_files = fts3.get_job_status(
    fts_context,
    job_id,
    list_files=True
)
```

%% Cell type:code id:8cf9cbdd-35b8-4a23-beb6-0e0a4874d774 tags:

``` python
reasons = [e['reason'] for e in status_files['files']]
```

%% Cell type:code id:924a1068-14eb-4408-a27d-d986710b6270 tags:

``` python
Counter(reasons)
```

%% Output

    {'job_id': '106e8c30-69f1-11ef-9268-001a4aae003f',
     'user_dn': '3c49c912-a25f-a795-650f-8583d9e8a0e8',
     'dst_file_report': False,
     'source_se': 'https://globus-xrootd.cells.es',
     'cred_id': 'adf087ff3b34b961',
     'overwrite_flag': False,
     'job_metadata': {'auth_method': 'oauth2'},
     'dest_se': 'https://door04.pic.es',
     'vo_name': 'indigoiam-test.pic.es',
     'job_finished': None,
     'retry': -1,
     'source_spacetoken': None,
     'job_state': 'ACTIVE',
     'reason': None,
     'copy_pin_lifetime': -1,
     'retry_delay': 0,
     'destination_spacetoken': None,
     'job_type': 'N',
     'submit_time': '2024-09-03T12:35:56',
     'bring_online': -1,
     'os_project_id': None,
     'verify_checksum': 'n',
     'cancel_job': False,
     'priority': 3,
     'archive_timeout': -1,
     'submit_host': 'fts02.pic.es',
     'max_time_in_queue': None,
     'target_qos': None,
     'internal_job_params': 'nostreams:1',
     'http_status': '200 Ok'}
    Counter({None: 425, '': 312})

%% Cell type:code id:8cf9cbdd-35b8-4a23-beb6-0e0a4874d774 tags:
%% Cell type:code id:b6377892 tags:

``` python
fts3.cancel(
```

%% Output

    ['Ban',
     'ClientError',
     'Context',
     'Delegator',
     'Inquirer',
     'JobIdGenerator',
     'Submitter',
     '__builtins__',
     '__cached__',
     '__doc__',
     '__file__',
     '__loader__',
     '__name__',
     '__package__',
     '__path__',
     '__spec__',
     'add_alternative_source',
     'ban',
     'ban_dn',
     'ban_se',
     'cancel',
     'cancel_all',
     'delegate',
     'exceptions',
     'get_job_status',
     'get_jobs_statuses',
     'list_jobs',
     'new_delete_job',
     'new_job',
     'new_staging_job',
     'new_transfer',
     'state',
     'submission',
     'submit',
     'timedelta',
     'unban_dn',
     'unban_se',
     'warnings',
     'whoami']

%% Cell type:code id:4454c15e-3c2b-4a9b-a87a-ab035606f55f tags:

``` python
```
+35 −10
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ import os
import sys
import stat
from datetime import datetime
from collections import Counter

import gfal2
# -
@@ -47,14 +48,17 @@ import gfal2
#
# Copy paste the BEARER_TOKEN in the following cell.

TOKEN = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJ3bGNnLnZlciI6IjEuMCIsInN1YiI6IjNjNDljOTEyLWEyNWYtYTc5NS02NTBmLTg1ODNkOWU4YTBlOCIsImF1ZCI6Imh0dHBzOlwvXC93bGNnLmNlcm4uY2hcL2p3dFwvdjFcL2FueSIsIm5iZiI6MTcyNTM2NTg1MSwic2NvcGUiOiJzdG9yYWdlLmNyZWF0ZTpcLyBvcGVuaWQgb2ZmbGluZV9hY2Nlc3MgcHJvZmlsZSBzdG9yYWdlLnJlYWQ6XC8gc3RvcmFnZS5tb2RpZnk6XC8iLCJpc3MiOiJodHRwczpcL1wvaW5kaWdvaWFtLXRlc3QucGljLmVzXC8iLCJleHAiOjE3MjUzNjk0NTEsImlhdCI6MTcyNTM2NTg1MSwianRpIjoiMzI3OGI5ODAtYTM0MC00OTJmLTk4MTUtYjU4M2FjM2ExMWE3IiwiY2xpZW50X2lkIjoiNjY3OWVkZDMtYzc0YS00ZTc1LTg3NzQtMGZjMGNjNzI3OTlkIn0.WgH2rff3vBaE45GADjCOh4TxZL0NyVDID7U4VYKej5A1ZnmFCbcVY6EUMcMsCDAE-NpgEPWw_F7cKwYf6CX6IzGk73SZ6VAQxTimDlF4BqVe7QIMtDkBkW4ZVyVsb_Yq1HlD8C5ORDTA2nU4a4Z0weJVCd_rotS_nWmwBAwhcjc"
TOKEN = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJ3bGNnLnZlciI6IjEuMCIsInN1YiI6IjNjNDljOTEyLWEyNWYtYTc5NS02NTBmLTg1ODNkOWU4YTBlOCIsImF1ZCI6Imh0dHBzOlwvXC93bGNnLmNlcm4uY2hcL2p3dFwvdjFcL2FueSIsIm5iZiI6MTcyNTYwOTIxNiwic2NvcGUiOiJzdG9yYWdlLnJlYWQ6XC8gc3RvcmFnZS5jcmVhdGU6XC8gc3RvcmFnZS5tb2RpZnk6XC8gb3BlbmlkIG9mZmxpbmVfYWNjZXNzIHByb2ZpbGUiLCJpc3MiOiJodHRwczpcL1wvaW5kaWdvaWFtLXRlc3QucGljLmVzXC8iLCJleHAiOjE3MjU2OTU2MTYsImlhdCI6MTcyNTYwOTIxNiwianRpIjoiOTZkYjkwZWItYWJhOC00ODc2LTg3OGMtMTAzMWIyZmFjZTM5IiwiY2xpZW50X2lkIjoiNjY3OWVkZDMtYzc0YS00ZTc1LTg3NzQtMGZjMGNjNzI3OTlkIn0.O5J26M3vSHn6W_UnY-9raDJvtEidttfGiVaP6O_1XcvL6AW-Jlx3TDvCAwYrF9lyGww6Wzv0dlVhTsvv5chwmL232rFhenZb9WXRmNEPabl5V7lbD6j8tahwEQu2CQsANTFTU4DuLtp_kcHC09SmMUaqOc-SXg9H1QTVefyFtfg"

# +
# Origin endpoint / folder
ORIGIN_RSE = "https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um"
#ORIGIN_RSE = "https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um"
ORIGIN_RSE = "https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um"

# Destination endpoint / folder
DES_RSE = "https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um"
#DEST_RSE = "https://door04.pic.es:8460/pnfs/pic.es/data/incaem/20201125_multislice_largeBandwidth_zRange10um"
#DEST_RSE = "https://incaem-ops01.pic.es:1094/20201125_multislice_largeBandwidth_zRange10um"
DEST_RSE = "https://globus-xrootd.cells.es:1094/20201125_multislice_largeBandwidth_zRange10um_copy"

# FTS server to submit the transfer to
FTS_SERVER = 'https://fts02.pic.es:8446'
@@ -150,7 +154,6 @@ class Crawler:
        return self.file_list



crawler = Crawler(output="list", recursive=True, context=ctx, max_levels=float("inf"))

origin_file_list = crawler.crawl(ORIGIN_RSE)
@@ -169,16 +172,19 @@ import fts3.rest.client.easy as fts3

transfer_list = [ fts3.new_transfer(
    source=f,
    destination=f.replace(ORIGIN_RSE, DES_RSE),
    checksum=None
    destination=f.replace(ORIGIN_RSE, DEST_RSE)
) for f in origin_file_list ]


fts_job = fts3.new_job(transfer_list)
transfer_list[0]

fts_job = fts3.new_job(transfer_list, retry=3,
                       verify_checksum="none",
                       overwrite=True)

fts_context = fts3.Context(
    FTS_SERVER,
    fts_access_token=os.environ['BEARER_TOKEN'],
    fts_access_token=TOKEN,
    verify=False
)

@@ -192,6 +198,25 @@ job_id = fts3.submit(fts_context, fts_job)

job_id

fts3.get_job_status(fts_context, job_id)
# Old job ids
#
# * 2nd job where all transfers crashed: 'd150702e-69fd-11ef-907f-001a4aae003f'
# * job from dcache to pic's xrootd that succeeded: c552a874-6ac9-11ef-8fbd-001a4aae003f
# * 2024-09-04 17:28 from ALBA's xrootd to pic's dcache: 304520d2-6ad2-11ef-b6f3-001a4aae003f
# * 2024-09-05 07:40 from ALBA's xrootd to pic's dcache after enabling token daemons: 4ee171a4-6b49-11ef-b94b-001a4aae003f
# * 2024-09-05 09:07 same with 24h token: 42b7249c-6b66-11ef-8b4d-001a4aae003f. Refresh tokens expired in 1h, same result
# * 

status_files = fts3.get_job_status(
    fts_context,
    job_id,
    list_files=True
)

reasons = [e['reason'] for e in status_files['files']]

Counter(reasons)

fts3.cancel(