|
1 | | -from typing import Optional, Sequence |
2 | | -from django.shortcuts import render, get_object_or_404 |
| 1 | +from __future__ import annotations |
3 | 2 |
|
4 | | -from django.views.decorators.http import require_http_methods |
| 3 | +from collections import namedtuple |
| 4 | +import logging |
| 5 | +from typing import Optional, TYPE_CHECKING |
| 6 | + |
| 7 | +from django.http import QueryDict |
| 8 | +from django.contrib import messages |
5 | 9 | from django.http import HttpResponse |
| 10 | +from django.shortcuts import redirect, render, get_object_or_404 |
| 11 | +from django.views.decorators.http import require_http_methods |
6 | 12 |
|
7 | 13 | from argus.notificationprofile.models import DestinationConfig, Media |
8 | | -from argus.notificationprofile.media import api_safely_get_medium_object |
9 | | -from argus.notificationprofile.media.base import NotificationMedium |
| 14 | +from argus.notificationprofile.media import get_medium_object |
| 15 | +from argus.notificationprofile.media.base import NotificationMedium, LabelForm |
| 16 | + |
| 17 | +if TYPE_CHECKING: |
| 18 | + from django.forms import Form |
| 19 | + |
| 20 | + |
| 21 | +LOG = logging.getLogger(__name__) |
| 22 | +Forms = namedtuple("Forms", ["label_form", "settings_form"]) |
| 23 | + |
| 24 | + |
| 25 | +# not a view |
| 26 | +def get_forms(request, media: str, instance: DestinationConfig = None): |
| 27 | + prefix = "destinationform" |
| 28 | + medium = get_medium_object(media) |
| 29 | + |
| 30 | + if instance and instance.pk: |
| 31 | + prefix += f"-{instance.pk}-{media}" |
| 32 | + label_name = medium.get_label(instance) |
| 33 | + if label_name: |
| 34 | + label_initial = {"label": label_name} |
| 35 | + else: |
| 36 | + prefix += f"-{media}" |
| 37 | + label_initial = {} |
| 38 | + |
| 39 | + data = None |
| 40 | + for key in request.POST: |
| 41 | + if key.startswith(prefix): |
| 42 | + data = request.POST |
| 43 | + break |
| 44 | + label_form = LabelForm(data=data, user=request.user, initial=label_initial, instance=instance, prefix=prefix) |
| 45 | + settings_form = medium.validate_settings(data, request.user, instance=instance, prefix=prefix) |
| 46 | + |
| 47 | + if data: |
| 48 | + label_form.is_valid() |
| 49 | + return Forms(label_form, settings_form) |
| 50 | + |
10 | 51 |
|
11 | | -from .forms import DestinationFormCreate, DestinationFormUpdate |
| 52 | +# not a view |
| 53 | +def save_forms(user, media: str, label_form: LabelForm, settings_form: Form): |
| 54 | + if label_form.instance.pk: |
| 55 | + media = label_form.instance.media_id |
| 56 | + if label_form.is_valid() and settings_form.is_valid(): |
| 57 | + obj = label_form.save(commit=False) |
| 58 | + obj.user = user |
| 59 | + obj.media_id = media |
| 60 | + obj.settings = settings_form.cleaned_data |
| 61 | + obj.save() |
| 62 | + return obj |
| 63 | + return None |
12 | 64 |
|
13 | 65 |
|
14 | 66 | @require_http_methods(["GET"]) |
15 | 67 | def destination_list(request): |
16 | 68 | return _render_destination_list(request) |
17 | 69 |
|
18 | 70 |
|
19 | | -@require_http_methods(["POST"]) |
20 | | -def create_htmx(request) -> HttpResponse: |
21 | | - form = DestinationFormCreate(request.POST or None, user=request.user) |
| 71 | +@require_http_methods(["GET", "POST"]) |
| 72 | +def create_destination(request, media: str) -> HttpResponse: |
| 73 | + medium = get_medium_object(media) |
| 74 | + label_form, settings_form = get_forms(request, media) |
22 | 75 | template = "htmx/destination/_content.html" |
23 | | - if form.is_valid(): |
24 | | - form.save() |
25 | | - return _render_destination_list(request, template=template) |
26 | | - return _render_destination_list(request, create_form=form, template=template) |
| 76 | + context = { |
| 77 | + "label_form": label_form, |
| 78 | + "settings_form": settings_form, |
| 79 | + "media": media, |
| 80 | + } |
| 81 | + obj = save_forms(request.user, media, label_form, settings_form) |
| 82 | + if obj: |
| 83 | + label = medium.get_label(obj) |
| 84 | + message = f'Created new {media} destination "{label}"' |
| 85 | + messages.success(request, message) |
| 86 | + LOG.info(message) |
| 87 | + request.POST = QueryDict("") |
| 88 | + return _render_destination_list(request, context=context, template=template) |
| 89 | + # return redirect("htmx:destination-list") |
| 90 | + error_msg = f"Could not create new {media} destination" |
| 91 | + messages.warning(request, error_msg) |
| 92 | + LOG.warn(error_msg) |
| 93 | + return _render_destination_list(request, context=context, template=template) |
27 | 94 |
|
28 | 95 |
|
29 | 96 | @require_http_methods(["POST"]) |
30 | | -def delete_htmx(request, pk: int) -> HttpResponse: |
| 97 | +def delete_destination(request, pk: int) -> HttpResponse: |
31 | 98 | destination = get_object_or_404(request.user.destinations.all(), pk=pk) |
32 | 99 | media = destination.media |
33 | 100 | error_msg = None |
| 101 | + medium = get_medium_object(media.slug) |
| 102 | + destination_label = medium.get_label(destination) |
34 | 103 | try: |
35 | | - medium = api_safely_get_medium_object(destination.media.slug) |
36 | 104 | medium.raise_if_not_deletable(destination) |
37 | | - except NotificationMedium.NotDeletableError: |
38 | | - error_msg = "That destination cannot be deleted." |
| 105 | + except NotificationMedium.NotDeletableError as e: |
| 106 | + # template? |
| 107 | + error_msg = ", ".join(e.args) |
| 108 | + message = f'Failed to delete {media} destination "{destination}": {error_msg}' |
| 109 | + messages.warning(request, message) |
| 110 | + LOG.warn(message) |
39 | 111 | else: |
40 | 112 | destination.delete() |
| 113 | + message = f'Deleted {media} destination "{destination_label}"' |
| 114 | + messages.success(request, message) |
| 115 | + LOG.info(message) |
41 | 116 |
|
42 | | - forms = _get_update_forms(request.user, media=media) |
43 | | - |
44 | | - context = { |
45 | | - "error_msg": error_msg, |
46 | | - "forms": forms, |
47 | | - "media": media, |
48 | | - } |
49 | | - return render(request, "htmx/destination/_collapse_with_forms.html", context=context) |
| 117 | + return redirect("htmx:destination-list") |
50 | 118 |
|
51 | 119 |
|
52 | 120 | @require_http_methods(["POST"]) |
53 | | -def update_htmx(request, pk: int) -> HttpResponse: |
54 | | - destination = DestinationConfig.objects.get(pk=pk) |
55 | | - form = DestinationFormUpdate(request.POST or None, instance=destination, request=request.user) |
| 121 | +def update_destination(request, pk: int, media: str) -> HttpResponse: |
| 122 | + medium = get_medium_object(media) |
56 | 123 | template = "htmx/destination/_form_list.html" |
57 | | - if form.is_valid(): |
58 | | - form.save() |
| 124 | + destination = get_object_or_404(request.user.destinations.all(), pk=pk) |
| 125 | + label = medium.get_label(destination) |
| 126 | + forms = get_forms(request, media, instance=destination) |
| 127 | + obj = save_forms(request.user, media, *forms) |
| 128 | + if obj: |
| 129 | + label = medium.get_label(obj) |
| 130 | + message = f'Updated {media} destination "{label}"' |
| 131 | + messages.success(request, message) |
| 132 | + LOG.info(message) |
| 133 | + request.POST = QueryDict("") |
59 | 134 | return _render_destination_list(request, template=template) |
60 | | - update_forms = _get_update_forms(request.user) |
61 | | - for index, update_form in enumerate(update_forms): |
62 | | - if update_form.instance.pk == pk: |
63 | | - update_forms[index] = form |
| 135 | + error_msg = f'Could not update {media} destination "{label}"' |
| 136 | + messages.warning(request, error_msg) |
| 137 | + LOG.warn(request, error_msg) |
| 138 | + all_forms = get_all_forms_grouped_by_media(request) |
| 139 | + update_forms = get_all_update_forms_for_media(request, media) |
| 140 | + for index, forms in enumerate(update_forms): |
| 141 | + if forms.label_form.instance.pk == pk: |
| 142 | + update_forms[index] = forms |
64 | 143 | break |
65 | | - return _render_destination_list(request, update_forms=update_forms, template=template) |
| 144 | + all_forms[media] = update_forms |
| 145 | + context = { |
| 146 | + "forms": all_forms, |
| 147 | + "label_form": forms.label_form, |
| 148 | + "settings_form": forms.settings_form, |
| 149 | + "media": media, |
| 150 | + } |
| 151 | + return _render_destination_list(request, context=context, template=template) |
66 | 152 |
|
67 | 153 |
|
68 | 154 | def _render_destination_list( |
69 | 155 | request, |
70 | | - create_form: Optional[DestinationFormCreate] = None, |
71 | | - update_forms: Optional[Sequence[DestinationFormUpdate]] = None, |
| 156 | + context: Optional[dict] = None, |
72 | 157 | template: str = "htmx/destination/destination_list.html", |
73 | 158 | ) -> HttpResponse: |
74 | | - """Function to render the destinations page. |
75 | | -
|
76 | | - :param create_form: this is used to display the form for creating a new destination |
77 | | - with errors while retaining the user input. If you want a blank form, pass None. |
78 | | - :param update_forms: list of update forms to display. Useful for rendering forms |
79 | | - with error messages while retaining the user input. |
80 | | - If this is None, the update forms will be generated from the user's destinations.""" |
81 | | - |
82 | | - if create_form is None: |
83 | | - create_form = DestinationFormCreate() |
84 | | - if update_forms is None: |
85 | | - update_forms = _get_update_forms(request.user) |
86 | | - grouped_forms = _group_update_forms_by_media(update_forms) |
87 | | - context = { |
88 | | - "create_form": create_form, |
89 | | - "grouped_forms": grouped_forms, |
90 | | - "page_title": "Destinations", |
91 | | - } |
| 159 | + """Function to render the destinations page""" |
| 160 | + |
| 161 | + if not context: |
| 162 | + context = {} |
| 163 | + if "forms" not in context: |
| 164 | + context["forms"] = get_all_forms_grouped_by_media(request) |
| 165 | + context["page_title"] = "Destinations" |
92 | 166 | return render(request, template, context=context) |
93 | 167 |
|
94 | 168 |
|
95 | | -def _get_update_forms(user, media: Media = None) -> list[DestinationFormUpdate]: |
| 169 | +def get_all_update_forms_for_media(request, media: str) -> list[Forms]: |
96 | 170 | """Get a list of update forms for the user's destinations. |
97 | | - :param media: if provided, only return destinations for this media. |
| 171 | + :param media: Only return destinations for this media. |
98 | 172 | """ |
99 | | - if media: |
100 | | - destinations = user.destinations.filter(media=media) |
101 | | - else: |
102 | | - destinations = user.destinations.all() |
103 | | - # Sort by oldest first |
104 | | - destinations = destinations.order_by("pk") |
105 | | - return [DestinationFormUpdate(instance=destination) for destination in destinations] |
| 173 | + destinations = request.user.destinations.filter(media_id=media).order_by("pk") |
106 | 174 |
|
| 175 | + return [get_forms(request, media, instance=destination) for destination in destinations] |
107 | 176 |
|
108 | | -def _group_update_forms_by_media( |
109 | | - destination_forms: Sequence[DestinationFormUpdate], |
110 | | -) -> dict[Media, list[DestinationFormUpdate]]: |
111 | | - grouped_destinations = {} |
112 | 177 |
|
113 | | - # Adding a media to the dict even if there are no destinations for it |
114 | | - # is useful so that the template can render a section for that media |
| 178 | +def get_all_forms_grouped_by_media(request): |
| 179 | + forms = {} |
115 | 180 | for media in Media.objects.all(): |
116 | | - grouped_destinations[media] = [] |
117 | | - |
118 | | - for form in destination_forms: |
119 | | - grouped_destinations[form.instance.media].append(form) |
120 | | - |
121 | | - return grouped_destinations |
122 | | - |
123 | | - |
124 | | -def _replace_form_in_list(forms: list[DestinationFormUpdate], form: DestinationFormUpdate): |
125 | | - for index, f in enumerate(forms): |
126 | | - if f.instance.pk == form.instance.pk: |
127 | | - forms[index] = form |
128 | | - break |
| 181 | + create_form = get_forms(request, media.slug) |
| 182 | + update_forms = get_all_update_forms_for_media(request, media.slug) |
| 183 | + forms[media] = { |
| 184 | + "create_form": create_form, |
| 185 | + "update_forms": update_forms, |
| 186 | + } |
129 | 187 | return forms |
0 commit comments