Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc07952b2b | ||
|
|
a52fc338c9 | ||
|
|
f22f0f2ccc | ||
|
|
88db93b07f | ||
|
|
33aeb8ad16 | ||
|
|
f8364afd01 | ||
|
|
ab12fe57da | ||
|
|
a51eb33496 | ||
|
|
551e283f54 | ||
|
|
e73c849a7e |
No files matched your search
@@ -0,0 +1,11 @@
|
|||||||
|
# Compilation et développement
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lors de la construction des bibliothèques ou des images du dépôt, ou de l’exécution d’une tâche ponctuelle.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- Privilégier une cible du `makefile` racine lorsqu’elle couvre l’opération demandée.
|
||||||
|
- `make build_libs` construit les wheels locales dans `_dist/`.
|
||||||
|
- `make build_api` et `make build_worker` construisent les images correspondantes et dépendent de `build_libs`.
|
||||||
|
- `make run_docker` construit les deux images puis lance la pile Compose.
|
||||||
|
- Pour les tâches ponctuelles sans cible Make dédiée, utiliser `job_id="$(portly temp '<command>' --path . --timeout 30m)"`, puis `portly wait "$job_id"`.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Style de code
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lors de la modification du code Python de l’API ou des workers.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- Indenter avec quatre espaces.
|
||||||
|
- Utiliser `snake_case` pour les modules, fonctions et variables, et `CapWords` pour les classes.
|
||||||
|
- Garder les noms de tâches et de files d’attente identiques entre l’API et les workers.
|
||||||
|
- Aucun formateur ni linter n’est configuré : suivre le code voisin.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Configuration et secrets
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lors de la configuration de l’API ou du worker.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- Les paramètres de l’API et du worker sont chargés depuis l’environnement dans `app/core/config.py`.
|
||||||
|
- Ne pas versionner de secrets ; fournir les secrets locaux via l’environnement.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Serveurs de développement
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lors du démarrage ou de la gestion de la pile locale persistante.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- Vérifier d’abord `portly status` et réutiliser un serveur géré sain, ou créer un projet et un serveur Portly.
|
||||||
|
- Configurer `make run_docker` comme commande du serveur Portly au premier plan sur le port `5001`.
|
||||||
|
- Ne pas démarrer directement les services persistants ni les lancer en arrière-plan.
|
||||||
|
- La documentation de l’API est disponible sur `http://127.0.0.1:5001/docs`.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Git et demandes de fusion
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lors de la préparation d’un commit ou d’une demande de fusion.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- Utiliser un sujet de commit bref à l’impératif, par exemple `Gérer les reprises du worker`.
|
||||||
|
- Décrire le changement, les services touchés, les vérifications et le ticket associé s’il existe.
|
||||||
|
- Ajouter des captures uniquement si un tableau de bord change.
|
||||||
|
- Le dépôt ne définit pas de convention de commit ni de modèle de demande de fusion.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Structure de l’API
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lorsqu’il faut s’orienter dans l’API ou modifier ses routes, utilitaires ou tests.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- `booksync-api/booksync-api/` contient l’API FastAPI, ses routes (`app/api/`), ses utilitaires partagés (`app/core/`) et ses tests (`tests/`).
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Structure du worker
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lorsqu’il faut s’orienter dans le worker ou modifier ses tâches.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- `booksync-worker/booksync-worker/` contient les tâches Celery et leur configuration.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Structure du dépôt
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lorsqu’il faut s’orienter dans la structure générale du dépôt.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- `docker-compose.yml` définit les services ; `_docker/` contient leurs configurations.
|
||||||
|
- Centraliser toute la documentation du projet dans `_docs/`, triée par dossier concerné (par exemple `_docs/app/`, `_docs/api/`, `_docs/worker/` et `_docs/libs/`).
|
||||||
|
- Pour la structure détaillée de chaque application, consulter les règles API et worker liées dans `AGENTS.md`.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Tests
|
||||||
|
|
||||||
|
## Quand cette règle s’applique
|
||||||
|
- Lors de l’ajout ou de la modification du comportement de l’API ou des workers.
|
||||||
|
|
||||||
|
## Règles
|
||||||
|
- Ajouter des tests ciblés pour l’API et les workers.
|
||||||
|
- Lancer les tests API avec `python -m pytest booksync-api/booksync-api/tests`.
|
||||||
|
- Pytest doit être installé ; le dépôt contient une fixture, mais aucun test, et ne déclare pas pytest dans ses dépendances.
|
||||||
|
- Aucun seuil de couverture n’est défini.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.git
|
||||||
|
.idea
|
||||||
|
.venv
|
||||||
|
**/.venv
|
||||||
|
_data
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
**/__pycache__
|
||||||
|
*.py[cod]
|
||||||
|
**/.pytest_cache
|
||||||
|
**/.mypy_cache
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
_dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
*.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# Pycharm
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
_data/
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"python.defaultInterpreterPath": "${workspaceFolder}/.venv",
|
||||||
|
"python.analysis.enableEditableInstalls": true,
|
||||||
|
"python.analysis.extraPaths": [
|
||||||
|
"${workspaceFolder}/libs/mangacollec/src"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
groups:
|
||||||
|
|
||||||
|
- name: Alertes monitoring
|
||||||
|
rules:
|
||||||
|
- alert: NodeHasSwapIn
|
||||||
|
expr: irate(node_vmstat_pswpin{job="prod"}[5m]) > 0
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Node has swap in (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> La VM SWAP (in) LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: NodeHasSwapOut
|
||||||
|
expr: irate(node_vmstat_pswpin{job="prod"}[5m]) > 0
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Node has swap out (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> La VM SWAP (out) LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: OutOfInodes
|
||||||
|
expr: node_filesystem_files_free{mountpoint ="/rootfs"} / node_filesystem_files{mountpoint ="/rootfs"} * 100 < 10
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Out of inodes (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> Le disque n'a presque plus d'inode (< 10% restant) VALUE = {{ humanize $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
|
||||||
|
- alert: OutOfMemory
|
||||||
|
expr: (node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes) / node_memory_MemTotal_bytes * 100 < 10
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Out of memory (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> L'hôte n'a presque plus de RAM (< 10% restant) VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
|
||||||
|
- alert: UnusualNetworkThroughputIn
|
||||||
|
expr: sum by (instance) (irate(node_network_receive_bytes_total[2m])) / 1024 / 1024 > 100
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Unusual network throughput in (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> Les interfaces réseau reçoivent sans doute trop de données (> 100 MB/s) VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: UnusualNetworkThroughputOut
|
||||||
|
expr: sum by (instance) (irate(node_network_transmit_bytes_total[2m])) / 1024 / 1024 > 100
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Unusual network throughput out (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> Les interfaces réseau envoient sans doute trop de données (> 100 MB/s) VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: UnusualDiskReadRate
|
||||||
|
expr: sum by (instance) (irate(node_disk_read_bytes_total[2m])) / 1024 / 1024 > 50
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Unusual disk read rate (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> Le disque lit sans doute trop de données (> 50 MB/s) VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: UnusualDiskWriteRate
|
||||||
|
expr: sum by (instance) (irate(node_disk_written_bytes_total[2m])) / 1024 / 1024 > 50
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Unusual disk write rate (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> Le disque écrit sans doute trop de données (> 50 MB/s) VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: OutOfDiskSpace
|
||||||
|
expr: node_filesystem_free_bytes{mountpoint ="/"} / node_filesystem_size_bytes{mountpoint ="/"} * 100 < 20
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Out of disk space (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> L'espace disque est presque complet (< 20% restant) VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: UnusualDiskReadLatency
|
||||||
|
expr: rate(node_disk_read_time_seconds_total[1m]) / rate(node_disk_reads_completed_total[1m]) > 100
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Unusual disk read latency (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> La latence du disque augmente (read operations > 100ms) VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: CpuLoad
|
||||||
|
expr: node_load15 / (count without (cpu, mode) (node_cpu_seconds_total{mode="system"})) > 2
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "CPU load (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> La charge CPU (15m) est élevée VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: ContextSwitching
|
||||||
|
expr: rate(node_context_switches_total[5m]) > ((count without (cpu, mode) (node_cpu_seconds_total{mode="system"})) * 1500)
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Context switching (instance {{ $labels.instance }})"
|
||||||
|
description: "<!channel> Le contexte switch augement sur l'hôte VALUE = {{ $value }} LABELS: {{ $labels.job }} : {{ $labels.instance }}"
|
||||||
|
|
||||||
|
- alert: Health endpoint down
|
||||||
|
expr: probe_success == 0
|
||||||
|
for: 10s
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "Health endpoint is down"
|
||||||
|
description: "<!channel> L'URL ne répond pas : {{ $labels.addresse }}"
|
||||||
|
|
||||||
|
- alert: Certificate exprire
|
||||||
|
expr: probe_ssl_earliest_cert_expiry{job="blackbox"} - time() < 86400 * 30
|
||||||
|
for: 10m
|
||||||
|
labels:
|
||||||
|
severity: critique
|
||||||
|
annotations:
|
||||||
|
identifier: "{{ $labels.instance }}"
|
||||||
|
summary: "SSL certificate expire"
|
||||||
|
description: "<!channel> Le certificat SSL exprire dans 3 mois {{ $labels.addresse }}"
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
global:
|
||||||
|
resolve_timeout: 5m
|
||||||
|
|
||||||
|
route:
|
||||||
|
group_by: ['...']
|
||||||
|
receiver: slack_general # <-- Si une alerte est soulevée, l'envoyée à slack
|
||||||
|
|
||||||
|
receivers:
|
||||||
|
- name: slack_general
|
||||||
|
slack_configs:
|
||||||
|
- api_url: https://hooks.slack.com/services/<VOTRE WEBHOOK SLACK> # <-- Pensez à mettre votre URL de webhook slack ici
|
||||||
|
channel: alert-prod # <-- Penser à mettre le bon channel ici
|
||||||
|
icon_emoji: ":rotating_light:" # <-- L'emoji de votre choix pour s'afficher dans le channel slack
|
||||||
|
title_link: "http://127.0.0.1:9090/alerts" # <-- Ici on met l'url d'alertmanage pour reprendre le nom des alertes sur le /alerts
|
||||||
|
send_resolved: true # <-- Ce booléen nous permet d'envoyer une seconde alerte quand elle est résolue
|
||||||
|
title: '{{ template "custom_title" . }}' # <-- Reprise du template
|
||||||
|
text: '{{ template "custom_slack_message" . }}' # <-- Reprise du template
|
||||||
|
|
||||||
|
templates:
|
||||||
|
- '/etc/alertmanager/templates/default.tmpl' # <-- Template en go pour l'alerte slack, vous pouvez à peu près tout faire
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{{ define "custom_title" }}
|
||||||
|
{{ range .Alerts }}
|
||||||
|
{{ if eq .Status "firing" }}:broken_heart:{{else}}:green_heart:{{ end }} [{{ .Annotations.identifier }}] {{.Annotations.summary }}
|
||||||
|
{{end}}
|
||||||
|
{{ end }}
|
||||||
|
{{ define "custom_slack_message" }}
|
||||||
|
{{ range .Alerts }}
|
||||||
|
{{ .Annotations.description }}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
providers:
|
||||||
|
|
||||||
|
- name: 'Monitoring'
|
||||||
|
orgId: 1
|
||||||
|
folder: ''
|
||||||
|
type: file
|
||||||
|
disableDeletion: false
|
||||||
|
updateIntervalSeconds: 10 #how often Grafana will scan for changed dashboards
|
||||||
|
options:
|
||||||
|
path: /var/lib/grafana/dashboards
|
||||||
@@ -0,0 +1,770 @@
|
|||||||
|
{
|
||||||
|
"__inputs": [
|
||||||
|
{
|
||||||
|
"name": "DS_PROMETHEUS",
|
||||||
|
"label": "DS_PROMETHEUS",
|
||||||
|
"description": "",
|
||||||
|
"type": "datasource",
|
||||||
|
"pluginId": "prometheus",
|
||||||
|
"pluginName": "Prometheus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"__requires": [
|
||||||
|
{
|
||||||
|
"type": "grafana",
|
||||||
|
"id": "grafana",
|
||||||
|
"name": "Grafana",
|
||||||
|
"version": "7.5.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "panel",
|
||||||
|
"id": "graph",
|
||||||
|
"name": "Graph",
|
||||||
|
"version": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "datasource",
|
||||||
|
"id": "prometheus",
|
||||||
|
"name": "Prometheus",
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "Basic celery monitoring example",
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": null,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"description": "This panel shows status of celery workers. 1 = online, 0 = offline.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 4,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.2",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "flower_worker_online",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{ worker }}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Celery Worker Status",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:150",
|
||||||
|
"format": "short",
|
||||||
|
"label": "",
|
||||||
|
"logBase": 1,
|
||||||
|
"max": "1",
|
||||||
|
"min": "0",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:151",
|
||||||
|
"decimals": null,
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"description": "This panel shows number of tasks currently executing at worker.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 9,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": true,
|
||||||
|
"avg": true,
|
||||||
|
"current": true,
|
||||||
|
"max": true,
|
||||||
|
"min": true,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": true
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.2",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "flower_worker_number_of_currently_executing_tasks",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{worker}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Number of Tasks Currently Executing at Worker",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:79",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:80",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"description": "This panel shows average task runtime at worker by worker and task name.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 11,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": true,
|
||||||
|
"avg": true,
|
||||||
|
"current": true,
|
||||||
|
"max": true,
|
||||||
|
"min": true,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": true
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.2",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "rate(flower_task_runtime_seconds_sum[5m]) / rate(flower_task_runtime_seconds_count[5m])",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{task}}, {{worker}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Average Task Runtime at Worker",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:337",
|
||||||
|
"format": "s",
|
||||||
|
"label": "",
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:338",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"description": "This panel shows task prefetch time at worker by worker and task name.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 17
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 12,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": true,
|
||||||
|
"avg": true,
|
||||||
|
"current": true,
|
||||||
|
"max": true,
|
||||||
|
"min": true,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": true
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.2",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "flower_task_prefetch_time_seconds",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{task}}, {{worker}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Task Prefetch Time at Worker",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:337",
|
||||||
|
"format": "s",
|
||||||
|
"label": "",
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:338",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"description": "This panel shows number of tasks prefetched at worker by task and worker name.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 26
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 10,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": true,
|
||||||
|
"avg": true,
|
||||||
|
"current": true,
|
||||||
|
"max": true,
|
||||||
|
"min": true,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": true
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.2",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "flower_worker_prefetched_tasks",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{task}}, {{worker}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Number of Tasks Prefetched At Worker",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:337",
|
||||||
|
"format": "short",
|
||||||
|
"label": "",
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:338",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"description": "This panel presents average task success ratio over time by task name.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 7,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 35
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 2,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": true,
|
||||||
|
"avg": false,
|
||||||
|
"current": true,
|
||||||
|
"max": true,
|
||||||
|
"min": true,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": true
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.2",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "(sum(avg_over_time(flower_events_total{type=\"task-succeeded\"}[15m])) by (task) / sum(avg_over_time(flower_events_total{type=~\"task-failed|task-succeeded\"}[15m])) by (task)) * 100",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{ task }}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Task Success Ratio",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:63",
|
||||||
|
"format": "percent",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:64",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"description": "This panel presents average task failure ratio over time by task name.",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 7,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 35
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 7,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": true,
|
||||||
|
"avg": false,
|
||||||
|
"current": true,
|
||||||
|
"max": true,
|
||||||
|
"min": true,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": true
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.2",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "(sum(avg_over_time(flower_events_total{type=\"task-failed\"}[15m])) by (task) / sum(avg_over_time(flower_events_total{type=~\"task-failed|task-succeeded\"}[15m])) by (task)) * 100",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{ task }}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Task Failure Ratio",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:63",
|
||||||
|
"format": "percent",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:64",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": "10s",
|
||||||
|
"schemaVersion": 27,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [
|
||||||
|
"celery",
|
||||||
|
"monitoring",
|
||||||
|
"flower"
|
||||||
|
],
|
||||||
|
"templating": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"hide": 0,
|
||||||
|
"label": "datasource",
|
||||||
|
"name": "DS_PROMETHEUS",
|
||||||
|
"options": [],
|
||||||
|
"query": "prometheus",
|
||||||
|
"refresh": 1,
|
||||||
|
"regex": "",
|
||||||
|
"type": "datasource"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-15m",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Flower",
|
||||||
|
"uid": "3OBI1flGz",
|
||||||
|
"version": 9
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,50 @@
|
|||||||
|
# config file version
|
||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
# list of datasources that should be deleted from the database
|
||||||
|
deleteDatasources:
|
||||||
|
- name: Prometheus
|
||||||
|
orgId: 1
|
||||||
|
|
||||||
|
# list of datasources to insert/update depending
|
||||||
|
# whats available in the database
|
||||||
|
datasources:
|
||||||
|
# <string, required> name of the datasource. Required
|
||||||
|
- name: Prometheus
|
||||||
|
# <string, required> datasource type. Required
|
||||||
|
type: prometheus
|
||||||
|
# <string, required> access mode. direct or proxy. Required
|
||||||
|
access: proxy
|
||||||
|
# <int> org id. will default to orgId 1 if not specified
|
||||||
|
orgId: 1
|
||||||
|
# <string> url
|
||||||
|
url: http://prometheus:9090
|
||||||
|
# <string> database password, if used
|
||||||
|
password:
|
||||||
|
# <string> database user, if used
|
||||||
|
user:
|
||||||
|
# <string> database name, if used
|
||||||
|
database:
|
||||||
|
# <bool> enable/disable basic auth
|
||||||
|
basicAuth: false
|
||||||
|
# <string> basic auth username, if used
|
||||||
|
basicAuthUser:
|
||||||
|
# <string> basic auth password, if used
|
||||||
|
basicAuthPassword:
|
||||||
|
# <bool> enable/disable with credentials headers
|
||||||
|
withCredentials:
|
||||||
|
# <bool> mark as default datasource. Max one per org
|
||||||
|
isDefault: true
|
||||||
|
# <map> fields that will be converted to json and stored in json_data
|
||||||
|
jsonData:
|
||||||
|
graphiteVersion: "1.1"
|
||||||
|
tlsAuth: false
|
||||||
|
tlsAuthWithCACert: false
|
||||||
|
# <string> json object of data that will be encrypted.
|
||||||
|
secureJsonData:
|
||||||
|
tlsCACert: "..."
|
||||||
|
tlsClientCert: "..."
|
||||||
|
tlsClientKey: "..."
|
||||||
|
version: 1
|
||||||
|
# <bool> allow users to edit datasources from the UI.
|
||||||
|
editable: true
|
||||||
@@ -0,0 +1,486 @@
|
|||||||
|
##################### Grafana Configuration Example #####################
|
||||||
|
#
|
||||||
|
# Everything has defaults so you only need to uncomment things you want to
|
||||||
|
# change
|
||||||
|
|
||||||
|
# possible values : production, development
|
||||||
|
;app_mode = production
|
||||||
|
|
||||||
|
# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
|
||||||
|
;instance_name = ${HOSTNAME}
|
||||||
|
|
||||||
|
#################################### Paths ####################################
|
||||||
|
[paths]
|
||||||
|
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
|
||||||
|
;data = /var/lib/grafana
|
||||||
|
|
||||||
|
# Temporary files in `data` directory older than given duration will be removed
|
||||||
|
;temp_data_lifetime = 24h
|
||||||
|
|
||||||
|
# Directory where grafana can store logs
|
||||||
|
;logs = /var/log/grafana
|
||||||
|
|
||||||
|
# Directory where grafana will automatically scan and look for plugins
|
||||||
|
;plugins = /var/lib/grafana/plugins
|
||||||
|
|
||||||
|
# folder that contains provisioning config files that grafana will apply on startup and while running.
|
||||||
|
;provisioning = /etc/grafana/provisioning
|
||||||
|
|
||||||
|
#################################### Server ####################################
|
||||||
|
[server]
|
||||||
|
# Protocol (http, https, socket)
|
||||||
|
;protocol = http
|
||||||
|
|
||||||
|
# The ip address to bind to, empty will bind to all interfaces
|
||||||
|
;http_addr =
|
||||||
|
|
||||||
|
# The http port to use
|
||||||
|
;http_port = 3000
|
||||||
|
|
||||||
|
# The public facing domain name used to access grafana from a browser
|
||||||
|
;domain =
|
||||||
|
|
||||||
|
# Redirect to correct domain if host header does not match domain
|
||||||
|
# Prevents DNS rebinding attacks
|
||||||
|
;enforce_domain = false
|
||||||
|
|
||||||
|
# The full public facing url you use in browser, used for redirects and emails
|
||||||
|
# If you use reverse proxy and sub path specify full url (with sub path)
|
||||||
|
;root_url =
|
||||||
|
|
||||||
|
# Log web requests
|
||||||
|
;router_logging = false
|
||||||
|
|
||||||
|
# the path relative working path
|
||||||
|
;static_root_path = public
|
||||||
|
|
||||||
|
# enable gzip
|
||||||
|
;enable_gzip = false
|
||||||
|
|
||||||
|
# https certs & key file
|
||||||
|
;cert_file =
|
||||||
|
;cert_key =
|
||||||
|
|
||||||
|
# Unix socket path
|
||||||
|
;socket =
|
||||||
|
|
||||||
|
#################################### Database ####################################
|
||||||
|
[database]
|
||||||
|
# You can configure the database connection by specifying type, host, name, user and password
|
||||||
|
# as separate properties or as on string using the url properties.
|
||||||
|
|
||||||
|
# Either "mysql", "postgres" or "sqlite3", it's your choice
|
||||||
|
;type = sqlite3
|
||||||
|
;host = 127.0.0.1:3306
|
||||||
|
;name = grafana
|
||||||
|
;user = root
|
||||||
|
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
|
||||||
|
;password =
|
||||||
|
|
||||||
|
# Use either URL or the previous fields to configure the database
|
||||||
|
# Example: mysql://user:secret@host:port/database
|
||||||
|
;url =
|
||||||
|
|
||||||
|
# For "postgres" only, either "disable", "require" or "verify-full"
|
||||||
|
;ssl_mode = disable
|
||||||
|
|
||||||
|
# For "sqlite3" only, path relative to data_path setting
|
||||||
|
;path = grafana.db
|
||||||
|
|
||||||
|
# Max idle conn setting default is 2
|
||||||
|
;max_idle_conn = 2
|
||||||
|
|
||||||
|
# Max conn setting default is 0 (mean not set)
|
||||||
|
;max_open_conn =
|
||||||
|
|
||||||
|
# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
|
||||||
|
;conn_max_lifetime = 14400
|
||||||
|
|
||||||
|
# Set to true to log the sql calls and execution times.
|
||||||
|
log_queries =
|
||||||
|
|
||||||
|
#################################### Session ####################################
|
||||||
|
[session]
|
||||||
|
# Either "memory", "file", "redis", "mysql", "postgres", default is "file"
|
||||||
|
;provider = file
|
||||||
|
|
||||||
|
# Provider config options
|
||||||
|
# memory: not have any config yet
|
||||||
|
# file: session dir path, is relative to grafana data_path
|
||||||
|
# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=grafana`
|
||||||
|
# mysql: go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name`
|
||||||
|
# postgres: user=a password=b host=localhost port=5432 dbname=c sslmode=disable
|
||||||
|
;provider_config = sessions
|
||||||
|
|
||||||
|
# Session cookie name
|
||||||
|
;cookie_name = grafana_sess
|
||||||
|
|
||||||
|
# If you use session in https only, default is false
|
||||||
|
;cookie_secure = false
|
||||||
|
|
||||||
|
# Session life time, default is 86400
|
||||||
|
;session_life_time = 86400
|
||||||
|
|
||||||
|
#################################### Data proxy ###########################
|
||||||
|
[dataproxy]
|
||||||
|
|
||||||
|
# This enables data proxy logging, default is false
|
||||||
|
;logging = false
|
||||||
|
|
||||||
|
#################################### Analytics ####################################
|
||||||
|
[analytics]
|
||||||
|
# Server reporting, sends usage counters to stats.grafana.org every 24 hours.
|
||||||
|
# No ip addresses are being tracked, only simple counters to track
|
||||||
|
# running instances, dashboard and error counts. It is very helpful to us.
|
||||||
|
# Change this option to false to disable reporting.
|
||||||
|
;reporting_enabled = true
|
||||||
|
|
||||||
|
# Set to false to disable all checks to https://grafana.net
|
||||||
|
# for new vesions (grafana itself and plugins), check is used
|
||||||
|
# in some UI views to notify that grafana or plugin update exists
|
||||||
|
# This option does not cause any auto updates, nor send any information
|
||||||
|
# only a GET request to http://grafana.com to get latest versions
|
||||||
|
;check_for_updates = true
|
||||||
|
|
||||||
|
# Google Analytics universal tracking code, only enabled if you specify an id here
|
||||||
|
;google_analytics_ua_id =
|
||||||
|
|
||||||
|
#################################### Security ####################################
|
||||||
|
[security]
|
||||||
|
# default admin user, created on startup
|
||||||
|
admin_user = admin
|
||||||
|
|
||||||
|
# default admin password, can be changed before first start of grafana, or in profile settings
|
||||||
|
admin_password = admin
|
||||||
|
|
||||||
|
# used for signing
|
||||||
|
;secret_key = SW2YcwTIb9zpOOhoPsMm
|
||||||
|
|
||||||
|
# Auto-login remember days
|
||||||
|
;login_remember_days = 7
|
||||||
|
;cookie_username = grafana_user
|
||||||
|
;cookie_remember_name = grafana_remember
|
||||||
|
|
||||||
|
# disable gravatar profile images
|
||||||
|
;disable_gravatar = false
|
||||||
|
|
||||||
|
# data source proxy whitelist (ip_or_domain:port separated by spaces)
|
||||||
|
;data_source_proxy_whitelist =
|
||||||
|
|
||||||
|
# disable protection against brute force login attempts
|
||||||
|
;disable_brute_force_login_protection = false
|
||||||
|
|
||||||
|
#################################### Snapshots ###########################
|
||||||
|
[snapshots]
|
||||||
|
# snapshot sharing options
|
||||||
|
;external_enabled = true
|
||||||
|
;external_snapshot_url = https://snapshots-origin.raintank.io
|
||||||
|
;external_snapshot_name = Publish to snapshot.raintank.io
|
||||||
|
|
||||||
|
# remove expired snapshot
|
||||||
|
;snapshot_remove_expired = true
|
||||||
|
|
||||||
|
#################################### Dashboards History ##################
|
||||||
|
[dashboards]
|
||||||
|
# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1
|
||||||
|
;versions_to_keep = 20
|
||||||
|
|
||||||
|
#################################### Users ###############################
|
||||||
|
[users]
|
||||||
|
# disable user signup / registration
|
||||||
|
;allow_sign_up = true
|
||||||
|
|
||||||
|
# Allow non admin users to create organizations
|
||||||
|
;allow_org_create = true
|
||||||
|
|
||||||
|
# Set to true to automatically assign new users to the default organization (id 1)
|
||||||
|
;auto_assign_org = true
|
||||||
|
|
||||||
|
# Default role new users will be automatically assigned (if disabled above is set to true)
|
||||||
|
;auto_assign_org_role = Viewer
|
||||||
|
|
||||||
|
# Background text for the user field on the login page
|
||||||
|
;login_hint = email or username
|
||||||
|
|
||||||
|
# Default UI theme ("dark" or "light")
|
||||||
|
;default_theme = dark
|
||||||
|
|
||||||
|
# External user management, these options affect the organization users view
|
||||||
|
;external_manage_link_url =
|
||||||
|
;external_manage_link_name =
|
||||||
|
;external_manage_info =
|
||||||
|
|
||||||
|
# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
|
||||||
|
;viewers_can_edit = false
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false
|
||||||
|
;disable_login_form = false
|
||||||
|
|
||||||
|
# Set to true to disable the signout link in the side menu. useful if you use auth.proxy, defaults to false
|
||||||
|
;disable_signout_menu = false
|
||||||
|
|
||||||
|
# URL to redirect the user to after sign out
|
||||||
|
;signout_redirect_url =
|
||||||
|
|
||||||
|
# Set to true to attempt login with OAuth automatically, skipping the login screen.
|
||||||
|
# This setting is ignored if multiple OAuth providers are configured.
|
||||||
|
;oauth_auto_login = false
|
||||||
|
|
||||||
|
#################################### Anonymous Auth ##########################
|
||||||
|
[auth.anonymous]
|
||||||
|
# enable anonymous access
|
||||||
|
;enabled = false
|
||||||
|
|
||||||
|
# specify organization name that should be used for unauthenticated users
|
||||||
|
;org_name = Main Org.
|
||||||
|
|
||||||
|
# specify role for unauthenticated users
|
||||||
|
;org_role = Viewer
|
||||||
|
|
||||||
|
#################################### Github Auth ##########################
|
||||||
|
[auth.github]
|
||||||
|
;enabled = false
|
||||||
|
;allow_sign_up = true
|
||||||
|
;client_id = some_id
|
||||||
|
;client_secret = some_secret
|
||||||
|
;scopes = user:email,read:org
|
||||||
|
;auth_url = https://github.com/login/oauth/authorize
|
||||||
|
;token_url = https://github.com/login/oauth/access_token
|
||||||
|
;api_url = https://api.github.com/user
|
||||||
|
;team_ids =
|
||||||
|
;allowed_organizations =
|
||||||
|
|
||||||
|
#################################### Google Auth ##########################
|
||||||
|
[auth.google]
|
||||||
|
# enabled = true
|
||||||
|
# allow_sign_up = true
|
||||||
|
# client_id =
|
||||||
|
# client_secret =
|
||||||
|
# scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
|
||||||
|
# auth_url = https://accounts.google.com/o/oauth2/auth
|
||||||
|
# token_url = https://accounts.google.com/o/oauth2/token
|
||||||
|
# api_url = https://www.googleapis.com/oauth2/v1/userinfo
|
||||||
|
;allowed_domains =
|
||||||
|
|
||||||
|
#################################### Generic OAuth ##########################
|
||||||
|
[auth.generic_oauth]
|
||||||
|
;enabled = false
|
||||||
|
;name = OAuth
|
||||||
|
;allow_sign_up = true
|
||||||
|
;client_id = some_id
|
||||||
|
;client_secret = some_secret
|
||||||
|
;scopes = user:email,read:org
|
||||||
|
;auth_url = https://foo.bar/login/oauth/authorize
|
||||||
|
;token_url = https://foo.bar/login/oauth/access_token
|
||||||
|
;api_url = https://foo.bar/user
|
||||||
|
;team_ids =
|
||||||
|
;allowed_organizations =
|
||||||
|
;tls_skip_verify_insecure = false
|
||||||
|
;tls_client_cert =
|
||||||
|
;tls_client_key =
|
||||||
|
;tls_client_ca =
|
||||||
|
|
||||||
|
#################################### Grafana.com Auth ####################
|
||||||
|
[auth.grafana_com]
|
||||||
|
;enabled = false
|
||||||
|
;allow_sign_up = true
|
||||||
|
;client_id = some_id
|
||||||
|
;client_secret = some_secret
|
||||||
|
;scopes = user:email
|
||||||
|
;allowed_organizations =
|
||||||
|
|
||||||
|
#################################### Auth Proxy ##########################
|
||||||
|
[auth.proxy]
|
||||||
|
;enabled = false
|
||||||
|
;header_name = X-WEBAUTH-USER
|
||||||
|
;header_property = username
|
||||||
|
;auto_sign_up = true
|
||||||
|
;ldap_sync_ttl = 60
|
||||||
|
;whitelist = 192.168.1.1, 192.168.2.1
|
||||||
|
;headers = Email:X-User-Email, Name:X-User-Name
|
||||||
|
|
||||||
|
#################################### Basic Auth ##########################
|
||||||
|
[auth.basic]
|
||||||
|
;enabled = true
|
||||||
|
|
||||||
|
#################################### Auth LDAP ##########################
|
||||||
|
[auth.ldap]
|
||||||
|
;enabled = false
|
||||||
|
;config_file = /etc/grafana/ldap.toml
|
||||||
|
;allow_sign_up = true
|
||||||
|
|
||||||
|
#################################### SMTP / Emailing ##########################
|
||||||
|
[smtp]
|
||||||
|
;enabled = true
|
||||||
|
;host =
|
||||||
|
;user =
|
||||||
|
# If the password contains # or ; you have to wrap it with trippel quotes. Ex """#password;"""
|
||||||
|
;password = ""
|
||||||
|
;cert_file =
|
||||||
|
;key_file =
|
||||||
|
;skip_verify = true
|
||||||
|
;from_address =
|
||||||
|
;from_name =
|
||||||
|
# EHLO identity in SMTP dialog (defaults to instance_name)
|
||||||
|
;ehlo_identity = dashboard.example.com
|
||||||
|
|
||||||
|
[emails]
|
||||||
|
;welcome_email_on_sign_up = false
|
||||||
|
|
||||||
|
#################################### Logging ##########################
|
||||||
|
[log]
|
||||||
|
# Either "console", "file", "syslog". Default is console and file
|
||||||
|
# Use space to separate multiple modes, e.g. "console file"
|
||||||
|
;mode = console file
|
||||||
|
|
||||||
|
# Either "debug", "info", "warn", "error", "critical", default is "info"
|
||||||
|
;level = info
|
||||||
|
|
||||||
|
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
|
||||||
|
;filters =
|
||||||
|
|
||||||
|
# For "console" mode only
|
||||||
|
[log.console]
|
||||||
|
;level =
|
||||||
|
|
||||||
|
# log line format, valid options are text, console and json
|
||||||
|
;format = console
|
||||||
|
|
||||||
|
# For "file" mode only
|
||||||
|
[log.file]
|
||||||
|
;level =
|
||||||
|
|
||||||
|
# log line format, valid options are text, console and json
|
||||||
|
;format = text
|
||||||
|
|
||||||
|
# This enables automated log rotate(switch of following options), default is true
|
||||||
|
;log_rotate = true
|
||||||
|
|
||||||
|
# Max line number of single file, default is 1000000
|
||||||
|
;max_lines = 1000000
|
||||||
|
|
||||||
|
# Max size shift of single file, default is 28 means 1 << 28, 256MB
|
||||||
|
;max_size_shift = 28
|
||||||
|
|
||||||
|
# Segment log daily, default is true
|
||||||
|
;daily_rotate = true
|
||||||
|
|
||||||
|
# Expired days of log file(delete after max days), default is 7
|
||||||
|
;max_days = 7
|
||||||
|
|
||||||
|
[log.syslog]
|
||||||
|
;level =
|
||||||
|
|
||||||
|
# log line format, valid options are text, console and json
|
||||||
|
;format = text
|
||||||
|
|
||||||
|
# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used.
|
||||||
|
;network =
|
||||||
|
;address =
|
||||||
|
|
||||||
|
# Syslog facility. user, daemon and local0 through local7 are valid.
|
||||||
|
;facility =
|
||||||
|
|
||||||
|
# Syslog tag. By default, the process' argv[0] is used.
|
||||||
|
;tag =
|
||||||
|
|
||||||
|
#################################### Alerting ############################
|
||||||
|
[alerting]
|
||||||
|
# Disable alerting engine & UI features
|
||||||
|
;enabled = true
|
||||||
|
# Makes it possible to turn off alert rule execution but alerting UI is visible
|
||||||
|
;execute_alerts = true
|
||||||
|
|
||||||
|
# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state)
|
||||||
|
;error_or_timeout = alerting
|
||||||
|
|
||||||
|
# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok)
|
||||||
|
;nodata_or_nullvalues = no_data
|
||||||
|
|
||||||
|
# Alert notifications can include images, but rendering many images at the same time can overload the server
|
||||||
|
# This limit will protect the server from render overloading and make sure notifications are sent out quickly
|
||||||
|
;concurrent_render_limit = 5
|
||||||
|
|
||||||
|
#################################### Explore #############################
|
||||||
|
[explore]
|
||||||
|
# Enable the Explore section
|
||||||
|
;enabled = false
|
||||||
|
|
||||||
|
#################################### Internal Grafana Metrics ##########################
|
||||||
|
# Metrics available at HTTP API Url /metrics
|
||||||
|
[metrics]
|
||||||
|
# Disable / Enable internal metrics
|
||||||
|
;enabled = true
|
||||||
|
|
||||||
|
# Publish interval
|
||||||
|
;interval_seconds = 10
|
||||||
|
|
||||||
|
# Send internal metrics to Graphite
|
||||||
|
[metrics.graphite]
|
||||||
|
# Enable by setting the address setting (ex localhost:2003)
|
||||||
|
;address =
|
||||||
|
;prefix = prod.grafana.%(instance_name)s.
|
||||||
|
|
||||||
|
#################################### Distributed tracing ############
|
||||||
|
[tracing.jaeger]
|
||||||
|
# Enable by setting the address sending traces to jaeger (ex localhost:6831)
|
||||||
|
;address = localhost:6831
|
||||||
|
# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
|
||||||
|
;always_included_tag = tag1:value1
|
||||||
|
# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
|
||||||
|
;sampler_type = const
|
||||||
|
# jaeger samplerconfig param
|
||||||
|
# for "const" sampler, 0 or 1 for always false/true respectively
|
||||||
|
# for "probabilistic" sampler, a probability between 0 and 1
|
||||||
|
# for "rateLimiting" sampler, the number of spans per second
|
||||||
|
# for "remote" sampler, param is the same as for "probabilistic"
|
||||||
|
# and indicates the initial sampling rate before the actual one
|
||||||
|
# is received from the mothership
|
||||||
|
;sampler_param = 1
|
||||||
|
|
||||||
|
#################################### Grafana.com integration ##########################
|
||||||
|
# Url used to import dashboards directly from Grafana.com
|
||||||
|
[grafana_com]
|
||||||
|
;url = https://grafana.com
|
||||||
|
|
||||||
|
#################################### External image storage ##########################
|
||||||
|
[external_image_storage]
|
||||||
|
# Used for uploading images to public servers so they can be included in slack/email messages.
|
||||||
|
# you can choose between (s3, webdav, gcs, azure_blob, local)
|
||||||
|
;provider =
|
||||||
|
|
||||||
|
[external_image_storage.s3]
|
||||||
|
;bucket =
|
||||||
|
;region =
|
||||||
|
;path =
|
||||||
|
;access_key =
|
||||||
|
;secret_key =
|
||||||
|
|
||||||
|
[external_image_storage.webdav]
|
||||||
|
;url =
|
||||||
|
;public_url =
|
||||||
|
;username =
|
||||||
|
;password =
|
||||||
|
|
||||||
|
[external_image_storage.gcs]
|
||||||
|
;key_file =
|
||||||
|
;bucket =
|
||||||
|
;path =
|
||||||
|
|
||||||
|
[external_image_storage.azure_blob]
|
||||||
|
;account_name =
|
||||||
|
;account_key =
|
||||||
|
;container_name =
|
||||||
|
|
||||||
|
[external_image_storage.local]
|
||||||
|
# does not require any configuration
|
||||||
|
|
||||||
|
[rendering]
|
||||||
|
# Options to configure external image rendering server like https://github.com/grafana/grafana-image-renderer
|
||||||
|
;server_url =
|
||||||
|
;callback_url =
|
||||||
|
|
||||||
|
[enterprise]
|
||||||
|
# Path to a valid Grafana Enterprise license.jwt file
|
||||||
|
;license_path =
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
-- Source unique de creation des tables de la base booksync.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "AUTHORS" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
first_name VARCHAR(255),
|
||||||
|
tasks_count INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "JOBS" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "KINDS" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "PUBLISHERS" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(500) NOT NULL,
|
||||||
|
closed BOOLEAN NOT NULL,
|
||||||
|
editions_count INTEGER NOT NULL,
|
||||||
|
no_amazon BOOLEAN NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "SERIES_TYPES" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
to_display BOOLEAN,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "BOX_EDITIONS" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(500) NOT NULL,
|
||||||
|
publisher_id VARCHAR(36) NOT NULL,
|
||||||
|
boxes_count INTEGER NOT NULL,
|
||||||
|
adult_content BOOLEAN NOT NULL,
|
||||||
|
box_follow_editions_count INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(publisher_id) REFERENCES "PUBLISHERS" (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "SERIES" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(500) NOT NULL,
|
||||||
|
type_id VARCHAR(36),
|
||||||
|
adult_content BOOLEAN,
|
||||||
|
editions_count INTEGER,
|
||||||
|
tasks_count INTEGER,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(type_id) REFERENCES "SERIES_TYPES" (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "BOXES" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(500) NOT NULL,
|
||||||
|
number INTEGER NOT NULL,
|
||||||
|
release_date DATE,
|
||||||
|
isbn VARCHAR(32),
|
||||||
|
asin VARCHAR(32),
|
||||||
|
commercial_stop BOOLEAN NOT NULL,
|
||||||
|
box_edition_id VARCHAR(36) NOT NULL,
|
||||||
|
box_possessions_count INTEGER NOT NULL,
|
||||||
|
image_url TEXT,
|
||||||
|
content TEXT,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(box_edition_id) REFERENCES "BOX_EDITIONS" (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "EDITIONS" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(500),
|
||||||
|
series_id VARCHAR(36) NOT NULL,
|
||||||
|
publisher_id VARCHAR(36) NOT NULL,
|
||||||
|
parent_edition_id VARCHAR(36),
|
||||||
|
volumes_count INTEGER NOT NULL,
|
||||||
|
last_volume_number INTEGER,
|
||||||
|
commercial_stop BOOLEAN NOT NULL,
|
||||||
|
not_finished BOOLEAN NOT NULL,
|
||||||
|
follow_editions_count INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(series_id) REFERENCES "SERIES" (id),
|
||||||
|
FOREIGN KEY(publisher_id) REFERENCES "PUBLISHERS" (id),
|
||||||
|
FOREIGN KEY(parent_edition_id) REFERENCES "EDITIONS" (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "SERIES_KINDS" (
|
||||||
|
series_id VARCHAR(36) NOT NULL,
|
||||||
|
kind_id VARCHAR(36) NOT NULL,
|
||||||
|
PRIMARY KEY (series_id, kind_id),
|
||||||
|
FOREIGN KEY(series_id) REFERENCES "SERIES" (id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY(kind_id) REFERENCES "KINDS" (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "TASKS" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
series_id VARCHAR(36) NOT NULL,
|
||||||
|
author_id VARCHAR(36),
|
||||||
|
job_id VARCHAR(36),
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(series_id) REFERENCES "SERIES" (id),
|
||||||
|
FOREIGN KEY(author_id) REFERENCES "AUTHORS" (id),
|
||||||
|
FOREIGN KEY(job_id) REFERENCES "JOBS" (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "VOLUMES" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
title VARCHAR(500),
|
||||||
|
number INTEGER NOT NULL,
|
||||||
|
release_date DATE,
|
||||||
|
isbn VARCHAR(32),
|
||||||
|
asin VARCHAR(32),
|
||||||
|
edition_id VARCHAR(36) NOT NULL,
|
||||||
|
possessions_count INTEGER NOT NULL,
|
||||||
|
not_sold BOOLEAN NOT NULL,
|
||||||
|
image_url TEXT,
|
||||||
|
nb_pages INTEGER,
|
||||||
|
content TEXT,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(edition_id) REFERENCES "EDITIONS" (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "BOX_VOLUMES" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
box_id VARCHAR(36) NOT NULL,
|
||||||
|
volume_id VARCHAR(36) NOT NULL,
|
||||||
|
included BOOLEAN NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(box_id) REFERENCES "BOXES" (id),
|
||||||
|
FOREIGN KEY(volume_id) REFERENCES "VOLUMES" (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "VOLUME_POSSESSION_HISTORY" (
|
||||||
|
id_volume VARCHAR(36) NOT NULL,
|
||||||
|
observed_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||||
|
possessions_count INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (id_volume, observed_at),
|
||||||
|
FOREIGN KEY(id_volume) REFERENCES "VOLUMES" (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "BOX_POSSESSION_HISTORY" (
|
||||||
|
id_box VARCHAR(36) NOT NULL,
|
||||||
|
observed_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||||
|
box_possessions_count INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (id_box, observed_at),
|
||||||
|
FOREIGN KEY(id_box) REFERENCES "BOXES" (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# my global config
|
||||||
|
global:
|
||||||
|
scrape_interval: 5s # By default, scrape targets every 15 seconds.
|
||||||
|
evaluation_interval: 5s # By default, scrape targets every 15 seconds.
|
||||||
|
# scrape_timeout is set to the global default (10s).
|
||||||
|
|
||||||
|
# Attach these labels to any time series or alerts when communicating with
|
||||||
|
# external systems (federation, remote storage, Alertmanager).
|
||||||
|
external_labels:
|
||||||
|
env: 'infrastructure' # C'est simplement le nom de votre environnement
|
||||||
|
|
||||||
|
# spécification du serveur alertmanager
|
||||||
|
alerting:
|
||||||
|
alertmanagers:
|
||||||
|
- static_configs:
|
||||||
|
- targets:
|
||||||
|
- alertmanager:9093
|
||||||
|
scheme: http
|
||||||
|
timeout: 10s
|
||||||
|
|
||||||
|
# fichier de règles pour l'alerting, c'est dans ce fichier que sont les définitions des alertes
|
||||||
|
rule_files:
|
||||||
|
- '/alertmanager/alert.rules/alerts.rules.yml'
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'prometheus'
|
||||||
|
scrape_interval: 5s
|
||||||
|
static_configs:
|
||||||
|
- targets: ['prometheus:9090']
|
||||||
|
|
||||||
|
- job_name: 'node-exporter'
|
||||||
|
scrape_interval: 5s
|
||||||
|
static_configs:
|
||||||
|
- targets: ['node-exporter:9100']
|
||||||
|
|
||||||
|
- job_name: 'postgres-exporter'
|
||||||
|
scrape_interval: 300s
|
||||||
|
scrape_timeout: 60s
|
||||||
|
static_configs:
|
||||||
|
- targets: ['postgres-exporter:9187']
|
||||||
|
|
||||||
|
- job_name: 'rabbitmq'
|
||||||
|
metrics_path: /metrics/per-object
|
||||||
|
static_configs:
|
||||||
|
- targets: ['rabbitmq:15692']
|
||||||
|
|
||||||
|
- job_name: 'redis-exporter'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['redis-exporter:9121']
|
||||||
|
|
||||||
|
- job_name: 'flower'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['flower:5555']
|
||||||
|
|
||||||
|
- job_name: 'booksync-api'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['booksync-api:5000']
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pydantic>=2,<3
|
||||||
|
pyside6>=6.8,<7
|
||||||
|
qasync>=0.27,<1
|
||||||
|
python-dotenv>=1.2.3
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
name: booksync
|
||||||
|
volumes:
|
||||||
|
prometheus-data:
|
||||||
|
grafana-data:
|
||||||
|
postgres-data:
|
||||||
|
pgadmin-data:
|
||||||
|
flower-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
booksync-front:
|
||||||
|
booksync-back:
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Tableau de bord Plotly Dash : démonstration autonome
|
||||||
|
#
|
||||||
|
dash:
|
||||||
|
image: booksync-dash:latest
|
||||||
|
hostname: booksync-dash
|
||||||
|
ports:
|
||||||
|
- "8050:8050"
|
||||||
|
networks:
|
||||||
|
- booksync-front
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- python
|
||||||
|
- -c
|
||||||
|
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8050/health', timeout=3)"
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Prometheus enregistre des métriques en temps réel dans une base de données de séries temporelles.
|
||||||
|
# Pour enregistrer les métrique, Prometheus se base sur le contenu de point d'entrée exposé à l'aide du protocole HTTP (/metrics).
|
||||||
|
# Son rôle est de collecter les métriques de chaque service.
|
||||||
|
#
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
hostname: prometheus
|
||||||
|
container_name: prometheus
|
||||||
|
volumes:
|
||||||
|
- prometheus-data:/prometheus
|
||||||
|
- ./_docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
|
- ./_docker/alertmanager/alert.rules/alerts.rules.yml:/alertmanager/alert.rules/alerts.rules.yml
|
||||||
|
command:
|
||||||
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||||
|
- '--storage.tsdb.path=/prometheus'
|
||||||
|
- '--storage.tsdb.retention.time=365d'
|
||||||
|
ports:
|
||||||
|
- 9090:9090
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Exporte les métriques de la machine
|
||||||
|
#
|
||||||
|
node_exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
hostname: node-exporter
|
||||||
|
container_name: node-exporter
|
||||||
|
ports:
|
||||||
|
- 9100:9100
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Gestionnaire d'alertes
|
||||||
|
#
|
||||||
|
alertmanager:
|
||||||
|
image: prom/alertmanager:latest
|
||||||
|
hostname: alertmanager
|
||||||
|
container_name: alertmanager
|
||||||
|
volumes:
|
||||||
|
- ./_docker/alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
||||||
|
- ./_docker/alertmanager/templates/default.tmpl:/etc/alertmanager/templates/default.tmpl
|
||||||
|
command:
|
||||||
|
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
||||||
|
ports:
|
||||||
|
- 9093:9093
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Permet la visualisation de données. Il permet de réaliser des tableaux de bord et des graphiques depuis plusieurs sources dont des bases de données temporelles
|
||||||
|
# Monitoring des métriques
|
||||||
|
#
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
hostname: grafana
|
||||||
|
container_name: grafana
|
||||||
|
environment:
|
||||||
|
- GF_PATHS_CONFIG=/etc/grafana/grafana.ini
|
||||||
|
volumes:
|
||||||
|
- grafana-data:/var/lib/grafana
|
||||||
|
- ./_docker/grafana/datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml
|
||||||
|
- ./_docker/grafana/dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml
|
||||||
|
- ./_docker/grafana/grafana.ini:/etc/grafana/grafana.ini
|
||||||
|
- ./_docker/grafana/dashboards:/var/lib/grafana/dashboards
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
depends_on:
|
||||||
|
- prometheus
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Base de donnée relationnelle
|
||||||
|
#
|
||||||
|
postgres:
|
||||||
|
image: pgvector/pgvector:pg17
|
||||||
|
hostname: postgres
|
||||||
|
container_name: postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: postgres
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
- ./_docker/postgres/create_tables.sql:/docker-entrypoint-initdb.d/create_tables.sql
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U", "postgres" ]
|
||||||
|
timeout: 45s
|
||||||
|
interval: 10s
|
||||||
|
retries: 10
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Exporte des métriques de la base de données
|
||||||
|
#
|
||||||
|
postgres-exporter:
|
||||||
|
image: bitnami/postgres-exporter:latest
|
||||||
|
hostname: postgres-exporter
|
||||||
|
container_name: postgres-exporter
|
||||||
|
environment:
|
||||||
|
DATA_SOURCE_URI: postgres:5432/postgres?sslmode=disable
|
||||||
|
DATA_SOURCE_USER: postgres
|
||||||
|
DATA_SOURCE_PASS: postgres
|
||||||
|
ports:
|
||||||
|
- 9187:9187
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Administration de la base de donnée relationnelle
|
||||||
|
#
|
||||||
|
pgadmin:
|
||||||
|
image: dpage/pgadmin4:latest
|
||||||
|
hostname: pgadmin
|
||||||
|
container_name: pgadmin
|
||||||
|
environment:
|
||||||
|
PGADMIN_DEFAULT_EMAIL: admin@domain.com
|
||||||
|
PGADMIN_DEFAULT_PASSWORD: admin
|
||||||
|
PGADMIN_CONFIG_SERVER_MODE: 'False'
|
||||||
|
volumes:
|
||||||
|
- pgadmin-data:/var/lib/pgadmin
|
||||||
|
ports:
|
||||||
|
- 5050:80
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Agent de messages pour celery (broker), il est utilisé comme middleware entre des producers et des workers
|
||||||
|
# Exporte ses propres métriques
|
||||||
|
#
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3-management
|
||||||
|
hostname: rabbitmq
|
||||||
|
container_name: rabbitmq
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "rabbitmq-diagnostics", "-q", "check_port_connectivity"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 15s
|
||||||
|
ports:
|
||||||
|
- 5672:5672
|
||||||
|
- 15672:15672
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Mémoire cache pour celery (result backend), il est utilisé comme middleware entre des producers et des workers
|
||||||
|
#
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
hostname: redis
|
||||||
|
container_name: redis
|
||||||
|
environment:
|
||||||
|
- ALLOW_EMPTY_PASSWORD=yes
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 5s
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Exporteur des métriques redis
|
||||||
|
#
|
||||||
|
redis-exporter:
|
||||||
|
image: bitnami/redis-exporter:latest
|
||||||
|
hostname: redis-exporter
|
||||||
|
container_name: redis-exporter
|
||||||
|
environment:
|
||||||
|
REDIS_ADDR: redis://redis:6379
|
||||||
|
ports:
|
||||||
|
- 9121:9121
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Exporteur des métriques celery & monitoring des workers
|
||||||
|
#
|
||||||
|
flower:
|
||||||
|
image: mher/flower:latest
|
||||||
|
hostname: flower
|
||||||
|
container_name: flower
|
||||||
|
environment:
|
||||||
|
CELERY_BROKER_URL: "pyamqp://guest:guest@rabbitmq:5672//"
|
||||||
|
CELERY_RESULT_BACKEND: "redis://redis:6379/0"
|
||||||
|
FLOWER_BROKER_API: "http://guest:guest@rabbitmq:15672/api/"
|
||||||
|
FLOWER_PORT: "5555"
|
||||||
|
FLOWER_PERSISTENT: "true"
|
||||||
|
ports:
|
||||||
|
- 5555:5555
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
depends_on:
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# API REST basée sur FastAPI
|
||||||
|
# Contiens :
|
||||||
|
# - des requêtes à la base de donnée relationnelle
|
||||||
|
# Producteur de tâches celery
|
||||||
|
#
|
||||||
|
api:
|
||||||
|
image: booksync-api:latest
|
||||||
|
hostname: booksync-api
|
||||||
|
command: [
|
||||||
|
"uvicorn",
|
||||||
|
"api:app",
|
||||||
|
"--host",
|
||||||
|
"0.0.0.0",
|
||||||
|
"--port",
|
||||||
|
"5001",
|
||||||
|
"--workers",
|
||||||
|
"2",
|
||||||
|
"--log-level",
|
||||||
|
"info"]
|
||||||
|
environment:
|
||||||
|
POSTGRES_DATABASE_URL: postgresql+asyncpg://postgres:postgres@postgres/postgres
|
||||||
|
CELERY_BROKER_URL: pyamqp://guest:guest@rabbitmq:5672//
|
||||||
|
CELERY_RESULT_BACKEND_URL: redis://redis:6379/0
|
||||||
|
MANGACOLLEC_ID: 38fee110b53a75af6cc72f6fb66fa504fc6241e566788f4b2f5b21c25ba2fefb
|
||||||
|
MANGACOLLEC_SECRET: 060658630f7d199c19ab1cf34ed4e50935c748267e318a24e048d6ab45871da2
|
||||||
|
ports:
|
||||||
|
- 5001:5001
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
- booksync-front
|
||||||
|
depends_on:
|
||||||
|
- rabbitmq
|
||||||
|
- redis
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Worker basé sur celery
|
||||||
|
# 5 Replicas sont créer
|
||||||
|
#
|
||||||
|
worker:
|
||||||
|
image: booksync-worker:latest
|
||||||
|
deploy:
|
||||||
|
replicas: 5
|
||||||
|
command: [
|
||||||
|
"celery",
|
||||||
|
"-A",
|
||||||
|
"tasks:app",
|
||||||
|
"worker",
|
||||||
|
"--pool",
|
||||||
|
"prefork",
|
||||||
|
"-Q",
|
||||||
|
"booksync-worker",
|
||||||
|
"--concurrency=3",
|
||||||
|
"-n",
|
||||||
|
"booksync-worker@%h",
|
||||||
|
"--loglevel",
|
||||||
|
"INFO"]
|
||||||
|
environment:
|
||||||
|
CELERY_BROKER_URL: pyamqp://guest:guest@rabbitmq:5672//
|
||||||
|
CELERY_RESULT_BACKEND_URL: redis://redis:6379/0
|
||||||
|
MANGACOLLEC_ID: 38fee110b53a75af6cc72f6fb66fa504fc6241e566788f4b2f5b21c25ba2fefb
|
||||||
|
MANGACOLLEC_SECRET: 060658630f7d199c19ab1cf34ed4e50935c748267e318a24e048d6ab45871da2
|
||||||
|
networks:
|
||||||
|
- booksync-back
|
||||||
|
depends_on:
|
||||||
|
flower:
|
||||||
|
condition: service_started
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: always
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "container"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Dependency injection container for the BookSync applications."
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["src"]
|
||||||
|
include = ["container*"]
|
||||||
|
namespaces = true
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
"""Public API for the dependency injection container."""
|
||||||
|
|
||||||
|
from .container import Container
|
||||||
|
from .container_interface import IContainer
|
||||||
|
|
||||||
|
__all__ = ["Container", "IContainer"]
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
||||||
|
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
||||||
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
import importlib
|
||||||
|
import inspect
|
||||||
|
from typing import Any, TypeVar, cast
|
||||||
|
|
||||||
|
from .container_interface import IContainer
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
class Container(IContainer):
|
||||||
|
"""Conteneur léger de services avec résolution par type."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Initialise des registres indépendants pour ce conteneur."""
|
||||||
|
self.__instances: dict[str, Any] = {
|
||||||
|
"container": self,
|
||||||
|
self.get_type(Container): self,
|
||||||
|
}
|
||||||
|
self.__alias: dict[str, Any] = {}
|
||||||
|
self.__parameters: dict[str, Any] = {}
|
||||||
|
self.alias(IContainer, Container)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_type(name: type[Any] | str) -> str:
|
||||||
|
"""Retourner l'identifiant stable d'un type ou d'une chaîne."""
|
||||||
|
if isinstance(name, str):
|
||||||
|
return name
|
||||||
|
module = inspect.getmodule(name)
|
||||||
|
if module is None:
|
||||||
|
raise TypeError(f"Impossible de déterminer le module de {name!r}.")
|
||||||
|
return f"{module.__name__}.{name.__name__}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_instance(type_: str, args: list[Any]) -> object:
|
||||||
|
"""Construire dynamiquement une instance à partir de son identifiant."""
|
||||||
|
module = importlib.import_module(type_[0 : type_.rfind(".")])
|
||||||
|
return getattr(module, type_[type_.rfind(".") + 1 :])(*args)
|
||||||
|
|
||||||
|
def set(self, name: type[Any], obj: object) -> None:
|
||||||
|
"""Enregistrer une instance pour un type."""
|
||||||
|
self.__instances[Container.get_type(name)] = obj
|
||||||
|
|
||||||
|
def __getitem__(self, name: type[T]) -> T:
|
||||||
|
"""Résoudre un service avec la syntaxe d'indexation historique du sandbox."""
|
||||||
|
return cast("T", self.get(name))
|
||||||
|
|
||||||
|
def __setitem__(self, name: type[T], obj: T) -> None:
|
||||||
|
"""Enregistrer un service avec la syntaxe d'indexation historique du sandbox."""
|
||||||
|
self.set(name, obj)
|
||||||
|
|
||||||
|
def __contains__(self, name: type[Any] | str) -> bool:
|
||||||
|
"""Indiquer si un service a déjà été enregistré dans le conteneur."""
|
||||||
|
return self.get_type(name) in self.__instances
|
||||||
|
|
||||||
|
def get(self, name: type[Any] | str) -> Any:
|
||||||
|
"""Résoudre ou construire un service par son type ou son identifiant."""
|
||||||
|
if name in self.__parameters:
|
||||||
|
return self.__parameters[name]
|
||||||
|
|
||||||
|
service_id: str = self.get_type(name)
|
||||||
|
|
||||||
|
if service_id in self.__alias:
|
||||||
|
return self.get(self.__alias[Container.get_type(name)])
|
||||||
|
|
||||||
|
if service_id not in self.__instances:
|
||||||
|
if isinstance(name, str):
|
||||||
|
raise KeyError(f"Service introuvable : {name}.")
|
||||||
|
args: list[Any] = []
|
||||||
|
if len(inspect.signature(name.__init__).parameters) > 1:
|
||||||
|
for arg, parameter in inspect.signature(name.__init__).parameters.items():
|
||||||
|
if arg in ["self", "args", "kwargs"]:
|
||||||
|
continue
|
||||||
|
if parameter.annotation != inspect.Parameter.empty:
|
||||||
|
try:
|
||||||
|
args.append(self.get(parameter.annotation))
|
||||||
|
except (AttributeError, KeyError, TypeError):
|
||||||
|
args.append(self.get(arg))
|
||||||
|
else:
|
||||||
|
args.append(self.get(arg))
|
||||||
|
self.__instances[service_id] = Container.get_instance(service_id, args)
|
||||||
|
|
||||||
|
return self.__instances[service_id]
|
||||||
|
|
||||||
|
def alias(self, alias: type[Any] | str, name: type[Any] | str) -> None:
|
||||||
|
"""Associer un identifiant à un autre service."""
|
||||||
|
self.__alias[Container.get_type(alias)] = name
|
||||||
|
|
||||||
|
def set_parameter(self, name: str, value: Any) -> None:
|
||||||
|
"""Enregistrer un paramètre nommé."""
|
||||||
|
self.__parameters[name] = value
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
||||||
|
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
||||||
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
from typing import Any, Protocol
|
||||||
|
|
||||||
|
|
||||||
|
class IContainer(Protocol):
|
||||||
|
"""Définir le port partagé de résolution des dépendances."""
|
||||||
|
|
||||||
|
def get(self, name: type[Any] | str) -> Any:
|
||||||
|
"""Résoudre un service par son type ou son identifiant."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set(self, name: type[Any], obj: object) -> None:
|
||||||
|
"""Enregistrer une instance pour un type."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def alias(self, alias: type[Any] | str, name: type[Any] | str) -> None:
|
||||||
|
"""Associer un identifiant à un autre service."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set_parameter(self, name: str, value: Any) -> None:
|
||||||
|
"""Enregistrer un paramètre nommé."""
|
||||||
|
...
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerPublicApiTests(unittest.TestCase):
|
||||||
|
def test_public_types_are_importable(self) -> None:
|
||||||
|
from container import Container, IContainer
|
||||||
|
|
||||||
|
self.assertEqual(Container.__name__, "Container")
|
||||||
|
self.assertEqual(IContainer.__name__, "IContainer")
|
||||||
@@ -1 +1,3 @@
|
|||||||
"""Persistence library for MangaCollec data."""
|
from mangacollec_store.archives.store import Store
|
||||||
|
|
||||||
|
__all__ = ["Store"]
|
||||||
Whitespace-only changes.
@@ -0,0 +1,14 @@
|
|||||||
|
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
||||||
|
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
||||||
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
"""Chargement des auteurs dans le store local."""
|
||||||
|
|
||||||
|
from mangacollec_store.archives.authors.add_authors import add_authors
|
||||||
|
from mangacollec_store.archives.authors.add_jobs import add_jobs
|
||||||
|
from mangacollec_store.archives.authors.add_tasks import add_tasks
|
||||||
|
from mangacollec_store.archives.authors.load_author import LoadAuthor
|
||||||
|
from mangacollec_store.archives.authors.load_authors import LoadAuthors
|
||||||
|
|
||||||
|
__all__ = ["LoadAuthor", "LoadAuthors", "add_authors", "add_jobs", "add_tasks"]
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.author import Author
|
from mangacollec.model.catalogue import Author
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_authors(store: Store, authors: list[Author]) -> None:
|
def add_authors(store: Store, authors: list[Author]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.job import Job
|
from mangacollec.model.catalogue import Job
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_jobs(store: Store, jobs: list[Job]) -> None:
|
def add_jobs(store: Store, jobs: list[Job]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.task import Task
|
from mangacollec.model.catalogue import Task
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_tasks(store: Store, tasks: list[Task]) -> None:
|
def add_tasks(store: Store, tasks: list[Task]) -> None:
|
||||||
+7
-8
@@ -3,14 +3,13 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto import AuthorResponse
|
from mangacollec.dto.catalogue import AuthorResponse
|
||||||
from libs.store.authors.add_authors import add_authors
|
|
||||||
from libs.store.authors.add_jobs import add_jobs
|
from mangacollec_store.archives.authors import add_authors, add_jobs, add_tasks
|
||||||
from libs.store.authors.add_tasks import add_tasks
|
from mangacollec_store.archives.editions import add_editions
|
||||||
from libs.store.core.store_core import Store
|
from mangacollec_store.archives.series import add_serie
|
||||||
from libs.store.editions.add_editions import add_editions
|
from mangacollec_store import Store
|
||||||
from libs.store.series.add_serie import add_serie
|
from mangacollec_store.archives.volumes import add_volumes
|
||||||
from libs.store.volumes.add_volumes import add_volumes
|
|
||||||
|
|
||||||
|
|
||||||
class LoadAuthor:
|
class LoadAuthor:
|
||||||
+4
-3
@@ -5,9 +5,10 @@
|
|||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Chargement de la liste des auteurs dans le store local."""
|
"""Chargement de la liste des auteurs dans le store local."""
|
||||||
|
|
||||||
from libs.mangacollec.dto import AuthorsResponse
|
from mangacollec.dto.catalogue import AuthorsResponse
|
||||||
from libs.store.authors.add_authors import add_authors
|
|
||||||
from libs.store.core.store_core import Store
|
from mangacollec_store.archives.authors import add_authors
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
class LoadAuthors:
|
class LoadAuthors:
|
||||||
+5
-2
@@ -3,7 +3,10 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
"""Chargement des réponses d'édition dans le store."""
|
||||||
|
|
||||||
from libs.store.kinds.load_kinds import LoadKinds
|
from mangacollec_store.archives.editions.add_box_editions import add_box_editions
|
||||||
|
from mangacollec_store.archives.editions.add_editions import add_editions
|
||||||
|
from mangacollec_store.archives.editions.load_edition import LoadEdition
|
||||||
|
|
||||||
__all__ = ["LoadKinds"]
|
__all__ = ["add_box_editions", "add_editions", "LoadEdition"]
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue import BoxEdition
|
from mangacollec.model.catalogue import BoxEdition
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_box_editions(store: Store, box_editions: list[BoxEdition]) -> None:
|
def add_box_editions(store: Store, box_editions: list[BoxEdition]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue import Edition
|
from mangacollec.model.catalogue import Edition
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_editions(store: Store, editions: list[Edition]) -> None:
|
def add_editions(store: Store, editions: list[Edition]) -> None:
|
||||||
+7
-7
@@ -3,13 +3,13 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto.edition_response import EditionResponse
|
from mangacollec.dto.catalogue import EditionResponse
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.editions.add_editions import add_editions
|
from mangacollec_store.archives.editions import add_editions
|
||||||
from libs.store.publishers.add_publishers import add_publishers
|
from mangacollec_store.archives.publishers import add_publishers
|
||||||
from libs.store.series.add_serie import add_serie
|
from mangacollec_store.archives.series import add_serie, add_types
|
||||||
from libs.store.series.add_types import add_types
|
from mangacollec_store import Store
|
||||||
from libs.store.volumes.add_volumes import add_volumes
|
from mangacollec_store.archives.volumes import add_volumes
|
||||||
|
|
||||||
|
|
||||||
class LoadEdition:
|
class LoadEdition:
|
||||||
+3
-3
@@ -3,8 +3,8 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Chargement des réponses d'édition dans le store."""
|
|
||||||
|
|
||||||
from libs.store.editions.load_edition import LoadEdition
|
from mangacollec_store.archives.kinds.add_kinds import add_kinds
|
||||||
|
from mangacollec_store.archives.kinds.load_kinds import LoadKinds
|
||||||
|
|
||||||
__all__ = ["LoadEdition"]
|
__all__ = ["add_kinds", "LoadKinds"]
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.kind import Kind
|
from mangacollec.model.catalogue import Kind
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_kinds(store: Store, kinds: list[Kind]) -> None:
|
def add_kinds(store: Store, kinds: list[Kind]) -> None:
|
||||||
+4
-3
@@ -3,9 +3,10 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto.kinds_response import KindsResponse
|
from mangacollec.dto.catalogue import KindsResponse
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.kinds.add_kinds import add_kinds
|
from mangacollec_store import Store
|
||||||
|
from mangacollec_store.archives.kinds import add_kinds
|
||||||
|
|
||||||
|
|
||||||
class LoadKinds:
|
class LoadKinds:
|
||||||
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
|
||||||
from .database import Database
|
from mangacollec_store.archives.model.database import Database
|
||||||
from .news import NewsIndex
|
from mangacollec_store.archives.model.news import NewsStore
|
||||||
|
|
||||||
__all__ = ["Database", "NewsIndex"]
|
__all__ = ["Database", "NewsStore"]
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
||||||
|
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
||||||
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
from mangacollec.model.catalogue import (
|
||||||
|
Author,
|
||||||
|
BoxEdition,
|
||||||
|
BoxVolume,
|
||||||
|
Boxe,
|
||||||
|
Edition,
|
||||||
|
Job,
|
||||||
|
Kind,
|
||||||
|
Publisher,
|
||||||
|
Serie,
|
||||||
|
SerieType,
|
||||||
|
Task,
|
||||||
|
Volume,
|
||||||
|
)
|
||||||
|
from mangacollec.model.possessions import (
|
||||||
|
BoxFollowEdition,
|
||||||
|
BoxPossession,
|
||||||
|
VolumeFollowEdition,
|
||||||
|
VolumePossession,
|
||||||
|
)
|
||||||
|
from mangacollec.model.reads import Read
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from mangacollec.value_object import ID
|
||||||
|
from mangacollec_store.archives.model.news import NewsStore
|
||||||
|
|
||||||
|
|
||||||
|
class Database(BaseModel):
|
||||||
|
"""État complet du store local."""
|
||||||
|
|
||||||
|
series: dict[ID, Serie] = Field(default_factory=dict)
|
||||||
|
kinds: dict[ID, Kind] = Field(default_factory=dict)
|
||||||
|
types: dict[ID, SerieType] = Field(default_factory=dict)
|
||||||
|
tasks: dict[ID, Task] = Field(default_factory=dict)
|
||||||
|
jobs: dict[ID, Job] = Field(default_factory=dict)
|
||||||
|
authors: dict[ID, Author] = Field(default_factory=dict)
|
||||||
|
editions: dict[ID, Edition] = Field(default_factory=dict)
|
||||||
|
publishers: dict[ID, Publisher] = Field(default_factory=dict)
|
||||||
|
volumes: dict[ID, Volume] = Field(default_factory=dict)
|
||||||
|
news: NewsStore = Field(default_factory=NewsStore)
|
||||||
|
box_editions: dict[ID, BoxEdition] = Field(default_factory=dict)
|
||||||
|
boxes: dict[ID, Boxe] = Field(default_factory=dict)
|
||||||
|
box_volumes: dict[ID, BoxVolume] = Field(default_factory=dict)
|
||||||
|
follow_editions: dict[ID, VolumeFollowEdition] = Field(default_factory=dict)
|
||||||
|
box_follow_editions: dict[ID, BoxFollowEdition] = Field(default_factory=dict)
|
||||||
|
possessions: dict[ID, VolumePossession] = Field(default_factory=dict)
|
||||||
|
box_possessions: dict[ID, BoxPossession] = Field(default_factory=dict)
|
||||||
|
reads: dict[ID, Read] = Field(default_factory=dict)
|
||||||
+2
-3
@@ -3,12 +3,11 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
from mangacollec.value_object import ID
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from libs.mangacollec.value_object.id import ID
|
|
||||||
|
|
||||||
|
class NewsStore(BaseModel):
|
||||||
class NewsIndex(BaseModel):
|
|
||||||
"""Index persistant des nouveautés affichées par le store local."""
|
"""Index persistant des nouveautés affichées par le store local."""
|
||||||
|
|
||||||
volumes: list[ID] = Field(default_factory=list)
|
volumes: list[ID] = Field(default_factory=list)
|
||||||
+1
-1
@@ -5,6 +5,6 @@
|
|||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Chargement des nouveautés dans le store local."""
|
"""Chargement des nouveautés dans le store local."""
|
||||||
|
|
||||||
from .load_news import LoadNews
|
from mangacollec_store.archives.news.load_news import LoadNews
|
||||||
|
|
||||||
__all__ = ["LoadNews"]
|
__all__ = ["LoadNews"]
|
||||||
+8
-11
@@ -3,16 +3,13 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto.news_response import NewsResponse
|
from mangacollec.dto import NewsResponse
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.editions.add_box_editions import add_box_editions
|
from mangacollec_store import Store
|
||||||
from libs.store.editions.add_editions import add_editions
|
from mangacollec_store.archives.editions import add_box_editions, add_editions
|
||||||
from libs.store.model.news import NewsIndex
|
from mangacollec_store.archives.model import NewsStore
|
||||||
from libs.store.series.add_serie import add_serie
|
from mangacollec_store.archives.series import add_serie, add_types
|
||||||
from libs.store.series.add_types import add_types
|
from mangacollec_store.archives.volumes import add_box_volumes, add_boxes, add_volumes
|
||||||
from libs.store.volumes.add_box_volumes import add_box_volumes
|
|
||||||
from libs.store.volumes.add_boxes import add_boxes
|
|
||||||
from libs.store.volumes.add_volumes import add_volumes
|
|
||||||
|
|
||||||
|
|
||||||
class LoadNews:
|
class LoadNews:
|
||||||
@@ -33,7 +30,7 @@ class LoadNews:
|
|||||||
add_boxes(self._store, response.boxes)
|
add_boxes(self._store, response.boxes)
|
||||||
add_box_editions(self._store, response.box_editions)
|
add_box_editions(self._store, response.box_editions)
|
||||||
add_box_volumes(self._store, response.box_volumes)
|
add_box_volumes(self._store, response.box_volumes)
|
||||||
self._store.news = NewsIndex(
|
self._store.news = NewsStore(
|
||||||
volumes=[volume.id for volume in response.volumes],
|
volumes=[volume.id for volume in response.volumes],
|
||||||
boxes=[box.id for box in response.boxes],
|
boxes=[box.id for box in response.boxes],
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
||||||
|
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
||||||
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
|
||||||
|
from mangacollec_store.archives.possessions.add_box_follow_editions import add_box_follow_editions
|
||||||
|
from mangacollec_store.archives.possessions.add_box_possessions import add_box_possessions
|
||||||
|
from mangacollec_store.archives.possessions.add_follow_editions import add_follow_editions
|
||||||
|
from mangacollec_store.archives.possessions.add_possessions import add_possessions
|
||||||
|
from mangacollec_store.archives.possessions.delete_box_possessions import DeleteBoxPossessions
|
||||||
|
from mangacollec_store.archives.possessions.delete_possessions import DeletePossessions
|
||||||
|
from mangacollec_store.archives.possessions.load_box_possessions import LoadBoxPossessions
|
||||||
|
from mangacollec_store.archives.possessions.load_volume_possessions import LoadVolumePossessions
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"add_box_follow_editions",
|
||||||
|
"add_box_possessions",
|
||||||
|
"add_follow_editions",
|
||||||
|
"add_possessions",
|
||||||
|
"DeleteBoxPossessions",
|
||||||
|
"DeletePossessions",
|
||||||
|
"LoadBoxPossessions",
|
||||||
|
"LoadVolumePossessions",
|
||||||
|
]
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.possessions.box_follow_edition import BoxFollowEdition
|
from mangacollec.model.possessions import BoxFollowEdition
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_box_follow_editions(store: Store, box_follow_editions: list[BoxFollowEdition]) -> None:
|
def add_box_follow_editions(store: Store, box_follow_editions: list[BoxFollowEdition]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.possessions.box_possession import BoxPossession
|
from mangacollec.model.possessions import BoxPossession
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_box_possessions(store: Store, box_possessions: list[BoxPossession]) -> None:
|
def add_box_possessions(store: Store, box_possessions: list[BoxPossession]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.possessions.volume_follow_edition import VolumeFollowEdition
|
from mangacollec.model.possessions import VolumeFollowEdition
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_follow_editions(store: Store, follow_editions: list[VolumeFollowEdition]) -> None:
|
def add_follow_editions(store: Store, follow_editions: list[VolumeFollowEdition]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.possessions.volume_possession import VolumePossession
|
from mangacollec.model.possessions import VolumePossession
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_possessions(store: Store, possessions: list[VolumePossession]) -> None:
|
def add_possessions(store: Store, possessions: list[VolumePossession]) -> None:
|
||||||
+3
-4
@@ -5,10 +5,9 @@
|
|||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Suppression des possessions de boxes dans le store."""
|
"""Suppression des possessions de boxes dans le store."""
|
||||||
|
|
||||||
from libs.mangacollec.dto import (
|
from mangacollec.dto.possessions import DeleteBoxPossessionsMultipleResponse
|
||||||
DeleteBoxPossessionsMultipleResponse,
|
|
||||||
)
|
from mangacollec_store import Store
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteBoxPossessions:
|
class DeleteBoxPossessions:
|
||||||
+2
-4
@@ -5,10 +5,8 @@
|
|||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Suppression des possessions dans le store."""
|
"""Suppression des possessions dans le store."""
|
||||||
|
|
||||||
from libs.mangacollec.dto import (
|
from mangacollec.dto.possessions import DeleteVolumePossessionsMultipleResponse
|
||||||
DeleteVolumePossessionsMultipleResponse,
|
from mangacollec_store import Store
|
||||||
)
|
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
|
||||||
|
|
||||||
class DeletePossessions:
|
class DeletePossessions:
|
||||||
+7
-7
@@ -5,14 +5,14 @@
|
|||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Chargement des possessions dans le store."""
|
"""Chargement des possessions dans le store."""
|
||||||
|
|
||||||
from libs.mangacollec.dto import (
|
from mangacollec.dto.possessions import CreateBoxPossessionsMultipleResponse
|
||||||
CreateBoxPossessionsMultipleResponse,
|
from mangacollec_store.archives.possessions import (
|
||||||
|
add_box_follow_editions,
|
||||||
|
add_box_possessions,
|
||||||
|
add_follow_editions,
|
||||||
|
add_possessions,
|
||||||
)
|
)
|
||||||
from libs.store.core.store_core import Store
|
from mangacollec_store import Store
|
||||||
from libs.store.possessions.add_box_follow_editions import add_box_follow_editions
|
|
||||||
from libs.store.possessions.add_box_possessions import add_box_possessions
|
|
||||||
from libs.store.possessions.add_follow_editions import add_follow_editions
|
|
||||||
from libs.store.possessions.add_possessions import add_possessions
|
|
||||||
|
|
||||||
|
|
||||||
class LoadBoxPossessions:
|
class LoadBoxPossessions:
|
||||||
+3
-6
@@ -5,12 +5,9 @@
|
|||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Chargement des possessions dans le store."""
|
"""Chargement des possessions dans le store."""
|
||||||
|
|
||||||
from libs.mangacollec.dto import (
|
from mangacollec.dto.possessions import CreateVolumePossessionsMultipleResponse
|
||||||
CreateVolumePossessionsMultipleResponse,
|
from mangacollec_store.archives.possessions import add_follow_editions, add_possessions
|
||||||
)
|
from mangacollec_store import Store
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.possessions.add_follow_editions import add_follow_editions
|
|
||||||
from libs.store.possessions.add_possessions import add_possessions
|
|
||||||
|
|
||||||
|
|
||||||
class LoadVolumePossessions:
|
class LoadVolumePossessions:
|
||||||
+4
-5
@@ -3,9 +3,8 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Chargement des auteurs dans le store local."""
|
from mangacollec_store.archives.publishers.add_publishers import add_publishers
|
||||||
|
from mangacollec_store.archives.publishers.load_publisher import LoadPublisher
|
||||||
|
from mangacollec_store.archives.publishers.load_publishers import LoadPublishers
|
||||||
|
|
||||||
from libs.store.authors.load_author import LoadAuthor
|
__all__ = ["add_publishers", "LoadPublisher", "LoadPublishers"]
|
||||||
from libs.store.authors.load_authors import LoadAuthors
|
|
||||||
|
|
||||||
__all__ = ["LoadAuthor", "LoadAuthors"]
|
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue import Publisher
|
from mangacollec.model.catalogue import Publisher
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_publishers(store: Store, publishers: list[Publisher]) -> None:
|
def add_publishers(store: Store, publishers: list[Publisher]) -> None:
|
||||||
+7
-9
@@ -3,15 +3,13 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto import PublisherResponse
|
from mangacollec.dto.catalogue import PublisherResponse
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.editions.add_box_editions import add_box_editions
|
from mangacollec_store import Store
|
||||||
from libs.store.editions.add_editions import add_editions
|
from mangacollec_store.archives.editions import add_box_editions, add_editions
|
||||||
from libs.store.publishers.add_publishers import add_publishers
|
from mangacollec_store.archives.publishers import add_publishers
|
||||||
from libs.store.series.add_serie import add_serie
|
from mangacollec_store.archives.series import add_serie, add_types
|
||||||
from libs.store.series.add_types import add_types
|
from mangacollec_store.archives.volumes import add_boxes, add_volumes
|
||||||
from libs.store.volumes.add_boxes import add_boxes
|
|
||||||
from libs.store.volumes.add_volumes import add_volumes
|
|
||||||
|
|
||||||
|
|
||||||
class LoadPublisher:
|
class LoadPublisher:
|
||||||
+4
-3
@@ -3,9 +3,10 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto.publishers_response import PublishersResponse
|
from mangacollec.dto.catalogue import PublishersResponse
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.publishers.add_publishers import add_publishers
|
from mangacollec_store import Store
|
||||||
|
from mangacollec_store.archives.publishers import add_publishers
|
||||||
|
|
||||||
|
|
||||||
class LoadPublishers:
|
class LoadPublishers:
|
||||||
+5
-2
@@ -4,6 +4,9 @@
|
|||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
|
|
||||||
from libs.store.core.store_core import Store
|
from mangacollec_store.archives.series.add_serie import add_serie
|
||||||
|
from mangacollec_store.archives.series.add_types import add_types
|
||||||
|
from mangacollec_store.archives.series.load_serie import LoadSerie
|
||||||
|
from mangacollec_store.archives.series.load_series import LoadSeries
|
||||||
|
|
||||||
__all__ = ["Store"]
|
__all__ = ["add_serie", "add_types", "LoadSerie", "LoadSeries"]
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.serie import Serie
|
from mangacollec.model.catalogue import Serie
|
||||||
from libs.store import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_serie(store: Store, series: list[Serie]) -> None:
|
def add_serie(store: Store, series: list[Serie]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.serie_type import SerieType
|
from mangacollec.model.catalogue import SerieType
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_types(store: Store, serie_types: list[SerieType]) -> None:
|
def add_types(store: Store, serie_types: list[SerieType]) -> None:
|
||||||
+9
-14
@@ -3,20 +3,15 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto import SerieResponse
|
from mangacollec.dto.catalogue import SerieResponse
|
||||||
from libs.store.authors.add_authors import add_authors
|
|
||||||
from libs.store.authors.add_jobs import add_jobs
|
from mangacollec_store.archives.authors import add_authors, add_jobs, add_tasks
|
||||||
from libs.store.authors.add_tasks import add_tasks
|
from mangacollec_store.archives.editions import add_box_editions, add_editions
|
||||||
from libs.store.core.store_core import Store
|
from mangacollec_store.archives.kinds import add_kinds
|
||||||
from libs.store.editions.add_box_editions import add_box_editions
|
from mangacollec_store.archives.publishers import add_publishers
|
||||||
from libs.store.editions.add_editions import add_editions
|
from mangacollec_store.archives.series import add_serie, add_types
|
||||||
from libs.store.kinds.add_kinds import add_kinds
|
from mangacollec_store import Store
|
||||||
from libs.store.publishers.add_publishers import add_publishers
|
from mangacollec_store.archives.volumes import add_box_volumes, add_boxes, add_volumes
|
||||||
from libs.store.series.add_serie import add_serie
|
|
||||||
from libs.store.series.add_types import add_types
|
|
||||||
from libs.store.volumes.add_box_volumes import add_box_volumes
|
|
||||||
from libs.store.volumes.add_boxes import add_boxes
|
|
||||||
from libs.store.volumes.add_volumes import add_volumes
|
|
||||||
|
|
||||||
|
|
||||||
class LoadSerie:
|
class LoadSerie:
|
||||||
+4
-4
@@ -3,10 +3,10 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto.series_response import SeriesResponse
|
from mangacollec.dto.catalogue import SeriesResponse
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.series.add_serie import add_serie
|
from mangacollec_store import Store
|
||||||
from libs.store.series.add_types import add_types
|
from mangacollec_store.archives.series import add_serie, add_types
|
||||||
|
|
||||||
|
|
||||||
class LoadSeries:
|
class LoadSeries:
|
||||||
+60
-55
@@ -7,25 +7,30 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from libs.mangacollec.model.catalogue.author import Author
|
from mangacollec.model.catalogue import (
|
||||||
from libs.mangacollec.model.catalogue.box_edition import BoxEdition
|
Author,
|
||||||
from libs.mangacollec.model.catalogue.box_volume import BoxVolume
|
BoxEdition,
|
||||||
from libs.mangacollec.model.catalogue.boxe import Boxe
|
BoxVolume,
|
||||||
from libs.mangacollec.model.catalogue.edition import Edition
|
Boxe,
|
||||||
from libs.mangacollec.model.catalogue.job import Job
|
Edition,
|
||||||
from libs.mangacollec.model.catalogue.kind import Kind
|
Job,
|
||||||
from libs.mangacollec.model.catalogue.publisher import Publisher
|
Kind,
|
||||||
from libs.mangacollec.model.catalogue.serie import Serie
|
Publisher,
|
||||||
from libs.mangacollec.model.catalogue.serie_type import SerieType
|
Serie,
|
||||||
from libs.mangacollec.model.catalogue.task import Task
|
SerieType,
|
||||||
from libs.mangacollec.model.catalogue.volume import Volume
|
Task,
|
||||||
from libs.mangacollec.model.possessions.box_follow_edition import BoxFollowEdition
|
Volume,
|
||||||
from libs.mangacollec.model.possessions.box_possession import BoxPossession
|
)
|
||||||
from libs.mangacollec.model.possessions.volume_follow_edition import VolumeFollowEdition
|
from mangacollec.model.possessions import (
|
||||||
from libs.mangacollec.model.possessions.volume_possession import VolumePossession
|
BoxFollowEdition,
|
||||||
from libs.mangacollec.model.reads.read import Read
|
BoxPossession,
|
||||||
from libs.store.model.database import Database
|
VolumeFollowEdition,
|
||||||
from libs.store.model.news import NewsIndex
|
VolumePossession,
|
||||||
|
)
|
||||||
|
from mangacollec.model.reads import Read
|
||||||
|
from mangacollec.value_object import ID
|
||||||
|
|
||||||
|
from mangacollec_store.archives.model import Database, NewsStore
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -43,182 +48,182 @@ class Store:
|
|||||||
self._read()
|
self._read()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def series(self) -> dict[str, Serie]:
|
def series(self) -> dict[ID, Serie]:
|
||||||
"""Retourner les séries."""
|
"""Retourner les séries."""
|
||||||
return self._database.series
|
return self._database.series
|
||||||
|
|
||||||
@series.setter
|
@series.setter
|
||||||
def series(self, value: dict[str, Serie]) -> None:
|
def series(self, value: dict[ID, Serie]) -> None:
|
||||||
"""Définir les séries."""
|
"""Définir les séries."""
|
||||||
self._database.series = value
|
self._database.series = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kinds(self) -> dict[str, Kind]:
|
def kinds(self) -> dict[ID, Kind]:
|
||||||
"""Retourner les types de série."""
|
"""Retourner les types de série."""
|
||||||
return self._database.kinds
|
return self._database.kinds
|
||||||
|
|
||||||
@kinds.setter
|
@kinds.setter
|
||||||
def kinds(self, value: dict[str, Kind]) -> None:
|
def kinds(self, value: dict[ID, Kind]) -> None:
|
||||||
"""Définir les types de série."""
|
"""Définir les types de série."""
|
||||||
self._database.kinds = value
|
self._database.kinds = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def types(self) -> dict[str, SerieType]:
|
def types(self) -> dict[ID, SerieType]:
|
||||||
"""Retourner les types."""
|
"""Retourner les types."""
|
||||||
return self._database.types
|
return self._database.types
|
||||||
|
|
||||||
@types.setter
|
@types.setter
|
||||||
def types(self, value: dict[str, SerieType]) -> None:
|
def types(self, value: dict[ID, SerieType]) -> None:
|
||||||
"""Définir les types."""
|
"""Définir les types."""
|
||||||
self._database.types = value
|
self._database.types = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks(self) -> dict[str, Task]:
|
def tasks(self) -> dict[ID, Task]:
|
||||||
"""Retourner les tâches."""
|
"""Retourner les tâches."""
|
||||||
return self._database.tasks
|
return self._database.tasks
|
||||||
|
|
||||||
@tasks.setter
|
@tasks.setter
|
||||||
def tasks(self, value: dict[str, Task]) -> None:
|
def tasks(self, value: dict[ID, Task]) -> None:
|
||||||
"""Définir les tâches."""
|
"""Définir les tâches."""
|
||||||
self._database.tasks = value
|
self._database.tasks = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def jobs(self) -> dict[str, Job]:
|
def jobs(self) -> dict[ID, Job]:
|
||||||
"""Retourner les métiers."""
|
"""Retourner les métiers."""
|
||||||
return self._database.jobs
|
return self._database.jobs
|
||||||
|
|
||||||
@jobs.setter
|
@jobs.setter
|
||||||
def jobs(self, value: dict[str, Job]) -> None:
|
def jobs(self, value: dict[ID, Job]) -> None:
|
||||||
"""Définir les métiers."""
|
"""Définir les métiers."""
|
||||||
self._database.jobs = value
|
self._database.jobs = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def authors(self) -> dict[str, Author]:
|
def authors(self) -> dict[ID, Author]:
|
||||||
"""Retourner les auteurs."""
|
"""Retourner les auteurs."""
|
||||||
return self._database.authors
|
return self._database.authors
|
||||||
|
|
||||||
@authors.setter
|
@authors.setter
|
||||||
def authors(self, value: dict[str, Author]) -> None:
|
def authors(self, value: dict[ID, Author]) -> None:
|
||||||
"""Définir les auteurs."""
|
"""Définir les auteurs."""
|
||||||
self._database.authors = value
|
self._database.authors = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def editions(self) -> dict[str, Edition]:
|
def editions(self) -> dict[ID, Edition]:
|
||||||
"""Retourner les éditions."""
|
"""Retourner les éditions."""
|
||||||
return self._database.editions
|
return self._database.editions
|
||||||
|
|
||||||
@editions.setter
|
@editions.setter
|
||||||
def editions(self, value: dict[str, Edition]) -> None:
|
def editions(self, value: dict[ID, Edition]) -> None:
|
||||||
"""Définir les éditions."""
|
"""Définir les éditions."""
|
||||||
self._database.editions = value
|
self._database.editions = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def publishers(self) -> dict[str, Publisher]:
|
def publishers(self) -> dict[ID, Publisher]:
|
||||||
"""Retourner les éditeurs."""
|
"""Retourner les éditeurs."""
|
||||||
return self._database.publishers
|
return self._database.publishers
|
||||||
|
|
||||||
@publishers.setter
|
@publishers.setter
|
||||||
def publishers(self, value: dict[str, Publisher]) -> None:
|
def publishers(self, value: dict[ID, Publisher]) -> None:
|
||||||
"""Définir les éditeurs."""
|
"""Définir les éditeurs."""
|
||||||
self._database.publishers = value
|
self._database.publishers = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volumes(self) -> dict[str, Volume]:
|
def volumes(self) -> dict[ID, Volume]:
|
||||||
"""Retourner les volumes."""
|
"""Retourner les volumes."""
|
||||||
return self._database.volumes
|
return self._database.volumes
|
||||||
|
|
||||||
@volumes.setter
|
@volumes.setter
|
||||||
def volumes(self, value: dict[str, Volume]) -> None:
|
def volumes(self, value: dict[ID, Volume]) -> None:
|
||||||
"""Définir les volumes."""
|
"""Définir les volumes."""
|
||||||
self._database.volumes = value
|
self._database.volumes = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def news(self) -> NewsIndex:
|
def news(self) -> NewsStore:
|
||||||
"""Retourner l'index des nouveautés."""
|
"""Retourner l'index des nouveautés."""
|
||||||
return self._database.news
|
return self._database.news
|
||||||
|
|
||||||
@news.setter
|
@news.setter
|
||||||
def news(self, value: NewsIndex) -> None:
|
def news(self, value: NewsStore) -> None:
|
||||||
"""Définir l'index des nouveautés."""
|
"""Définir l'index des nouveautés."""
|
||||||
self._database.news = value
|
self._database.news = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def box_editions(self) -> dict[str, BoxEdition]:
|
def box_editions(self) -> dict[ID, BoxEdition]:
|
||||||
"""Retourner les éditions de boxes."""
|
"""Retourner les éditions de boxes."""
|
||||||
return self._database.box_editions
|
return self._database.box_editions
|
||||||
|
|
||||||
@box_editions.setter
|
@box_editions.setter
|
||||||
def box_editions(self, value: dict[str, BoxEdition]) -> None:
|
def box_editions(self, value: dict[ID, BoxEdition]) -> None:
|
||||||
"""Définir les éditions de boxes."""
|
"""Définir les éditions de boxes."""
|
||||||
self._database.box_editions = value
|
self._database.box_editions = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def boxes(self) -> dict[str, Boxe]:
|
def boxes(self) -> dict[ID, Boxe]:
|
||||||
"""Retourner les boxes."""
|
"""Retourner les boxes."""
|
||||||
return self._database.boxes
|
return self._database.boxes
|
||||||
|
|
||||||
@boxes.setter
|
@boxes.setter
|
||||||
def boxes(self, value: dict[str, Boxe]) -> None:
|
def boxes(self, value: dict[ID, Boxe]) -> None:
|
||||||
"""Définir les boxes."""
|
"""Définir les boxes."""
|
||||||
self._database.boxes = value
|
self._database.boxes = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def box_volumes(self) -> dict[str, BoxVolume]:
|
def box_volumes(self) -> dict[ID, BoxVolume]:
|
||||||
"""Retourner les volumes de boxes."""
|
"""Retourner les volumes de boxes."""
|
||||||
return self._database.box_volumes
|
return self._database.box_volumes
|
||||||
|
|
||||||
@box_volumes.setter
|
@box_volumes.setter
|
||||||
def box_volumes(self, value: dict[str, BoxVolume]) -> None:
|
def box_volumes(self, value: dict[ID, BoxVolume]) -> None:
|
||||||
"""Définir les volumes de boxes."""
|
"""Définir les volumes de boxes."""
|
||||||
self._database.box_volumes = value
|
self._database.box_volumes = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def possessions(self) -> dict[str, VolumePossession]:
|
def possessions(self) -> dict[ID, VolumePossession]:
|
||||||
"""Retourner les possessions de volumes."""
|
"""Retourner les possessions de volumes."""
|
||||||
return self._database.possessions
|
return self._database.possessions
|
||||||
|
|
||||||
@possessions.setter
|
@possessions.setter
|
||||||
def possessions(self, value: dict[str, VolumePossession]) -> None:
|
def possessions(self, value: dict[ID, VolumePossession]) -> None:
|
||||||
"""Définir les possessions de volumes."""
|
"""Définir les possessions de volumes."""
|
||||||
self._database.possessions = value
|
self._database.possessions = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def box_possessions(self) -> dict[str, BoxPossession]:
|
def box_possessions(self) -> dict[ID, BoxPossession]:
|
||||||
"""Retourner les possessions de boxes."""
|
"""Retourner les possessions de boxes."""
|
||||||
return self._database.box_possessions
|
return self._database.box_possessions
|
||||||
|
|
||||||
@box_possessions.setter
|
@box_possessions.setter
|
||||||
def box_possessions(self, value: dict[str, BoxPossession]) -> None:
|
def box_possessions(self, value: dict[ID, BoxPossession]) -> None:
|
||||||
"""Définir les possessions de boxes."""
|
"""Définir les possessions de boxes."""
|
||||||
self._database.box_possessions = value
|
self._database.box_possessions = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reads(self) -> dict[str, Read]:
|
def reads(self) -> dict[ID, Read]:
|
||||||
"""Retourner les lectures."""
|
"""Retourner les lectures."""
|
||||||
return self._database.reads
|
return self._database.reads
|
||||||
|
|
||||||
@reads.setter
|
@reads.setter
|
||||||
def reads(self, value: dict[str, Read]) -> None:
|
def reads(self, value: dict[ID, Read]) -> None:
|
||||||
"""Définir les lectures."""
|
"""Définir les lectures."""
|
||||||
self._database.reads = value
|
self._database.reads = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def follow_editions(self) -> dict[str, VolumeFollowEdition]:
|
def follow_editions(self) -> dict[ID, VolumeFollowEdition]:
|
||||||
"""Retourner les suivis d'éditions de volumes."""
|
"""Retourner les suivis d'éditions de volumes."""
|
||||||
return self._database.follow_editions
|
return self._database.follow_editions
|
||||||
|
|
||||||
@follow_editions.setter
|
@follow_editions.setter
|
||||||
def follow_editions(self, value: dict[str, VolumeFollowEdition]) -> None:
|
def follow_editions(self, value: dict[ID, VolumeFollowEdition]) -> None:
|
||||||
"""Définir les suivis d'éditions de volumes."""
|
"""Définir les suivis d'éditions de volumes."""
|
||||||
self._database.follow_editions = value
|
self._database.follow_editions = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def box_follow_editions(self) -> dict[str, BoxFollowEdition]:
|
def box_follow_editions(self) -> dict[ID, BoxFollowEdition]:
|
||||||
"""Retourner les suivis d'éditions de boxes."""
|
"""Retourner les suivis d'éditions de boxes."""
|
||||||
return self._database.box_follow_editions
|
return self._database.box_follow_editions
|
||||||
|
|
||||||
@box_follow_editions.setter
|
@box_follow_editions.setter
|
||||||
def box_follow_editions(self, value: dict[str, BoxFollowEdition]) -> None:
|
def box_follow_editions(self, value: dict[ID, BoxFollowEdition]) -> None:
|
||||||
"""Définir les suivis d'éditions de boxes."""
|
"""Définir les suivis d'éditions de boxes."""
|
||||||
self._database.box_follow_editions = value
|
self._database.box_follow_editions = value
|
||||||
|
|
||||||
+5
-2
@@ -5,6 +5,9 @@
|
|||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
"""Chargement des volumes dans le store local."""
|
"""Chargement des volumes dans le store local."""
|
||||||
|
|
||||||
from libs.store.volumes.load_volume import LoadVolume
|
from mangacollec_store.archives.volumes.add_box_volumes import add_box_volumes
|
||||||
|
from mangacollec_store.archives.volumes.add_boxes import add_boxes
|
||||||
|
from mangacollec_store.archives.volumes.add_volumes import add_volumes
|
||||||
|
from mangacollec_store.archives.volumes.load_volume import LoadVolume
|
||||||
|
|
||||||
__all__ = ["LoadVolume"]
|
__all__ = ["add_box_volumes", "add_boxes", "add_volumes", "LoadVolume"]
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.box_volume import BoxVolume
|
from mangacollec.model.catalogue import BoxVolume
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_box_volumes(store: Store, box_volumes: list[BoxVolume]) -> None:
|
def add_box_volumes(store: Store, box_volumes: list[BoxVolume]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.boxe import Boxe
|
from mangacollec.model.catalogue import Boxe
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_boxes(store: Store, boxes: list[Boxe]) -> None:
|
def add_boxes(store: Store, boxes: list[Boxe]) -> None:
|
||||||
+3
-2
@@ -3,8 +3,9 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.model.catalogue.volume import Volume
|
from mangacollec.model.catalogue import Volume
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
|
from mangacollec_store import Store
|
||||||
|
|
||||||
|
|
||||||
def add_volumes(store: Store, volumes: list[Volume]) -> None:
|
def add_volumes(store: Store, volumes: list[Volume]) -> None:
|
||||||
+7
-10
@@ -3,16 +3,13 @@
|
|||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
||||||
from libs.mangacollec.dto.volume_response import VolumeResponse
|
from mangacollec.dto.catalogue import VolumeResponse
|
||||||
from libs.store.core.store_core import Store
|
|
||||||
from libs.store.editions.add_box_editions import add_box_editions
|
from mangacollec_store import Store
|
||||||
from libs.store.editions.add_editions import add_editions
|
from mangacollec_store.archives.editions import add_box_editions, add_editions
|
||||||
from libs.store.publishers.add_publishers import add_publishers
|
from mangacollec_store.archives.publishers import add_publishers
|
||||||
from libs.store.series.add_serie import add_serie
|
from mangacollec_store.archives.series import add_serie, add_types
|
||||||
from libs.store.series.add_types import add_types
|
from mangacollec_store.archives.volumes import add_box_volumes, add_boxes, add_volumes
|
||||||
from libs.store.volumes.add_box_volumes import add_box_volumes
|
|
||||||
from libs.store.volumes.add_boxes import add_boxes
|
|
||||||
from libs.store.volumes.add_volumes import add_volumes
|
|
||||||
|
|
||||||
|
|
||||||
class LoadVolume:
|
class LoadVolume:
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
|
||||||
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
|
||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
|
|
||||||
from libs.mangacollec.model.catalogue.author import Author
|
|
||||||
from libs.mangacollec.model.catalogue.box_edition import BoxEdition
|
|
||||||
from libs.mangacollec.model.catalogue.box_volume import BoxVolume
|
|
||||||
from libs.mangacollec.model.catalogue.boxe import Boxe
|
|
||||||
from libs.mangacollec.model.catalogue.edition import Edition
|
|
||||||
from libs.mangacollec.model.catalogue.job import Job
|
|
||||||
from libs.mangacollec.model.catalogue.kind import Kind
|
|
||||||
from libs.mangacollec.model.catalogue.publisher import Publisher
|
|
||||||
from libs.mangacollec.model.catalogue.serie import Serie
|
|
||||||
from libs.mangacollec.model.catalogue.serie_type import SerieType
|
|
||||||
from libs.mangacollec.model.catalogue.task import Task
|
|
||||||
from libs.mangacollec.model.catalogue.volume import Volume
|
|
||||||
from libs.mangacollec.model.possessions.box_follow_edition import BoxFollowEdition
|
|
||||||
from libs.mangacollec.model.possessions.box_possession import BoxPossession
|
|
||||||
from libs.mangacollec.model.possessions.volume_follow_edition import VolumeFollowEdition
|
|
||||||
from libs.mangacollec.model.possessions.volume_possession import VolumePossession
|
|
||||||
from libs.mangacollec.model.reads.read import Read
|
|
||||||
from libs.store.model.news import NewsIndex
|
|
||||||
|
|
||||||
|
|
||||||
class Database(BaseModel):
|
|
||||||
"""État complet du store local."""
|
|
||||||
|
|
||||||
series: dict[str, Serie] = {}
|
|
||||||
kinds: dict[str, Kind] = {}
|
|
||||||
types: dict[str, SerieType] = {}
|
|
||||||
tasks: dict[str, Task] = {}
|
|
||||||
jobs: dict[str, Job] = {}
|
|
||||||
authors: dict[str, Author] = {}
|
|
||||||
editions: dict[str, Edition] = {}
|
|
||||||
publishers: dict[str, Publisher] = {}
|
|
||||||
volumes: dict[str, Volume] = {}
|
|
||||||
news: NewsIndex = Field(default_factory=NewsIndex)
|
|
||||||
box_editions: dict[str, BoxEdition] = {}
|
|
||||||
boxes: dict[str, Boxe] = {}
|
|
||||||
box_volumes: dict[str, BoxVolume] = {}
|
|
||||||
follow_editions: dict[str, VolumeFollowEdition] = {}
|
|
||||||
box_follow_editions: dict[str, BoxFollowEdition] = {}
|
|
||||||
possessions: dict[str, VolumePossession] = {}
|
|
||||||
box_possessions: dict[str, BoxPossession] = {}
|
|
||||||
reads: dict[str, Read] = {}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
|
||||||
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
|
||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
|
||||||
|
|
||||||
from libs.store.possessions.delete_box_possessions import DeleteBoxPossessions
|
|
||||||
from libs.store.possessions.delete_possessions import DeletePossessions
|
|
||||||
from libs.store.possessions.load_box_possessions import LoadBoxPossessions
|
|
||||||
from libs.store.possessions.load_volume_possessions import LoadVolumePossessions
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"DeleteBoxPossessions",
|
|
||||||
"DeletePossessions",
|
|
||||||
"LoadBoxPossessions",
|
|
||||||
"LoadVolumePossessions",
|
|
||||||
]
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
|
||||||
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
|
||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
|
||||||
from libs.store.publishers.load_publisher import LoadPublisher
|
|
||||||
from libs.store.publishers.load_publishers import LoadPublishers
|
|
||||||
|
|
||||||
__all__ = ["LoadPublisher", "LoadPublishers"]
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# __ _____ _ __ ____________________ __ _____________________ _____ _______________
|
|
||||||
# / ___/ / /_ ____ ____ / /_ ___ ____ / __ \ ___ _ __/
|
|
||||||
# \__ \ / __ \ / __ \ / __ \ / __/ / _ \ / __/ ____ / / / / / _ \ | | / /
|
|
||||||
# ___/ / / / / / / /_/ / / /_/ / / /_ / __/ / / /___/ / /_/ / / __/ | |/ /
|
|
||||||
# /____/ /_/ /_/ \____/ \____/ \__/ \___/ /_/ _______ /______/ \___/ |___/
|
|
||||||
|
|
||||||
from libs.store.series.load_serie import LoadSerie
|
|
||||||
from libs.store.series.load_series import LoadSeries
|
|
||||||
|
|
||||||
__all__ = ["LoadSerie", "LoadSeries"]
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
PYTHON ?= python3
|
||||||
|
DOCKER ?= docker
|
||||||
|
DOCKER_COMPOSE ?= $(DOCKER) compose -f docker-compose.yml
|
||||||
|
|
||||||
|
RUNTIME_LIBS := libs/mangacollec libs/mangacollec-store libs/container
|
||||||
|
RUNTIME_WHEEL_DIR := _dist/
|
||||||
|
API_IMAGE := booksync-api:latest
|
||||||
|
WORKER_IMAGE := booksync-worker:latest
|
||||||
|
DASH_IMAGE := booksync-dash:latest
|
||||||
|
|
||||||
|
.PHONY: build_libs build_api build_worker build_dash run_dash run_docker
|
||||||
|
build_libs:
|
||||||
|
@mkdir -p "$(RUNTIME_WHEEL_DIR)" && find "$(RUNTIME_WHEEL_DIR)" -maxdepth 1 -type f -name '*.whl' -exec trash {} +
|
||||||
|
$(PYTHON) -m pip wheel --no-deps --wheel-dir="$(RUNTIME_WHEEL_DIR)" $(RUNTIME_LIBS)
|
||||||
|
|
||||||
|
########## DOCKER ######################################################################################################
|
||||||
|
build_api: build_libs
|
||||||
|
$(DOCKER) build --tag "$(API_IMAGE)" --file booksync-api/Dockerfile .
|
||||||
|
|
||||||
|
build_worker: build_libs
|
||||||
|
$(DOCKER) build --tag "$(WORKER_IMAGE)" --file booksync-worker/Dockerfile .
|
||||||
|
|
||||||
|
build_dash:
|
||||||
|
$(DOCKER) build --tag "$(DASH_IMAGE)" --file booksync-dash/Dockerfile .
|
||||||
|
|
||||||
|
run_dash:
|
||||||
|
$(DOCKER_COMPOSE) up --no-deps --no-recreate --no-build --pull never dash
|
||||||
|
|
||||||
|
run_docker: build_worker build_api build_dash
|
||||||
|
$(DOCKER_COMPOSE) down
|
||||||
|
$(DOCKER_COMPOSE) up --build -d
|
||||||
Reference in new issue
Block a user