├── .gitignore ├── Deployment ├── apache-site.conf └── ems_gunicorn_service ├── Dockerfile ├── Dockerrun.aws.json ├── READEME.md ├── _config.yml ├── app.json ├── employee ├── __init__.py ├── admin.py ├── api_urls.py ├── apps.py ├── forms.py ├── middlewares.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20180415_0554.py │ ├── 0003_profile_picture.py │ ├── 0004_employee.py │ ├── 0005_auto_20180906_1552.py │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── ems ├── .project ├── __init__.py ├── decorators.py ├── settings.py ├── static │ └── css │ │ └── custom.css ├── templates │ ├── auth │ │ ├── login.html │ │ ├── profile.html │ │ ├── profile_update.html │ │ └── success.html │ ├── base.html │ ├── employee │ │ ├── add.html │ │ ├── delete.html │ │ ├── details.html │ │ ├── edit.html │ │ └── index.html │ ├── header.html │ └── polls │ │ ├── details.html │ │ ├── edit_poll.html │ │ ├── index.html │ │ ├── new_poll.html │ │ └── poll.html ├── urls.py └── wsgi.py ├── manage.py ├── poll ├── __init__.py ├── admin.py ├── api_urls.py ├── api_views.py ├── apps.py ├── context_processors.py ├── documents.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20180203_1058.py │ ├── 0003_auto_20180206_2134.py │ ├── 0004_auto_20180212_1627.py │ ├── 0005_auto_20180214_2152.py │ ├── 0006_auto_20180415_1140.py │ ├── 0007_auto_20180705_1601.py │ ├── 0008_auto_20180909_0514.py │ ├── 0009_auto_20181002_1544.py │ └── __init__.py ├── models.py ├── serializers.py ├── templatetags │ ├── __init__.py │ └── poll_extras.py ├── tests.py ├── urls.py └── views.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite3 2 | __pycache__ 3 | .vscode 4 | .project 5 | static 6 | media 7 | whoosh_index 8 | -------------------------------------------------------------------------------- /Deployment/apache-site.conf: -------------------------------------------------------------------------------- 1 | 2 | ServerAdmin 3 | ServerName 4 | DocumentRoot /srv 5 | 6 | Alias /static 7 | "> 8 | Options FollowSymLinks 9 | Order allow,deny 10 | Allow from all 11 | Require all granted 12 | 13 | 14 | Alias /media 15 | "> 16 | Options FollowSymLinks 17 | Order allow,deny 18 | Allow from all 19 | Require all granted 20 | 21 | ErrorLog ${APACHE_LOG_DIR}/apis_error.log 22 | CustomLog ${APACHE_LOG_DIR}/apis_access.log combined 23 | 24 | WSGIDaemonProcess python-home= python-path= 25 | WSGIProcessGroup 26 | WSGIScriptAlias / 27 | 28 | > 29 | 30 | Require all granted 31 | 32 | 33 | -------------------------------------------------------------------------------- /Deployment/ems_gunicorn_service: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NAME="demo" # Name of the application 4 | DJANGODIR=/home/hardik/projects/demo # Django project directory 5 | SOCKFILE=/home/hardik/projects/demo/run/gunicorn.sock # we will communicte using this unix socket 6 | USER=hardik # the user to run as 7 | GROUP=hardik # the group to run as 8 | NUM_WORKERS=3 # how many worker processes should Gunicorn spawn 9 | DJANGO_SETTINGS_MODULE=demo.settings # which settings file should Django use 10 | DJANGO_WSGI_MODULE=demo.wsgi # WSGI module name 11 | 12 | echo "Starting $NAME as `whoami`" 13 | 14 | # Activate the virtual environment 15 | cd $DJANGODIR 16 | Source ../venv/bin/activate 17 | export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE 18 | export PYTHONPATH=$DJANGODIR:$PYTHONPATH 19 | 20 | # Create the run directory if it doesn't exist 21 | RUNDIR=$(dirname $SOCKFILE) 22 | test -d $RUNDIR || mkdir -p $RUNDIR 23 | 24 | # Start your Django Unicorn 25 | # Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon) 26 | exec /home/hardik/projects/venv/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \ 27 | --name $NAME \ 28 | --workers $NUM_WORKERS \ 29 | --user=$USER --group=$GROUP \ 30 | --bind=$SOCKFILE \ 31 | --log-level=debug \ 32 | --log-file=- -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3-onbuild 2 | 3 | EXPOSE 8000 4 | 5 | CMD ["python", "./manage.py", "runserver", "0:8000"] 6 | -------------------------------------------------------------------------------- /Dockerrun.aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSEBDockerrunVersion": "1", 3 | "Image": { 4 | "Name": "hnmpatel/ems", 5 | "Update": "true" 6 | }, 7 | "Ports": [ 8 | { 9 | "ContainerPort": "8000" 10 | } 11 | ], 12 | "Logging": "/var/log/nginx" 13 | } 14 | -------------------------------------------------------------------------------- /READEME.md: -------------------------------------------------------------------------------- 1 | # Setup project and run with following steps 2 | ## Install virtual environment 3 | ```sudo apt install python-virtualenv``` 4 | 5 | ## Setup virtual environment with python 3.6+ 6 | ```virtualenv venv -p python3``` 7 | 8 | ## Install python dependencies 9 | ```pip install -r requirements.txt``` 10 | 11 | ## Migrate database 12 | ```python manage.py migrate``` 13 | 14 | ## Run server 15 | ```python manage.py runserver 8000``` 16 | 17 | ## Test on browser 18 | http://localhost:8000 19 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-leap-day -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ems", 3 | "description": "", 4 | "scripts": { 5 | }, 6 | "env": { 7 | }, 8 | "formation": { 9 | }, 10 | "addons": [ 11 | 12 | ], 13 | "buildpacks": [ 14 | { 15 | "url": "git://github.com/heroku/heroku-buildpack-python.git" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /employee/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarav-tech/ems/0f43447b8d698e35bae12c0c8a3723e3b1f9c29c/employee/__init__.py -------------------------------------------------------------------------------- /employee/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from employee.models import * 3 | 4 | 5 | admin.site.register(Profile) -------------------------------------------------------------------------------- /employee/api_urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from employee.views import * 3 | 4 | 5 | urlpatterns = [ 6 | path('employee/', EmployeeListView.as_view()) 7 | ] -------------------------------------------------------------------------------- /employee/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EmployeeConfig(AppConfig): 5 | name = 'employee' 6 | -------------------------------------------------------------------------------- /employee/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.core.exceptions import ValidationError 3 | from django.contrib.auth.models import User, Group 4 | 5 | 6 | class UserForm(forms.ModelForm): 7 | password = forms.CharField(widget=forms.PasswordInput) 8 | role = forms.ModelChoiceField(queryset=Group.objects.all()) 9 | 10 | class Meta: 11 | model = User 12 | fields = ['first_name', 'last_name', 13 | 'email', 'username', 14 | 'password'] 15 | # excludes = [''] 16 | 17 | label = { 18 | 'password': 'Password' 19 | } 20 | 21 | def __init__(self, *args, **kwargs): 22 | if kwargs.get('instance'): 23 | # We get the 'initial' keyword argument or initialize it 24 | # as a dict if it didn't exist. 25 | initial = kwargs.setdefault('initial', {}) 26 | # The widget for a ModelMultipleChoiceField expects 27 | # a list of primary key for the selected data. 28 | if kwargs['instance'].groups.all(): 29 | initial['role'] = kwargs['instance'].groups.all()[0] 30 | else: 31 | initial['role'] = None 32 | 33 | forms.ModelForm.__init__(self, *args, **kwargs) 34 | 35 | 36 | def save(self): 37 | password = self.cleaned_data.pop('password') 38 | role = self.cleaned_data.pop('role') 39 | u = super().save() 40 | u.groups.set([role]) 41 | 42 | u.set_password(password) 43 | u.save() 44 | return u 45 | 46 | -------------------------------------------------------------------------------- /employee/middlewares.py: -------------------------------------------------------------------------------- 1 | class RoleMiddleware: 2 | def __init__(self, get_response): 3 | self.get_response = get_response 4 | # One-time configuration and initialization. 5 | 6 | def __call__(self, request): 7 | """ 8 | Code to be executed for each request before 9 | the view (and later middleware) are called. 10 | """ 11 | 12 | response = self.get_response(request) 13 | 14 | """ 15 | Code to be executed for each request/response after 16 | the view is called. 17 | """ 18 | 19 | return response 20 | 21 | def process_view(self, request, view_func, *view_args, **view_kargs): 22 | """ 23 | called just before Django calls the view. 24 | Return either none or HttpResponse 25 | """ 26 | if request.user.is_authenticated: 27 | request.role = None 28 | groups = request.user.groups.all() 29 | if groups: 30 | request.role = groups[0].name 31 | 32 | # def process_exception(self, request, exception): 33 | # """ 34 | # Called for the response if exception is raised by view. 35 | # Return either none or HttpResponse 36 | # """ 37 | # pass 38 | 39 | 40 | # def process_template_response(self, request, response): 41 | # """ 42 | # request - HttpRequest object. 43 | # response - TemplateResponse object 44 | 45 | # return templateresponse 46 | # use this for changing template or context if it is needed. 47 | # """ 48 | # pass -------------------------------------------------------------------------------- /employee/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.1 on 2018-02-06 16:04 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Profile', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('designation', models.CharField(max_length=20)), 21 | ('salary', models.IntegerField(blank=True, null=True)), 22 | ('user', models.OneToOneField(on_delete='models.CASCADE', to=settings.AUTH_USER_MODEL)), 23 | ], 24 | options={ 25 | 'ordering': ('-salary',), 26 | }, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /employee/migrations/0002_auto_20180415_0554.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-15 05:54 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('employee', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='profile', 17 | name='user', 18 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /employee/migrations/0003_profile_picture.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-06-02 15:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('employee', '0002_auto_20180415_0554'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='profile', 15 | name='picture', 16 | field=models.ImageField(blank=True, max_length=255, null=True, upload_to='pictures/%Y/%m/%d/'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /employee/migrations/0004_employee.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-07-16 15:37 2 | 3 | import django.contrib.auth.models 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('auth', '0009_alter_user_last_name_max_length'), 11 | ('employee', '0003_profile_picture'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Employee', 17 | fields=[ 18 | ], 19 | options={ 20 | 'proxy': True, 21 | 'indexes': [], 22 | 'ordering': ('last_name',), 23 | }, 24 | bases=('auth.user',), 25 | managers=[ 26 | ('objects', django.contrib.auth.models.UserManager()), 27 | ], 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /employee/migrations/0005_auto_20180906_1552.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-09-06 15:52 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('employee', '0004_employee'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='employee', 15 | options={'ordering': ('username',)}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /employee/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarav-tech/ems/0f43447b8d698e35bae12c0c8a3723e3b1f9c29c/employee/migrations/__init__.py -------------------------------------------------------------------------------- /employee/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from django.db.models.signals import post_save 4 | from django.dispatch import receiver 5 | 6 | 7 | class Profile(models.Model): 8 | user = models.OneToOneField(User, on_delete=models.CASCADE) 9 | designation = models.CharField(max_length=20, null=False, blank=False) 10 | salary = models.IntegerField(null=True, blank=True) 11 | picture = models.ImageField(upload_to='pictures/%Y/%m/%d/', max_length=255, null=True, blank=True) 12 | 13 | class Meta: 14 | ordering = ('-salary',) 15 | 16 | def __str__(self): 17 | return "{0} - {1}".format(self.user.username, self.designation) 18 | 19 | 20 | 21 | class EmployeeManager(models.Manager): 22 | def get_queryset(self): 23 | return super().get_queryset().filter(profile__designation="Employee") 24 | 25 | class Employee(User): 26 | class Meta: 27 | ordering = ("username",) 28 | proxy = True 29 | 30 | objects = EmployeeManager() 31 | 32 | def full_name(self): 33 | return self.first_name + " - " + self.last_name 34 | 35 | 36 | @receiver(post_save, sender=User) 37 | def user_is_created(sender, instance, created, **kwargs): 38 | print(created) 39 | if created: 40 | Profile.objects.create(user=instance) 41 | else: 42 | instance.profile.save() 43 | 44 | 45 | from django.contrib.auth.models import User 46 | 47 | @property 48 | def full_name(self): 49 | return "{} {}".format(self.first_name, self.last_name) 50 | 51 | User.add_to_class('full_name', full_name) -------------------------------------------------------------------------------- /employee/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from django.contrib.auth.models import User 4 | from django.contrib.auth import authenticate 5 | from rest_framework import exceptions 6 | from .models import Profile 7 | 8 | 9 | class ProfileSerializer(serializers.ModelSerializer): 10 | class Meta: 11 | model = Profile 12 | fields = ['salary', 'designation', 'picture'] 13 | 14 | class EmployeeSerializer(serializers.ModelSerializer): 15 | profile = ProfileSerializer() 16 | 17 | class Meta: 18 | model = User 19 | fields = ['username', 'first_name', 20 | 'last_name', 'profile', 'email', 21 | 'is_staff', 'is_active', 'date_joined', 22 | 'is_superuser'] 23 | 24 | 25 | class LoginSerializer(serializers.Serializer): 26 | username = serializers.CharField() 27 | password = serializers.CharField() 28 | 29 | def validate(self, data): 30 | username = data.get("username", "") 31 | password = data.get("password", "") 32 | 33 | if username and password: 34 | user = authenticate(username=username, password=password) 35 | if user: 36 | if user.is_active: 37 | data["user"] = user 38 | else: 39 | msg = "User is deactivated." 40 | raise exceptions.ValidationError(msg) 41 | else: 42 | msg = "Unable to login with given credentials." 43 | raise exceptions.ValidationError(msg) 44 | else: 45 | msg = "Must provide username and password both." 46 | raise exceptions.ValidationError(msg) 47 | return data -------------------------------------------------------------------------------- /employee/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /employee/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from employee.views import * 3 | 4 | urlpatterns = [ 5 | path('', employee_list, name='employee_list'), 6 | path('/details/', employee_details, name="employee_details"), 7 | path('/edit/', employee_edit, name="employee_edit"), 8 | path('add/', employee_add, name="employee_add"), 9 | path('/delete/', employee_delete, name="employee_delete"), 10 | ] -------------------------------------------------------------------------------- /employee/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, get_object_or_404 2 | from django.http import HttpResponseRedirect 3 | from django.contrib.auth.models import User 4 | from django.contrib.auth import authenticate, login, logout 5 | from django.contrib.auth.decorators import login_required 6 | from django.views.generic import DetailView, ListView 7 | from django.views.generic.edit import UpdateView 8 | from django.urls import reverse 9 | from django.urls import reverse_lazy 10 | from employee.forms import UserForm 11 | from ems.decorators import admin_hr_required, admin_only 12 | 13 | def index(request): 14 | return HttpResponseRedirect(reverse('employee_list')) 15 | 16 | def user_login(request): 17 | context = {} 18 | if request.method == "POST": 19 | username = request.POST['username'] 20 | password = request.POST['password'] 21 | user = authenticate(request, username=username, password=password) 22 | if user: 23 | login(request, user) 24 | if request.GET.get('next', None): 25 | return HttpResponseRedirect(request.GET['next']) 26 | return HttpResponseRedirect(reverse('employee_list')) 27 | else: 28 | context["error"] = "Provide valid credentials !!" 29 | return render(request, "auth/login.html", context) 30 | else: 31 | return render(request, "auth/login.html", context) 32 | 33 | @login_required(login_url="/login/") 34 | def success(request): 35 | context = {} 36 | context['user'] = request.user 37 | return render(request, "auth/success.html", context) 38 | 39 | def user_logout(request): 40 | logout(request) 41 | return HttpResponseRedirect(reverse('user_login')) 42 | 43 | 44 | @login_required(login_url="/login/") 45 | def employee_list(request): 46 | print(request.role) 47 | context = {} 48 | context['users'] = User.objects.all() 49 | context['title'] = 'Employees' 50 | return render(request, 'employee/index.html', context) 51 | 52 | @login_required(login_url="/login/") 53 | def employee_details(request, id=None): 54 | context = {} 55 | context['user'] = get_object_or_404(User, id=id) 56 | return render(request, 'employee/details.html', context) 57 | 58 | @login_required(login_url="/login/") 59 | @admin_only 60 | def employee_add(request): 61 | context = {} 62 | if request.method == 'POST': 63 | user_form = UserForm(request.POST) 64 | context['user_form'] = user_form 65 | if user_form.is_valid(): 66 | u = user_form.save() 67 | return HttpResponseRedirect(reverse('employee_list')) 68 | else: 69 | return render(request, 'employee/add.html', context) 70 | else: 71 | user_form = UserForm() 72 | context['user_form'] = user_form 73 | return render(request, 'employee/add.html', context) 74 | 75 | @login_required(login_url="/login/") 76 | def employee_edit(request, id=None): 77 | user = get_object_or_404(User, id=id) 78 | if request.method == 'POST': 79 | user_form = UserForm(request.POST, instance=user) 80 | if user_form.is_valid(): 81 | user_form.save() 82 | return HttpResponseRedirect(reverse('employee_list')) 83 | else: 84 | return render(request, 'employee/edit.html', {"user_form": user_form}) 85 | else: 86 | user_form = UserForm(instance=user) 87 | return render(request, 'employee/edit.html', {"user_form": user_form}) 88 | 89 | @login_required(login_url="/login/") 90 | def employee_delete(request, id=None): 91 | user = get_object_or_404(User, id=id) 92 | if request.method == 'POST': 93 | user.delete() 94 | return HttpResponseRedirect(reverse('employee_list')) 95 | else: 96 | context = {} 97 | context['user'] = user 98 | return render(request, 'employee/delete.html', context) 99 | 100 | 101 | class ProfileUpdate(UpdateView): 102 | fields = ['designation', 'salary', 'picture'] 103 | template_name = 'auth/profile_update.html' 104 | success_url = reverse_lazy('my_profile') 105 | 106 | def get_object(self): 107 | return self.request.user.profile 108 | 109 | 110 | 111 | class MyProfile(DetailView): 112 | template_name = 'auth/profile.html' 113 | 114 | def get_object(self): 115 | return self.request.user.profile 116 | 117 | 118 | from rest_framework.views import APIView 119 | from rest_framework.generics import GenericAPIView 120 | from .serializers import LoginSerializer 121 | from django.contrib.auth import login as django_login, logout as django_logout 122 | from rest_framework.authtoken.models import Token 123 | from rest_framework.authentication import TokenAuthentication 124 | from rest_framework.response import Response 125 | from rest_framework import generics 126 | from .serializers import EmployeeSerializer 127 | from django_filters.rest_framework import DjangoFilterBackend 128 | from rest_framework.filters import OrderingFilter, SearchFilter 129 | from django_filters import FilterSet 130 | from django_filters import rest_framework as filters 131 | 132 | 133 | class LoginView(GenericAPIView): 134 | serializer_class = LoginSerializer 135 | 136 | def post(self, request): 137 | serializer = LoginSerializer(data=request.data) 138 | serializer.is_valid(raise_exception=True) 139 | user = serializer.validated_data["user"] 140 | django_login(request, user) 141 | token, created = Token.objects.get_or_create(user=user) 142 | return Response({"token": token.key}, status=200) 143 | 144 | 145 | class LogoutView(APIView): 146 | authentication_classes = (TokenAuthentication, ) 147 | 148 | def post(self, request): 149 | django_logout(request) 150 | return Response(status=204) 151 | 152 | class EmployeeFilter(FilterSet): 153 | is_active = filters.CharFilter('is_active') 154 | designation = filters.CharFilter('profile__designation') 155 | min_salary = filters.CharFilter(method="filter_by_min_salary") 156 | max_salary = filters.CharFilter(method="filter_by_max_salary") 157 | 158 | class Meta: 159 | model = User 160 | fields = ('is_active', 'designation', 'username',) 161 | 162 | def filter_by_min_salary(self, queryset, name, value): 163 | queryset = queryset.filter(profile__salary__gt=value) 164 | return queryset 165 | 166 | def filter_by_max_salary(self, queryset, name, value): 167 | queryset = queryset.filter(profile__salary__lt=value) 168 | return queryset 169 | 170 | 171 | class EmployeeListView(generics.ListAPIView): 172 | serializer_class = EmployeeSerializer 173 | queryset = User.objects.all() 174 | filter_backends = (DjangoFilterBackend, OrderingFilter, SearchFilter) 175 | # filter_fields = ('is_active', 'profile__designation', ) 176 | filter_class = EmployeeFilter 177 | 178 | ordering_fields = ('is_active', 'username') 179 | ordering = ('username',) 180 | search_fields = ('username', 'first_name') 181 | 182 | 183 | # def get_queryset(self): 184 | # queryset = User.objects.all() 185 | # active = self.request.query_params.get('is_active', '') 186 | # if active: 187 | # if active == "False": 188 | # active = False 189 | # elif active == "True": 190 | # active = True 191 | # else: 192 | # return queryset 193 | # return queryset.filter(is_active=active) 194 | # return queryset 195 | -------------------------------------------------------------------------------- /ems/.project: -------------------------------------------------------------------------------- 1 | /home/hardik-p/projects/mine/ems 2 | -------------------------------------------------------------------------------- /ems/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarav-tech/ems/0f43447b8d698e35bae12c0c8a3723e3b1f9c29c/ems/__init__.py -------------------------------------------------------------------------------- /ems/decorators.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponseRedirect 2 | from django.urls import reverse 3 | from django.core.exceptions import PermissionDenied 4 | 5 | def admin_hr_required(view_func): 6 | def wrap(request, *args, **kwargs): 7 | allowed_roles=["Admin", "HR"] 8 | if request.role in allowed_roles: 9 | return view_func(request, *args, **kwargs) 10 | else: 11 | return HttpResponseRedirect(reverse('employee_list')) 12 | return wrap 13 | 14 | def admin_only(view_func): 15 | def wrap(request, *args, **kwargs): 16 | if request.role == "Admin": 17 | return view_func(request, *args, **kwargs) 18 | else: 19 | return HttpResponseRedirect(reverse('employee_list')) 20 | return wrap 21 | -------------------------------------------------------------------------------- /ems/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for ems project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '#c=44^9m)58=k5el2oi&0b7kr=clo%^m#5repy5ytkymok-v4e' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ["*"] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'rest_framework_swagger', 41 | 42 | # 'haystack', 43 | 44 | 'poll', 45 | 'employee', 46 | 'rest_framework', 47 | 'rest_framework.authtoken', 48 | 'django_extensions', 49 | 'django_filters', 50 | # 'django_elasticsearch_dsl', 51 | 52 | 'material.theme.amber', 53 | 'material', 54 | ] 55 | 56 | MIDDLEWARE = [ 57 | 'django.middleware.security.SecurityMiddleware', 58 | 'django.contrib.sessions.middleware.SessionMiddleware', 59 | 'django.middleware.common.CommonMiddleware', 60 | 'django.middleware.csrf.CsrfViewMiddleware', 61 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 62 | 'django.contrib.messages.middleware.MessageMiddleware', 63 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 64 | 'employee.middlewares.RoleMiddleware' 65 | ] 66 | 67 | ROOT_URLCONF = 'ems.urls' 68 | 69 | TEMPLATES = [ 70 | { 71 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 72 | 'APP_DIRS': True, 73 | 'DIRS': [ 74 | os.path.join(BASE_DIR, 'ems', 'templates'), 75 | ], 76 | 'OPTIONS': { 77 | 'context_processors': [ 78 | 'django.template.context_processors.debug', 79 | 'django.template.context_processors.request', 80 | 'django.contrib.auth.context_processors.auth', 81 | 'django.contrib.messages.context_processors.messages', 82 | 'poll.context_processors.polls_count' 83 | ], 84 | }, 85 | }, 86 | ] 87 | 88 | WSGI_APPLICATION = 'ems.wsgi.application' 89 | 90 | 91 | # Database 92 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 93 | 94 | DATABASES = { 95 | 'default': { 96 | 'ENGINE': 'django.db.backends.sqlite3', 97 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 98 | } 99 | } 100 | 101 | 102 | # Password validation 103 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 104 | 105 | AUTH_PASSWORD_VALIDATORS = [ 106 | { 107 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 108 | }, 109 | { 110 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 111 | }, 112 | { 113 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 114 | }, 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 117 | }, 118 | ] 119 | 120 | 121 | # Internationalization 122 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 123 | 124 | LANGUAGE_CODE = 'en-us' 125 | 126 | TIME_ZONE = 'UTC' 127 | 128 | USE_I18N = True 129 | 130 | USE_L10N = True 131 | 132 | USE_TZ = True 133 | 134 | 135 | # Static files (CSS, JavaScript, Images) 136 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 137 | 138 | STATIC_URL = '/static/' 139 | STATIC_ROOT = 'static' 140 | 141 | MEDIA_URL = '/media/' 142 | MEDIA_ROOT = 'media' 143 | 144 | LOGIN_URL = '/login/' 145 | 146 | REST_FRAMEWORK = { 147 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 148 | 'rest_framework.authentication.TokenAuthentication', 149 | 'rest_framework.authentication.BasicAuthentication', 150 | 'rest_framework.authentication.SessionAuthentication', 151 | ), 152 | 'DEFAULT_PAGINATION_CLASS': 153 | 'rest_framework.pagination.PageNumberPagination', 154 | 'PAGE_SIZE': 100, 155 | 'ORDERING_PARAM': 'ordering', 156 | } 157 | 158 | 159 | ELASTICSEARCH_DSL={ 160 | 'default': { 161 | 'hosts': 'localhost:9200' 162 | }, 163 | } 164 | 165 | SWAGGER_SETTINGS = { 166 | 'SECURITY_DEFINITIONS': { 167 | 'api_key': { 168 | "type": "apiKey", 169 | "name": "Authorization", 170 | "in": "header" 171 | } 172 | }, 173 | } -------------------------------------------------------------------------------- /ems/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .brand-logo { 2 | position: relative !important; 3 | } -------------------------------------------------------------------------------- /ems/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | Employee Management System 5 | 6 | 7 | 8 | 9 |
10 | {% csrf_token %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 |

{{error}}

22 |
18 |
23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /ems/templates/auth/profile.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |
6 |

My Profile

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 |
First Name{{object.user.first_name}}
Last Name{{object.user.last_name}}
Email Id{{object.user.email}}
Salary{{object.salary}}
Designation{{object.designation}}
Profile Picture 32 | {% if object.picture %} 33 | 34 | {% endif %} 35 |
39 |
40 | Edit Profile 41 |
42 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/auth/profile_update.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |
6 |
7 | {% csrf_token %} 8 | 9 | {{form.as_table}} 10 |
11 |
12 | 13 |
14 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/auth/success.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |

User {{user.username}} is logged in successfully.

6 |
7 | {% csrf_token %} 8 | 9 |
10 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | Employee Management System 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% include 'header.html' %} 13 | 14 |
15 | {% block content %} 16 | {% endblock content %} 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ems/templates/employee/add.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |
6 |
7 | {% csrf_token %} 8 | 9 | {{user_form.as_table}} 10 |
11 |
12 | 13 |
14 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/employee/delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |
6 |

