• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

iplweb / bpp / 18634744198

19 Oct 2025 07:00PM UTC coverage: 31.618% (-29.9%) from 61.514%
18634744198

push

github

mpasternak
Merge branch 'release/v202510.1270'

657 of 9430 branches covered (6.97%)

Branch coverage included in aggregate %.

229 of 523 new or added lines in 42 files covered. (43.79%)

11303 existing lines in 316 files now uncovered.

14765 of 39346 relevant lines covered (37.53%)

0.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.0
src/pbn_export_queue/views.py
1
import re
1✔
2
import sys
1✔
3

4
import rollbar
1✔
5
from django.contrib import messages
1✔
6
from django.contrib.auth.decorators import login_required
1✔
7
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
1✔
8
from django.db.models import F
1✔
9
from django.db.models.functions import Coalesce
1✔
10
from django.http import HttpResponseRedirect, JsonResponse
1✔
11
from django.shortcuts import get_object_or_404
1✔
12
from django.urls import reverse_lazy
1✔
13
from django.utils.safestring import mark_safe
1✔
14
from django.views import View
1✔
15
from django.views.decorators.http import require_POST
1✔
16
from django.views.generic import DetailView, ListView
1✔
17

18
from bpp.const import GR_WPROWADZANIE_DANYCH
1✔
19

20
from .models import PBN_Export_Queue
1✔
21

22

23
class PBNExportQueuePermissionMixin(UserPassesTestMixin):
1✔
24
    """Mixin for permission checking - user must be staff or have GR_WPROWADZANIE_DANYCH group"""
25

26
    def test_func(self):
1✔
UNCOV
27
        user = self.request.user
×
UNCOV
28
        return user.is_staff or user.groups.filter(name=GR_WPROWADZANIE_DANYCH).exists()
×
29

30

31
class PBNExportQueueListView(
1✔
32
    LoginRequiredMixin, PBNExportQueuePermissionMixin, ListView
33
):
34
    """ListView for PBN Export Queue with filtering by success status"""
35

36
    model = PBN_Export_Queue
1✔
37
    template_name = "pbn_export_queue/pbn_export_queue_list.html"
1✔
38
    context_object_name = "export_queue_items"
1✔
39
    paginate_by = 25
1✔
40

41
    def get_queryset(self):
1✔
UNCOV
42
        queryset = super().get_queryset()
×
43

44
        # Annotate with ostatnia_aktualizacja for sorting
UNCOV
45
        queryset = queryset.annotate(
×
46
            ostatnia_aktualizacja_sort=Coalesce(
47
                F("wysylke_zakonczono"), F("wysylke_podjeto"), F("zamowiono")
48
            )
49
        )
50

51
        # Filter by zakonczono_pomyslnie if parameter provided
UNCOV
52
        success_filter = self.request.GET.get("zakonczono_pomyslnie")
×
UNCOV
53
        if success_filter == "true":
×
UNCOV
54
            queryset = queryset.filter(zakonczono_pomyslnie=True)
×
UNCOV
55
        elif success_filter == "false":
×
UNCOV
56
            queryset = queryset.filter(zakonczono_pomyslnie=False)
×
UNCOV
57
        elif success_filter == "none":
×
UNCOV
58
            queryset = queryset.filter(zakonczono_pomyslnie=None)
×
59

60
        # Handle title search
UNCOV
61
        search_query = self.request.GET.get("q")
×
UNCOV
62
        if search_query:
×
63
            # Import Q for complex queries
UNCOV
64
            from django.db.models import Q
×
65

66
            # Search in cached description and original title of the related record
67
            # We need to get the content types for the models we're interested in
68
            # Create filters for searching in related models
69
            # Since we're using GenericForeignKey, we need to filter differently
70
            # We'll search for the query in the komunikat field which contains publication info
71
            # and also try to match against object IDs if they exist
UNCOV
72
            queryset = queryset.filter(Q(komunikat__icontains=search_query))
×
73

74
            # Additionally, we can filter by checking the actual related objects
75
            # This requires more complex filtering through the generic relation
UNCOV
76
            matching_ids = []
×
77

78
            # Get all content types that might be in the queue
UNCOV
79
            for item in queryset.select_related("content_type"):
×
UNCOV
80
                if item.rekord_do_wysylki:
×
UNCOV
81
                    try:
×
UNCOV
82
                        record = item.rekord_do_wysylki
×
83
                        # Check if record has title fields
UNCOV
84
                        if (
×
85
                            hasattr(record, "tytul_oryginalny")
86
                            and record.tytul_oryginalny
87
                        ):
UNCOV
88
                            if search_query.lower() in record.tytul_oryginalny.lower():
×
89
                                matching_ids.append(item.pk)
×
90
                        elif (
×
91
                            hasattr(record, "opis_bibliograficzny_cache")
92
                            and record.opis_bibliograficzny_cache
93
                        ):
94
                            if (
×
95
                                search_query.lower()
96
                                in record.opis_bibliograficzny_cache.lower()
97
                            ):
98
                                matching_ids.append(item.pk)
×
99
                    except BaseException:
×
100
                        pass
×
101

102
            # If we found matching IDs, filter by them
UNCOV
103
            if matching_ids:
×
104
                queryset = queryset.filter(
×
105
                    Q(pk__in=matching_ids) | Q(komunikat__icontains=search_query)
106
                )
107

108
        # Handle sorting
UNCOV
109
        sort_by = self.request.GET.get(
×
110
            "sort", "-ostatnia_aktualizacja"
111
        )  # Default to newest update first
UNCOV
112
        allowed_sorts = {
×
113
            "pk": "pk",
114
            "-pk": "-pk",
115
            "zamowiono": "zamowiono",
116
            "-zamowiono": "-zamowiono",
117
            "ostatnia_aktualizacja": "ostatnia_aktualizacja_sort",
118
            "-ostatnia_aktualizacja": "-ostatnia_aktualizacja_sort",
119
            "ilosc_prob": "ilosc_prob",
120
            "-ilosc_prob": "-ilosc_prob",
121
            "zakonczono_pomyslnie": "zakonczono_pomyslnie",
122
            "-zakonczono_pomyslnie": "-zakonczono_pomyslnie",
123
        }
UNCOV
124
        if sort_by in allowed_sorts:
×
UNCOV
125
            queryset = queryset.order_by(allowed_sorts[sort_by])
×
126

UNCOV
127
        return queryset.select_related("zamowil", "content_type")
×
128

129
    def get_context_data(self, **kwargs):
1✔
UNCOV
130
        context = super().get_context_data(**kwargs)
×
UNCOV
131
        context["current_filter"] = self.request.GET.get("zakonczono_pomyslnie", "all")
×
UNCOV
132
        context["search_query"] = self.request.GET.get("q", "")
×
133
        # Add count of error records for the resend button
UNCOV
134
        context["error_count"] = PBN_Export_Queue.objects.filter(
×
135
            zakonczono_pomyslnie=False
136
        ).count()
137
        # Add count of waiting records for the resend button
UNCOV
138
        context["waiting_count"] = PBN_Export_Queue.objects.filter(
×
139
            retry_after_user_authorised=True
140
        ).count()
141
        # Add count of never sent records for the wake up button
UNCOV
142
        context["never_sent_count"] = PBN_Export_Queue.objects.filter(
×
143
            wysylke_podjeto=None,
144
            wysylke_zakonczono=None,
145
        ).count()
146
        # Add counts for filter buttons
UNCOV
147
        context["total_count"] = PBN_Export_Queue.objects.count()
×
UNCOV
148
        context["success_count"] = PBN_Export_Queue.objects.filter(
×
149
            zakonczono_pomyslnie=True
150
        ).count()
UNCOV
151
        context["pending_count"] = PBN_Export_Queue.objects.filter(
×
152
            zakonczono_pomyslnie=None
153
        ).count()
154
        # Add current sort parameter
UNCOV
155
        context["current_sort"] = self.request.GET.get("sort", "-ostatnia_aktualizacja")
×
UNCOV
156
        return context
×
157

158

159
class PBNExportQueueTableView(
1✔
160
    LoginRequiredMixin, PBNExportQueuePermissionMixin, ListView
161
):
162
    """Table-only view for HTMX auto-refresh"""
163

164
    model = PBN_Export_Queue
1✔
165
    template_name = "pbn_export_queue/pbn_export_queue_table.html"
1✔
166
    context_object_name = "export_queue_items"
1✔
167
    paginate_by = 25
1✔
168

169
    def get_queryset(self):
1✔
UNCOV
170
        queryset = super().get_queryset()
×
171

172
        # Annotate with ostatnia_aktualizacja for sorting
UNCOV
173
        queryset = queryset.annotate(
×
174
            ostatnia_aktualizacja_sort=Coalesce(
175
                F("wysylke_zakonczono"), F("wysylke_podjeto"), F("zamowiono")
176
            )
177
        )
178

179
        # Filter by zakonczono_pomyslnie if parameter provided
UNCOV
180
        success_filter = self.request.GET.get("zakonczono_pomyslnie")
×
UNCOV
181
        if success_filter == "true":
×
182
            queryset = queryset.filter(zakonczono_pomyslnie=True)
×
UNCOV
183
        elif success_filter == "false":
×
184
            queryset = queryset.filter(zakonczono_pomyslnie=False)
×
UNCOV
185
        elif success_filter == "none":
×
186
            queryset = queryset.filter(zakonczono_pomyslnie=None)
×
187

188
        # Handle title search
UNCOV
189
        search_query = self.request.GET.get("q")
×
UNCOV
190
        if search_query:
×
191
            # Import Q for complex queries
192
            from django.db.models import Q
×
193

194
            # Search in cached description and original title of the related record
195
            # We need to get the content types for the models we're interested in
196
            # Create filters for searching in related models
197
            # Since we're using GenericForeignKey, we need to filter differently
198
            # We'll search for the query in the komunikat field which contains publication info
199
            # and also try to match against object IDs if they exist
200
            queryset = queryset.filter(Q(komunikat__icontains=search_query))
×
201

202
            # Additionally, we can filter by checking the actual related objects
203
            # This requires more complex filtering through the generic relation
204
            matching_ids = []
×
205

206
            # Get all content types that might be in the queue
207
            for item in queryset.select_related("content_type"):
×
208
                if item.rekord_do_wysylki:
×
209
                    try:
×
210
                        record = item.rekord_do_wysylki
×
211
                        # Check if record has title fields
212
                        if (
×
213
                            hasattr(record, "tytul_oryginalny")
214
                            and record.tytul_oryginalny
215
                        ):
216
                            if search_query.lower() in record.tytul_oryginalny.lower():
×
217
                                matching_ids.append(item.pk)
×
218
                        elif (
×
219
                            hasattr(record, "opis_bibliograficzny_cache")
220
                            and record.opis_bibliograficzny_cache
221
                        ):
222
                            if (
×
223
                                search_query.lower()
224
                                in record.opis_bibliograficzny_cache.lower()
225
                            ):
226
                                matching_ids.append(item.pk)
×
227
                    except BaseException:
×
228
                        pass
×
229

230
            # If we found matching IDs, filter by them
231
            if matching_ids:
×
232
                queryset = queryset.filter(
×
233
                    Q(pk__in=matching_ids) | Q(komunikat__icontains=search_query)
234
                )
235

236
        # Handle sorting
UNCOV
237
        sort_by = self.request.GET.get(
×
238
            "sort", "-ostatnia_aktualizacja"
239
        )  # Default to newest update first
UNCOV
240
        allowed_sorts = {
×
241
            "pk": "pk",
242
            "-pk": "-pk",
243
            "zamowiono": "zamowiono",
244
            "-zamowiono": "-zamowiono",
245
            "ostatnia_aktualizacja": "ostatnia_aktualizacja_sort",
246
            "-ostatnia_aktualizacja": "-ostatnia_aktualizacja_sort",
247
            "ilosc_prob": "ilosc_prob",
248
            "-ilosc_prob": "-ilosc_prob",
249
            "zakonczono_pomyslnie": "zakonczono_pomyslnie",
250
            "-zakonczono_pomyslnie": "-zakonczono_pomyslnie",
251
        }
