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

liqd / adhocracy-plus / 18908688697

29 Oct 2025 12:59PM UTC coverage: 44.622% (-44.5%) from 89.135%
18908688697

Pull #2986

github

web-flow
Merge 1dfde8ee7 into 445e1d498
Pull Request #2986: Draft: Speed up Github Ci Tests

3012 of 6750 relevant lines covered (44.62%)

0.45 hits per line

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

0.0
/apps/organisations/views.py
1
import base64
×
2
import json
×
3
import os
×
4
from io import BytesIO
×
5

6
from django.conf import settings
×
7
from django.contrib.messages.views import SuccessMessageMixin
×
8
from django.core.exceptions import ImproperlyConfigured
×
9
from django.shortcuts import get_object_or_404
×
10
from django.shortcuts import redirect
×
11
from django.urls import reverse
×
12
from django.utils.translation import gettext_lazy as _
×
13
from django.views import generic
×
14
from django.views.generic import DetailView
×
15
from PIL import Image
×
16
from PIL import ImageDraw
×
17
from PIL import ImageFont
×
18

19
from adhocracy4.dashboard import mixins as a4dashboard_mixins
×
20
from apps.projects.models import Project
×
21

22
from . import forms
×
23
from .forms import SOCIAL_MEDIA_SIZES
×
24
from .forms import CommunicationContentCreationForm
×
25
from .models import Organisation
×
26

27

28
class OrganisationView(DetailView):
×
29
    template_name = "a4_candy_organisations/organisation_landing_page.html"
×
30
    model = Organisation
×
31
    slug_url_kwarg = "organisation_slug"
×
32

33
    def get_context_data(self, **kwargs):
×
34
        context = super().get_context_data(**kwargs)
×
35
        active, future, past = self.object.get_projects_list(self.request.user)
×
36

37
        context["active_projects"] = active
×
38
        context["future_projects"] = future
×
39
        context["past_projects"] = past
×
40

41
        project_headline = ""
×
42
        if active:
×
43
            project_headline = _("Participate now!")
×
44
        elif future:
×
45
            project_headline = _("Upcoming participation")
×
46
        elif past:
×
47
            project_headline = _("Ended participation")
×
48
        context["project_headline"] = project_headline
×
49

50
        return context
×
51

52

53
class InformationView(DetailView):
×
54
    template_name = "a4_candy_organisations/organisation_information.html"
×
55
    model = Organisation
×
56
    slug_url_kwarg = "organisation_slug"
×
57

58

59
class ImprintView(DetailView):
×
60
    template_name = "a4_candy_organisations/organisation_imprint.html"
×
61
    model = Organisation
×
62
    slug_url_kwarg = "organisation_slug"
×
63

64

65
class TermsOfUseView(DetailView):
×
66
    template_name = "a4_candy_organisations/organisation_terms_of_use.html"
×
67
    model = Organisation
×
68
    slug_url_kwarg = "organisation_slug"
×
69

70

71
class NetiquetteView(DetailView):
×
72
    template_name = "a4_candy_organisations/organisation_netiquette.html"
×
73
    model = Organisation
×
74
    slug_url_kwarg = "organisation_slug"
×
75

76

77
class DataProtectionView(DetailView):
×
78
    template_name = "a4_candy_organisations/organisation_data_protection.html"
×
79
    model = Organisation
×
80
    slug_url_kwarg = "organisation_slug"
×
81

82

83
class DashboardOrganisationUpdateView(
×
84
    a4dashboard_mixins.DashboardBaseMixin, SuccessMessageMixin, generic.UpdateView
85
):
86
    model = Organisation
×
87
    form_class = forms.OrganisationForm
×
88
    slug_url_kwarg = "organisation_slug"
×
89
    template_name = "a4_candy_organisations/organisation_form.html"
×
90
    success_message = _("Organisation information successfully updated.")
×
91
    permission_required = "a4_candy_organisations.change_organisation"
×
92
    menu_item = "organisation"
×
93

94
    def get_permission_object(self):
×
95
        return self.organisation
×
96

97
    def get_success_url(self):
×
98
        return self.request.path
×
99

100
    def get_project_languages(self):
×
101
        languages = getattr(settings, "LANGUAGES", None)
×
102
        if languages:
×
103
            language_dict = dict((x, str(y)) for x, y in languages)
×
104
            return json.dumps(language_dict)
×
105
        else:
106
            raise ImproperlyConfigured("set LANGUAGES in settings")
×
107

108
    def get_context_data(self, **kwargs):
×
109
        context = super().get_context_data(**kwargs)
×
110
        context["language_dict"] = self.get_project_languages()
×
111
        return context
×
112

113

