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

iplweb / bpp / #819

18 Oct 2025 12:03PM UTC coverage: 59.791% (+25.6%) from 34.185%
#819

push

local-test

Michał Pasternak
autostash

3781 of 9280 branches covered (40.74%)

Branch coverage included in aggregate %.

24978 of 38819 relevant lines covered (64.34%)

0.64 hits per line

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

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

4
from django.forms import widgets
1✔
5

6
from django.utils.safestring import mark_safe
1✔
7

8

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

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

28

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

36

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

40

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

159
        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