grimoireplot 0.0.1__tar.gz → 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/PKG-INFO +3 -3
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/README.md +2 -2
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/models.py +39 -18
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/server.py +43 -0
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/pyproject.toml +1 -1
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/__init__.py +0 -0
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/client.py +0 -0
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/common.py +0 -0
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/create_some_plots.py +0 -0
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/main.py +0 -0
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/ui.py +0 -0
- {grimoireplot-0.0.1 → grimoireplot-0.0.2}/grimoireplot/ui_elements.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: grimoireplot
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: GrimoirePlot is a live dashboard of plotly-compatible plots of remote data
|
|
5
5
|
Author: William Droz
|
|
6
6
|
Author-email: William Droz <william.droz@idiap.ch>
|
|
@@ -24,10 +24,10 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
|
|
25
25
|

|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Adding to your current project
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
uv
|
|
30
|
+
uv add grimoireplot
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Or install from source:
|
|
@@ -7,6 +7,7 @@ from typing import Optional
|
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from sqlmodel import Field, Relationship, SQLModel, create_engine, Session
|
|
9
9
|
from sqlalchemy import ForeignKeyConstraint
|
|
10
|
+
from sqlalchemy.exc import IntegrityError
|
|
10
11
|
from dotenv import load_dotenv
|
|
11
12
|
|
|
12
13
|
load_dotenv()
|
|
@@ -118,36 +119,56 @@ def add_plot(
|
|
|
118
119
|
# Get or create Grimoire
|
|
119
120
|
grimoire = session.get(Grimoire, grimoire_name)
|
|
120
121
|
if grimoire is None:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
try:
|
|
123
|
+
grimoire = Grimoire(name=grimoire_name)
|
|
124
|
+
session.add(grimoire)
|
|
125
|
+
session.commit()
|
|
126
|
+
session.refresh(grimoire)
|
|
127
|
+
except IntegrityError:
|
|
128
|
+
session.rollback()
|
|
129
|
+
grimoire = session.get(Grimoire, grimoire_name)
|
|
125
130
|
|
|
126
131
|
# Get or create Chapter
|
|
127
132
|
chapter = session.get(Chapter, (chapter_name, grimoire_name))
|
|
128
133
|
if chapter is None:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
try:
|
|
135
|
+
chapter = Chapter(name=chapter_name, grimoire_name=grimoire_name)
|
|
136
|
+
session.add(chapter)
|
|
137
|
+
session.commit()
|
|
138
|
+
session.refresh(chapter)
|
|
139
|
+
except IntegrityError:
|
|
140
|
+
session.rollback()
|
|
141
|
+
chapter = session.get(Chapter, (chapter_name, grimoire_name))
|
|
133
142
|
|
|
134
143
|
# Get or create/replace Plot
|
|
135
144
|
plot = session.get(Plot, (plot_name, chapter_name, grimoire_name))
|
|
136
145
|
if plot is None:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
try:
|
|
147
|
+
plot = Plot(
|
|
148
|
+
name=plot_name,
|
|
149
|
+
chapter_name=chapter_name,
|
|
150
|
+
grimoire_name=grimoire_name,
|
|
151
|
+
json_data=json_data,
|
|
152
|
+
)
|
|
153
|
+
session.add(plot)
|
|
154
|
+
session.commit()
|
|
155
|
+
session.refresh(plot)
|
|
156
|
+
except IntegrityError:
|
|
157
|
+
session.rollback()
|
|
158
|
+
plot = session.get(Plot, (plot_name, chapter_name, grimoire_name))
|
|
159
|
+
if plot is not None:
|
|
160
|
+
plot.json_data = json_data
|
|
161
|
+
session.add(plot)
|
|
162
|
+
session.commit()
|
|
163
|
+
session.refresh(plot)
|
|
144
164
|
else:
|
|
145
165
|
plot.json_data = json_data
|
|
146
166
|
session.add(plot)
|
|
167
|
+
session.commit()
|
|
168
|
+
session.refresh(plot)
|
|
147
169
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
170
|
+
if plot is None:
|
|
171
|
+
raise RuntimeError("Failed to create or retrieve plot")
|
|
151
172
|
return plot
|
|
152
173
|
|
|
153
174
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
# SPDX-FileContributor: William Droz <william.droz@idiap.ch>
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
|
|
5
|
+
import time
|
|
6
|
+
import logging
|
|
7
|
+
from functools import wraps
|
|
8
|
+
|
|
5
9
|
from fastapi import HTTPException, Request
|
|
6
10
|
from nicegui import app, ui
|
|
7
11
|
from grimoireplot.common import get_grimoire_secret
|
|
@@ -19,6 +23,44 @@ from grimoireplot.ui_elements import setup_theme
|
|
|
19
23
|
|
|
20
24
|
_GRIMOIRE_SECRET = get_grimoire_secret()
|
|
21
25
|
|
|
26
|
+
logger = logging.getLogger("grimoireplot")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def retry_on_db_error(max_retries: int = 5, base_delay: float = 0.1):
|
|
30
|
+
"""Decorator that retries a function on database errors up to max_retries times."""
|
|
31
|
+
|
|
32
|
+
def decorator(func):
|
|
33
|
+
@wraps(func)
|
|
34
|
+
def wrapper(*args, **kwargs):
|
|
35
|
+
for attempt in range(1, max_retries + 1):
|
|
36
|
+
try:
|
|
37
|
+
return func(*args, **kwargs)
|
|
38
|
+
except HTTPException:
|
|
39
|
+
raise
|
|
40
|
+
except Exception as e:
|
|
41
|
+
if attempt < max_retries:
|
|
42
|
+
delay = base_delay * (2 ** (attempt - 1))
|
|
43
|
+
logger.warning(
|
|
44
|
+
"Database error on attempt %d/%d: %s. Retrying in %.2fs...",
|
|
45
|
+
attempt,
|
|
46
|
+
max_retries,
|
|
47
|
+
str(e),
|
|
48
|
+
delay,
|
|
49
|
+
)
|
|
50
|
+
time.sleep(delay)
|
|
51
|
+
else:
|
|
52
|
+
logger.error(
|
|
53
|
+
"Database error on attempt %d/%d: %s. No more retries.",
|
|
54
|
+
attempt,
|
|
55
|
+
max_retries,
|
|
56
|
+
str(e),
|
|
57
|
+
)
|
|
58
|
+
raise
|
|
59
|
+
|
|
60
|
+
return wrapper
|
|
61
|
+
|
|
62
|
+
return decorator
|
|
63
|
+
|
|
22
64
|
|
|
23
65
|
def verify_secret(request: Request):
|
|
24
66
|
if (secret := request.headers.get("grimoire-secret")) is None:
|
|
@@ -31,6 +73,7 @@ def my_app(host: str = "localhost", port: int = 8080):
|
|
|
31
73
|
create_db_and_tables()
|
|
32
74
|
|
|
33
75
|
@app.post("/add_plot")
|
|
76
|
+
@retry_on_db_error(max_retries=5)
|
|
34
77
|
def add_plot_endpoint(add_plot_request: AddPlotRequest, request: Request):
|
|
35
78
|
verify_secret(request)
|
|
36
79
|
plot = add_plot(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|