Skip to content
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

Improve API (API 1.1) #421

Merged
merged 28 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
88f1114
Streamline GetMemberID with other Member-API endpoints
krestenlaust Apr 7, 2024
1b2a2e5
Consistent member/user terminology
krestenlaust Apr 8, 2024
7414038
Consistent view-method naming
krestenlaust Apr 8, 2024
50ec758
Make get ID consistent as well
krestenlaust Apr 8, 2024
1a04d40
Rename Get Payment QR-endpoint
krestenlaust Apr 8, 2024
c7141fd
Refactor Get Payment QR
krestenlaust Apr 8, 2024
00a7723
Rename named_products method
krestenlaust Apr 16, 2024
2479a6f
Change return text
krestenlaust Apr 17, 2024
aadf199
Remove use of annotations from future
krestenlaust Apr 17, 2024
2b24bc1
Object instead of array in Active Products-endpoint
krestenlaust Apr 17, 2024
65cb2f5
TODO Check whether room exists
krestenlaust Apr 17, 2024
b8679eb
Rename URL name
krestenlaust Apr 17, 2024
121f7b2
Strongly typed return-object for category mapping
krestenlaust May 4, 2024
da61929
Make QR Form and helper function generic
krestenlaust May 12, 2024
06aa0cb
Merge branch 'next' into improve-api
krestenlaust May 12, 2024
a5d2a04
Merge branch 'next' into improve-api
krestenlaust May 17, 2024
3015ca2
Merge branch 'next' into improve-api
krestenlaust Oct 24, 2024
21aaeb1
Rename 'member' -> 'username'-parameter
krestenlaust Nov 2, 2024
837b6f0
Merge branch 'next' into improve-api
krestenlaust Nov 12, 2024
cdaff44
Change minimum value for QR-code form to 0
krestenlaust Nov 27, 2024
40e878f
Fix incorrect 400 response body's
krestenlaust Nov 27, 2024
9c6a5f7
Make api/sale-endpoint responses more specific
krestenlaust Nov 27, 2024
dba5abd
Remove unused import
krestenlaust Nov 28, 2024
3c9f739
Rename view-functions
krestenlaust Nov 28, 2024
f0f5b50
Change naming scheme
krestenlaust Nov 28, 2024
3042946
Merge branch 'next' into improve-api
krestenlaust Nov 28, 2024
adbdf88
Merge branch 'next' into improve-api
krestenlaust Nov 29, 2024
88c893c
Merge branch 'next' into improve-api
krestenlaust Nov 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stregsystem/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def __init__(self, *args, **kwargs):

class QRPaymentForm(forms.Form):
member = forms.CharField(max_length=16)
amount = forms.DecimalField(min_value=50, decimal_places=2, required=False)
amount = forms.DecimalField(min_value=0.01, decimal_places=2, required=False)
krestenlaust marked this conversation as resolved.
Show resolved Hide resolved


class PurchaseForm(forms.Form):
Expand Down
16 changes: 8 additions & 8 deletions stregsystem/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@
re_path(r'^(?P<room_id>\d+)/user/(?P<member_id>\d+)/pay$', views.menu_userpay, name="userpay"),
re_path(r'^(?P<room_id>\d+)/user/(?P<member_id>\d+)/rank$', views.menu_userrank, name="userrank"),
re_path(r'^(?P<room_id>\d+)/send_csv_mail/(?P<member_id>\d+)/$', views.send_userdata, name="send_userdata"),
re_path(r'^api/member/payment/qr$', views.qr_payment, name="payment_qr"),
re_path(r'^api/member/active$', views.check_user_active, name="active_member"),
re_path(r'^api/member/sales$', views.get_user_sales, name="get_user_sales"),
re_path(r'^api/member/get_id$', views.convert_username_to_id, name="get_id"),
re_path(r'^api/member/balance$', views.get_user_balance, name="get_user_balance"),
re_path(r'^api/member$', views.get_user_info, name="get_user_transactions"),
re_path(r'^api/products/named_products$', views.dump_named_items, name="named_products"),
re_path(r'^api/products/active_products$', views.dump_active_items, name="active_products"),
re_path(r'^api/member/payment/qr$', views.get_payment_qr, name="get_payment_qr"),
re_path(r'^api/member/active$', views.get_member_active, name="get_member_active"),
re_path(r'^api/member/sales$', views.get_member_sales, name="get_member_sales"),
re_path(r'^api/member/get_id$', views.get_member_id, name="get_member_id"),
re_path(r'^api/member/balance$', views.get_member_balance, name="get_member_balance"),
re_path(r'^api/member$', views.get_member_info, name="get_member_info"),
re_path(r'^api/products/named_products$', views.dump_named_products, name="dump_named_products"),
krestenlaust marked this conversation as resolved.
Show resolved Hide resolved
re_path(r'^api/products/active_products$', views.dump_active_items, name="dump_active_products"),
re_path(r'^api/products/category_mappings$', views.dump_product_category_mappings, name="product_mappings"),
re_path(r'^api/sale$', views.api_sale, name="sale"),
]
13 changes: 12 additions & 1 deletion stregsystem/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import qrcode
import qrcode.image.svg

import urllib.parse

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -161,14 +163,23 @@ def strip_emoji(text):
).strip()


def qr_code(data):
def qr_code(data) -> HttpResponse:
response = HttpResponse(content_type="image/svg+xml")
qr = qrcode.make(data, image_factory=qrcode.image.svg.SvgPathFillImage)
qr.save(response)

return response


def mobilepay_launch_uri(comment: str, amount: float) -> str:
query = {'phone': '90601', 'comment': comment}

if amount is not None:
query['amount'] = amount

return 'mobilepay://send?{}'.format(urllib.parse.urlencode(query))


