diff --git a/bbssite/settings.py b/bbssite/settings.py index 72d89cb..829796a 100755 --- a/bbssite/settings.py +++ b/bbssite/settings.py @@ -4,7 +4,6 @@ import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) DEBUG = True -TEMPLATE_DEBUG = DEBUG ADMINS = ( # ('Your Name', 'your_email@example.com'), @@ -14,12 +13,8 @@ MANAGERS = ADMINS DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': 'db.sqlite3', # Or path to database file if using sqlite3. - 'USER': '', # Not used with sqlite3. - 'PASSWORD': '', # Not used with sqlite3. - 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3. + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'db.sqlite3', }, } @@ -90,12 +85,26 @@ STATICFILES_FINDERS = ( # Make this unique, and don't share it with anybody. SECRET_KEY = 'osv$uy-ea$*%lc)+xiwt5@2b7au%u*c!pa6hmh#vl)*+tp%fss' -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -# 'django.template.loaders.eggs.Loader', -) +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.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_CLASSES = ( 'django.middleware.common.CommonMiddleware', @@ -107,13 +116,6 @@ MIDDLEWARE_CLASSES = ( ROOT_URLCONF = 'bbssite.urls' -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. - os.path.join(BASE_DIR, 'templates'), -) - INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', @@ -122,13 +124,15 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', - 'main', 'widget_tweaks', - 'south', 'bootstrapform', 'compressor', 'django_extensions', ) +LOCAL_APPS = ( + 'main', +) +INSTALLED_APPS += LOCAL_APPS # A sample logging configuration. The only tangible logging # performed by this configuration is to send an email to diff --git a/bbssite/urls.py b/bbssite/urls.py index 893bac4..0d82bab 100755 --- a/bbssite/urls.py +++ b/bbssite/urls.py @@ -1,17 +1,25 @@ from django.conf import settings -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.contrib import admin +from django.contrib.staticfiles.urls import staticfiles_urlpatterns + +from main import urls as main_urls + + admin.autodiscover() -urlpatterns = patterns('', - url(r'^$', 'main.views.register', name='home'), - url(r'', include('main.urls')), +urlpatterns = [ + url(r'', include(main_urls)), url(r'^accounts/login/$', 'django.contrib.auth.views.login', name='login'), - url(r'^logout/$', 'main.views.logout', name='logout'), + url(r'^accounts/logout/$', 'main.views.users.logout', name='logout'), url(r'^admin/', include(admin.site.urls)), -) +] if settings.DEBUG: - from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns += staticfiles_urlpatterns() + urlpatterns += [ + url(r'^media/(?P.*)$', 'django.views.static.serve', { + 'document_root': settings.MEDIA_ROOT, + }) + ] diff --git a/main/forms.py b/main/forms.py index a462221..22fcfb4 100644 --- a/main/forms.py +++ b/main/forms.py @@ -1,18 +1,19 @@ # coding: utf-8 +from __future__ import unicode_literals from django import forms +from django.contrib.auth.models import User from django.utils.translation import ugettext as _ -from models import * - -import utils +from main.models import Order, Transaction, InventoryTransaction, Product +from main.utils import format_username 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'] != u"Ekstern": - raise forms.ValidationError(_(u"Du har ikke valgt hvem som skal kjøpe.")) + 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 @@ -20,8 +21,8 @@ class OrderForm(forms.ModelForm): if self.cleaned_data['customer'] is None: return self.cleaned_data['customer'] - if self.cleaned_data['customer'].get_profile().balance <= 0: - raise forms.ValidationError(_(u"Du har negativ saldo og kan ikke kjøpe noe.")) + 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'] @@ -41,6 +42,7 @@ class DepositForm(forms.ModelForm): class Meta: model = Transaction + fields = ['user', 'amount'] class SimpleCreateUserForm(forms.Form): @@ -55,12 +57,12 @@ class SimpleCreateUserForm(forms.Form): return "https://graph.facebook.com/{0}/picture".format(self.cleaned_data['facebook_username']) def save(self, commit=True): - username = utils.format_username(self.cleaned_data['first_name'], self.cleaned_data['last_name']) + 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 = user.get_profile() + profile = user.profile profile.image = self._get_facebook_url() if commit: diff --git a/main/migrations/0001_initial.py b/main/migrations/0001_initial.py index 2e879e5..71bd271 100644 --- a/main/migrations/0001_initial.py +++ b/main/migrations/0001_initial.py @@ -1,172 +1,101 @@ # -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings -class Migration(SchemaMigration): +class Migration(migrations.Migration): - def forwards(self, orm): - # Adding model 'Product' - db.create_table('main_product', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('name', self.gf('django.db.models.fields.CharField')(max_length=64)), - ('sale_price_int', self.gf('django.db.models.fields.FloatField')()), - ('sale_price_ext', self.gf('django.db.models.fields.FloatField')()), - ('description', self.gf('django.db.models.fields.CharField')(max_length=256, blank=True)), - ('volume_liter', self.gf('django.db.models.fields.FloatField')(null=True)), - ('alcohol_percent', self.gf('django.db.models.fields.FloatField')(null=True)), - ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100, blank=True)), - ('customer_support', self.gf('django.db.models.fields.CharField')(max_length=32, blank=True)), - ('active', self.gf('django.db.models.fields.BooleanField')(default=True)), - ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - ('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), - )) - db.send_create_signal('main', ['Product']) + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] - # Adding model 'Order' - db.create_table('main_order', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('customer', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), - ('order_sum', self.gf('django.db.models.fields.FloatField')(db_column='sum')), - ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal('main', ['Order']) - - # Adding model 'OrderLine' - db.create_table('main_orderline', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('order', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Order'])), - ('product', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Product'])), - ('amount', self.gf('django.db.models.fields.IntegerField')()), - ('unit_price', self.gf('django.db.models.fields.FloatField')()), - )) - db.send_create_signal('main', ['OrderLine']) - - # Adding unique constraint on 'OrderLine', fields ['order', 'product'] - db.create_unique('main_orderline', ['order_id', 'product_id']) - - # Adding model 'Transaction' - db.create_table('main_transaction', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), - ('amount', self.gf('django.db.models.fields.FloatField')()), - ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal('main', ['Transaction']) - - # Adding model 'UserProfile' - db.create_table('main_userprofile', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)), - ('balance', self.gf('django.db.models.fields.FloatField')(default=0.0)), - ('image', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True)), - )) - db.send_create_signal('main', ['UserProfile']) - - - def backwards(self, orm): - # Removing unique constraint on 'OrderLine', fields ['order', 'product'] - db.delete_unique('main_orderline', ['order_id', 'product_id']) - - # Deleting model 'Product' - db.delete_table('main_product') - - # Deleting model 'Order' - db.delete_table('main_order') - - # Deleting model 'OrderLine' - db.delete_table('main_orderline') - - # Deleting model 'Transaction' - db.delete_table('main_transaction') - - # Deleting model 'UserProfile' - db.delete_table('main_userprofile') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'main.order': { - 'Meta': {'object_name': 'Order'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'customer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order_sum': ('django.db.models.fields.FloatField', [], {'db_column': "'sum'"}) - }, - 'main.orderline': { - 'Meta': {'unique_together': "(('order', 'product'),)", 'object_name': 'OrderLine'}, - 'amount': ('django.db.models.fields.IntegerField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Order']"}), - 'product': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Product']"}), - 'unit_price': ('django.db.models.fields.FloatField', [], {}) - }, - 'main.product': { - 'Meta': {'object_name': 'Product'}, - 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'alcohol_percent': ('django.db.models.fields.FloatField', [], {'null': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'customer_support': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), - 'description': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), - 'sale_price_ext': ('django.db.models.fields.FloatField', [], {}), - 'sale_price_int': ('django.db.models.fields.FloatField', [], {}), - 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'volume_liter': ('django.db.models.fields.FloatField', [], {'null': 'True'}) - }, - 'main.transaction': { - 'Meta': {'object_name': 'Transaction'}, - 'amount': ('django.db.models.fields.FloatField', [], {}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'main.userprofile': { - 'Meta': {'object_name': 'UserProfile'}, - 'balance': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) - } - } - - complete_apps = ['main'] \ No newline at end of file + operations = [ + migrations.CreateModel( + 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)), + ], + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('order_sum', models.FloatField(db_column=b'sum')), + ('created', models.DateTimeField(auto_now_add=True)), + ('customer', models.ForeignKey(related_name='orders', blank=True, to=settings.AUTH_USER_MODEL, null=True)), + ], + ), + migrations.CreateModel( + 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(related_name='orderlines', to='main.Order')), + ], + ), + migrations.CreateModel( + 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=b'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'], + }, + ), + migrations.CreateModel( + 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(related_name='transactions', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + 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(related_name='profile', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='orderline', + name='product', + field=models.ForeignKey(related_name='orderlines', to='main.Product'), + ), + migrations.AddField( + model_name='inventorytransaction', + name='product', + field=models.ForeignKey(related_name='transactions', verbose_name='Produkt', to='main.Product'), + ), + migrations.AddField( + model_name='inventorytransaction', + name='user', + field=models.ForeignKey(related_name='inventory_transactions', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterUniqueTogether( + name='orderline', + unique_together=set([('order', 'product')]), + ), + ] diff --git a/main/migrations/0002_auto__add_inventorytransaction__add_field_product_inventory_amount.py b/main/migrations/0002_auto__add_inventorytransaction__add_field_product_inventory_amount.py deleted file mode 100644 index 0fde3d8..0000000 --- a/main/migrations/0002_auto__add_inventorytransaction__add_field_product_inventory_amount.py +++ /dev/null @@ -1,131 +0,0 @@ -# -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'InventoryTransaction' - db.create_table(u'main_inventorytransaction', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), - ('product', self.gf('django.db.models.fields.related.ForeignKey')(related_name='transactions', to=orm['main.Product'])), - ('amount', self.gf('django.db.models.fields.FloatField')()), - ('unit_price', self.gf('django.db.models.fields.FloatField')()), - ('comment', self.gf('django.db.models.fields.TextField')(blank=True)), - ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal(u'main', ['InventoryTransaction']) - - # Adding field 'Product.inventory_amount' - db.add_column(u'main_product', 'inventory_amount', - self.gf('django.db.models.fields.FloatField')(default=0), - keep_default=False) - - - def backwards(self, orm): - # Deleting model 'InventoryTransaction' - db.delete_table(u'main_inventorytransaction') - - # Deleting field 'Product.inventory_amount' - db.delete_column(u'main_product', 'inventory_amount') - - - models = { - u'auth.group': { - 'Meta': {'object_name': 'Group'}, - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - u'auth.permission': { - 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - u'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - u'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - u'main.inventorytransaction': { - 'Meta': {'object_name': 'InventoryTransaction'}, - 'amount': ('django.db.models.fields.FloatField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transactions'", 'to': u"orm['main.Product']"}), - 'unit_price': ('django.db.models.fields.FloatField', [], {}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) - }, - u'main.order': { - 'Meta': {'object_name': 'Order'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'customer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order_sum': ('django.db.models.fields.FloatField', [], {'db_column': "'sum'"}) - }, - u'main.orderline': { - 'Meta': {'unique_together': "(('order', 'product'),)", 'object_name': 'OrderLine'}, - 'amount': ('django.db.models.fields.IntegerField', [], {}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Order']"}), - 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'orderlines'", 'to': u"orm['main.Product']"}), - 'unit_price': ('django.db.models.fields.FloatField', [], {}) - }, - u'main.product': { - 'Meta': {'ordering': "['name']", 'object_name': 'Product'}, - 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'alcohol_percent': ('django.db.models.fields.FloatField', [], {'null': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'customer_support': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), - 'description': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}), - 'inventory_amount': ('django.db.models.fields.FloatField', [], {'default': '0'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), - 'sale_price_ext': ('django.db.models.fields.FloatField', [], {}), - 'sale_price_int': ('django.db.models.fields.FloatField', [], {}), - 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'volume_liter': ('django.db.models.fields.FloatField', [], {'null': 'True'}) - }, - u'main.transaction': { - 'Meta': {'object_name': 'Transaction'}, - 'amount': ('django.db.models.fields.FloatField', [], {}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) - }, - u'main.userprofile': { - 'Meta': {'object_name': 'UserProfile'}, - 'balance': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) - } - } - - complete_apps = ['main'] \ No newline at end of file diff --git a/main/migrations/0003_auto__chg_field_order_customer.py b/main/migrations/0003_auto__chg_field_order_customer.py deleted file mode 100644 index 131f35d..0000000 --- a/main/migrations/0003_auto__chg_field_order_customer.py +++ /dev/null @@ -1,118 +0,0 @@ -# -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Changing field 'Order.customer' - db.alter_column(u'main_order', 'customer_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)) - - def backwards(self, orm): - - # User chose to not deal with backwards NULL issues for 'Order.customer' - raise RuntimeError("Cannot reverse this migration. 'Order.customer' and its values cannot be restored.") - - # The following code is provided here to aid in writing a correct migration - # Changing field 'Order.customer' - db.alter_column(u'main_order', 'customer_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])) - - models = { - u'auth.group': { - 'Meta': {'object_name': 'Group'}, - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - u'auth.permission': { - 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - u'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - u'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - u'main.inventorytransaction': { - 'Meta': {'object_name': 'InventoryTransaction'}, - 'amount': ('django.db.models.fields.FloatField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transactions'", 'to': u"orm['main.Product']"}), - 'unit_price': ('django.db.models.fields.FloatField', [], {}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) - }, - u'main.order': { - 'Meta': {'object_name': 'Order'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'customer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order_sum': ('django.db.models.fields.FloatField', [], {'db_column': "'sum'"}) - }, - u'main.orderline': { - 'Meta': {'unique_together': "(('order', 'product'),)", 'object_name': 'OrderLine'}, - 'amount': ('django.db.models.fields.IntegerField', [], {}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Order']"}), - 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'orderlines'", 'to': u"orm['main.Product']"}), - 'unit_price': ('django.db.models.fields.FloatField', [], {}) - }, - u'main.product': { - 'Meta': {'ordering': "['name']", 'object_name': 'Product'}, - 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'alcohol_percent': ('django.db.models.fields.FloatField', [], {'null': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'customer_support': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), - 'description': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}), - 'inventory_amount': ('django.db.models.fields.FloatField', [], {'default': '0'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), - 'sale_price_ext': ('django.db.models.fields.FloatField', [], {}), - 'sale_price_int': ('django.db.models.fields.FloatField', [], {}), - 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'volume_liter': ('django.db.models.fields.FloatField', [], {'null': 'True'}) - }, - u'main.transaction': { - 'Meta': {'object_name': 'Transaction'}, - 'amount': ('django.db.models.fields.FloatField', [], {}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) - }, - u'main.userprofile': { - 'Meta': {'object_name': 'UserProfile'}, - 'balance': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) - } - } - - complete_apps = ['main'] \ No newline at end of file diff --git a/main/models.py b/main/models.py index fece09f..f40d684 100755 --- a/main/models.py +++ b/main/models.py @@ -117,7 +117,7 @@ class InventoryTransaction(models.Model): class UserProfile(models.Model): - user = models.OneToOneField(User) + user = models.OneToOneField(User, related_name='profile') balance = models.FloatField(default=0.0) #image = models.ImageField(upload_to='users', blank=True) image = models.URLField(blank=True) diff --git a/main/urls.py b/main/urls.py index 82ccd9a..0366b69 100644 --- a/main/urls.py +++ b/main/urls.py @@ -1,24 +1,33 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import url +from main.views.stats import stats_list, stats_orders, stats_orders_hourly, stats_products_realtime, stats_products, \ + stats_products_per_user, products_json +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 -urlpatterns = patterns('main.views', - 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'^stats/$', 'stats', name='stats'), - url(r'^stats/orders/$', 'stats_orders', name='stats-orders'), - url(r'^stats/orders/hourly$', 'stats_orders_hourly', name='stats-orders-hourly'), - url(r'^stats/orders/products_realtime$', 'stats_products_realtime', name='stats-products-realtime'), - url(r'^stats/products/$', 'stats_products', name='stats-products'), - url(r'^stats/products/(?P[0-9]+)/$', 'stats_products', name='stats-product'), - url(r'^stats/products/by_user/(?P[0-9]+)/$', 'stats_products_per_user', name='stats-products-per-user'), - url(r'^products/(?P[0-9]+)/$', 'products_json', name='json-product'), - url(r'^products/$', 'products_json', name='json-products'), - url(r'^user/create/$', 'create_user', name='create-user'), - url(r'^profile/$', 'profile', name='profile'), - url(r'^inventory/$', 'inventory', name='inventory'), - url(r'^inventory/add/$', 'inventory_add', name='inventory-add'), - url(r'^report$', 'report', name='report'), - -) +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'), + # Stats + url(r'^stats/$', stats_list, name='stats'), + url(r'^stats/orders/$', stats_orders, name='stats-orders'), + url(r'^stats/orders/hourly$', stats_orders_hourly, name='stats-orders-hourly'), + url(r'^stats/orders/products_realtime$', stats_products_realtime, name='stats-products-realtime'), + url(r'^stats/products/$', stats_products, name='stats-products'), + url(r'^stats/products/(?P[0-9]+)/$', stats_products, name='stats-product'), + url(r'^stats/products/by_user/(?P[0-9]+)/$', stats_products_per_user, name='stats-products-per-user'), + # Products + url(r'^products/(?P[0-9]+)/$', products_json, name='json-product'), + url(r'^products/$', products_json, name='json-products'), + # Users + 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'), +] diff --git a/main/utils.py b/main/utils.py index dfaaffc..825eec2 100644 --- a/main/utils.py +++ b/main/utils.py @@ -1,14 +1,25 @@ +from __future__ import unicode_literals 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') last = lastname.lower().strip().replace(" ", "")[:6].encode('ascii', 'ignore') 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() + + +def users_format_js(users): + users_js = [] + for user in users: + users_js.append('"' + user.first_name + ' ' + user.last_name[:1] + '"') + users_js = "[{0}]".format(u",".join(users_js)) + return users_js diff --git a/main/views.py b/main/views.py deleted file mode 100755 index 6265bc1..0000000 --- a/main/views.py +++ /dev/null @@ -1,542 +0,0 @@ -# coding: utf-8 -from datetime import timedelta -from itertools import groupby -import json -import time - -from django.db import connection -from django.conf import settings -from django.contrib.auth import login, logout as auth_logout -from django.contrib.auth.decorators import login_required -from django.contrib.auth.forms import AuthenticationForm -from django.contrib import messages -from django.core.urlresolvers import reverse -from django.db.models import Count, Sum, Min -from django.forms.formsets import formset_factory -from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render_to_response, render, get_object_or_404, redirect -from django.template import RequestContext -from forms import * -from models import * -import utils - - -def home(request): - if request.method == "POST": - form = AuthenticationForm(data=request.POST) - if form.is_valid(): - # Log the user in. - login(request, form.get_user()) - else: - if request.user.is_authenticated(): - # Redirect to the register - return HttpResponseRedirect(reverse('main.views.register')) - else: - form = AuthenticationForm(request) - return render_to_response('home.html', locals(), context_instance=RequestContext(request)) - - -def register(request): - products = Product.objects.filter(active=True) - cheapest_product_price = products.aggregate(Min('sale_price_int')).values()[0] or 0 - half_a_year_ago = datetime.now() - timedelta(days=180) - users = User.objects.filter(userprofile__balance__gt=cheapest_product_price) - # sort users after user's last purchase time - users = sorted(users, key=lambda u: u.get_profile().last_purchase_date(), reverse=True) - users_js = users_format_js(users) - - # multiple orderlines (one per product) - OrderLineFormSet = formset_factory(OrderLineForm) - if request.method == "POST": - orderform = OrderForm(request.POST) - formset = OrderLineFormSet(request.POST) - - if formset.is_valid() and orderform.is_valid(): - order = orderform.save(commit=False) - - if order.is_external(): - 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]) - - # empty order? - if order_sum == 0: - messages.error(request, u'Du har ikke valgt hva du skal kjøpe...') - return HttpResponseRedirect(reverse('main.views.register')) - - if not order.is_external(): - profile = order.customer.get_profile() - # can she afford it? - if order_sum >= profile.balance: - messages.error(request, u'{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('main.views.register')) - # substract order from balance - profile.balance -= order_sum - profile.save() - - order.order_sum = order_sum - order.save() - - orderlines = [] - for ol in formset.cleaned_data: - if ol['amount'] > 0: - product = Product.objects.get(pk=ol['product']) - - if order.is_external(): - unit_price = ol['unit_price_ext'] - else: - unit_price = ol['unit_price_int'] - - OrderLine.objects.create( - order=order, - product=product, - amount=ol['amount'], - unit_price=unit_price) - - product.inventory_amount -= ol['amount'] - product.save() - - orderlines.append(u"{0} {1}".format(ol['amount'], product)) - - if order.is_external(): - messages.success(request, u'Ekstern kjøpte {0}.'.format(u", ".join(orderlines))) - else: - messages.success(request, u'{0} {1} kjøpte {2}.'.format( - order.customer.first_name, - order.customer.last_name, - u", ".join(orderlines) - )) - - return HttpResponseRedirect(reverse('main.views.register')) - else: - # TODO specify error(s) - messages.error(request, u'Skjemaet er ikke gyldig.') - orderform = OrderForm(request.POST) - formset = OrderLineFormSet(request.POST) - else: - orderform = OrderForm() - formset = OrderLineFormSet() - return render_to_response('register.html', locals(), context_instance=RequestContext(request)) - - -def deposit(request): - users = User.objects.all().order_by('first_name', 'last_name') - users_js = users_format_js(users) - allowed_users = utils.users_with_perm('add_transaction') - error_message_template = u'{0} {1} kan ikke sette inn {2} kr, det overskrider maks saldo ({3} kr) med {4} kr' - - # FIXME: dont need this - try: - limit_deposists = settings.BBS_LIMIT_DEPOSITS - except: - pass - - if request.method == "POST": - form = DepositForm(request.POST) - if form.is_valid(): - amount = form.cleaned_data['amount'] - user = form.cleaned_data['user'] - if amount + user.get_profile().balance > settings.BBS_SALDO_MAX and amount + user.get_profile().balance != 1337: - messages.error(request, error_message_template.format( - user.first_name, - user.last_name, - amount, - settings.BBS_SALDO_MAX, - amount + user.get_profile().balance - settings.BBS_SALDO_MAX - )) - return HttpResponseRedirect(reverse('main.views.deposit')) - - transaction = form.save() - profile = transaction.user.get_profile() - profile.balance += transaction.amount - profile.save() - - messages.success(request, u'{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('main.views.deposit')) - else: - messages.error(request, u'Skjemaet er ikke gyldig.') - form = DepositForm(request.POST) - else: - form = DepositForm() - - return render_to_response('deposit.html', locals(), context_instance=RequestContext(request)) - - -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') - - # 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] - - if len(transactions) == 0: - 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') - - return render_to_response('log.html', locals(), context_instance=RequestContext(request)) - - -def stats(request): - products = Product.objects.filter(active=True) - - return render_to_response('stats.html', locals(), context_instance=RequestContext(request)) - - -def serialize_product(product): - f_product = {} - for attr in Product._meta.get_all_field_names(): - if attr == "transactions" or attr == "orderlines": - continue - - if hasattr(product, attr) and product.__getattribute__(attr): - if type(product.__getattribute__(attr)) is datetime: - f_product[attr] = str(product.__getattribute__(attr)) - else: - f_product[attr] = product.__getattribute__(attr) - return f_product - - -def products_json(request, product_id=None): - if product_id: - #single - products = get_object_or_404(Product, pk=product_id) - else: - #many - products = Product.objects.filter(active=True) - - if product_id: - f_products = serialize_product(products) - else: - f_products = [serialize_product(product) for product in products] - - return HttpResponse(json.dumps(f_products), content_type='application/javascript; charset=utf8') - - -def stats_product(product): - # reverse lookup via order, customer - per_user = OrderLine.objects\ - .filter(product=product)\ - .values('order__customer__first_name', 'order__customer__last_name')\ - .annotate(num=Sum('amount'))\ - .order_by('num')\ - .reverse() - f_per_user = [] - for row in per_user: - if row['order__customer__first_name'] is not None: - who = row['order__customer__first_name'] + " " + row['order__customer__last_name'] - else: - who = u"Ekstern" - num = row['num'] - f_per_user.append([who, num]) - response = { - 'product': serialize_product(Product.objects.get(pk=product)) if type(product) is unicode else serialize_product(product), - 'counts': f_per_user, - 'total_counts': sum([row['num'] for row in per_user]), - } - return response - - -def stats_products(request, product=None): - if product: - #single - f_products = [stats_product(product)] - sales = OrderLine.objects.filter(product=product) - else: - #all - sales = OrderLine.objects.filter(product__active=True) - f_products = [stats_product(p) for p in Product.objects.filter(active=True)] - - totals = float(sum([sale.amount for sale in sales])) - response = { - 'request': 'all' if not product else product, - 'response': { - 'products': f_products, - 'total_count': totals, - } - } - return HttpResponse(json.dumps(response), content_type='application/javascript; charset=utf8') - - -def stats_orders(request): - num_external_orders = Order.objects.filter(customer=None).count() - per_user = Order.objects\ - .values('customer__first_name', 'customer__last_name')\ - .annotate(num=Count('customer'))\ - .order_by('num').reverse() - f_per_user = [] - for row in per_user: - if row['customer__first_name'] is not None: - who = row['customer__first_name'] + " " + row['customer__last_name'] - num = row['num'] - else: - who = u"Ekstern" - num = num_external_orders - f_per_user.append([who, num]) - return HttpResponse(json.dumps(f_per_user), content_type='application/javascript; charset=utf8') - - -def stats_orders_hourly(request): - orders = Order.objects.all() - f_hourly = dict([(key, 0) for key in range(0, 24)]) # init - # group by hour - for key, values in groupby(orders, key=lambda row: row.created.hour): - count = len(list(values)) - f_hourly[key] += count - - if len(orders) == 0: - return HttpResponse(json.dumps({}), content_type='application/javascript; charset=utf8') - response = { - 'start': str(orders[0].created), - 'hourly': [[k, v] for k, v in f_hourly.iteritems()], - 'total': sum(f_hourly.values()), - } - return HttpResponse(json.dumps(response), content_type='application/javascript; charset=utf8') - - -def stats_products_realtime(request): - end_time = datetime.now() - start_time = datetime.now() - timedelta(hours=24) - - order_lines = OrderLine.objects.filter(order__created__range=(start_time, end_time)).order_by("order__created") - products = {} - for order_line in order_lines: - order_time_in_milliseconds = int(time.mktime(order_line.order.created.timetuple()) * 1000) - if order_line.product not in products: - products[order_line.product] = [[order_time_in_milliseconds, order_line.amount]] - else: - prev_entry = products[order_line.product][-1] - products[order_line.product].append([order_time_in_milliseconds, prev_entry[1] + order_line.amount]) - - serialized_products = {} - for product, value in products.iteritems(): - serialized_products[product.name] = value - - response = { - 'products': serialized_products - } - - return HttpResponse(json.dumps(response), content_type='application/javascript; charset=utf8') - - -def logout(request): - auth_logout(request) - return render_to_response('registration/logout.html', locals(), context_instance=RequestContext(request)) - - -def users_format_js(users): - users_js = [] - for user in users: - users_js.append('"' + user.first_name + ' ' + user.last_name[:1] + '"') - users_js = u"[{0}]".format(u",".join(users_js)) - return users_js - - -def create_user(request): - if request.method == "POST": - form = SimpleCreateUserForm(data=request.POST) - if form.is_valid(): - user = form.save() - messages.success(request, u"Hurra! {0} {1} er lagt til".format(user.first_name, user.last_name)) - return redirect('create-user') - - else: - messages.error(request, u'Feil med skjemaet, se under.') - form = SimpleCreateUserForm(data=request.POST) - else: - form = SimpleCreateUserForm() - - return render(request, 'registration/create_user.html', locals()) - - -def inventory(request): - 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)) - - return render(request, 'inventory.html', locals()) - - -def inventory_add(request): - if request.method == "POST": - form = InventoryTransactionForm(data=request.POST) - if form.is_valid(): - itrans = form.save(commit=False) - # Add amount and user, then save - itrans.product.inventory_amount += itrans.amount - itrans.user = request.user - itrans.product.save() - itrans.save() - messages.success(request, u"Satt inn {0} stk {1}.".format(itrans.amount, itrans.product)) - return redirect('inventory-add') - - else: - messages.error(request, u'Feil med skjemaet, se under.') - form = InventoryTransactionForm(data=request.POST) - else: - form = InventoryTransactionForm() - - return render(request, 'inventory_add.html', locals()) - - -def report(request): - # FIXME: split this up in smaller pieces - datetime_now = datetime.now() - start_time = datetime.now() - timedelta(days=30) - # start of day, 1 month - start_time = start_time.replace(hour=0, minute=0, second=0, microsecond=0) - # 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): - form = DateRangeForm(request.GET) - if form.is_valid(): - 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) - - else: - 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') - # group by name and sum - inventory_products = {} - 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'] - 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)) - }}) - - 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())) - - # out - 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']): - group = [] - for el in g: - el['orderlines__price'] = el['orderlines__amount'] * el['orderlines__unit_price'] - group.append(el) - - product = { - 'name': k, - "orderlines": 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 - inventory_products[k].update(product) - else: - 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) - - 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 = 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)) - 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)) - 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()) - - -@login_required -def profile(request): - transactions = Transaction.objects.filter(user=request.user.pk) - 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]) - } - - #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] - return render(request, 'profile.html', locals()) - - -def stats_products_per_user(request, user_id=None): - if not user_id: - return HttpResponse( - json.dumps({'error': 'Missing param user_id'}), - content_type='application/javascript; charset=utf8') - - order_lines = OrderLine.objects.filter(order__customer__pk=user_id).order_by("order__created") - products = {} - for order_line in order_lines: - order_time_in_milliseconds = int(time.mktime(order_line.order.created.timetuple()) * 1000) - if order_line.product not in products: - products[order_line.product] = [[order_time_in_milliseconds, order_line.amount]] - else: - prev_entry = products[order_line.product][-1] - products[order_line.product].append([order_time_in_milliseconds, prev_entry[1] + order_line.amount]) - - serialized_products = [] - for product, value in products.iteritems(): - serialized_products.append({ - 'name': product.name, - 'data': value - }) - return HttpResponse(json.dumps(serialized_products), content_type='application/javascript; charset=utf8') - - -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) - - return order_report diff --git a/main/views/__init__.py b/main/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/views/general.py b/main/views/general.py new file mode 100644 index 0000000..959aed8 --- /dev/null +++ b/main/views/general.py @@ -0,0 +1,171 @@ +# coding: utf-8 +from __future__ import unicode_literals + +from datetime import datetime, timedelta + +import settings +from django.contrib import messages +from django.contrib.auth.models import User +from django.core.urlresolvers import reverse +from django.db.models import Min +from django.forms import formset_factory +from django.http import HttpResponseRedirect +from django.shortcuts import render, redirect +from main.forms import OrderLineForm, OrderForm, DepositForm +from main.models import Product, OrderLine, Order, Transaction +from main.utils import users_format_js, users_with_perm + + +def register(request): + products = Product.objects.filter(active=True) + cheapest_product_price = 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) + # sort users after user's last purchase time + users = sorted(users, key=lambda u: u.profile.last_purchase_date(), reverse=True) + users_js = users_format_js(users) + + # multiple orderlines (one per product) + OrderLineFormSet = formset_factory(OrderLineForm) + if request.method == "POST": + orderform = OrderForm(request.POST) + formset = OrderLineFormSet(request.POST) + + if formset.is_valid() and orderform.is_valid(): + order = orderform.save(commit=False) + + if order.is_external(): + 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]) + + # empty order? + if order_sum == 0: + 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 order_sum >= profile.balance: + 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() + + order.order_sum = order_sum + order.save() + + orderlines = [] + for ol in formset.cleaned_data: + if ol['amount'] > 0: + product = Product.objects.get(pk=ol['product']) + + if order.is_external(): + unit_price = ol['unit_price_ext'] + else: + unit_price = ol['unit_price_int'] + + OrderLine.objects.create( + order=order, + product=product, + amount=ol['amount'], + unit_price=unit_price) + + product.inventory_amount -= ol['amount'] + product.save() + + orderlines.append("{0} {1}".format(ol['amount'], product)) + + if order.is_external(): + 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) + )) + + return redirect('register') + else: + # TODO specify error(s) + 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()) + + +def deposit(request): + 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' + + # FIXME: dont need this + try: + limit_deposists = settings.BBS_LIMIT_DEPOSITS + except: + pass + + if request.method == "POST": + form = DepositForm(request.POST) + if form.is_valid(): + 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')) + + 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')) + else: + messages.error(request, 'Skjemaet er ikke gyldig.') + form = DepositForm(request.POST) + else: + form = DepositForm() + + return render(request, 'deposit.html', locals()) + + +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') + + # 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] + + if len(transactions) == 0: + 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') + + return render(request, 'log.html', locals()) diff --git a/main/views/reports.py b/main/views/reports.py new file mode 100644 index 0000000..50613ad --- /dev/null +++ b/main/views/reports.py @@ -0,0 +1,136 @@ +from datetime import datetime, timedelta +from itertools import groupby + +from django.contrib import messages +from django.shortcuts import render, redirect +from main.forms import DateRangeForm, InventoryTransactionForm +from main.models import Product, InventoryTransaction, UserProfile, Transaction, Order + + +def report(request): + # FIXME: split this up in smaller pieces + datetime_now = datetime.now() + start_time = datetime.now() - timedelta(days=30) + # start of day, 1 month + start_time = start_time.replace(hour=0, minute=0, second=0, microsecond=0) + # 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): + form = DateRangeForm(request.GET) + if form.is_valid(): + 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) + + else: + 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') + # group by name and sum + inventory_products = {} + 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'] + 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)) + }}) + + 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())) + + # out + 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']): + group = [] + for el in g: + el['orderlines__price'] = el['orderlines__amount'] * el['orderlines__unit_price'] + group.append(el) + + product = { + 'name': k, + "orderlines": 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 + inventory_products[k].update(product) + else: + 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) + + 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 = 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)) + 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)) + 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()) + + +def inventory(request): + 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)) + + return render(request, 'inventory.html', locals()) + + +def inventory_add(request): + if request.method == "POST": + form = InventoryTransactionForm(data=request.POST) + if form.is_valid(): + itrans = form.save(commit=False) + # Add amount and user, then save + itrans.product.inventory_amount += itrans.amount + itrans.user = request.user + itrans.product.save() + itrans.save() + messages.success(request, "Satt inn {0} stk {1}.".format(itrans.amount, itrans.product)) + return redirect('inventory-add') + + else: + messages.error(request, 'Feil med skjemaet, se under.') + form = InventoryTransactionForm(data=request.POST) + else: + form = InventoryTransactionForm() + + return render(request, 'inventory_add.html', locals()) diff --git a/main/views/stats.py b/main/views/stats.py new file mode 100644 index 0000000..e138292 --- /dev/null +++ b/main/views/stats.py @@ -0,0 +1,176 @@ +from __future__ import unicode_literals +from datetime import datetime, timedelta, time +from django.db.models import Sum, Count +from django.http import JsonResponse +from django.shortcuts import render, get_object_or_404 +from itertools import groupby + +from main.models import Product, OrderLine, Order + + +def stats_list(request): + products = Product.objects.filter(active=True) + + return render(request, 'stats.html', {'products': products}) + + +def serialize_product(product): + f_product = {} + for attr in Product._meta.get_all_field_names(): + if attr in ["transactions", "orderlines", "image"]: + continue + + if hasattr(product, attr) and product.__getattribute__(attr): + if type(product.__getattribute__(attr)) is datetime: + f_product[attr] = str(product.__getattribute__(attr)) + else: + f_product[attr] = product.__getattribute__(attr) + return f_product + + +def products_json(request, product_id=None): + if product_id: + # single + products = get_object_or_404(Product, pk=product_id) + else: + # many + products = Product.objects.filter(active=True) + + if product_id: + f_products = serialize_product(products) + else: + f_products = [serialize_product(product) for product in products] + + return JsonResponse(f_products) + + +def stats_product(product): + # reverse lookup via order, customer + per_user = OrderLine.objects\ + .filter(product=product)\ + .values('order__customer__first_name', 'order__customer__last_name')\ + .annotate(num=Sum('amount'))\ + .order_by('-num') + f_per_user = [] + for row in per_user: + if row['order__customer__first_name'] is not None: + who = row['order__customer__first_name'] + " " + row['order__customer__last_name'] + else: + who = "Ekstern" + num = row['num'] + f_per_user.append([who, num]) + + response = { + 'product': serialize_product(Product.objects.get(pk=product)) if type(product) is unicode else serialize_product(product), + 'counts': f_per_user, + 'total_counts': sum([row['num'] for row in per_user]), + } + + return response + + +def stats_products(request, product=None): + if product: + # single + f_products = [stats_product(product)] + sales = OrderLine.objects.filter(product=product) + else: + # all + sales = OrderLine.objects.filter(product__active=True) + f_products = [stats_product(p) for p in Product.objects.filter(active=True)] + + totals = float(sum([sale.amount for sale in sales])) + response = { + 'request': 'all' if not product else product, + 'response': { + 'products': f_products, + 'total_count': totals, + } + } + return JsonResponse(response) + + +def stats_orders(request): + num_external_orders = Order.objects.filter(customer=None).count() + per_user = Order.objects\ + .values('customer__first_name', 'customer__last_name')\ + .annotate(num=Count('customer'))\ + .order_by('num').reverse() + f_per_user = [] + for row in per_user: + if row['customer__first_name'] is not None: + who = row['customer__first_name'] + " " + row['customer__last_name'] + num = row['num'] + else: + who = "Ekstern" + num = num_external_orders + f_per_user.append([who, num]) + + return JsonResponse(f_per_user, safe=False) + + +def stats_orders_hourly(request): + orders = Order.objects.all() + f_hourly = dict([(key, 0) for key in range(0, 24)]) # init + # group by hour + for key, values in groupby(orders, key=lambda row: row.created.hour): + count = len(list(values)) + f_hourly[key] += count + + if len(orders) == 0: + return JsonResponse({}) + + response = { + 'start': str(orders[0].created), + 'hourly': [[k, v] for k, v in f_hourly.iteritems()], + 'total': sum(f_hourly.values()), + } + return JsonResponse(response) + + +def stats_products_realtime(request): + end_time = datetime.now() + start_time = datetime.now() - timedelta(hours=24) + + order_lines = OrderLine.objects.filter(order__created__range=(start_time, end_time)).order_by("order__created") + products = {} + for order_line in order_lines: + order_time_in_milliseconds = int(time.mktime(order_line.order.created.timetuple()) * 1000) + if order_line.product not in products: + products[order_line.product] = [[order_time_in_milliseconds, order_line.amount]] + else: + prev_entry = products[order_line.product][-1] + products[order_line.product].append([order_time_in_milliseconds, prev_entry[1] + order_line.amount]) + + serialized_products = {} + for product, value in products.iteritems(): + serialized_products[product.name] = value + + 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'}) + + order_lines = OrderLine.objects.filter(order__customer__pk=user_id).order_by("order__created") + products = {} + for order_line in order_lines: + order_time_in_milliseconds = int(time.mktime(order_line.order.created.timetuple()) * 1000) + if order_line.product not in products: + products[order_line.product] = [[order_time_in_milliseconds, order_line.amount]] + else: + prev_entry = products[order_line.product][-1] + products[order_line.product].append([order_time_in_milliseconds, prev_entry[1] + order_line.amount]) + + serialized_products = [] + for product, value in products.iteritems(): + serialized_products.append({ + 'name': product.name, + 'data': value + }) + return JsonResponse(serialized_products) diff --git a/main/views/users.py b/main/views/users.py new file mode 100644 index 0000000..34d83d2 --- /dev/null +++ b/main/views/users.py @@ -0,0 +1,61 @@ +# coding: utf-8 +from __future__ import unicode_literals + +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.db import connection +from django.db.models import Sum, Count +from django.shortcuts import render, redirect +from django.contrib.auth import logout as auth_logout +from main.forms import SimpleCreateUserForm +from main.models import Order +from main.models import Transaction + + +def logout(request): + auth_logout(request) + return render(request, 'registration/logout.html') + + +def create_user(request): + if request.method == "POST": + form = SimpleCreateUserForm(data=request.POST) + if form.is_valid(): + user = form.save() + messages.success(request, "Hurra! {0} {1} er lagt til".format(user.first_name, user.last_name)) + return redirect('create-user') + + else: + 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}) + + +@login_required +def profile(request): + transactions = Transaction.objects.filter(user=request.user.pk) + 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]) + } + + # 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] + + return render(request, 'profile.html', locals()) + + +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) + + return order_report diff --git a/requirements.txt b/requirements.txt index 274ecc3..7e87a4d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,8 @@ -Django>=1.6,<1.7 +Django>=1.8,<1.9 django-bootstrap-form==3.1 django-compressor==1.4 -django-extensions==1.5.5 +django-extensions==1.5.9 django-widget-tweaks==1.3 facebook-sdk==0.4.0 mysqlclient==1.3.6 -Pillow==2.5.1 -South==1.0 +Pillow==3.0.0 diff --git a/templates/base.html b/templates/base.html index f2915d6..2c5579a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,13 +1,13 @@ -{% load compress %} +{% load compress staticfiles %} {%block title%}BBS{%endblock%} - + {% compress css %} - + {% endcompress %} {%block headinc %}{%endblock%} @@ -38,7 +38,7 @@
  • Logg inn
  • {% else %}