-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Powiadomienie do obserwujących sprawę #934
Conversation
for more information, see https://pre-commit.ci
Czy potrzebujesz pomocy? |
Sry, ostatnio zajmowałem się czymś innym i dopiero dzisiaj nad tym usiadłem. Wydaje mi się, że na razie wszystko jest dla mnie jasne, chociaż nie do końca rozumiem, dlaczego w tym twoim utilsie do wysyłania maili stworzyłeś tego enuma, tzn. dlaczego nie zrobiłeś czegoś w stylu TEMPLATE_MAP = {
"CASE_CLOSED": MailTemplate.from_prefix("cases/email/case_closed")
# ...
} a robiłeś to tak bardzo naokoło, czy to miało jakiś cel? |
Jak dla mnie dzięki temu jest skuteczniejsza statyczna kontrola kodu z zewnętrznych (znajdujących się w wielu miejscach poza użytkownikiem) modułów, ale wprowadził to @rwakulszowa w watchdogpolska/poradnia@86b0fac#diff-8c555138c101946f482a4a30648dae6f6ee462daf7a92c3f6739ea401ec8fea3 , więc ewentualnie jego pytaj. |
for more information, see https://pre-commit.ci
|
||
def test_do_not_send_disabled_notification(self): | ||
actor = "" | ||
action = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proponuje wprowadzić w tym miejscu pewne wartości, które wskaża na niewłączone powiadomienie.
{% block content %} | ||
<p>W systemie small_eod pojawiła się nowa sprawa.</p> | ||
|
||
<p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To jest wystarczające na ten moment. Zaznaczam, że pewnie w przyszłości informacje o sprawie prawdopodobnie będziemy chcieli mieć w jakiejś formie stopki, aby było jedno miejsce do klikania. Pozwólby jednak temu działać i wykorzystać przez Agnieszkę.
Jesteśmy całkiem blisko. Drobne komentarze mam, ale przede wszystkim sam muszę uruchomić. |
Przepraszam, nie zauważyłem wcześniej powiadomienia. W mocnym skrócie - idea była taka jak opisał Adam, tj. przeniesienie dynamicznych wartości (stringów) na statyczne wartości (enumy). Pomijając jakieś tam kwestie estetyczne, które są względne, z tym podejściem po prostu trudniej o jakieś drobne literówki, IDE lepiej radzą sobie z sugestiami, a każde odniesienie do klucza szablonu maila wymaga od użytkownika użycia typu Koniec końców oba podejścia są w stanie zrobić to samo, a wybór to kwestia gustu. |
@NameeLesS , czy możesz zrobić rebase? Poza tym wciąż jest to draft. |
Sry, próbowałem i nie bardzo potrafię ogarnąć tego rebase |
Nie szkodzi. Albo mnie złap na Messengerze wieczorem, albo domerguj tu branch |
@NameeLesS czy mogę Ci pomóc? |
Zrobiłem merge rozwiązałem konflikty, przed commitem chciałem wykonać testy aby sprawdzić, czy wszystko jest ok no i wywala mi błąd |
Jak instalowałeś te paczki? Czy wykorzystujesz Dockera? Jeżeli tak, w jaki sposób? Czy możesz pokazać logi, które prowadzą Cię do błędu? Czy możesz wykonać |
for more information, see https://pre-commit.ci
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nie mam żadnych uwag więcej.
Proszę o opinie jeszcze @rwakulszowa , bo może mieć wartościowy wkład.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dodałem parę groszy od siebie.
Część to drobiazgi, część to większe znaki zapytania.
Gdyby coś było niejasne (pisałem na szybko) - daj znać.
backend-project/small_eod/notes/templates/notes/email/note_created.html
Outdated
Show resolved
Hide resolved
logger = logging.getLogger(__name__) | ||
|
||
|
||
def make_auto(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Funkcję make_auto
można usunąć i zamiast niej użyć enum.auto
:
https://docs.python.org/3/library/enum.html#enum.auto
W poradni make_auto
dopisano w momencie, gdy używaliśmy starszej wersji pythona. W small_eod używamy pythona 3.7, więc funkcja enum.auto
jest już dostępna (od wersji 3.6).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Początkowo chciałem użyć enum.auto
ale trochę zmylił mnie komentarz "enum.auto replacement"
|
||
|
||
class MailTemplate: | ||
def __init__(self, txt_path, html_path=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jeśli każde powiadomienie ma wersje txt i html, sugerowałbym usunięcie domyślnej wartości html_path=None
i wymuszenie, by użytkownik podał oba argumenty.
W poradni dodano html_path=None
na czas migracji z txt do html (pewnie powinniśmy byli usunąć domyślny None, ale nam się zapomniało, lub nadal brakuje paru szablonów html).
|
||
@classmethod | ||
def send(cls, template_key, recipient_list, context=None, from_email=None): | ||
template = random.choice(cls.TEMPLATE_MAP[template_key]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nie do końca rozumiem skąd wziął się tutaj random.choice
. Z tego co widzę, każdy klucz w TEMPLATE_MAP
ma przypisaną 1-elementową listę, przez co random.choice
wybiera losowy element z 1-elementowego zbioru, czyli jedyny element.
Być może czegoś nie rozumiem, ale tę samą logikę można zaimplementować przez zmianę typu wartości TEMPLATE_MAP
: zamiast używać 1-elementowych list, używając po prostu jedynego elementu danej listy. W konsekwencji można będzie też usunąć referencje do random
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Był taki pomysł, aby maile były wybierane losowo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Zacznijmy z małym rozwiązaniem. Poza tym takie podejście do losowości wydaje mi się czytelniejsze: https://github.com/watchdogpolska/docker-images/blob/master/schedule-bot/lib/template.htm
init_instance = None | ||
http_action = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O ile dobrze rozumiem, te dwa pola są używane tylko wewnątrz SendNotificationMixin
i nie powinny być ustawiane przez inne klasy (innymi słowy - trzymają stan mixinu).
W takiej sytuacji sugerowałbym dodanie prefiksu _
(tj. np. _init_instance
) przed nazwą zmiennej. W nomenklaturze pythona pola zaczynające się od _
traktowane są jako prywatne. Użytkownikom będzie łatwiej zrozumieć, że nie powinni używać tych pól poza klasą.
(Co do sekcji poniżej nie jestem w 100% przekonany że mam rację (ostatnio mniej pracuję z pythonem), więc tutaj proszę @ad-m o potwierdzenie)
Co więcej, w tym momencie pola te są ustawiane na poziomie klasy, a nie instancji. To oznacza, że każda klasa używająca SendNotificationsMixin
będzie nadpisywała wartości tych pól dla każdej innej klasy. Bardziej naturalne wydaje mi się dodanie metody __init__
i tam przypisanie wartości do pól self._init_instance
i self._http_action
.
self.http_action = list(self.action_map.keys())[ | ||
list(self.action_map.values()).index(self.action) | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O ile dobrze rozumiem, ten blok wyszukuje w słowniku d
klucz k
przypisany do wartości v
na podstawie v
.
Można tę samą logikę napisać odrobinę prościej:
next(k for k, v in self.action_map.items() if v == self.action)
dict.items()
zwraca iterator par (klucz, wartość)
.
Kod wewnątrz nawiasu iteruje po parach, jednocześnie filtrując wartości, zwracając jedynie te k
, z którymi sparowany jest v == self.action
.
next
zwraca pierwszy element utworzonego iteratora.
notified_users = None | ||
ignored_fields = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sugerowałbym delikatne zmiany nazw tych pól:
notified_users
->notified_users_field
. Obecna nazwa pasowałaby (moim zdaniem) bardziej do konkretnej listy użytkowników otrzymujących powiadomienie, nie zaś pole w którym taką listę można znaleźć.ignored_fields
->notification_diff_ignored_fields
. Dla czytelności. Poleignored_fields
jest na tyle generyczne, że trudno domyślić się, iż dotyczy tylko powiadomień.
from rest_framework.views import APIView | ||
|
||
|
||
class SendNotificationsMixin(APIView): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Pytanie do @ad-m, ponieważ nie jestem w 100% przekonany czy mam rację.)
Czy nasz Mixin powinien dziedziczyć po pełnoprawnej klasie (tj. nie po innym Mixinie), czy powinniśmy pozwolić klasie używającej naszego mixinu na dodanie APIView? W tym momencie nasz SendNotificationMixin
nie jest sensu stricto Mixinem, a pełnoprawną klasą, o ile dobrze kojarzę.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A no nie jest mixinem, trudno będzie nie dziedziczyć po ApiView, więc można po prostu zmienić nazwę tej klasy na SendNotificationsView
lub NotificationsView
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moim zdaniem należaloby użyć class SendNotificationsMixin
. Wówczas powiadomienia są tylko czymś dodawanym do jakiegoś istniejącego widoku, a nie definiują kolejności klas w żadnym widoku. Wydaje mi się, że to wystarczy, aby uznać tę klasę za Mixin.
Oczywiście, z drugiej strony należy zadbać o kolejność klasy, więc zapisać raczej jako:
class CaseViewSet(NotificationsMixin, viewsets.ModelViewSet):
@@ -22,6 +23,12 @@ class EventViewSetTestCase( | |||
def validate_item(self, item): | |||
self.assertEqual(item["name"], self.obj.name) | |||
|
|||
def validate_notifications(self, action): | |||
mail_types = [_mail.extra_headers["Action"] for _mail in mail.outbox] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit - _mail
-> mail
.
Zmienne z przedrostkiem _
używane są w zasadzie tylko gdy nie są używane, lub gdy są polami prywatnymi instancji. Tutaj sugerowałbym zmianę nazwy zmiennej na mail
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gdy nie są używane lub aby uniknąć konfliktu nazw, ale tutaj rzeczywiście może być po prostu mail
def validate_notifications(self, action): | ||
mail_types = [_mail.extra_headers["Action"] for _mail in mail.outbox] | ||
self.assertEqual( | ||
mail_types, [action for _ in self.obj.case.notified_users.all()] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O ile dobrze rozumiem, ta metoda najpierw pobiera wartość Action
dla wszystkich wiadomości w outbox
, po czym tworzy listę N
powtórzeń wartości action
, gdzie N = deklarowana liczba odbiorców powiadomienia
.
Innymi słowy, metoda sprawdza 2 kwestie:
- Liczba powiadomień w outbox jest równa liczbie
self.obj.case.notified_users
- Każdy element
mail_types
jest równyaction
.
Sugerowałbym rozbić tę asercję na dwie oddzielne - moim zdaniem łatwiej będzie wtedy odczytać ewentualne błędy jak i zrozumieć kod.
Co do 2
- prostym sposobem sprawdzenia czy wszystkie elementy danego zbioru mają tę samą wartość jest konwersja na zbiór: self.assertEqual(set(mail_types), { action })
{% endblock %} | ||
|
||
{% block content %} | ||
<<<<<<< Updated upstream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O cholera xD Pycharm pokazywał mi że nie mam żadnych konfliktów.
Widziałem że w
cases/views.py
jestNotifiedUserViewSet
, więc chyba podszedłem do tego nie do końca po twojej myśli #923