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

iplweb / bpp / #820

19 Oct 2025 06:59PM UTC coverage: 65.093% (+5.3%) from 59.791%
#820

push

coveralls-python

Michał Pasternak
Fixes

4215 of 9430 branches covered (44.7%)

Branch coverage included in aggregate %.

27562 of 39388 relevant lines covered (69.98%)

0.7 hits per line

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

34.88
src/pbn_api/admin/widgets.py
1
# Register your models here.
2
import json
1✔
3

4
from django.forms import widgets
1✔
5
from django.utils.safestring import mark_safe
1✔
6

7

8
class PrettyJSONWidget(widgets.Textarea):
1✔
9
    show_only_current = False
1✔
10

11
    def format_value(self, value):
1✔
12
        try:
1✔
13
            v = json.loads(value)
×
14
            if self.show_only_current:
×
15
                # Pokazuj tylko ostatnią wersję z PBNu
16
                v = [value for value in v if value.get("current", False) is True]
×
17
            value = json.dumps(v, indent=4, sort_keys=True)
×
18
            # these lines will try to adjust size of TextArea to fit to content
19
            row_lengths = [len(r) for r in value.split("\n")]
×
20
            self.attrs["rows"] = min(max(len(row_lengths) + 2, 10), 60)
×
21
            self.attrs["cols"] = min(max(max(row_lengths) + 2, 40), 120)
×
22
            return value
×
23
        except Exception:
×
24
            # logger.warning("Error while formatting JSON: {}".format(e))
25
            return super().format_value(value)
×
26

27

28
class PrettyJSONWidgetReadonly(PrettyJSONWidget):
1✔
29
    def __init__(self, attrs=None):
1✔
30
        default_attrs = {"readonly": True}
1✔
31
        if attrs:
×
32
            default_attrs.update(attrs)
×
33
        super().__init__(default_attrs)
×
34

35

36
class PrettyJSONWidgetReadonlyOnlyCurrent(PrettyJSONWidgetReadonly):
1✔
37
    show_only_current = True
1✔
38

39

40
class JSONWithActionsWidget(PrettyJSONWidgetReadonly):
1✔
41
    """
42
    Widget that displays formatted JSON with copy and download buttons.
43
    """
44

45
    def render(self, name, value, attrs=None, renderer=None):
1✔
46
        if attrs is None:
1!
47
            attrs = {}
×
48

49
        # Add a unique ID for the container
50
        if "id" not in attrs:
×
51
            attrs["id"] = "id_%s" % name
×
52

53
        container_id = f"{attrs['id']}_container"
×
54

55
        # Format the JSON value
56
        formatted_value = self.format_value(value)
×
57

58
        # Create the HTML with buttons and textarea
59
        html = f"""
×
60
        <div id="{container_id}" class="json-with-actions-widget">
61
            <div class="json-actions" style="margin-bottom: 10px;">
62
                <button type="button" class="button json-copy-btn" data-target="{attrs['id']}">
63
                    📋 Kopiuj JSON
64
                </button>
65
                <button type="button" class="button json-download-btn" data-target="{attrs['id']}">
66
                    💾 Pobierz JSON
67
                </button>
68
            </div>
69
            {super().render(name, formatted_value, attrs, renderer)}
70
        </div>
71

72
        <script>
73
        (function($) {{
74
            $(document).ready(function() {{
75
                // Copy to clipboard functionality
76
                $('.json-copy-btn').on('click', function() {{
77
                    var targetId = $(this).data('target');
78
                    var textarea = $('#' + targetId);
79
                    var text = textarea.val();
80

81
                    // Try modern clipboard API first
82
                    if (navigator.clipboard && navigator.clipboard.writeText) {{
83
                        navigator.clipboard.writeText(text).then(function() {{
84
                            // Show success feedback
85
                            var btn = $('.json-copy-btn[data-target="' + targetId + '"]');
86
                            var originalText = btn.text();
87
                            btn.text('✓ Skopiowano!').addClass('success');
88
                            setTimeout(function() {{
89
                                btn.text(originalText).removeClass('success');
90
                            }}, 2000);
91
                        }}).catch(function(err) {{
92
                            console.error('Failed to copy: ', err);
93
                            fallbackCopyToClipboard(text, targetId);
94
                        }});
95
                    }} else {{
96
                        fallbackCopyToClipboard(text, targetId);
97
                    }}
98
                }});
99

100
                // Fallback copy method
101
                function fallbackCopyToClipboard(text, targetId) {{
102
                    var textarea = document.createElement('textarea');
103
                    textarea.value = text;
104
                    textarea.style.position = 'fixed';
105
                    textarea.style.opacity = '0';
106
                    document.body.appendChild(textarea);
107
                    textarea.select();
108

109
                    try {{
110
                        document.execCommand('copy');
111
                        var btn = $('.json-copy-btn[data-target="' + targetId + '"]');
112
                        var originalText = btn.text();
113
                        btn.text('✓ Skopiowano!').addClass('success');
114
                        setTimeout(function() {{
115
                            btn.text(originalText).removeClass('success');
116
                        }}, 2000);
117
                    }} catch (err) {{
118
                        console.error('Fallback copy failed: ', err);
119
                    }}
120

121
                    document.body.removeChild(textarea);
122
                }}
123

124
                // Download functionality
125
                $('.json-download-btn').on('click', function() {{
126
                    var targetId = $(this).data('target');
127
                    var textarea = $('#' + targetId);
128
                    var text = textarea.val();
129

130
                    // Find object ID dynamically from the admin form
131
                    var objectId = 'unknown';
132
                    $('.field-box').each(function() {{
133
                        var label = $(this).find('label').text().trim();
134
                        if (label === 'Object id') {{
135
                            objectId = $(this).find('.grp-readonly').text().trim();
136
                            return false; // break the loop
137
                        }}
138
                    }});
139

140
                    var filename = 'sent-data-' + objectId + '.json';
141

142
                    // Create blob and download link
143
                    var blob = new Blob([text], {{ type: 'application/json' }});
144
                    var url = window.URL.createObjectURL(blob);
145
                    var a = document.createElement('a');
146
                    a.href = url;
147
                    a.download = filename;
148
                    document.body.appendChild(a);
149
                    a.click();
150
                    window.URL.revokeObjectURL(url);
151
                    document.body.removeChild(a);
152
                }});
153
            }});
154
        }})(django.jQuery);
155
        </script>
156
        """
157

158
        return mark_safe(html)
×
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