Skip to content
Snippets Groups Projects
Commit 5d7ccb6c authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge remote-tracking branch 'origin/master' into...

Merge remote-tracking branch 'origin/master' into 132-possibility-to-modify-contact-attempt-comments
parents 9ff92dc8 a698930e
No related branches found
No related tags found
1 merge request!63Resolve "Possibility to modify contact attempt comments"
Pipeline #
Showing
with 260 additions and 74 deletions
......@@ -6,6 +6,10 @@ smash/~/
# Disable python bytecode
*.pyc
#vim swap files
*.swp
#vim backup files
*~
# Disable local developer settings
local_settings.py
......
......@@ -19,4 +19,4 @@ test:
- cd smash
- python manage.py makemigrations web && python manage.py migrate
- coverage run --source web manage.py test
- coverage report -m
- coverage report -m --omit="*/test*,*/migrations*"
......@@ -8,4 +8,8 @@ python-docx==0.8.6
django-cleanup==0.4.2
django_cron==0.5.0
django-two-factor-auth==1.6.1
nexmo
\ No newline at end of file
nexmo
django-excel==0.0.9
pyexcel-xls==0.5.0
pyexcel==0.5.3
import traceback
from datetime import datetime
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.urls import reverse
from django.utils import timezone
from web.models import Appointment
from web.views import e500_error
......@@ -29,9 +31,11 @@ def get_appointments(request, type, min_date, max_date):
raise TypeError("Unknown query type: " + type)
if min_date is not None:
min_date = datetime.strptime(min_date, "%Y-%m-%d").replace(tzinfo=timezone.now().tzinfo)
result = result.filter(datetime_when__gt=min_date)
if max_date is not None:
max_date = datetime.strptime(max_date, "%Y-%m-%d").replace(tzinfo=timezone.now().tzinfo)
result = result.filter(datetime_when__lt=max_date)
return result.order_by("datetime_when")
......@@ -57,12 +61,12 @@ def appointments(request, type):
sliced_subjects = all_appointments[start:(start + length)]
appointments = sliced_subjects
result_appointments = sliced_subjects
count_filtered = all_appointments.count()
data = []
for appointment in appointments:
for appointment in result_appointments:
data.append(serialize_appointment(appointment))
return JsonResponse({
......
......@@ -10,25 +10,25 @@ from web.views.subject import SUBJECT_LIST_GENERIC, SUBJECT_LIST_NO_VISIT, SUBJE
@login_required
def cities(request):
subjects = Subject.objects.filter(city__isnull=False).values_list('city').distinct()
result_subjects = Subject.objects.filter(city__isnull=False).values_list('city').distinct()
return JsonResponse({
"cities": [x[0] for x in subjects]
"cities": [x[0] for x in result_subjects]
})
@login_required
def countries(request):
subjects = Subject.objects.filter(country__isnull=False).values_list('country').distinct()
result_subjects = Subject.objects.filter(country__isnull=False).values_list('country').distinct()
return JsonResponse({
"countries": [x[0] for x in subjects]
"countries": [x[0] for x in result_subjects]
})
@login_required
def referrals(request):
subjects = Subject.objects.filter(referral__isnull=False).values_list('referral').distinct()
result_subjects = Subject.objects.filter(referral__isnull=False).values_list('referral').distinct()
return JsonResponse({
"referrals": [x[0] for x in subjects]
"referrals": [x[0] for x in result_subjects]
})
......@@ -135,12 +135,12 @@ def subjects(request, type):
filtered_subjects = get_subjects_filtered(ordered_subjects, filters)
sliced_subjects = filtered_subjects[start:(start + length)]
subjects = sliced_subjects
result_subjects = sliced_subjects
count_filtered = filtered_subjects.count()
data = []
for subject in subjects:
for subject in result_subjects:
data.append(serialize_subject(subject))
return JsonResponse({
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-09-11 10:07
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web', '0045_holiday_info'),
]
operations = [
migrations.AddField(
model_name='subject',
name='flying_team',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='web.FlyingTeam', verbose_name=b'Flying team (if applicable)'),
),
]
# -*- coding: utf-8 -*-
# assigns default flying team location for subjects that had in the past finished appointment at flying team location
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('web', '0046_subject_flying_team'),
]
operations = [
migrations.RunSQL(
"update web_subject set flying_team_id = web_appointment.flying_team_id from web_visit, web_appointment " +
"where web_subject.id=web_visit.subject_id and " +
"web_subject.flying_team_id is null and " +
"web_visit.id = web_appointment.visit_id and " +
"web_appointment.flying_team_id is not null and status = 'FINISHED';"
),
]
......@@ -52,6 +52,12 @@ class Subject(models.Model):
default_location = models.ForeignKey(Location,
verbose_name='Default appointment location',
)
flying_team = models.ForeignKey("web.FlyingTeam",
verbose_name='Default flying team location (if applicable)',
null=True, blank=True
)
first_name = models.CharField(max_length=50,
verbose_name='First name'
)
......
.cell {
display: table-cell;
border-bottom: 1px solid black;
border: 1px solid #BBBBBB;
padding:4px;
text-align: center;
vertical-align: middle;
}
......@@ -26,4 +26,15 @@
.top-buffer {
margin-top: 20px;
}
.checkbox-circle {
width: 10px;
height: 10px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
float:left;
margin:5px;
display: inline;
}
\ No newline at end of file
......@@ -12,7 +12,9 @@ $(document).ready(function () {
var formDataChanged = false;
$(":input", this).change(function () {
formDataChanged = true;
if (this.form !== undefined && this.form !== null) {
formDataChanged = true;
}
});
$('.main-sidebar a').click(function () {
......
......@@ -3,6 +3,7 @@
{% block styles %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'css/export.css' %}">
{% endblock styles %}
{% block ui_active_tab %}'export'{% endblock ui_active_tab %}
......@@ -18,15 +19,21 @@
{% block maincontent %}
<div>
<a href="{% url 'web.views.export_to_csv2' 'subjects' %}" class="btn btn-app">
<i class="fa fa-download"></i>
Subjects
</a>
<br/>
<a href="{% url 'web.views.export_to_csv2' 'appointments' %}" class="btn btn-app">
<i class="fa fa-download"></i>
Appointments
</a>
<h3>Subjects</h3>
<ul>
<li><a href="{% url 'web.views.export_to_excel' 'subjects' %}"><i class="fa fa-file-excel-o"></i> XLS -
Excel</a>
</li>
<li><a href="{% url 'web.views.export_to_excel' 'appointments' %}"><i class="fa fa-file-text-o"></i> CSV -
Text based</a></li>
</ul>
<h3>Appointments</h3>
<ul>
<li><a href="{% url 'web.views.export_to_csv' 'subjects' %}"><i class="fa fa-file-excel-o"></i> XLS -
Excel</a></li>
<li><a href="{% url 'web.views.export_to_csv' 'appointments' %}"><i class="fa fa-file-text-o"></i> CSV -
Text based</a></li>
</ul>
</div>
<div class="box-body">
......
<div class="col-md-6 form-group {% if field.errors %}has-error{% endif %} multi-checkboxes">
<label for="{# TODO #}" class="col-sm-4 control-label">{{ field.label }}</label>
<div class="col-sm-8">
<ul id="id_{{ field.name }}">
{% for pk, choice in field.field.widget.choices %}
<li>
<label for="id_{{ field.name }}_{{ forloop.counter0 }}">
<div class="checkbox-circle"
style="
{% for appointment in appointments %}{% for appointment_type in appointment.appointment_types.all %}{% if appointment_type.id == pk|slugify or appointment_type.id == pk %}{% if appointment.status == "FINISHED" %}
background: green;
{% elif appointment.status == "SCHEDULED" %}
background: orange;
{% endif %}{% endif %}{% endfor %}{% endfor %}
"></div>
<input {% if readonly %}disabled="disabled"{% endif %}
{% for option in field.value %}{% if option == pk|slugify or option == pk %}checked="checked"
{% endif %}{% endfor %} type="checkbox"
id="id_{{ field.name }}_{{ forloop.counter0 }}"
name="{{ field.name }}"
value="{{ pk }}"
/>{{ choice }}</label>
</li>
{% endfor %}
</ul>
</div>
</div>
......@@ -44,21 +44,34 @@
</h5>
</div>
<div class="box-footer">
{% if not element.2 %}
<div>
<a href="{% url 'web.views.appointment_add' element.3 %}" class="btn btn-app">
<i class="fa fa-plus"></i>
Add new appointment
</a>
</div>
{% endif %}
{% for field in element.0 %}
<div class="col-md-6 form-group {% if field.errors %}has-error{% endif %} {% if field|is_checkbox %}multi-checkboxes{% endif %}">
<label class="col-sm-4 control-label">{{ field.label }}</label>
{% if not field|is_checkbox %}
<div class="col-md-6 form-group {% if field.errors %}has-error{% endif %} {% if field|is_checkbox %}multi-checkboxes{% endif %}">
<label class="col-sm-4 control-label">{{ field.label }}</label>
<div class="col-sm-8">
{{ field|disable|add_class:'form-control' }}
</div>
<div class="col-sm-8">
{{ field|disable|add_class:'form-control' }}
</div>
{% if field.errors %}
<span class="help-block">{{ field.errors }}</span>{% endif %}
</div>
{% if field.errors %}
<span class="help-block">{{ field.errors }}</span>{% endif %}
</div>
{% else %}
{% include 'includes/visit_appointment_types_field.html' with field=field appointments=element.1 readonly="true" %}
{% endif %}
{% endfor %}
</div>
</div>
<div class="box box-widget widget-user-2">
<div class="widget-user-header bg-default">
<h3 class="widget-user-username">Visit's appointments</h3>
......
......@@ -25,12 +25,6 @@
<a href="{% url 'web.views.visits' %}" class="btn btn-block btn-default">Cancel</a>
</div>
{% comment %}
<div class="box-header with-border">
<h3 class="box-title">Details of a visit</h3>
</div>
{% endcomment %}
<form method="post" action="" class="form-horizontal">
{% csrf_token %}
......
......@@ -30,30 +30,32 @@
</div>
<div class="box-header with-border">
<h3 class="box-title">Details of visit
</h3>
<h3 class="box-title">Details of visit </h3>
</div>
<form method="post" action="" class="form-horizontal">
{% csrf_token %}
<div class="box-body">
{% for field in vform %}
<div class="col-md-6 form-group {% if field.errors %}has-error{% endif %} {% if field|is_checkbox %}multi-checkboxes{% endif %}">
<label for="{# TODO #}" class="col-sm-4 control-label">
{{ field.label }}
</label>
{% if not field|is_checkbox %}
<div class="col-md-6 form-group {% if field.errors %}has-error{% endif %} {% if field|is_checkbox %}multi-checkboxes{% endif %}">
<label for="{# TODO #}" class="col-sm-4 control-label">{{ field.label }}</label>
<div class="col-sm-8">
{{ field|add_class:'form-control' }}
{# {% if not field|is_checkbox %}#}
<div class="col-sm-8">
{{ field|add_class:'form-control' }}
</div>
{% if field.errors %}
<span class="help-block">
{{ field.errors }}
</span>
{% endif %}
</div>
{% else %}
{% include 'includes/visit_appointment_types_field.html' with field=field appointments=loApp %}
{% endif %}
{% if field.errors %}
<span class="help-block">
{{ field.errors }}
</span>
{% endif %}
</div>
{% endfor %}
<div class="col-md-6 form-group">
......@@ -165,8 +167,8 @@
{% if field.errors %}
<span class="help-block">
{{ field.errors }}
</span>
{{ field.errors }}
</span>
{% endif %}
</div>
{% endfor %}
......
......@@ -4,7 +4,7 @@ import os
from django.contrib.auth.models import User
from web.models import Location, AppointmentType, Subject, Worker, Visit, Appointment, ConfigurationItem, Language, \
ContactAttempt
ContactAttempt, FlyingTeam
from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, CONTACT_TYPES_PHONE
from web.views.notifications import get_today_midnight_date
......@@ -44,12 +44,12 @@ def create_contact_attempt(subject=None, worker=None):
worker = create_worker()
return ContactAttempt.objects.create(subject=subject,
worker=worker,
type=CONTACT_TYPES_PHONE,
datetime_when=get_today_midnight_date(),
success=True,
comment="Successful contact attempt",
)
worker=worker,
type=CONTACT_TYPES_PHONE,
datetime_when=get_today_midnight_date(),
success=True,
comment="Successful contact attempt",
)
def create_subject(subject_id=1):
......@@ -121,6 +121,11 @@ def create_configuration_item():
return item
def create_flying_team():
result = FlyingTeam.objects.create()
return result
def create_language(name="French", locale="fr_FR"):
language = Language(name=name, locale=locale)
language.save()
......@@ -129,3 +134,10 @@ def create_language(name="French", locale="fr_FR"):
def get_resource_path(filename):
return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', filename)
def format_form_field(value):
if isinstance(value, datetime.datetime):
return value.strftime('%Y-%m-%d %H:%M')
else:
return value
......@@ -2,7 +2,8 @@ import datetime
from django.urls import reverse
from functions import create_subject, create_visit, create_appointment, create_worker
from functions import create_subject, create_visit, create_appointment, create_worker, create_flying_team, \
format_form_field
from web.forms import AppointmentEditForm, SubjectEditForm
from web.models import Appointment, Subject
from web.views.notifications import get_today_midnight_date
......@@ -10,6 +11,10 @@ from . import LoggedInTestCase
class AppointmentsViewTests(LoggedInTestCase):
def setUp(self):
super(AppointmentsViewTests, self).setUp()
create_worker(self.user, True)
def test_appointments_list_request(self):
response = self.client.get(reverse('web.views.appointments'))
self.assertEqual(response.status_code, 200)
......@@ -18,7 +23,6 @@ class AppointmentsViewTests(LoggedInTestCase):
subject = create_subject()
visit = create_visit(subject)
appointment = create_appointment(visit, when=datetime.datetime.now())
create_worker(self.user, True)
new_comment = 'new comment'
new_status = appointment.APPOINTMENT_STATUS_NO_SHOW
new_last_name = "new last name"
......@@ -44,8 +48,6 @@ class AppointmentsViewTests(LoggedInTestCase):
self.assertEqual(new_last_name, updated_subject.last_name)
def test_appointments_edit_without_visit(self):
create_worker(self.user)
appointment = create_appointment()
appointment.visit = None
appointment.save()
......@@ -55,8 +57,6 @@ class AppointmentsViewTests(LoggedInTestCase):
self.assertEqual(response.status_code, 200)
def test_save_appointments_edit_without_visit(self):
create_worker(self.user)
appointment = create_appointment()
appointment.visit = None
appointment.save()
......@@ -73,25 +73,44 @@ class AppointmentsViewTests(LoggedInTestCase):
def test_save_appointments_edit(self):
subject = create_subject()
create_worker(self.user, True)
visit = create_visit(subject)
appointment = create_appointment(visit, get_today_midnight_date())
form_data = self.prepare_form(appointment, subject)
form_data["appointment-status"] = Appointment.APPOINTMENT_STATUS_FINISHED
response = self.client.post(
reverse('web.views.appointment_edit', kwargs={'id': appointment.id}), data=form_data)
self.assertEqual(response.status_code, 302)
updated_subject = Subject.objects.get(id=subject.id)
self.assertTrue(updated_subject.information_sent)
def prepare_form(self, appointment, subject):
form_appointment = AppointmentEditForm(user=self.user, instance=appointment, prefix="appointment")
form_subject = SubjectEditForm(instance=subject, prefix="subject")
form_data = {}
for key, value in form_appointment.initial.items():
if value is not None:
form_data['appointment-{}'.format(key)] = value
form_data['appointment-{}'.format(key)] = format_form_field(value)
for key, value in form_subject.initial.items():
if value is not None:
form_data['subject-{}'.format(key)] = value
form_data['subject-{}'.format(key)] = format_form_field(value)
return form_data
def test_subject_flying_team_location(self):
subject = create_subject()
visit = create_visit(subject)
appointment = create_appointment(visit, get_today_midnight_date())
form_data = self.prepare_form(appointment, subject)
form_data["appointment-status"] = Appointment.APPOINTMENT_STATUS_FINISHED
form_data["appointment-flying_team"] = create_flying_team().id
form_data['appointment-status'] = Appointment.APPOINTMENT_STATUS_FINISHED
response = self.client.post(
reverse('web.views.appointment_edit', kwargs={'id': appointment.id}), data=form_data)
self.assertEqual(response.status_code, 302)
updated_subject = Subject.objects.get(id=subject.id)
self.assertTrue(updated_subject.information_sent)
self.assertIsNotNone(updated_subject.flying_team)
......@@ -4,7 +4,7 @@ from django.urls import reverse
from django.utils import timezone
from web.forms import ContactAttemptEditForm
from functions import create_subject, create_contact_attempt
from functions import create_subject, create_contact_attempt, format_form_field
from web.models import ContactAttempt
from web.models.constants import CONTACT_TYPES_EMAIL
from . import LoggedInWithWorkerTestCase
......@@ -60,11 +60,13 @@ class ContactAttemptViewTests(LoggedInWithWorkerTestCase):
form_data = {}
for key, value in form_subject.initial.items():
if value is not None:
form_data[key] = value
form_data[key] = format_form_field(value)
response = self.client.post(
reverse('web.views.contact_edit',
kwargs={'subject_id': contact_attempt.subject.id, 'contact_attempt_id': contact_attempt.id}),
data=form_data)
print response.content
self.assertEqual(response.status_code, 302)
# coding=utf-8
from django.urls import reverse
from functions import create_subject, create_appointment
from . import LoggedInTestCase
class TestExportView(LoggedInTestCase):
def test_export_subjects_to_csv(self):
create_subject()
response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "subjects"}))
self.assertEqual(response.status_code, 200)
def test_export_appointments_to_csv(self):
create_appointment()
response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "appointments"}))
self.assertEqual(response.status_code, 200)
def test_export_subjects_to_excel(self):
create_subject()
response = self.client.get(reverse('web.views.export_to_excel', kwargs={'data_type': "subjects"}))
self.assertEqual(response.status_code, 200)
def test_export_appointments_to_excel(self):
create_appointment()
response = self.client.get(reverse('web.views.export_to_excel', kwargs={'data_type': "appointments"}))
self.assertEqual(response.status_code, 200)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment