django-spire 0.23.5__py3-none-any.whl → 0.23.7__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 CHANGED
@@ -1,4 +1,4 @@
1
- __VERSION__ = '0.23.5'
1
+ __VERSION__ = '0.23.7'
2
2
 
3
3
  MAINTENANCE_MODE_SETTINGS_NAME = 'MAINTENANCE_MODE'
4
4
 
@@ -4,7 +4,6 @@ from django_spire.contrib.progress.runner import TaskProgressUpdater
4
4
  from django_spire.contrib.progress.states import TaskState, TrackerState
5
5
  from django_spire.contrib.progress.task import ProgressMessages, Task, TaskResult
6
6
  from django_spire.contrib.progress.tracker import ProgressTracker
7
- from django_spire.contrib.progress.views import sse_stream_view
8
7
 
9
8
 
10
9
  __all__ = [
@@ -16,6 +15,5 @@ __all__ = [
16
15
  'TaskProgressUpdater',
17
16
  'TaskResult',
18
17
  'TaskState',
19
- 'TrackerState',
20
- 'sse_stream_view',
18
+ 'TrackerState'
21
19
  ]
@@ -1,61 +1,86 @@
1
1
  class ProgressStream {
2
2
  constructor(url, config = {}) {
3
3
  this.url = url;
4
- this.event_source = null;
4
+ this.is_running = false;
5
+ this.poll_interval = null;
6
+
5
7
  this.config = {
6
8
  on_update: config.on_update || (() => {}),
7
9
  on_complete: config.on_complete || (() => {}),
8
10
  on_error: config.on_error || (() => {}),
9
11
  redirect_on_complete: config.redirect_on_complete || null,
10
- redirect_delay: config.redirect_delay || 1000
12
+ redirect_delay: config.redirect_delay || 1000,
13
+ poll_interval: config.poll_interval || 1000
11
14
  };
12
15
  }
13
16
 
14
- start() {
15
- this.event_source = new EventSource(this.url);
17
+ async start() {
18
+ if (this.is_running) return;
19
+
20
+ this.is_running = true;
21
+ this._start_polling();
22
+ }
23
+
24
+ stop() {
25
+ if (!this.is_running) return;
26
+
27
+ this.is_running = false;
28
+
29
+ if (this.poll_interval) {
30
+ clearInterval(this.poll_interval);
31
+ this.poll_interval = null;
32
+ }
33
+ }
34
+
35
+ _start_polling() {
36
+ this._poll();
37
+
38
+ this.poll_interval = setInterval(() => {
39
+ if (this.is_running) {
40
+ this._poll();
41
+ }
42
+ }, this.config.poll_interval);
43
+ }
16
44
 
17
- this.event_source.onmessage = (event) => {
18
- const data = JSON.parse(event.data);
45
+ async _poll() {
46
+ try {
47
+ let response = await fetch(this.url, {
48
+ method: 'POST',
49
+ headers: {
50
+ 'X-CSRFToken': get_cookie('csrftoken'),
51
+ }
52
+ });
53
+
54
+ if (!response.ok) {
55
+ throw new Error(`HTTP ${response.status}`);
56
+ }
57
+
58
+ let data = await response.json();
19
59
 
20
60
  this.config.on_update(data);
21
61
 
22
62
  if (data.step === 'error') {
23
63
  this.config.on_error(data);
24
64
  this.stop();
25
-
26
65
  return;
27
66
  }
28
67
 
29
68
  if (data.progress >= 100) {
30
69
  this.config.on_complete(data);
31
70
  this.stop();
32
-
33
- if (this.config.redirect_on_complete) {
34
- setTimeout(() => {
35
- window.location.href = this.config.redirect_on_complete;
36
- }, this.config.redirect_delay);
37
- }
71
+ this._redirect();
38
72
  }
39
- };
40
-
41
- this.event_source.onerror = (error) => {
42
- console.error('ProgressStream error:', error);
43
-
44
- this.config.on_error({
45
- step: 'error',
46
- message: 'Connection error',
47
- progress: 0
48
- });
49
-
50
- this.stop();
51
- };
73
+ } catch (error) {
74
+ console.warn('Poll error:', error.message);
75
+ }
52
76
  }
53
77
 
54
- stop() {
55
- if (this.event_source) {
56
- this.event_source.close();
57
- this.event_source = null;
58
- }
78
+ _redirect() {
79
+ if (!this.config.redirect_on_complete) return;
80
+
81
+ setTimeout(() => {
82
+ window.location.href = this.config.redirect_on_complete;
83
+ }, this.config.redirect_delay);
59
84
  }
60
85
  }
61
86
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-spire
3
- Version: 0.23.5
3
+ Version: 0.23.7
4
4
  Summary: A project for Django Spire
5
5
  Author-email: Brayden Carlson <braydenc@stratusadv.com>, Nathan Johnson <nathanj@stratusadv.com>
6
6
  License: Copyright (c) 2024 Stratus Advanced Technologies and Contributors.
@@ -1,6 +1,6 @@
1
1
  django_spire/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  django_spire/conf.py,sha256=c5Hs-7lk9T15254tOasiQ2ZTFLQIVJof9_QJDfm1PAI,933
3
- django_spire/consts.py,sha256=YR9sX6FXBUPmKDLHD7b1mDlQp6jwxIOoAF39q4rJoiM,171
3
+ django_spire/consts.py,sha256=ab54JzPcaJsVKbgkIP9oB2Dn-58eOdewXxUUZlbYeRw,171
4
4
  django_spire/exceptions.py,sha256=L5ndRO5ftMmh0pHkO2z_NG3LSGZviJ-dDHNT73SzTNw,48
5
5
  django_spire/settings.py,sha256=B4GPqBGt_dmkt0Ay0j-IP-SZ6mY44m2Ap5kVSON5YLA,1005
6
6
  django_spire/urls.py,sha256=mKeZszb5U4iIGqddMb5Tt5fRC72U2wABEOi6mvOfEBU,656
@@ -348,16 +348,15 @@ django_spire/contrib/pagination/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TI
348
348
  django_spire/contrib/pagination/templatetags/pagination_tags.py,sha256=xE3zTDJZDdDvWf6D7fMyg_Vi0PW37OA3aZxCjOHDp4I,1173
349
349
  django_spire/contrib/performance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
350
350
  django_spire/contrib/performance/decorators.py,sha256=xOjXX-3FVOEWqr2TTTqdh4lkIhmHTlROevOVgXDeyt8,507
351
- django_spire/contrib/progress/__init__.py,sha256=YEXnnbomc32c2J7e3sXHjtLboZ-PijiZOguoVHPfoUQ,719
351
+ django_spire/contrib/progress/__init__.py,sha256=pvd6_7puMDWStjZqcwsWEulpeGupQPv2fladV1IiaXg,631
352
352
  django_spire/contrib/progress/enums.py,sha256=nwyfKxQno_ghGGTbpK8lRO1cY7dCfbxnnsQ4cSbkXBw,194
353
353
  django_spire/contrib/progress/mixins.py,sha256=V84LRtniFDLK2E5dKcSZNh-a1zTf9Ee7QXg1rDfoFHo,895
354
354
  django_spire/contrib/progress/runner.py,sha256=3jSqpdk2MJ7HPNutwF43Hs81O0NlzX4E9gnVaenhXUs,4445
355
355
  django_spire/contrib/progress/states.py,sha256=V7JU_nzpZN77TQFW0S51Gjil8xn8CKLKkAWDOpqzA8k,1908
356
356
  django_spire/contrib/progress/task.py,sha256=4pMbzOsjRRNqPKNohh08D39DRFpC3QomTCH5IdqQS0A,780
357
357
  django_spire/contrib/progress/tracker.py,sha256=xjGlEyKe5aiKW_KC8NKZ8F0BjQdFpLUqHz5Gr_C7jws,6490
358
- django_spire/contrib/progress/views.py,sha256=CwJu-L09tTiCpsjVuqzvMEKVqp6-etLDi7L9ibmlq8U,1694
359
358
  django_spire/contrib/progress/static/django_spire/css/contrib/progress/progress.css,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
360
- django_spire/contrib/progress/static/django_spire/js/contrib/progress/progress.js,sha256=2Zuwiu1nWJpghzU-6fmf6s4qKLLyiZUcAJwB9-v5xWI,1701
359
+ django_spire/contrib/progress/static/django_spire/js/contrib/progress/progress.js,sha256=BeT52b8rHoBzk3tMpXX3bO3R-bDYTjyrEHjjc1AS2LQ,2228
361
360
  django_spire/contrib/progress/templates/django_spire/contrib/progress/card/card.html,sha256=2Ajv0UBJEGwc4InVe69aLe2u4K56yh2JAuvO6uH24j8,3353
362
361
  django_spire/contrib/progress/templates/django_spire/contrib/progress/modal/content.html,sha256=Qxer-q4GBC5h8onzno2NUlPrXluw_tO1kWlGComQ98E,5232
363
362
  django_spire/contrib/queryset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1201,8 +1200,8 @@ django_spire/theme/urls/page_urls.py,sha256=S8nkKkgbhG3XHI3uMUL-piOjXIrRkuY2UlM_
1201
1200
  django_spire/theme/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1202
1201
  django_spire/theme/views/json_views.py,sha256=PWwVTaty0BVGbj65L5cxex6JNhc-xVAI_rEYjbJWqEM,1893
1203
1202
  django_spire/theme/views/page_views.py,sha256=WenjOa6Welpu3IMolY56ZwBjy4aK9hpbiMNuygjAl1A,3922
1204
- django_spire-0.23.5.dist-info/licenses/LICENSE.md,sha256=tlTbOtgKoy-xAQpUk9gPeh9O4oRXCOzoWdW3jJz0wnA,1091
1205
- django_spire-0.23.5.dist-info/METADATA,sha256=NsK-z_MdElXotF4ZnWMERuHNpLZODw6_kRI_lO7bt-Q,5127
1206
- django_spire-0.23.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1207
- django_spire-0.23.5.dist-info/top_level.txt,sha256=xf3QV1e--ONkVpgMDQE9iqjQ1Vg4--_6C8wmO-KxPHQ,13
1208
- django_spire-0.23.5.dist-info/RECORD,,
1203
+ django_spire-0.23.7.dist-info/licenses/LICENSE.md,sha256=tlTbOtgKoy-xAQpUk9gPeh9O4oRXCOzoWdW3jJz0wnA,1091
1204
+ django_spire-0.23.7.dist-info/METADATA,sha256=NY3tdv-HT8M7IrLFbnRDxJtMCYxoanCmUPR7PDyy0Uk,5127
1205
+ django_spire-0.23.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1206
+ django_spire-0.23.7.dist-info/top_level.txt,sha256=xf3QV1e--ONkVpgMDQE9iqjQ1Vg4--_6C8wmO-KxPHQ,13
1207
+ django_spire-0.23.7.dist-info/RECORD,,
@@ -1,64 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import time
5
-
6
- from typing import TYPE_CHECKING
7
-
8
- from django.core.cache import cache
9
- from django.http import StreamingHttpResponse
10
-
11
- if TYPE_CHECKING:
12
- from typing import Callable, Generator
13
-
14
-
15
- def sse_stream_view(
16
- key: str,
17
- interval: float = 0.5,
18
- should_continue: Callable[[dict], bool] | None = None,
19
- timeout: int = 300
20
- ) -> StreamingHttpResponse:
21
- cache_key = f'progress_tracker_{key}'
22
-
23
- def event_stream() -> Generator[str, None, None]:
24
- previous_data = None
25
- start_time = time.time()
26
- last_heartbeat = time.time()
27
-
28
- yield ': connected\n\n'
29
-
30
- while True:
31
- elapsed = time.time() - start_time
32
-
33
- if elapsed > timeout:
34
- yield f'data: {json.dumps({"step": "error", "message": "Timeout", "progress": 0})}\n\n'
35
- break
36
-
37
- data = cache.get(cache_key)
38
-
39
- if data and data != previous_data:
40
- yield f'data: {json.dumps(data)}\n\n'
41
- previous_data = data
42
- last_heartbeat = time.time()
43
-
44
- if should_continue and not should_continue(data):
45
- break
46
-
47
- if data.get('progress', 0) >= 100 or data.get('step') == 'error':
48
- break
49
-
50
- if time.time() - last_heartbeat > 10:
51
- yield ': heartbeat\n\n'
52
- last_heartbeat = time.time()
53
-
54
- time.sleep(interval)
55
-
56
- response = StreamingHttpResponse(
57
- event_stream(),
58
- content_type='text/event-stream'
59
- )
60
-
61
- response['Cache-Control'] = 'no-cache, no-store'
62
- response['X-Accel-Buffering'] = 'no'
63
-
64
- return response