django-spire 0.23.8__py3-none-any.whl → 0.23.10__py3-none-any.whl
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.
- django_spire/consts.py +1 -1
- django_spire/contrib/progress/__init__.py +7 -13
- django_spire/contrib/progress/enums.py +2 -1
- django_spire/contrib/progress/session.py +283 -0
- django_spire/contrib/progress/static/django_spire/js/contrib/progress/progress.js +35 -57
- django_spire/contrib/progress/tasks.py +67 -0
- django_spire/contrib/progress/templates/django_spire/contrib/progress/card/card.html +25 -24
- django_spire/contrib/progress/templates/django_spire/contrib/progress/modal/content.html +58 -67
- django_spire/core/management/commands/spire_startapp_pkg/template/app/apps.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/__init__.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/form_urls.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/page_urls.py.template +2 -0
- django_spire/core/templates/django_spire/card/card.html +1 -1
- django_spire/core/templates/django_spire/card/title_card.html +7 -4
- django_spire/core/templates/django_spire/table/base.html +4 -4
- {django_spire-0.23.8.dist-info → django_spire-0.23.10.dist-info}/METADATA +2 -2
- {django_spire-0.23.8.dist-info → django_spire-0.23.10.dist-info}/RECORD +20 -23
- {django_spire-0.23.8.dist-info → django_spire-0.23.10.dist-info}/licenses/LICENSE.md +1 -1
- django_spire/contrib/progress/mixins.py +0 -36
- django_spire/contrib/progress/runner.py +0 -140
- django_spire/contrib/progress/states.py +0 -64
- django_spire/contrib/progress/task.py +0 -40
- django_spire/contrib/progress/tracker.py +0 -245
- {django_spire-0.23.8.dist-info → django_spire-0.23.10.dist-info}/WHEEL +0 -0
- {django_spire-0.23.8.dist-info → django_spire-0.23.10.dist-info}/top_level.txt +0 -0
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
from typing import Any, Callable
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class ProgressMessages:
|
|
12
|
-
complete: str = 'Complete'
|
|
13
|
-
error: str = 'Error'
|
|
14
|
-
starting: str = 'Starting...'
|
|
15
|
-
steps: list[str] = field(default_factory=lambda: [
|
|
16
|
-
'Initializing...',
|
|
17
|
-
'Processing...',
|
|
18
|
-
'Analyzing...',
|
|
19
|
-
'Working...',
|
|
20
|
-
'Almost there...',
|
|
21
|
-
'Finalizing...',
|
|
22
|
-
])
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@dataclass
|
|
26
|
-
class Task:
|
|
27
|
-
func: Callable
|
|
28
|
-
label: str
|
|
29
|
-
name: str
|
|
30
|
-
parallel: bool = False
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@dataclass
|
|
34
|
-
class TaskResult:
|
|
35
|
-
error: BaseException | None = None
|
|
36
|
-
value: Any = None
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def failed(self) -> bool:
|
|
40
|
-
return self.error is not None
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import threading
|
|
5
|
-
import time
|
|
6
|
-
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
8
|
-
|
|
9
|
-
from django.core.cache import cache
|
|
10
|
-
|
|
11
|
-
from django_spire.contrib.progress.enums import ProgressStatus
|
|
12
|
-
from django_spire.contrib.progress.runner import TaskRunner
|
|
13
|
-
from django_spire.contrib.progress.states import TaskState, TrackerState
|
|
14
|
-
from django_spire.contrib.progress.task import Task, TaskResult
|
|
15
|
-
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
from typing import Any, Callable
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
log = logging.getLogger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class ProgressTracker:
|
|
24
|
-
def __init__(self, key: str, timeout: int = 300) -> None:
|
|
25
|
-
self._key = f'progress_tracker_{key}'
|
|
26
|
-
self._lock = threading.Lock()
|
|
27
|
-
self._tasks: list[Task] = []
|
|
28
|
-
self._timeout = timeout
|
|
29
|
-
|
|
30
|
-
def clear(self) -> None:
|
|
31
|
-
cache.delete(self._key)
|
|
32
|
-
|
|
33
|
-
def complete(self, message: str = 'Complete!') -> None:
|
|
34
|
-
with self._lock:
|
|
35
|
-
state = self._get_state()
|
|
36
|
-
state.message = message
|
|
37
|
-
state.progress = 100
|
|
38
|
-
state.status = ProgressStatus.COMPLETE
|
|
39
|
-
self._save_state(state)
|
|
40
|
-
|
|
41
|
-
def error(self, message: str) -> None:
|
|
42
|
-
with self._lock:
|
|
43
|
-
state = self._get_state()
|
|
44
|
-
state.message = message
|
|
45
|
-
state.progress = 0
|
|
46
|
-
state.status = ProgressStatus.ERROR
|
|
47
|
-
self._save_state(state)
|
|
48
|
-
|
|
49
|
-
def execute(self) -> dict[str, Any] | None:
|
|
50
|
-
self._create_state()
|
|
51
|
-
|
|
52
|
-
error = False
|
|
53
|
-
results: dict[str, TaskResult] = {}
|
|
54
|
-
|
|
55
|
-
try:
|
|
56
|
-
error = self._execute_parallel_tasks(results)
|
|
57
|
-
|
|
58
|
-
if not error:
|
|
59
|
-
error = self._execute_sequential_tasks(results)
|
|
60
|
-
|
|
61
|
-
if not error:
|
|
62
|
-
self.complete()
|
|
63
|
-
time.sleep(1)
|
|
64
|
-
except BaseException:
|
|
65
|
-
log.exception('Progress tracker failed')
|
|
66
|
-
error = True
|
|
67
|
-
|
|
68
|
-
if error:
|
|
69
|
-
self.error('An unexpected error occurred')
|
|
70
|
-
time.sleep(2)
|
|
71
|
-
|
|
72
|
-
self.clear()
|
|
73
|
-
|
|
74
|
-
if error:
|
|
75
|
-
return None
|
|
76
|
-
|
|
77
|
-
return {name: result.value for name, result in results.items()}
|
|
78
|
-
|
|
79
|
-
def get(self) -> dict[str, Any] | None:
|
|
80
|
-
return cache.get(self._key)
|
|
81
|
-
|
|
82
|
-
def parallel(
|
|
83
|
-
self,
|
|
84
|
-
name: str,
|
|
85
|
-
func: Callable[[], Any],
|
|
86
|
-
label: str | None = None
|
|
87
|
-
) -> ProgressTracker:
|
|
88
|
-
task = Task(
|
|
89
|
-
func=func,
|
|
90
|
-
label=label or self._generate_label(name),
|
|
91
|
-
name=name,
|
|
92
|
-
parallel=True,
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
self._tasks.append(task)
|
|
96
|
-
|
|
97
|
-
return self
|
|
98
|
-
|
|
99
|
-
def sequential(
|
|
100
|
-
self,
|
|
101
|
-
name: str,
|
|
102
|
-
func: Callable[[dict[str, Any]], Any],
|
|
103
|
-
label: str | None = None
|
|
104
|
-
) -> ProgressTracker:
|
|
105
|
-
task = Task(
|
|
106
|
-
func=func,
|
|
107
|
-
label=label or self._generate_label(name),
|
|
108
|
-
name=name,
|
|
109
|
-
parallel=False,
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
self._tasks.append(task)
|
|
113
|
-
|
|
114
|
-
return self
|
|
115
|
-
|
|
116
|
-
def start(self) -> None:
|
|
117
|
-
state = TrackerState()
|
|
118
|
-
cache.set(self._key, state.to_dict(), timeout=self._timeout)
|
|
119
|
-
|
|
120
|
-
def update(
|
|
121
|
-
self,
|
|
122
|
-
status: ProgressStatus,
|
|
123
|
-
message: str,
|
|
124
|
-
progress: int,
|
|
125
|
-
**kwargs: Any
|
|
126
|
-
) -> None:
|
|
127
|
-
with self._lock:
|
|
128
|
-
state = self._get_state()
|
|
129
|
-
state.message = message
|
|
130
|
-
state.progress = progress
|
|
131
|
-
state.status = status
|
|
132
|
-
self._save_state(state, **kwargs)
|
|
133
|
-
|
|
134
|
-
def _calculate_overall_progress(self, state: TrackerState) -> int:
|
|
135
|
-
if not state.tasks:
|
|
136
|
-
return 0
|
|
137
|
-
|
|
138
|
-
total = sum(
|
|
139
|
-
task.progress
|
|
140
|
-
for task in state.tasks.values()
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
return int(total / len(state.tasks))
|
|
144
|
-
|
|
145
|
-
def _create_state(self) -> None:
|
|
146
|
-
task_names = [task.name for task in self._tasks]
|
|
147
|
-
|
|
148
|
-
state = TrackerState(
|
|
149
|
-
task_order=task_names,
|
|
150
|
-
tasks={name: TaskState() for name in task_names}
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
cache.set(self._key, state.to_dict(), timeout=self._timeout)
|
|
154
|
-
|
|
155
|
-
def _execute_parallel_tasks(self, results: dict[str, TaskResult]) -> bool:
|
|
156
|
-
tasks = [
|
|
157
|
-
task
|
|
158
|
-
for task in self._tasks if task.parallel
|
|
159
|
-
]
|
|
160
|
-
|
|
161
|
-
if not tasks:
|
|
162
|
-
return False
|
|
163
|
-
|
|
164
|
-
runners: list[TaskRunner] = []
|
|
165
|
-
threads: list[threading.Thread] = []
|
|
166
|
-
|
|
167
|
-
for task in tasks:
|
|
168
|
-
runner = TaskRunner(self, task)
|
|
169
|
-
runners.append(runner)
|
|
170
|
-
|
|
171
|
-
thread = threading.Thread(target=runner.run_parallel, args=(results,))
|
|
172
|
-
threads.append(thread)
|
|
173
|
-
thread.start()
|
|
174
|
-
|
|
175
|
-
for thread in threads:
|
|
176
|
-
thread.join(timeout=self._timeout)
|
|
177
|
-
|
|
178
|
-
if thread.is_alive():
|
|
179
|
-
for runner in runners:
|
|
180
|
-
runner.stop()
|
|
181
|
-
|
|
182
|
-
return True
|
|
183
|
-
|
|
184
|
-
return any(
|
|
185
|
-
results.get(task.name, TaskResult()).failed
|
|
186
|
-
for task in tasks
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
def _execute_sequential_tasks(self, results: dict[str, TaskResult]) -> bool:
|
|
190
|
-
tasks = [
|
|
191
|
-
task
|
|
192
|
-
for task in self._tasks
|
|
193
|
-
if not task.parallel
|
|
194
|
-
]
|
|
195
|
-
|
|
196
|
-
for task in tasks:
|
|
197
|
-
runner = TaskRunner(self, task)
|
|
198
|
-
runner.run_sequential(results)
|
|
199
|
-
|
|
200
|
-
if results.get(task.name, TaskResult()).failed:
|
|
201
|
-
return True
|
|
202
|
-
|
|
203
|
-
return False
|
|
204
|
-
|
|
205
|
-
def _generate_label(self, name: str) -> str:
|
|
206
|
-
return name.replace('_', ' ').title()
|
|
207
|
-
|
|
208
|
-
def _get_state(self) -> TrackerState:
|
|
209
|
-
task_names = [task.name for task in self._tasks]
|
|
210
|
-
data = cache.get(self._key)
|
|
211
|
-
|
|
212
|
-
if data is None:
|
|
213
|
-
return TrackerState(
|
|
214
|
-
task_order=task_names,
|
|
215
|
-
tasks={name: TaskState() for name in task_names}
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
return TrackerState.from_dict(data)
|
|
219
|
-
|
|
220
|
-
def _save_state(self, state: TrackerState, **kwargs: Any) -> None:
|
|
221
|
-
data = state.to_dict()
|
|
222
|
-
data.update(kwargs)
|
|
223
|
-
cache.set(self._key, data, timeout=self._timeout)
|
|
224
|
-
|
|
225
|
-
def _update_task(
|
|
226
|
-
self,
|
|
227
|
-
name: str,
|
|
228
|
-
status: ProgressStatus,
|
|
229
|
-
message: str,
|
|
230
|
-
progress: int
|
|
231
|
-
) -> None:
|
|
232
|
-
with self._lock:
|
|
233
|
-
state = self._get_state()
|
|
234
|
-
|
|
235
|
-
state.tasks[name] = TaskState(
|
|
236
|
-
message=message,
|
|
237
|
-
progress=progress,
|
|
238
|
-
status=status,
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
state.message = message
|
|
242
|
-
state.progress = self._calculate_overall_progress(state)
|
|
243
|
-
state.status = ProgressStatus.PROCESSING
|
|
244
|
-
|
|
245
|
-
self._save_state(state)
|
|
File without changes
|
|
File without changes
|