From 3099414454eb3ed74fbc52b12155c642025dbf12 Mon Sep 17 00:00:00 2001 From: Nathan Zeppel Date: Fri, 6 Mar 2015 01:32:01 +1030 Subject: [PATCH 1/4] Added wallet journal export --- evething/urls.py | 1 + thing/views/wallet_journal.py | 159 ++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/evething/urls.py b/evething/urls.py index b7dd25fb..130f28a4 100755 --- a/evething/urls.py +++ b/evething/urls.py @@ -76,6 +76,7 @@ (r'^wallet_journal/$', 'wallet_journal'), (r'^wallet_journal/aggregate/$', 'wallet_journal_aggregate'), + (r'^wallet_journal/export/$', 'wallet_journal_export'), (r'^pi/$', 'pi'), ) diff --git a/thing/views/wallet_journal.py b/thing/views/wallet_journal.py index 6a2f155f..45962fdd 100644 --- a/thing/views/wallet_journal.py +++ b/thing/views/wallet_journal.py @@ -24,10 +24,12 @@ # ------------------------------------------------------------------------------ import json +import csv from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.db.models import Q, Count, Sum +from django.http import StreamingHttpResponse from thing.models import * # NOPEP8 from thing.stuff import * # NOPEP8 @@ -241,6 +243,163 @@ def wallet_journal(request): corporation_ids, ) + +class Echo(object): + """An object that implements just the write method of the file-like + interface. + """ + def write(self, value): + """Write the value by returning it, instead of storing in a buffer.""" + return value + +@login_required +def wallet_journal_export(request): + """Wallet journal""" + # Get profile + profile = request.user.profile + + characters = Character.objects.filter( + apikeys__user=request.user, + apikeys__valid=True, + apikeys__key_type__in=[APIKey.ACCOUNT_TYPE, APIKey.CHARACTER_TYPE] + ).distinct() + character_ids = [c.id for c in characters] + + corporation_ids = Corporation.get_ids_with_access(request.user, APIKey.CORP_WALLET_JOURNAL_MASK) + corporations = Corporation.objects.filter(pk__in=corporation_ids) + + # Parse filters and apply magic + filters, journal_ids, days = _journal_queryset(request, character_ids, corporation_ids) + + # Return query set of journal entries with current filter + #entriesx = JournalEntry.objects.filter(pk__in=journal_ids).select_related('character', 'corp_wallet__corporation') + entries = journal_ids.select_related('character', 'corp_wallet__corporation') + + # Do some stuff with entries + item_ids = set() + owner_ids = set() + reftype_ids = set() + station_ids = set() + + for entry in entries: + owner_ids.add(entry.owner1_id) + owner_ids.add(entry.owner2_id) + reftype_ids.add(entry.ref_type_id) + + # Insurance + if entry.ref_type_id == 19: + item_ids.add(int(entry.arg_name)) + # Clone Transfer + elif entry.ref_type_id == 52: + station_ids.add(int(entry.arg_id)) + # Bounty Prizes + elif entry.ref_type_id == 85: + for thing in entry.reason.split(','): + thing = thing.strip() + if ':' in thing: + item_ids.add(int(thing.split(':')[0])) + + char_map = Character.objects.in_bulk(owner_ids) + corp_map = Corporation.objects.in_bulk(owner_ids) + alliance_map = Alliance.objects.in_bulk(owner_ids) + item_map = Item.objects.in_bulk(item_ids) + rt_map = RefType.objects.in_bulk(reftype_ids) + station_map = Station.objects.in_bulk(station_ids) + + pseudo_buffer = Echo() + writer = csv.writer(pseudo_buffer) + + def rowformatter(queryset): + yield writer.writerow([ + 'reference id', + 'source', + 'wallet division', + 'timestamp', + 'reference type', + 'owner 1', + 'owner 2', + 'amount', + 'balance', + 'tax corp', + 'tax amount', + 'description' + ]) + for row in queryset: + owner1 = char_map.get(row.owner1_id) + if owner1 is None: + owner1 = corp_map.get(row.owner1_id) + if owner1 is None: + alliance_map.get(row.owner1_id) + if owner1 is None: + owner1 = row.owner1_id + + owner2 = char_map.get(row.owner2_id) + if owner2 is None: + owner2 = corp_map.get(row.owner2_id) + if owner2 is None: + alliance_map.get(row.owner2_id) + if owner2 is None: + owner2 = row.owner2_id + + description = '' + # Inheritance + if row.ref_type_id == 9: + description = row.reason + # Player Donation/Corporation Account Withdrawal + elif row.ref_type_id in (10, 37) and row.reason != '': + description = '"%s"' % (row.get_unescaped_reason()[5:].strip()) + # Insurance, arg_name is the item_id of the ship that exploded + elif row.ref_type_id == 19: + if row.amount >= 0: + item = item_map.get(int(row.arg_name)) + if item: + description = 'Insurance payment for loss of a %s' % item.name + else: + description = 'Insurance purchased (RefID: %s)' % (row.arg_name[1:]) + # Clone Transfer, arg_name is the name of the station you're going to + elif row.ref_type_id == 52: + station = station_map.get(row.arg_id) + if station: + description = 'Clone transfer to %s' % (station.short_name) + # Bounty Prizes + elif row.ref_type_id == 85: + killed = [] + + for thing in row.reason.split(','): + thing = thing.strip() + if ':' in thing: + item_id, count = thing.split(':') + item = item_map.get(int(item_id)) + if item: + killed.append((item.name, '%sx %s' % (count, item.name))) + elif thing == '...': + killed.append(('ZZZ', '... (list truncated)')) + + # Sort killed + killed = [k[1] for k in sorted(killed)] + + description = 'Bounty prizes for killing pirates in %s' % (row.arg_name.strip()) + + yield writer.writerow([ + row.ref_id, + row.character, + row.corp_wallet, + row.date, + rt_map.get(row.ref_type_id), + owner1, + owner2, + row.amount, + row.balance, + row.tax_corp, + row.tax_amount, + description + ]) + + response = StreamingHttpResponse(rowformatter(entries), content_type="text/csv") + response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' + return response + #for entry in entries: + # print entry.date,entry.balance @login_required def wallet_journal_aggregate(request): From b49e0402079cee79e0c95371904e51cb7dbbe960 Mon Sep 17 00:00:00 2001 From: Nathan Zeppel Date: Fri, 6 Mar 2015 16:20:41 +1030 Subject: [PATCH 2/4] Added filter export button to wallet journal page --- static/js/evething/wallet_journal.js | 11 ++++++++++- templates/thing/wallet_journal.html | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/static/js/evething/wallet_journal.js b/static/js/evething/wallet_journal.js index c74edd9b..6a8aa1ac 100644 --- a/static/js/evething/wallet_journal.js +++ b/static/js/evething/wallet_journal.js @@ -9,7 +9,16 @@ EVEthing.wallet_journal = { $(this).attr('href', '?' + $.param(params)); } }); - + + // Add query string to export link + $('a#export-button').each(function() { + var href = $(this).attr('href').split('?', 1)[0]; + var param = $.param(parseQueryString()) + if (param !== '') { + $(this).attr('href', href + '?' + param); + } + }); + // Bind filter events EVEthing.filters.bind_events(); diff --git a/templates/thing/wallet_journal.html b/templates/thing/wallet_journal.html index 8f79a9a2..26f7b62e 100644 --- a/templates/thing/wallet_journal.html +++ b/templates/thing/wallet_journal.html @@ -23,6 +23,7 @@

Wallet Journal

{% include 'includes/wallet_journal_filters.html' %} + Export filter as CSV Balance for this filter set: {{ total_amount|default('0.00')|commas }} ISK From 698ce890d3288147121f243c090dd00dbf0a773c Mon Sep 17 00:00:00 2001 From: Nathan Zeppel Date: Fri, 6 Mar 2015 19:10:29 +1030 Subject: [PATCH 3/4] pep8 fixes --- thing/views/wallet_journal.py | 76 ++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/thing/views/wallet_journal.py b/thing/views/wallet_journal.py index 45962fdd..e1c07c44 100644 --- a/thing/views/wallet_journal.py +++ b/thing/views/wallet_journal.py @@ -243,15 +243,16 @@ def wallet_journal(request): corporation_ids, ) - + class Echo(object): """An object that implements just the write method of the file-like interface. """ def write(self, value): """Write the value by returning it, instead of storing in a buffer.""" - return value - + return value + + @login_required def wallet_journal_export(request): """Wallet journal""" @@ -265,22 +266,31 @@ def wallet_journal_export(request): ).distinct() character_ids = [c.id for c in characters] - corporation_ids = Corporation.get_ids_with_access(request.user, APIKey.CORP_WALLET_JOURNAL_MASK) + corporation_ids = Corporation.get_ids_with_access( + request.user, + APIKey.CORP_WALLET_JOURNAL_MASK + ) corporations = Corporation.objects.filter(pk__in=corporation_ids) # Parse filters and apply magic - filters, journal_ids, days = _journal_queryset(request, character_ids, corporation_ids) - + filters, journal_ids, days = _journal_queryset( + request, + character_ids, + corporation_ids + ) + # Return query set of journal entries with current filter - #entriesx = JournalEntry.objects.filter(pk__in=journal_ids).select_related('character', 'corp_wallet__corporation') - entries = journal_ids.select_related('character', 'corp_wallet__corporation') - + entries = journal_ids.select_related( + 'character', + 'corp_wallet__corporation' + ) + # Do some stuff with entries item_ids = set() owner_ids = set() reftype_ids = set() station_ids = set() - + for entry in entries: owner_ids.add(entry.owner1_id) owner_ids.add(entry.owner2_id) @@ -298,14 +308,14 @@ def wallet_journal_export(request): thing = thing.strip() if ':' in thing: item_ids.add(int(thing.split(':')[0])) - + char_map = Character.objects.in_bulk(owner_ids) corp_map = Corporation.objects.in_bulk(owner_ids) alliance_map = Alliance.objects.in_bulk(owner_ids) item_map = Item.objects.in_bulk(item_ids) rt_map = RefType.objects.in_bulk(reftype_ids) station_map = Station.objects.in_bulk(station_ids) - + pseudo_buffer = Echo() writer = csv.writer(pseudo_buffer) @@ -332,7 +342,7 @@ def rowformatter(queryset): alliance_map.get(row.owner1_id) if owner1 is None: owner1 = row.owner1_id - + owner2 = char_map.get(row.owner2_id) if owner2 is None: owner2 = corp_map.get(row.owner2_id) @@ -340,8 +350,8 @@ def rowformatter(queryset): alliance_map.get(row.owner2_id) if owner2 is None: owner2 = row.owner2_id - - description = '' + + description = '' # Inheritance if row.ref_type_id == 9: description = row.reason @@ -353,10 +363,13 @@ def rowformatter(queryset): if row.amount >= 0: item = item_map.get(int(row.arg_name)) if item: - description = 'Insurance payment for loss of a %s' % item.name + description = \ + 'Insurance payment for loss of a %s' % item.name else: - description = 'Insurance purchased (RefID: %s)' % (row.arg_name[1:]) - # Clone Transfer, arg_name is the name of the station you're going to + description = \ + 'Insurance purchased (RefID: %s)' % (row.arg_name[1:]) + # Clone Transfer, arg_name is the name of the station + # you're going to elif row.ref_type_id == 52: station = station_map.get(row.arg_id) if station: @@ -371,15 +384,21 @@ def rowformatter(queryset): item_id, count = thing.split(':') item = item_map.get(int(item_id)) if item: - killed.append((item.name, '%sx %s' % (count, item.name))) + killed.append(( + item.name, + '%sx %s' % (count, item.name) + )) elif thing == '...': killed.append(('ZZZ', '... (list truncated)')) # Sort killed killed = [k[1] for k in sorted(killed)] - description = 'Bounty prizes for killing pirates in %s' % (row.arg_name.strip()) - + description = \ + 'Bounty prizes for killing pirates in %s' % ( + row.arg_name.strip() + ) + yield writer.writerow([ row.ref_id, row.character, @@ -394,12 +413,15 @@ def rowformatter(queryset): row.tax_amount, description ]) - - response = StreamingHttpResponse(rowformatter(entries), content_type="text/csv") - response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' - return response - #for entry in entries: - # print entry.date,entry.balance + + response = StreamingHttpResponse( + rowformatter(entries), + content_type="text/csv" + ) + response['Content-Disposition'] = \ + 'attachment; filename="WalletJournal.csv"' + return response + @login_required def wallet_journal_aggregate(request): From 7666409cd2468a7c7794f4f962309465b9f9b087 Mon Sep 17 00:00:00 2001 From: Nathan Zeppel Date: Wed, 11 Mar 2015 21:57:15 +1030 Subject: [PATCH 4/4] Initial Commit of wallet journal graphing - graph page is not linked and only partialy functional --- evething/urls.py | 3 ++ static/js/evething/wallet_graph.js | 45 +++++++++++++++++ templates/base.html | 1 + templates/thing/wallet_journal_graph.html | 34 +++++++++++++ thing/views/wallet_journal.py | 59 ++++++++++++++++++++++- 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 static/js/evething/wallet_graph.js create mode 100644 templates/thing/wallet_journal_graph.html diff --git a/evething/urls.py b/evething/urls.py index 130f28a4..44225335 100755 --- a/evething/urls.py +++ b/evething/urls.py @@ -77,6 +77,9 @@ (r'^wallet_journal/$', 'wallet_journal'), (r'^wallet_journal/aggregate/$', 'wallet_journal_aggregate'), (r'^wallet_journal/export/$', 'wallet_journal_export'), + (r'^wallet_journal/graph/single/data/$', 'wallet_journal_graph_single_character_data'), + (r'^wallet_journal/graph/$', 'wallet_journal_graph_single'), + (r'^pi/$', 'pi'), ) diff --git a/static/js/evething/wallet_graph.js b/static/js/evething/wallet_graph.js new file mode 100644 index 00000000..b9142725 --- /dev/null +++ b/static/js/evething/wallet_graph.js @@ -0,0 +1,45 @@ +function drawChart() { + $.getJSON( "single/data/?" + $.param(parseQueryString()), function( rawData ) { + var i, c, row, charOrder = []; + + var charLastBalance = {}; + for(i=0; i + {% else %} diff --git a/templates/thing/wallet_journal_graph.html b/templates/thing/wallet_journal_graph.html new file mode 100644 index 00000000..10a69741 --- /dev/null +++ b/templates/thing/wallet_journal_graph.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% import 'macros/icons.html' as icons %} + +{% block title %}Wallet Journal{% endblock %} + +{% block extra_js %} + + +{% endblock %} +{% block content %} +
+
+

Wallet Journal

+ + {% include 'includes/wallet_journal_filters.html' %} + + Export filter as CSV + Balance for this filter set: {{ total_amount|default('0.00')|commas }} ISK +
+
+
+{% endblock %} diff --git a/thing/views/wallet_journal.py b/thing/views/wallet_journal.py index e1c07c44..666c345b 100644 --- a/thing/views/wallet_journal.py +++ b/thing/views/wallet_journal.py @@ -29,12 +29,12 @@ from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.db.models import Q, Count, Sum -from django.http import StreamingHttpResponse +from django.http import StreamingHttpResponse, HttpResponse +from django.core.serializers.json import DjangoJSONEncoder from thing.models import * # NOPEP8 from thing.stuff import * # NOPEP8 - JOURNAL_EXPECTED = { 'char': { 'label': 'Character', @@ -423,6 +423,61 @@ def rowformatter(queryset): return response +@login_required +def wallet_journal_graph_single(request): + profile = request.user.profile + + characters = Character.objects.filter( + apikeys__user=request.user, + apikeys__valid=True, + apikeys__key_type__in=[APIKey.ACCOUNT_TYPE, APIKey.CHARACTER_TYPE] + ).distinct() + character_ids = [c.id for c in characters] + + corporation_ids = Corporation.get_ids_with_access(request.user, APIKey.CORP_WALLET_JOURNAL_MASK) + corporations = Corporation.objects.filter(pk__in=corporation_ids) + + # Parse filters and apply magic + filters, journal_ids, days = _journal_queryset(request, character_ids, corporation_ids) + return render_page( + 'thing/wallet_journal_graph.html', + { + "group_by":(), + 'json_data': _json_data(characters, corporations, filters) + }, + request, + character_ids, + corporation_ids + ) + +@login_required +def wallet_journal_graph_single_character_data(request): + # Get profile + profile = request.user.profile + + characters = Character.objects.filter( + apikeys__user=request.user, + apikeys__valid=True, + apikeys__key_type__in=[APIKey.ACCOUNT_TYPE, APIKey.CHARACTER_TYPE] + ).distinct() + character_ids = [c.id for c in characters] + + corporation_ids = Corporation.get_ids_with_access( + request.user, + APIKey.CORP_WALLET_JOURNAL_MASK + ) + corporations = Corporation.objects.filter(pk__in=corporation_ids) + + # Parse filters and apply magic + filters, journal_ids, days = _journal_queryset( + request, + character_ids, + corporation_ids + ) + + response_data = [[entry.date, entry.balance, str(entry.character)] for entry in journal_ids.order_by('date')] + return HttpResponse(json.dumps(response_data, cls=DjangoJSONEncoder), content_type="application/json") + @login_required def wallet_journal_aggregate(request): characters = Character.objects.filter(apikeys__user=request.user.id)