{{user.first_name}}   {{user.last_name}}

7 |
8 | {% csrf_token %} 9 |

Are you sure to delete this Employee?

10 | 11 |
12 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/employee/details.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
First Name{{user.first_name}}
Last Name{{user.last_name}}
Email Id{{user.email}}
Salary{{user.profile.salary}}
Designation{{user.profile.designation}}
30 |
31 | Edit 32 | Delete 33 |
34 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/employee/edit.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load material_form %} 3 | 4 | 5 | {% block content %} 6 |
7 |
8 | {% csrf_token %} 9 | 10 | {% form form=user_form %}{% endform %} 11 |
12 |
13 | 14 |
15 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/employee/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load poll_extras %} 3 | 4 | 5 | {% block content %} 6 |
7 |
8 |

9 |

List of {{title}}

10 | {% if request.role == 'Admin' %} 11 | Add employee 12 | {% endif %} 13 |

14 |
15 | {% if users %} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% for user in users %} 25 | 26 | 35 | 36 | {% endfor %} 37 | 38 |
NameActions
27 | {{user.first_name}} {{user.last_name}} 28 | {{user.email}} 29 | 30 | 31 | Details 32 | Edit 33 | Delete 34 |
39 | {% else %} 40 |

There is no employee available.

41 | {% endif %} 42 |
43 |
44 |
45 | {% recent_polls 1 name="hardik patel" as questions %} 46 |

