Source code for simulations.models

# -*- coding: UTF-8 -*-
# models.py
#
# Copyright (C) 2014 HES-SO//HEG Arc
#
# Author(s): Cédric Gaspoz <cedric.gaspoz@he-arc.ch>
#
# This file is part of MarMix.
#
# MarMix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# MarMix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MarMix. If not, see <http://www.gnu.org/licenses/>.

# Stdlib imports
import random
import uuid

# Core Django imports
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache

# Third-party app imports
from django_extensions.db.models import TimeStampedModel
from shorturls import baseconv

# MarMix imports
from customers.models import Customer
from users.models import User


def short_code_encode(sim_id, cust_code):
    encoded_id = baseconv.base32.from_decimal(sim_id)
    return "%s-%s" % (cust_code.upper(), encoded_id)


def generate_uuid(length):
    # TODO: Add a test of unicity in the DB...
    return str(uuid.uuid4().hex.upper()[0:length])


[docs]class Simulation(TimeStampedModel): """ A simulation hold all configuration parameters used to setup and run a simulation. """ INTRO = 'IN' ADVANCED = 'AD' LIVE = 'LI' INDEXED = 'ID' SIMULATION_TYPE_CHOICES = ( (INTRO, _('Introduction')), (ADVANCED, _('Advanced')), (LIVE, _('Live simulation')), (INDEXED, _('Indexed simulation')), ) CONFIGURING = 0 INITIALIZING = 1 READY = 2 RUNNING = 3 PAUSED = 4 FINISHED = 5 ARCHIVED = 9 SIMULATION_STATE_CHOICES = ( (CONFIGURING, _('Configuring')), (INITIALIZING, _('Initializing')), (READY, _('Ready to play')), (RUNNING, _('Running')), (PAUSED, _('Paused')), (FINISHED, _('Finished')), (ARCHIVED, _('Archived (closed)')), ) SIMULATION_STATE_DICT = { 'CONFIGURING': CONFIGURING, 'INITIALIZING': INITIALIZING, 'READY': READY, 'RUNNING': RUNNING, 'PAUSED': PAUSED, 'FINISHED': FINISHED, 'ARCHIVED': ARCHIVED, } code = models.CharField(verbose_name=_("code"), max_length=15, unique=True, blank=True, help_text=_("The code of the simulation (for the user interface)")) customer = models.ForeignKey(Customer, verbose_name=_('customer'), related_name=_('simulations'), help_text=_("The customer account running this simulation")) user = models.ForeignKey(User, verbose_name=_('user'), related_name=_('simulations'), help_text=_("The user account running this simulation")) simulation_type = models.CharField(verbose_name=_("type of simulation"), max_length=2, choices=SIMULATION_TYPE_CHOICES, default=INTRO, help_text=_("The type of this simulation")) capital = models.DecimalField(verbose_name=_("initial capital"), max_digits=14, decimal_places=4, default='0.0000', help_text=_("Initial capital paid to each team")) currency = models.ForeignKey('Currency', verbose_name=_("currency"), help_text=_("The currency symbol displayed in the interface (has no impact on the simulation)")) state = models.IntegerField(verbose_name=_("state of the simulation"), choices=SIMULATION_STATE_CHOICES, default=CONFIGURING, help_text=_("The current state of this simulation")) def save(self, *args, **kwargs): if not self.code: self.code = 'MM-99999999' super(Simulation, self).save(*args, **kwargs) self.code = short_code_encode(self.id, self.customer.short_code) if self.id: cache.delete('simulation-%s' % self.id) super(Simulation, self).save(*args, **kwargs) def _nb_teams(self): teams = self.teams.all().count() return teams nb_teams = property(_nb_teams) class Meta: verbose_name = _("simulation") verbose_name_plural = _("simulations") ordering = ['code'] def __str__(self): return self.code
[docs]class Currency(models.Model): """ A currency, used for display purpose in each views. It has no incidence on the core of the simulation. """ code = models.CharField(verbose_name=_("code of currency"), max_length=3, help_text=_("The ISO code of the currency")) symbol = models.CharField(verbose_name=_("symbol of currency"), max_length=3, blank=True, null=True, help_text=_("The symbol of the currency")) class Meta: verbose_name = _("currency") verbose_name_plural = _("currencies") ordering = ['code'] def __str__(self): if self.symbol: return self.symbol else: return self.code
[docs]class Team(TimeStampedModel): """ A team participating in a simulation. """ PLAYERS = 'P' LIQUIDITY_MANAGER = 'L' TEAM_TYPE_CHOICES = ( (PLAYERS, _('Players')), (LIQUIDITY_MANAGER, _('Liquidity manager')), ) customer = models.ForeignKey(Customer, verbose_name=_('organization'), related_name=_('teams'), help_text=_("The organization the team belongs to")) simulations = models.ManyToManyField('Simulation', verbose_name=_('simulations'), related_name=_('teams'), null=True, blank=True, help_text=_("The simulation(s) the team belongs to")) name = models.CharField(verbose_name=_("name"), max_length=50, help_text=_("A name that can be attributed to the team")) team_type = models.CharField(verbose_name=_("type of team"), max_length=10, choices=TEAM_TYPE_CHOICES, default=PLAYERS, help_text=_("Indicates if it is a team of players or a liquidity manager")) locked = models.BooleanField(verbose_name=_("locked"), default=False, help_text=_("Locked teams can not log in the simulation")) users = models.ManyToManyField(User, verbose_name=_('members'), related_name=_('teams'), null=True, blank=True, help_text=_("The users belonging to the team")) uuid = models.CharField(verbose_name=_("registration key"), max_length=8, blank=True, null=True, unique=True, help_text=_("A unique registration key that is automatically created")) def _get_holdings(self): # TODO: Compute it for real ;-) holdings = random.randint(10000, 99999) return holdings get_holdings = property(_get_holdings) def _get_members(self): return self.users.all().count() get_members = property(_get_members) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.uuid is None: self.uuid = generate_uuid(8) models.Model.save(self, force_insert, force_update, using, update_fields) class Meta: verbose_name = _('team') verbose_name_plural = _('teams') unique_together = ('customer', 'name') ordering = ['customer', 'name'] def __str__(self): return self.name
[docs]def create_liquidity_manager(simulation): """ Create a new team to host the liquidity manager. :param simulation: Simulation object in which to add a new team :return: Team object created """ trader = Team(customer=simulation.customer, name=_("Liquidity trader %s" % simulation.code), team_type=Team.LIQUIDITY_MANAGER) trader.save() trader.simulations.add(simulation) return trader