diff --git a/Pipfile b/Pipfile index f1bcbb5..4268359 100644 --- a/Pipfile +++ b/Pipfile @@ -15,6 +15,7 @@ Django = "==3.2.*" Pillow = "==8.*" [dev-packages] +black = "==21.9b0" [requires] python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock index f40422c..a4fa163 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c079e1ab20a4d9bd30eed7407120599adbc7ef1de635764c5adcab332a08ca1e" + "sha256": "91cd00454ddb91ac311b12f438935d7f4eb3e79e3650bb7a522e8fe3b00e78a2" }, "pipfile-spec": 6, "requires": { @@ -194,5 +194,101 @@ "version": "==1.26.7" } }, - "develop": {} + "develop": { + "black": { + "hashes": [ + "sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115", + "sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91" + ], + "index": "pypi", + "version": "==21.9b0" + }, + "click": { + "hashes": [ + "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", + "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" + ], + "markers": "python_version >= '3.6'", + "version": "==8.0.3" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, + "pathspec": { + "hashes": [ + "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", + "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" + ], + "version": "==0.9.0" + }, + "platformdirs": { + "hashes": [ + "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2", + "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d" + ], + "markers": "python_version >= '3.6'", + "version": "==2.4.0" + }, + "regex": { + "hashes": [ + "sha256:0c186691a7995ef1db61205e00545bf161fb7b59cdb8c1201c89b333141c438a", + "sha256:0dcc0e71118be8c69252c207630faf13ca5e1b8583d57012aae191e7d6d28b84", + "sha256:0f7552429dd39f70057ac5d0e897e5bfe211629652399a21671e53f2a9693a4e", + "sha256:129472cd06062fb13e7b4670a102951a3e655e9b91634432cfbdb7810af9d710", + "sha256:13ec99df95003f56edcd307db44f06fbeb708c4ccdcf940478067dd62353181e", + "sha256:1f2b59c28afc53973d22e7bc18428721ee8ca6079becf1b36571c42627321c65", + "sha256:2b20f544cbbeffe171911f6ce90388ad36fe3fad26b7c7a35d4762817e9ea69c", + "sha256:2fb698037c35109d3c2e30f2beb499e5ebae6e4bb8ff2e60c50b9a805a716f79", + "sha256:34d870f9f27f2161709054d73646fc9aca49480617a65533fc2b4611c518e455", + "sha256:391703a2abf8013d95bae39145d26b4e21531ab82e22f26cd3a181ee2644c234", + "sha256:450dc27483548214314640c89a0f275dbc557968ed088da40bde7ef8fb52829e", + "sha256:45b65d6a275a478ac2cbd7fdbf7cc93c1982d613de4574b56fd6972ceadb8395", + "sha256:5095a411c8479e715784a0c9236568ae72509450ee2226b649083730f3fadfc6", + "sha256:530fc2bbb3dc1ebb17f70f7b234f90a1dd43b1b489ea38cea7be95fb21cdb5c7", + "sha256:56f0c81c44638dfd0e2367df1a331b4ddf2e771366c4b9c5d9a473de75e3e1c7", + "sha256:5e9c9e0ce92f27cef79e28e877c6b6988c48b16942258f3bc55d39b5f911df4f", + "sha256:6d7722136c6ed75caf84e1788df36397efdc5dbadab95e59c2bba82d4d808a4c", + "sha256:74d071dbe4b53c602edd87a7476ab23015a991374ddb228d941929ad7c8c922e", + "sha256:7b568809dca44cb75c8ebb260844ea98252c8c88396f9d203f5094e50a70355f", + "sha256:80bb5d2e92b2258188e7dcae5b188c7bf868eafdf800ea6edd0fbfc029984a88", + "sha256:8d1cdcda6bd16268316d5db1038965acf948f2a6f43acc2e0b1641ceab443623", + "sha256:9f665677e46c5a4d288ece12fdedf4f4204a422bb28ff05f0e6b08b7447796d1", + "sha256:a30513828180264294953cecd942202dfda64e85195ae36c265daf4052af0464", + "sha256:a7a986c45d1099a5de766a15de7bee3840b1e0e1a344430926af08e5297cf666", + "sha256:a940ca7e7189d23da2bfbb38973832813eab6bd83f3bf89a977668c2f813deae", + "sha256:ab7c5684ff3538b67df3f93d66bd3369b749087871ae3786e70ef39e601345b0", + "sha256:be04739a27be55631069b348dda0c81d8ea9822b5da10b8019b789e42d1fe452", + "sha256:c0938ddd60cc04e8f1faf7a14a166ac939aac703745bfcd8e8f20322a7373019", + "sha256:cb46b542133999580ffb691baf67410306833ee1e4f58ed06b6a7aaf4e046952", + "sha256:d134757a37d8640f3c0abb41f5e68b7cf66c644f54ef1cb0573b7ea1c63e1509", + "sha256:de557502c3bec8e634246588a94e82f1ee1b9dfcfdc453267c4fb652ff531570", + "sha256:ded0c4a3eee56b57fcb2315e40812b173cafe79d2f992d50015f4387445737fa", + "sha256:e1dae12321b31059a1a72aaa0e6ba30156fe7e633355e445451e4021b8e122b6", + "sha256:eb672217f7bd640411cfc69756ce721d00ae600814708d35c930930f18e8029f", + "sha256:ee684f139c91e69fe09b8e83d18b4d63bf87d9440c1eb2eeb52ee851883b1b29", + "sha256:f3f9a91d3cc5e5b0ddf1043c0ae5fa4852f18a1c0050318baf5fc7930ecc1f9c" + ], + "version": "==2021.10.23" + }, + "tomli": { + "hashes": [ + "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee", + "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade" + ], + "markers": "python_version >= '3.6'", + "version": "==1.2.2" + }, + "typing-extensions": { + "hashes": [ + "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", + "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", + "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" + ], + "version": "==3.10.0.2" + } + } } diff --git a/bbssite/settings.py b/bbssite/settings.py index 0cbae41..8e14838 100755 --- a/bbssite/settings.py +++ b/bbssite/settings.py @@ -1,5 +1,6 @@ # Django settings for bbs project. import os + BASE_DIR = os.path.dirname(os.path.dirname(__file__)) DEBUG = True @@ -10,92 +11,89 @@ ADMINS = ( MANAGERS = ADMINS DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "db.sqlite3", }, } -TIME_ZONE = 'Europe/Oslo' -LANGUAGE_CODE = 'nb' +TIME_ZONE = "Europe/Oslo" +LANGUAGE_CODE = "nb" USE_I18N = True USE_L10N = True SITE_ID = 1 # Media and uploads -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') -MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, "media") +MEDIA_URL = "/media/" # Static files -STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') -STATIC_URL = '/static/' -STATICFILES_DIRS = ( - os.path.join(BASE_DIR, 'static'), -) +STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") +STATIC_URL = "/static/" +STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),) STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", ) # Make this unique, and don't share it with anybody. -SECRET_KEY = 'osv$uy-ea$*%lc)+xiwt5@2b7au%u*c!pa6hmh#vl)*+tp%fss' +SECRET_KEY = "osv$uy-ea$*%lc)+xiwt5@2b7au%u*c!pa6hmh#vl)*+tp%fss" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.request', - 'django.template.context_processors.debug', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.tz', - 'django.contrib.messages.context_processors.messages', + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.request", + "django.template.context_processors.debug", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.tz", + "django.contrib.messages.context_processors.messages", ] }, }, ] MIDDLEWARE = [ - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', + "django.middleware.common.CommonMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", ] -ROOT_URLCONF = 'bbssite.urls' +ROOT_URLCONF = "bbssite.urls" INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.admin', - - 'widget_tweaks', - 'bootstrapform', - 'django_extensions', - 'rest_framework', - 'easy_thumbnails', - 'raven.contrib.django.raven_compat', + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.sites", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.admin", + "widget_tweaks", + "bootstrapform", + "django_extensions", + "rest_framework", + "easy_thumbnails", + "raven.contrib.django.raven_compat", ] LOCAL_APPS = [ - 'main', + "main", ] INSTALLED_APPS += LOCAL_APPS -LOGIN_REDIRECT_URL = '/' +LOGIN_REDIRECT_URL = "/" SESSION_COOKIE_AGE = 1209600 # 15 minutes SESSION_EXPIRE_AT_BROWSER_CLOSE = True @@ -105,54 +103,52 @@ BBS_LIMIT_DEPOSITS = False # Thumbnails THUMBNAIL_ALIASES = { - '': { - 'product': {'size': (64, 64), 'crop': True}, + "": { + "product": {"size": (64, 64), "crop": True}, }, } LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'root': { - 'level': 'INFO', - 'handlers': ['console', 'sentry'], + "version": 1, + "disable_existing_loggers": False, + "root": { + "level": "INFO", + "handlers": ["console", "sentry"], }, - 'formatters': { - 'verbose': { - 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + "formatters": { + "verbose": {"format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"}, + }, + "handlers": { + "sentry": { + "level": "WARNING", + "class": "raven.contrib.django.raven_compat.handlers.SentryHandler", + }, + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "verbose", }, }, - 'handlers': { - 'sentry': { - 'level': 'WARNING', - 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler', + "loggers": { + "django.db.backends": { + "level": "ERROR", + "handlers": ["console"], + "propagate": False, }, - 'console': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', - 'formatter': 'verbose' - } - }, - 'loggers': { - 'django.db.backends': { - 'level': 'ERROR', - 'handlers': ['console'], - 'propagate': False, + "raven": { + "level": "WARNING", + "handlers": ["console"], + "propagate": False, }, - 'raven': { - 'level': 'WARNING', - 'handlers': ['console'], - 'propagate': False, - }, - 'sentry.errors': { - 'level': 'WARNING', - 'handlers': ['console'], - 'propagate': False, + "sentry.errors": { + "level": "WARNING", + "handlers": ["console"], + "propagate": False, }, }, } -DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" try: from .local_settings import * diff --git a/bbssite/urls.py b/bbssite/urls.py index 3d9d8d6..b9e9be9 100755 --- a/bbssite/urls.py +++ b/bbssite/urls.py @@ -11,17 +11,24 @@ from main import urls as main_urls admin.autodiscover() urlpatterns = [ - re_path(r'', include(main_urls)), - path('accounts/login/', LoginView.as_view(), name='django.contrib.auth.views.login'), - path('accounts/logout/', LogoutView.as_view(), name='django.contrib.auth.views.logout'), - - path('admin/', admin.site.urls), + re_path(r"", include(main_urls)), + path("accounts/login/", LoginView.as_view(), name="django.contrib.auth.views.login"), + path( + "accounts/logout/", + LogoutView.as_view(), + name="django.contrib.auth.views.logout", + ), + path("admin/", admin.site.urls), ] urlpatterns += staticfiles_urlpatterns() if settings.DEBUG: urlpatterns += [ - re_path(r'^media/(?P.*)$', serve, { - 'document_root': settings.MEDIA_ROOT, - }) + re_path( + r"^media/(?P.*)$", + serve, + { + "document_root": settings.MEDIA_ROOT, + }, + ) ] diff --git a/bbssite/wsgi.py b/bbssite/wsgi.py index 6a29147..b48fafd 100644 --- a/bbssite/wsgi.py +++ b/bbssite/wsgi.py @@ -2,7 +2,7 @@ import os from django.core.wsgi import get_wsgi_application from raven.contrib.django.middleware.wsgi import Sentry -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bbssite.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bbssite.settings") application = get_wsgi_application() application = Sentry(application) diff --git a/bin/format b/bin/format new file mode 100755 index 0000000..370ed80 --- /dev/null +++ b/bin/format @@ -0,0 +1,2 @@ +#!/bin/bash +black main bbssite diff --git a/bin/lint b/bin/lint new file mode 100755 index 0000000..e69de29 diff --git a/main/admin.py b/main/admin.py index 08dd370..eba38ec 100644 --- a/main/admin.py +++ b/main/admin.py @@ -5,6 +5,7 @@ from main.models import * class ProductAdmin(admin.ModelAdmin): pass + admin.site.register(Product, ProductAdmin) admin.site.register(InventoryTransaction) admin.site.register(Order) diff --git a/main/api/serializers.py b/main/api/serializers.py index bb8274e..2a00f87 100644 --- a/main/api/serializers.py +++ b/main/api/serializers.py @@ -8,33 +8,42 @@ from main.models import Product, OrderLine, Order class ProductSerializer(ModelSerializer): class Meta: model = Product - fields = '__all__' + fields = "__all__" class ProductStatSerializer(ModelSerializer): - EXTERNAL_USER = 'Ekstern' + EXTERNAL_USER = "Ekstern" counts = serializers.SerializerMethodField() def _format_counts(self, per_user): per_user = list(per_user) for row in per_user: - first = row.pop('order__customer__first_name') - last = row.pop('order__customer__last_name') + first = row.pop("order__customer__first_name") + last = row.pop("order__customer__last_name") if first is None: who = self.EXTERNAL_USER else: - who = '{} {}'.format(first, last) - row['full_name'] = who + who = "{} {}".format(first, last) + row["full_name"] = who return per_user def get_counts(self, obj): - name_fields = ['order__customer__first_name', 'order__customer__last_name'] + name_fields = ["order__customer__first_name", "order__customer__last_name"] per_user = OrderLine.objects.filter(product=obj) - per_user = per_user.values(*name_fields).annotate(amount_total=Sum('amount')).order_by('-amount_total') + per_user = per_user.values(*name_fields).annotate(amount_total=Sum("amount")).order_by("-amount_total") return self._format_counts(per_user) class Meta: model = Product - fields = ['id', 'name', 'description', 'image', 'inventory_amount', 'active', 'created', 'counts'] + fields = [ + "id", + "name", + "description", + "image", + "inventory_amount", + "active", + "created", + "counts", + ] diff --git a/main/api/views.py b/main/api/views.py index f39d2ee..be87511 100644 --- a/main/api/views.py +++ b/main/api/views.py @@ -7,16 +7,13 @@ from main.models import Product from rest_framework.viewsets import GenericViewSet -class ProductViewSet(mixins.ListModelMixin, - mixins.RetrieveModelMixin, - GenericViewSet): +class ProductViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): serializer_class = ProductSerializer queryset = Product.objects.all() - @action(detail=False, methods=['get']) + @action(detail=False, methods=["get"]) def group_by_user(self, request): products = self.get_queryset().filter(active=True) serializer = ProductStatSerializer(products, many=True) return Response(serializer.data) - diff --git a/main/apps.py b/main/apps.py index 3ea1f72..d552816 100644 --- a/main/apps.py +++ b/main/apps.py @@ -2,8 +2,8 @@ from django.apps import AppConfig class BBSAppConfig(AppConfig): - name = 'main' - verbose_name = 'Biceps Bar System' + name = "main" + verbose_name = "Biceps Bar System" def ready(self): # all models are loaded, could attach signals diff --git a/main/forms.py b/main/forms.py index 6a2eaa8..a9285e0 100644 --- a/main/forms.py +++ b/main/forms.py @@ -10,23 +10,23 @@ class OrderForm(forms.ModelForm): customer_name = forms.CharField(required=False) def clean(self): - if self.cleaned_data['customer'] is None and self.cleaned_data['customer_name'] != "Ekstern": + if self.cleaned_data["customer"] is None and self.cleaned_data["customer_name"] != "Ekstern": raise forms.ValidationError(_("Du har ikke valgt hvem som skal kjøpe.")) return self.cleaned_data def clean_customer(self): - if self.cleaned_data['customer'] is None: - return self.cleaned_data['customer'] + if self.cleaned_data["customer"] is None: + return self.cleaned_data["customer"] - if self.cleaned_data['customer'].profile.balance <= 0: + if self.cleaned_data["customer"].profile.balance <= 0: raise forms.ValidationError(_("Du har negativ saldo og kan ikke kjøpe noe.")) - return self.cleaned_data['customer'] + return self.cleaned_data["customer"] class Meta: model = Order - fields = ('customer',) + fields = ("customer",) class OrderLineForm(forms.Form): @@ -37,10 +37,9 @@ class OrderLineForm(forms.Form): class DepositForm(forms.ModelForm): - class Meta: model = Transaction - fields = ['user', 'amount'] + fields = ["user", "amount"] class SimpleCreateUserForm(forms.Form): @@ -50,15 +49,15 @@ class SimpleCreateUserForm(forms.Form): facebook_username = forms.CharField(required=False, label="Facebook-brukernavn") def _get_facebook_url(self): - if self.cleaned_data['facebook_username'] == "": + if self.cleaned_data["facebook_username"] == "": return "" - return "https://graph.facebook.com/{0}/picture".format(self.cleaned_data['facebook_username']) + return "https://graph.facebook.com/{0}/picture".format(self.cleaned_data["facebook_username"]) - def save(self, commit=True): - username = format_username(self.cleaned_data['first_name'], self.cleaned_data['last_name']) - user = User.objects.create_user(username, email=self.cleaned_data['email']) - user.first_name = self.cleaned_data['first_name'] - user.last_name = self.cleaned_data['last_name'] + def save(self, commit=True): + username = format_username(self.cleaned_data["first_name"], self.cleaned_data["last_name"]) + user = User.objects.create_user(username, email=self.cleaned_data["email"]) + user.first_name = self.cleaned_data["first_name"] + user.last_name = self.cleaned_data["last_name"] profile = UserProfile.objects.create(user=user) profile.image = self._get_facebook_url() @@ -76,11 +75,11 @@ class InventoryTransactionForm(forms.ModelForm): def __init__(self, *args, **kwargs): # Only show active products super(InventoryTransactionForm, self).__init__(*args, **kwargs) - self.fields['product'].queryset = Product.objects.filter(active=True) + self.fields["product"].queryset = Product.objects.filter(active=True) class Meta: model = InventoryTransaction - fields = ['product', 'amount', 'unit_price', 'comment'] + fields = ["product", "amount", "unit_price", "comment"] class DateRangeForm(forms.Form): diff --git a/main/management/__init__.py b/main/management/__init__.py index 89ba269..977de6d 100644 --- a/main/management/__init__.py +++ b/main/management/__init__.py @@ -1 +1 @@ -__author__ = 'Nikolai' +__author__ = "Nikolai" diff --git a/main/management/commands/__init__.py b/main/management/commands/__init__.py index 89ba269..977de6d 100644 --- a/main/management/commands/__init__.py +++ b/main/management/commands/__init__.py @@ -1 +1 @@ -__author__ = 'Nikolai' +__author__ = "Nikolai" diff --git a/main/management/commands/facebook_sync.py b/main/management/commands/facebook_sync.py index 03934b3..b3f0d58 100644 --- a/main/management/commands/facebook_sync.py +++ b/main/management/commands/facebook_sync.py @@ -1,4 +1,4 @@ -xfrom django.contrib.auth.models import User +from django.contrib.auth.models import User import difflib from django.core.management.base import BaseCommand, CommandError @@ -13,16 +13,17 @@ class Command(BaseCommand): - Matches names against the local DB - Saves the users Facebook picture in his/her user profile. """ - KAK_GROUP_ID = '108265352570560' + + KAK_GROUP_ID = "108265352570560" graph = None def get_kak_members(self): - members = self.graph.get_connections(id=self.KAK_GROUP_ID, connection_name='members') - return [(member['name'], member['id']) for member in members['data']] + members = self.graph.get_connections(id=self.KAK_GROUP_ID, connection_name="members") + return [(member["name"], member["id"]) for member in members["data"]] def handle(self, *args, **options): if len(args) != 1: - raise CommandError('Missing access token') + raise CommandError("Missing access token") access_token = args[0] self.graph = facebook.GraphAPI(access_token=access_token) @@ -31,8 +32,8 @@ class Command(BaseCommand): names = [] # to fuzzy search for a name name_lookup = {} # to update the matching UserProfile object - for u in User.objects.all().order_by('id'): - fullname = u'{} {}'.format(u.first_name, u.last_name) + for u in User.objects.all().order_by("id"): + fullname = "{} {}".format(u.first_name, u.last_name) names.append(fullname) name_lookup[fullname] = u.id @@ -41,6 +42,5 @@ class Command(BaseCommand): if match: u = UserProfile.objects.get(pk=name_lookup[match[0]]) if str(u.image) is "": - u.image = 'https://graph.facebook.com/{0}/picture'.format(member[1]) + u.image = "https://graph.facebook.com/{0}/picture".format(member[1]) u.save() - diff --git a/main/migrations/0001_initial.py b/main/migrations/0001_initial.py index 6c67cfd..fe7872a 100644 --- a/main/migrations/0001_initial.py +++ b/main/migrations/0001_initial.py @@ -10,89 +10,176 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='InventoryTransaction', + name="InventoryTransaction", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('amount', models.FloatField(verbose_name='Antall')), - ('unit_price', models.FloatField(verbose_name='Enhetspris')), - ('comment', models.TextField(verbose_name='Kommentar', blank=True)), - ('created', models.DateTimeField(auto_now_add=True)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("amount", models.FloatField(verbose_name="Antall")), + ("unit_price", models.FloatField(verbose_name="Enhetspris")), + ("comment", models.TextField(verbose_name="Kommentar", blank=True)), + ("created", models.DateTimeField(auto_now_add=True)), ], ), migrations.CreateModel( - name='Order', + name="Order", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('order_sum', models.FloatField(db_column='sum')), - ('created', models.DateTimeField(auto_now_add=True)), - ('customer', models.ForeignKey(related_name='orders', on_delete=models.CASCADE, blank=True, to=settings.AUTH_USER_MODEL, null=True)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("order_sum", models.FloatField(db_column="sum")), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "customer", + models.ForeignKey( + related_name="orders", + on_delete=models.CASCADE, + blank=True, + to=settings.AUTH_USER_MODEL, + null=True, + ), + ), ], ), migrations.CreateModel( - name='OrderLine', + name="OrderLine", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('amount', models.IntegerField()), - ('unit_price', models.FloatField(verbose_name='Enhetspris')), - ('order', models.ForeignKey(on_delete=models.CASCADE, related_name='orderlines', to='main.Order')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("amount", models.IntegerField()), + ("unit_price", models.FloatField(verbose_name="Enhetspris")), + ( + "order", + models.ForeignKey( + on_delete=models.CASCADE, + related_name="orderlines", + to="main.Order", + ), + ), ], ), migrations.CreateModel( - name='Product', + name="Product", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=64)), - ('sale_price_int', models.FloatField()), - ('sale_price_ext', models.FloatField()), - ('description', models.CharField(max_length=256, blank=True)), - ('volume_liter', models.FloatField(null=True)), - ('alcohol_percent', models.FloatField(null=True)), - ('image', models.ImageField(upload_to='products', blank=True)), - ('customer_support', models.CharField(max_length=32, blank=True)), - ('inventory_amount', models.FloatField(default=0)), - ('active', models.BooleanField(default=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, + ), + ), + ("name", models.CharField(max_length=64)), + ("sale_price_int", models.FloatField()), + ("sale_price_ext", models.FloatField()), + ("description", models.CharField(max_length=256, blank=True)), + ("volume_liter", models.FloatField(null=True)), + ("alcohol_percent", models.FloatField(null=True)), + ("image", models.ImageField(upload_to="products", blank=True)), + ("customer_support", models.CharField(max_length=32, blank=True)), + ("inventory_amount", models.FloatField(default=0)), + ("active", models.BooleanField(default=True)), + ("created", models.DateTimeField(auto_now_add=True)), + ("updated", models.DateTimeField(auto_now=True)), ], options={ - 'ordering': ['name'], + "ordering": ["name"], }, ), migrations.CreateModel( - name='Transaction', + name="Transaction", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('amount', models.FloatField()), - ('created', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=models.CASCADE, related_name='transactions', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("amount", models.FloatField()), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "user", + models.ForeignKey( + on_delete=models.CASCADE, + related_name="transactions", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='UserProfile', + name="UserProfile", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('balance', models.FloatField(default=0.0)), - ('image', models.URLField(blank=True)), - ('user', models.OneToOneField(on_delete=models.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("balance", models.FloatField(default=0.0)), + ("image", models.URLField(blank=True)), + ( + "user", + models.OneToOneField( + on_delete=models.CASCADE, + related_name="profile", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( - model_name='orderline', - name='product', - field=models.ForeignKey(on_delete=models.CASCADE, related_name='orderlines', to='main.Product'), + model_name="orderline", + name="product", + field=models.ForeignKey(on_delete=models.CASCADE, related_name="orderlines", to="main.Product"), ), migrations.AddField( - model_name='inventorytransaction', - name='product', - field=models.ForeignKey(on_delete=models.CASCADE, related_name='transactions', verbose_name='Produkt', to='main.Product'), + model_name="inventorytransaction", + name="product", + field=models.ForeignKey( + on_delete=models.CASCADE, + related_name="transactions", + verbose_name="Produkt", + to="main.Product", + ), ), migrations.AddField( - model_name='inventorytransaction', - name='user', - field=models.ForeignKey(on_delete=models.CASCADE, related_name='inventory_transactions', to=settings.AUTH_USER_MODEL), + model_name="inventorytransaction", + name="user", + field=models.ForeignKey( + on_delete=models.CASCADE, + related_name="inventory_transactions", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterUniqueTogether( - name='orderline', - unique_together=set([('order', 'product')]), + name="orderline", + unique_together=set([("order", "product")]), ), ] diff --git a/main/models.py b/main/models.py index 0579810..5ea39a4 100755 --- a/main/models.py +++ b/main/models.py @@ -1,4 +1,3 @@ - from datetime import datetime from django.conf import settings @@ -6,7 +5,6 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ - class Product(models.Model): name = models.CharField(max_length=64) sale_price_int = models.FloatField() @@ -14,7 +12,7 @@ class Product(models.Model): description = models.CharField(max_length=256, blank=True) volume_liter = models.FloatField(null=True) alcohol_percent = models.FloatField(null=True) - image = models.ImageField(upload_to='products', blank=True) + image = models.ImageField(upload_to="products", blank=True) customer_support = models.CharField(max_length=32, blank=True) inventory_amount = models.FloatField(default=0) active = models.BooleanField(default=True) @@ -24,20 +22,20 @@ class Product(models.Model): @property def wholesale_unit_price(self): """ - This is not accurate. + This is not accurate. """ if self.inventory_amount <= 0: return 0 - unit_prices = self.transactions.order_by('created').values('amount', 'unit_price') + unit_prices = self.transactions.order_by("created").values("amount", "unit_price") sum_units = 0 for up in unit_prices: - sum_units += up['amount'] + sum_units += up["amount"] if self.inventory_amount <= sum_units: - return up['unit_price'] + return up["unit_price"] if len(unit_prices) > 0: - return unit_prices[0]['unit_price'] + return unit_prices[0]["unit_price"] return 0 @@ -49,13 +47,18 @@ class Product(models.Model): return self.name class Meta: - ordering = ['name'] - + ordering = ["name"] class Order(models.Model): - customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True, related_name='orders') - order_sum = models.FloatField(db_column='sum') + customer = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + null=True, + blank=True, + related_name="orders", + ) + order_sum = models.FloatField(db_column="sum") created = models.DateTimeField(auto_now_add=True) def is_external(self): @@ -68,12 +71,11 @@ class Order(models.Model): return "{}: {} kr".format(self.customer or "Ekstern", self.order_sum) - class OrderLine(models.Model): - order = models.ForeignKey('main.Order', on_delete=models.CASCADE, related_name='orderlines') - product = models.ForeignKey('main.Product', on_delete=models.CASCADE, related_name='orderlines') + order = models.ForeignKey("main.Order", on_delete=models.CASCADE, related_name="orderlines") + product = models.ForeignKey("main.Product", on_delete=models.CASCADE, related_name="orderlines") amount = models.IntegerField() - unit_price = models.FloatField(verbose_name=_('Enhetspris')) + unit_price = models.FloatField(verbose_name=_("Enhetspris")) @property def price(self): @@ -83,13 +85,13 @@ class OrderLine(models.Model): return "{} {} ({} kr per)".format(self.amount, self.product, self.unit_price) class Meta: - unique_together = ('order', 'product') - + unique_together = ("order", "product") class Transaction(models.Model): - """ Money """ - user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='transactions') + """Money""" + + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="transactions") amount = models.FloatField() created = models.DateTimeField(auto_now_add=True) @@ -97,14 +99,23 @@ class Transaction(models.Model): return "{}: {}: {}".format(self.id, self.user, self.amount) - class InventoryTransaction(models.Model): - """ Goods """ - user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='inventory_transactions') - product = models.ForeignKey('main.Product', on_delete=models.CASCADE, related_name='transactions', verbose_name=_('Produkt')) - amount = models.FloatField(verbose_name=_('Antall')) - unit_price = models.FloatField(verbose_name=_('Enhetspris')) - comment = models.TextField(verbose_name=_('Kommentar'), blank=True) + """Goods""" + + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="inventory_transactions", + ) + product = models.ForeignKey( + "main.Product", + on_delete=models.CASCADE, + related_name="transactions", + verbose_name=_("Produkt"), + ) + amount = models.FloatField(verbose_name=_("Antall")) + unit_price = models.FloatField(verbose_name=_("Enhetspris")) + comment = models.TextField(verbose_name=_("Kommentar"), blank=True) created = models.DateTimeField(auto_now_add=True) @property @@ -113,16 +124,12 @@ class InventoryTransaction(models.Model): def __str__(self): return "{}: {}: {}. {:.0f} stykker ({} kr per)".format( - self.id, - self.user, - self.product, - self.amount, - self.unit_price) - + self.id, self.user, self.product, self.amount, self.unit_price + ) class UserProfile(models.Model): - user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile') + user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile") balance = models.FloatField(default=0.0) image = models.URLField(blank=True) @@ -130,7 +137,7 @@ class UserProfile(models.Model): if len(self.user.orders.all()) == 0: return None - return self.user.orders.order_by('-created')[0] + return self.user.orders.order_by("-created")[0] def last_purchase_date(self): last = self.last_purchase() @@ -141,7 +148,7 @@ class UserProfile(models.Model): from django.templatetags.static import static if not self.image: - return static('dist/images/unknown_person.png') + return static("dist/images/unknown_person.png") return self.image diff --git a/main/urls.py b/main/urls.py index de9561e..af4b711 100644 --- a/main/urls.py +++ b/main/urls.py @@ -2,46 +2,64 @@ from django.conf.urls import url from django.contrib.staticfiles.views import serve from main.api.views import ProductViewSet -from main.views.stats import (stats_list, stats_products_realtime, stats_products_per_user, - OrdersHourlyView, OrdersDailyView, OrdersMonthlyView, OrdersYearlyView) +from main.views.stats import ( + stats_list, + stats_products_realtime, + stats_products_per_user, + OrdersHourlyView, + OrdersDailyView, + OrdersMonthlyView, + OrdersYearlyView, +) from main.views.general import register, deposit, log from main.views.reports import inventory, inventory_add, report from main.views.users import create_user, profile from rest_framework.routers import DefaultRouter urlpatterns = [ - url(r'^$', register, name='home'), - url(r'^register/$', register, name='register'), - url(r'^deposit/$', deposit, name='deposit'), - url(r'^log/$', log, name='log'), - url(r'^log/all$', log, kwargs={'limit': None}, name='log-all'), - + url(r"^$", register, name="home"), + url(r"^register/$", register, name="register"), + url(r"^deposit/$", deposit, name="deposit"), + url(r"^log/$", log, name="log"), + url(r"^log/all$", log, kwargs={"limit": None}, name="log-all"), # Users - url(r'^user/create/$', create_user, name='create-user'), - url(r'^profile/$', profile, name='profile'), - + url(r"^user/create/$", create_user, name="create-user"), + url(r"^profile/$", profile, name="profile"), # Reports - url(r'^inventory/$', inventory, name='inventory'), - url(r'^inventory/add/$', inventory_add, name='inventory-add'), - url(r'^report$', report, name='report'), - + url(r"^inventory/$", inventory, name="inventory"), + url(r"^inventory/add/$", inventory_add, name="inventory-add"), + url(r"^report$", report, name="report"), ] # Stats urlpatterns += [ - url(r'^stats/$', stats_list, name='stats'), - url(r'^stats/orders/hourly/$', OrdersHourlyView.as_view(), name='stats-orders-hourly'), - url(r'^stats/orders/daily/', OrdersDailyView.as_view(), name='stats-orders-daily'), - url(r'^stats/orders/monthly/', OrdersMonthlyView.as_view(), name='stats-orders-monthly'), - url(r'^stats/orders/yearly/', OrdersYearlyView.as_view(), name='stats-orders-yearly'), - url(r'^stats/orders/products_realtime$', stats_products_realtime, name='stats-products-realtime'), - url(r'^stats/products/by_user/(?P[0-9]+)/$', stats_products_per_user, name='stats-products-per-user'), + url(r"^stats/$", stats_list, name="stats"), + url( + r"^stats/orders/hourly/$", + OrdersHourlyView.as_view(), + name="stats-orders-hourly", + ), + url(r"^stats/orders/daily/", OrdersDailyView.as_view(), name="stats-orders-daily"), + url( + r"^stats/orders/monthly/", + OrdersMonthlyView.as_view(), + name="stats-orders-monthly", + ), + url(r"^stats/orders/yearly/", OrdersYearlyView.as_view(), name="stats-orders-yearly"), + url( + r"^stats/orders/products_realtime$", + stats_products_realtime, + name="stats-products-realtime", + ), + url( + r"^stats/products/by_user/(?P[0-9]+)/$", + stats_products_per_user, + name="stats-products-per-user", + ), ] # Root level service-worker.js -urlpatterns += [ - url(r'^service-worker\.js$', serve, kwargs={'path': 'dist/service-worker.js'}) -] +urlpatterns += [url(r"^service-worker\.js$", serve, kwargs={"path": "dist/service-worker.js"})] # API router = DefaultRouter() -router.register(r'api/products', ProductViewSet) +router.register(r"api/products", ProductViewSet) urlpatterns += router.urls diff --git a/main/utils.py b/main/utils.py index ea273d3..7175fbe 100644 --- a/main/utils.py +++ b/main/utils.py @@ -1,25 +1,23 @@ - from django.contrib.auth.models import User from django.db.models import Q def format_username(firstname, lastname): - first = firstname.lower().strip().replace(" ", "")[:6].encode('ascii', 'ignore').decode('utf-8') - last = lastname.lower().strip().replace(" ", "")[:6].encode('ascii', 'ignore').decode('utf-8') + first = firstname.lower().strip().replace(" ", "")[:6].encode("ascii", "ignore").decode("utf-8") + last = lastname.lower().strip().replace(" ", "")[:6].encode("ascii", "ignore").decode("utf-8") return "{0}{1}".format(first, last) def users_with_perm(perm_name): return User.objects.filter( - Q(is_superuser=True) | - Q(user_permissions__codename=perm_name) | - Q(groups__permissions__codename=perm_name)).distinct() + Q(is_superuser=True) | Q(user_permissions__codename=perm_name) | Q(groups__permissions__codename=perm_name) + ).distinct() def users_format_js(users): users_js = [] for user in users: - users_js.append('"' + user.first_name + ' ' + user.last_name[:1] + '"') - users_js = "[{}]".format(u",".join(users_js)) + users_js.append('"' + user.first_name + " " + user.last_name[:1] + '"') + users_js = "[{}]".format(",".join(users_js)) return users_js diff --git a/main/views/general.py b/main/views/general.py index 22edf97..b4d425a 100644 --- a/main/views/general.py +++ b/main/views/general.py @@ -16,7 +16,7 @@ from main.utils import users_format_js, users_with_perm def register(request): products = Product.objects.filter(active=True) - cheapest_product_price = list(products.aggregate(Min('sale_price_int')).values())[0] or 0 + cheapest_product_price = list(products.aggregate(Min("sale_price_int")).values())[0] or 0 half_a_year_ago = datetime.now() - timedelta(days=180) users = User.objects.filter(profile__balance__gte=cheapest_product_price) @@ -34,24 +34,28 @@ def register(request): order = orderform.save(commit=False) if order.is_external(): - order_sum = sum([ol['amount'] * ol['unit_price_ext'] for ol in formset.cleaned_data]) + order_sum = sum([ol["amount"] * ol["unit_price_ext"] for ol in formset.cleaned_data]) else: - order_sum = sum([ol['amount'] * ol['unit_price_int'] for ol in formset.cleaned_data]) + order_sum = sum([ol["amount"] * ol["unit_price_int"] for ol in formset.cleaned_data]) # empty order? if order_sum == 0: - messages.error(request, 'Du har ikke valgt hva du skal kjøpe...') - return HttpResponseRedirect(reverse('register')) + messages.error(request, "Du har ikke valgt hva du skal kjøpe...") + return HttpResponseRedirect(reverse("register")) if not order.is_external(): profile = order.customer.profile # can she afford it? if profile.balance < order_sum: - messages.error(request, '{0} {1} har ikke råd, mangler {2} kr.'.format( - order.customer.first_name, - order.customer.last_name, - int(order_sum - profile.balance))) - return HttpResponseRedirect(reverse('register')) + messages.error( + request, + "{0} {1} har ikke råd, mangler {2} kr.".format( + order.customer.first_name, + order.customer.last_name, + int(order_sum - profile.balance), + ), + ) + return HttpResponseRedirect(reverse("register")) # substract order from balance profile.balance -= order_sum profile.save() @@ -61,106 +65,117 @@ def register(request): orderlines = [] for ol in formset.cleaned_data: - if ol['amount'] > 0: - product = Product.objects.get(pk=ol['product']) + if ol["amount"] > 0: + product = Product.objects.get(pk=ol["product"]) if order.is_external(): - unit_price = ol['unit_price_ext'] + unit_price = ol["unit_price_ext"] else: - unit_price = ol['unit_price_int'] + unit_price = ol["unit_price_int"] OrderLine.objects.create( order=order, product=product, - amount=ol['amount'], - unit_price=unit_price) + amount=ol["amount"], + unit_price=unit_price, + ) - product.inventory_amount -= ol['amount'] + product.inventory_amount -= ol["amount"] product.save() - orderlines.append("{0} {1}".format(ol['amount'], product)) + orderlines.append("{0} {1}".format(ol["amount"], product)) if order.is_external(): - messages.success(request, 'Ekstern kjøpte {0}.'.format(", ".join(orderlines))) + messages.success(request, "Ekstern kjøpte {0}.".format(", ".join(orderlines))) else: - messages.success(request, '{0} {1} kjøpte {2}.'.format( - order.customer.first_name, - order.customer.last_name, - ", ".join(orderlines) - )) + messages.success( + request, + "{0} {1} kjøpte {2}.".format( + order.customer.first_name, + order.customer.last_name, + ", ".join(orderlines), + ), + ) - return redirect('register') + return redirect("register") else: # TODO specify error(s) - messages.error(request, 'Skjemaet er ikke gyldig.') + messages.error(request, "Skjemaet er ikke gyldig.") orderform = OrderForm(request.POST) formset = OrderLineFormSet(request.POST) else: orderform = OrderForm() formset = OrderLineFormSet() - - return render(request, 'register.html', locals()) + + return render(request, "register.html", locals()) def deposit(request): - users = User.objects.all().order_by('first_name', 'last_name') + users = User.objects.all().order_by("first_name", "last_name") users_js = users_format_js(users) - allowed_users = users_with_perm('add_transaction') - error_message_template = '{0} {1} kan ikke sette inn {2} kr, det overskrider maks saldo ({3} kr) med {4} kr' + allowed_users = users_with_perm("add_transaction") + error_message_template = "{0} {1} kan ikke sette inn {2} kr, det overskrider maks saldo ({3} kr) med {4} kr" limit_deposists = settings.BBS_LIMIT_DEPOSITS if request.method == "POST": form = DepositForm(request.POST) if form.is_valid(): - amount = form.cleaned_data['amount'] - user = form.cleaned_data['user'] + amount = form.cleaned_data["amount"] + user = form.cleaned_data["user"] if amount + user.profile.balance > settings.BBS_SALDO_MAX and amount + user.profile.balance != 1337: - messages.error(request, error_message_template.format( - user.first_name, - user.last_name, - amount, - settings.BBS_SALDO_MAX, - amount + user.profile.balance - settings.BBS_SALDO_MAX - )) - return HttpResponseRedirect(reverse('deposit')) + messages.error( + request, + error_message_template.format( + user.first_name, + user.last_name, + amount, + settings.BBS_SALDO_MAX, + amount + user.profile.balance - settings.BBS_SALDO_MAX, + ), + ) + return HttpResponseRedirect(reverse("deposit")) transaction = form.save() profile = transaction.user.profile profile.balance += transaction.amount profile.save() - messages.success(request, '{0} {1} satte inn {2} kr. Ny saldo er {3}'.format( - transaction.user.first_name, - transaction.user.last_name, - transaction.amount, - profile.balance)) - return HttpResponseRedirect(reverse('deposit')) + messages.success( + request, + "{0} {1} satte inn {2} kr. Ny saldo er {3}".format( + transaction.user.first_name, + transaction.user.last_name, + transaction.amount, + profile.balance, + ), + ) + return HttpResponseRedirect(reverse("deposit")) else: - messages.error(request, 'Skjemaet er ikke gyldig.') + messages.error(request, "Skjemaet er ikke gyldig.") form = DepositForm(request.POST) else: form = DepositForm() - return render(request, 'deposit.html', locals()) + return render(request, "deposit.html", locals()) -def log(request, limit=datetime.now()-timedelta(days=2)): +def log(request, limit=datetime.now() - timedelta(days=2)): if limit: # display last two days of orders and transactions - orders = Order.objects.filter(created__gte=limit).order_by('-created') - transactions = Transaction.objects.filter(created__gte=limit).order_by('-created') + orders = Order.objects.filter(created__gte=limit).order_by("-created") + transactions = Transaction.objects.filter(created__gte=limit).order_by("-created") # no action in the last two days? # ...then display the last 5 items if len(orders) == 0: - orders = Order.objects.all().order_by('-created')[:5] + orders = Order.objects.all().order_by("-created")[:5] if len(transactions) == 0: - transactions = Transaction.objects.all().order_by('-created')[:5] + transactions = Transaction.objects.all().order_by("-created")[:5] else: # display everything - orders = Order.objects.all().order_by('-created') - transactions = Transaction.objects.all().order_by('-created') + orders = Order.objects.all().order_by("-created") + transactions = Transaction.objects.all().order_by("-created") - return render(request, 'log.html', locals()) + return render(request, "log.html", locals()) diff --git a/main/views/reports.py b/main/views/reports.py index 50613ad..f5653eb 100644 --- a/main/views/reports.py +++ b/main/views/reports.py @@ -16,58 +16,66 @@ def report(request): # end of day end_time = datetime.now().replace(hour=23, minute=59, second=59, microsecond=999) - if request.GET.get('start_time', False) or request.GET.get('end_time', False): + if request.GET.get("start_time", False) or request.GET.get("end_time", False): form = DateRangeForm(request.GET) if form.is_valid(): - start_time = form.cleaned_data['start_time'] + start_time = form.cleaned_data["start_time"] # end of day - end_time = form.cleaned_data['end_time'].replace(hour=23, minute=59, second=59, microsecond=999) + end_time = form.cleaned_data["end_time"].replace(hour=23, minute=59, second=59, microsecond=999) else: - form = DateRangeForm(initial={ - 'start_time': start_time.strftime("%Y-%m-%d"), - 'end_time': end_time.strftime("%Y-%m-%d") - }) + form = DateRangeForm( + initial={ + "start_time": start_time.strftime("%Y-%m-%d"), + "end_time": end_time.strftime("%Y-%m-%d"), + } + ) # Inventory - products = Product.objects\ - .filter(transactions__created__range=[start_time, end_time])\ - .values('name', 'transactions__unit_price', 'transactions__amount')\ - .order_by('name') + products = ( + Product.objects.filter(transactions__created__range=[start_time, end_time]) + .values("name", "transactions__unit_price", "transactions__amount") + .order_by("name") + ) # group by name and sum inventory_products = {} - for k, g in groupby(products, lambda x: x['name']): + for k, g in groupby(products, lambda x: x["name"]): group = [] for el in g: - el['transactions__price'] = el['transactions__amount'] * el['transactions__unit_price'] + el["transactions__price"] = el["transactions__amount"] * el["transactions__unit_price"] group.append(el) - inventory_products.update({k: { - 'name': k, - "transactions": group, - "transactions_sum": sum(map(lambda x: x['transactions__price'], group)), - "transactions_units": sum(map(lambda x: x['transactions__amount'], group)) - }}) + inventory_products.update( + { + k: { + "name": k, + "transactions": group, + "transactions_sum": sum(map(lambda x: x["transactions__price"], group)), + "transactions_units": sum(map(lambda x: x["transactions__amount"], group)), + } + } + ) itrans = InventoryTransaction.objects.filter(created__range=[start_time, end_time]) - inv_in = sum(map(lambda x: x[1].get('transactions_sum', 0), inventory_products.items())) + inv_in = sum(map(lambda x: x[1].get("transactions_sum", 0), inventory_products.items())) # out - out_products = Product.objects\ - .filter(orderlines__order__created__range=[start_time, end_time])\ - .values('name', 'orderlines__unit_price', 'orderlines__amount')\ - .order_by('name') + out_products = ( + Product.objects.filter(orderlines__order__created__range=[start_time, end_time]) + .values("name", "orderlines__unit_price", "orderlines__amount") + .order_by("name") + ) # group by name and sum - for k, g in groupby(out_products, lambda x: x['name']): + for k, g in groupby(out_products, lambda x: x["name"]): group = [] for el in g: - el['orderlines__price'] = el['orderlines__amount'] * el['orderlines__unit_price'] + el["orderlines__price"] = el["orderlines__amount"] * el["orderlines__unit_price"] group.append(el) product = { - 'name': k, + "name": k, "orderlines": group, - "orderlines_sum": sum(map(lambda x: x['orderlines__price'], group)), - "orderlines_units": sum(map(lambda x: x['orderlines__amount'], group)) + "orderlines_sum": sum(map(lambda x: x["orderlines__price"], group)), + "orderlines_units": sum(map(lambda x: x["orderlines__amount"], group)), } if k in inventory_products: # add diff @@ -76,42 +84,42 @@ def report(request): inventory_products.update({k: product}) for p in inventory_products.items(): - p[1]['value_diff'] = p[1].get('transactions_sum', 0) - p[1].get('orderlines_sum', 0) - p[1]['units_diff'] = p[1].get('transactions_units', 0) - p[1].get('orderlines_units', 0) + p[1]["value_diff"] = p[1].get("transactions_sum", 0) - p[1].get("orderlines_sum", 0) + p[1]["units_diff"] = p[1].get("transactions_units", 0) - p[1].get("orderlines_units", 0) - inv_out = sum(map(lambda x: x[1].get('orderlines_sum', 0), inventory_products.items())) + inv_out = sum(map(lambda x: x[1].get("orderlines_sum", 0), inventory_products.items())) inv_diff = inv_in - inv_out # Deposits and sales - saldo_profiles = sum(UserProfile.objects.all().values_list('balance', flat=True)) - deposits = sum(Transaction.objects.all().values_list('amount', flat=True)) - purchases = sum(Order.objects.all().values_list('order_sum', flat=True)) + saldo_profiles = sum(UserProfile.objects.all().values_list("balance", flat=True)) + deposits = sum(Transaction.objects.all().values_list("amount", flat=True)) + purchases = sum(Order.objects.all().values_list("order_sum", flat=True)) saldo = deposits - purchases - deposits_start = sum(Transaction.objects.filter(created__lt=start_time).values_list('amount', flat=True)) - purchases_start = sum(Order.objects.filter(created__lt=start_time).values_list('order_sum', flat=True)) + deposits_start = sum(Transaction.objects.filter(created__lt=start_time).values_list("amount", flat=True)) + purchases_start = sum(Order.objects.filter(created__lt=start_time).values_list("order_sum", flat=True)) saldo_start = deposits_start - purchases_start - deposits_end = sum(Transaction.objects.filter(created__lt=end_time).values_list('amount', flat=True)) - purchases_end = sum(Order.objects.filter(created__lt=end_time).values_list('order_sum', flat=True)) + deposits_end = sum(Transaction.objects.filter(created__lt=end_time).values_list("amount", flat=True)) + purchases_end = sum(Order.objects.filter(created__lt=end_time).values_list("order_sum", flat=True)) saldo_end = deposits_end - purchases_end deposits_diff = deposits_end - deposits_start purchases_diff = purchases_end - purchases_start saldo_diff = saldo_end - saldo_start - return render(request, 'report.html', locals()) + return render(request, "report.html", locals()) def inventory(request): - products = Product.objects.all().order_by('-active', 'inventory_amount', 'name') - transactions = InventoryTransaction.objects.all().order_by('-created') + products = Product.objects.all().order_by("-active", "inventory_amount", "name") + transactions = InventoryTransaction.objects.all().order_by("-created") products_active = products.filter(active=True) wholesale_value = sum([p.wholesale_value for p in products_active]) - num_products = sum(products_active.values_list('inventory_amount', flat=True)) + num_products = sum(products_active.values_list("inventory_amount", flat=True)) - return render(request, 'inventory.html', locals()) + return render(request, "inventory.html", locals()) def inventory_add(request): @@ -125,12 +133,12 @@ def inventory_add(request): itrans.product.save() itrans.save() messages.success(request, "Satt inn {0} stk {1}.".format(itrans.amount, itrans.product)) - return redirect('inventory-add') + return redirect("inventory-add") else: - messages.error(request, 'Feil med skjemaet, se under.') + messages.error(request, "Feil med skjemaet, se under.") form = InventoryTransactionForm(data=request.POST) else: form = InventoryTransactionForm() - return render(request, 'inventory_add.html', locals()) + return render(request, "inventory_add.html", locals()) diff --git a/main/views/stats.py b/main/views/stats.py index f39430b..a9aaf0f 100644 --- a/main/views/stats.py +++ b/main/views/stats.py @@ -1,5 +1,3 @@ - - import time from datetime import datetime, timedelta from django.http import JsonResponse @@ -13,13 +11,13 @@ from main.models import Product, OrderLine, Order def stats_list(request): products = Product.objects.filter(active=True) - order_groups = ['hourly', 'daily', 'monthly', 'yearly'] + order_groups = ["hourly", "daily", "monthly", "yearly"] - return render(request, 'stats.html', {'products': products, 'order_groups': order_groups}) + return render(request, "stats.html", {"products": products, "order_groups": order_groups}) class OrdersView(View): - grouping = 'orders' + grouping = "orders" def get_range(self): raise NotImplementedError @@ -42,16 +40,16 @@ class OrdersView(View): orders_grouped = self.group_by(orders) data = { - 'start': str(orders[0].created), - 'orders': [[k, v] for k, v in orders_grouped.items()], - 'total': sum(orders_grouped.values()), - 'grouping': self.grouping + "start": str(orders[0].created), + "orders": [[k, v] for k, v in orders_grouped.items()], + "total": sum(orders_grouped.values()), + "grouping": self.grouping, } return JsonResponse(data) class OrdersHourlyView(OrdersView): - grouping = 'hourly' + grouping = "hourly" def get_range(self): return range(0, 24) @@ -61,7 +59,7 @@ class OrdersHourlyView(OrdersView): class OrdersDailyView(OrdersView): - grouping = 'daily' + grouping = "daily" def get_range(self): return range(0, 7) @@ -71,7 +69,7 @@ class OrdersDailyView(OrdersView): class OrdersMonthlyView(OrdersView): - grouping = 'monthly' + grouping = "monthly" def get_range(self): return range(1, 13) @@ -81,14 +79,14 @@ class OrdersMonthlyView(OrdersView): class OrdersYearlyView(OrdersView): - grouping = 'yearly' + grouping = "yearly" def get_range(self): - first = Order.objects.order_by('created').first() + first = Order.objects.order_by("created").first() last = timezone.now().year first = first.created.year if first else last - return range(first, last+1) + return range(first, last + 1) def get_key(self, order): return order.created.year @@ -112,16 +110,14 @@ def stats_products_realtime(request): for product, value in products.items(): serialized_products[product.name] = value - response = { - 'products': serialized_products - } + response = {"products": serialized_products} return JsonResponse(response) def stats_products_per_user(request, user_id=None): if not user_id: - return JsonResponse({'error': 'Missing param user_id'}) + return JsonResponse({"error": "Missing param user_id"}) order_lines = OrderLine.objects.filter(order__customer__pk=user_id).order_by("order__created") products = {} @@ -135,8 +131,5 @@ def stats_products_per_user(request, user_id=None): serialized_products = [] for product, value in products.items(): - serialized_products.append({ - 'name': product.name, - 'data': value - }) + serialized_products.append({"name": product.name, "data": value}) return JsonResponse(serialized_products, safe=False) diff --git a/main/views/users.py b/main/views/users.py index 57a1fc8..96747e0 100644 --- a/main/views/users.py +++ b/main/views/users.py @@ -13,40 +13,39 @@ def create_user(request): form = SimpleCreateUserForm(data=request.POST) if form.is_valid(): user = form.save() - messages.success(request, "Hurra! {} {} er lagt til".format(user.first_name, user.last_name)) - return redirect('create-user') + messages.success( + request, + "Hurra! {} {} er lagt til".format(user.first_name, user.last_name), + ) + return redirect("create-user") else: - messages.error(request, 'Feil med skjemaet, se under.') + messages.error(request, "Feil med skjemaet, se under.") form = SimpleCreateUserForm(data=request.POST) else: form = SimpleCreateUserForm() - return render(request, 'registration/create_user.html', {'form': form}) + return render(request, "registration/create_user.html", {"form": form}) @login_required def profile(request): transactions = Transaction.objects.filter(user=request.user.pk) - transaction_totals = { - 'sum': sum([t.amount for t in transactions]) - } + transaction_totals = {"sum": sum([t.amount for t in transactions])} orders = Order.objects.filter(customer=request.user.pk) - orders_totals = { - 'sum': sum([o.order_sum for o in orders]) - } + orders_totals = {"sum": sum([o.order_sum for o in orders])} # order_lines = OrderLine.objects.filter(order__customer__pk=1).order_by("order__created") - order_report = _orders_monthly(filter_by={'customer': request.user.pk}) - top_months = order_report.order_by('-sum')[:5] + order_report = _orders_monthly(filter_by={"customer": request.user.pk}) + top_months = order_report.order_by("-sum")[:5] - return render(request, 'profile.html', locals()) + return render(request, "profile.html", locals()) -def _orders_monthly(filter_by={}, order_by='month'): +def _orders_monthly(filter_by={}, order_by="month"): - truncate_date = connection.ops.date_trunc_sql('month', 'created') - qs = Order.objects.filter(**filter_by).extra({'month': truncate_date}) - order_report = qs.values('month').annotate(sum=Sum('order_sum'), num=Count('pk')).order_by(order_by) + truncate_date = connection.ops.date_trunc_sql("month", "created") + qs = Order.objects.filter(**filter_by).extra({"month": truncate_date}) + order_report = qs.values("month").annotate(sum=Sum("order_sum"), num=Count("pk")).order_by(order_by) return order_report diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a05282f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +line-length = 120 +target_version = ['py39']