UNCOV
252
        if sort_by in allowed_sorts:
×
UNCOV
253
            queryset = queryset.order_by(allowed_sorts[sort_by])
×
254

UNCOV
255
        return queryset.select_related("zamowil", "content_type")
×
256

257
    def get_context_data(self, **kwargs):
1✔
UNCOV
258
        context = super().get_context_data(**kwargs)
×
UNCOV
259
        context["current_filter"] = self.request.GET.get("zakonczono_pomyslnie", "all")
×
UNCOV
260
        context["search_query"] = self.request.GET.get("q", "")
×
261
        # Add count of error records for the resend button
UNCOV
262
        context["error_count"] = PBN_Export_Queue.objects.filter(
×
263
            zakonczono_pomyslnie=False
264
        ).count()
265
        # Add count of waiting records for the resend button
UNCOV
266
        context["waiting_count"] = PBN_Export_Queue.objects.filter(
×
267
            retry_after_user_authorised=True
268
        ).count()
269
        # Add count of never sent records for the wake up button
UNCOV
270
        context["never_sent_count"] = PBN_Export_Queue.objects.filter(
×
271
            wysylke_podjeto=None,
272
            wysylke_zakonczono=None,
273
        ).count()
274
        # Add counts for filter buttons
UNCOV
275
        context["total_count"] = PBN_Export_Queue.objects.count()
×
UNCOV
276
        context["success_count"] = PBN_Export_Queue.objects.filter(
×
277
            zakonczono_pomyslnie=True
278
        ).count()
UNCOV
279
        context["pending_count"] = PBN_Export_Queue.objects.filter(
×
280
            zakonczono_pomyslnie=None
281
        ).count()
282
        # Add current sort parameter
UNCOV
283
        context["current_sort"] = self.request.GET.get("sort", "-ostatnia_aktualizacja")
×
UNCOV
284
        return context
×
285

286

287
class PBNExportQueueDetailView(
1✔
288
    LoginRequiredMixin, PBNExportQueuePermissionMixin, DetailView
289
):
290
    """DetailView for PBN Export Queue showing logs and action buttons"""
291

292
    model = PBN_Export_Queue
1✔
293
    template_name = "pbn_export_queue/pbn_export_queue_detail.html"
1✔
294
    context_object_name = "export_queue_item"
1✔
295

296
    def get_queryset(self):
1✔
UNCOV
297
        return super().get_queryset().select_related("zamowil", "content_type")
×
298

299
    def parse_komunikat_links(self, komunikat):
1✔
300
        """Parse the komunikat field to extract and format links"""
UNCOV
301
        if not komunikat:
×
302
            return None
×
303

304
        # Find the SentData link
UNCOV
305
        sentdata_match = re.search(
×
306
            r'href="(/admin/pbn_api/sentdata/\d+/change/)"', komunikat
307
        )
308

UNCOV
309
        links = {}
×
UNCOV
310
        if sentdata_match:
×
UNCOV
311
            links["sentdata_url"] = sentdata_match.group(1)
×
312

313
            # Try to extract PBN publication ID from the komunikat
314
            # Looking for patterns like "PBN ID: xxx" or similar
UNCOV
315
            pbn_match = re.search(r"publication/([a-f0-9-]+)", komunikat)
×
UNCOV
316
            if pbn_match:
×
UNCOV
317
                links["pbn_uid"] = pbn_match.group(1)
×
UNCOV
318
                links["pbn_url"] = (
×
319
                    f"https://pbn.nauka.gov.pl/works/publication/{pbn_match.group(1)}"
320
                )
321

322
        # Check if this was successful
UNCOV
323
        if "Wysłano poprawnie" in komunikat:
