Files
caminus/market/models.py
2012-11-17 16:09:29 -05:00

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)