114
class DashboardLegalInformationUpdateView(
×
115
    a4dashboard_mixins.DashboardBaseMixin, SuccessMessageMixin, generic.UpdateView
116
):
117
    model = Organisation
×
118
    form_class = forms.OrganisationLegalInformationForm
×
119
    slug_url_kwarg = "organisation_slug"
×
120
    template_name = "a4_candy_organisations/organisation_form_legal_info.html"
×
121
    success_message = _("Legal information successfully updated.")
×
122
    permission_required = "a4_candy_organisations.change_organisation"
×
123
    menu_item = "organisation"
×
124

125
    def get_permission_object(self):
×
126
        return self.organisation
×
127

128
    def get_success_url(self):
×
129
        return self.request.path
×
130

131

132
class DashboardCommunicationProjectChoiceView(
×
133
    a4dashboard_mixins.DashboardBaseMixin, generic.FormView
134
):
135

136
    menu_item = "communication"
×
137
    form_class = forms.CommunicationProjectChoiceForm
×
138
    permission_required = "a4_candy_organisations.change_organisation"
×
139
    template_name = "a4_candy_organisations/" "communication_form_social_media.html"
×
140
    slug_url_kwarg = "organisation_slug"
×
141

142
    def get_permission_object(self):
×
143
        return self.organisation
×
144

145
    def get_form_kwargs(self):
×
146
        kwargs = super().get_form_kwargs()
×
147
        kwargs["organisation"] = self.organisation
×
148
        return kwargs
×
149

150
    def form_valid(self, form):
×
151
        """If the form is valid, redirect to the content creation form."""
152
        project = form.cleaned_data["project"]
×
153
        img_format = form.cleaned_data["format"]
×
154
        return redirect(
×
155
            reverse(
156
                "a4dashboard:communication-content-create",
157
                kwargs={
158
                    "organisation_slug": self.organisation.slug,
159
                    "project_slug": project.slug,
160
                    "format": int(img_format),
161
                },
162
            )
163
        )
164

165
    def get_context_data(self, **kwargs):
×
166
        """Insert the form as project_form into the context dict."""
167
        kwargs = super().get_context_data(**kwargs)
×
168
        if "form" in kwargs:
×
169
            kwargs.pop("form")
×
170
        if "project_form" not in kwargs:
×
171
            kwargs["project_form"] = self.get_form()
×
172
        if "organisation" not in kwargs:
×
173
            kwargs["organisation"] = self.organisation
×
174

175
        return kwargs
×
176

177

178
class DashboardCommunicationContentCreateView(
×
179
    a4dashboard_mixins.DashboardBaseMixin, generic.FormView
180
):
181

182
    menu_item = "communication"
×
183
    form_class = forms.CommunicationContentCreationForm
×
184
    permission_required = "a4_candy_organisations.change_organisation"
×
185
    template_name = "a4_candy_organisations/" "communication_form_social_media.html"
×
186
    slug_url_kwarg = "organisation_slug"
×
187

188
    def get_permission_object(self):
×
189
        return self.organisation
×
190

191
    def get_success_url(self):
×
192
        return self.request.path
×
193

194
    def get_form_kwargs(self):
×
195
        kwargs = super().get_form_kwargs()
×
196
        if not self.request.POST:
×
197
            kwargs["project"] = self.project
×
198
        kwargs["format"] = self.format
×
199

200
        return kwargs
×
201

202
    def get_context_data(self, **kwargs):
×
203
        kwargs = super().get_context_data(**kwargs)
×
204
        if "form" in kwargs:
×
205
            kwargs.pop("form")
×
206
        if "content_form" not in kwargs:
×
207
            kwargs["content_form"] = self.get_form()
×
208
        if "project_form" not in kwargs:
×
209
            project_form = forms.CommunicationProjectChoiceForm(
×
210
                initial={"project": self.project, "format": self.format},
211
                organisation=self.organisation,
212
            )
213
            kwargs["project_form"] = project_form
×
214
        if "organisation" not in kwargs:
×
215
            kwargs["organisation"] = self.organisation
×
216

217
        return kwargs
×
218

219
    def form_valid(self, form):
×
220
        data = form.cleaned_data
×
221
        context = self.get_context_data()
×
222
        img = self.generate_image(data)
×
223
        # get the content form with unchanged cleaned data, as image generation
224
        # changes the image data (why?)
225
        content_form = CommunicationContentCreationForm(
×
226
            initial=data, project=self.project, format=self.format
227
        )
228
        context["image_preview"] = img
×
229
        context["content_form"] = content_form
×
230
        return self.render_to_response(context)
×
231

232
    @staticmethod
×
233
    def calc_aspect_ratio(width, height, req_width, req_height):
×
234
        # calculate aspect_ratio
235
        aspect_ratio = width / float(height)
×
236
        required_ratio = req_width / float(req_height)