×
324
            links["success"] = True
×
325
        else:
UNCOV
326
            links["success"] = False
×
327

328
        # Make the komunikat HTML-safe but preserve existing HTML
UNCOV
329
        links["formatted_komunikat"] = mark_safe(komunikat.replace("\n", "<br>"))
×
330

UNCOV
331
        return links
×
332

333
    def get_context_data(self, **kwargs):
1✔
UNCOV
334
        context = super().get_context_data(**kwargs)
×
335

336
        # Parse komunikat for links if the record was sent successfully
UNCOV
337
        if self.object.komunikat:
×
UNCOV
338
            context["parsed_links"] = self.parse_komunikat_links(self.object.komunikat)
×
339

340
        # Try to get the related SentData if it exists
UNCOV
341
        if self.object.zakonczono_pomyslnie:
×
342
            try:
×
343
                from pbn_api.models.sentdata import SentData
×
344

345
                sent_data = SentData.objects.get(
×
346
                    content_type=self.object.content_type,
347
                    object_id=self.object.object_id,
348
                )
349
                context["sent_data"] = sent_data
×
350
                if sent_data.pbn_uid_id:
×
351
                    # pbn_uid is a ForeignKey to Publication, so we need to use pbn_uid_id or pbn_uid.pbn_uid
352
                    context["pbn_publication_url"] = (
×
353
                        f"https://pbn.nauka.gov.pl/works/publication/{sent_data.pbn_uid_id}"
354
                    )
355
            except SentData.DoesNotExist:
×
356
                pass
×
357

358
        # Generate admin URL for the record if it exists
UNCOV
359
        if self.object.rekord_do_wysylki:
×
360
            from django.urls import reverse
×
361

362
            content_type = self.object.content_type
×
363
            if content_type:
×
364
                try:
×
365
                    # Generate admin change URL for the specific model
366
                    admin_url = reverse(
×
367
                        f"admin:{content_type.app_label}_{content_type.model}_change",
368
                        args=[self.object.object_id],
369
                    )
370
                    context["record_admin_url"] = admin_url
×
371
                except BaseException:
×
372
                    # If URL pattern doesn't exist, skip it
373
                    pass
×
374

UNCOV
375
        return context
×
376

377

378
@login_required
1✔
379
@require_POST
1✔
380
def resend_to_pbn(request, pk):
1✔
381
    """View to resend an export queue item to PBN (combines prepare and send)"""
UNCOV
382
    if not (
×
383
        request.user.is_staff
384
        or request.user.groups.filter(name=GR_WPROWADZANIE_DANYCH).exists()
385
    ):
UNCOV
386
        messages.error(request, "Brak uprawnień do wykonania tej operacji.")
×
UNCOV
387
        return HttpResponseRedirect(
×
388
            reverse_lazy("pbn_export_queue:export-queue-detail", args=[pk])
389
        )
390

UNCOV
391
    queue_item = get_object_or_404(PBN_Export_Queue, pk=pk)
×
392
    # First prepare for resend
UNCOV
393
    queue_item.prepare_for_resend(
×
394
        user=request.user, message_suffix=f" przez {request.user}"
395
    )
396
    # Then trigger the send
UNCOV
397
    queue_item.sprobuj_wyslac_do_pbn()
×
UNCOV
398
    messages.success(request, "Przygotowano i zlecono ponowną wysyłkę do PBN.")
×
UNCOV
399
    return HttpResponseRedirect(
×
400
        reverse_lazy("pbn_export_queue:export-queue-detail", args=[pk])
401
    )
402

403

404
# Keep old views for backward compatibility but they won't be used in new UI
405
@login_required
1✔
406
@require_POST
1✔
407
def prepare_for_resend(request, pk):
1✔
408
    """View to prepare an export queue item for resending - DEPRECATED"""
UNCOV
409
    if not (
×
410
        request.user.is_staff
411
        or request.user.groups.filter(name=GR_WPROWADZANIE_DANYCH).exists()
412
    ):
UNCOV
413
        messages.error(request, "Brak uprawnień do wykonania tej operacji.")
