diff --git a/uncloud_v3/README.md b/uncloud_v3/README.md index 70c2bf5..1d53093 100644 --- a/uncloud_v3/README.md +++ b/uncloud_v3/README.md @@ -57,11 +57,8 @@ machine. Use `kubectl get nodes` to verify minikube is up and running. in ResourceOrders1 * Need to pass in the price for the selected timeframe [done] * On submit - * List of - * resources + values - * Product - * Timeframe -* Confirm & create + * Create ProductOrder + * Create ResourceOrder(s) #### 3.0.0 (2022-01-14) diff --git a/uncloud_v3/app/admin.py b/uncloud_v3/app/admin.py index b14e989..27ac5b2 100644 --- a/uncloud_v3/app/admin.py +++ b/uncloud_v3/app/admin.py @@ -6,6 +6,7 @@ from .models import * for m in [ Currency, Order, + OneTimePrice, PricePerTime, Product, ProductOrder, diff --git a/uncloud_v3/app/forms.py b/uncloud_v3/app/forms.py index 218cb8d..0e094f2 100644 --- a/uncloud_v3/app/forms.py +++ b/uncloud_v3/app/forms.py @@ -1,8 +1,8 @@ from django import forms class ProductOrderForm(forms.Form): - product = forms.SlugField(required=True) - timeframe = forms.SlugField(required=True) + product = forms.SlugField(required=True, disabled=True) + timeframe = forms.SlugField(required=True, disabled=True) def __init__(self, resources, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/uncloud_v3/app/management/commands/test-data.py b/uncloud_v3/app/management/commands/test-data.py new file mode 100644 index 0000000..7e01b28 --- /dev/null +++ b/uncloud_v3/app/management/commands/test-data.py @@ -0,0 +1,75 @@ +from django.core.management.base import BaseCommand +from app.models import * + + +class Command(BaseCommand): + help = 'Add test data' + +# def add_arguments(self, parser): + #parser.add_argument('--username', type=str, required=True) + + def handle(self, *args, **options): + currency, created = Currency.objects.get_or_create(defaults= + { + "slug": "CHF", + "name": "Swiss Franc", + "short_name": "CHF" + }) + for timeframe in [ (3600, "1 hour", "1-hour"), + (86400, "1 day", "1-day"), + (7*86400, "7 days", "7-days"), + (30*86400, "30 days", "30-days"), + (365*86400, "365 days", "365 days") ]: + TimeFrame.objects.get_or_create(slug=timeframe[2], + defaults= + { + "name": timeframe[1], + "seconds": timeframe[0] + }) + + for ppt in [ + ("1-day", 1, currency), + ("1-day", 2, currency), + ("30-days", 10, currency), # Nextcloud + ("30-days", 15, currency), # Gitea + ("30-days", 35, currency), # Matrix + ("30-days", 29, currency), + ("30-days", 55, currency) ]: + tf = TimeFrame.objects.get(slug=ppt[0]) + + PricePerTime.objects.get_or_create(timeframe=tf, + value=ppt[1], + defaults= + { + "currency": currency + }) + + for res in [ + ("cpu-1", "CPU", "Core(s)", None, None), + ("cpu-min-max", "CPU", "Core(s)", 1, 20), + ("ram-1", "RAM", "GB", None, None), + ("ram-min-max", "RAM", "GB", 1, 200), + ("matrix-maintenance", "Matrix Maintenance Fee", "", 1, 1), + ("nextcloud-maintenance", "Nextcloud Maintenance Fee", "", 1, 1), + ("gitea-maintenance", "Gitea Maintenance Fee", "", 1, 1), + + ]: + Resource.objects.get_or_create(slug=res[0], + defaults= + { + "name": res[1], + "unit": res[2], + "minimum_units": res[3], + "maximum_units": res[4] + }) + # Link to PPT -- later + # for ppt_res in res[5]: + # ppt = PricePerTime.objects.get( + + + for product in [ + ("matrix", "Matrix"), + ("nextcloud", "Nextcloud"), + ("gitea", "Gitea") ]: + Product.objects.get_or_create(slug=product[0], + defaults = { "name": product[1] }) diff --git a/uncloud_v3/app/migrations/0001_initial.py b/uncloud_v3/app/migrations/0001_initial.py index d13191f..3302d90 100644 --- a/uncloud_v3/app/migrations/0001_initial.py +++ b/uncloud_v3/app/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.0 on 2022-01-02 19:50 +# Generated by Django 4.0 on 2022-01-16 16:44 from django.db import migrations, models import django.db.models.deletion @@ -18,6 +18,7 @@ class Migration(migrations.Migration): name='Currency', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(null=True, unique=True)), ('name', models.CharField(max_length=128, unique=True)), ('short_name', models.CharField(max_length=3, unique=True)), ], @@ -26,7 +27,7 @@ class Migration(migrations.Migration): name='PricePerTime', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('price', models.FloatField()), + ('value', models.FloatField()), ('currency', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.currency')), ], ), @@ -34,6 +35,7 @@ class Migration(migrations.Migration): name='Product', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(null=True, unique=True)), ('name', models.CharField(max_length=128, unique=True)), ], ), @@ -41,8 +43,9 @@ class Migration(migrations.Migration): name='Resource', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=128, unique=True)), - ('unit', models.CharField(max_length=128, unique=True)), + ('slug', models.SlugField(null=True, unique=True)), + ('name', models.CharField(max_length=128)), + ('unit', models.CharField(max_length=128)), ('minimum_units', models.FloatField(blank=True, null=True)), ('maximum_units', models.FloatField(blank=True, null=True)), ('step_size', models.FloatField(default=1)), @@ -53,6 +56,7 @@ class Migration(migrations.Migration): name='TimeFrame', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(null=True, unique=True)), ('name', models.CharField(max_length=128, unique=True)), ('seconds', models.IntegerField(blank=True, null=True)), ], @@ -63,7 +67,6 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('value', models.FloatField()), ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.resource')), - ('timeframe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.timeframe')), ], ), migrations.CreateModel( @@ -72,12 +75,18 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.product')), ('resources', models.ManyToManyField(to='app.ResourceOrder')), + ('timeframe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.timeframe')), ], ), migrations.AddField( model_name='product', name='resources', - field=models.ManyToManyField(to='app.Resource'), + field=models.ManyToManyField(blank=True, to='app.Resource'), + ), + migrations.AddField( + model_name='product', + name='timeframes', + field=models.ManyToManyField(blank=True, to='app.TimeFrame'), ), migrations.AddField( model_name='pricepertime', @@ -95,4 +104,12 @@ class Migration(migrations.Migration): ('product', models.ManyToManyField(blank=True, to='app.ProductOrder')), ], ), + migrations.CreateModel( + name='OneTimePrice', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.FloatField()), + ('currency', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.currency')), + ], + ), ] diff --git a/uncloud_v3/app/migrations/0002_auto_20220102_1953.py b/uncloud_v3/app/migrations/0002_auto_20220102_1953.py deleted file mode 100644 index 4846c25..0000000 --- a/uncloud_v3/app/migrations/0002_auto_20220102_1953.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.0 on 2022-01-02 19:53 - -from django.db import migrations - -def gen_timeframes(apps, schema_editor): - # We can't import the Person model directly as it may be a newer - # version than this migration expects. We use the historical version. - TimeFrame = apps.get_model('app', 'TimeFrame') - - for timeframe in [ (3600, "1 hour"), - (86400, "1 day"), - (7*86400, "7 days"), - (30*86400, "30 days"), - (365*86400, "365 days") ]: - tf = TimeFrame(name=timeframe[1], - seconds=timeframe[0]) - tf.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0001_initial'), - ] - - operations = [ - migrations.RunPython(gen_timeframes), - ] diff --git a/uncloud_v3/app/migrations/0003_alter_product_resources_alter_resource_unit.py b/uncloud_v3/app/migrations/0003_alter_product_resources_alter_resource_unit.py deleted file mode 100644 index c784159..0000000 --- a/uncloud_v3/app/migrations/0003_alter_product_resources_alter_resource_unit.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.0 on 2022-01-02 20:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0002_auto_20220102_1953'), - ] - - operations = [ - migrations.AlterField( - model_name='product', - name='resources', - field=models.ManyToManyField(blank=True, to='app.Resource'), - ), - migrations.AlterField( - model_name='resource', - name='unit', - field=models.CharField(max_length=128), - ), - ] diff --git a/uncloud_v3/app/migrations/0004_auto_20220102_2020.py b/uncloud_v3/app/migrations/0004_auto_20220102_2020.py deleted file mode 100644 index 775ca2d..0000000 --- a/uncloud_v3/app/migrations/0004_auto_20220102_2020.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.0 on 2022-01-02 20:20 - -from django.db import migrations - -def gen_currencies(apps, schema_editor): - Currency = apps.get_model('app', 'Currency') - - Currency.objects.get_or_create(name="Swiss Franc", - short_name="CHF") - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0003_alter_product_resources_alter_resource_unit'), - ] - - operations = [ - migrations.RunPython(gen_currencies), - ] diff --git a/uncloud_v3/app/migrations/0005_product_slug.py b/uncloud_v3/app/migrations/0005_product_slug.py deleted file mode 100644 index 44f56b0..0000000 --- a/uncloud_v3/app/migrations/0005_product_slug.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.0 on 2022-01-14 22:20 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0004_auto_20220102_2020'), - ] - - operations = [ - migrations.AddField( - model_name='product', - name='slug', - field=models.SlugField(null=True), - ), - ] diff --git a/uncloud_v3/app/migrations/0006_resource_slug_alter_product_slug_alter_resource_name.py b/uncloud_v3/app/migrations/0006_resource_slug_alter_product_slug_alter_resource_name.py deleted file mode 100644 index 0473546..0000000 --- a/uncloud_v3/app/migrations/0006_resource_slug_alter_product_slug_alter_resource_name.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.0 on 2022-01-14 23:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0005_product_slug'), - ] - - operations = [ - migrations.AddField( - model_name='resource', - name='slug', - field=models.SlugField(null=True, unique=True), - ), - migrations.AlterField( - model_name='product', - name='slug', - field=models.SlugField(null=True, unique=True), - ), - migrations.AlterField( - model_name='resource', - name='name', - field=models.CharField(max_length=128), - ), - ] diff --git a/uncloud_v3/app/migrations/0007_timeframe_slug.py b/uncloud_v3/app/migrations/0007_timeframe_slug.py deleted file mode 100644 index 4a97046..0000000 --- a/uncloud_v3/app/migrations/0007_timeframe_slug.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.0 on 2022-01-14 23:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0006_resource_slug_alter_product_slug_alter_resource_name'), - ] - - operations = [ - migrations.AddField( - model_name='timeframe', - name='slug', - field=models.SlugField(null=True, unique=True), - ), - ] diff --git a/uncloud_v3/app/migrations/0008_currency_slug.py b/uncloud_v3/app/migrations/0008_currency_slug.py deleted file mode 100644 index 36fa83f..0000000 --- a/uncloud_v3/app/migrations/0008_currency_slug.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.0 on 2022-01-14 23:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0007_timeframe_slug'), - ] - - operations = [ - migrations.AddField( - model_name='currency', - name='slug', - field=models.SlugField(null=True, unique=True), - ), - ] diff --git a/uncloud_v3/app/migrations/0009_product_timeframes.py b/uncloud_v3/app/migrations/0009_product_timeframes.py deleted file mode 100644 index 3272de7..0000000 --- a/uncloud_v3/app/migrations/0009_product_timeframes.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.0 on 2022-01-14 23:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0008_currency_slug'), - ] - - operations = [ - migrations.AddField( - model_name='product', - name='timeframes', - field=models.ManyToManyField(blank=True, to='app.TimeFrame'), - ), - ] diff --git a/uncloud_v3/app/models.py b/uncloud_v3/app/models.py index 6d1f28a..0db9fbd 100644 --- a/uncloud_v3/app/models.py +++ b/uncloud_v3/app/models.py @@ -12,7 +12,6 @@ class Currency(models.Model): 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) @@ -38,13 +37,20 @@ class TimeFrame(models.Model): #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() +class OneTimePrice(models.Model): + value = models.FloatField() currency = models.ForeignKey(Currency, on_delete=models.CASCADE) def __str__(self): - return f"{self.price} {self.currency.short_name}/{self.timeframe}" + 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 @@ -52,7 +58,7 @@ class Resource(models.Model): 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 + step_size = models.FloatField(default=1) # step size price_per_time = models.ManyToManyField(PricePerTime, blank=True) @@ -66,11 +72,7 @@ class Resource(models.Model): 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) + pricing = ", ".join([str(x) for x in self.price_per_time.all()]) return f"{self.slug}: {minimum}-{maximum} (+/-){self.step_size} {self.unit} ({pricing})" @@ -113,14 +115,11 @@ class Product(models.Model): class ResourceOrder(models.Model): """ Resources that have been ordered - The timeframe should be in the ProductOrder, as it needs to be consistent - for all ordered resources We need to record the selected value *and* potentially the calculated price """ - timeframe = models.ForeignKey(TimeFrame, on_delete=models.CASCADE) value = models.FloatField() resource = models.ForeignKey(Resource, on_delete=models.CASCADE) @@ -130,8 +129,13 @@ class ProductOrder(models.Model): Describes a product a user bought """ product = models.ForeignKey(Product, on_delete=models.CASCADE) + timeframe = models.ForeignKey(TimeFrame, on_delete=models.CASCADE) resources = models.ManyToManyField(ResourceOrder) + def __str__(self): + return f"Order of {self.product} / {self.timeframe}" + + class Order(models.Model): owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, editable=False) diff --git a/uncloud_v3/app/views.py b/uncloud_v3/app/views.py index 5faa27f..f137e54 100644 --- a/uncloud_v3/app/views.py +++ b/uncloud_v3/app/views.py @@ -6,68 +6,45 @@ from django.views.generic.base import TemplateView from django.views.generic.list import ListView from django.views.generic.detail import DetailView - -from django.http import HttpResponse - from .models import * from .forms import * -#class ProductOrderView(CreateView): class ProductOrderView(FormView): form_class = ProductOrderForm template_name = 'app/productorder_form.html' - success_url = '/' + success_url = '/order/matrix/30-days/' def get_form_kwargs(self): kwargs = super().get_form_kwargs() -# print(self) -# print(self.request) - print(self.kwargs) - #context = self.get_context_data(**kwargs) - #kwargs['resources'] = self.request['object'].resources.all() + + # Set the product so the form can retrieve the resources product = get_object_or_404(Product, slug=self.kwargs['product']) kwargs['resources'] = product.resources.all() return kwargs + def get_initial(self): + initial = super().get_initial() + + initial['product'] = self.kwargs['product'] + initial['timeframe'] = self.kwargs['timeframe'] + return initial + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['product'] = get_object_or_404(Product, slug=self.kwargs['product']) context['timeframe'] = get_object_or_404(TimeFrame, slug=self.kwargs['timeframe']) - # context['form'] = ProductOrderForm( - # context['product'].resources.all(), - # initial={'product': context['product'].slug, - # 'timeframe': context['timeframe'].slug} - # ) return context - # v2 - # def post(self, request, *args, **kwargs): - # context = self.get_context_data() - # if context["form"].is_valid(): - # print("form good") - # else: - # print("form not good") - # print(context["form"].errors) - # return HttpResponse("All good") - - - # v1 - # def get_context_data(self, **kwargs): - # context = super().get_context_data(**kwargs) - # context['product'] = get_object_or_404(Product, slug=self.kwargs['product']) - # context['timeframe'] = get_object_or_404(TimeFrame, slug=self.kwargs['timeframe']) - - # res_price = [] - # for res in context['product'].resources.all(): - # price = res.price_per_time.filter(timeframe=context['timeframe'])[0].price - # currency = res.price_per_time.filter(timeframe=context['timeframe'])[0].currency - - # res_price.append((res, price, currency)) - # context['res_price'] = res_price - - # print(context) - # print(self.kwargs) - # return context + def form_valid(self, form): + print("We got a valid form, let's create the order") + for f in form.fields: + print(f) + print(form.cleaned_data) + product = get_object_or_404(Product, slug=form.cleaned_data['product']) + tf = get_object_or_404(TimeFrame, slug=form.cleaned_data['timeframe']) + po = ProductOrder(product=product, timeframe=tf) + print(po) + return super().form_valid(form) class ProductDetailView(DetailView):