diff --git a/activities/management/commands/bluefrog_adoptions.py b/activities/management/commands/bluefrog_adoptions.py
index 143fe17efa..6d473dc345 100644
--- a/activities/management/commands/bluefrog_adoptions.py
+++ b/activities/management/commands/bluefrog_adoptions.py
@@ -78,4 +78,3 @@ def handle(self, *args, **options):
wtr.writerow(['Not able to Save Adoption in AP Adoption TABLE', member_code, ap_adopt_practice, e])
-
diff --git a/activities/models.py b/activities/models.py
index e7fd123dd6..f946d2ffcc 100644
--- a/activities/models.py
+++ b/activities/models.py
@@ -33,8 +33,8 @@
class VRPpayment(models.Manager):
-
"Custom manager filters standard query set with given args."
+
def __init__(self, partner_id, block_id, start_period, end_period):
super(VRPpayment, self).__init__()
self.start_yyyy = start_period[-4:]
@@ -95,6 +95,7 @@ class FrontLineWorkerPresent(models.Model):
def __unicode__(self):
return self.worker_type
+
class Screening(CocoModel):
id = models.AutoField(primary_key=True)
old_coco_id = models.BigIntegerField(editable=False, null=True)
@@ -147,6 +148,7 @@ class PersonMeetingAttendance(CocoModel):
def __unicode__(self):
return u'%s' % (self.id)
+
class PersonAdoptPractice(CocoModel):
id = models.AutoField(primary_key=True)
old_coco_id = models.BigIntegerField(editable=False, null=True)
@@ -173,9 +175,11 @@ def __unicode__(self):
class Meta:
unique_together = ("person", "video", "date_of_adoption")
+
post_save.connect(save_log, sender=PersonAdoptPractice)
pre_delete.connect(delete_log, sender=PersonAdoptPractice)
+
class JSLPS_Screening(CocoModel):
id = models.AutoField(primary_key=True)
screenig_code = models.CharField(max_length=100)
@@ -213,7 +217,6 @@ class Meta:
verbose_name_plural = "JSLPS Adoption"
-
class AP_Screening(CocoModel):
screening_code = models.CharField(max_length=100)
screening = models.ForeignKey(Screening, null=True, blank=True)
@@ -240,4 +243,3 @@ class Meta:
verbose_name = "Adoption"
verbose_name_plural = "Adoption"
-
diff --git a/activities/serializers.py b/activities/serializers.py
new file mode 100644
index 0000000000..8e5ad79253
--- /dev/null
+++ b/activities/serializers.py
@@ -0,0 +1,19 @@
+# rest framework imports
+from rest_framework import serializers
+# app imports
+from activities.models import Screening
+from api.utils import DynamicFieldsModelSerializer
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class ScreeningSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Screening model
+ """
+
+ class Meta:
+ model = Screening
+ fields = '__all__'
diff --git a/activities/urls.py b/activities/urls.py
new file mode 100644
index 0000000000..952af84a2c
--- /dev/null
+++ b/activities/urls.py
@@ -0,0 +1,14 @@
+# rest framework imports
+from django.conf.urls import url, include, patterns
+# app imports
+from activities import views
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+urlpatterns=[
+ url(r'^api/screening', views.ScreeningAPIView.as_view(), name='upavan'),
+]
+
diff --git a/activities/views.py b/activities/views.py
new file mode 100644
index 0000000000..34fa3d1d9c
--- /dev/null
+++ b/activities/views.py
@@ -0,0 +1,133 @@
+"""
+This file consists of views for activities.models
+"""
+# default imports
+from django.shortcuts import render
+# rest framework imports
+from rest_framework import viewsets, generics
+from rest_framework import permissions
+from rest_framework.response import Response
+from rest_framework import status
+# app imports
+from videos.models import *
+from activities.models import *
+from activities.serializers import *
+# authentication imports
+from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
+from rest_framework.permissions import IsAuthenticated
+# logging, pagination and permission imports
+import time
+from api.utils import Utils, CustomPagination
+from api.permissions import IsAllowed
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class ScreeningAPIView( generics.ListCreateAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Screening model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes =[IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = ScreeningSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Screening obects:
+ user_created_id - this is id associated with CoCoModel inherited by Screening model,
+ start_day - takes day part value of a search in date range,
+ start_month - takes month part value of a search in date range,
+ start_year - takes year part value of a search in date range,
+ end_day - takes day part value of a search in date range,
+ end_month - takes month part value of a search in date range,
+ end_year - takes year part value of a search in date range,
+ fields - to pass comma separated value to be returned a value for each Screening object, e.g. pass
+ fields value as id,user_created to get only these key-value pairs for each Screening object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ queryset = Screening.objects.get_queryset().order_by('id')
+
+ start_day = request.POST.get('start_day')
+ start_month = request.POST.get('start_month')
+ start_year = request.POST.get('start_year')
+ end_day = request.POST.get('end_day')
+ end_month = request.POST.get('end_month')
+ end_year = request.POST.get('end_year')
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+ uc_id = request.POST.get('user_created_id') # POST param 'user_created', default value is empty string
+
+ if uc_id:
+ queryset = queryset.filter(user_created__exact=uc_id) # filters for numeric values with exact match
+
+ # case1: all values are present
+ if start_day and start_month and start_year and end_day and end_month and end_year:
+ # params type value is string, trimmed spaces,convert to int and then make date by combining values
+ try:
+ start_date = datetime.date(int(start_year.strip()), int(start_month.strip()), int(start_day.strip()))
+ end_date = datetime.date(int(end_year.strip()), int(end_month.strip()), int(end_day.strip()))
+ queryset = queryset.filter(date__range=(start_date, end_date)) # filters values in date range
+ except:
+ utils.logMessage(self, self.post.__name__, "Date error occurred")
+ # case2: only start values are present
+ elif start_day and start_month and start_year and not end_day and not end_month and not end_year:
+ try:
+ start_date = datetime.date(int(start_year.strip()), int(start_month.strip()), int(start_day.strip()))
+ queryset = queryset.filter(date__gte=start_date) # filters values greater than or equal to start date
+ except:
+ utils.logMessage(self, self.post.__name__, "Start Date error occurred")
+ # case3: only end values are present
+ elif not start_day and not start_month and not start_year and end_day and end_month and end_year:
+ try:
+ end_date = datetime.date(int(end_year.strip()), int(end_month.strip()), int(end_day.strip()))
+ queryset = queryset.filter(date__lte=end_date) # filters values less than or equal to end date
+ except:
+ utils.logMessage(self, self.post.__name__, "End Date error occurred")
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = ScreeningSerializer(queryset, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = ScreeningSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
diff --git a/api/__init__.py b/api/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api/management/__init__.py b/api/management/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api/management/commands/__init__.py b/api/management/commands/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api/management/commands/insert_views_for_permissions.py b/api/management/commands/insert_views_for_permissions.py
new file mode 100644
index 0000000000..daa1ee89d6
--- /dev/null
+++ b/api/management/commands/insert_views_for_permissions.py
@@ -0,0 +1,82 @@
+# django imports
+from django.contrib.auth.models import Group
+from django.core.management.base import BaseCommand
+# csv imports
+import unicodecsv as csv
+# app imports
+from api.models import View
+from activities.views import ScreeningAPIView
+from geographies.views import VillageAPIView, BlockAPIView, DistrictAPIView, StateAPIView, CountryAPIView, GeoInfoView
+from people.views import FarmersJsonAPIView, FarmersCsvAPIView, FarmerInfoView
+from programs.views import PartnerAPIView, ProjectAPIView
+from videos.views import VideoAPIView
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+# add a view in this list to insert it in database
+VIEWS_LIST = {
+ ScreeningAPIView.__name__,
+ VillageAPIView.__name__,
+ BlockAPIView.__name__,
+ DistrictAPIView.__name__,
+ StateAPIView.__name__,
+ CountryAPIView.__name__,
+ GeoInfoView.__name__,
+ FarmersJsonAPIView.__name__,
+ FarmersCsvAPIView.__name__,
+ FarmerInfoView.__name__,
+ PartnerAPIView.__name__,
+ ProjectAPIView.__name__,
+ VideoAPIView.__name__,
+}
+
+class CreateViewAndAddGroups():
+ """
+ Class to create views and add groups for View model
+ """
+
+ def createAView(self, view_class):
+ '''
+ This function adds a view to the database
+ '''
+
+ view = View(view_name = view_class)
+ view.save()
+ return
+
+ def addAGroupByName(self, view_class_name, group_name):
+ '''
+ This function adds a group to a view by view name to the database
+ '''
+
+ view = View.objects.get(view_name=view_class_name)
+ gr = Group.objects.get(name=group_name)
+ view.permission_groups.add(gr)
+ view.save()
+ return
+
+
+class Command(BaseCommand):
+ """
+ This class is used to add management commands to insert views in the database
+ """
+
+ def handle(self, *args, **options):
+ prod_path = '/home/ubuntu/code/dg_git/api/management/commands/errors.csv'
+ local_path = '/Users/stuti/Desktop/dg/api/management/commands/errors.csv'
+ error_file = open(local_path, 'wb')
+ wrtr = csv.writer(error_file, delimiter=',', quotechar='"')
+ createAdd = CreateViewAndAddGroups()
+
+ for (i, view_name) in enumerate(VIEWS_LIST):
+ try:
+ createAdd.createAView(view_name)
+ # createAdd.addAGroupByName(view_name, "cocoadmin")
+ except Exception as e:
+ wrtr.writerow([i, "**insertion-error**", view_name, e])
+ print(e, "**insertion-error")
+
+ error_file.close()
diff --git a/api/management/log/logfile b/api/management/log/logfile
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api/migrations/0001_initial.py b/api/migrations/0001_initial.py
new file mode 100644
index 0000000000..c7cbb5e551
--- /dev/null
+++ b/api/migrations/0001_initial.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('auth', '0006_require_contenttypes_0002'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='View',
+ fields=[
+ ('id', models.AutoField(serialize=False, primary_key=True)),
+ ('view_name', models.CharField(max_length=200)),
+ ('permission_groups', models.ManyToManyField(to='auth.Group')),
+ ],
+ ),
+ ]
diff --git a/api/migrations/__init__.py b/api/migrations/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api/models.py b/api/models.py
new file mode 100644
index 0000000000..1e18ceb403
--- /dev/null
+++ b/api/models.py
@@ -0,0 +1,19 @@
+from django.db import models
+from django.contrib.auth.models import Group
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class View(models.Model):
+ '''
+ This is model for View present in the views.py. Groups represent the permitted groups allowed to access it.
+ '''
+ id = models.AutoField(primary_key=True)
+ view_name = models.CharField(max_length=200)
+ permission_groups = models.ManyToManyField(Group)
+
+ def __str__(self):
+ "Returns the view name and groups names mapped together"
+ return '%s, %s'%(self.view_name, self.permission_groups)
diff --git a/api/permissions.py b/api/permissions.py
new file mode 100644
index 0000000000..59afe9f88a
--- /dev/null
+++ b/api/permissions.py
@@ -0,0 +1,31 @@
+# rest framework imports
+from rest_framework import viewsets, generics, status, permissions
+from rest_framework.response import Response
+# app imports
+from api.models import View
+# logging imports
+import logging
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+logger = logging
+logger = logging.getLogger('coco_api')
+
+class IsAllowed(permissions.BasePermission):
+ """
+ View-level permission to allow group-wise access to the view-based-apis.
+ """
+
+ def has_permission(self, request, view):
+ view = View.objects.get(view_name=view.__class__.__name__)
+ user_groups = request.user.groups.all()
+ if view.permission_groups.filter(name__in=list(user_groups)).exists():
+ common_groups = view.permission_groups.filter(name__in=list(user_groups))
+ logger.info("Permission granted for view: %s to user: %s of group: %s"%(view.view_name, request.user, common_groups))
+ return True
+ else:
+ logger.info("Permission denied for view: %s to user: %s of groups: %s"%(view.view_name, request.user, user_groups))
+ return False
diff --git a/api/serializers.py b/api/serializers.py
new file mode 100644
index 0000000000..d865f874e9
--- /dev/null
+++ b/api/serializers.py
@@ -0,0 +1,31 @@
+from rest_framework import serializers
+from django.contrib.auth.models import Group
+from models import View
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class ViewSerializer(serializers.Serializer):
+ """
+ Serializer class for View model
+ """
+
+ permission_group_id = serializers.CharField(source='permission_groups.id', read_only=True)
+ permission_group_name = serializers.CharField(source='permission_groups.name', read_only=True)
+
+ class Meta:
+ model = View
+ fields = '__all__'
+
+
+class GroupSerializer(serializers.Serializer):
+ """
+ Serializer class for Group model
+ """
+
+ class Meta:
+ model = Group
+ fields = '__all__'
+
\ No newline at end of file
diff --git a/api/utils.py b/api/utils.py
new file mode 100644
index 0000000000..c3fd4a289b
--- /dev/null
+++ b/api/utils.py
@@ -0,0 +1,99 @@
+# django imports
+from django.contrib.auth.models import User
+from django.utils import importlib
+# python imports
+import time
+import logging
+from collections import OrderedDict
+# rest framework imports
+from rest_framework import pagination, serializers
+from rest_framework.response import Response
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class CustomPagination(pagination.PageNumberPagination):
+ """
+ Pagination class to paginate queryset
+ """
+
+ page_size = 50
+ page_size_query_param = 'page_size'
+ page_query_param = 'page'
+ max_page_size = 100
+
+ # overriding of method to get customised paginated response
+ def get_paginated_response(self, data):
+ return Response(OrderedDict([
+ ('count', self.page.paginator.count),
+ ('next', self.get_next_link()),
+ ('previous', self.get_previous_link()),
+ ('results', data)
+ ]))
+
+
+class DynamicFieldsModelSerializer(serializers.ModelSerializer):
+ """
+ A ModelSerializer that takes an additional `fields` argument that
+ controls which fields should be displayed.
+ """
+
+ def __init__(self, *args, **kwargs):
+ # Don't pass the 'fields' arg up to the superclass
+ fields = kwargs.pop('fields', None)
+
+ # Instantiate the superclass normally
+ super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
+
+ if fields is not None:
+ # Drop any fields that are not specified in the `fields` argument.
+ allowed = set(fields)
+ existing = set(self.fields)
+ for field_name in existing - allowed:
+ self.fields.pop(field_name)
+
+
+class Utils:
+ """
+ Utility class for api
+ """
+
+ def limitQueryset(self, queryset, start_limit, end_limit):
+ """
+ limits the total response count
+ """
+
+ if start_limit and end_limit: # case1: both are present
+ queryset = queryset[int(start_limit)-1:int(end_limit)]
+ elif start_limit: # case2: only start_limit is present
+ queryset = queryset[int(start_limit)-1:]
+ elif end_limit: # case3: only end_limit is present
+ queryset = queryset[:int(end_limit)]
+ return queryset
+
+ def logRequest(self, request, class_instance, view_fun, processing_time, status_code ):
+ """
+ Logs the request, username and other details
+ """
+
+ logger = logging.getLogger('coco_api')
+ user_obj = User.objects.get(username=request.user)
+ ip_addr = request.META['REMOTE_ADDR']
+ method = request.method
+ user_id = user_obj.id
+ class_name = class_instance.__class__.__name__
+ module_name = class_instance.__module__
+ logger.info("Accessed: %s.%s.%s, user_id: %s, username: %s, ip_address: %s, method: %s, processing_time: %s seconds, status_code: %s" % ( module_name, class_name, view_fun, user_id, user_obj, ip_addr, method, processing_time, status_code))
+
+ def logMessage(self, class_instance, view_fun, message):
+ """
+ Logs the message associated with class and its method
+ """
+
+ logger = logging.getLogger('coco_api')
+ class_name = class_instance.__class__.__name__
+ module_name = class_instance.__module__
+ logger.info("Log for %s.%s.%s: %s " % ( module_name, class_name, view_fun, message))
+
\ No newline at end of file
diff --git a/dashboard/admin.py b/dashboard/admin.py
index 8c1fe56a22..9c9da2d483 100644
--- a/dashboard/admin.py
+++ b/dashboard/admin.py
@@ -27,6 +27,7 @@ class Meta:
model = PersonMeetingAttendance
exclude = ()
+
class FarmerAttendanceInline(admin.TabularInline):
model = PersonMeetingAttendance
raw_id_fields = ("person",)
@@ -58,6 +59,7 @@ class Meta:
model = Screening
exclude = ()
+
class ScreeningAdmin(admin.ModelAdmin):
filter_horizontal = ('videoes_screened',)
list_display = ('id', 'date', 'screening_location', 'observation_status', 'screening_grade', 'observer')
@@ -106,11 +108,11 @@ class DirectBeneficiariesAdmin(admin.ModelAdmin):
list_display = ['id', 'direct_beneficiaries_category']
search_fields = ['direct_beneficiaries_category']
+
class PartnerAdmin(admin.ModelAdmin):
list_display = ['id', 'partner_name']
-
class VideoAdmin(admin.ModelAdmin):
inlines = [NonNegotiablesInline]
fieldsets = [
@@ -134,6 +136,7 @@ class Media:
settings.STATIC_URL + "js/qa_video.js",
)
+
class AnimatorAssignedVillages(admin.StackedInline):
model = AnimatorAssignedVillage
@@ -149,11 +152,13 @@ class PersonGroupInline(admin.TabularInline):
model = PersonGroup
extra = 5
+
class AnimatorInline(admin.TabularInline):
model = Animator
extra = 5
exclude = ('assigned_villages',)
+
class VillageAdmin(admin.ModelAdmin):
list_display = ('id', 'village_name', 'block', 'active')
search_fields = ['village_name', 'block__block_name', 'block__district__state__state_name']
@@ -184,7 +189,6 @@ class Media:
#}
-
class PersonGroupAdmin(admin.ModelAdmin):
inlines = [PersonInline]
list_display = ('group_name','village')
@@ -201,6 +205,7 @@ class PersonAdoptPracticeInline(admin.StackedInline):
model = PersonAdoptPractice
extra = 3
+
class PersonAdoptPracticeAdmin(admin.ModelAdmin):
formfield_overrides = {
models.CharField: {'widget': forms.CheckboxSelectMultiple(choices=NONNEGOTIABLE_OPTION)},
@@ -229,55 +234,69 @@ class PersonAdmin(admin.ModelAdmin):
search_fields = ['person_name','village__village_name','group__group_name']
raw_id_fields = ('village','group')
+
class BlockAdmin(admin.ModelAdmin):
list_display = ('id', 'block_name', 'district', 'active')
search_fields = ['block_name', 'district__district_name', 'district__state__state_name']
+
class DistrictAdmin(admin.ModelAdmin):
list_display = ('id', 'district_name', 'state', 'active')
search_fields = ['district_name', 'state__state_name']
+
class StateAdmin(admin.ModelAdmin):
list_display = ('id', 'state_name','active')
search_fields = ['state_name', 'country__country_name']
+
class SubCategoryAdmin(admin.ModelAdmin):
list_display = ('subcategory_name', 'category')
search_fields = ['subcategory_name', 'category__category_name']
+
class VideoPracticeAdmin(admin.ModelAdmin):
list_display = ('videopractice_name', 'subcategory')
search_fields = ['videopractice_name', 'subcategory__subcategory_name']
+
class PracticesAdmin(admin.ModelAdmin):
list_display = ('id', 'practice_name', 'practice_sector', 'practice_subject', 'practice_subsector', 'practice_topic', 'practice_subtopic')
search_fields = ['id', 'practice_name', 'practice_sector__name', 'practice_subject__name', 'practice_subsector__name', 'practice_topic__name', 'practice_subtopic__name']
+
class PracticeSectorAdmin(admin.ModelAdmin):
search_fields = ['name']
+
class PracticeSubSectorAdmin(admin.ModelAdmin):
search_fields = ['name']
+
class PracticeTopicAdmin(admin.ModelAdmin):
search_fields = ['name']
+
class PracticeSubtopicAdmin(admin.ModelAdmin):
search_fields = ['name']
+
class PracticeSubjectAdmin(admin.ModelAdmin):
search_fields = ['name']
+
class CocoUserAdmin(admin.ModelAdmin):
form = CocoUserForm
list_display = ('id', 'user', 'partner','get_villages')
search_fields = ['user__username']
+
class QACocoUserAdmin(admin.ModelAdmin):
form = QACocoUserForm
list_display = ('user','partner','get_blocks')
search_fields = ['user__username']
+
class ProjectAdmin(admin.ModelAdmin):
filter_horizontal = ('associate_partner',)
list_display = ('id','project_name')
@@ -299,8 +318,6 @@ def __init__(self, *args, **kwargs):
self.fields['video'].queryset = Video.objects.filter(partner_id__in=(50,72),village__block__district__state_id= 6).exclude(id__in=mapped_videos)
-
-
class BluefrogSubcategoryAdmin(admin.ModelAdmin):
list_display = ['crop_id', 'crop_name', 'crop_name_telgu']
search_fields = ['crop_id', 'crop_name', 'crop_name_telgu']
@@ -331,10 +348,6 @@ def save_related(self, request, form, formsets, change):
form.instance.video.tags.add(*current_instance_tag)
-
-
-
-
class AP_DistrictAdmin(admin.ModelAdmin):
list_display = ['id', 'district_code', 'district_name', 'user_created',
'time_created', '_district']
@@ -426,12 +439,12 @@ def _animator(self, obj):
_animator.allow_tags = True
_animator.short_description = "COCO-DB-Animator-ID"
+
class AP_AnimatorAssignedVillageAdmin(admin.ModelAdmin):
list_display = ['id', 'animator', 'village', 'user_created', 'time_created']
search_fields = ['animator']
-
class JSLPS_AnimatorAdmin(admin.ModelAdmin):
list_display = ('id','animator_code', 'user_created', 'time_created',
'_animator', 'activity')
@@ -525,6 +538,7 @@ def _village(self, obj):
def has_add_permission(self, request):
return False
+
class JSLPS_VideoAdmin(admin.ModelAdmin):
list_display = ['id', 'vc', 'title', 'user_created', 'time_created',
'_video', 'activity']
@@ -564,7 +578,6 @@ def has_add_permission(self, request):
return False
-
class JSLPS_ScreeningAdmin(admin.ModelAdmin):
list_display = ['id', 'screenig_code', 'activity',
'screening', '_village', '_dg_screening_id',
@@ -597,4 +610,7 @@ def has_add_permission(self, request):
return False
+class ViewAdmin(admin.ModelAdmin):
+ list_display = ('id', 'view_name', 'permission_groups')
+ search_fields = ['id', 'view_name']
diff --git a/dg/base_settings.py b/dg/base_settings.py
index 9833fc29df..d18c57e479 100644
--- a/dg/base_settings.py
+++ b/dg/base_settings.py
@@ -149,6 +149,7 @@
'people',
'videos',
'activities',
+ 'api',
#'debug_toolbar',
'output',
'django.contrib.humanize',
@@ -185,8 +186,12 @@
'mrppayment',
'smart_selects',
'loop_ivr',
- 'dataexport'
+ 'dataexport',
+ 'rest_framework',
# 3rd Party
+ 'django_extensions',
+ #drf TokenAuthentication
+ 'rest_framework.authtoken',
)
# Store these package names here as they may change in the future since
@@ -241,6 +246,12 @@
'class':'logging.StreamHandler',
'formatter': 'standard'
},
+ 'api_access_log': {
+ 'level':'DEBUG',
+ 'class':'logging.handlers.RotatingFileHandler',
+ 'filename': os.path.join(PROJECT_PATH, '../api/management/log/logfile'),
+ 'formatter': 'standard',
+ },
},
'loggers': {
@@ -259,6 +270,10 @@
'geographies': {
'handlers': ['ap_migration_log'],
'level' : 'INFO',
+ },
+ 'coco_api':{
+ 'handlers': ['api_access_log'],
+ 'level': 'INFO',
}
}
}
@@ -270,3 +285,13 @@
VIDEOS_PAGE = ('%s%s')%(WEBSITE_DOMAIN, 'videos/')
LOOP_APP_PAGE = ('%s')%('https://play.google.com/store/apps/details?id=loop.org.digitalgreen.loop')
TRAINING_APP_PAGE = ('%s')%('https://play.google.com/store/apps/details?id=org.digitalgreen.trainingapp')
+
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
+ 'rest_framework.authentication.BasicAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ],
+ 'DEFAULT_PERMISSION_CLASSES': [
+ 'rest_framework.permissions.IsAuthenticated',
+ ],
+}
\ No newline at end of file
diff --git a/dg/coco_admin.py b/dg/coco_admin.py
index 742253dadc..b81e199679 100644
--- a/dg/coco_admin.py
+++ b/dg/coco_admin.py
@@ -1,4 +1,5 @@
from django.contrib.admin.sites import AdminSite
+# admin
from django.contrib.auth.admin import Group, GroupAdmin, User, UserAdmin
from dashboard.admin import AnimatorAdmin
from dashboard.admin import AnimatorAssignedVillageAdmin
@@ -24,6 +25,9 @@
from dashboard.admin import ProjectAdmin
from dashboard.admin import TagAdmin
from dashboard.admin import PartnerAdmin
+from dashboard.admin import ViewAdmin
+# models
+from api.models import View
from activities.models import PersonAdoptPractice, Screening
from coco.models import CocoUser
from geographies.models import Block, Country, District, State, Village
@@ -55,10 +59,10 @@ def has_permission(self, request):
coco_admin.index_template = 'social_website/index.html'
coco_admin.login_template = 'social_website/login.html'
coco_admin.logout_template = 'social_website/home.html'
-
+# default admin
coco_admin.register(User, UserAdmin)
coco_admin.register(Group, GroupAdmin)
-
+# custom admin
coco_admin.register(AnimatorAssignedVillage, AnimatorAssignedVillageAdmin)
coco_admin.register(Video, VideoAdmin)
coco_admin.register(Country)
@@ -86,3 +90,4 @@ def has_permission(self, request):
coco_admin.register(PracticeSubtopic, PracticeSubtopicAdmin)
coco_admin.register(PracticeSubject, PracticeSubjectAdmin)
coco_admin.register(CocoUser, CocoUserAdmin)
+coco_admin.register(View, ViewAdmin)
diff --git a/dg/media/Output/JS/adoption_module.js b/dg/media/Output/JS/adoption_module.js
index 356c533733..1d0d2e0d7a 100644
--- a/dg/media/Output/JS/adoption_module.js
+++ b/dg/media/Output/JS/adoption_module.js
@@ -1,5 +1,9 @@
-google.load("visualization", "1", { packages: ["controls"] });
-google.setOnLoadCallback(drawCharts);
+// google.load("visualization", "1", { packages: ["controls"] });
+// google.setOnLoadCallback(drawCharts);
+google.charts.load("current", "1", {
+ packages: ["controls"]
+});
+google.charts.setOnLoadCallback(drawCharts);
function drawCharts() {
$.getJSON('/coco/analytics/adoption_geog_pie_data/' + search_params, function(json) { geog_pie(json); });
diff --git a/dg/media/Output/JS/screening_module.js b/dg/media/Output/JS/screening_module.js
index db031ec122..558f121417 100644
--- a/dg/media/Output/JS/screening_module.js
+++ b/dg/media/Output/JS/screening_module.js
@@ -1,5 +1,10 @@
-google.load("visualization", "1", { packages: ["controls"] });
-google.setOnLoadCallback(drawCharts);
+// google.load("visualization", "1", { packages: ["controls"] });
+// google.setOnLoadCallback(drawCharts);
+
+google.charts.load("current", "1", {
+ packages: ["controls"]
+});
+google.charts.setOnLoadCallback(drawCharts);
var geog_pie_chart;
var geog_pie_chart_data;
diff --git a/dg/media/Output/JS/video_module.js b/dg/media/Output/JS/video_module.js
index d51cc14cbb..f8a30c462e 100644
--- a/dg/media/Output/JS/video_module.js
+++ b/dg/media/Output/JS/video_module.js
@@ -1,5 +1,10 @@
-google.load("visualization", "1", { packages: ["controls"] });
-google.setOnLoadCallback(drawCharts);
+// google.load("visualization", "1", { packages: ["controls"] });
+// google.setOnLoadCallback(drawCharts);
+
+google.charts.load("current", "1", {
+ packages: ["controls"]
+});
+google.charts.setOnLoadCallback(drawCharts);
var geog_pie_chart;
var geog_pie_chart_data;
@@ -49,8 +54,6 @@ function remove_loader(div_id) {
function monthwise_column(json) {
if (json.length > 1) {
-
-
var monthwise_column_chart_data = google.visualization.arrayToDataTable(json, false);
var options = jQuery.extend(true, {}, column_options);
options['chartArea'] = { left: 70, top: 20, width: "80%", height: "80%" };
diff --git a/dg/templates/output/adoption_module.html b/dg/templates/output/adoption_module.html
index ab90d83c75..fca4e482c9 100644
--- a/dg/templates/output/adoption_module.html
+++ b/dg/templates/output/adoption_module.html
@@ -352,7 +352,8 @@
-
+
+
{% endblock contentbody %}
\ No newline at end of file
diff --git a/dg/templates/output/overview_module.html b/dg/templates/output/overview_module.html
index 948e057d46..9d44040937 100644
--- a/dg/templates/output/overview_module.html
+++ b/dg/templates/output/overview_module.html
@@ -251,7 +251,8 @@
-
+
+
{% endblock contentbody %}
diff --git a/dg/templates/output/screening_module.html b/dg/templates/output/screening_module.html
index ea51a83fad..6abc7e1952 100644
--- a/dg/templates/output/screening_module.html
+++ b/dg/templates/output/screening_module.html
@@ -254,7 +254,8 @@
-
+
+
{% endblock contentbody %}
\ No newline at end of file
diff --git a/dg/templates/output/video_module.html b/dg/templates/output/video_module.html
index 23bbe1865b..70765154b7 100644
--- a/dg/templates/output/video_module.html
+++ b/dg/templates/output/video_module.html
@@ -181,7 +181,8 @@
-
+
+
{% endblock contentbody %}
\ No newline at end of file
diff --git a/dg/urls.py b/dg/urls.py
index 30a9053e3d..9548c49451 100644
--- a/dg/urls.py
+++ b/dg/urls.py
@@ -20,6 +20,10 @@
import ivr.urls
import training.urls
import videos.urls
+import people.urls
+import activities.urls
+import geographies.urls
+import programs.urls
from django.contrib import admin
admin.autodiscover()
@@ -65,6 +69,13 @@
# loop_ivr_admin.login_template = 'social_website/login.html'
# loop_ivr_admin.logout_template = 'social_website/home.html'
+from django.conf.urls import url, include
+from django.contrib.auth.models import User
+from rest_framework import routers, serializers, viewsets
+
+#drf token authentication
+from rest_framework.authtoken import views
+
urlpatterns = patterns('',
(r'^', include(social_website.urls)),
#(r'^', include(website_archive_urls)),
@@ -81,6 +92,17 @@
(r'^qacoco/', include(qacoco.urls)),
(r'^dimagi/', include(dimagi.urls)),
(r'^videos/', include(videos.urls)),
+
+
+ # coco api changes start here
+ (r'^api-token-auth', views.obtain_auth_token),
+ (r'^farmer/', include('people.urls', namespace='farmer')),
+ (r'^geo/', include('geographies.urls', namespace='geographies')),
+ (r'^activities/', include('activities.urls', namespace='activities')),
+ (r'^programs/', include('programs.urls', namespace='programs')),
+ # coco api changes end here
+
+
# ivrsadmin/logout/ should be above admin/ URL
url(r'^ivrsadmin/logout/?$', 'django.contrib.auth.views.logout', {'next_page': '/ivrsadmin'}),
(r'^ivrsadmin/', include(ivr_admin.urls)),
@@ -140,8 +162,10 @@
#(r'^agri/', include(videokheti.urls)),
#AJAX for Feedback
#url(r'^feedbacksubmit_json$', 'dg.feedback_view.ajax'),
+
+
+
+
+
)
-# Static files serving locally
-if settings.DEBUG:
- urlpatterns += staticfiles_urlpatterns()
diff --git a/geographies/apps.py b/geographies/apps.py
new file mode 100644
index 0000000000..dd836c6747
--- /dev/null
+++ b/geographies/apps.py
@@ -0,0 +1,5 @@
+from __future__ import unicode_literals
+from django.apps import AppConfig
+
+class GeoConfig(AppConfig):
+ name = 'geo'
\ No newline at end of file
diff --git a/geographies/models.py b/geographies/models.py
index 6c1a3d456c..c521086db7 100644
--- a/geographies/models.py
+++ b/geographies/models.py
@@ -1,7 +1,6 @@
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models.signals import pre_delete, post_save
-
from coco.base_models import CocoModel, ACTIVITY_CHOICES
from coco.data_log import delete_log, save_log
from farmerbook.managers import VillageFarmerbookManager
@@ -73,6 +72,7 @@ def clean(self):
post_save.connect(enter_to_log, sender=District)
pre_delete.connect(enter_to_log, sender=District)
+
class Block(CocoModel):
id = models.AutoField(primary_key=True)
old_coco_id = models.BigIntegerField(editable=False, null=True)
@@ -111,6 +111,7 @@ def get_partner(self):
def __unicode__(self):
return self.village_name
+
post_save.connect(save_log, sender = Village)
pre_delete.connect(delete_log, sender = Village)
diff --git a/geographies/serializers.py b/geographies/serializers.py
new file mode 100644
index 0000000000..59f811318e
--- /dev/null
+++ b/geographies/serializers.py
@@ -0,0 +1,83 @@
+from rest_framework import serializers
+from geographies.models import Country, State, District, Block, Village
+from api.utils import DynamicFieldsModelSerializer
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class CountrySerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Country model
+ """
+
+ class Meta:
+ model = Country
+ fields = '__all__'
+
+
+class StateSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for State model
+ """
+
+ country_id = serializers.IntegerField(source='country.id', read_only=True)
+ country_name = serializers.CharField(source='country.country_name', read_only=True)
+
+ class Meta:
+ model = State
+ fields = '__all__'
+
+
+class DistrictSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for District model
+ """
+
+ country_id = serializers.IntegerField(source='state.country.id', read_only=True)
+ country_name = serializers.CharField(source='state.country.country_name', read_only=True)
+ state_id = serializers.IntegerField(source='state.id', read_only=True)
+ state_name = serializers.CharField(source='state.state_name', read_only=True)
+
+ class Meta:
+ model = District
+ fields = '__all__'
+
+
+class BlockSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Block model
+ """
+
+ country_id = serializers.IntegerField(source='district.state.country.id', read_only=True)
+ country_name = serializers.CharField(source='district.state.country.country_name', read_only=True)
+ state_id = serializers.IntegerField(source='district.state.id', read_only=True)
+ state_name = serializers.CharField(source='district.state.state_name', read_only=True)
+ district_id = serializers.IntegerField(source='district.id', read_only=True)
+ district_name = serializers.CharField(source='district.district_name', read_only=True)
+
+ class Meta:
+ model = Block
+ fields = '__all__'
+
+
+class VillageSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Village model
+ """
+
+ country_id = serializers.IntegerField(source='block.district.state.country.id', read_only=True)
+ country_name = serializers.CharField(source='block.district.state.country.country_name', read_only=True)
+ state_id = serializers.IntegerField(source='block.district.state.id', read_only=True)
+ state_name = serializers.CharField(source='block.district.state.state_name', read_only=True)
+ district_id = serializers.IntegerField(source='block.district.id', read_only=True)
+ district_name = serializers.CharField(source='block.district.district_name', read_only=True)
+ block_id = serializers.IntegerField(source='block.id', read_only=True)
+ block_name = serializers.CharField(source='block.block_name', read_only=True)
+
+ class Meta:
+ model = Village
+ fields = '__all__'
+
+
\ No newline at end of file
diff --git a/geographies/urls.py b/geographies/urls.py
new file mode 100644
index 0000000000..682c25cebf
--- /dev/null
+++ b/geographies/urls.py
@@ -0,0 +1,16 @@
+from django.conf.urls import url, include
+from geographies import views
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+urlpatterns=[
+ url(r'^api/info', views.GeoInfoView.as_view(), name='info'),
+ url(r'^api/village', views.VillageAPIView.as_view(), name='village'),
+ url(r'^api/block', views.BlockAPIView.as_view(), name='block'),
+ url(r'^api/district', views.DistrictAPIView.as_view(), name='district'),
+ url(r'^api/state', views.StateAPIView.as_view(), name='state'),
+ url(r'^api/country', views.CountryAPIView.as_view(), name='country'),
+]
diff --git a/geographies/views.py b/geographies/views.py
new file mode 100644
index 0000000000..d8169bf64e
--- /dev/null
+++ b/geographies/views.py
@@ -0,0 +1,410 @@
+# django imports
+from django.shortcuts import render
+# rest framework imports
+from rest_framework import viewsets, generics
+from rest_framework import permissions
+from rest_framework.response import Response
+from rest_framework import status
+# rest framework TokenAuthentication imports
+from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
+from rest_framework.permissions import IsAuthenticated
+# pagination and permissions
+import time
+from api.utils import Utils, CustomPagination
+from api.permissions import IsAllowed
+# app imports
+from geographies.serializers import *
+from geographies.models import *
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class GeoInfoView(generics.ListAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to provide default message in JSON format.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes =[IsAuthenticated and IsAllowed]
+
+ # GET request
+ def get(self, request, *args, **kwargs):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request):
+ # dictionary results as JSON format message
+ return Response({"message":"Welcome to COCO APIs", "base_url":"/geo/api/",
+ "url_list":["/geo/api/village",
+ "/geo/api/block",
+ "/geo/api/district",
+ "/geo/api/state",
+ "/geo/api/country"]})
+
+
+class VillageAPIView(generics.ListAPIView):
+ '''
+ coco_api class-based view to query Village model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes =[IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = VillageSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Village obects:
+ 1.) id - to find village by id
+ 2.) fields - to pass comma separated value to be returned a value for each Village object, e.g. pass
+ fields value as id,village_name to get only these key-value pairs for each Village object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ village_id = self.request.POST.get('id', 0) # POST param 'id'
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+
+ if village_id:
+ queryset = Village.objects.filter(id__exact=village_id) # to search by id
+ else:
+ queryset = Village.objects.get_queryset().order_by('id') # basic query to be filtered later in this method
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = VillageSerializer(queryset, fields=fields_values ,many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = VillageSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
+
+class BlockAPIView(generics.ListAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Block model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes =[IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = BlockSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Block obects:
+ 1.) id - to find block by id
+ 2.) fields - to pass comma separated value to be returned a value for each Block object, e.g. pass
+ fields value as id,block_name to get only these key-value pairs for each Block object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ block_id = self.request.POST.get('id', 0) # POST param 'id'
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+
+ if block_id:
+ queryset = Block.objects.filter(id__exact=block_id) # to search by id
+ else:
+ queryset = Block.objects.get_queryset().order_by('id') # basic query to be filtered later in this method
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = BlockSerializer(queryset, fields=fields_values ,many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = BlockSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
+
+class DistrictAPIView(generics.ListAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query District model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes =[IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = DistrictSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on District obects:
+ 1.) id - to find district by id
+ 2.) fields - to pass comma separated value to be returned a value for each District object, e.g. pass
+ fields value as id,district_name to get only these key-value pairs for each District object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ district_id = self.request.POST.get('id', 0) # POST param 'id'
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+
+ if district_id:
+ queryset = District.objects.filter(id__exact=district_id) # to search by id
+ else:
+ queryset = District.objects.get_queryset().order_by('id') # basic query to be filtered later in this method
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = DistrictSerializer(queryset, fields=fields_values ,many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = DistrictSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
+
+class StateAPIView(generics.ListAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query State model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes = [IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = StateSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on State obects:
+ 1.) id - to find state by id
+ 2.) fields - to pass comma separated value to be returned a value for each State object, e.g. pass
+ fields value as id,state_name to get only these key-value pairs for each State object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ state_id = self.request.POST.get('id', 0) # POST param 'id'
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+
+ if state_id:
+ queryset = State.objects.filter(id__exact=state_id) # to search by id
+ else:
+ queryset = State.objects.get_queryset().order_by('id') # basic query to be filtered later in this method
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = StateSerializer(queryset, fields=fields_values ,many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = StateSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
+
+class CountryAPIView(generics.ListAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Country model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes = [IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = CountrySerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Country obects:
+ 1.) id - to find country by id
+ 2.) fields - to pass comma separated value to be returned a value for each Country object, e.g. pass
+ fields value as id,country_name to get only these key-value pairs for each Country object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ country_id = self.request.POST.get('id', 0) # POST param 'id'
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+
+ if country_id:
+ queryset = Country.objects.filter(id__exact=country_id) # to search by id
+ else:
+ queryset = Country.objects.get_queryset().order_by('id') # basic query to be filtered later in this method
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = CountrySerializer(queryset, fields=fields_values ,many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = CountrySerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
diff --git a/loop/management/commands/Outliers.py b/loop/management/commands/Outliers.py
index 4de29c1a61..07184b5e0e 100644
--- a/loop/management/commands/Outliers.py
+++ b/loop/management/commands/Outliers.py
@@ -100,4 +100,6 @@ def file_creator_per_case(self, from_to_date, case=''):
create_xlsx(workbook, aggregator_wise_outliers, table_properties, table_position_to_start,
worksheet_name)
- return file_to_send
\ No newline at end of file
+ return file_to_send
+
+
\ No newline at end of file
diff --git a/output/database/utility.py b/output/database/utility.py
index 5d2f2a3423..004015549f 100644
--- a/output/database/utility.py
+++ b/output/database/utility.py
@@ -10,6 +10,8 @@ def construct_query(var, context_dict):
#This abstracts away sql part to return everything by cursor.fetchall()
#which is a tuple of tuples containing row-values
def run_query_raw(query_string, *query_args):
+ # change query to lower case
+ query_string = query_string.lower()
if(not query_string):
return ()
cursor = connection.cursor()
@@ -18,6 +20,8 @@ def run_query_raw(query_string, *query_args):
#This generates a list of dictionaries of key=column_header_name, value = row_value
def run_query(query_string, *query_args):
+ # change query to lower case
+ query_string = query_string.lower()
if(not query_string):
return []
return_list = []
@@ -33,6 +37,8 @@ def run_query(query_string, *query_args):
#{ dict_key : (tuple of remaing columns), ...}
#dict_key should be the first column in returned value.
def run_query_dict(query_string, dict_key, *query_args):
+ # change query to lower case
+ query_string = query_string.lower()
if(not query_string):
return {}
return_list = {}
@@ -51,6 +57,8 @@ def run_query_dict(query_string, dict_key, *query_args):
#{ dict_key : [list of remaing columns], ...}
#dict_key should be the first column in returned value.
def run_query_dict_list(query_string, dict_key, *query_args):
+ # change query to lower case
+ query_string = query_string.lower()
if(not query_string):
return {}
return_list = {}
diff --git a/output/views/common.py b/output/views/common.py
index 6c39d01c40..fde0ec81bf 100644
--- a/output/views/common.py
+++ b/output/views/common.py
@@ -483,22 +483,22 @@ def __cmp__(self,date1):
def month_bar_data(sqlFunc, setting_from_date, setting_to_date, **args):
rs = run_query(sqlFunc(**args));
if rs:
- min = int(rs[0]['YEAR'])
- max = int(rs[-1]['YEAR'])+1
+ min = int(rs[0]['year'])
+ max = int(rs[-1]['year'])+1
dic = {}
for y in range(min,max):
dic[y] = {}
for item in rs:
- if int(item['YEAR'])>y:
+ if int(item['year'])>y:
break
- if int(item['YEAR'])==y:
- dic[y][int(item['MONTH'])] = item['count']
+ if int(item['year'])==y:
+ dic[y][int(item['month'])] = item['count']
else:
return HttpResponse(json.dumps([['Name','Value']]));
if(not(setting_from_date and setting_to_date)):
- setting_from_date = str(rs[0]['YEAR'])+'-'+str(rs[0]['MONTH'])+'-01'
+ setting_from_date = str(rs[0]['year'])+'-'+str(rs[0]['month'])+'-01'
setting_to_date = (datetime.datetime.utcnow() - datetime.timedelta(1)).strftime('%Y-%m-%d')
setting_from_date = MyDate(* [int(x) for x in reversed(setting_from_date.split('-')[:2])])
setting_to_date = MyDate(* [int(x) for x in reversed(setting_to_date.split('-')[:2])])
diff --git a/people/models.py b/people/models.py
index 5288a3d79e..0b27cf4372 100644
--- a/people/models.py
+++ b/people/models.py
@@ -1,6 +1,5 @@
from django.db import models
from django.db.models.signals import pre_delete, post_save
-
from coco.data_log import delete_log, save_log
from coco.base_models import CocoModel, DAY_CHOICES, GENDER_CHOICES, TYPE_OF_ROLE
from farmerbook.managers import FarmerbookManager
@@ -9,6 +8,7 @@
from programs.models import Partner
from training.log.training_log import enter_to_log
+
class Animator(CocoModel):
id = models.AutoField(primary_key = True)
old_coco_id = models.BigIntegerField(editable=False, null=True)
@@ -48,6 +48,7 @@ class AnimatorAssignedVillage(CocoModel):
village = models.ForeignKey(Village)
start_date = models.DateField(null=True, blank=True)
+
class PersonGroup(CocoModel):
id = models.AutoField(primary_key=True)
old_coco_id = models.BigIntegerField(editable=False, null=True)
@@ -64,6 +65,7 @@ def __unicode__(self):
post_save.connect(save_log, sender=PersonGroup)
pre_delete.connect(delete_log, sender=PersonGroup)
+
class Person(CocoModel):
id = models.AutoField(primary_key=True)
old_coco_id = models.BigIntegerField(editable=False, null=True)
@@ -94,6 +96,7 @@ def __unicode__(self):
post_save.connect(save_log, sender=Person)
pre_delete.connect(delete_log, sender=Person)
+
class JSLPS_Animator(CocoModel):
id = models.AutoField(primary_key=True)
animator_code = models.CharField(max_length=100)
@@ -108,6 +111,7 @@ class Meta:
def __unicode__(self):
return self.animator_code
+
class JSLPS_AnimatorAssignedVillage(CocoModel):
id = models.AutoField(primary_key=True)
animator = models.ForeignKey(JSLPS_Animator)
@@ -118,6 +122,7 @@ class Meta:
verbose_name = "JSLPS AnimatorAssignedVillage"
verbose_name_plural = "JSLPS AnimatorAssignedVillage"
+
class JSLPS_Persongroup(CocoModel):
id = models.AutoField(primary_key=True)
group_code = models.CharField(max_length=100)
@@ -128,6 +133,7 @@ class Meta:
verbose_name = "JSLPS Persongroup"
verbose_name_plural = "JSLPS Persongroup"
+
class JSLPS_Person(CocoModel):
id = models.AutoField(primary_key=True)
person_code = models.CharField(max_length=100)
@@ -153,6 +159,7 @@ class Meta:
def __unicode__(self):
return self.animator_code
+
class AP_AnimatorAssignedVillage(CocoModel):
animator = models.ForeignKey(AP_Animator)
village = models.ForeignKey(AP_Village)
@@ -162,7 +169,6 @@ class Meta:
verbose_name_plural = "AP AnimatorAssignedVillage"
-
class AP_Person(CocoModel):
person_code = models.CharField(max_length=100)
person = models.ForeignKey(Person, null=True, blank=True)
@@ -172,4 +178,3 @@ class Meta:
verbose_name = "AP Person"
verbose_name_plural = "AP Person"
-
diff --git a/people/serializers.py b/people/serializers.py
new file mode 100644
index 0000000000..69f31f1d1b
--- /dev/null
+++ b/people/serializers.py
@@ -0,0 +1,35 @@
+from people.models import Person
+from rest_framework import serializers
+from geographies.models import Village, Block, District, State, Country
+from geographies.serializers import VillageSerializer
+from api.utils import DynamicFieldsModelSerializer
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class FarmerSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Village model
+ """
+
+ village_id = serializers.CharField(source='village.id', read_only=True)
+ village = serializers.CharField(source='village.village_name', read_only=True)
+
+ block_id = serializers.IntegerField(source='village.block.id', read_only=True)
+ block_name = serializers.CharField(source='village.block.block_name', read_only=True)
+
+ district_id = serializers.IntegerField(source='village.block.district.id', read_only=True)
+ district_name = serializers.CharField(source='village.block.district.district_name', read_only=True)
+
+ state_id = serializers.IntegerField(source='village.block.district.state.id', read_only=True)
+ state_name = serializers.CharField(source='village.block.district.state.state_name', read_only=True)
+
+ country_id = serializers.IntegerField(source='village.block.district.state.country.id', read_only=True)
+ country_name = serializers.CharField(source='village.block.district.state.country.country_name', read_only=True)
+
+ class Meta:
+ model = Person
+ fields = '__all__'
+
diff --git a/people/urls.py b/people/urls.py
new file mode 100644
index 0000000000..c371eb81a0
--- /dev/null
+++ b/people/urls.py
@@ -0,0 +1,17 @@
+# django imports
+from django.conf.urls import url, include
+# app imports
+from people import views
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+urlpatterns=[
+ url(r'^api/info', views.FarmerInfoView.as_view(), name='info'),
+ url(r'^api/farmers', views.FarmersJsonAPIView.as_view({'post':'getAllFarmers'}), name='getAllFarmers'),
+ url(r'^api/match/phone', views.FarmersJsonAPIView.as_view({'post':'getPhoneMatchedResults'}), name='getPhoneMatchedResults'),
+ url(r'^api/csv', views.FarmersCsvAPIView.as_view({'post':'post'})), # r'^$' is used for regex of exact match as mentioned
+]
+
diff --git a/people/views.py b/people/views.py
new file mode 100644
index 0000000000..3dbe1263c4
--- /dev/null
+++ b/people/views.py
@@ -0,0 +1,267 @@
+# default imports
+from django.shortcuts import render
+# rest framework imports
+from rest_framework import viewsets, generics
+from rest_framework import permissions, filters
+from rest_framework.response import Response
+# app import
+from people.serializers import FarmerSerializer
+from people.models import *
+from geographies.models import *
+# django-rest-framework TokenAuthentication imports
+from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
+from rest_framework.permissions import IsAuthenticated
+# CSV View imports
+from rest_framework.renderers import JSONRenderer
+from rest_framework.views import APIView
+from rest_framework.settings import api_settings
+from rest_framework_csv import renderers as r
+# logging, pagination and permissions
+import time
+from api.utils import Utils, CustomPagination
+from api.permissions import IsAllowed
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class FarmerInfoView(generics.ListCreateAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to provide default message in JSON format.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes = [IsAuthenticated and IsAllowed]
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ # dictionary results as JSON format message
+ return Response({"message":"Welcome to COCO APIs", "base_url":"/farmer/api",
+ "url_list":["/farmer/api/farmers", "/farmer/api/csv"]})
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed."})
+
+
+class FarmersJsonAPIView(viewsets.GenericViewSet):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Person model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes = [IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = FarmerSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def getAllFarmers(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Person obects:
+ 1.) country_id - to find people belonging to a country
+ 2.) phoneNumberExists - to find people with valid phone numbers
+ 3.) fields - to pass comma separated value to be returned a value for each Person object, e.g. pass
+ fields value as id,person_name to get only these key-value pairs for each Person object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ country_id = self.request.POST.get('country_id', 0) # POST param 'country_id', default value is 0
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+ phoneNumberExists = request.POST.get('phoneNumberExists','') # POST param 'filter_phone_no', default value is empty string
+
+ try:
+ # fetches country id from database model Country to verify param value
+ got_country_id = Country.objects.get(id=country_id).id
+ # if the country id is found same as param value entered, filters Person model
+ queryset = Person.objects.all().filter(village__block__district__state__country__exact=got_country_id).order_by('id')
+ except:
+ # in case of failure of above try statement, all Person objects are retrieved
+ queryset = Person.objects.all().order_by('id')
+
+ # phone number exists or not
+ if phoneNumberExists.lower() in ["true","t","yes","y"]:
+ queryset = queryset.filter(phone_no__isnull=False).exclude(phone_no__in=[''])
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = FarmerSerializer(queryset, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = FarmerSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
+ # POST request
+ def getPhoneMatchedResults(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Person obects:
+ 1.) country_id - to find people belonging to a country
+ 2.) phoneNumberExists - to find people with valid phone numbers
+ 3.) phone_numbers - to pass comma separated value to search for exact phone numbers
+ 4.) fields - to pass comma separated value to be returned a value for each Person object, e.g. pass
+ fields value as id,person_name to get only these key-value pairs for each Person object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ queryset = Person.objects.all().order_by('id')
+
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+ phone_numbers = request.POST.get('phoneNumbers', '') # POST param 'fields', default value is empty string
+ phoneNumberExists = request.POST.get('phoneNumberExists','') # POST param 'phoneNumberExists', default value is empty string
+
+ # phone number exists or not
+ if phoneNumberExists.lower() in ["true","t","yes","y"]:
+ queryset = queryset.filter(phone_no__isnull=False).exclude(phone_no__in=[''])
+
+ # phone number matches
+ if phone_numbers:
+ ph_no_values = [ph.strip() for ph in phone_numbers.split(",")]
+ queryset = queryset.filter(phone_no__in=ph_no_values)
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = FarmerSerializer(queryset, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = FarmerSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
+
+class FarmersCsvAPIView(viewsets.GenericViewSet):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Person model and provide CSV response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # CSV Renderer class setting to return response of this view as CSV
+ renderer_classes = (r.CSVRenderer, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes = [IsAuthenticated and IsAllowed]
+ serializer_class = FarmerSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Person obects:
+ 1.) country_id - to find people belonging to a country
+ 2.) phoneNumberExists - to find people with valid phone numbers
+ 3.) fields - to pass comma separated value to be returned a value for each Person object, e.g. pass
+ fields value as id,person_name to get only these key-value pairs for each Person object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ country_id = self.request.POST.get('country_id', 0) # POST param 'country_id', default value is 0
+ fields_values = request.POST.get('fields', '') # POST param 'fields', default value is empty string
+ phoneNumberExists = request.POST.get('phoneNumberExists','') # POST param 'filter_phone_no', default value is empty string
+ start_limit = request.POST.get('start_limit','') # POST param 'filter_phone_no', default value is empty string
+ end_limit = request.POST.get('end_limit','') # POST param 'filter_phone_no', default value is empty string
+
+ try:
+ # fetches country id from database model Country to verify param value
+ got_country_id = Country.objects.get(id=country_id).id
+ # if the country id is found same as param value entered, filters Person model
+ queryset = Person.objects.all().filter(village__block__district__state__country__exact=got_country_id).order_by('id')
+ except:
+ # in case of failure of above try statement, all Person objects are retrieved
+ queryset = Person.objects.all().order_by('id')
+
+ # phone number exists or not
+ if phoneNumberExists.lower() in ["true","t","yes","y"]:
+ queryset = queryset.filter(phone_no__isnull=False).exclude(phone_no__in=[''])
+
+ queryset = utils.limitQueryset(queryset, start_limit, end_limit)
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = FarmerSerializer(queryset, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = FarmerSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
diff --git a/programs/models.py b/programs/models.py
index 7b1665a352..17a625fee8 100644
--- a/programs/models.py
+++ b/programs/models.py
@@ -3,7 +3,6 @@
from django.db.models.signals import post_save, pre_delete
from training.log.training_log import enter_to_log
-
class Partner(CocoModel):
id = models.AutoField(primary_key=True)
old_coco_id = models.BigIntegerField(editable=False, null=True, db_index=True)
@@ -17,6 +16,7 @@ def __unicode__(self):
post_save.connect(enter_to_log,sender=Partner)
pre_delete.connect(enter_to_log,sender=Partner)
+
class Project(CocoModel):
id = models.AutoField(primary_key=True)
project_name = models.CharField(max_length=100, unique=True, help_text="Short Name of Project. It will be display on Analytics")
@@ -27,3 +27,4 @@ class Project(CocoModel):
def __unicode__(self):
return self.project_name
+
diff --git a/programs/serializers.py b/programs/serializers.py
new file mode 100644
index 0000000000..c8e620cfd0
--- /dev/null
+++ b/programs/serializers.py
@@ -0,0 +1,24 @@
+from programs.models import *
+from rest_framework import serializers
+from api.utils import DynamicFieldsModelSerializer
+
+class PartnerSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Partner model
+ """
+
+ class Meta:
+ model = Partner
+ fields = '__all__'
+
+
+class ProjectSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Project model
+ """
+
+ class Meta:
+ model = Project
+ fields = '__all__'
+
+
\ No newline at end of file
diff --git a/programs/urls.py b/programs/urls.py
new file mode 100644
index 0000000000..1dfca6427b
--- /dev/null
+++ b/programs/urls.py
@@ -0,0 +1,10 @@
+from django.conf.urls import url, include
+from programs.views import *
+
+urlpatterns = [
+
+ url(r'^api/partner', PartnerAPIView.as_view(), name='partner'),
+ url(r'^api/project', ProjectAPIView.as_view(), name='project'),
+
+]
+
diff --git a/programs/views.py b/programs/views.py
new file mode 100644
index 0000000000..be515b9424
--- /dev/null
+++ b/programs/views.py
@@ -0,0 +1,168 @@
+# default imports
+from django.shortcuts import render
+# rest_framework imports
+from rest_framework import generics, viewsets
+from rest_framework.response import Response
+# app imports
+from programs.models import *
+from programs.serializers import *
+# authentication imports
+from rest_framework.authentication import TokenAuthentication
+from rest_framework.permissions import IsAuthenticated
+# pagination and permissions
+import time
+from api.utils import Utils, CustomPagination
+from api.permissions import IsAllowed
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class PartnerAPIView(generics.ListCreateAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Partner model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes = [IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = PartnerSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Partner obects:
+ 1.) id - to find partner by id
+ 2.) fields - to pass comma separated value to be returned a value for each Partner object, e.g. pass
+ fields value as id,partner_name to get only these key-value pairs for each Partner object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ fields_values = request.POST.get('fields', '') # POST param 'fields'
+ partner_id = self.request.POST.get('id', 0) # POST param 'id'
+
+ serializer_class = PartnerSerializer
+
+ if partner_id: # checks if partner id is present
+ queryset = Partner.objects.filter(id__exact=partner_id)
+ else:
+ queryset = Partner.objects.all().order_by('id')
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = serializer_class(queryset, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = serializer_class(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
+
+class ProjectAPIView(generics.ListCreateAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Project model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes = [IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = ProjectSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Project obects:
+ 1.) id - to find project by id
+ 2.) fields - to pass comma separated value to be returned a value for each Project object, e.g. pass
+ fields value as id,project_name to get only these key-value pairs for each Project object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ fields_values = request.POST.get('fields', '') # POST param 'fields'
+ project_id = self.request.POST.get('id', 0) # POST param 'id'
+
+ serializer_class = ProjectSerializer
+
+ if project_id: # checks if project id is present
+ queryset = Project.objects.filter(id__exact=project_id)
+ else:
+ queryset = Project.objects.all().order_by('id')
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = serializer_class(queryset, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = serializer_class(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response
+
\ No newline at end of file
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index ba7ac40c33..bfad13cc1b 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -1,4 +1,5 @@
appdirs==1.4.3
+appnope==0.1.0
backports.shutil-get-terminal-size==1.0.0
BeautifulSoup==3.2.1
beautifulsoup4==4.5.3
@@ -17,7 +18,10 @@ django-contrib-comments==1.8.0
django-cors-headers==2.1.0
django-debug-toolbar==0.8.5
django-easy-select2==1.3.4
+django-extensions==2.2.9
django-tastypie==0.12.2
+djangorestframework==3.6.4
+djangorestframework-csv==2.1.0
enum34==1.1.6
et-xmlfile==1.0.1
filebrowser-safe==0.4.6
@@ -34,7 +38,9 @@ ipython-genutils==0.2.0
isodate==0.5.4
jdcal==1.3
Mezzanine==4.1.0
-MySQL-python==1.2.3
+mysql-connector==2.2.9
+#mysqlclient==1.4.6
+mysql-python
newrelic==2.82.0.62
numpy==1.12.0
oauth2==1.5.211
@@ -72,6 +78,7 @@ six==1.10.0
tinycss2==0.6.1
traitlets==4.3.2
twython==3.0.0
+typing==3.7.4.1
tzlocal==1.3
unicodecsv==0.9.4
uritemplate==3.0.0
diff --git a/videos/models.py b/videos/models.py
index b7e2d7531f..33e7fc7bb8 100644
--- a/videos/models.py
+++ b/videos/models.py
@@ -238,6 +238,7 @@ def location(self):
post_save.connect(save_log, sender=Video)
pre_delete.connect(delete_log, sender=Video)
+
class NonNegotiable(CocoModel):
id = models.AutoField(primary_key=True)
video = models.ForeignKey(Video)
diff --git a/videos/serializers.py b/videos/serializers.py
new file mode 100644
index 0000000000..8855efa98d
--- /dev/null
+++ b/videos/serializers.py
@@ -0,0 +1,18 @@
+from rest_framework import serializers
+from videos.models import *
+from api.utils import DynamicFieldsModelSerializer
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class VideoSerializer(DynamicFieldsModelSerializer):
+ """
+ Serializer class inherited from DynamicFieldsModelSerializer for Video model
+ """
+
+ class Meta:
+ model = Video
+ fields = '__all__'
+
diff --git a/videos/urls.py b/videos/urls.py
index 441f255063..ddf3ce8b14 100644
--- a/videos/urls.py
+++ b/videos/urls.py
@@ -1,16 +1,15 @@
+# django imports
from django.conf.urls import include, patterns, url
from django.views.generic import TemplateView
from django.views.generic.base import RedirectView
-
-from social_website.views import collection_view, video_view, search_view, collection_edit_view, collection_add_view, partner_view
-from social_website.urls import DirectTemplateView
-
+# app imports
from output.views import video_analytics
-
from dg.base_settings import VIDEOS_PAGE
from dg.website_admin import website_admin
-
+from social_website.views import collection_view, video_view, search_view, collection_edit_view, collection_add_view, partner_view
+from social_website.urls import DirectTemplateView
import videokheti.urls
+from videos import views
urlpatterns = patterns('',
url(r'^$', RedirectView.as_view(url=VIDEOS_PAGE)),
@@ -28,4 +27,9 @@
# admin/logout/ should be above admin/ URL
url(r'^admin/logout/?$', 'django.contrib.auth.views.logout', {'next_page': '/videos/admin/'}),
(r'^admin/', include(website_admin.urls)),
+
+ # coco_api video urls
+ url(r'^api/video', views.VideoAPIView.as_view()),
+
)
+
diff --git a/videos/views.py b/videos/views.py
new file mode 100644
index 0000000000..7a91115ff5
--- /dev/null
+++ b/videos/views.py
@@ -0,0 +1,92 @@
+# django imports
+from django.shortcuts import render
+# rest framework imports
+from rest_framework import viewsets, generics
+from rest_framework import permissions
+from rest_framework.response import Response
+# django-rest-framework TokenAuthentication imports
+from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
+from rest_framework.permissions import IsAuthenticated
+# logging, pagination and permissions
+import time
+from api.utils import Utils, CustomPagination
+from api.permissions import IsAllowed
+# app imports
+from videos.models import *
+from videos.serializers import *
+
+__author__ = "Stuti Verma"
+__credits__ = ["Sujit Chaurasia", "Sagar Singh"]
+__email__ = "stuti@digitalgreen.org"
+__status__ = "Development"
+
+class VideoAPIView(generics.ListCreateAPIView):
+ '''
+ This view is specifically written for coco api access.
+ This class-based view is to query Videos model and provide JSON response.
+ django-rest-framework based token passed in Header as {'Authorization': 'Token 12345exampleToken'}
+ is required to access data from this View.
+ Only POST method is allowed.
+ GET request sent will show a message : "Method \"GET\" not allowed."
+ '''
+
+ # django-rest-framework TokenAuthentication
+ authentication_classes = [TokenAuthentication]
+ permission_classes =[IsAuthenticated and IsAllowed]
+ pagination_class = CustomPagination
+ serializer_class = VideoSerializer
+
+ # GET request
+ def get(self, request):
+ return Response({"detail":"Method \"GET\" not allowed"})
+
+ # POST request
+ def post(self, request, *args, **kwargs):
+ """
+ This function can take following optional POST params to filter on Video obects:
+ 1.) id - to find video by id
+ 2.) fields - to pass comma separated value to be returned a value for each Video object, e.g. pass
+ fields value as id,title to get only these key-value pairs for each Video object
+
+ If none of the above parameters are provided, then all the objects from respective model
+ will be sent to the response.
+ """
+
+ start_time = time.time()
+ utils = Utils()
+
+ fields_values = request.POST.get('fields', '') # POST param 'fields'
+ video_id = self.request.POST.get('id', 0) # POST param 'id'
+
+ if video_id: # checks if video id is present
+ queryset = Video.objects.filter(id__exact=video_id)
+ else:
+ queryset = Video.objects.all().order_by('id')
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = self.get_serializer(page, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = self.get_serializer(page, many=True)
+ paginated_response = self.get_paginated_response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, paginated_response.status_code)
+ return paginated_response
+
+ if fields_values: # fields provided in POST request and if not empty serves those fields only
+ fields_values = [val.strip() for val in fields_values.split(",")]
+ # updated queryset is passed and fields provided in POST request is passed to the dynamic serializer
+ serializer = VideoSerializer(queryset, fields=fields_values, many=True)
+ else:
+ # if fields param is empty then all the fields as mentioned in serializer are served to the response
+ serializer = VideoSerializer(queryset, many=True)
+
+ response = Response(serializer.data)
+ processing_time = time.time() - start_time
+ utils.logRequest(request, self, self.post.__name__ , processing_time, response.status_code)
+ # JSON Response is provided
+ return response