47 |

Recent Polls

48 |

49 | {% if questions %} 50 |
51 | {% for question in questions %} 52 | {{question.title}} 53 | {% endfor %} 54 |
55 | {% else %} 56 |

There is no poll available.

57 | {% endif %} 58 |
59 |
60 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/header.html: -------------------------------------------------------------------------------- 1 | {% load poll_extras %} 2 | -------------------------------------------------------------------------------- /ems/templates/polls/details.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |

6 |

{{question.title}}

7 |

8 |
9 | {% for choice in question.choices %} 10 | 11 | {{choice.votes}} 12 | {{choice.text}} 13 | 14 | 15 | {% empty %} 16 |
There is no chice available for this question.
17 | {% endfor %} 18 |
19 |
20 |
Created by: {{question.created_by.first_name}}
21 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/polls/edit_poll.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |
6 |
7 | {% csrf_token %} 8 | 9 | {{poll_form.as_table}} 10 | {% for form in choice_forms %} 11 | {{form.as_table}} 12 | {% endfor %} 13 |
14 |
15 | 16 |
17 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/polls/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |

6 |

List of {{title}}

7 |

8 | 9 |
10 | {% if questions %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for question in questions %} 20 | 21 | 24 | 29 | 30 | {% endfor %} 31 | 32 |
PollActions
22 | {{question.title}} 23 | 25 | Details 26 | Edit 27 | Delete 28 |
33 | {% else %} 34 |

