diff --git a/pyconbalkan/conference/__init__.py b/pyconbalkan/conference/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyconbalkan/conference/admin.py b/pyconbalkan/conference/admin.py new file mode 100644 index 00000000..9cb49893 --- /dev/null +++ b/pyconbalkan/conference/admin.py @@ -0,0 +1,17 @@ +from django.contrib import admin + +from pyconbalkan.conference.models import Conference, CountDown + + +class ConferenceAdmin(admin.ModelAdmin): + class Meta: + model = Conference + + +class CountDownAdmin(admin.ModelAdmin): + class Meta: + model = CountDown + + +admin.site.register(Conference, ConferenceAdmin) +admin.site.register(CountDown, CountDownAdmin) diff --git a/pyconbalkan/conference/api_urls.py b/pyconbalkan/conference/api_urls.py new file mode 100644 index 00000000..8d3e84e8 --- /dev/null +++ b/pyconbalkan/conference/api_urls.py @@ -0,0 +1,6 @@ +from rest_framework import routers + +from pyconbalkan.conference.views import ConferenceViewSet + +router = routers.DefaultRouter() +router.register(r'conference', ConferenceViewSet) \ No newline at end of file diff --git a/pyconbalkan/conference/apps.py b/pyconbalkan/conference/apps.py new file mode 100644 index 00000000..cd92fc5c --- /dev/null +++ b/pyconbalkan/conference/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ConferenceConfig(AppConfig): + name = 'conference' diff --git a/pyconbalkan/conference/migrations/0001_initial.py b/pyconbalkan/conference/migrations/0001_initial.py new file mode 100644 index 00000000..bf02991d --- /dev/null +++ b/pyconbalkan/conference/migrations/0001_initial.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.11 on 2018-04-26 21:51 +from __future__ import unicode_literals + +from django.db import migrations, models +import django_countries.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Conference', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('active', models.BooleanField(default=False)), + ('event', models.CharField(blank=True, max_length=100, null=True)), + ('name', models.CharField(blank=True, max_length=100, null=True)), + ('year', models.PositiveIntegerField()), + ('number', models.PositiveIntegerField()), + ('city', models.CharField(blank=True, max_length=200, null=True)), + ('country', django_countries.fields.CountryField(blank=True, max_length=2, null=True)), + ('address', models.TextField(blank=True, null=True)), + ('from_date', models.DateField(blank=True, null=True)), + ('to_date', models.DateField(blank=True, null=True)), + ('max_attendees', models.PositiveIntegerField(blank=True, null=True)), + ('type', models.IntegerField(choices=[(0, 'International'), (1, 'National')])), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='CountDown', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('active', models.BooleanField(default=False)), + ('title', models.CharField(blank=True, max_length=100, null=True)), + ('count_down', models.DateTimeField(blank=True, null=True)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/pyconbalkan/conference/migrations/__init__.py b/pyconbalkan/conference/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyconbalkan/conference/models.py b/pyconbalkan/conference/models.py new file mode 100644 index 00000000..fb3e69cc --- /dev/null +++ b/pyconbalkan/conference/models.py @@ -0,0 +1,37 @@ +from django.db import models +from django_countries.fields import CountryField + +from pyconbalkan.core.models import SingleActiveModel + + +class Conference(SingleActiveModel): + INTERNATIONAL = 0 + NATIONAL = 1 + CONF_TYPE = ( + (INTERNATIONAL, 'International'), + (NATIONAL, 'National'), + ) + + event = models.CharField(null=True, blank=True, max_length=100) + name = models.CharField(null=True, blank=True, max_length=100) + year = models.PositiveIntegerField() + number = models.PositiveIntegerField() + city = models.CharField(null=True, blank=True, max_length=200) + country = CountryField(null=True, blank=True) + address = models.TextField(null=True, blank=True) + from_date = models.DateField(null=True, blank=True) + to_date = models.DateField(null=True, blank=True) + max_attendees = models.PositiveIntegerField(null=True, blank=True) + type = models.IntegerField(choices=CONF_TYPE) + + + def __str__(self): + return '{} {} {}'.format(self.event, self.name, self.year) + + +class CountDown(SingleActiveModel): + title = models.CharField(null=True, blank=True, max_length=100) + count_down = models.DateTimeField(null=True, blank=True) + + def __str__(self): + return self.title \ No newline at end of file diff --git a/pyconbalkan/conference/serializers.py b/pyconbalkan/conference/serializers.py new file mode 100644 index 00000000..5e8a3ed8 --- /dev/null +++ b/pyconbalkan/conference/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from pyconbalkan.conference.models import Conference + + +class ConferenceSerializer(serializers.ModelSerializer): + class Meta: + model = Conference + fields = '__all__' diff --git a/pyconbalkan/conference/templates/index.html b/pyconbalkan/conference/templates/index.html new file mode 100644 index 00000000..e69de29b diff --git a/pyconbalkan/conference/tests.py b/pyconbalkan/conference/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/pyconbalkan/conference/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pyconbalkan/conference/views.py b/pyconbalkan/conference/views.py new file mode 100644 index 00000000..fbfda053 --- /dev/null +++ b/pyconbalkan/conference/views.py @@ -0,0 +1,9 @@ +from rest_framework import viewsets + +from pyconbalkan.conference.models import Conference +from pyconbalkan.conference.serializers import ConferenceSerializer + + +class ConferenceViewSet(viewsets.ModelViewSet): + queryset = Conference.objects.all() + serializer_class = ConferenceSerializer diff --git a/pyconbalkan/core/admin.py b/pyconbalkan/core/admin.py index e1f78093..c42474ae 100644 --- a/pyconbalkan/core/admin.py +++ b/pyconbalkan/core/admin.py @@ -1,19 +1,6 @@ from django.apps import AppConfig -from django.contrib import admin - -from pyconbalkan.core.models import Speaker, SpeakerPhoto class CoreConfig(AppConfig): name = 'core' - -class SpeakerImageInline(admin.TabularInline): - model = SpeakerPhoto - - -class SpeakerAdmin(admin.ModelAdmin): - inlines = [SpeakerImageInline] - - -admin.site.register(Speaker, SpeakerAdmin) diff --git a/pyconbalkan/core/api_urls.py b/pyconbalkan/core/api_urls.py new file mode 100644 index 00000000..e69de29b diff --git a/pyconbalkan/core/models.py b/pyconbalkan/core/models.py index 8f3083f0..98103407 100644 --- a/pyconbalkan/core/models.py +++ b/pyconbalkan/core/models.py @@ -1,16 +1,21 @@ from django.db import models -# Create your models here. +class SingleActiveModel(models.Model): + active = models.BooleanField(default=False) -class Speaker(models.Model): - name = models.CharField(max_length=50) - job = models.CharField(max_length=100) + class Meta: + abstract = True - def __str__(self): - return self.name + def save(self, *args, **kwargs): + if self.active: + # select all other active items + qs = type(self).objects.filter(active=True) + # except self (if self already exists) + if self.pk: + qs = qs.exclude(pk=self.pk) + # and deactive them + qs.update(active=False) + super(SingleActiveModel, self).save(*args, **kwargs) -class SpeakerPhoto(models.Model): - speaker = models.ForeignKey('Speaker', related_name='images') - profile_picture = models.ImageField(upload_to="static/img") diff --git a/pyconbalkan/core/routers.py b/pyconbalkan/core/routers.py new file mode 100644 index 00000000..a0924cdc --- /dev/null +++ b/pyconbalkan/core/routers.py @@ -0,0 +1,15 @@ +from rest_framework import routers + + +class DefaultRouter(routers.DefaultRouter): + """ + Extends `DefaultRouter` class to add a method for extending url routes from another router. + """ + def extend(self, router): + """ + Extend the routes with url routes of the passed in router. + + Args: + router: SimpleRouter instance containing route definitions. + """ + self.registry.extend(router.registry) \ No newline at end of file diff --git a/pyconbalkan/core/static/css/style.css b/pyconbalkan/core/static/css/style.css index 8c066fa1..d9bf9cd2 100644 --- a/pyconbalkan/core/static/css/style.css +++ b/pyconbalkan/core/static/css/style.css @@ -185,7 +185,7 @@ ul.countdown li div { } .moveDown { - margin-top: 30px; + margin-top: 50px; } #btnBuyNow { diff --git a/pyconbalkan/core/static/img/userphoto.png b/pyconbalkan/core/static/img/userphoto.png deleted file mode 100644 index 0d0fd634..00000000 Binary files a/pyconbalkan/core/static/img/userphoto.png and /dev/null differ diff --git a/pyconbalkan/core/static/js/timer.js b/pyconbalkan/core/static/js/timer.js index 0a99400b..8f0f69e7 100644 --- a/pyconbalkan/core/static/js/timer.js +++ b/pyconbalkan/core/static/js/timer.js @@ -1,5 +1,5 @@ // Set the date we're counting down to -var countDownDate = new Date("Sep 8, 2018 10:00:00").getTime(); +var countDownDate = new Date("May 15, 2018 00:00:01").getTime(); // Update the count down every 1 second var x = setInterval(function() { diff --git a/pyconbalkan/core/templates/index.html b/pyconbalkan/core/templates/home.html similarity index 56% rename from pyconbalkan/core/templates/index.html rename to pyconbalkan/core/templates/home.html index f086b189..a700f6c6 100644 --- a/pyconbalkan/core/templates/index.html +++ b/pyconbalkan/core/templates/home.html @@ -21,12 +21,37 @@ + + + @@ -65,28 +90,32 @@
-

