From 1f02ed5eff47a32841d1bfd09710f629ef667687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikolai=20R=C3=B8ed=20Kristiansen?= Date: Fri, 30 May 2025 11:07:20 +0200 Subject: [PATCH] chore: ruff --fix --- apps/kassa/admin.py | 23 ++- apps/kassa/context_processors.py | 4 +- apps/kassa/forms.py | 9 +- apps/kassa/migrations/0001_initial.py | 35 ++-- .../migrations/0002_auto_20150810_1327.py | 25 +-- .../migrations/0003_auto_20150812_1530.py | 22 ++- .../migrations/0004_auto_20150825_1130.py | 23 ++- .../migrations/0005_auto_20150829_1525.py | 24 ++- .../migrations/0006_auto_20150829_1811.py | 25 ++- .../0007_kassaevent_transaction_id.py | 9 +- .../0008_kassaevent_user_galtinn_id.py | 9 +- apps/kassa/models.py | 34 ++-- apps/kassa/tests.py | 1 - apps/kassa/urls.py | 24 ++- apps/kassa/utils.py | 110 +++++------ apps/kassa/views.py | 186 +++++++++--------- fabfile.py | 32 +-- kassasite/settings.py | 146 ++++++-------- kassasite/urls.py | 10 +- 19 files changed, 382 insertions(+), 369 deletions(-) diff --git a/apps/kassa/admin.py b/apps/kassa/admin.py index 040a78a..02477d4 100644 --- a/apps/kassa/admin.py +++ b/apps/kassa/admin.py @@ -1,12 +1,19 @@ -from apps.kassa.models import KassaEvent from django.contrib import admin +from apps.kassa.models import KassaEvent + +@admin.register(KassaEvent) class KassaEventAdmin(admin.ModelAdmin): - list_display = ['id', 'event', 'card_number', 'user_phone_number', 'user_galtinn_id', - 'user_inside_id', 'created', 'transaction_id'] - list_filter = ['event'] - search_fields = ['card_number', 'user_phone_number', 'user_galtinn_id', - 'user_inside_id', 'transaction_id'] - -admin.site.register(KassaEvent, KassaEventAdmin) + list_display = [ + "id", + "event", + "card_number", + "user_phone_number", + "user_galtinn_id", + "user_inside_id", + "created", + "transaction_id", + ] + list_filter = ["event"] + search_fields = ["card_number", "user_phone_number", "user_galtinn_id", "user_inside_id", "transaction_id"] diff --git a/apps/kassa/context_processors.py b/apps/kassa/context_processors.py index 35dfa6a..88809a6 100644 --- a/apps/kassa/context_processors.py +++ b/apps/kassa/context_processors.py @@ -1,5 +1,5 @@ from django.conf import settings -def sentry(request): - return {'SENTRY_DSN': settings.SENTRY_DSN or '', 'SENTRY_ENVIRONMENT': settings.SENTRY_ENVIRONMENT} +def sentry(_): + return {"SENTRY_DSN": settings.SENTRY_DSN or "", "SENTRY_ENVIRONMENT": settings.SENTRY_ENVIRONMENT} diff --git a/apps/kassa/forms.py b/apps/kassa/forms.py index 4c2e005..5bef424 100644 --- a/apps/kassa/forms.py +++ b/apps/kassa/forms.py @@ -1,17 +1,16 @@ from django import forms +from django.utils.translation import gettext_lazy as _ from phonenumber_field.formfields import PhoneNumberField -from django.utils.translation import ugettext_lazy as _ class SearchUserForm(forms.Form): query = forms.CharField( - required=False, - widget=forms.TextInput(attrs={'placeholder': _('Firstname, lastname, card number or email')}) + required=False, widget=forms.TextInput(attrs={"placeholder": _("Firstname, lastname, card number or email")}) ) class AddCardForm(forms.Form): - card_number = forms.IntegerField(widget=forms.NumberInput(attrs={'autofocus': True})) - phone_number = PhoneNumberField(widget=forms.TextInput(attrs={'type': 'tel'})) + card_number = forms.IntegerField(widget=forms.NumberInput(attrs={"autofocus": True})) + phone_number = PhoneNumberField(widget=forms.TextInput(attrs={"type": "tel"})) user_id = forms.IntegerField(widget=forms.HiddenInput(), required=False) order_uuid = forms.IntegerField(widget=forms.HiddenInput(), required=False) diff --git a/apps/kassa/migrations/0001_initial.py b/apps/kassa/migrations/0001_initial.py index 91d3f0d..ec341aa 100644 --- a/apps/kassa/migrations/0001_initial.py +++ b/apps/kassa/migrations/0001_initial.py @@ -1,25 +1,30 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='KassaEvent', + name="KassaEvent", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('event', models.TextField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership')])), - ('card_number', models.IntegerField(null=True, blank=True)), - ('user_inside_id', models.IntegerField(null=True, blank=True)), - ('user_phone_number', models.TextField(max_length=100, null=True, blank=True)), - ('created', models.DateTimeField(auto_now_add=True)), - ('updated', models.DateTimeField(auto_now=True)), + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ( + "event", + models.TextField( + max_length=255, + choices=[ + ("new_card_membership", "Added new card membership (no user yet)"), + ("update_card", "Updated user card number"), + ("add_or_renew", "Added or renewed membership user membership"), + ], + ), + ), + ("card_number", models.IntegerField(null=True, blank=True)), + ("user_inside_id", models.IntegerField(null=True, blank=True)), + ("user_phone_number", models.TextField(max_length=100, null=True, blank=True)), + ("created", models.DateTimeField(auto_now_add=True)), + ("updated", models.DateTimeField(auto_now=True)), ], ), ] diff --git a/apps/kassa/migrations/0002_auto_20150810_1327.py b/apps/kassa/migrations/0002_auto_20150810_1327.py index 198192d..9f9cac5 100644 --- a/apps/kassa/migrations/0002_auto_20150810_1327.py +++ b/apps/kassa/migrations/0002_auto_20150810_1327.py @@ -1,24 +1,27 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kassa', '0001_initial'), + ("kassa", "0001_initial"), ] operations = [ migrations.AlterField( - model_name='kassaevent', - name='event', - field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership')]), + model_name="kassaevent", + name="event", + field=models.CharField( + max_length=255, + choices=[ + ("new_card_membership", "Added new card membership (no user yet)"), + ("update_card", "Updated user card number"), + ("add_or_renew", "Added or renewed membership user membership"), + ], + ), ), migrations.AlterField( - model_name='kassaevent', - name='user_phone_number', + model_name="kassaevent", + name="user_phone_number", field=models.CharField(max_length=100, null=True, blank=True), ), ] diff --git a/apps/kassa/migrations/0003_auto_20150812_1530.py b/apps/kassa/migrations/0003_auto_20150812_1530.py index 17327bb..088ccc6 100644 --- a/apps/kassa/migrations/0003_auto_20150812_1530.py +++ b/apps/kassa/migrations/0003_auto_20150812_1530.py @@ -1,19 +1,23 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kassa', '0002_auto_20150810_1327'), + ("kassa", "0002_auto_20150810_1327"), ] operations = [ migrations.AlterField( - model_name='kassaevent', - name='event', - field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('sms_card_notify', 'Notified phone number about activation'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership')]), + model_name="kassaevent", + name="event", + field=models.CharField( + max_length=255, + choices=[ + ("new_card_membership", "Added new card membership (no user yet)"), + ("sms_card_notify", "Notified phone number about activation"), + ("update_card", "Updated user card number"), + ("add_or_renew", "Added or renewed membership user membership"), + ], + ), ), ] diff --git a/apps/kassa/migrations/0004_auto_20150825_1130.py b/apps/kassa/migrations/0004_auto_20150825_1130.py index bf45c6a..a07c87e 100644 --- a/apps/kassa/migrations/0004_auto_20150825_1130.py +++ b/apps/kassa/migrations/0004_auto_20150825_1130.py @@ -1,19 +1,24 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kassa', '0003_auto_20150812_1530'), + ("kassa", "0003_auto_20150812_1530"), ] operations = [ migrations.AlterField( - model_name='kassaevent', - name='event', - field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('sms_card_notify', 'Notified phone number about activation'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership'), ('refund', 'Refunded membership')]), + model_name="kassaevent", + name="event", + field=models.CharField( + max_length=255, + choices=[ + ("new_card_membership", "Added new card membership (no user yet)"), + ("sms_card_notify", "Notified phone number about activation"), + ("update_card", "Updated user card number"), + ("add_or_renew", "Added or renewed membership user membership"), + ("refund", "Refunded membership"), + ], + ), ), ] diff --git a/apps/kassa/migrations/0005_auto_20150829_1525.py b/apps/kassa/migrations/0005_auto_20150829_1525.py index 2556117..09124aa 100644 --- a/apps/kassa/migrations/0005_auto_20150829_1525.py +++ b/apps/kassa/migrations/0005_auto_20150829_1525.py @@ -1,19 +1,25 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kassa', '0004_auto_20150825_1130'), + ("kassa", "0004_auto_20150825_1130"), ] operations = [ migrations.AlterField( - model_name='kassaevent', - name='event', - field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('sms_card_notify', 'Notified phone number about activation'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed user membership'), ('renew_only', 'Renewed user membership'), ('refund', 'Refunded membership')]), + model_name="kassaevent", + name="event", + field=models.CharField( + max_length=255, + choices=[ + ("new_card_membership", "Added new card membership (no user yet)"), + ("sms_card_notify", "Notified phone number about activation"), + ("update_card", "Updated user card number"), + ("add_or_renew", "Added or renewed user membership"), + ("renew_only", "Renewed user membership"), + ("refund", "Refunded membership"), + ], + ), ), ] diff --git a/apps/kassa/migrations/0006_auto_20150829_1811.py b/apps/kassa/migrations/0006_auto_20150829_1811.py index 1a83743..d4c7f33 100644 --- a/apps/kassa/migrations/0006_auto_20150829_1811.py +++ b/apps/kassa/migrations/0006_auto_20150829_1811.py @@ -1,19 +1,26 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kassa', '0005_auto_20150829_1525'), + ("kassa", "0005_auto_20150829_1525"), ] operations = [ migrations.AlterField( - model_name='kassaevent', - name='event', - field=models.CharField(max_length=255, choices=[('add_or_renew', 'New or renewed user membership'), ('new_card_membership', 'New card membership (no user yet)'), ('refund', 'Refunded membership'), ('renew_only', 'Renewed user membership'), ('sms_card_notify', 'Sent activation link to phone number'), ('update_card', 'Updated card'), ('membership_trial', 'New membership trial')]), + model_name="kassaevent", + name="event", + field=models.CharField( + max_length=255, + choices=[ + ("add_or_renew", "New or renewed user membership"), + ("new_card_membership", "New card membership (no user yet)"), + ("refund", "Refunded membership"), + ("renew_only", "Renewed user membership"), + ("sms_card_notify", "Sent activation link to phone number"), + ("update_card", "Updated card"), + ("membership_trial", "New membership trial"), + ], + ), ), ] diff --git a/apps/kassa/migrations/0007_kassaevent_transaction_id.py b/apps/kassa/migrations/0007_kassaevent_transaction_id.py index a13ca64..2f3d3e7 100644 --- a/apps/kassa/migrations/0007_kassaevent_transaction_id.py +++ b/apps/kassa/migrations/0007_kassaevent_transaction_id.py @@ -1,20 +1,17 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.3 on 2017-07-22 12:40 -from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kassa', '0006_auto_20150829_1811'), + ("kassa", "0006_auto_20150829_1811"), ] operations = [ migrations.AddField( - model_name='kassaevent', - name='transaction_id', + model_name="kassaevent", + name="transaction_id", field=models.UUIDField(blank=True, null=True), ), ] diff --git a/apps/kassa/migrations/0008_kassaevent_user_galtinn_id.py b/apps/kassa/migrations/0008_kassaevent_user_galtinn_id.py index 639cc66..902ed43 100644 --- a/apps/kassa/migrations/0008_kassaevent_user_galtinn_id.py +++ b/apps/kassa/migrations/0008_kassaevent_user_galtinn_id.py @@ -1,20 +1,17 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.3 on 2017-07-22 12:43 -from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kassa', '0007_kassaevent_transaction_id'), + ("kassa", "0007_kassaevent_transaction_id"), ] operations = [ migrations.AddField( - model_name='kassaevent', - name='user_galtinn_id', + model_name="kassaevent", + name="user_galtinn_id", field=models.IntegerField(blank=True, null=True), ), ] diff --git a/apps/kassa/models.py b/apps/kassa/models.py index 290bbdb..d542245 100644 --- a/apps/kassa/models.py +++ b/apps/kassa/models.py @@ -1,32 +1,32 @@ from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class KassaEvent(models.Model): - ADD_OR_RENEW = 'add_or_renew' - NEW_CARD_MEMBERSHIP = 'new_card_membership' - REFUND = 'refund' - RENEW_ONLY = 'renew_only' - SMS_CARD_NOTIFY = 'sms_card_notify' - UPDATE_CARD = 'update_card' - MEMBERSHIP_TRIAL = 'membership_trial' + ADD_OR_RENEW = "add_or_renew" + NEW_CARD_MEMBERSHIP = "new_card_membership" + REFUND = "refund" + RENEW_ONLY = "renew_only" + SMS_CARD_NOTIFY = "sms_card_notify" + UPDATE_CARD = "update_card" + MEMBERSHIP_TRIAL = "membership_trial" EVENT_CHOICES = ( - (ADD_OR_RENEW, _('New or renewed user membership')), - (NEW_CARD_MEMBERSHIP, _('New card membership (no user yet)')), - (REFUND, _('Refunded membership')), - (RENEW_ONLY, _('Renewed user membership')), - (SMS_CARD_NOTIFY, _('Sent activation link to phone number')), - (UPDATE_CARD, _('Updated card')), - (MEMBERSHIP_TRIAL, _('New membership trial')) + (ADD_OR_RENEW, _("New or renewed user membership")), + (NEW_CARD_MEMBERSHIP, _("New card membership (no user yet)")), + (REFUND, _("Refunded membership")), + (RENEW_ONLY, _("Renewed user membership")), + (SMS_CARD_NOTIFY, _("Sent activation link to phone number")), + (UPDATE_CARD, _("Updated card")), + (MEMBERSHIP_TRIAL, _("New membership trial")), ) event = models.CharField(max_length=255, choices=EVENT_CHOICES) card_number = models.IntegerField(null=True, blank=True) user_galtinn_id = models.IntegerField(null=True, blank=True) user_inside_id = models.IntegerField(null=True, blank=True) - user_phone_number = models.CharField(max_length=100, null=True, blank=True) + user_phone_number = models.CharField(max_length=100, null=True, blank=True) # noqa: DJ001 transaction_id = models.UUIDField(null=True, blank=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) def __str__(self): - return 'KassaEvent: <{}>'.format(self.pk) + return f"KassaEvent: <{self.pk}>" diff --git a/apps/kassa/tests.py b/apps/kassa/tests.py index 2e9cb5f..e69de29 100644 --- a/apps/kassa/tests.py +++ b/apps/kassa/tests.py @@ -1 +0,0 @@ -from django.test import TestCase diff --git a/apps/kassa/urls.py b/apps/kassa/urls.py index 1e29216..b263acf 100644 --- a/apps/kassa/urls.py +++ b/apps/kassa/urls.py @@ -1,13 +1,19 @@ -from django.urls import path, re_path +from django.urls import path -from apps.kassa.views import (register, user_search, check_card, check_phone_number, register_card_and_membership, - renew_membership) +from apps.kassa.views import ( + check_card, + check_phone_number, + register, + register_card_and_membership, + renew_membership, + user_search, +) urlpatterns = [ - re_path(r'^$', register, name='register'), - path('user-search/', user_search, name='user-search'), - path('check-phone-number/', check_phone_number, name='check-phone-number'), - path('check-card/', check_card, name='check-card'), - path('register-card-membership/', register_card_and_membership, name='register-card-and-membership'), - path('renew-membership/', renew_membership, name='renew-membership'), + path("", register, name="register"), + path("user-search/", user_search, name="user-search"), + path("check-phone-number/", check_phone_number, name="check-phone-number"), + path("check-card/", check_card, name="check-card"), + path("register-card-membership/", register_card_and_membership, name="register-card-and-membership"), + path("renew-membership/", renew_membership, name="renew-membership"), ] diff --git a/apps/kassa/utils.py b/apps/kassa/utils.py index a46f08b..bbf0dea 100644 --- a/apps/kassa/utils.py +++ b/apps/kassa/utils.py @@ -1,102 +1,98 @@ -# coding: utf-8 -from django.conf import settings import datetime +import logging + import phonenumbers import requests -import logging +from django.conf import settings logger = logging.getLogger(__name__) -galtinn_auth = { - 'Authorization': 'Token {}'.format(settings.GALTINN_API_KEY) -} +galtinn_auth = {"Authorization": f"Token {settings.GALTINN_API_KEY}"} -tekstmelding_auth = { - 'Authorization': 'Token {}'.format(settings.TEKSTMELDING_API_KEY) -} +tekstmelding_auth = {"Authorization": f"Token {settings.TEKSTMELDING_API_KEY}"} def send_sms_activation_link(phone_number, transaction_id): - to = phone_number.replace('+', '') + to = phone_number.replace("+", "") code = transaction_id[:8] activation_url = settings.GALTINN_ACTIVATION_URL.format(to=to, code=code) message = settings.TEKSTMELDING_ACTIVATION_SMS_TEMPLATE.format(url=activation_url) - payload = { - 'to': to, - 'message': message - } + payload = {"to": to, "message": message} if not settings.TEKSTMELDING_ENABLED: - logger.info('Tekstmelding disabled. Payload would be: {}'.format(payload)) - return - url = '{}send'.format(settings.TEKSTMELDING_API_URL) - response = requests.post(url, json=payload, headers=tekstmelding_auth) + logger.info(f"Tekstmelding disabled. Payload would be: {payload}") + return None + url = f"{settings.TEKSTMELDING_API_URL}send" + response = requests.post(url, json=payload, headers=tekstmelding_auth, timeout=10) return response.json() def get_card(card_number): - url = '{}cards/{}/'.format(settings.GALTINN_API_URL, card_number) - return requests.get(url, headers=galtinn_auth) + url = f"{settings.GALTINN_API_URL}cards/{card_number}/" + return requests.get(url, headers=galtinn_auth, timeout=10) def get_order(order_uuid): - url = '{}orders/{}/'.format(settings.GALTINN_API_URL, order_uuid) - return requests.get(url, headers=galtinn_auth) + url = f"{settings.GALTINN_API_URL}orders/{order_uuid}/" + return requests.get(url, headers=galtinn_auth, timeout=10) def get_latest_order_by_card(card_number): - url = '{}orders/'.format(settings.GALTINN_API_URL) - payload = {'card_number': card_number} - orders = requests.get(url, params=payload, headers=galtinn_auth).json() - return orders.get('results')[0] + url = f"{settings.GALTINN_API_URL}orders/" + payload = {"card_number": card_number} + orders = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json() + return orders.get("results")[0] def get_latest_order_by_phone(phone_number): - url = '{}orders/'.format(settings.GALTINN_API_URL) - payload = {'phone_number': phone_number} - orders = requests.get(url, params=payload, headers=galtinn_auth).json() - if orders.get('count'): - return orders.get('results')[0] + url = f"{settings.GALTINN_API_URL}orders/" + payload = {"phone_number": phone_number} + orders = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json() + if orders.get("count"): + return orders.get("results")[0] return None def get_user(user_id): - url = '{}users/{}/'.format(settings.GALTINN_API_URL, user_id) - return requests.get(url, headers=galtinn_auth) + url = f"{settings.GALTINN_API_URL}users/{user_id}/" + return requests.get(url, headers=galtinn_auth, timeout=10) def get_user_by_phone(phone_number): - url = '{}users/'.format(settings.GALTINN_API_URL) - payload = {'phone_number': phone_number} - users = requests.get(url, params=payload, headers=galtinn_auth).json() - if users.get('count') == 1: - return users.get('results')[0] + url = f"{settings.GALTINN_API_URL}users/" + payload = {"phone_number": phone_number} + users = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json() + if users.get("count") == 1: + return users.get("results")[0] return None def update_card(card_number, user_id=None, order_uuid=None, transaction_id=None): - assert not (user_id and order_uuid) - url = '{}kassa/card/'.format(settings.GALTINN_API_URL) + if user_id and order_uuid: + raise ValueError("Cannot provide both user_id and order_uuid. Provide one or the other.") + + url = f"{settings.GALTINN_API_URL}kassa/card/" payload = { - 'member_card': card_number, - 'user': user_id, - 'order': order_uuid, - 'transaction_id': str(transaction_id) if transaction_id else None, + "member_card": card_number, + "user": user_id, + "order": order_uuid, + "transaction_id": str(transaction_id) if transaction_id else None, } - return requests.patch(url, json=payload, headers=galtinn_auth) + return requests.patch(url, json=payload, headers=galtinn_auth, timeout=10) -def update_membership(user=None, phone_number=None, card_number=None, membership_type=None, - transaction_id=None): - assert membership_type in ('standard', 'trial') - url = '{}kassa/membership/'.format(settings.GALTINN_API_URL) +def update_membership(user=None, phone_number=None, card_number=None, membership_type=None, transaction_id=None): + if membership_type not in ("standard", "trial"): + raise ValueError("Invalid membership type provided. Must be 'standard' or 'trial'.") + + url = f"{settings.GALTINN_API_URL}kassa/membership/" payload = { - 'user': user, - 'phone_number': phone_number, - 'member_card': card_number, - 'membership_type': membership_type, - 'transaction_id': transaction_id, + "user": user, + "phone_number": phone_number, + "member_card": card_number, + "membership_type": membership_type, + "transaction_id": transaction_id, } - return requests.post(url, json=payload, headers=galtinn_auth) + return requests.post(url, json=payload, headers=galtinn_auth, timeout=10) def format_phone_number(number): @@ -104,7 +100,7 @@ def format_phone_number(number): return number try: - p = phonenumbers.parse(number, region='NO') + p = phonenumbers.parse(number, region="NO") except phonenumbers.NumberParseException: return number @@ -115,7 +111,7 @@ def format_phone_number(number): def is_autumn(): - today = datetime.date.today() + today = datetime.datetime.now(tz=datetime.UTC).date() _min = datetime.date(year=today.year, month=8, day=1) _max = datetime.date(year=today.year + 1, month=1, day=1) return _min <= today < _max diff --git a/apps/kassa/views.py b/apps/kassa/views.py index 77178e2..d0a36a3 100644 --- a/apps/kassa/views.py +++ b/apps/kassa/views.py @@ -1,21 +1,30 @@ -# coding: utf-8 +import json +import logging +import uuid +from http import HTTPStatus + +import phonenumbers +import requests from django.conf import settings from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.shortcuts import render -from django.utils import timezone -from itertools import groupby -import json -import uuid -import logging -import phonenumbers -import requests -from apps.kassa.models import KassaEvent from apps.kassa.forms import AddCardForm, SearchUserForm -from apps.kassa.utils import send_sms_activation_link, update_card, get_card, \ - get_order, get_latest_order_by_card, get_latest_order_by_phone, get_user, get_user_by_phone, \ - galtinn_auth, update_membership, format_phone_number, is_autumn +from apps.kassa.models import KassaEvent +from apps.kassa.utils import ( + format_phone_number, + galtinn_auth, + get_card, + get_latest_order_by_card, + get_latest_order_by_phone, + get_user, + get_user_by_phone, + is_autumn, + send_sms_activation_link, + update_card, + update_membership, +) logger = logging.getLogger(__name__) @@ -23,142 +32,136 @@ logger = logging.getLogger(__name__) @login_required def register(request): context = { - 'add_card_form': AddCardForm(), - 'search_user_form': SearchUserForm(), - 'show_trial_membership': is_autumn() + "add_card_form": AddCardForm(), + "search_user_form": SearchUserForm(), + "show_trial_membership": is_autumn(), } - return render(request, 'kassa/register.html', context) + return render(request, "kassa/register.html", context) @login_required def user_search(request): - payload = { - 'search': request.GET.get('search', '') - } - url = '{}users/'.format(settings.GALTINN_API_URL) - data = requests.get(url, params=payload, headers=galtinn_auth).json() + payload = {"search": request.GET.get("search", "")} + url = f"{settings.GALTINN_API_URL}users/" + data = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json() return JsonResponse(data) @login_required def check_phone_number(request): - number = request.GET.get('phone_number', '').strip() + number = request.GET.get("phone_number", "").strip() - if len(number) < 2: - return JsonResponse({'error': 'Phone number is too short'}) + min_length = 2 + if len(number) < min_length: + return JsonResponse({"error": "Phone number is too short"}) # Is phone number valid? try: - p = phonenumbers.parse(number, region='NO') + p = phonenumbers.parse(number, region="NO") if not phonenumbers.is_valid_number(p): - return JsonResponse({'error': "Phone number '{}' is invalid.".format(number)}) + return JsonResponse({"error": f"Phone number '{number}' is invalid."}) + except phonenumbers.NumberParseException as e: - return JsonResponse({'error': str(e).replace('(1) ', '')}) + return JsonResponse({"error": str(e).replace("(1) ", "")}) number = phonenumbers.format_number(p, phonenumbers.PhoneNumberFormat.E164) user = get_user_by_phone(number) order = get_latest_order_by_phone(number) - return JsonResponse({ - 'user': user, - 'order': order - }) + return JsonResponse({"user": user, "order": order}) @login_required def check_card(request): - if request.method != 'GET': - return JsonResponse({'error': 'Only method GET supported'}) + if request.method != "GET": + return JsonResponse({"error": "Only method GET supported"}) - card_number = request.GET.get('card_number') + card_number = request.GET.get("card_number") response = get_card(card_number) - if response.status_code != 200: + if response.status_code != HTTPStatus.OK: return JsonResponse(response.json(), status=response.status_code) + card = response.json() - if card.get('user'): - card['user'] = get_user(card['user']).json() - card['order'] = None - if card.get('orders'): - card['order'] = get_latest_order_by_card(card_number) - card.pop('orders') + if card.get("user"): + card["user"] = get_user(card["user"]).json() + card["order"] = None + if card.get("orders"): + card["order"] = get_latest_order_by_card(card_number) + card.pop("orders") + return JsonResponse(card, status=response.status_code) @login_required def register_card_and_membership(request): - if request.method != 'POST': - return JsonResponse({'error': 'Only method POST supported'}) + if request.method != "POST": + return JsonResponse({"error": "Only method POST supported"}) - post_data = json.loads(request.body.decode('utf-8')) + post_data = json.loads(request.body.decode("utf-8")) # new_card_membership, update_card, add_or_renew, sms_card_notify # TODO: multiple actions (to allow renewal only) - action = post_data.get('action') - user_id = post_data.get('user_id') - order_uuid = post_data.get('order_uuid') - card_number = post_data.get('card_number') - phone_number_raw = post_data.get('phone_number') - membership_trial = post_data.get('membership_trial') - membership_type = 'trial' if membership_trial else 'standard' + action = post_data.get("action") + user_id = post_data.get("user_id") + order_uuid = post_data.get("order_uuid") + card_number = post_data.get("card_number") + phone_number_raw = post_data.get("phone_number") + membership_trial = post_data.get("membership_trial") + membership_type = "trial" if membership_trial else "standard" transaction_id = uuid.uuid4() - logger.debug('register_card_and_membership post_data %r', post_data) + logger.debug("register_card_and_membership post_data %r", post_data) phone_number = format_phone_number(phone_number_raw) # Update card number on user or order - if action in ('update_card', 'sms_card_notify'): - if action == 'update_card' and user_id: - response = update_card(card_number, - user_id=user_id) - elif action == 'sms_card_notify' and order_uuid: - response = update_card(card_number, - order_uuid=order_uuid, - transaction_id=transaction_id) - else: - assert user_id or order_uuid - if response.status_code != 200: + if action in ("update_card", "sms_card_notify"): + if action == "update_card" and user_id: + response = update_card(card_number, user_id=user_id) + elif action == "sms_card_notify" and order_uuid: + response = update_card(card_number, order_uuid=order_uuid, transaction_id=transaction_id) + elif not user_id and not order_uuid: + return JsonResponse({"error": "Either user_id or order_uuid must be set"}, status=HTTPStatus.BAD_REQUEST) + + if response.status_code != HTTPStatus.OK: return JsonResponse(response.json(), status=response.status_code) event_data = { - 'event': KassaEvent.UPDATE_CARD, - 'user_phone_number': phone_number, - 'card_number': card_number, - 'user_galtinn_id': user_id, - 'transaction_id': transaction_id + "event": KassaEvent.UPDATE_CARD, + "user_phone_number": phone_number, + "card_number": card_number, + "user_galtinn_id": user_id, + "transaction_id": transaction_id, } logger.debug(event_data) KassaEvent.objects.create(**event_data) # Add initial or renew membership for existing user or order - if action in ('add_or_renew', 'new_card_membership'): + if action in ("add_or_renew", "new_card_membership"): response = update_membership( user=user_id, phone_number=phone_number, card_number=card_number, membership_type=membership_type, - transaction_id=str(transaction_id)) + transaction_id=str(transaction_id), + ) KassaEvent.objects.create( - event=KassaEvent.ADD_OR_RENEW if membership_type != 'trial' else KassaEvent.MEMBERSHIP_TRIAL, + event=KassaEvent.ADD_OR_RENEW if membership_type != "trial" else KassaEvent.MEMBERSHIP_TRIAL, user_galtinn_id=user_id, card_number=card_number, user_phone_number=phone_number, - transaction_id=transaction_id + transaction_id=transaction_id, ) # Send activation notification (link) to user by SMS - if action in ('new_card_membership', 'sms_card_notify'): + if action in ("new_card_membership", "sms_card_notify"): event = KassaEvent.NEW_CARD_MEMBERSHIP - if action == 'sms_card_notify': + if action == "sms_card_notify": event = KassaEvent.SMS_CARD_NOTIFY # FIXME: could be async - send_sms_activation_link(phone_number=phone_number, - transaction_id=str(transaction_id)) + send_sms_activation_link(phone_number=phone_number, transaction_id=str(transaction_id)) KassaEvent.objects.create( - event=event, - card_number=card_number, - user_phone_number=phone_number, - transaction_id=transaction_id + event=event, card_number=card_number, user_phone_number=phone_number, transaction_id=transaction_id ) return JsonResponse(response.json(), status=response.status_code) @@ -166,18 +169,18 @@ def register_card_and_membership(request): @login_required def renew_membership(request): - if request.method != 'POST': - return JsonResponse({'error': 'Only method POST supported'}) + if request.method != "POST": + return JsonResponse({"error": "Only method POST supported"}) - post_data = json.loads(request.body.decode('utf-8')) - user_id = post_data.get('user_id') - phone_number_raw = post_data.get('phone_number') - card_number = post_data.get('card_number') - membership_trial = post_data.get('membership_trial') - membership_type = 'trial' if membership_trial else 'standard' + post_data = json.loads(request.body.decode("utf-8")) + user_id = post_data.get("user_id") + phone_number_raw = post_data.get("phone_number") + card_number = post_data.get("card_number") + membership_trial = post_data.get("membership_trial") + membership_type = "trial" if membership_trial else "standard" transaction_id = str(uuid.uuid4()) - logger.debug('renew_membership post_data %r', post_data) + logger.debug("renew_membership post_data %r", post_data) phone_number = format_phone_number(phone_number_raw) response = update_membership( @@ -185,11 +188,12 @@ def renew_membership(request): phone_number=phone_number, card_number=card_number, membership_type=membership_type, - transaction_id=transaction_id) + transaction_id=transaction_id, + ) KassaEvent.objects.create( event=KassaEvent.RENEW_ONLY if membership_trial is None else KassaEvent.MEMBERSHIP_TRIAL, user_galtinn_id=user_id, - transaction_id=transaction_id + transaction_id=transaction_id, ) return JsonResponse(response.json(), status=response.status_code) diff --git a/fabfile.py b/fabfile.py index 3be6e3e..fed77c9 100644 --- a/fabfile.py +++ b/fabfile.py @@ -7,27 +7,27 @@ from invoke import task @task() def deploy(c): """Make sure proxy_user is set to your neuf username.""" - project_path = '/var/www/neuf.no/kassa' - proxy_user = os.getenv('DEPLOY_USER', os.getenv('USER')) + project_path = "/var/www/neuf.no/kassa" + proxy_user = os.getenv("DEPLOY_USER", os.getenv("USER")) - c = Connection(host='gitdeploy@dreamcast.neuf.no', gateway=Connection('login.neuf.no', user=proxy_user)) + c = Connection(host="gitdeploy@dreamcast.neuf.no", gateway=Connection("login.neuf.no", user=proxy_user)) - with c.cd(project_path), c.prefix('source {}/venv/bin/activate'.format(project_path)): - c.run('git pull') # Get source - c.run('pip install -U pip') - c.run('pip install -r requirements.txt') # install deps in virtualenv - with c.cd('static'): # install and compile frontend deps - c.run('npm i') - c.run('npm run build') - c.run('python manage.py collectstatic --noinput -i node_modules') # Collect static - c.run('python manage.py migrate') # Run DB migrations + with c.cd(project_path), c.prefix(f"source {project_path}/venv/bin/activate"): + c.run("git pull") # Get source + c.run("pip install -U pip") + c.run("pip install -r requirements.txt") # install deps in virtualenv + with c.cd("static"): # install and compile frontend deps + c.run("npm i") + c.run("npm run build") + c.run("python manage.py collectstatic --noinput -i node_modules") # Collect static + c.run("python manage.py migrate") # Run DB migrations # Reload gunicorn - c.sudo('/usr/bin/supervisorctl pid kassa.neuf.no | xargs kill -HUP', shell=False) + c.sudo("/usr/bin/supervisorctl pid kassa.neuf.no | xargs kill -HUP", shell=False) @task def build(c): - with c.cd('static'): - c.run('npm i') - c.run('npm run build') + with c.cd("static"): + c.run("npm i") + c.run("npm run build") diff --git a/kassasite/settings.py b/kassasite/settings.py index 91a9fd4..f123583 100644 --- a/kassasite/settings.py +++ b/kassasite/settings.py @@ -9,16 +9,17 @@ https://docs.djangoproject.com/en/1.8/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import os +import contextlib +from pathlib import Path -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BASE_DIR = Path(__file__).parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '79l0@&(^ra9=s783rqeo2awhs6@c!j&wan6t-0wi8azyd@!x+7' +SECRET_KEY = "79l0@&(^ra9=s783rqeo2awhs6@c!j&wan6t-0wi8azyd@!x+7" # noqa: S105 # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -29,134 +30,117 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = ( - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'bootstrapform', - 'django_extensions', - 'corsheaders', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "bootstrapform", + "django_extensions", + "corsheaders", ) -LOCAL_APPS = ('apps.kassa',) +LOCAL_APPS = ("apps.kassa",) INSTALLED_APPS += LOCAL_APPS MIDDLEWARE = ( - 'django.contrib.sessions.middleware.SessionMiddleware', - 'corsheaders.middleware.CorsMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.middleware.security.SecurityMiddleware', + "django.contrib.sessions.middleware.SessionMiddleware", + "corsheaders.middleware.CorsMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django.middleware.security.SecurityMiddleware", ) -ROOT_URLCONF = 'kassasite.urls' +ROOT_URLCONF = "kassasite.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'apps/kassa/templates') - ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'apps.kassa.context_processors.sentry', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR.joinpath("apps/kassa/templates")], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "apps.kassa.context_processors.sentry", ], }, }, ] -WSGI_APPLICATION = 'kassasite.wsgi.application' +WSGI_APPLICATION = "kassasite.wsgi.application" # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR.joinpath("db.sqlite3"), } } # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ -LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'Europe/Oslo' +LANGUAGE_CODE = "en-us" +TIME_ZONE = "Europe/Oslo" USE_I18N = True -USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/ -STATIC_URL = '/static/' -STATICFILES_DIRS = ( - os.path.join(BASE_DIR, 'static'), -) -STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') +STATIC_URL = "/static/" +STATICFILES_DIRS = (BASE_DIR.joinpath("static"),) +STATIC_ROOT = BASE_DIR.joinpath("staticfiles") -LOGIN_REDIRECT_URL = '/' +LOGIN_REDIRECT_URL = "/" -GALTINN_API_KEY = '' -GALTINN_API_URL = 'https://galtinn.neuf.no/api/' -GALTINN_ACTIVATION_URL = 'https://galtinn.neuf.no/activate/{to}/{code}' +GALTINN_API_KEY = "" +GALTINN_API_URL = "https://galtinn.neuf.no/api/" +GALTINN_ACTIVATION_URL = "https://galtinn.neuf.no/activate/{to}/{code}" -TEKSTMELDING_API_KEY = '' -TEKSTMELDING_API_URL = 'https://tekstmelding.neuf.no/' +TEKSTMELDING_API_KEY = "" +TEKSTMELDING_API_URL = "https://tekstmelding.neuf.no/" TEKSTMELDING_ENABLED = True -TEKSTMELDING_ACTIVATION_SMS_TEMPLATE = 'Velkommen som medlem! Registrer deg her: {url}' +TEKSTMELDING_ACTIVATION_SMS_TEMPLATE = "Velkommen som medlem! Registrer deg her: {url}" CORS_ORIGIN_ALLOW_ALL = True LOGGING = { - 'version': 1, - 'disable_existing_loggers': True, - 'formatters': { - 'verbose': { - 'format': '%(levelname)s %(asctime)s %(module)s ' - '%(process)d %(thread)d %(message)s' - }, + "version": 1, + "disable_existing_loggers": True, + "formatters": { + "verbose": {"format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"}, }, - 'handlers': { - 'console': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'formatter': 'verbose' - } - }, - 'loggers': { - 'root': { - 'level': 'WARNING', - 'handlers': ['console'], + "handlers": {"console": {"level": "DEBUG", "class": "logging.StreamHandler", "formatter": "verbose"}}, + "loggers": { + "root": { + "level": "WARNING", + "handlers": ["console"], }, - 'django.db.backends': { - 'level': 'ERROR', - 'handlers': ['console'], - 'propagate': False, + "django.db.backends": { + "level": "ERROR", + "handlers": ["console"], + "propagate": False, }, }, } SENTRY_DSN = None -SENTRY_ENVIRONMENT = 'dev' +SENTRY_ENVIRONMENT = "dev" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Local settings -try: - from .local_settings import * -except ImportError: - pass +with contextlib.suppress(ImportError): + from .local_settings import * # noqa: F403 diff --git a/kassasite/urls.py b/kassasite/urls.py index 1d216de..dc2bfc4 100644 --- a/kassasite/urls.py +++ b/kassasite/urls.py @@ -1,13 +1,7 @@ -from django.conf.urls import include, url from django.contrib import admin -from django.urls import path - from django.contrib.auth import urls as auth_urls +from django.urls import include, path from apps.kassa import urls as kassa_urls -urlpatterns = [ - path('admin/', admin.site.urls), - url('^', include(kassa_urls)), - path('accounts/', include(auth_urls)) -] +urlpatterns = [path("admin/", admin.site.urls), path("", include(kassa_urls)), path("accounts/", include(auth_urls))]