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 PricePerTime(models.Model): timeframe = models.ForeignKey(TimeFrame, on_delete=models.CASCADE) price = models.FloatField() currency = models.ForeignKey(Currency, on_delete=models.CASCADE) def __str__(self): return f"{self.price} {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) # might/must step size price_per_time = models.ManyToManyField(PricePerTime, blank=True) 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 = [] for price in self.price_per_time.all(): pricing.append(f"{price.price}{price.currency.short_name}/{price.timeframe}") pricing = ", ".join(pricing) return f"{self.slug}: {minimum}-{maximum} (+/-){self.step_size} {self.unit} ({pricing})" class ResourceOrder(models.Model): """ Resources that have been ordered """ timeframe = models.ForeignKey(TimeFrame, on_delete=models.CASCADE) value = models.FloatField() resource = models.ForeignKey(Resource, on_delete=models.CASCADE) 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 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 ProductOrder(models.Model): """ Describes a product a user bought """ product = models.ForeignKey(Product, on_delete=models.CASCADE) resources = models.ManyToManyField(ResourceOrder) 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)