diff --git a/.gitignore b/.gitignore index d7e59973..3fc5dc76 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ local django-cloudlaunch/cloudlaunch/settings_local.py django-cloudlaunch/db.sqlite3 django-cloudlaunch/cloudlaunch/db.sqlite3 -django-cloudlaunch/baselaunch/migrations .idea diff --git a/django-cloudlaunch/baselaunch/admin.py b/django-cloudlaunch/baselaunch/admin.py index 2f160e05..68093320 100644 --- a/django-cloudlaunch/baselaunch/admin.py +++ b/django-cloudlaunch/baselaunch/admin.py @@ -60,9 +60,14 @@ class OSCredsInline(admin.StackedInline): class UserProfileAdmin(admin.ModelAdmin): inlines = [AWSCredsInline, OSCredsInline] + +class SponsorsAdmin(admin.ModelAdmin): + models = models.Sponsor + admin.site.register(models.Application, AppAdmin) admin.site.register(models.AWS, CloudAdmin) admin.site.register(models.EC2, EC2Admin) admin.site.register(models.S3, S3Admin) admin.site.register(models.OpenStack, CloudAdmin) admin.site.register(models.UserProfile, UserProfileAdmin) +admin.site.register(models.Sponsor, SponsorsAdmin) diff --git a/django-cloudlaunch/baselaunch/migrations/0006_public_services.py b/django-cloudlaunch/baselaunch/migrations/0006_public_services.py new file mode 100644 index 00000000..9b2f26e8 --- /dev/null +++ b/django-cloudlaunch/baselaunch/migrations/0006_public_services.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-06-25 23:49 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django_countries.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('baselaunch', '0005_deployment_task_handling'), + ] + + operations = [ + migrations.CreateModel( + name='PublicService', + fields=[ + ('added', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=60)), + ('slug', models.SlugField(max_length=100, primary_key=True, serialize=False)), + ('links', models.URLField()), + ('purpose', models.TextField(blank=True, null=True)), + ('comments', models.TextField(blank=True, null=True)), + ('email_user_support', models.EmailField(blank=True, max_length=254, null=True)), + ('quotas', models.TextField(blank=True, null=True)), + ('featured', models.BooleanField(default=False)), + ('logo', models.URLField(blank=True, null=True)), + ('location', models.TextField(blank=True, null=True)), + ('country', django_countries.fields.CountryField(max_length=2)), + ('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='baselaunch.Application')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Sponsor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField()), + ('url', models.URLField(null=True)), + ], + ), + migrations.CreateModel( + name='Tag', + fields=[ + ('name', models.TextField(primary_key=True, serialize=False)), + ], + ), + migrations.AlterField( + model_name='credentials', + name='default', + field=models.BooleanField(default=False, help_text='If set, use as default credentials for the selected cloud'), + ), + migrations.AddField( + model_name='publicservice', + name='sponsors', + field=models.ManyToManyField(blank=True, to='baselaunch.Sponsor'), + ), + migrations.AddField( + model_name='publicservice', + name='tags', + field=models.ManyToManyField(blank=True, to='baselaunch.Tag'), + ), + ] diff --git a/django-cloudlaunch/baselaunch/models.py b/django-cloudlaunch/baselaunch/models.py index b80698b5..de3f1025 100644 --- a/django-cloudlaunch/baselaunch/models.py +++ b/django-cloudlaunch/baselaunch/models.py @@ -3,7 +3,11 @@ from django.template.defaultfilters import slugify from fernet_fields import EncryptedCharField from model_utils.managers import InheritanceManager -from smart_selects.db_fields import ChainedForeignKey +from smart_selects.db_fields import ChainedForeignKey + +# For Public Service +from django_countries.fields import CountryField + import json @@ -265,3 +269,56 @@ def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.user.username) super(UserProfile, self).save(*args, **kwargs) + +### PublicServer Models ### +class Tag(models.Model): + """ + Tag referencing a keyword for search features + """ + name = models.TextField(primary_key=True) + + +class Sponsor(models.Model): + """ + A Sponsor is defined by his name and his link url. + Directly inspired by https://wiki.galaxyproject.org/PublicGalaxyServers Sponsor(s) part + """ + name = models.TextField() + url = models.URLField(null=True) + + def __str__(self): + return "{0}".format(self.name) + + +class PublicService(DateNameAwareModel): + """ + Public Service class to display the public services available, + for example, on https://wiki.galaxyproject.org/PublicGalaxyServers + The fields have been inspired by this public galaxy page + """ + slug = models.SlugField(max_length=100, primary_key=True) + links = models.URLField() + purpose = models.TextField(blank=True, null=True) + comments = models.TextField(blank=True, null=True) + email_user_support = models.EmailField(blank=True, null=True) + quotas = models.TextField(blank=True, null=True) + sponsors = models.ManyToManyField(Sponsor, blank=True) + # Featured links means a more important link to show "first" + featured = models.BooleanField(default=False) + # The referenced application, if existing + application = models.ForeignKey(Application, blank=True, null=True) + # The url link to the logo of the Service + logo = models.URLField(blank=True, null=True) + tags = models.ManyToManyField(Tag, blank=True) + location = models.TextField(blank=True, null=True) + # Country => TODO: Add the https://github.com/SmileyChris/django-countries to manage this field + country = CountryField(blank_label='(select country)') + + def __str__(self): + return "{0}".format(self.name) + + def save(self, *args, **kwargs): + if not self.slug: + # Newly created object, so set slug + self.slug = slugify(self.name) + super(PublicService, self).save(*args, **kwargs) diff --git a/django-cloudlaunch/baselaunch/serializers.py b/django-cloudlaunch/baselaunch/serializers.py index 88bb5e34..d64324b8 100644 --- a/django-cloudlaunch/baselaunch/serializers.py +++ b/django-cloudlaunch/baselaunch/serializers.py @@ -17,6 +17,8 @@ from django.contrib.sessions.serializers import JSONSerializer from celery.result import AsyncResult from baselaunch import util +from django_countries.serializer_fields import CountryField + class ZoneSerializer(serializers.Serializer): id = serializers.CharField() @@ -686,3 +688,16 @@ def get_openstack_creds(self, obj): class Meta(UserDetailsSerializer.Meta): fields = UserDetailsSerializer.Meta.fields + \ ('aws_creds', 'openstack_creds', 'credentials') + + +### Public Services Serializers ### +class SponsorSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = models.Sponsor + + +class PublicServiceSerializer(serializers.HyperlinkedModelSerializer): + country = CountryField(country_dict=True) + + class Meta: + model = models.PublicService diff --git a/django-cloudlaunch/baselaunch/urls.py b/django-cloudlaunch/baselaunch/urls.py index be6a8df1..b42f671c 100644 --- a/django-cloudlaunch/baselaunch/urls.py +++ b/django-cloudlaunch/baselaunch/urls.py @@ -32,6 +32,11 @@ router.register(r'deployments', views.DeploymentViewSet) router.register(r'auth', views.AuthView, base_name='auth') +### Public services ### +router.register(r'public_services', views.PublicServiceViewSet) +public_services_router = HybridSimpleRouter() +public_services_router.register(r'sponsors', views.SponsorViewSet) + infra_router = HybridSimpleRouter() infra_router.register(r'clouds', views.CloudViewSet) @@ -100,22 +105,25 @@ profile_router.register(r'credentials/openstack', views.OpenstackCredentialsViewSet) - +infrastructure_regex_pattern = r'api/v1/infrastructure/' +auth_regex_pattern = r'api/v1/auth/' urlpatterns = [ url(r'api/v1/', include(router.urls)), - url(r'api/v1/infrastructure/', include(infra_router.urls)), - url(r'api/v1/infrastructure/', include(cloud_router.urls)), - url(r'api/v1/infrastructure/', include(region_router.urls)), - url(r'api/v1/infrastructure/', include(security_group_router.urls)), - url(r'api/v1/infrastructure/', include(network_router.urls)), - url(r'api/v1/infrastructure/', include(bucket_router.urls)), - url(r'api/v1/auth/', include('rest_auth.urls', namespace='rest_auth')), + url(infrastructure_regex_pattern, include(infra_router.urls)), + url(infrastructure_regex_pattern, include(cloud_router.urls)), + url(infrastructure_regex_pattern, include(region_router.urls)), + url(infrastructure_regex_pattern, include(security_group_router.urls)), + url(infrastructure_regex_pattern, include(network_router.urls)), + url(infrastructure_regex_pattern, include(bucket_router.urls)), + url(auth_regex_pattern, include('rest_auth.urls', namespace='rest_auth')), url(r'api/v1/auth/registration', include('rest_auth.registration.urls', namespace='rest_auth_reg')), - url(r'api/v1/auth/', include('rest_framework.urls', + url(auth_regex_pattern, include('rest_framework.urls', namespace='rest_framework')), url(r'api/v1/auth/user/', include(profile_router.urls)), # The following is required because rest_auth calls allauth internally and # reverse urls need to be resolved. url(r'accounts/', include('allauth.urls')), + # Public services + url(r'api/v1/public_services/', include(public_services_router.urls)), ] diff --git a/django-cloudlaunch/baselaunch/views.py b/django-cloudlaunch/baselaunch/views.py index 4456a6bd..9b69f4e8 100644 --- a/django-cloudlaunch/baselaunch/views.py +++ b/django-cloudlaunch/baselaunch/views.py @@ -455,3 +455,21 @@ class DeploymentViewSet(viewsets.ModelViewSet): queryset = models.ApplicationDeployment.objects.all() permission_classes = (IsAuthenticated,) serializer_class = serializers.DeploymentSerializer + + +### Public Services ### +class SponsorViewSet(viewsets.ModelViewSet): + """ + List sponsors + """ + queryset = models.Sponsor.objects.all() + serializer_class = serializers.SponsorSerializer + + +class PublicServiceViewSet(viewsets.ModelViewSet): + """ + List public services + """ + queryset = models.PublicService.objects.all() + serializer_class = serializers.PublicServiceSerializer + diff --git a/django-cloudlaunch/cloudlaunch/settings.py b/django-cloudlaunch/cloudlaunch/settings.py index b653e1a1..1f8f9612 100644 --- a/django-cloudlaunch/cloudlaunch/settings.py +++ b/django-cloudlaunch/cloudlaunch/settings.py @@ -69,6 +69,7 @@ 'rest_framework', 'kombu.transport.django', # must be last so all celery tasks are discovered 'djcelery', + 'django_countries', ] MIDDLEWARE_CLASSES = [ diff --git a/requirements.txt b/requirements.txt index 00debc73..8020ebb7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,9 +11,10 @@ django-fernet-fields==0.4 # for encryption of user credentials django-cors-headers>=1.1.0 django-nested-admin>=3.0.8 # for nested object editing in django admin django-smart-selects>=1.2.2 # For dependencies between key fields in django admin +django-countries>=3.4.1 jsonmerge>=1.2.0 # For merging userdata/config dictionaries pyyaml>=3.11 kombu==3.0.35 billiard==3.3.0.23 pytz==2016.4 -gunicorn \ No newline at end of file +gunicorn