class stregsystemTestRunner(DiscoverRunner):
def __init__(self, *args, **kwargs):
settings.TEST_MODE = True
Expand Down
69 changes: 39 additions & 30 deletions stregsystem/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from django.shortcuts import get_object_or_404, render, redirect
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from django_select2 import forms as s2forms

from stregreport.views import fjule_party

Expand All @@ -44,6 +45,7 @@
from stregsystem.utils import (
make_active_productlist_query,
qr_code,
mobilepay_launch_uri,
make_room_specific_query,
make_unprocessed_mobilepayment_query,
parse_csv_and_create_mobile_payments,
Expand Down Expand Up @@ -563,18 +565,18 @@ def signup_tool(request):
return render(request, "admin/stregsystem/approval_tools/signup_tool.html", data)


def qr_payment(request):
# API views


def get_payment_qr(request):
form = QRPaymentForm(request.GET)
if not form.is_valid():
return HttpResponseBadRequest("Invalid input for MobilePay QR code generation")

query = {'phone': '90601', 'comment': form.cleaned_data.get('member')}

if form.cleaned_data.get("amount") is not None:
query['amount'] = form.cleaned_data.get("amount")
username = form.cleaned_data.get('username')
amount = form.cleaned_data.get('amount')

data = 'mobilepay://send?{}'.format(urllib.parse.urlencode(query))
krestenlaust marked this conversation as resolved.
Show resolved Hide resolved
return qr_code(data)
return qr_code(mobilepay_launch_uri(username, amount))


def signup(request):
Expand Down Expand Up @@ -620,78 +622,85 @@ def signup_status(request, signup_id):
return render(request, "stregsystem/signup_status.html", locals())


# API views


def dump_active_items(request):
room_id = request.GET.get('room_id') or None
if room_id is None:
return HttpResponseBadRequest("Missing room_id")
return HttpResponseBadRequest("Parameter missing: room_id")
elif not room_id.isdigit():
return HttpResponseBadRequest("Invalid room_id")
return HttpResponseBadRequest("Parameter invalid: room_id")
# TODO: Check whether room exists
items = __get_productlist(room_id)
items_dict = {item.id: (item.name, item.price) for item in items}
items_dict = {item.id: {'name': item.name, 'price': item.price} for item in items}
return JsonResponse(items_dict, json_dumps_params={'ensure_ascii': False})


def check_user_active(request):
def get_member_active(request):
member_id = request.GET.get('member_id') or None
if member_id is None:
return HttpResponseBadRequest("Missing member_id")
return HttpResponseBadRequest("Parameter missing: member_id")
elif not member_id.isdigit():
return HttpResponseBadRequest("Invalid member_id")
return HttpResponseBadRequest("Parameter invalid: member_id")
try:
member = Member.objects.get(pk=member_id)
except Member.DoesNotExist:
return HttpResponseBadRequest("Member not found")
return JsonResponse({'active': member.active})


def convert_username_to_id(request):
def get_member_id(request):
username = request.GET.get('username') or None
if username is None:
return HttpResponseBadRequest("Missing username")
return HttpResponseBadRequest("Parameter missing: username")

try:
member = Member.objects.get(username=username)
except Member.DoesNotExist:
return HttpResponseBadRequest("Invalid username")
return HttpResponseBadRequest("Member not found")
krestenlaust marked this conversation as resolved.
Show resolved Hide resolved

return JsonResponse({'member_id': member.id})


def dump_product_category_mappings(request):
return JsonResponse({p.id: [(cat.id, cat.name) for cat in p.categories.all()] for p in Product.objects.all()})
return JsonResponse(
{
p.id: [{'category_id': cat.id, 'category_name': cat.name} for cat in p.categories.all()]
for p in Product.objects.all()
}
)


def get_user_sales(request):
def get_member_sales(request):
member_id = request.GET.get('member_id') or None
if member_id is None:
return HttpResponseBadRequest("Missing member_id")
return HttpResponseBadRequest("Parameter missing: member_id")
elif not member_id.isdigit():
return HttpResponseBadRequest("Invalid member_id")
return HttpResponseBadRequest("Parameter invalid: member_id")
count = 10 if request.GET.get('count') is None else int(request.GET.get('count') or 10)
sales = Sale.objects.filter(member=member_id).order_by('-timestamp')[:count]
return JsonResponse(
{'sales': [{'timestamp': s.timestamp, 'product': s.product.name, 'price': s.product.price} for s in sales]}
)


def get_user_balance(request):
def get_member_balance(request):
member_id = request.GET.get('member_id') or None
if member_id is None:
return HttpResponseBadRequest("Missing member_id")
return HttpResponseBadRequest("Parameter missing: member_id")
elif not member_id.isdigit():
return HttpResponseBadRequest("Invalid member_id")
return HttpResponseBadRequest("Parameter invalid: member_id")
try:
member = Member.objects.get(pk=member_id)
except Member.DoesNotExist:
return HttpResponseBadRequest("Member not found")
return JsonResponse({'balance': member.balance})


def get_user_info(request):
def get_member_info(request):
member_id = str(request.GET.get('member_id')) or None
if member_id is None or not member_id.isdigit():
return HttpResponseBadRequest("Missing or invalid member_id")
if member_id is None:
return HttpResponseBadRequest("Parameter missing: member_id")
elif not member_id.isdigit():
return HttpResponseBadRequest("Parameter invalid: member_id")

member = find_user_from_id(int(member_id))
if member is None:
Expand All @@ -714,7 +723,7 @@ def find_user_from_id(user_id: int):
return None


def dump_named_items(request):
def dump_named_products(request):
items = NamedProduct.objects.all()
items_dict = {item.name: item.product.id for item in items}
return JsonResponse(items_dict, json_dumps_params={'ensure_ascii': False})
Expand Down
Loading