Implement a market
This commit is contained in:
@@ -64,6 +64,11 @@ class PlayerMessageEvent(Event):
|
||||
super(PlayerMessageEvent, self).__init__(type='player-message',
|
||||
data={'message': message, 'player': user})
|
||||
|
||||
class MarketOrderEvent(Event):
|
||||
def __init__(self, orderID):
|
||||
super(MarketOrderEvent, self).__init__(type='market-order',
|
||||
data={'orderID': orderID})
|
||||
|
||||
def server_queue(server, users=[]):
|
||||
queueName = 'caminus-broadcast-%s'%server.id
|
||||
queue = beanstalkc.Connection(host=settings.CAMINUS_BEANSTALKD_HOST,
|
||||
@@ -110,6 +115,19 @@ def web_queue(id):
|
||||
queue.watch(queueName)
|
||||
return queue
|
||||
|
||||
def market_queue():
|
||||
queueName = 'caminus-market'
|
||||
queue = beanstalkc.Connection(host=settings.CAMINUS_BEANSTALKD_HOST,
|
||||
port = settings.CAMINUS_BEANSTALKD_PORT)
|
||||
queue.use(queueName)
|
||||
queue.watch(queueName)
|
||||
return queue
|
||||
|
||||
def queue_market_event(event):
|
||||
queue = market_queue()
|
||||
json = dumps({'stamp': time.time(), 'event': event}, cls=EventEncoder)
|
||||
queue.put(json)
|
||||
|
||||
def send_web_event(event):
|
||||
latest = cache.get('minecraft-web-events')
|
||||
if latest is None:
|
||||
|
0
market/__init__.py
Normal file
0
market/__init__.py
Normal file
5
market/admin.py
Normal file
5
market/admin.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import models
|
||||
from django.contrib import admin
|
||||
|
||||
admin.site.register(models.MarketOrder)
|
||||
admin.site.register(models.Transaction)
|
0
market/management/__init__.py
Normal file
0
market/management/__init__.py
Normal file
0
market/management/commands/__init__.py
Normal file
0
market/management/commands/__init__.py
Normal file
37
market/management/commands/market_processor.py
Normal file
37
market/management/commands/market_processor.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from django.core.management.base import NoArgsCommand
|
||||
from market.models import MarketOrder
|
||||
import json
|
||||
from api.events import market_queue
|
||||
from datetime import datetime
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
help = 'Daemon to process market orders'
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
print "Processing currently open orders"
|
||||
for order in MarketOrder.objects.filter(close_stamp__isnull=True):
|
||||
order.process()
|
||||
queue = market_queue()
|
||||
while True:
|
||||
job = queue.reserve()
|
||||
jobInfo = json.loads(job.body)
|
||||
if jobInfo['event']['type'] == "market-order":
|
||||
try:
|
||||
order = MarketOrder.objects.get(id=jobInfo['event']['payload']['orderID'])
|
||||
except MarketOrder.DoesNotExist:
|
||||
# The orders might not be saved yet due to transactions
|
||||
job.release()
|
||||
continue
|
||||
if order.close_stamp:
|
||||
print "Got event for a closed order", order.id
|
||||
job.delete()
|
||||
continue
|
||||
if order.quantity == 0:
|
||||
print "Saving bugged order", order.id
|
||||
order.close_stamp = datetime.now()
|
||||
order.save()
|
||||
job.delete()
|
||||
continue
|
||||
order.process()
|
||||
|
||||
job.delete()
|
88
market/migrations/0001_initial.py
Normal file
88
market/migrations/0001_initial.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# encoding: utf-8
|
||||
import 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 'MarketOrder'
|
||||
db.create_table('market_marketorder', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('item', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['minecraft.Item'])),
|
||||
('creator', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
|
||||
('unit_price', self.gf('django.db.models.fields.FloatField')()),
|
||||
('open_stamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('close_stamp', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
('quantity', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('type', self.gf('django.db.models.fields.IntegerField')(default=-1)),
|
||||
))
|
||||
db.send_create_signal('market', ['MarketOrder'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Deleting model 'MarketOrder'
|
||||
db.delete_table('market_marketorder')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'market.marketorder': {
|
||||
'Meta': {'ordering': "['-open_stamp', 'unit_price']", 'object_name': 'MarketOrder'},
|
||||
'close_stamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['minecraft.Item']"}),
|
||||
'open_stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'quantity': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'type': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
|
||||
'unit_price': ('django.db.models.fields.FloatField', [], {})
|
||||
},
|
||||
'minecraft.item': {
|
||||
'Meta': {'object_name': 'Item'},
|
||||
'damage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'data': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'material': ('django.db.models.fields.IntegerField', [], {'default': '0'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['market']
|
@@ -0,0 +1,79 @@
|
||||
# encoding: utf-8
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
|
||||
# Adding field 'MarketOrder.initial_quantity'
|
||||
db.add_column('market_marketorder', 'initial_quantity', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Deleting field 'MarketOrder.initial_quantity'
|
||||
db.delete_column('market_marketorder', 'initial_quantity')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'market.marketorder': {
|
||||
'Meta': {'ordering': "['-open_stamp', 'unit_price']", 'object_name': 'MarketOrder'},
|
||||
'close_stamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'initial_quantity': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['minecraft.Item']"}),
|
||||
'open_stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'quantity': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'type': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
|
||||
'unit_price': ('django.db.models.fields.FloatField', [], {})
|
||||
},
|
||||
'minecraft.item': {
|
||||
'Meta': {'object_name': 'Item'},
|
||||
'damage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'data': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'material': ('django.db.models.fields.IntegerField', [], {'default': '0'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['market']
|
94
market/migrations/0003_auto__add_transaction.py
Normal file
94
market/migrations/0003_auto__add_transaction.py
Normal file
@@ -0,0 +1,94 @@
|
||||
# encoding: utf-8
|
||||
import 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 'Transaction'
|
||||
db.create_table('market_transaction', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('buy_order', self.gf('django.db.models.fields.related.ForeignKey')(related_name='buy_transactions', to=orm['market.MarketOrder'])),
|
||||
('sell_order', self.gf('django.db.models.fields.related.ForeignKey')(related_name='sell_transactions', to=orm['market.MarketOrder'])),
|
||||
('quantity', self.gf('django.db.models.fields.IntegerField')()),
|
||||
('stamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('market', ['Transaction'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Deleting model 'Transaction'
|
||||
db.delete_table('market_transaction')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'market.marketorder': {
|
||||
'Meta': {'ordering': "['-open_stamp', 'unit_price']", 'object_name': 'MarketOrder'},
|
||||
'close_stamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'initial_quantity': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['minecraft.Item']"}),
|
||||
'open_stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'quantity': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'type': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
|
||||
'unit_price': ('django.db.models.fields.FloatField', [], {})
|
||||
},
|
||||
'market.transaction': {
|
||||
'Meta': {'object_name': 'Transaction'},
|
||||
'buy_order': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buy_transactions'", 'to': "orm['market.MarketOrder']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'quantity': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'sell_order': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sell_transactions'", 'to': "orm['market.MarketOrder']"}),
|
||||
'stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
'minecraft.item': {
|
||||
'Meta': {'object_name': 'Item'},
|
||||
'damage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'data': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'material': ('django.db.models.fields.IntegerField', [], {'default': '0'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['market']
|
0
market/migrations/__init__.py
Normal file
0
market/migrations/__init__.py
Normal file
145
market/models.py
Normal file
145
market/models.py
Normal file
@@ -0,0 +1,145 @@
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from datetime import datetime
|
||||
from django.db.models.signals import post_save
|
||||
from api.events import queue_market_event, MarketOrderEvent
|
||||
from vault.models import VaultSlot
|
||||
from django.contrib.auth.models import User
|
||||
from minecraft.items import ITEMS
|
||||
from minecraft.models import Item
|
||||
import logging
|
||||
|
||||
OFFER_TYPE = (
|
||||
(-1, 'Sell'),
|
||||
(1, 'Buy'),
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class TransactionError(Exception):
|
||||
pass
|
||||
|
||||
class MarketOrder(models.Model):
|
||||
item = models.ForeignKey(Item)
|
||||
creator = models.ForeignKey(User)
|
||||
unit_price = models.FloatField()
|
||||
open_stamp = models.DateTimeField(auto_now_add=True)
|
||||
close_stamp = models.DateTimeField(null=True, blank=True)
|
||||
initial_quantity = models.IntegerField(blank=True)
|
||||
quantity = models.IntegerField()
|
||||
type = models.IntegerField(default=-1, choices = OFFER_TYPE)
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s %s %s %s @%s/ea"%(self.creator, self.get_type_display(),
|
||||
self.quantity, self.item, self.unit_price)
|
||||
|
||||
@staticmethod
|
||||
def lowest_price(item):
|
||||
orders = MarketOrder.objects.filter(item=item, quantity__gt=0)
|
||||
lowestPrice = None
|
||||
for o in orders:
|
||||
if lowestPrice is None or o.unit_price < lowestPrice:
|
||||
lowestPrice = o.unit_price
|
||||
return lowestPrice
|
||||
|
||||
@staticmethod
|
||||
def highest_price(item):
|
||||
orders = MarketOrder.objects.filter(item=item, quantity__gt=0)
|
||||
highestPrice = None
|
||||
for o in orders:
|
||||
if highestPrice is None or o.unit_price > highestPrice:
|
||||
highestPrice = o.unit_price
|
||||
return highestPrice
|
||||
|
||||
def total_price(self):
|
||||
return self.quantity * self.unit_price
|
||||
|
||||
class Meta:
|
||||
ordering = ['-open_stamp', 'unit_price']
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.close_stamp is None and self.quantity == 0:
|
||||
self.close_stamp = datetime.now()
|
||||
if not self.initial_quantity:
|
||||
self.initial_quantity = self.quantity
|
||||
super(MarketOrder, self).save(*args, **kwargs)
|
||||
|
||||
def enqueue_process(self):
|
||||
if settings.CAMINUS_USE_BEANSTALKD:
|
||||
evt = MarketOrderEvent(self.id)
|
||||
queue_market_event(evt)
|
||||
else:
|
||||
self.process()
|
||||
|
||||
@transaction.commit_on_success
|
||||
def process(self):
|
||||
if self.quantity == 0:
|
||||
return
|
||||
log.debug("Processing Order %d:", self.id)
|
||||
log.debug("\tItem: %s", self.item)
|
||||
log.debug("\tQuantity: %d", self.quantity)
|
||||
log.debug("\tUnit price: %d", self.unit_price)
|
||||
log.debug("\tTotal price: %d", self.total_price())
|
||||
matchingOrders = MarketOrder.objects.filter(item=self.item,
|
||||
unit_price=self.unit_price, type=-self.type,
|
||||
quantity__gt=0)
|
||||
for match in matchingOrders:
|
||||
if self.type == 1:
|
||||
buyOrder = MarketOrder.objects.get(pk=self.pk)
|
||||
sellOrder = MarketOrder.objects.get(pk=match.pk)
|
||||
else:
|
||||
buyOrder = MarketOrder.objects.get(pk=match.pk)
|
||||
sellOrder = MarketOrder.objects.get(pk=self.pk)
|
||||
|
||||
try:
|
||||
Transaction.create(buy_order=buyOrder,
|
||||
sell_order=sellOrder)
|
||||
except TransactionError, e:
|
||||
log.exception("Couldn't process transaction", e)
|
||||
|
||||
def process_order(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
instance.enqueue_process()
|
||||
|
||||
post_save.connect(process_order, sender=MarketOrder)
|
||||
|
||||
class Transaction(models.Model):
|
||||
buy_order = models.ForeignKey(MarketOrder, related_name='buy_transactions')
|
||||
sell_order = models.ForeignKey(MarketOrder, related_name='sell_transactions')
|
||||
quantity = models.IntegerField()
|
||||
stamp = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
@staticmethod
|
||||
@transaction.commit_on_success
|
||||
def create(buy_order, sell_order, quantity=None):
|
||||
buyerFunds = buy_order.creator.minecraftprofile.currencyaccount
|
||||
sellerFunds = sell_order.creator.minecraftprofile.currencyaccount
|
||||
|
||||
if quantity is None:
|
||||
quantity = min(float(buyerFunds.balance) / sell_order.unit_price,
|
||||
buy_order.quantity, buy_order.quantity)
|
||||
|
||||
purchaseCost = buy_order.unit_price * quantity
|
||||
|
||||
if purchaseCost > buyerFunds.balance:
|
||||
raise TransactionError, "Insufficient funds."
|
||||
|
||||
buy_order.quantity = F('quantity')-quantity
|
||||
sell_order.quantity = F('quantity')-quantity
|
||||
buyerFunds.balance = F('balance')-purchaseCost
|
||||
sellerFunds.balance = F('balance')+purchaseCost
|
||||
|
||||
log.info("%s buying %d from %s for %d", buy_order.creator,
|
||||
quantity, sell_order.creator, purchaseCost)
|
||||
|
||||
buyerFunds.save()
|
||||
sellerFunds.save()
|
||||
buy_order.save()
|
||||
sell_order.save()
|
||||
VaultSlot.addToInventory(buy_order.creator.minecraftprofile, buy_order.item,
|
||||
quantity)
|
||||
|
||||
return Transaction.objects.create(buy_order=buy_order, sell_order=sell_order,
|
||||
quantity=quantity)
|
16
market/tests.py
Normal file
16
market/tests.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
9
market/urls.py
Normal file
9
market/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.conf.urls.defaults import patterns, include, url
|
||||
|
||||
urlpatterns = patterns('market',
|
||||
url(r'^$', 'views.index'),
|
||||
url(r'^buy$', 'views.buy'),
|
||||
url(r'^buy/(?P<orderID>[0-9]+)$', 'views.buy'),
|
||||
url(r'^sell$', 'views.sell'),
|
||||
url(r'^item/(?P<itemID>[0-9]+)$', 'views.itemStats'),
|
||||
)
|
54
market/views.py
Normal file
54
market/views.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import models
|
||||
from minecraft.models import Item
|
||||
from datetime import datetime
|
||||
from django.contrib import messages
|
||||
from django.shortcuts import render
|
||||
from django.template import RequestContext
|
||||
from vault.models import VaultSlot
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
def index(request):
|
||||
buy_orders = models.MarketOrder.objects.filter(type=1,
|
||||
quantity__gt=0)
|
||||
sell_orders = models.MarketOrder.objects.filter(type=-1,
|
||||
quantity__gt=0)
|
||||
items = Item.objects.all()
|
||||
marketData = []
|
||||
for i in items:
|
||||
data = {}
|
||||
data['item'] = i
|
||||
data['high'] = models.MarketOrder.highest_price(i)
|
||||
data['low'] = models.MarketOrder.lowest_price(i)
|
||||
marketData.append(data)
|
||||
|
||||
return render(request, 'market/index.html', {'buy_orders': buy_orders, 'sell_orders':
|
||||
sell_orders, 'items': marketData})
|
||||
|
||||
def buy(request, orderID=None):
|
||||
if orderID:
|
||||
order = models.MarketOrder.objects.get(id=orderID, type=-1)
|
||||
if order.quantity == 0:
|
||||
messages.info(request, "That order is already closed.")
|
||||
else:
|
||||
buyOrder = models.MarketOrder.objects.create(item=order.item,
|
||||
creator=request.user, unit_price=order.unit_price,
|
||||
quantity=order.quantity, type=1)
|
||||
messages.info(request, "Order queued.")
|
||||
return HttpResponseRedirect(reverse('market.views.itemStats', kwargs={'itemID':
|
||||
order.item.id}))
|
||||
return render(request, 'market/buy.html', {'form': None})
|
||||
|
||||
def sell(request):
|
||||
return render(request, 'market/sell.html', {'form': None})
|
||||
|
||||
def itemStats(request, itemID):
|
||||
item = Item.objects.get(id=itemID)
|
||||
buy_orders = models.MarketOrder.objects.filter(type=1,
|
||||
quantity__gt=0, item=item)
|
||||
sell_orders = models.MarketOrder.objects.filter(type=-1,
|
||||
quantity__gt=0, item=item)
|
||||
return render(request, 'market/item.html', {'item': item, 'high':
|
||||
models.MarketOrder.highest_price(item), 'low':
|
||||
models.MarketOrder.lowest_price(item), 'buy_orders': buy_orders,
|
||||
'sell_orders': sell_orders})
|
@@ -140,6 +140,7 @@ INSTALLED_APPS = (
|
||||
'donate',
|
||||
'badges',
|
||||
'vault',
|
||||
'market',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
)
|
||||
|
@@ -125,6 +125,7 @@ s.parentNode.insertBefore(po, s);
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="{%url 'market.views.index' %}">Market</a></li>
|
||||
<li><a href="{%url 'local.views.list' %}">User List</a></li>
|
||||
<li><a href="{% url 'petition.views.create' %}">Create Petition</a></li>
|
||||
<li><a href="{% url 'donate.views.index' %}">Donate!</a></li>
|
||||
|
63
templates/market/index.html
Normal file
63
templates/market/index.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% extends 'base_simple.html' %}
|
||||
|
||||
{% block title %}The Market{% endblock %}
|
||||
{% block sectiontitle %}The Market{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Market Prices</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>High</th>
|
||||
<th>Low</th>
|
||||
</tr>
|
||||
{% for itemData in items %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url market.views.itemStats itemID=itemData.item.id%}">
|
||||
{% include 'common/item.html' with item=itemData.item %}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{itemData.high}}</td>
|
||||
<td>{{itemDta.low}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<h2>Sell Orders</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Quantity</th>
|
||||
<th>Unit Price</th>
|
||||
<th>Total Price</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
{% for order in sell_orders %}
|
||||
<tr>
|
||||
<td><a href="{% url market.views.itemStats itemID=order.item.id%}">{{order.item}}</a></td>
|
||||
<td>{{order.quantity}}</td>
|
||||
<td>{{order.unit_price}}</td>
|
||||
<td>{{order.total_price}}</td>
|
||||
<td><a href="{% url market.views.buy orderID=order.id %}">Buy Now</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<h2>Buy Orders</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Quantity</th>
|
||||
<th>Unit Price</th>
|
||||
<th>Total Price</th>
|
||||
</tr>
|
||||
{% for order in buy_orders %}
|
||||
<tr>
|
||||
<td>{{order.item}}</td>
|
||||
<td>{{order.quantity}}</td>
|
||||
<td>{{order.unit_price}}</td>
|
||||
<td>{{order.total_price}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
53
templates/market/item.html
Normal file
53
templates/market/item.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{% extends 'base_simple.html' %}
|
||||
{% block title %}Market Details{% endblock %}
|
||||
{% block sectiontitle %}The Market{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
Home
|
||||
> <a href="{% url market.views.index %}">Market</a>
|
||||
> {{item.name}}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'common/item.html' with item=item %}
|
||||
<table>
|
||||
<tr><th>High</th><th>Low</th></tr>
|
||||
<tr><td>{{high}}</td><td>{{low}}</td></tr>
|
||||
</table>
|
||||
<h2>Sell Orders</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Quantity</th>
|
||||
<th>Unit Price</th>
|
||||
<th>Total Price</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
{% for order in sell_orders %}
|
||||
<tr>
|
||||
<td><a href="{% url market.views.itemStats itemID=order.item.id%}">{{order.item}}</a></td>
|
||||
<td>{{order.quantity}}</td>
|
||||
<td>{{order.unit_price}}</td>
|
||||
<td>{{order.total_price}}</td>
|
||||
<td><a href="{% url market.views.buy orderID=order.id %}">Buy Now</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<h2>Buy Orders</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Quantity</th>
|
||||
<th>Unit Price</th>
|
||||
<th>Total Price</th>
|
||||
</tr>
|
||||
{% for order in buy_orders %}
|
||||
<tr>
|
||||
<td>{{order.item}}</td>
|
||||
<td>{{order.quantity}}</td>
|
||||
<td>{{order.unit_price}}</td>
|
||||
<td>{{order.total_price}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user