PyCon

-

Balkan

-

Belgrade 2018

+ {% if conference %} +

{{ conference.event }}

+

{{ conference.name }}

+

{{ conference.city }} {{ conference.year }}

+ {% endif %}

-
-

#1

-

Hilton, Kralja Milana 35, 11000 Belgrade Serbia

-
-

8-12 September

-

Over 400 Attendees

-

International Event

-
-
+ {% if conference %} +
+

#{{ conference.number }}

+

{{ conference.address }}

+
+

{{ conference.from_date }} - {{ conference.to_date }}

+

{{ conference.max_attendees }} attendees

+

{{ conference.get_type_display }}

+
+{#
#} {# JOIN US#} -
-
+{#
#} +{#
#} {# #} {# #} {# #} +{#
#}
-
+ {% endif %}
@@ -95,54 +124,64 @@

#1

-

Early bird tickets

-
- - Days -
    -
  • - -
    hours
    -
  • -
  • - -
    minutes
    -
  • -
  • - -
    sec
    -
  • -
-
-
-{# Buy Now#} -
+ {% if count_down %} +

{{ count_down.title }}

+
+ + Days +
    +
  • + +
    hours
    +
  • +
  • + +
    minutes
    +
  • +
  • + +
    sec
    +
  • +
+
-
-{#

Speakers

#} -
+{#
#} +{# Buy Now#} +{#
#} + {% endif %} + +
+

Speakers

+
-
-
-
- {% for speaker in speakerPhoto %} -
- -
    -
  • {{ speaker.speaker.name }}
  • -
    -
  • {{ speaker.speaker.job }}
  • -
+ {% if speakers %} +
+
+
+ {% for speaker in speakers %} +
+ +
    +
  • {{ speaker.name }}
  • +
    +
  • {{ speaker.job }}
  • +
+
+ {% endfor %}
- {% endfor %}
-
+ {% else %} +
+ COMING SOON +
+ {% endif %} + -
+{#
#} {# See More#} {# Attend#} -
+{#
#}
diff --git a/pyconbalkan/core/views.py b/pyconbalkan/core/views.py index ed6c6384..a42c158a 100644 --- a/pyconbalkan/core/views.py +++ b/pyconbalkan/core/views.py @@ -1,11 +1,18 @@ from django.shortcuts import render -from .models import Speaker, SpeakerPhoto + +from pyconbalkan.conference.models import Conference, CountDown +from pyconbalkan.speaker.models import Speaker, SpeakerPhoto def home(request): - speaker = Speaker.objects.all() - speakerPh = SpeakerPhoto.objects.all() - context = {'speakers': speaker, 'speakerPhoto': speakerPh} - return render(request, 'index.html', context) + conference = Conference.objects.filter(active=True) + count_down = CountDown.objects.filter(active=True) + speakers = Speaker.objects.filter(active=True) + context = { + 'speakers': speakers, + 'conference': conference.first() if conference else None, + 'count_down': count_down.first() if count_down else None, + } + return render(request, 'home.html', context) diff --git a/pyconbalkan/settings.py b/pyconbalkan/settings.py index 89e26565..e2d93d08 100644 --- a/pyconbalkan/settings.py +++ b/pyconbalkan/settings.py @@ -39,7 +39,13 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + # apps 'pyconbalkan.core', + 'pyconbalkan.conference', + 'pyconbalkan.speaker', + # others + 'rest_framework', + 'django_countries', ] MIDDLEWARE = [ @@ -124,3 +130,14 @@ PDF_ROOT = os.path.join(BASE_DIR, 'pyconbalkan/core/static/pdf/') MEDIA_ROOT = os.path.join(BASE_DIR, 'pyconbalkan/core/') MEDIA_URL = '/img/' + + +REST_FRAMEWORK = { + # Use Django's standard `django.contrib.auth` permissions, + # or allow read-only access for unauthenticated users. + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' + ], + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 10 +} diff --git a/pyconbalkan/speaker/__init__.py b/pyconbalkan/speaker/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyconbalkan/speaker/admin.py b/pyconbalkan/speaker/admin.py new file mode 100644 index 00000000..1141726b --- /dev/null +++ b/pyconbalkan/speaker/admin.py @@ -0,0 +1,14 @@ +from django.contrib import admin + +from pyconbalkan.speaker.models import SpeakerPhoto, Speaker + + +class SpeakerImageInline(admin.TabularInline): + model = SpeakerPhoto + + +class SpeakerAdmin(admin.ModelAdmin): + inlines = [SpeakerImageInline] + + +admin.site.register(Speaker, SpeakerAdmin) diff --git a/pyconbalkan/speaker/api_urls.py b/pyconbalkan/speaker/api_urls.py new file mode 100644 index 00000000..f98ae6c2 --- /dev/null +++ b/pyconbalkan/speaker/api_urls.py @@ -0,0 +1,6 @@ +from rest_framework import routers + +from pyconbalkan.speaker.views import SpeakerViewSet + +router = routers.DefaultRouter() +router.register(r'speaker', SpeakerViewSet) \ No newline at end of file diff --git a/pyconbalkan/speaker/apps.py b/pyconbalkan/speaker/apps.py new file mode 100644 index 00000000..4e6e611a --- /dev/null +++ b/pyconbalkan/speaker/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SpeakerConfig(AppConfig): + name = 'speaker' diff --git a/pyconbalkan/core/migrations/0001_initial.py b/pyconbalkan/speaker/migrations/0001_initial.py similarity index 78% rename from pyconbalkan/core/migrations/0001_initial.py rename to pyconbalkan/speaker/migrations/0001_initial.py index f8130f4f..1b49b6a1 100644 --- a/pyconbalkan/core/migrations/0001_initial.py +++ b/pyconbalkan/speaker/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.11 on 2018-04-23 11:11 +# Generated by Django 1.11.11 on 2018-04-26 21:50 from __future__ import unicode_literals from django.db import migrations, models @@ -18,16 +18,20 @@ class Migration(migrations.Migration): name='Speaker', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('active', models.BooleanField(default=False)), ('name', models.CharField(max_length=50)), ('job', models.CharField(max_length=100)), ], + options={ + 'abstract': False, + }, ), migrations.CreateModel( name='SpeakerPhoto', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('profile_picture', models.ImageField(upload_to='static/img')), - ('speaker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='core.Speaker')), + ('speaker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='speaker.Speaker')), ], ), ] diff --git a/pyconbalkan/speaker/migrations/__init__.py b/pyconbalkan/speaker/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyconbalkan/speaker/models.py b/pyconbalkan/speaker/models.py new file mode 100644 index 00000000..2dd71bc5 --- /dev/null +++ b/pyconbalkan/speaker/models.py @@ -0,0 +1,16 @@ +from django.db import models + +from pyconbalkan.core.models import SingleActiveModel + + +class Speaker(SingleActiveModel): + name = models.CharField(max_length=50) + job = models.CharField(max_length=100) + + def __str__(self): + return self.name + + +class SpeakerPhoto(models.Model): + speaker = models.ForeignKey(Speaker, related_name='images') + profile_picture = models.ImageField(upload_to="static/img") diff --git a/pyconbalkan/speaker/serializers.py b/pyconbalkan/speaker/serializers.py new file mode 100644 index 00000000..933baea3 --- /dev/null +++ b/pyconbalkan/speaker/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from pyconbalkan.speaker.models import Speaker + + +class SpeakerSerializer(serializers.ModelSerializer): + class Meta: + model = Speaker + fields = '__all__' diff --git a/pyconbalkan/speaker/tests.py b/pyconbalkan/speaker/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/pyconbalkan/speaker/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pyconbalkan/speaker/views.py b/pyconbalkan/speaker/views.py new file mode 100644 index 00000000..be56084e --- /dev/null +++ b/pyconbalkan/speaker/views.py @@ -0,0 +1,9 @@ +from rest_framework import viewsets + +from pyconbalkan.speaker.models import Speaker +from pyconbalkan.speaker.serializers import SpeakerSerializer + + +class SpeakerViewSet(viewsets.ModelViewSet): + queryset = Speaker.objects.all() + serializer_class = SpeakerSerializer diff --git a/pyconbalkan/urls.py b/pyconbalkan/urls.py index bc7936af..afd152d4 100644 --- a/pyconbalkan/urls.py +++ b/pyconbalkan/urls.py @@ -1,29 +1,20 @@ -"""pyconbalkan URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.11/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) -""" -import os - -from django.conf.urls import url +from django.conf.urls import url, include from django.contrib import admin from django.views.static import serve -from pyconbalkan.core import views +from pyconbalkan.core import views, routers from pyconbalkan.settings import PDF_ROOT +from pyconbalkan.conference.api_urls import router as conference +from pyconbalkan.speaker.api_urls import router as speaker + + +router = routers.DefaultRouter() +router.extend(conference) +router.extend(speaker) urlpatterns = [ url(r'^$', views.home, name='index'), url(r'^coc$', serve, {'path': 'coc_pyconbalkan.pdf', 'document_root': PDF_ROOT}), url(r'^admin/', admin.site.urls), + url(r'^api/', include(router.urls)), # API ] diff --git a/requirements.txt b/requirements.txt index e210818e..a1c76a15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,13 @@ dj-database-url==0.5.0 dj-static==0.0.6 Django==1.11.11 +django-countries==5.3 +django-filter==1.1.0 +djangorestframework==3.8.2 gunicorn==19.7.1 +Markdown==2.6.11 Pillow==5.1.0 -psycopg2==2.7.4 +psycopg2-binary==2.7.4 python-decouple==3.1 pytz==2018.3 static3==0.7.0