×
UNCOV
414
        return HttpResponseRedirect(
×
415
            reverse_lazy("pbn_export_queue:export-queue-detail", args=[pk])
416
        )
417

UNCOV
418
    queue_item = get_object_or_404(PBN_Export_Queue, pk=pk)
×
UNCOV
419
    queue_item.prepare_for_resend(
×
420
        user=request.user, message_suffix=f" przez {request.user}"
421
    )
UNCOV
422
    messages.success(request, "Przygotowano rekord do ponownej wysyłki.")
×
UNCOV
423
    return HttpResponseRedirect(
×
424
        reverse_lazy("pbn_export_queue:export-queue-detail", args=[pk])
425
    )
426

427

428
@login_required
1✔
429
@require_POST
1✔
430
def try_send_to_pbn(request, pk):
1✔
431
    """View to trigger sending an export queue item to PBN - DEPRECATED"""
UNCOV
432
    if not (
×
433
        request.user.is_staff
434
        or request.user.groups.filter(name=GR_WPROWADZANIE_DANYCH).exists()
435
    ):
UNCOV
436
        messages.error(request, "Brak uprawnień do wykonania tej operacji.")
×
UNCOV
437
        return HttpResponseRedirect(
×
438
            reverse_lazy("pbn_export_queue:export-queue-detail", args=[pk])
439
        )
440

UNCOV
441
    queue_item = get_object_or_404(PBN_Export_Queue, pk=pk)
×
UNCOV
442
    queue_item.sprobuj_wyslac_do_pbn()
×
UNCOV
443
    messages.success(request, "Zlecono ponowną wysyłkę do PBN.")
×
UNCOV
444
    return HttpResponseRedirect(
×
445
        reverse_lazy("pbn_export_queue:export-queue-detail", args=[pk])
446
    )
447

448

449
@login_required
1✔
450
@require_POST
1✔
451
def resend_all_waiting(request):
1✔
452
    """View to resend all export queue items waiting for authorization"""
UNCOV
453
    if not (
×
454
        request.user.is_staff
455
        or request.user.groups.filter(name=GR_WPROWADZANIE_DANYCH).exists()
456
    ):
UNCOV
457
        messages.error(request, "Brak uprawnień do wykonania tej operacji.")
×
UNCOV
458
        return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
459

460
    # Get all items waiting for authorization (retry_after_user_authorised=True)
UNCOV
461
    waiting_items = PBN_Export_Queue.objects.filter(retry_after_user_authorised=True)
×
UNCOV
462
    count = waiting_items.count()
×
463

UNCOV
464
    if count == 0:
×
UNCOV
465
        messages.warning(request, "Brak rekordów oczekujących na autoryzację.")
×
UNCOV
466
        return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
467

468
    # Process each waiting item
UNCOV
469
    for queue_item in waiting_items:
×
UNCOV
470
        try:
×
471
            # Prepare for resend
UNCOV
472
            queue_item.prepare_for_resend(
×
473
                user=request.user,
474
                message_suffix=f" przez {request.user} (masowa wysyłka oczekujących)",
475
            )
476
            # Trigger the send
UNCOV
477
            queue_item.sprobuj_wyslac_do_pbn()
×
478
        except Exception:
×
479
            # Log the error but continue processing other items
480
            rollbar.report_exc_info(sys.exc_info())
×
481
            continue
×
482

UNCOV
483
    messages.success(
×
484
        request,
485
        f"Przygotowano i zlecono ponowną wysyłkę {count} rekordów oczekujących na autoryzację.",
486
    )
UNCOV
487
    return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
488

489

490
@login_required
1✔
491
@require_POST
1✔
492
def resend_all_errors(request):
1✔
493
    """View to resend all export queue items with error status"""
UNCOV
494
    if not (
×
495
        request.user.is_staff
496
        or request.user.groups.filter(name=GR_WPROWADZANIE_DANYCH).exists()
497
    ):
UNCOV
498
        messages.error(request, "Brak uprawnień do wykonania tej operacji.")
