diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 4ead663..0000000 --- a/.coveragerc +++ /dev/null @@ -1,3 +0,0 @@ -[run] -relative_files = True -omit = tests/* diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 6a7695c..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "weekly" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 5b44549..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Docker - -on: - push: - branches: [ master ] - release: - types: [ created ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: 'Checkout GitHub Action' - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Docker hub meta - id: meta - uses: docker/metadata-action@v5 - with: - flavor: | - latest=true - tags: | - type=semver,pattern=v{{version}} - type=ref,event=branch - type=sha - images: ${{ github.actor }}/prometheus-qbittorrent-exporter - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.REGISTRY_USERNAME }} - password: ${{ secrets.REGISTRY_PASSWORD }} - - - name: Build and push docker to DockerHub - uses: docker/build-push-action@v5 - with: - push: true - platforms: linux/amd64,linux/arm64,linux/386 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - - name: GHCR Docker meta - id: metaghcr - uses: docker/metadata-action@v5 - with: - flavor: | - latest=true - tags: | - type=semver,pattern=v{{version}} - type=ref,event=branch - type=sha - images: ghcr.io/${{ github.actor }}/prometheus-qbittorrent-exporter - - - name: Login to Github Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push docker to Github Container Registry - uses: docker/build-push-action@v5 - with: - push: true - platforms: linux/amd64,linux/arm64,linux/386 - tags: ${{ steps.metaghcr.outputs.tags }} - labels: ${{ steps.metaghcr.outputs.labels }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index d1b7059..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Run Unit Test via Pytest - -on: - pull_request: - push: - branches: - - "master" - -jobs: - lint-and-test: - runs-on: ubuntu-latest - permissions: - pull-requests: write - contents: write - checks: write - strategy: - matrix: - python-version: ["3.11"] - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Setup PDM - uses: pdm-project/setup-pdm@v3 - - name: Install dependencies - run: pdm install - - name: Lint with black - uses: psf/black@stable - with: - options: "--check --verbose" - - name: Test with pytest - run: | - pdm run pytest --junit-xml=test-results.xml - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - files: | - test-results.xml - - name: Test with coverage - run: | - pdm run coverage run -m pytest -v -s - - name: Generate Coverage Report - run: | - pdm run coverage report -m - - name: Coverage comment - id: coverage_comment - uses: py-cov-action/python-coverage-comment-action@v3 - with: - GITHUB_TOKEN: ${{ github.token }} - - name: Store Pull Request comment to be posted - uses: actions/upload-artifact@v3 - if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' - with: - name: python-coverage-comment-action - path: python-coverage-comment-action.txt diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml deleted file mode 100644 index 36caa06..0000000 --- a/.github/workflows/pythonpublish.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Upload Python Package - -on: - release: - types: [created] - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: '3.9' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python setup.py sdist - twine upload dist/* diff --git a/.idea/misc.xml b/.idea/misc.xml index e82613a..154d3e7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,7 @@ + + \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index a63f4e4..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,29 +0,0 @@ -repos: - - repo: local - hooks: - - id: black - name: black - stages: [commit] - types: [python] - entry: pdm run black . - language: system - pass_filenames: false - always_run: true - - id: ruff - name: ruff - stages: [commit] - types: [python] - entry: pdm run ruff . - language: system - pass_filenames: false - always_run: true - fail_fast: true - - id: pytest - name: pytest - stages: [commit] - types: [python] - entry: pdm run pytest - language: system - pass_filenames: false - always_run: true - fail_fast: true diff --git a/Dockerfile b/Dockerfile index f58eb14..2a3912f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16.4 +FROM alpine:3.18.4 # Installing required packages FROM alpine/doctl diff --git a/config.env.example b/config.env.example deleted file mode 100644 index 55bbd2a..0000000 --- a/config.env.example +++ /dev/null @@ -1,6 +0,0 @@ -QBITTORRENT_HOST=localhost -QBITTORRENT_PORT=8080 -QBITTORRENT_USER=admin -QBITTORRENT_PASS=adminadmin -EXPORTER_PORT=8000 -METRICS_PREFIX=qbittorrent \ No newline at end of file diff --git a/immich_exporter/exporter.py b/immich_exporter/exporter.py index b3a4afc..4db3f14 100644 --- a/immich_exporter/exporter.py +++ b/immich_exporter/exporter.py @@ -49,7 +49,6 @@ class ImmichMetricsCollector: return metrics - def get_immich_users_stat_growth(self): try: @@ -63,24 +62,24 @@ class ImmichMetricsCollector: except requests.exceptions.RequestException as e: logger.error(f"Couldn't get server version: {e}") - userData = response_user_stats.json()["usageByUser"] + user_data = response_user_stats.json()["usageByUser"] # photos growth gauge - userCount = len(response_user_stats.json()["usageByUser"]) + user_count = len(response_user_stats.json()["usageByUser"]) photos_growth_total = 0 videos_growth_total = 0 usage_growth_total = 0 - for x in range(0, userCount): - photos_growth_total += userData[x]["photos"] + for x in range(0, user_count): + photos_growth_total += user_data[x]["photos"] # total video growth - videos_growth_total += userData[x]["videos"] + videos_growth_total += user_data[x]["videos"] # total disk growth - usage_growth_total += userData[x]["usage"] + usage_growth_total += user_data[x]["usage"] return [ { "name": f"{self.config['metrics_prefix']}_server_stats_user_count", - "value": userCount, + "value": user_count, "help": "number of users on the immich server" }, { @@ -122,53 +121,54 @@ class ImmichMetricsCollector: # To get the user count an api-endpoint exists but this works too. As a result one less api call is being made try: - userCount = len(response_user_stats.json()["usageByUser"]) + user_count = len(response_user_stats.json()["usageByUser"]) except Exception: logger.error("Is the Immich api token valid? Traceback:KeyError: 'usageByUser': ") # json array of all users with stats - # this line throws an error if api token is wrong. if the token is wrong or inavlid this will return a KeyError : 'usage by user' - userData = response_user_stats.json()["usageByUser"] + # this line throws an error if api token is wrong. if the token is wrong + # or invalid this will return a KeyError : 'usage by user' + user_data = response_user_stats.json()["usageByUser"] - for x in range(0, userCount): + for x in range(0, user_count): metrics.append( { "name": f"{self.config['metrics_prefix']}_server_stats_photos_by_users", - "value": userData[x]['photos'], + "value": user_data[x]['photos'], "labels": { - "firstName": userData[x]["userName"].split()[0], + "firstName": user_data[x]["userName"].split()[0], }, - "help": f"Number of photos by user {userData[x]['userName'].split()[0]} " + "help": f"Number of photos by user {user_data[x]['userName'].split()[0]} " } ) # videos - for x in range(0, userCount): + for x in range(0, user_count): metrics.append( { "name": f"{self.config['metrics_prefix']}_server_stats_videos_by_users", - "value": userData[x]['videos'], + "value": user_data[x]['videos'], "labels": { - "firstName": userData[x]["userName"].split()[0], + "firstName": user_data[x]["userName"].split()[0], }, - "help": f"Number of photos by user {userData[x]['userName'].split()[0]} " + "help": f"Number of photos by user {user_data[x]['userName'].split()[0]} " } ) # usage - for x in range(0, userCount): + for x in range(0, user_count): metrics.append( { "name": f"{self.config['metrics_prefix']}_server_stats_usage_by_users", - "value": (userData[x]['usage']), + "value": (user_data[x]['usage']), "labels": { - "firstName": userData[x]["userName"].split()[0], + "firstName": user_data[x]["userName"].split()[0], }, - "help": f"Number of photos by user {userData[x]['userName'].split()[0]} " + "help": f"Number of photos by user {user_data[x]['userName'].split()[0]} " } ) @@ -294,11 +294,9 @@ def get_config_value(key, default=""): def check_server_up(immichHost, immichPort): - # counter = 0 - while True: counter = counter + 1 try: @@ -310,7 +308,7 @@ def check_server_up(immichHost, immichPort): ) except requests.exceptions.RequestException as e: logger.error(f"CONNECTION ERROR. Cannot reach immich at " + immichHost + ":" + immichPort + "." - f"Is immich up and running?") + f"Is immich up and running?") if 0 <= counter <= 60: time.sleep(1) elif 11 <= counter <= 300: @@ -326,13 +324,12 @@ def check_server_up(immichHost, immichPort): def check_immich_api_key(immichHost, immichPort, immichApiKey): - while True: try: requests.request( "GET", - "http://"+immichHost+":"+immichPort+"/api/server-info/", + "http://" + immichHost + ":" + immichPort + "/api/server-info/", headers={'Accept': 'application/json', "x-api-key": immichApiKey} ) @@ -394,9 +391,7 @@ def main(): f"Exporter listening on port {config['exporter_port']}" ) - while not signal_handler.is_shutting_down(): time.sleep(1) logger.info("Exporter has shutdown") - diff --git a/logo.png b/logo.png deleted file mode 100644 index 996f2be..0000000 Binary files a/logo.png and /dev/null differ diff --git a/pdm.lock b/pdm.lock deleted file mode 100644 index 62b32fe..0000000 --- a/pdm.lock +++ /dev/null @@ -1,284 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default", "dev"] -strategy = ["cross_platform"] -lock_version = "4.4" -content_hash = "sha256:474c773ee86217d652b5fc82f921ce2fbb513b244d50fbe82b74ea9e47dad96e" - -[[package]] -name = "black" -version = "23.11.0" -requires_python = ">=3.8" -summary = "The uncompromising code formatter." -dependencies = [ - "click>=8.0.0", - "mypy-extensions>=0.4.3", - "packaging>=22.0", - "pathspec>=0.9.0", - "platformdirs>=2", -] -files = [ - {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, - {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, - {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, - {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, - {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, - {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, -] - -[[package]] -name = "certifi" -version = "2023.7.22" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." -files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.2.0" -requires_python = ">=3.7.0" -summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - -[[package]] -name = "click" -version = "8.1.7" -requires_python = ">=3.7" -summary = "Composable command line interface toolkit" -dependencies = [ - "colorama; platform_system == \"Windows\"", -] -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.3.2" -requires_python = ">=3.8" -summary = "Code coverage measurement for Python" -files = [ - {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, - {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, - {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, - {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, - {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, - {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, - {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, -] - -[[package]] -name = "idna" -version = "3.4" -requires_python = ">=3.5" -summary = "Internationalized Domain Names in Applications (IDNA)" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" -summary = "brain-dead simple config-ini parsing" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "isort" -version = "5.12.0" -requires_python = ">=3.8.0" -summary = "A Python utility / library to sort Python imports." -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" -summary = "Type system extensions for programs checked with the mypy type checker." -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "packaging" -version = "23.1" -requires_python = ">=3.7" -summary = "Core utilities for Python packages" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "pathspec" -version = "0.11.2" -requires_python = ">=3.7" -summary = "Utility library for gitignore style pattern matching of file paths." -files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, -] - -[[package]] -name = "platformdirs" -version = "4.0.0" -requires_python = ">=3.7" -summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, -] - -[[package]] -name = "pluggy" -version = "1.3.0" -requires_python = ">=3.8" -summary = "plugin and hook calling mechanisms for python" -files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, -] - -[[package]] -name = "prometheus-client" -version = "0.17.1" -requires_python = ">=3.6" -summary = "Python client for the Prometheus monitoring system." -files = [ - {file = "prometheus_client-0.17.1-py3-none-any.whl", hash = "sha256:e537f37160f6807b8202a6fc4764cdd19bac5480ddd3e0d463c3002b34462101"}, - {file = "prometheus_client-0.17.1.tar.gz", hash = "sha256:21e674f39831ae3f8acde238afd9a27a37d0d2fb5a28ea094f0ce25d2cbf2091"}, -] - -[[package]] -name = "pytest" -version = "7.4.3" -requires_python = ">=3.7" -summary = "pytest: simple powerful testing with Python" -dependencies = [ - "colorama; sys_platform == \"win32\"", - "iniconfig", - "packaging", - "pluggy<2.0,>=0.12", -] -files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, -] - -[[package]] -name = "python-json-logger" -version = "2.0.7" -requires_python = ">=3.6" -summary = "A python library adding a json log formatter" -files = [ - {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, - {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, -] - -[[package]] -name = "qbittorrent-api" -version = "2023.9.53" -summary = "Python client for qBittorrent v4.1+ Web API." -dependencies = [ - "packaging", - "requests>=2.16.0", - "six", - "urllib3>=1.24.2", -] -files = [ - {file = "qbittorrent-api-2023.9.53.tar.gz", hash = "sha256:fead1b2f55b1227ea088ea7d90b5022d94694bfd9dd9176beb5ad1c195d044ff"}, - {file = "qbittorrent_api-2023.9.53-py2.py3-none-any.whl", hash = "sha256:963ae59d16a9c4a9aa1714fb7f6799539dc2693136cdc0e377daab3612ca775a"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -requires_python = ">=3.7" -summary = "Python HTTP for Humans." -dependencies = [ - "certifi>=2017.4.17", - "charset-normalizer<4,>=2", - "idna<4,>=2.5", - "urllib3<3,>=1.21.1", -] -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[[package]] -name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python 2 and 3 compatibility utilities" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "urllib3" -version = "2.0.5" -requires_python = ">=3.7" -summary = "HTTP library with thread-safe connection pooling, file post, and more." -files = [ - {file = "urllib3-2.0.5-py3-none-any.whl", hash = "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e"}, - {file = "urllib3-2.0.5.tar.gz", hash = "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594"}, -] diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 8f79963..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,36 +0,0 @@ -[project] -name = "prometheus-qbittorrent-exporter" -version = "1.4.0" -description = "Prometheus exporter for qbittorrent" -authors = [ - {name = "Esteban Sanchez", email = "esteban.sanchez@gmail.com"}, -] -dependencies = [ - "prometheus-client>=0.17.1", - "python-json-logger>=2.0.7", - "qbittorrent-api>=2023.9.53", -] -requires-python = ">=3.11" -readme = "README.md" -keywords = ["prometheus", "qbittorrent"] -license = {text = "GPL-3.0"} -classifiers = [] - -[project.urls] -Homepage = "https://github.com/esanchezm/prometheus-qbittorrent-exporter" -Downloads = "https://github.com/esanchezm/prometheus-qbittorrent-exporter/archive/1.4.0.tar.gz" - -[project.scripts] -qbittorrent-exporter = "qbittorrent_exporter.exporter:main" - -[build-system] -requires = ["pdm-backend"] -build-backend = "pdm.backend" - -[tool.pdm.dev-dependencies] -dev = [ - "pytest>=7.4.3", - "isort>=5.12.0", - "black>=23.11.0", - "coverage>=7.3.2", -] diff --git a/setup.py b/setup.py index 0c133dc..280b451 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open("README.md", "r") as fh: setup( name='prometheus-immich-exporter', packages=['immich_exporter'], - version='1.0.9', + version='1.1.0', long_description=long_description, long_description_content_type="text/markdown", description='Prometheus exporter for immich', @@ -18,7 +18,7 @@ setup( keywords=['prometheus', 'immich'], classifiers=[], python_requires='>=3', - install_requires=['attrdict==2.0.1', 'prometheus_client==0.12.0 ', 'requests==2.28.2', 'python-json-logger==2.0.2'], + install_requires=['attrdict==2.0.1', 'prometheus_client==0.19.0 ', 'requests==2.31.0', 'python-json-logger==2.0.7'], entry_points={ 'console_scripts': [ 'immich_exporter=immich_exporter.exporter:main', diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/exporter_test.py b/tests/exporter_test.py deleted file mode 100644 index 7153ab8..0000000 --- a/tests/exporter_test.py +++ /dev/null @@ -1,276 +0,0 @@ -import unittest -from unittest.mock import MagicMock, patch - -from prometheus_client.metrics_core import CounterMetricFamily, GaugeMetricFamily - -from qbittorrent_exporter.exporter import ( - Metric, - MetricType, - QbittorrentMetricsCollector, -) -from qbittorrentapi import TorrentStates - - -class TestQbittorrentMetricsCollector(unittest.TestCase): - @patch("qbittorrent_exporter.exporter.Client") - def setUp(self, mock_client): - self.mock_client = mock_client - self.config = { - "host": "localhost", - "port": "8080", - "username": "user", - "password": "pass", - "verify_webui_certificate": False, - "metrics_prefix": "qbittorrent", - } - self.torrentsState = [ - {"name": "Torrent DOWNLOADING 1", "state": TorrentStates.DOWNLOADING}, - {"name": "Torrent UPLOADING 1", "state": TorrentStates.UPLOADING}, - {"name": "Torrent DOWNLOADING 2", "state": TorrentStates.DOWNLOADING}, - {"name": "Torrent UPLOADING 2", "state": TorrentStates.UPLOADING}, - ] - self.torrentsCategories = [ - {"name": "Torrent Movies 1", "category": "Movies"}, - {"name": "Torrent Music 1", "category": "Music"}, - {"name": "Torrent Movies 2", "category": "Movies"}, - {"name": "Torrent unknown", "category": ""}, - {"name": "Torrent Music 2", "category": "Music"}, - {"name": "Torrent Uncategorized 1", "category": "Uncategorized"}, - ] - self.collector = QbittorrentMetricsCollector(self.config) - - def test_init(self): - self.assertEqual(self.collector.config, self.config) - self.mock_client.assert_called_once_with( - host=self.config["host"], - port=self.config["port"], - username=self.config["username"], - password=self.config["password"], - VERIFY_WEBUI_CERTIFICATE=self.config["verify_webui_certificate"], - ) - - def test_collect_gauge(self): - mock_metric = Metric( - name="test_gauge", - metric_type=MetricType.GAUGE, - help_text="Test Gauge", - labels={"label1": "value1"}, - value=10, - ) - self.collector.get_qbittorrent_metrics = MagicMock(return_value=[mock_metric]) - - result = next(self.collector.collect()) - - self.assertIsInstance(result, GaugeMetricFamily) - self.assertEqual(result.name, "test_gauge") - self.assertEqual(result.documentation, "Test Gauge") - self.assertEqual(result.samples[0].labels, {"label1": "value1"}) - self.assertEqual(result.samples[0].value, 10) - - def test_collect_counter(self): - mock_metric = Metric( - name="test_counter", - metric_type=MetricType.COUNTER, - help_text="Test Counter", - labels={"label2": "value2"}, - value=230, - ) - self.collector.get_qbittorrent_metrics = MagicMock(return_value=[mock_metric]) - - result = next(self.collector.collect()) - - self.assertIsInstance(result, CounterMetricFamily) - self.assertEqual(result.name, "test_counter") - self.assertEqual(result.documentation, "Test Counter") - self.assertEqual(result.samples[0].labels, {"label2": "value2"}) - self.assertEqual(result.samples[0].value, 230) - - def test_get_qbittorrent_metrics(self): - metrics = self.collector.get_qbittorrent_metrics() - self.assertNotEqual(len(metrics), 0) - - def test_fetch_categories(self): - # Mock the client.torrent_categories.categories attribute - self.collector.client.torrent_categories.categories = { - "category1": {"name": "Category 1"}, - "category2": {"name": "Category 2"}, - "category3": {"name": "Category 3"}, - } - - categories = self.collector._fetch_categories() - self.assertIsInstance(categories, dict) - self.assertNotEqual(len(categories), 0) - self.assertEqual(categories["category1"]["name"], "Category 1") - self.assertEqual(categories["category2"]["name"], "Category 2") - self.assertEqual(categories["category3"]["name"], "Category 3") - - def test_fetch_categories_exception(self): - self.collector.client.torrent_categories.categories = Exception( - "Error fetching categories" - ) - categories = self.collector._fetch_categories() - self.assertEqual(categories, {}) - - def test_fetch_torrents_success(self): - # Mock the return value of self.client.torrents.info() - self.collector.client.torrents.info.return_value = [ - {"name": "Torrent 1", "size": 100}, - {"name": "Torrent 2", "size": 200}, - {"name": "Torrent 3", "size": 300}, - ] - - expected_result = [ - {"name": "Torrent 1", "size": 100}, - {"name": "Torrent 2", "size": 200}, - {"name": "Torrent 3", "size": 300}, - ] - - result = self.collector._fetch_torrents() - self.assertEqual(result, expected_result) - - def test_fetch_torrents_exception(self): - # Mock an exception being raised by self.client.torrents.info() - self.collector.client.torrents.info.side_effect = Exception("Connection error") - - expected_result = [] - - result = self.collector._fetch_torrents() - self.assertEqual(result, expected_result) - - def test_filter_torrents_by_state(self): - expected = [ - {"name": "Torrent DOWNLOADING 1", "state": TorrentStates.DOWNLOADING}, - {"name": "Torrent DOWNLOADING 2", "state": TorrentStates.DOWNLOADING}, - ] - result = self.collector._filter_torrents_by_state( - TorrentStates.DOWNLOADING, self.torrentsState - ) - self.assertEqual(result, expected) - - expected = [ - {"name": "Torrent UPLOADING 1", "state": TorrentStates.UPLOADING}, - {"name": "Torrent UPLOADING 2", "state": TorrentStates.UPLOADING}, - ] - result = self.collector._filter_torrents_by_state( - TorrentStates.UPLOADING, self.torrentsState - ) - self.assertEqual(result, expected) - - expected = [] - result = self.collector._filter_torrents_by_state( - TorrentStates.ERROR, self.torrentsState - ) - self.assertEqual(result, expected) - - def test_filter_torrents_by_category(self): - expected_result = [ - {"name": "Torrent Movies 1", "category": "Movies"}, - {"name": "Torrent Movies 2", "category": "Movies"}, - ] - result = self.collector._filter_torrents_by_category( - "Movies", self.torrentsCategories - ) - self.assertEqual(result, expected_result) - - expected_result = [ - {"name": "Torrent unknown", "category": ""}, - {"name": "Torrent Uncategorized 1", "category": "Uncategorized"}, - ] - result = self.collector._filter_torrents_by_category( - "Uncategorized", self.torrentsCategories - ) - self.assertEqual(result, expected_result) - - expected_result = [] - result = self.collector._filter_torrents_by_category( - "Books", self.torrentsCategories - ) - self.assertEqual(result, expected_result) - - def test_construct_metric_with_valid_state_and_category(self): - state = "downloading" - category = "movies" - count = 10 - - metric = self.collector._construct_metric(state, category, count) - - self.assertEqual(metric.name, "qbittorrent_torrents_count") - self.assertEqual(metric.value, count) - self.assertEqual(metric.labels["status"], state) - self.assertEqual(metric.labels["category"], category) - self.assertEqual( - metric.help_text, - f"Number of torrents in status {state} under category {category}", - ) - - def test_construct_metric_with_empty_state_and_category(self): - state = "" - category = "" - count = 5 - - metric = self.collector._construct_metric(state, category, count) - - self.assertEqual(metric.name, "qbittorrent_torrents_count") - self.assertEqual(metric.value, count) - self.assertEqual(metric.labels["status"], state) - self.assertEqual(metric.labels["category"], category) - self.assertEqual( - metric.help_text, "Number of torrents in status under category " - ) - - def test_get_qbittorrent_status_metrics(self): - self.collector.client.transfer.info = {"connection_status": "connected"} - self.collector.client.app.version = "1.2.3" - - expected_metrics = [ - Metric( - name="qbittorrent_up", - value=True, - labels={"version": "1.2.3"}, - help_text=( - "Whether the qBittorrent server is answering requests from this" - " exporter. A `version` label with the server version is added." - ), - ), - Metric( - name="qbittorrent_connected", - value=True, - labels={}, - help_text=( - "Whether the qBittorrent server is connected to the Bittorrent" - " network." - ), - ), - Metric( - name="qbittorrent_firewalled", - value=False, - labels={}, - help_text=( - "Whether the qBittorrent server is connected to the Bittorrent" - " network but is behind a firewall." - ), - ), - Metric( - name="qbittorrent_dht_nodes", - value=0, - labels={}, - help_text="Number of DHT nodes connected to.", - ), - Metric( - name="qbittorrent_dl_info_data", - value=0, - labels={}, - help_text="Data downloaded since the server started, in bytes.", - metric_type=MetricType.COUNTER, - ), - Metric( - name="qbittorrent_up_info_data", - value=0, - labels={}, - help_text="Data uploaded since the server started, in bytes.", - metric_type=MetricType.COUNTER, - ), - ] - - metrics = self.collector._get_qbittorrent_status_metrics() - self.assertEqual(metrics, expected_metrics) diff --git a/tests/metric_test.py b/tests/metric_test.py deleted file mode 100644 index 7c597ee..0000000 --- a/tests/metric_test.py +++ /dev/null @@ -1,13 +0,0 @@ -import unittest - -from qbittorrent_exporter.exporter import Metric, MetricType - - -class TestMetric(unittest.TestCase): - def test_metric_initialization(self): - metric = Metric(name="test_metric", value=10) - self.assertEqual(metric.name, "test_metric") - self.assertEqual(metric.value, 10) - self.assertEqual(metric.labels, {}) - self.assertEqual(metric.help_text, "") - self.assertEqual(metric.metric_type, MetricType.GAUGE) diff --git a/unraid/prometheus-immich-exporter.xml b/unraid/prometheus-immich-exporter.xml index 706bf0e..b78a32c 100644 --- a/unraid/prometheus-immich-exporter.xml +++ b/unraid/prometheus-immich-exporter.xml @@ -22,7 +22,7 @@ https://www.paypal.com/donate/?hosted_button_id=DPDKED3T3BFV8 8000 - + \ No newline at end of file