There is no question available.

35 | {% endif %} 36 |
37 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/polls/new_poll.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |
6 |
7 | {% csrf_token %} 8 | 9 | {{poll_form.as_table}} 10 | {% for form in choice_forms %} 11 | {{form.as_table}} 12 | {% endfor %} 13 |
14 |
15 | 16 |
17 | {% endblock content %} -------------------------------------------------------------------------------- /ems/templates/polls/poll.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 |

6 |

{{question.title}}

7 |

8 |
9 | {% csrf_token %} 10 | {% for choice in question.choices %} 11 |
12 | 16 |
17 | {% empty %} 18 |

There is no chice available for this question.

19 | {% endfor %} 20 | 21 |
22 | {% endblock content %} -------------------------------------------------------------------------------- /ems/urls.py: -------------------------------------------------------------------------------- 1 | """ems URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | from rest_framework.schemas import get_schema_view 19 | from django.conf.urls.static import static 20 | from rest_framework_swagger.views import get_swagger_view 21 | 22 | from django.conf import settings 23 | 24 | 25 | from employee.views import ( index, user_login, user_logout, 26 | success, ProfileUpdate, MyProfile, LoginView, LogoutView) 27 | 28 | schema_view = get_swagger_view(title='EMS API Documentation') 29 | 30 | urlpatterns = [ 31 | path('api_documentation/', schema_view), 32 | path('', index, name='home'), 33 | path('admin/', admin.site.urls), 34 | path('poll/', include('poll.urls')), 35 | path('api/v1/', include('poll.api_urls')), 36 | path('api/v1/', include('employee.api_urls')), 37 | # path('api/v1/auth/', include('rest_auth.urls')), 38 | path('api/v1/auth/login/', LoginView.as_view()), 39 | path('api/v1/auth/logout/', LogoutView.as_view()), 40 | path('employee/', include('employee.urls')), 41 | 42 | path('login/', user_login, name="user_login"), 43 | path('success/', success, name="user_success"), 44 | path('logout/', user_logout, name="user_logout"), 45 | path('profile/', MyProfile.as_view(), name="my_profile"), 46 | path('profile/update', ProfileUpdate.as_view(), name="update_profile"), 47 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 48 | 49 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 50 | -------------------------------------------------------------------------------- /ems/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ems project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ems.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ems.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /poll/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarav-tech/ems/0f43447b8d698e35bae12c0c8a3723e3b1f9c29c/poll/__init__.py -------------------------------------------------------------------------------- /poll/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from poll.models import * 3 | 4 | admin.site.register(Question) 5 | admin.site.register(Choice) 6 | admin.site.register(Answer) 7 | admin.site.register(Tag) -------------------------------------------------------------------------------- /poll/api_urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from poll.views import * 3 | 4 | from rest_framework.routers import DefaultRouter, SimpleRouter 5 | 6 | router = DefaultRouter() 7 | router.register('poll', PollViewSet) 8 | 9 | 10 | poll_list_view = PollViewSet.as_view({ 11 | "get": "list", 12 | "post": "create" 13 | }) 14 | 15 | urlpatterns = [ 16 | path('', include(router.urls)), 17 | # path('poll/', PollAPIView.as_view()), 18 | # path('poll//', PollDetailView.as_view()), 19 | # path('generics/poll/', poll_list_view), 20 | # path('generics/poll//', PollListView.as_view()), 21 | # path('poll/search/', QuestionSearchViewSet.as_view({'get': 'list'})), 22 | ] -------------------------------------------------------------------------------- /poll/api_views.py: -------------------------------------------------------------------------------- 1 | from django_filters.rest_framework import DjangoFilterBackend 2 | from django_filters import FilterSet 3 | from django_filters import rest_framework as filters 4 | 5 | from rest_framework.parsers import JSONParser 6 | from rest_framework.views import APIView 7 | from rest_framework.response import Response 8 | from rest_framework import status 9 | from rest_framework import generics 10 | from rest_framework import mixins 11 | from rest_framework.decorators import action 12 | from rest_framework.authentication import ( 13 | SessionAuthentication, 14 | BasicAuthentication, 15 | TokenAuthentication, 16 | ) 17 | from rest_framework.permissions import IsAuthenticated, IsAdminUser 18 | from rest_framework import viewsets 19 | from rest_framework.decorators import action 20 | 21 | from poll.forms import PollForm, ChoiceForm 22 | from poll.models import * 23 | from poll.serializers import ( 24 | QuestionSerializer, 25 | ChoiceSerializer, 26 | QuestionSearchSerializer, 27 | ) 28 | 29 | 30 | class QuestionSearchViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): 31 | serializer_class = QuestionSearchSerializer 32 | 33 | def get_queryset(self): 34 | result = QuestionDocument.search() 35 | 36 | 37 | class PollFilter(FilterSet): 38 | tags = filters.CharFilter(method="filter_by_tags") 39 | 40 | class Meta: 41 | model = Question 42 | fields = ["tags"] 43 | 44 | def filter_by_tags(self, queryset, name, value): 45 | tag_names = value.strip().split(",") 46 | tags = Tag.objects.filter(name__in=tag_names) 47 | return queryset.filter(tags__in=tags).distinct() 48 | 49 | 50 | class PollViewSet(viewsets.ModelViewSet): 51 | serializer_class = QuestionSerializer 52 | queryset = Question.objects.all() 53 | lookup_field = "id" 54 | filter_backends = (DjangoFilterBackend,) 55 | filter_class = PollFilter 56 | authentication_classes = (TokenAuthentication,) 57 | 58 | @action(detail=True, methods=["GET"]) 59 | def choices(self, request, id=None): 60 | question = self.get_object() 61 | choices = Choice.objects.filter(question=question) 62 | serializer = ChoiceSerializer(choices, many=True) 63 | return Response(serializer.data, status=200) 64 | 65 | @action(detail=True, methods=["POST"]) 66 | def choice(self, request, id=None): 67 | question = self.get_object() 68 | data = request.data 69 | data["question"] = question.id 70 | serializer = ChoiceSerializer(data=data) 71 | if serializer.is_valid(): 72 | serializer.save() 73 | return Response(serializer.data, status=201) 74 | return Response(serializer.erros, status=400) 75 | 76 | class PollListView( 77 | generics.GenericAPIView, 78 | mixins.ListModelMixin, 79 | mixins.CreateModelMixin, 80 | mixins.RetrieveModelMixin, 81 | mixins.UpdateModelMixin, 82 | mixins.DestroyModelMixin, 83 | ): 84 | serializer_class = QuestionSerializer 85 | queryset = Question.objects.all() 86 | lookup_field = "id" 87 | authentication_classes = [ 88 | TokenAuthentication, 89 | SessionAuthentication, 90 | BasicAuthentication, 91 | ] 92 | permission_classes = [IsAuthenticated, IsAdminUser] 93 | 94 | 95 | def get(self, request, id=None): 96 | if id: 97 | return self.retrieve(request, id) 98 | else: 99 | return self.list(request) 100 | 101 | def post(self, request): 102 | return self.create(request) 103 | 104 | def perform_create(self, serializer): 105 | serializer.save(created_by=self.request.user) 106 | 107 | def put(self, request, id=None): 108 | return self.update(request, id) 109 | 110 | def perform_update(self, serializer): 111 | print(self.request.user) 112 | serializer.save(created_by=self.request.user) 113 | 114 | def delete(self, request, id=None): 115 | return self.destroy(request, id) 116 | 117 | 118 | class PollAPIView(APIView): 119 | def get(self, request): 120 | questions = Question.objects.all() 121 | serailizer = QuestionSerializer(questions, many=True) 122 | return Response(serailizer.data, status=200) 123 | 124 | def post(self, request): 125 | data = request.data 126 | serializer = QuestionSerializer(data=data) 127 | if serializer.is_valid(): 128 | serializer.save() 129 | return Response(serializer.data, status=201) 130 | return Response(serializer.erros, status=400) 131 | 132 | 133 | class PollDetailView(APIView): 134 | def get_object(self, id): 135 | try: 136 | return Question.objects.get(id=id) 137 | except Question.DoesNotExist as e: 138 | return Response({"error": "Given question object not found."}, status=404) 139 | 140 | def get(self, request, id=None): 141 | instance = self.get_object(id) 142 | serailizer = QuestionSerializer(instance) 143 | return Response(serailizer.data) 144 | 145 | def put(self, request, id=None): 146 | data = request.data 147 | instance = self.get_object(id) 148 | serializer = QuestionSerializer(instance, data=data) 149 | if serializer.is_valid(): 150 | serializer.save() 151 | return Response(serializer.data, status=200) 152 | return Response(serializer.erros, status=400) 153 | 154 | def delete(self, request, id=None): 155 | instance = self.get_object(id) 156 | instance.delete() 157 | return HttpResponse(status=204) 158 | 159 | 160 | @csrf_exempt 161 | def poll(request): 162 | if request.method == "GET": 163 | questions = Question.objects.all() 164 | serailizer = QuestionSerializer(questions, many=True) 165 | return JsonResponse(serailizer.data, safe=False) 166 | 167 | elif request.method == "POST": 168 | json_parser = JSONParser() 169 | data = json_parser.parse(request) 170 | serializer = QuestionSerializer(data=data) 171 | if serializer.is_valid(): 172 | serializer.save() 173 | return JsonResponse(serializer.data, status=201) 174 | return JsonResponse(serializer.errors, status=400) 175 | 176 | 177 | @csrf_exempt 178 | def poll_details(request, id): 179 | try: 180 | instance = Question.objects.get(id=id) 181 | except Question.DoesNotExist as e: 182 | return JsonResponse({"error": "Given question object not found."}, status=404) 183 | 184 | if request.method == "GET": 185 | serailizer = QuestionSerializer(instance) 186 | return JsonResponse(serailizer.data) 187 | 188 | elif request.method == "PUT": 189 | json_parser = JSONParser() 190 | data = json_parser.parse(request) 191 | serializer = QuestionSerializer(instance, data=data) 192 | if serializer.is_valid(): 193 | serializer.save() 194 | return JsonResponse(serializer.data, status=200) 195 | return JsonResponse(serializer.erros, status=400) 196 | 197 | elif request.method == "DELETE": 198 | instance.delete() 199 | return HttpResponse(status=204) 200 | -------------------------------------------------------------------------------- /poll/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PollConfig(AppConfig): 5 | name = 'poll' 6 | -------------------------------------------------------------------------------- /poll/context_processors.py: -------------------------------------------------------------------------------- 1 | from poll.models import Question 2 | 3 | def polls_count(request): 4 | count = Question.objects.count() 5 | print("Polls count - ", count) 6 | return {"polls_count": count} -------------------------------------------------------------------------------- /poll/documents.py: -------------------------------------------------------------------------------- 1 | from django_elasticsearch_dsl import DocType, Index 2 | from .models import Question 3 | 4 | 5 | questions = Index('questions') 6 | questions.settings( 7 | number_of_shards=1, 8 | number_of_replicas=0 9 | ) 10 | 11 | @questions.doc_type 12 | class QuestionDocument(DocType): 13 | class Meta: 14 | model = Question 15 | fields = [ 16 | 'id', 17 | 'title', 18 | 'status', 19 | 'created_at' 20 | ] -------------------------------------------------------------------------------- /poll/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from poll.models import Question, Choice 3 | 4 | class PollForm(forms.ModelForm): 5 | title = forms.CharField(max_length=255, label='Question') 6 | 7 | class Meta: 8 | model = Question 9 | fields = ['title'] 10 | 11 | class ChoiceForm(forms.ModelForm): 12 | text = forms.CharField( 13 | max_length=255, label="Choice") 14 | 15 | class Meta: 16 | model = Choice 17 | exclude = ('question',) -------------------------------------------------------------------------------- /poll/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.1 on 2018-02-03 05:07 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Question', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('title', models.TextField(blank=True, null=True)), 21 | ('status', models.CharField(default='inactive', max_length=10)), 22 | ('created_at', models.DateTimeField(auto_now_add=True)), 23 | ('updated_at', models.DateTimeField(auto_now=True)), 24 | ('created_by', models.ForeignKey(blank=True, null=True, on_delete='CASCADE', to=settings.AUTH_USER_MODEL)), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /poll/migrations/0002_auto_20180203_1058.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.1 on 2018-02-03 05:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('poll', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Choice', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('text', models.TextField(blank=True, null=True)), 18 | ('created_at', models.DateTimeField(auto_now_add=True)), 19 | ('updated_at', models.DateTimeField(auto_now=True)), 20 | ], 21 | ), 22 | migrations.AddField( 23 | model_name='question', 24 | name='end_date', 25 | field=models.DateTimeField(blank=True, null=True), 26 | ), 27 | migrations.AddField( 28 | model_name='question', 29 | name='start_date', 30 | field=models.DateTimeField(blank=True, null=True), 31 | ), 32 | migrations.AddField( 33 | model_name='choice', 34 | name='question', 35 | field=models.ForeignKey(on_delete='CASCADE', to='poll.Question'), 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /poll/migrations/0003_auto_20180206_2134.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.1 on 2018-02-06 16:04 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('poll', '0002_auto_20180203_1058'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='question', 16 | name='created_by', 17 | field=models.ForeignKey(blank=True, null=True, on_delete='models.CASCADE', to=settings.AUTH_USER_MODEL), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /poll/migrations/0004_auto_20180212_1627.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.1 on 2018-02-12 10:57 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('poll', '0003_auto_20180206_2134'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='choice', 15 | name='question', 16 | field=models.ForeignKey(on_delete='models.CASCADE', to='poll.Question'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /poll/migrations/0005_auto_20180214_2152.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.1 on 2018-02-14 16:22 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('poll', '0004_auto_20180212_1627'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Answer', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('created_at', models.DateTimeField(auto_now_add=True)), 21 | ('updated_at', models.DateTimeField(auto_now=True)), 22 | ], 23 | ), 24 | migrations.AlterField( 25 | model_name='choice', 26 | name='question', 27 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='poll.Question'), 28 | ), 29 | migrations.AlterField( 30 | model_name='question', 31 | name='created_by', 32 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 33 | ), 34 | migrations.AddField( 35 | model_name='answer', 36 | name='choice', 37 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='poll.Choice'), 38 | ), 39 | migrations.AddField( 40 | model_name='answer', 41 | name='user', 42 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /poll/migrations/0006_auto_20180415_1140.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-04-15 11:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('poll', '0005_auto_20180214_2152'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='question', 15 | name='title', 16 | field=models.TextField(default=1), 17 | preserve_default=False, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /poll/migrations/0007_auto_20180705_1601.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-07-05 16:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('poll', '0006_auto_20180415_1140'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Tag', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('name', models.CharField(max_length=50)), 18 | ], 19 | ), 20 | migrations.AddField( 21 | model_name='question', 22 | name='tags', 23 | field=models.ManyToManyField(to='poll.Tag'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /poll/migrations/0008_auto_20180909_0514.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-09-09 05:14 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('poll', '0007_auto_20180705_1601'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='tag', 16 | name='created_at', 17 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), 18 | preserve_default=False, 19 | ), 20 | migrations.AddField( 21 | model_name='tag', 22 | name='updated_at', 23 | field=models.DateTimeField(auto_now=True), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /poll/migrations/0009_auto_20181002_1544.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.4 on 2018-10-02 15:44 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('contenttypes', '0002_remove_content_type_name'), 11 | ('poll', '0008_auto_20180909_0514'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Comment', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('text', models.TextField()), 20 | ('object_id', models.PositiveIntegerField()), 21 | ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), 22 | ], 23 | ), 24 | migrations.AlterModelOptions( 25 | name='question', 26 | options={'ordering': ('-created_at',)}, 27 | ), 28 | migrations.AlterModelOptions( 29 | name='tag', 30 | options={'ordering': []}, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /poll/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarav-tech/ems/0f43447b8d698e35bae12c0c8a3723e3b1f9c29c/poll/migrations/__init__.py -------------------------------------------------------------------------------- /poll/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from django.contrib.contenttypes.models import ContentType 4 | from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey 5 | 6 | 7 | class ObjectTracking(models.Model): 8 | created_at = models.DateTimeField(auto_now_add=True) 9 | updated_at = models.DateTimeField(auto_now=True) 10 | 11 | class Meta: 12 | abstract = True 13 | ordering = ('-created_at',) 14 | 15 | class Comment(models.Model): 16 | text = models.TextField(null=False, blank=False) 17 | 18 | content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 19 | object_id = models.PositiveIntegerField() 20 | content_object = GenericForeignKey('content_type', 'object_id') 21 | 22 | def __str__(self): 23 | return self.text[:20] 24 | 25 | class Tag(ObjectTracking): 26 | name = models.CharField(max_length=50) 27 | 28 | def __str__(self): 29 | return self.name 30 | 31 | class Meta: 32 | ordering = [] 33 | 34 | class QuestionManager(models.Manager): 35 | def get_queryset(self): 36 | return super().get_queryset().filter(status="active") 37 | 38 | def all_objects(self): 39 | return super().get_queryset() 40 | 41 | def inactive(self): 42 | return self.all_objects().filter(status='inactive') 43 | 44 | 45 | 46 | class Question(ObjectTracking): 47 | title = models.TextField(null=False, blank=False) 48 | status = models.CharField(default='inactive', max_length=10) 49 | created_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE) 50 | 51 | start_date = models.DateTimeField(null=True, blank=True) 52 | end_date = models.DateTimeField(null=True, blank=True) 53 | tags = models.ManyToManyField(Tag) 54 | 55 | comments = GenericRelation(Comment, related_query_name="question") 56 | 57 | objects = QuestionManager() 58 | 59 | 60 | def __str__(self): 61 | return self.title 62 | 63 | @property 64 | def choices(self): 65 | return self.choice_set.all() 66 | 67 | 68 | class Choice(models.Model): 69 | question = models.ForeignKey('poll.Question', on_delete=models.CASCADE) 70 | text = models.TextField(null=True, blank=True) 71 | 72 | created_at = models.DateTimeField(auto_now_add=True) 73 | updated_at = models.DateTimeField(auto_now=True) 74 | 75 | def __str__(self): 76 | return self.text 77 | 78 | @property 79 | def votes(self): 80 | return self.answer_set.count() 81 | 82 | 83 | class Answer(models.Model): 84 | user = models.ForeignKey(User, on_delete=models.CASCADE) 85 | choice = models.ForeignKey(Choice, on_delete=models.CASCADE) 86 | 87 | created_at = models.DateTimeField(auto_now_add=True) 88 | updated_at = models.DateTimeField(auto_now=True) 89 | 90 | comments = GenericRelation(Comment, related_query_name="answer") 91 | 92 | def __str__(self): 93 | return self.user.first_name + '-' + self.choice.text -------------------------------------------------------------------------------- /poll/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from poll.models import Question, Choice, Tag 4 | from employee.serializers import EmployeeSerializer 5 | 6 | 7 | class ChoiceSerializer(serializers.ModelSerializer): 8 | id = serializers.IntegerField(required=False) 9 | 10 | class Meta: 11 | model = Choice 12 | fields = [ 13 | 'id', 14 | 'question', 15 | 'text' 16 | ] 17 | read_only_fields = ('question',) 18 | 19 | 20 | class TagSerializer(serializers.ModelSerializer): 21 | class Meta: 22 | model = Tag 23 | fields = ["name"] 24 | 25 | 26 | class QuestionSerializer(serializers.ModelSerializer): 27 | choices = ChoiceSerializer(many=True) 28 | tags = TagSerializer(many=True) 29 | 30 | class Meta: 31 | model = Question 32 | fields = [ 33 | "id", 34 | "title", 35 | "status", 36 | "created_by", 37 | "choices", 38 | "tags" 39 | ] 40 | read_only_fields = ["tags"] 41 | 42 | def create(self, validated_data): 43 | choices = validated_data.pop('choices') 44 | tags = validated_data.pop('tags') 45 | question = Question.objects.create(**validated_data) 46 | for choice in choices: 47 | Choice.objects.create(**choice, question=question) 48 | question.tags.set(tags) 49 | return question 50 | 51 | def update(self, instance, validated_data): 52 | choices = validated_data.pop('choices') 53 | instance.title = validated_data.get("title", instance.title) 54 | instance.save() 55 | keep_choices = [] 56 | for choice in choices: 57 | if "id" in choice.keys(): 58 | if Choice.objects.filter(id=choice["id"]).exists(): 59 | c = Choice.objects.get(id=choice["id"]) 60 | c.text = choice.get('text', c.text) 61 | c.save() 62 | keep_choices.append(c.id) 63 | else: 64 | continue 65 | else: 66 | c = Choice.objects.create(**choice, question=instance) 67 | keep_choices.append(c.id) 68 | 69 | for choice in instance.choices: 70 | if choice.id not in keep_choices: 71 | choice.delete() 72 | 73 | return instance 74 | 75 | 76 | class QuestionSearchSerializer(serializers.Serializer): 77 | id = serializers.IntegerField() 78 | title = serializers.CharField() 79 | status = serializers.CharField() 80 | created_at = serializers.DateTimeField() -------------------------------------------------------------------------------- /poll/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aarav-tech/ems/0f43447b8d698e35bae12c0c8a3723e3b1f9c29c/poll/templatetags/__init__.py -------------------------------------------------------------------------------- /poll/templatetags/poll_extras.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from poll.models import Question 3 | 4 | register = template.Library() 5 | 6 | def upper(value, n): 7 | """Converts a string into all uppercase""" 8 | return value.upper()[0:n] 9 | 10 | register.filter('upper', upper) 11 | 12 | 13 | @register.simple_tag 14 | def recent_polls(n=5, **kwargs): 15 | """Return recent n polls""" 16 | name = kwargs.get("name", "Argument is not passed") 17 | print(name) 18 | questions = Question.objects.all().order_by('-created_at') 19 | return questions[0:n] -------------------------------------------------------------------------------- /poll/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /poll/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from poll.views import * 3 | 4 | urlpatterns = [ 5 | path('add/', PollView.as_view(), name='poll_add'), 6 | path('/edit/', PollView.as_view(), name='poll_edit'), 7 | path('/delete/', PollView.as_view(), name='poll_delete'), 8 | path('list/', index, name='polls_list'), 9 | path('/details/', details, name="poll_details"), 10 | path('/', vote_poll, name="poll_vote") 11 | ] -------------------------------------------------------------------------------- /poll/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, reverse, redirect, get_object_or_404 2 | from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse 3 | from django.contrib.auth.decorators import login_required 4 | from django.utils.decorators import method_decorator 5 | from django.views.generic import View 6 | from django.views.decorators.csrf import csrf_exempt 7 | from django.views.generic.edit import CreateView 8 | from django.db.models import Q 9 | 10 | from rest_framework.parsers import JSONParser 11 | from rest_framework.views import APIView 12 | from rest_framework.response import Response 13 | from rest_framework import status 14 | from rest_framework import generics 15 | from rest_framework import mixins 16 | from rest_framework.decorators import action 17 | 18 | from ems.decorators import admin_hr_required, admin_only 19 | from poll.forms import PollForm, ChoiceForm 20 | from poll.models import * 21 | from poll.serializers import QuestionSerializer, ChoiceSerializer, QuestionSearchSerializer 22 | from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication 23 | from rest_framework.permissions import IsAuthenticated, IsAdminUser 24 | from rest_framework import viewsets 25 | from rest_framework.decorators import action 26 | from django_filters.rest_framework import DjangoFilterBackend 27 | from django_filters import FilterSet 28 | from django_filters import rest_framework as filters 29 | 30 | class QuestionSearchViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): 31 | serializer_class = QuestionSearchSerializer 32 | 33 | def get_queryset(self): 34 | result = QuestionDocument.search() 35 | 36 | 37 | class PollFilter(FilterSet): 38 | tags = filters.CharFilter(method="filter_by_tags") 39 | 40 | class Meta: 41 | model = Question 42 | fields =["tags"] 43 | 44 | def filter_by_tags(self, queryset, name, value): 45 | tag_names = value.strip().split(",") 46 | tags = Tag.objects.filter(name__in=tag_names) 47 | return queryset.filter(tags__in=tags).distinct() 48 | 49 | 50 | class PollViewSet(viewsets.ModelViewSet): 51 | serializer_class = QuestionSerializer 52 | queryset = Question.objects.all() 53 | lookup_field = 'id' 54 | filter_backends = (DjangoFilterBackend,) 55 | filter_class = PollFilter 56 | authentication_classes = (TokenAuthentication,) 57 | 58 | 59 | @action(detail=True, methods=["GET"]) 60 | def choices(self, request, id=None): 61 | question = self.get_object() 62 | choices = Choice.objects.filter(question=question) 63 | serializer = ChoiceSerializer(choices, many=True) 64 | return Response(serializer.data, status=200) 65 | 66 | @action(detail=True, methods=["POST"]) 67 | def choice(self, request, id=None): 68 | question = self.get_object() 69 | data = request.data 70 | data["question"] = question.id 71 | serializer = ChoiceSerializer(data=data) 72 | if serializer.is_valid(): 73 | serializer.save() 74 | return Response(serializer.data, status=201) 75 | return Response(serializer.erros, status=400) 76 | 77 | 78 | 79 | class PollListView(generics.GenericAPIView, 80 | mixins.ListModelMixin, 81 | mixins.CreateModelMixin, 82 | mixins.RetrieveModelMixin, 83 | mixins.UpdateModelMixin, 84 | mixins.DestroyModelMixin): 85 | serializer_class = QuestionSerializer 86 | queryset = Question.objects.all() 87 | lookup_field = 'id' 88 | authentication_classes = [TokenAuthentication, SessionAuthentication, BasicAuthentication] 89 | permission_classes = [IsAuthenticated, IsAdminUser] 90 | 91 | 92 | 93 | def get(self, request, id=None): 94 | if id: 95 | return self.retrieve(request, id) 96 | else: 97 | return self.list(request) 98 | 99 | def post(self, request): 100 | return self.create(request) 101 | 102 | def perform_create(self, serializer): 103 | serializer.save(created_by=self.request.user) 104 | 105 | def put(self, request, id=None): 106 | return self.update(request, id) 107 | 108 | def perform_update(self, serializer): 109 | print(self.request.user) 110 | serializer.save(created_by=self.request.user) 111 | 112 | def delete(self, request, id=None): 113 | return self.destroy(request, id) 114 | 115 | 116 | 117 | class PollAPIView(APIView): 118 | def get(self, request): 119 | questions = Question.objects.all() 120 | serailizer = QuestionSerializer(questions, many=True) 121 | return Response(serailizer.data, status=200) 122 | 123 | def post(self, request): 124 | data = request.data 125 | serializer = QuestionSerializer(data=data) 126 | if serializer.is_valid(): 127 | serializer.save() 128 | return Response(serializer.data, status=201) 129 | return Response(serializer.erros, status=400) 130 | 131 | class PollDetailView(APIView): 132 | def get_object(self, id): 133 | try: 134 | return Question.objects.get(id=id) 135 | except Question.DoesNotExist as e: 136 | return Response( {"error": "Given question object not found."}, status=404) 137 | 138 | def get(self, request, id=None): 139 | instance = self.get_object(id) 140 | serailizer = QuestionSerializer(instance) 141 | return Response(serailizer.data) 142 | 143 | def put(self, request, id=None): 144 | data = request.data 145 | instance = self.get_object(id) 146 | serializer = QuestionSerializer(instance, data=data) 147 | if serializer.is_valid(): 148 | serializer.save() 149 | return Response(serializer.data, status=200) 150 | return Response(serializer.erros, status=400) 151 | 152 | def delete(self, request, id=None): 153 | instance = self.get_object(id) 154 | instance.delete() 155 | return HttpResponse(status=204) 156 | 157 | 158 | @csrf_exempt 159 | def poll(request): 160 | if request.method == "GET": 161 | questions = Question.objects.all() 162 | serailizer = QuestionSerializer(questions, many=True) 163 | return JsonResponse(serailizer.data, safe=False) 164 | 165 | elif request.method == "POST": 166 | json_parser = JSONParser() 167 | data = json_parser.parse(request) 168 | serializer = QuestionSerializer(data=data) 169 | if serializer.is_valid(): 170 | serializer.save() 171 | return JsonResponse(serializer.data, status=201) 172 | return JsonResponse(serializer.erros, status=400) 173 | 174 | 175 | @csrf_exempt 176 | def poll_details(request, id): 177 | try: 178 | instance = Question.objects.get(id=id) 179 | except Question.DoesNotExist as e: 180 | return JsonResponse( {"error": "Given question object not found."}, status=404) 181 | 182 | if request.method == "GET": 183 | serailizer = QuestionSerializer(instance) 184 | return JsonResponse(serailizer.data) 185 | 186 | elif request.method == "PUT": 187 | json_parser = JSONParser() 188 | data = json_parser.parse(request) 189 | serializer = QuestionSerializer(instance, data=data) 190 | if serializer.is_valid(): 191 | serializer.save() 192 | return JsonResponse(serializer.data, status=200) 193 | return JsonResponse(serializer.erros, status=400) 194 | 195 | elif request.method == "DELETE": 196 | instance.delete() 197 | return HttpResponse(status=204) 198 | 199 | 200 | class PollView(View): 201 | decorators = [login_required, admin_hr_required] 202 | 203 | @method_decorator(decorators) 204 | def get(self, request, id=None): 205 | if id: 206 | question = get_object_or_404(Question, id=id) 207 | poll_form = PollForm(instance=question) 208 | choices = question.choice_set.all() 209 | choice_forms = [ChoiceForm(prefix=str( 210 | choice.id), instance=choice) for choice in choices] 211 | template = 'polls/edit_poll.html' 212 | else: 213 | poll_form = PollForm(instance=Question()) 214 | choice_forms = [ChoiceForm(prefix=str( 215 | x), instance=Choice()) for x in range(3)] 216 | template = 'polls/new_poll.html' 217 | context = {'poll_form': poll_form, 'choice_forms': choice_forms} 218 | return render(request, template, context) 219 | 220 | @method_decorator(decorators) 221 | def post(self, request, id=None): 222 | context = {} 223 | if id: 224 | return self.put(request, id) 225 | poll_form = PollForm(request.POST, instance=Question()) 226 | choice_forms = [ChoiceForm(request.POST, prefix=str( 227 | x), instance=Choice()) for x in range(0, 3)] 228 | if poll_form.is_valid() and all([cf.is_valid() for cf in choice_forms]): 229 | new_poll = poll_form.save(commit=False) 230 | new_poll.created_by = request.user 231 | new_poll.save() 232 | for cf in choice_forms: 233 | new_choice = cf.save(commit=False) 234 | new_choice.question = new_poll 235 | new_choice.save() 236 | return HttpResponseRedirect('/poll/list/') 237 | context = {'poll_form': poll_form, 'choice_forms': choice_forms} 238 | return render(request, 'polls/new_poll.html', context) 239 | 240 | @method_decorator(decorators) 241 | def put(self, request, id=None): 242 | context = {} 243 | question = get_object_or_404(Question, id=id) 244 | poll_form = PollForm(request.POST, instance=question) 245 | choice_forms = [ChoiceForm(request.POST, prefix=str( 246 | choice.id), instance=choice) for choice in question.choice_set.all()] 247 | if poll_form.is_valid() and all([cf.is_valid() for cf in choice_forms]): 248 | new_poll = poll_form.save(commit=False) 249 | new_poll.created_by = request.user 250 | new_poll.save() 251 | for cf in choice_forms: 252 | new_choice = cf.save(commit=False) 253 | new_choice.question = new_poll 254 | new_choice.save() 255 | return redirect('polls_list') 256 | context = {'poll_form': poll_form, 'choice_forms': choice_forms} 257 | return render(request, 'polls/edit_poll.html', context) 258 | 259 | @method_decorator(decorators) 260 | def delete(self, request, id=None): 261 | question = get_object_or_404(Question) 262 | question.delete() 263 | return redirect('polls_list') 264 | 265 | 266 | @login_required(login_url="/login/") 267 | def index(request): 268 | context = {} 269 | questions = Question.objects.all() 270 | context['title'] = 'polls' 271 | context['questions'] = questions 272 | return render(request, 'polls/index.html', context) 273 | 274 | 275 | @login_required(login_url="/login/") 276 | def details(request, id=None): 277 | context = {} 278 | try: 279 | question = Question.objects.get(id=id) 280 | except: 281 | raise Http404 282 | context['question'] = question 283 | return render(request, 'polls/details.html', context) 284 | 285 | 286 | @login_required(login_url="/login/") 287 | def vote_poll(request, id=None): 288 | context = {} 289 | try: 290 | question = Question.objects.get(id=id) 291 | except: 292 | raise Http404 293 | context["question"] = question 294 | 295 | if request.method == "POST": 296 | user_id = 1 297 | print(request.POST) 298 | data = request.POST 299 | ret = Answer.objects.create(user_id=user_id, choice_id=data['choice']) 300 | if ret: 301 | return HttpResponseRedirect(reverse('poll_details', args=[question.id])) 302 | else: 303 | context["error"] = "Your vote is not done successfully" 304 | return render(request, 'polls/poll.html', context) 305 | else: 306 | return render(request, 'polls/poll.html', context) 307 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==2.0 2 | djangorestframework==3.8.2 3 | django-extensions 4 | pillow 5 | django-filter==1.1.0 6 | whoosh 7 | django-haystack 8 | django-elasticsearch-dsl 9 | django-rest-swagger 10 | --------------------------------------------------------------------------------