from django.db import models from django.contrib.auth import get_user_model from django.utils import timezone from django.urls import reverse from django.db.models import Q class Currency(models.Model): slug = models.SlugField(null=True, unique=True) name = models.CharField(max_length=128, unique=True) short_name = models.CharField(max_length=3, unique=True) def __str__(self): return f"{self.name} ({self.short_name})" class TimeFrame(models.Model): slug = models.SlugField(null=True, unique=True) name = models.CharField(max_length=128, unique=True) seconds = models.IntegerField(null=True, blank=True) @staticmethod def secs_to_name(secs): name = "" days = 0 hours = 0 if secs >= 24*3600: days = secs // (24*3600) secs -= (days*24*3600) if secs >= 3600: hours = secs // 3600 secs -= hours*3600 return f"{days} days {hours} hours {secs} seconds" def __str__(self): #return "{} ({})".format(self.name, self.secs_to_name(self.seconds)) return f"{self.name}" class OneTimePrice(models.Model): value = models.FloatField() currency = models.ForeignKey(Currency, on_delete=models.CASCADE) class Meta: ordering = ('value',) def __str__(self): return f"{self.value} {self.currency.short_name}" class PricePerTime(models.Model): timeframe = models.ForeignKey(TimeFrame, on_delete=models.CASCADE) value = models.FloatField() currency = models.ForeignKey(Currency, on_delete=models.CASCADE) def __str__(self): return f"{self.value}{self.currency.short_name}/{self.timeframe}" class Resource(models.Model): slug = models.SlugField(null=True, unique=True) # primary identifier name = models.CharField(max_length=128, unique=False) # CPU, RAM unit = models.CharField(max_length=128) # Count, GB minimum_units = models.FloatField(null=True, blank=True) # might have min maximum_units = models.FloatField(null=True, blank=True) # might have max step_size = models.FloatField(default=1) # step size price_per_time = models.ManyToManyField(PricePerTime, blank=True) onetime_price = models.ForeignKey(OneTimePrice, null=True, blank=True, on_delete=models.CASCADE) def __str__(self): if self.minimum_units: minimum = self.minimum_units else: minimum = "No minimum" if self.maximum_units: maximum = self.maximum_units else: maximum = "No maximum" pricing = ", ".join([str(x) for x in self.price_per_time.all()]) #return f"{self.slug}: {minimum}-{maximum} (+/-){self.step_size} {self.unit} ({pricing})" return f"{self.name} ({self.slug})" class Product(models.Model): """ Describes a product a user can buy """ slug = models.SlugField(null=True, unique=True) name = models.CharField(max_length=128, unique=True) resources = models.ManyToManyField(Resource, blank=True) # List of REQUIRED resources timeframes = models.ManyToManyField(TimeFrame, blank=True) # List of POSSIBLE timeframes def has_one_time_price(self): has_otp = False for res in self.resources.all(): if res.onetime_price: has_otp = True break return has_otp def valid_timeframes(self): """ Return all timeframes that have all resources configured """ valid_tf = [] num_res = self.resources.all().count() for tf in self.timeframes.all(): # Get all distinct source for this timeframe res = self.resources.filter(price_per_time__timeframe=tf).distinct().count() if res == num_res: valid_tf.append(tf) return valid_tf def get_absolute_url(self): return reverse('product-detail', kwargs={'slug' : self.slug}) def __str__(self): return self.name class ResourceOrder(models.Model): """ Resources that have been ordered We need to record the selected value *and* potentially the calculated price """ value = models.FloatField() resource = models.ForeignKey(Resource, on_delete=models.CASCADE) def __str__(self): return f"{self.value} x {self.resource}" class ProductOrder(models.Model): """ Describes a product a user bought """ product = models.ForeignKey(Product, on_delete=models.CASCADE) timeframe = models.ForeignKey(TimeFrame, null=True, blank=True, on_delete=models.CASCADE) resources = models.ManyToManyField(ResourceOrder) def __str__(self): if self.timeframe: txt = f"Order {self.id}: {self.product} for {self.timeframe}" else: txt = f"Order {self.id}: {self.product}" return txt class Order(models.Model): owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, editable=False) creation_date = models.DateTimeField(auto_now_add=True) starting_date = models.DateTimeField(default=timezone.now) ending_date = models.DateTimeField(blank=True, null=True) product = models.ManyToManyField(ProductOrder, blank=True) #textconfigs = models.ManyToManyField(ResourceConfig)