×
UNCOV
499
        return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
500

501
    # Get all items with errors (zakonczono_pomyslnie=False)
UNCOV
502
    error_items = PBN_Export_Queue.objects.filter(zakonczono_pomyslnie=False)
×
UNCOV
503
    count = error_items.count()
×
504

UNCOV
505
    if count == 0:
×
UNCOV
506
        messages.warning(request, "Brak rekordów z błędami do ponownej wysyłki.")
×
UNCOV
507
        return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
508

509
    # Process each error item
UNCOV
510
    for queue_item in error_items:
×
UNCOV
511
        try:
×
512
            # Prepare for resend
UNCOV
513
            queue_item.prepare_for_resend(
×
514
                user=request.user,
515
                message_suffix=f" przez {request.user} (masowa wysyłka błędów)",
516
            )
517
            # Trigger the send
UNCOV
518
            queue_item.sprobuj_wyslac_do_pbn()
×
519
        except Exception:
×
520
            # Log the error but continue processing other items
521
            rollbar.report_exc_info(sys.exc_info())
×
522
            continue
×
523

UNCOV
524
    messages.success(
×
525
        request, f"Przygotowano i zlecono ponowną wysyłkę {count} rekordów z błędami."
526
    )
UNCOV
527
    return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
528

529

530
@login_required
1✔
531
@require_POST
1✔
532
def wake_up_queue(request):
1✔
533
    """View to wake up and start sending all export queue items that were never attempted"""
534
    if not (
×
535
        request.user.is_staff
536
        or request.user.groups.filter(name=GR_WPROWADZANIE_DANYCH).exists()
537
    ):
538
        messages.error(request, "Brak uprawnień do wykonania tej operacji.")
×
539
        return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
540

541
    # Get all items that were never attempted to send
542
    # (wysylke_podjeto=None means sending was never started)
543
    never_sent_items = PBN_Export_Queue.objects.filter(
×
544
        wysylke_podjeto=None,
545
        wysylke_zakonczono=None,
546
    )
547
    count = never_sent_items.count()
×
548

549
    if count == 0:
×
550
        messages.warning(request, "Brak rekordów oczekujących na pierwszą wysyłkę.")
×
551
        return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
552

553
    # Process each never-sent item
554
    from pbn_export_queue.tasks import task_sprobuj_wyslac_do_pbn
×
555

556
    for queue_item in never_sent_items:
×
557
        try:
×
558
            # Trigger the send directly with Celery task
559
            task_sprobuj_wyslac_do_pbn.delay(queue_item.pk)
×
560
        except Exception:
×
561
            # Log the error but continue processing other items
562
            rollbar.report_exc_info(sys.exc_info())
×
563
            continue
×
564

565
    messages.success(
×
566
        request,
567
        f"Obudzono wysyłkę dla {count} rekordów które nigdy nie były wysyłane.",
568
    )
569
    return HttpResponseRedirect(reverse_lazy("pbn_export_queue:export-queue-list"))
×
570

571

572
class PBNExportQueueCountsView(LoginRequiredMixin, PBNExportQueuePermissionMixin, View):
1✔
573
    """JSON view that returns current counts for filter buttons"""
574

575
    def get(self, request, *args, **kwargs):
1✔
576
        """Return JSON response with current counts"""
UNCOV
577
        counts = {
×
578
            "total_count": PBN_Export_Queue.objects.count(),
579
            "success_count": PBN_Export_Queue.objects.filter(
580
                zakonczono_pomyslnie=True
581
            ).count(),
582
            "error_count": PBN_Export_Queue.objects.filter(
583
                zakonczono_pomyslnie=False
584
            ).count(),
585
            "pending_count": PBN_Export_Queue.objects.filter(
586
                zakonczono_pomyslnie=None
587
            ).count(),
588
            "waiting_count": PBN_Export_Queue.objects.filter(
589
                retry_after_user_authorised=True
590
            ).count(),
591
        }
UNCOV
592
        return JsonResponse(counts)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc