146 lines
4.6 KiB
Python
146 lines
4.6 KiB
Python
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)
|