×
237
        if aspect_ratio > required_ratio:
×
238
            new_width = int(required_ratio * height)
×
239
            offset = (width - new_width) / 2
×
240
            resize = (offset, 0, width - offset, height)
×
241
        else:
242
            new_height = int(width / required_ratio)
×
243
            offset = (height - new_height) / 2
×
244
            resize = (0, offset, width, height - offset)
×
245
        return resize
×
246

247
    def generate_image(self, data):
×
248
        # Get required image size and image
249
        sharepic_format = SOCIAL_MEDIA_SIZES[self.format]
×
250
        req_width = sharepic_format["img_min_width"]
×
251
        req_height = sharepic_format["img_min_height"]
×
252

253
        image_get = Image.open(data["image"].file)
×
254
        width, height = image_get.size
×
255
        resize = self.calc_aspect_ratio(width, height, req_width, req_height)
×
256
        # Use LANCZOS for resampling to keep better quality
257
        image = image_get.crop(resize).resize(
×
258
            (req_width, req_height), Image.Resampling.LANCZOS
259
        )
260

261
        # get required total size and add appropriate padding
262
        result = Image.new(
×
263
            image.mode,
264
            (sharepic_format["img_min_width"], sharepic_format["overall_height"]),
265
            (255, 255, 255),
266
        )
267
        result.paste(image, (0, 0))
×
268

269
        # image is converted into editable form using Draw function
270
        image = ImageDraw.Draw(result)
×
271

272
        # text dimension, color and font
273
        font = ImageFont.truetype(
×
274
            os.path.join(
275
                settings.BASE_DIR,
276
                "adhocracy-plus/assets/fonts/SourceSansPro-Semibold.otf",
277
            ),
278
            sharepic_format["title_size"],
279
        )
280
        fontsm = ImageFont.truetype(
×
281
            os.path.join(
282
                settings.BASE_DIR,
283
                "adhocracy-plus/assets/fonts/SourceSansPro-Regular.otf",
284
            ),
285
            sharepic_format["description_size"],
286
        )
287
        title = data["title"]
×
288
        description = data["description"]
×
289
        title_width = image.textlength(title, font=font)
×
290
        description_width = image.textlength(description, font=fontsm)
×
291
        # add text using width to center
292
        image.text(
×
293
            (
294
                (sharepic_format["img_min_width"] - title_width) / 2,
295
                sharepic_format["title_y"],
296
            ),
297
            title,
298
            fill=(0, 0, 0),
299
            font=font,
300
        )
301
        image.text(
×
302
            (
303
                (sharepic_format["img_min_width"] - description_width) / 2,
304
                sharepic_format["description_y"],
305
            ),
306
            description,
307
            fill=(0, 0, 0),
308
            font=fontsm,
309
        )
310

311
        if data["add_orga_logo"] and self.organisation.logo:
×
312
            # get organisations logo
313
            logo_org_get = Image.open(self.organisation.logo)
×
314
            logo_org_size = (144, 144)
×
315
            logo_org = logo_org_get.resize(logo_org_size)
×
316
            border = 8
×
317
            logo_org_result = Image.new(
×
318
                image.mode,
319
                ((border + 144 + border), (border + 144 + border)),
320
                (255, 255, 255),
321
            )
322
            logo_org_result.paste(logo_org, (border, border))
×
323
            result.paste(logo_org_result, (80, sharepic_format["org_logo_y"]))
×
324

325
        if data["add_aplus_logo"]:
×
326
            # get aplus logo
327
            logo_aplus_get = Image.open(
×
328
                os.path.join(settings.BASE_DIR, "adhocracy-plus/assets/images/logo.png")
329
            )
330
            logo_aplus_size = (
×
331
                sharepic_format["aplus_logo_width"],
332
                sharepic_format["aplus_logo_height"],
333
            )
334
            logo_aplus = logo_aplus_get.resize(logo_aplus_size)
×
335
            # position a+ logo
336
            logo2_offset_y = sharepic_format["aplus_logo_y"]
×
337
            logo2_offset_x = (
×
338
                sharepic_format["img_min_width"] - sharepic_format["aplus_logo_width"]
339
            ) // 2
340
            result.paste(logo_aplus, (logo2_offset_x, logo2_offset_y), mask=logo_aplus)
×
341

342
        buffered_image = BytesIO()
×
343
        result.save(buffered_image, format="PNG")
×
344

345
        return base64.b64encode(buffered_image.getvalue()).decode("utf-8")
×
346

347
    @property
×
348
    def project(self):
×
349
        return get_object_or_404(Project, slug=self.kwargs["project_slug"])
×
350

351
    @property
×
352
    def format(self):
×
353
        return self.kwargs["format"]
×
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

© 2025 Coveralls, Inc