datawinners/project/wizard_view.py
import json
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from django.utils.translation import ugettext
from django.views.decorators.csrf import csrf_exempt
from django.contrib import messages
from django.utils.translation import ugettext as _
from celery.task import current
from datawinners import settings
from mangrove.errors.MangroveException import DataObjectAlreadyExists, QuestionCodeAlreadyExistsException, \
EntityQuestionAlreadyExistsException, QuestionAlreadyExistsException
from mangrove.form_model.field import field_to_json
from mangrove.form_model.project import Project
from mangrove.transport.repository.survey_responses import survey_responses_by_form_model_id
from datawinners.accountmanagement.decorators import is_datasender, session_not_expired, is_not_expired
from datawinners.accountmanagement.models import Organization, NGOUserProfile
from datawinners.project.models import Reminder, ReminderMode
from datawinners.main.database import get_database_manager, get_db_manager
from datawinners.questionnaire.library import QuestionnaireLibrary
from datawinners.tasks import app
from datawinners.activitylog.models import UserActivityLog
from datawinners.utils import get_changed_questions, get_organization
from datawinners.common.constant import EDITED_QUESTIONNAIRE, ACTIVATED_REMINDERS, DEACTIVATED_REMINDERS, \
SET_DEADLINE, UPDATED_REMINDERS
from datawinners.questionnaire.questionnaire_builder import QuestionnaireBuilder
from datawinners.project.helper import is_project_exist
from datawinners.project.utils import is_quota_reached
from mangrove.utils.types import is_empty
def create_questionnaire(post, manager, name, language, reporter_id, question_set_json=None,
xform=None, is_open_survey=False):
questionnaire_code = post['questionnaire-code'].lower()
datasenders = json.loads(post.get('datasenders', "[]"))
question_set = question_set_json if question_set_json else json.loads(post['question-set'])
questionnaire = Project(manager, name=name,
fields=[], form_code=questionnaire_code, language=language,
devices=[u'sms', u'web', u'smartPhone'])
questionnaire.xform = xform
if is_open_survey:
questionnaire.is_open_survey = post.get('is_open_survey')
if reporter_id is not None:
questionnaire.data_senders.append(reporter_id)
if datasenders:
questionnaire.data_senders.extend(filter(lambda ds: ds != reporter_id, datasenders))
outgoing_sms_enabled = not xform and post.get('is_outgoing_sms_enabled', 'true')
QuestionnaireBuilder(questionnaire, manager)\
.update_questionnaire_with_questions(question_set)\
.update_reminder(json.loads(post.get('reminder_and_deadline', '{}')))\
.update_outgoing_sms_enabled_flag(outgoing_sms_enabled)
return questionnaire
def update_questionnaire(questionnaire, post, manager):
questionnaire.form_code = post['questionnaire-code'].lower()
json_string = post['question-set']
question_set = json.loads(json_string)
QuestionnaireBuilder(questionnaire, manager).update_questionnaire_with_questions(question_set)
return questionnaire
def get_preview_and_instruction_links():
links = {
'sms_preview': reverse("sms_preview"),
'web_preview': reverse("web_preview"),
'smart_phone_preview': reverse("smart_phone_preview"),
}
return links
@login_required
@session_not_expired
@csrf_exempt
@is_not_expired
def get_templates(request):
library = QuestionnaireLibrary()
return HttpResponse(json.dumps({'categories': library.get_template_groupings(request.LANGUAGE_CODE)}),
content_type='application/json')
@login_required
def get_template_details(request, template_id):
library = QuestionnaireLibrary()
template = library.get_questionnaire_template(template_id)
template_details = {'template_id': template.id, 'project_name': template.get('name'),
'project_language': template.get('language'),
'questionnaire_code': template.get('form_code'),
'existing_questions': json.dumps(template.get('json_fields'), default=field_to_json)}
return HttpResponse(json.dumps(template_details), content_type='application/json')
def _get_deleted_question_codes(new_codes, old_codes):
diff = set(old_codes) - set(new_codes)
return filter(diff.__contains__, old_codes)
def _get_changed_data(project, project_info):
changed_dict = dict()
for attr, value in project_info.iteritems():
if getattr(project, attr) != value:
changed_dict.update({attr.capitalize(): value})
return changed_dict
@login_required
@session_not_expired
@is_datasender
@csrf_exempt
@is_not_expired
@is_project_exist
def edit_project(request, project_id):
manager = get_database_manager(request.user)
dashboard_page = settings.HOME_PAGE + "?deleted=true"
questionnaire = Project.get(manager, project_id)
if questionnaire.is_void():
return HttpResponseRedirect(dashboard_page)
if request.method == 'GET':
return render_to_response('project/create_project.html',
{'preview_links': get_preview_and_instruction_links(),
'questionnaire_code': questionnaire.form_code,
'is_pro_sms': get_organization(request).is_pro_sms,
'is_edit': 'true',
'post_url': reverse(edit_project, args=[project_id])},
context_instance=RequestContext(request))
if request.method == 'POST':
project_info = json.loads(request.POST['profile_form'])
detail = _get_changed_data(questionnaire , project_info)
if detail.get("Name"):
detail.pop("Name")
try:
old_fields = questionnaire.fields
questionnaire.old_form_code = questionnaire.form_code
old_field_codes = questionnaire.field_codes()
questionnaire = update_questionnaire(questionnaire, request.POST, manager)
changed_questions = get_changed_questions(old_fields, questionnaire.fields, subject=False)
detail.update(changed_questions)
questionnaire.save()
deleted_question_codes = _get_deleted_question_codes(old_codes=old_field_codes,
new_codes=questionnaire.field_codes())
update_associated_submissions.delay(manager.database_name,
questionnaire.id,
deleted_question_codes)
UserActivityLog().log(request, project=questionnaire.name, action=EDITED_QUESTIONNAIRE, detail=json.dumps(detail))
except (QuestionCodeAlreadyExistsException, QuestionAlreadyExistsException,
EntityQuestionAlreadyExistsException) as ex:
return HttpResponse(
json.dumps({'success': False, 'error_in_project_section': False, 'error_message': _(ex.message)}))
except DataObjectAlreadyExists:
return HttpResponse(json.dumps({'success': False, 'error_in_project_section': False, "code_has_error":True,
'error_message': ugettext('Questionnaire with same code already exists.')}))
if request.POST['has_callback'] == 'false':
messages.add_message(request,messages.INFO,"success")
return HttpResponse(json.dumps({'success': True, 'project_id': project_id, 'is_pro_sms': get_organization(request).is_pro_sms,}))
@login_required
@session_not_expired
@is_datasender
@is_not_expired
@is_project_exist
@csrf_exempt
def reminder_settings(request, project_id):
dbm = get_database_manager(request.user)
questionnaire = Project.get(dbm, project_id)
dashboard_page = settings.HOME_PAGE + "?deleted=true"
if questionnaire.is_void():
return HttpResponseRedirect(dashboard_page)
if questionnaire.is_poll:
return HttpResponseRedirect('/project/'+ project_id + '/results/'+questionnaire.form_code)
from datawinners.project.views.views import make_project_links
project_links = make_project_links(questionnaire)
active_language = request.LANGUAGE_CODE
org_id = (NGOUserProfile.objects.get(user=request.user)).org_id
organization = Organization.objects.get(org_id=org_id)
is_reminder_disabled = is_empty(questionnaire.data_senders)
url_to_my_datasender = project_links['registered_datasenders_link']
html = 'project/reminders_trial.html' if organization.in_trial_mode else 'project/reminder_settings.html'
if request.method == 'GET':
data = (_reminder_info_about_project(questionnaire))
return render_to_response(html,
{'project_links': project_links,
'is_quota_reached': is_quota_reached(request, organization=organization),
'project': questionnaire,
'reminder_data': repr(json.dumps(data)),
'questionnaire_code': questionnaire.form_code,
'is_reminder_disabled': is_reminder_disabled,
'active_language': active_language,
'no_of_my_datasenders': len(questionnaire.data_senders),
'is_pro_sms': get_organization(request).is_pro_sms,
'url_to_my_datasender': url_to_my_datasender,
'post_url': reverse(reminder_settings, args=[project_id])
}, context_instance=RequestContext(request))
if request.method == 'POST':
data = (_reminder_info_about_project(questionnaire))
post_data = request.POST.copy()
details = dict()
if unicode(data['number_of_days_before_deadline'])!= post_data['number_of_days_before_deadline']:
details.update({"Before-deadline reminder updated: %s Day(s)": post_data['number_of_days_before_deadline']})
if unicode(data['number_of_days_after_deadline']) != post_data['number_of_days_after_deadline']:
details.update({"After-deadline reminder updated: %s Day(s)": post_data['number_of_days_after_deadline']})
if post_data['reminder_text_before_deadline'] != data['reminder_text_before_deadline']:
details.update({"Text updated for before-deadline reminder: %s": post_data['reminder_text_before_deadline']})
if post_data['reminder_text_on_deadline'] != data['reminder_text_on_deadline']:
details.update({"Text updated for on-deadline reminder: %s": post_data['reminder_text_on_deadline']})
if post_data['reminder_text_after_deadline'] != data['reminder_text_after_deadline']:
details.update({"Text updated for after-deadline reminder: %s": post_data['reminder_text_after_deadline']})
if data['should_send_reminders_before_deadline']== False and post_data['should_send_reminders_before_deadline'] =='true':
details.update({"Before-deadline reminder activated": ""})
elif data['should_send_reminders_before_deadline']== True and post_data['should_send_reminders_before_deadline'] =='false':
details.update({"Before-deadline reminder deactivated": ""})
if data['should_send_reminders_on_deadline'] == False and post_data['should_send_reminders_on_deadline'] =='true':
details.update({"On-deadline reminder activated": ""})
elif data['should_send_reminders_on_deadline'] == True and post_data['should_send_reminders_on_deadline'] =='false':
details.update({"On-deadline reminder deactivated": ""})
if data['should_send_reminders_after_deadline'] == False and post_data['should_send_reminders_after_deadline'] == 'true':
details.update({"After-deadline reminder activated": ""})
elif data['should_send_reminders_after_deadline'] == True and post_data['should_send_reminders_after_deadline'] == 'false':
details.update({"After-deadline reminder deactivated": ""})
if data['whom_to_send_message'] == True and post_data['whom_to_send_message'] == 'false':
details.update({"Reminders updated to: All My Data Senders": ""})
if data['whom_to_send_message'] == False and post_data['whom_to_send_message'] == 'true':
details.update({"Reminders updated to: My Data Senders who have not yet submitted for this deadline": ""})
if len(details):
UserActivityLog().log(request, action=UPDATED_REMINDERS, project=questionnaire.name, detail=json.dumps(details))
if data['frequency_period']== "week" and data['select_day'] != post_data['select_day']:
details = 'Deadline updated: weekday'
UserActivityLog().log(request, action=SET_DEADLINE, project=questionnaire.name, detail=details)
if data['frequency_period']== "month" and data['select_day'] != post_data['select_day']:
details = 'Deadline updated: month day'
UserActivityLog().log(request, action=SET_DEADLINE, project=questionnaire.name, detail=details)
post_data['should_send_reminder_to_all_ds'] = not post_data['whom_to_send_message'] == 'true'
post_data = _populate_week_month_data(post_data)
org_id = NGOUserProfile.objects.get(user=request.user).org_id
organization = Organization.objects.get(org_id=org_id)
reminder_list = Reminder.objects.filter(project_id=questionnaire.id)
action = _get_activity_log_action(reminder_list, post_data)
questionnaire, set_deadline, details = _add_reminder_info_to_project(post_data, questionnaire, organization,
reminder_list=reminder_list)
questionnaire.save()
if action is not None:
UserActivityLog().log(request, action=action, project=questionnaire.name)
if set_deadline:
UserActivityLog().log(request, action=SET_DEADLINE, project=questionnaire.name, detail=details)
response = {'success_message': ugettext("Reminder settings saved successfully."), 'success': True, 'is_pro_sms': get_organization(request).is_pro_sms,}
return HttpResponse(json.dumps(response))
def _populate_week_month_data(post_data):
if post_data['frequency_period'] == 'month':
post_data['deadline_month'] = post_data['select_day']
else:
post_data['deadline_week'] = post_data['select_day']
return post_data
def _reminder_info_about_project(project):
data = {}
deadline_information = project.reminder_and_deadline
data['has_deadline'] = deadline_information['has_deadline']
if deadline_information['has_deadline']:
data['frequency_period'] = deadline_information['frequency_period']
if deadline_information['frequency_period'] == 'month':
data['select_day'] = deadline_information['deadline_month']
else:
data['select_day'] = deadline_information['deadline_week']
reminder_before_deadline = Reminder.objects.filter(reminder_mode=ReminderMode.BEFORE_DEADLINE,
project_id=project.id)
if reminder_before_deadline.count() > 0:
data['should_send_reminders_before_deadline'] = True
data['number_of_days_before_deadline'] = reminder_before_deadline[0].day
data['reminder_text_before_deadline'] = reminder_before_deadline[0].message
else:
data['should_send_reminders_before_deadline'] = False
data['number_of_days_before_deadline'] = 2
data['reminder_text_before_deadline'] = ugettext("Hello. We have not received your data yet for this period. Please send it to us within the next 2 days. Thank you.")
reminder_on_deadline = Reminder.objects.filter(reminder_mode=ReminderMode.ON_DEADLINE, project_id=project.id)
if reminder_on_deadline.count() > 0:
data['should_send_reminders_on_deadline'] = True
data['reminder_text_on_deadline'] = reminder_on_deadline[0].message
else:
data['should_send_reminders_on_deadline'] = False
data['reminder_text_on_deadline'] = ugettext("Reports are due today. Please submit soon.")
reminder_after_deadline = Reminder.objects.filter(reminder_mode=ReminderMode.AFTER_DEADLINE,
project_id=project.id)
if reminder_after_deadline.count() > 0:
data['should_send_reminders_after_deadline'] = True
data['number_of_days_after_deadline'] = reminder_after_deadline[0].day
data['reminder_text_after_deadline'] = reminder_after_deadline[0].message
else:
data['should_send_reminders_after_deadline'] = False
data['number_of_days_after_deadline'] = 2
data['reminder_text_after_deadline'] = ugettext("Hello. We have not received your data yet for this period. Please send it to us by the end of today. Thank you.")
data['whom_to_send_message'] = not deadline_information['should_send_reminder_to_all_ds']
return data
def _add_reminder_info_to_project(cleaned_data, project, organization, reminder_list=None):
set_deadline = False
if project.reminder_and_deadline.get('has_deadline'):
project.reminder_and_deadline['frequency_period'] = cleaned_data['frequency_period']
if cleaned_data['frequency_period'] == 'month':
if project.reminder_and_deadline.get('deadline_week'):
del project.reminder_and_deadline['deadline_week']
set_deadline = True
project.reminder_and_deadline['deadline_month'] = cleaned_data['deadline_month']
else:
if project.reminder_and_deadline.get('deadline_month'):
del project.reminder_and_deadline['deadline_month']
set_deadline = True
project.reminder_and_deadline['deadline_week'] = cleaned_data['deadline_week']
if project.reminder_and_deadline.get('deadline_type', None):
del project.reminder_and_deadline['deadline_type']
if reminder_list is None:
reminder_list = Reminder.objects.filter(project_id=project.id)
reminder_list.delete()
if cleaned_data['should_send_reminders_before_deadline'] == 'true':
Reminder(project_id=project.id, day=cleaned_data['number_of_days_before_deadline'],
message=cleaned_data['reminder_text_before_deadline'],
reminder_mode=ReminderMode.BEFORE_DEADLINE, organization=organization).save()
if cleaned_data['should_send_reminders_on_deadline'] == 'true':
Reminder(project_id=project.id, day=0, message=cleaned_data['reminder_text_on_deadline'],
reminder_mode=ReminderMode.ON_DEADLINE, organization=organization).save()
if cleaned_data['should_send_reminders_after_deadline'] == 'true':
Reminder(project_id=project.id, day=cleaned_data['number_of_days_after_deadline'],
message=cleaned_data['reminder_text_after_deadline'],
reminder_mode=ReminderMode.AFTER_DEADLINE, organization=organization).save()
project.reminder_and_deadline['should_send_reminder_to_all_ds'] = not cleaned_data['whom_to_send_message'] == 'true'
else:
reminder_list = Reminder.objects.filter(project_id=project.id)
reminder_list.delete()
details = None
if project.reminder_and_deadline.get('deadline_month') and set_deadline == True:
set_deadline = True
details = 'Deadline updated: month'
elif project.reminder_and_deadline.get('deadline_week') and set_deadline == True:
set_deadline = True
details = 'Deadline updated: week'
return project, set_deadline, details
def _get_activity_log_action(reminder_list, new_value):
action = None
if reminder_list.count() == 0 and (new_value['should_send_reminders_after_deadline'] == 'true' or
new_value['should_send_reminders_on_deadline'] == 'true' or
new_value['should_send_reminders_before_deadline'] == 'true'):
action = ACTIVATED_REMINDERS
if reminder_list.count() > 0 and not (new_value['should_send_reminders_after_deadline'] == 'true' or
new_value['should_send_reminders_on_deadline'] == 'true 'or
new_value['should_send_reminders_before_deadline'] == 'true'):
action = DEACTIVATED_REMINDERS
return action
#def update_submissions_for_form_code_change(manager, new_form_code, old_form_code):
# if old_form_code != new_form_code:
# survey_responses = survey_responses_by_form_code(manager, old_form_code)
# documents = []
# for survey_response in survey_responses:
# survey_response.form_code = new_form_code
# documents.append(survey_response._doc)
# manager._save_documents(documents)
def remove_deleted_questions_from_submissions(manager, form_model_id, deleted_question_codes):
if deleted_question_codes:
survey_responses = survey_responses_by_form_model_id(manager, form_model_id)
for survey_response in survey_responses:
for code in deleted_question_codes:
survey_response._doc.values.pop(code, None)
survey_response.save()
@app.task(max_retries=3, throw=False)
def update_associated_submissions(database_name, form_model_id, deleted_question_codes):
try:
manager = get_db_manager(database_name)
#update_submissions_for_form_code_change(manager, new_form_code, old_form_code)
remove_deleted_questions_from_submissions(manager, form_model_id, deleted_question_codes)
except Exception as e:
current.retry(exc=e)